一、LangChain介绍
- 背景
一个应用程序通常需要多次对大语言模型写提示并对它输出的结果进行解析。因此,需要写很多胶水代码。而LangChain的目的使这个开发过程变得更容易。 - 目的
LangChain是一个构建大语言模型应用的开源框架。当有人在开发LLM的复杂应用,作者发现了可以从中提取一些公共的抽象,于是LangChain诞生。 - 内容
- 包含两个包,一个Python,一个JS(TS)。
- LangChain注重组合和模块化。LangChain一个很有价值的地方是有很多可以单独使用或与其他组件结合使用的独立组件。
- 有许多不同的用例,可以将这些模块化组件链式组合成更完整的应用程序,并且非常容易上手。
二、Model、prompt和parsers
Model:基础的语言模型
Prompt:给模型传递信息的一种方式
Parsers:将模型的输出解析成更结构化的格式
三、在CHATGLM2-6B上测试LangChain
- 本地部署CHATGLM2-6B
git clone https://github.com/THUDM/ChatGLM2-6B
cd ChatGLM2-6B
pip install -r requirements.txt
- 运行CHATGLM2-6B网页Demo
streamlit run web_demo2.py
- 使用LangChain测试CHATGLM2-6B
- 本地运行CHATGLM2-6B
python api.py
- 安装LangChain
pip install langchain
- 通过LangChain运行CHATGLM2-6B
from langchain_community.llms import ChatGLM
from langchain_core.messages import HumanMessage, SystemMessage
# LangChain的端口
endpoint_url = (
"http://127.0.0.1:8000"
)
# 这个接口在 https://python.langchain.com/v0.2/api_reference/community/llms.html,同样道理,可以使用其他大语言模型。
model = ChatGLM(
endpoint_url=endpoint_url
)
messages = [
SystemMessage(content="Translate the following from English into Chinese"),
HumanMessage(content="hi!"),
]
model.invoke(messages) # 输出 '人:嗨!'
-
四、 记忆
通常来说,语言模型不会记住和你对话之间的历史消息,每个请求交互,每次调用API都是独立的。聊天机器人之所以看起来好像是有记忆的,是因为借助代码的帮助,提供历史信息作为和LLM对话的上下文。常见三种存储方式:
- 存储若干对话
ConversationBufferMemory - 限制若干令牌
ConversationBufferWindowMemory - 针对历史信息生成摘要
ConversationSummaryMemory
五、链
链通常将LLM与提示词结合在一起,有个这个构建模块,还可以将一堆这样的构建模块组合在一起,对文本或其他数据按顺序进行操作。
-
Simple Sequential Chain
-
Sequential Chain
-
Router Chain
如果有多条子链,每条子链专门负责处理某种特定类型的输入,这种情况下可以使用路由链。
首先判断该使用哪条子链,然后将输入传递到相应的子链。
六、文档问答系统
给定一段可能从PDF文件、网页或某公司的内部文档库中提取的文本,使用LLM回答关于这些文档内容的问题,帮助用户深入了解并获取他们需要的信息。
- 底层
使用语言模型,并且与大量文档结合时,存在一个关键问题:语言模型一次只能接收几千个单词。那么,如果我们有一个很大的文档,如何让语言模型对文档所有内容进行问答?Embedding和向量存储(成为stuff)。- Embedding
Embedding将一段文本转换成数字,用一组数字表示这段文本。这组数字捕获了它所代表的文字片段的内容含义。通过观察不同文本数值空间中的表示, 利用向量相似度可以轻松找出哪些文本段相似,从而从文档中找到与问题相似的文本片段,一起传递给大模型帮助回答问题。 - 向量数据库
一种存储方式,可以存储前面创建的矢量数字数组。往向量数据库中新建数据的方式,就是将文档拆分成块,每块生成Embedding,然后把Embedding和原始块一起存储到数据库中。当我们处理大一点的文档时,首先要将其拆分为较小的文本块,只用把最相关的几块内容传递给LLM。然后,把每个文本块生成一个Embedding,然后将这些Embedding存储到向量数据库中,形成索引。当索引创建后,我们可以用它来找到与查询内容最相关的几个文本片段(类似于量化)。拿到文本块后,将这些文本块与原始的查询内容一起传递给语言模型,这样可以让语言模型根据检索出来的文档生成最终答案。 - 其他方式
- Map_readuce
基本对所有的分块,把每一块的内容连同问题一起传递给语言模型,得到一个独立返回结果。然后每一块得到的结果都合并在一起,再使用语言模型对这些结果进行总结,得到最终答案。 - Refine
和Map_readuce,不过是迭代的,依赖前一个快的结果,根据第一个分块依次迭代到最后分块。这种对于随着时间推移构建答案非常有用。 - Map_rerank
对于每个文档,只需对语言模型进行一次调用。另外还需要让他返回一个评分,然后选择最高分的结果。这依赖于语言模型知道分数应该是多少。所以你需要告诉给它指令,“嘿,如果与文档相关,分数应该很高”,并且需要具体优化那部分指令。
- Map_readuce
- Embedding
七、评估
当使用LLM构建复杂应用程序时,一个重要且棘手的问题是,如何评估应用程序的表现?是否达到了某种验收标准?此外,如果你决定换一种实现方式,可能换到不同的LLM,或者更改如何使用向量数据库的策略,或者使用其他方式检索数据。或者改变系统中的其他参数。怎么评估效果比之前好了,还是坏了?
这些应用系统实际上是许多不同步骤的链和序列,我们的首要任务是,了解每个步骤的输入和输出到底是什么?往往当返回错误的结果时,不一定是语言模型本身出了问题,实际上,可能是在检索的步骤出了问题。
- langchain.debug
显示模块在链操作时针对输入和输出的思考过程 - 可视化模型思考工具
八、代理
有时候人们把大语言模型看作是知识库,就好像它学会并记忆了大量从互联网获取的信息。但Wu认为更有用的方式是把大语言模型看作是一个推理引擎,LLM可能会使用它从互联网上学习的这些背景知识,也利用你提供给的新信息来帮助你回答问题,或者推理内容,甚至决定接下来要做什么。这就是LangChain Agent帮忙要做的事。比如内置在LangChain中的搜索引擎;如何创建自己的工具,让代理与任何数据存储、API或者功能进行互动。
Tip:temperature要设置为0,因为我们将使用语言模型作为代理的推理引擎,连接到其他的数据,需要使得推理尽可能准确,将emperature设置为0,以消除任何可能出现的随机性。
- AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION
一种Agent属性,其中CHAT是一个专为与CHAT模型一起工作而优化的代理。其次是REACT,是一种组织Prompt的技术,能最大化语言模型的推理能力。 - handing_parsing_erros
一种Agenet属性,当语言模型输出的内容无法被正常解析时,将格式错误的内容传回语言模型,并要求它自行纠正。 - verbose
一种Agent属性,详细打印每个步骤的记录。
九、RAG(Retrieval-Augmented Generation )
在利用大语言模型回答问题之前,先从外部知识库检索相关信息。主要分为两部分,检索和生成。
- 检索
首先将查询内容query向量化,从数据库中检索与输入矢量相关性高的部分内容context。 - 生成
查询和检索到的上下文都被注入到发送给LLM的提示中,生成最终结果。
十、RAG和微调(finetune )的优缺点
RAG提供类似教科书的信息检索功能,适用于解答具体问题或执行特定信息检索任务。
- 优点
- 提高答案准确性,减少虚假信息。
- 识别最新信息,保持回答的及时性和准确性。
- 高度透明,增强用户对输出结果的信任。
- 缺点
- 不适合教授模型理解广泛领域或学习新的语言、格式或风格。
微调通过广泛学习吸收知识,适用于模型需要模仿特定结构、风格或格式时。
- 优点
- 强化模型已有知识。
- 调整或定制模型输出。
- 适用于复杂指令的训练,提升交互效率。
- 缺点
不适合向模型中添加新知识,或应对需要快速迭代新场景的情况。