LLM笔记3:LLM项目设计及开发流程
本文最后更新于:2023年11月21日 晚上
1、大模型开发
1.1、大模型开发概念
初级大模型的开发,一般是将LLM作为一个调用工具,通过 Prompt Engineering、数据工程、业务逻辑分解等手段来发挥大模型的能力,一般不会去大幅度改动模型本身。通过调用LLM来适配不同的任务,之后在应用场景中去优化。
由于不需要对模型进行训练,无需构造类似于传统AI开发需要用到的训练集等内容,可以直接从我们所处的业务中自行构造出一小批特定场景的小验证集,重点工作主要还是在Prompt的设计上,以此来验证llm开发的效果是否达标。在开发过程中,尝试用 Prompt Engineering 来替代子模型的训练调优,通过 Prompt 链路组合来实现业务逻辑,用一个通用大模型 + 若干业务 Prompt 来解决任务
在实际的业务中,收集Bad Case,再通过这些Bad Case针对性优化Prompt,从而实现较好的泛化效果。相较于传统 AI 的评估过程,需要构造训练集、测试集、验证集,在训练集上训练模型、在测试集上调优模型、在验证集上最终验证模型效果,整个开发链路相对简化了一些。
1.2、LLM项目开发的整理流程
LLM项目的开发流程,整体上包含8个过程项,顺序如下图:
- 确定目标:需要明确出开发所针对的应用场景、目标人群和核心价值
- 功能设计:从业务逻辑中提炼出应用所应该要提供的功能,设计出一个完整的适配LLM场景的功能逻辑。
- 整体框架搭建:在明确好功能之后,需要对功能进行规划,明确好设计框架,当前通用的框架是 特定数据库(向量数据库)+ Prompt + 通用大模型。推荐使用LangChain框架来进行开发,其提供了Chain、Tool 等架构的实现,可以基于 LangChain 框架进行LLM的定制化,从而实现从用户输入到数据库再到大模型最后输出的整体架构连接
- 搭建数据库(向量):开发过程需要使用到个性化的数据库进行支撑,由于LLM需要进行向量语义检索,一般使用想chroma的向量数据库。
- Prompt Engineering:Prompt对LLM影响尤为深刻,这一步需要明确出Prompt的设计原则和技巧,一般借助于业务数据中收集到的验证集来设计满足要求、具备基本能力的Prompt。
- 验证迭代:模型开发完成之后,还需要对其中发现的Bad Case来进行针对性的改进Prompt,并验证改进,通过这个过程不断迭代不断优化,提升系统效果。
- 搭建前端后台:完整迭代优化之后,就已经完成了核心功能,后续可以进行前端后台的搭建。这个领域已经发展的非常成熟,主流的有两种快速开发Demo的框架有Gradio和Streamlit,可以是开发者迅速搭建可视化页面并实现Demo上线。
- 体验优化:到了这一步,基本上已经有了用户开始使用了,这一过程就需要来对用户体验进行跟踪,记录用户反馈及其中出现的Bad Case,从而对应用进行针对性的优化。
基本的环节如上,其中比较核心的几个环节有,搭建向量数据库、Prompt Engineering、验证迭代,这几步对应用影响较大,甚至可以说直接决定了用户体验好坏与否。
2、LLM项目流程
下面根据我们所需要实现的个人应用来按照上述的框架,做一些项目流程拆解:
2.1、项目规划与需求分析
明确设计需求及应用功能:
- 1)项目目标:
基于个人知识库的问答助手
- 2)核心功能:
- 上传文档、创建知识库;
- 选择知识库,检索用户提问的知识片段;
- 提供知识片段与提问,获取大模型回答;
- 流式回复;
- 历史对话记录;
- 3)确定技术架构和工具
- 开发框架:LangChain框架
- 向量数据库:Chroma知识库
- LLM:GPT、星火大模型、文心一言、GLM 等
- 前后端:Gradio 和 Streamlit
2.2、向量数据库构建与数据获取
1)项目原理
本项目实现原理如下图所示(图片来源 ):
过程概述如下:
加载本地文档 -> 读取文本 -> 文本分割 -> 文本向量化 -> question向量化 -> 在文本向量中匹配出与问句向量最相似的 top k个 -> 匹配出的文本作为上下文和问题一起添加到 prompt中 -> 提交给 LLM生成回答
2)收集和整理用户提供的文档
用户常用文档格式有 pdf、txt、doc 等,首先使用工具读取文本,通常使用 langchain 的文档加载器模块可以方便的将用户提供的文档加载进来,也可以使用一些 python 比较成熟的包进行读取。
由于目前大模型使用 token 的限制,我们需要对读取的文本进行切分,将较长的文本切分为较小的文本,这时一段文本就是一个单位的知识。
3)将文档词向量化
使用文本嵌入(Embeddings)对分割后的文档进行向量化,使语义相似的文本片段具有接近的向量表示。然后,存入向量数据库,这个流程正是创建 索引(index)
的过程。
向量数据库对各文档片段进行索引,支持快速检索。这样,当用户提出问题时,可以先将问题转换为向量,在数据库中快速找到语义最相关的文档片段。然后将这些文档片段与问题一起传递给语言模型,生成回答
4)将向量化后的文档导入Chroma知识库,建立知识库索引
Langchain 集成了超过 30 个不同的向量存储库。我们选择 Chroma 向量库是因为它轻量级且数据存储在内存中,这使得它非常容易启动和开始使用。
将用户知识库内容经过 embedding 存入向量知识库,然后用户每一次提问也会经过 embedding,利用向量相关性算法(例如余弦算法)找到最匹配的几个知识库片段,将这些知识库片段作为上下文,与用户问题一起作为 prompt 提交给 LLM 回答。
2.3、大模型集成与API连接
- 集成GPT、星火、文心、GLM 等大模型,配置 API 连接。
- 编写代码,实现与大模型 API 的交互,以便获取问题答案。
2.4、核心功能实现
- 构建 Prompt Engineering,实现大模型回答功能,根据用户提问和知识库内容生成回答。
- 实现流式回复,允许用户进行多轮对话。
- 添加历史对话记录功能,保存用户与助手的交互历史。
2.5、核心功能迭代优化
- 进行验证评估,收集 Bad Case。
- 根据 Bad Case 迭代优化核心功能实现。
2.6、前端与用户交互界面开发
- 使用 Gradio 和 Streamlit 搭建前端界面。
- 实现用户上传文档、创建知识库的功能。
- 设计用户界面,包括问题输入、知识库选择、历史记录展示等。
2.7、部署测试与上线
- 部署问答助手到服务器或云平台,确保可在互联网上访问。
- 进行生产环境测试,确保系统稳定。
- 上线并向用户发布。
2.8、维护与持续改进
- 监测系统性能和用户反馈,及时处理问题。
- 定期更新知识库,添加新的文档和信息。
- 收集用户需求,进行系统改进和功能扩展。
整个流程将确保项目从规划、开发、测试到上线和维护都能够顺利进行,为用户提供高质量的基于个人知识库的问答助手。
3、LLM项目开发架构
3.1、整体架构
经过上文分析,本项目为搭建一个基于大模型的个人知识库助手,基于 LangChain 框架搭建,核心技术包括 LLM API 调用、向量数据库、检索问答链等。项目整体架构如下:
图来源于:https://github.com/datawhalechina/llm-universe/blob/main/figures/structure.jpg
如上,本项目从底向上依次分为 LLM 层、数据层、数据库层、应用层与服务层:
① LLM 层主要基于四种流行 LLM API 进行了 LLM 调用封装,支持用户以统一的入口、方式来访问不同的模型,支持随时进行模型的切换;
② 数据层 主要包括个人知识库的源数据以及 Embedding API,源数据经过 Embedding 处理可以被向量数据库使用;
③ 数据库层 主要为基于个人知识库源数据搭建的向量数据库,在本项目中我们选择了 Chroma;
④ 应用层 为核心功能的最顶层封装,我们基于 LangChain 提供的检索问答链基类进行了进一步封装,从而支持不同模型切换以及便捷实现基于数据库的检索问答;
⑤ 最顶层为服务层,我们分别实现了 Gradio 搭建 Demo 与 FastAPI 组建 API 两种方式来支持本项目的服务访问。
3.2、代码结构
本项目的完整代码存放于 project 目录下,实现了项目的全部功能及封装,代码结构如下:
1 |
|
3.3、项目逻辑
- 用户:可以通过
run_gradio
或者run_api
启动整个服务; - 服务层调用 qa_chain.py 或 chat_qa_chain 实例化对话检索链对象,实现全部核心功能;
- 服务层和应用层都可以调用、切换 prompt_template.py 中的 prompt 模板来实现 prompt 的迭代;
- 也可以直接调用 call_llm 中的 get_completion 函数来实现不使用数据库的 LLM;
- 应用层调用已存在的数据库和 llm 中的自定义 LLM 来构建检索链;
- 如果数据库不存在,应用层调用 create_db.py 创建数据库,该脚本可以使用 openai embedding 也可以使用 embedding.py 中的自定义 embedding
3.4、 各层简析
1)LLM 层
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 层封装就是上一章讲解的代码实践。
2)数据层
数据层主要包括个人知识库的源数据(包括 pdf、txt、md 等)和 Embedding 对象。源数据需要经过 Embedding 处理才能进入向量数据库,我们在数据层自定义了智谱提供的 Embedding API 的封装,支持上层以统一方式调用智谱 Embedding 或 OpenAI Embedding。
在上一章,我们也已详细介绍了 Embdding API 的调用及封装方式。
3)数据库层
数据库层主要存放了向量数据库文件。同时,我们在该层实现了源数据处理、创建向量数据库的方法。
我们将在第四章详细介绍向量数据库、源数据处理方法以及构建向量数据库的具体实现。
4)应用层
应用层封装了整个项目的全部核心功能。我们基于 LangChain 提供的检索问答链,在 LLM 层、数据库层的基础上,实现了本项目检索问答链的封装。自定义的检索问答链除具备基本的检索问答功能外,也支持通过 model 参数来灵活切换使用的 LLM。我们实现了两个检索问答链,分别是有历史记录的 Chat_QA_Chain 和没有历史记录的 QA_Chain。
我们将在第五章讲解 Prompt 的构造与检索问答链的构建细节。
5)服务层
服务层主要是基于应用层的核心功能封装,实现了 Demo 的搭建或 API 的封装。在本项目中,我们分别实现了通过 Gradio 搭建前端界面与 FastAPI 进行封装,支持多样化的项目调用。
我们将在第六章详细介绍如何使用 Gradio 以及 FastAPI 来实现服务层的设计。