项目仓库地址
项目学习地址
将开发以LLM为功能核心,通过LLM的强大理解能力和生成能力,结合特殊的数据或业务逻辑来提供独特功能的应用。
在大模型开发中,我们一般不会去大幅度改动模型,而是将大模型作为一个调用工具。通过 Prompt Engineering、数据工程、业务逻辑分解等手段来充分发挥大模型能力,适配应用任务,而不会将精力聚焦在优化模型本身上。这因此,作为大模型开发的初学者,我们并不需要深研大模型内部原理,而更需要掌握使用大模型的实践技巧。
指令理解与文本生成,提供了复杂业务逻辑的简单平替方案。
传统 AI | 大模型开发 | ||
---|---|---|---|
开发 | 将复杂业务逻辑拆解,对于每一个子业务构造训练数据与验证数据,训练优化模型,形成完整的模型链路来解决整个业务逻辑。 | 用 Prompt Engineering 来替代子模型的训练调优。通过 Prompt 链路组合来实现业务逻辑,用一个通用大模型 + 若干业务 Prompt 来解决任务 | |
评估思路 | 训练集上训练、测试集调优、验证集验证 | 初始化验证集Prompt、收集BadCase、迭代优化Prompt |
确定目标:在进行开发前,我们首先需要确定开发的目标,即要开发的应用的应用场景、目标人群、核心价值。对于个体开发者或小型开发团队而言,一般应先设定最小化目标,从构建一个 mvp(最小可行性产品)开始,逐步进行完善和优化。
设计功能:在确定开发目标后,需要设计本应用所要提供的功能,以及每一个功能的大体实现逻辑。虽然我们通过使用大模型来简化了业务逻辑的拆解,但是越清晰、深入的业务逻辑理解往往也能带来更好的 Prompt 效果。同样,对于个体开发者或小型开发团队来说,首先要确定应用的核心功能,然后延展设计核心功能的上下游功能;例如,我们想打造一款个人知识库助手,那么核心功能就是结合个人知识库内容进行问题的回答,那么其上游功能的用户上传知识库、下游功能的用户手动纠正模型回答就是我们也必须要设计实现的子功能。
搭建整体架构:目前,绝大部分大模型应用都是采用的特定数据库+ Prompt + 通用大模型的架构。我们需要针对我们所设计的功能,搭建项目的整体架构,实现从用户输入到应用输出的全流程贯通。一般来说,我们推荐基于 LangChain 框架进行开发。LangChain 提供了 Chain、Tool 等架构的实现,我们可以基于 LangChain 进行个性化定制,实现从用户输入到数据库再到大模型最后输出的整体架构连接。
搭建数据库:个性化大模型应用需要有个性化数据库进行支撑。由于大模型应用需要进行向量语义检索,一般使用诸如 chroma 的向量数据库。在该步骤中,我们需要收集数据并进行预处理,再向量化存储到数据库中。数据预处理一般包括从多种格式向纯文本的转化,例如 pdf、markdown、html、音视频等,以及对错误数据、异常数据、脏数据进行清洗。完成预处理后,需要进行切片、向量化构建出个性化数据库。
验证迭代:验证迭代在大模型开发中是极其重要的一步,一般指通过不断发现 Bad Case 并针对性改进 Prompt Engineering 来提升系统效果、应对边界情况。在完成上一步的初始化 Prompt 设计后,我们应该进行实际业务测试,探讨边界情况,找到 Bad Case,并针对性分析 Prompt 存在的问题,从而不断迭代优化,直到达到一个较为稳定、可以基本实现目标的 Prompt 版本。
体验优化:在完成前后端搭建之后,应用就可以上线体验了。接下来就需要进行长期的用户体验跟踪,记录 Bad Case 与用户负反馈,再针对性进行优化即可。
以下我们将结合本实践项目与上文的整体流程介绍,简要分析本项目开发流程如下:
基于个人知识库的问答助手
本项目实现原理如下图所示(图片来源),过程包括:
用户常用文档格式有 pdf、txt、doc 等,首先使用工具读取文本,通常使用 langchain 的文档加载器模块可以方便的将用户提供的文档加载进来,也可以使用一些 python 比较成熟的包进行读取。
由于目前大模型使用 token 限制
,我们需要对读取的文本进行切分,将较长的文本切分
为较小的文本,这时一段文本就是一个单位的知识。
使用文本嵌入(Embeddings)
对分割后的文档进行向量化,使语义相似的文本片段具有接近的向量表示。然后,存入向量数据库,这个流程正是创建索引(index)
的过程。
向量数据库对各文档片段进行索引,支持快速检索。这样,当用户提出问题时,可以先将问题转换为向量,在数据库中快速找到语义最相关的文档片段
。然后将这些文档片段与问题一起传递给语言模型,生成回答。
Langchain集成了超过30个不同的向量存储库。我们选择 Chroma 向量库是因为它轻量级且数据存储在内存中,这使得它非常容易启动和开始使用。
将用户知识库内容经过 embedding 存入向量知识库,然后用户每一次提问也会经过 embedding,利用向量相关性算法(例如余弦算法
)找到最匹配的几个知识库片段,将这些知识库片段作为上下文,与用户问题一起作为 prompt 提交给 LLM 回答。
整个流程将确保项目从规划、开发、测试到上线和维护都能够顺利进行,为用户提供高质量的基于个人知识库的问答助手。
经过上文分析,本项目为搭建一个基于大模型的个人知识库助手,基于 LangChain 框架搭建,核心技术包括 LLM API 调用、向量数据库、检索问答链
等。项目整体架构如下:
如上,本项目从底向上依次分为 LLM 层、数据层、数据库层、应用层与服务层
。
本项目的完整代码存放于 project 目录下,实现了项目的全部功能及封装,代码结构如下:
-project
-readme.md 项目说明
-llm LLM调用封装
-self_llm.py 自定义 LLM 基类
-wenxin_llm.py 自定义百度文心 LLM
-spark_llm.py 自定义讯飞星火 LLM
-zhipu_llm.py 自定义智谱 LLM
-call_llm.py 将各个 LLM 的原生接口封装在一起
-embedding embedding调用封装
-zhipu_embedding.py 自定义智谱embedding
-data 源数据路径
-database 数据库层封装
-create_db.py 处理源数据及初始化数据库封装
-chain 应用层封装
-qa_chain.py 封装检索问答链,返回一个检索问答链对象
-chat_qa_chian.py:封装对话检索链,返回一个对话检索链对象
-prompt_template.py 存放多个版本的 Template
-serve 服务层封装
-run_gradio.py 启动 Gradio 界面
-api.py 封装 FastAPI
-run_api.sh 启动 API
run_gradio
或者run_api
启动整个服务;qa_chain.py
或chat_qa_chain
实例化对话检索链对象,实现全部核心功能;prompt_template.py
中的 prompt 模板来实现 prompt 的迭代;call_llm
中的get_completion
函数来实现不使用数据库的 LLM;create_db.py
创建数据库,该脚本可以使用 openai embedding 也可以使用embedding.py
中的自定义 embedding。LLM 层主要功能为:将国内外四种知名 LLM API(OpenAI-ChatGPT、百度文心、讯飞星火、智谱GLM
)进行封装,隐藏不同 API 的调用差异,实现在同一个对象或函数中通过不同的 model 参数来使用不同来源的 LLM。
在 LLM 层,我们首先构建了一个 Self_LLM 基类,基类定义了所有 API 的一些共同参数(如 API_Key,temperature 等);然后我们在该基类基础上继承实现了上述四种 LLM API 的自定义 LLM。同时,我们也将四种 LLM 的原生 API 封装在了统一的 get_completion 函数中。
在上一章,我们已详细介绍了每一种 LLM 的调用方式、封装方式,项目代码中的 LLM 层封装就是上一章讲解的代码实践。
数据层主要包括:个人知识库的源数据(包括 pdf、txt、md 等)和 Embedding 对象。源数据需要经过 Embedding 处理才能进入向量数据库,我们在数据层自定义了智谱提供的 Embedding API 的封装,支持上层以统一方式调用智谱 Embedding 或 OpenAI Embedding。
在上一章,我们也已详细介绍了 Embdding API 的调用及封装方式。
数据库层主要:存放了向量数据库文件。同时,我们在该层实现了源数据处理、创建向量数据库的方法。
我们将在第四章详细介绍向量数据库、源数据处理方法以及构建向量数据库的具体实现。
应用层:封装了整个项目的全部核心功能。我们基于 LangChain 提供的检索问答链,在 LLM 层、数据库层的基础上,实现了本项目检索问答链的封装。自定义的检索问答链除具备基本的检索问答功能外,也支持通过 model 参数来灵活切换使用的 LLM。我们实现了两个检索问答链,分别是有历史记录的 Chat_QA_Chain 和没有历史记录的 QA_Chain。
我们将在第五章讲解 Prompt 的构造与检索问答链的构建细节。
服务层主要是:基于应用层的核心功能封装,实现了 Demo 的搭建或 API 的封装。在本项目中,我们分别实现了通过 Gradio 搭建前端界面与 FastAPI 进行封装,支持多样化的项目调用。
我们将在第六章详细介绍如何使用 Gradio 以及 FastAPI 来实现服务层的设计。