学习基于LangChain实战课(掘金小册) | 青训营X豆包MarsCode AI 刷题 - AI方向
主题:【第二部分】基础篇 Ⅱ
类型:实践记录/学习体验/学习方法
Langchain实战第三课 _ 豆包MarsCode AI 刷题
学习目录路径:图源小册介绍
基础篇
4模型I/O:输入提示、调用模型、解析输出学习时长: 20分3秒
5提示工程(上):用少样本FewShotTemplate和ExampleSelector创建应景文案学习时长: 24分9秒
6提示工程(下):用思维链和思维树提升模型思考质量学习时长: 15分10秒
7调用模型:使用OpenAI API还是微调开源Llama2/ChatGLM?学习时长: 22分54秒
8输出解析:用OutputParser生成鲜花推荐列表学习时长: 26分8秒
9链(上):写一篇完美鲜花推文?用SequencialChain链接不同的组件学习时长: 15分2秒
10链(下):想学“育花”还是“插花”?用RouterChain确定客户意图学习时长: 17分49秒
11记忆:通过Memory记住客户上次买花时的对话细节学习时长: 19分6秒
12代理(上):ReAct框架,推理与行动的协同学习时长: 12分10秒
13代理(中):AgentExecutor究竟是怎样驱动模型和工具完成任务的?学习时长: 33分37秒
14代理(下):结构化工具对话、Self-Ask with Search以及Plan and execute代理学习时长: 20分9秒
LangChain实战基础:链、记忆与代理的深度探索
一、前言
本文将深入探讨LangChain中的三个重要主题:链(Chain)、记忆(Memory)和代理(Agent)。通过对这三个主题的介绍和实战案例分析,帮助读者更好地理解和应用LangChain来构建强大的语言模型应用程序。
LangChain是一个强大的框架,用于构建可以与大规模语言模型(LLMs)交互的应用程序。它提供了多种工具和功能,帮助开发者将语言模型集成到实际应用中,如聊天机器人、问答系统、内容生成、数据分析等。
链(Chain)
链是LangChain的核心组件,用于将多个处理步骤链接在一起。每个步骤可以是对语言模型的调用、数据处理、外部 API 调用等。链的设计使得处理流程清晰且易于管理和扩展。
什么是 Chain
在开发更复杂的应用程序时,需要通过“Chain”来链接LangChain的各个组件和功能。“Chain”可以将多个组件相互链接,组合成一个链,简化复杂应用程序的实现,并使之更加模块化,能够创建出单一的、连贯的应用程序,从而使调试、维护和改进应用程序变得容易。
例如,LLM链(LLMChain)能够接受用户输入,使用 PromptTemplate 对其进行格式化,然后将格式化的响应传递给 LLM。这就相当于把整个Model I/O的流程封装到链里面。
LLMChain:最简单的链
LLMChain围绕着语言模型推理功能又添加了一些功能,整合了PromptTemplate、语言模型(LLM或聊天模型)和 Output Parser,相当于把Model I/O放在一个链中整体操作。它使用提示模板格式化输入,将格式化的字符串传递给 LLM,并返回 LLM 输出。
如果不使用链,代码如下:
#----第一步 创建提示
# 导入LangChain中的提示模板
from langchain import PromptTemplate
# 原始字符串模板
template = "{flower}的花语是?"
# 创建LangChain模板
prompt_temp = PromptTemplate.from_template(template)
# 根据模板创建提示
prompt = prompt_temp.format(flower='玫瑰')
# 打印提示的内容
print(prompt)
#----第二步 创建并调用模型
# 导入LangChain中的OpenAI模型接口
from langchain import OpenAI
# 创建模型实例
model = OpenAI(temperature=0)
# 传入提示,调用模型,返回结果
result = model(prompt)
print(result)
输出:
玫瑰的花语是?
爱情、浪漫、美丽、永恒、誓言、坚贞不渝。
如果使用链,代码结构则显得更简洁。
# 导入所需的库
from langchain import PromptTemplate, OpenAI, LLMChain
# 原始字符串模板
template = "{flower}的花语是?"
# 创建模型实例
llm = OpenAI(temperature=0)
# 创建LLMChain
llm_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(template))
# 调用LLMChain,返回结果
result = llm_chain("玫瑰")
print(result)
输出:
{'flower': '玫瑰', 'text': '\n\n爱情、浪漫、美丽、永恒、誓言、坚贞不渝。'}
在这里,我们就把提示模板的构建和模型的调用封装在一起了。
链的调用方式
链有很多种调用方式。
- 直接调用:当我们像函数一样调用一个对象时,它实际上会调用该对象内部实现的__call__方法。如果你的提示模板中包含多个变量,在调用链的时候,可以使用字典一次性输入它们。
prompt = PromptTemplate(
input_variables=["flower", "season"],
template="{flower}在{season}的花语是?")
llm_chain = LLMChain(llm=llm, prompt=prompt)
print(llm_chain({
'flower': "玫瑰",
'season': "夏季"}))
输出:
{'flower': '玫瑰', 'season': '夏季', 'text': '\n\n玫瑰在夏季的花语是爱的誓言,热情,美丽,坚定的爱情。'}
- 通过run方法:通过run方法,也等价于直接调用_call_函数。
语句:
llm_chain("玫瑰")
等价于:
llm_chain.run("玫瑰")
- 通过predict方法:predict方法类似于run,只是输入键被指定为关键字参数而不是 Python 字典。
result = llm_chain.predict(flower="玫瑰")
print(result)
- 通过apply方法:apply方法允许我们针对输入列表运行链,一次处理多个输入。
示例如下:
# apply允许您针对输入列表运行链
input_list = [
{"flower": "玫瑰",'season': "夏季"},
{"flower": "百合",'season': "春季"},
{"flower": "郁金香",'season': "秋季"}
result = llm_chain.apply(input_list)
print(result)
输出:
'''[{'text': '\n\n玫瑰在夏季的花语是“恋爱”、“热情”和“浪漫”。'},
{'text': '\n\n百合在春季的花语是“爱情”和“友谊”。'},
{'text': '\n\n郁金香在秋季的花语表达的是“热情”、“思念”、“爱恋”、“回忆”和“持久的爱”。'}]'''
- 通过generate方法:generate方法类似于apply,只不过它返回一个LLMResult对象,而不是字符串。LLMResult通常包含模型生成文本过程中的一些相关信息,例如令牌数量、模型名称等。
result = llm_chain.generate(input_list)
print(result)
输出:
generations=[[Generation(text='\n\n玫瑰在夏季的花语是“热情”、“爱情”和“幸福”。',
generation_info={'finish_reason': 'stop', 'logprobs': None})],
[Generation(text='\n\n春季的花语是爱情、幸福、美满、坚贞不渝。',
generation_info={'finish_reason': 'stop', 'logprobs': None})],
[Generation(text='\n\n秋季的花语是“思念”。银色的百合象征着“真爱”,而淡紫色的郁金香则象征着“思念”,因为它们在秋天里绽放的时候,犹如在思念着夏天的温暖。',
generation_info={'finish_reason': 'stop', 'logprobs': None})]]
llm_output={'token_usage': {'completion_tokens': 243, 'total_tokens': 301, 'prompt_tokens': 58}, 'model_name': 'gpt-3.5-turbo-instruct'}
run=[RunInfo(run_id=UUID('13058cca-881d-4b76-b0cf-0f9c831af6c4')),
RunInfo(run_id=UUID('7f38e33e-bab5-4d03-b77c-f50cd195affb')),
RunInfo(run_id=UUID('7a1e45fd-77ee-4133-aab0-431147186db8'))]
Sequential Chain:顺序链
好,到这里,你已经掌握了最基本的LLMChain的用法。下面,我要带着你用Sequential Chain 把几个LLMChain串起来,形成一个顺序链。
这个示例中,我们的目标是这样的:
第一步,我们假设大模型是一个植物学家,让他给出某种特定鲜花的知识和介绍。
第二步,我们假设大模型是一个鲜花评论者,让他参考上面植物学家的文字输出,对鲜花进行评论。
第三步,我们假设大模型是易速鲜花的社交媒体运营经理,让他参考上面植物学家和鲜花评论者的文字输出,来写一篇鲜花运营文案。
下面我们就来一步步地实现这个示例。
首先,导入所有需要的库。
# 设置OpenAI API密钥
import os
os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains import SequentialChain
然后,添加第一个LLMChain,生成鲜花的知识性说明。
# 这是第一个LLMChain,用于生成鲜花的介绍,输入为花的名称和种类
llm = OpenAI(temperature=.7)
template = """
你是一个植物学家。给定花的名称和类型,你需要为这种花写一个200字左右的介绍。
花名: {name}
颜色: {color}
植物学家: 这是关于上述花的介绍:"""
prompt_template = PromptTemplate(input_variables=["name", "color"], template=template)
introduction_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="introduction")
接着,添加第二个LLMChain,根据鲜花的知识性说明生成评论。
# 这是第二个LLMChain,用于根据鲜花的介绍写出鲜花的评论
llm = OpenAI(temperature=.7)
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。
鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate(input_variables=["introduction"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")
接着,添加第三个LLMChain,根据鲜花的介绍和评论写出一篇自媒体的文案。
# 这是第三个LLMChain,用于根据鲜花的介绍和评论写出一篇自媒体的文案
template = """
你是一家花店的社交媒体经理。给定一种花的介绍和评论,你需要为这种花写一篇社交媒体的帖子,300字左右。
鲜花介绍:
{introduction}
花评人对上述花的评论:
{review}
社交媒体帖子:
"""
prompt_template = PromptTemplate(input_variables=["introduction", "review"], template=template)
social_post_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="social_post_text")
最后,添加SequentialChain,把前面三个链串起来。
# 这是总的链,我们按顺序运行这三个链
overall_chain = SequentialChain(
chains=[introduction_chain, review_chain, social_post_chain],
input_variables=["name", "color"],
output_variables=["introduction","review","social_post_text"],
verbose=True)
# 运行链,并打印结果
result = overall_chain({"name":"玫瑰", "color": "黑色"})
print(result)
最终的输出如下:
Entering new chain...
Finished chain.
{'name': '玫瑰', 'color': '黑色',
'introduction': '\n\n黑色玫瑰,这是一种对传统玫瑰花的独特颠覆,它的出现挑战了我们对玫瑰颜色的固有认知。它的花瓣如煤炭般黑亮,反射出独特的微光,而花蕊则是金黄色的,宛如夜空中的一颗星,强烈的颜色对比营造出一种前所未有的视觉效果。在植物学中,黑色玫瑰的出现无疑提供了一种新的研究方向,对于我们理解花朵色彩形成的机制有着重要的科学价值。',
'review': '\n\n黑色玫瑰,这不仅仅是一种花朵,更是一种完全颠覆传统的艺术表现形式。黑色的花瓣仿佛在诉说一种不可言喻的悲伤与神秘,而黄色的蕊瓣犹如漆黑夜空中的一抹亮色,给人带来无尽的想象。它将悲伤与欢乐,神秘与明亮完美地结合在一起,这是一种全新的视觉享受,也是一种对生活理解的深度表达。',
'social_post_text': '\n欢迎来到我们的自媒体平台,今天,我们要向您展示的是我们的全新产品——黑色玫瑰。这不仅仅是一种花,这是一种对传统观念的挑战,一种视觉艺术的革新,更是一种生活态度的象征。\n这种别样的玫瑰花,其黑色花瓣宛如漆黑夜空中闪烁的繁星,富有神秘的深度感,给人一种前所未有的视觉冲击力。这种黑色,它不是冷酷、不是绝望,而是充满着独特的魅力和力量。而位于黑色花瓣之中的金黄色花蕊,则犹如星星中的灵魂,默默闪烁,给人带来无尽的遐想,充满活力与生机。\n黑色玫瑰的存在,不仅挑战了我们对于玫瑰传统颜色的认知,它更是一种生动的生命象征,象征着那些坚韧、独特、勇敢面对生活的人们。黑色的花瓣中透露出一种坚韧的力量,而金黄的花蕊则是生活中的希望,二者的结合恰好象征了生活中的喜怒哀乐,体现了人生的百态。'}
至此,我们就通过两个LLM链和一个顺序链,生成了一篇完美的文案。
记忆(Memory)
在默认情况下,无论是LLM还是代理都是无状态的,每次模型的调用都是独立于其他交互的。也就是说,我们每次通过API开始和大语言模型展开一次新的对话,它都不知道你其实昨天或者前天曾经和它聊过天了。
而ChatGPT之所以能够记得你之前说过的话,正是因为它使用了记忆(Memory)机制,记录了之前的对话上下文,并且把这个上下文作为提示的一部分,在最新的调用中传递给了模型。在聊天机器人的构建中,记忆机制非常重要。
使用ConversationChain
这个Chain最主要的特点是,它提供了包含AI 前缀和人类前缀的对话摘要格式,这个对话格式和记忆机制结合得非常紧密。
示例如下:
from langchain import OpenAI
from langchain.chains import ConversationChain
# 初始化大语言模型
llm = OpenAI(
temperature=0.5,
model_name="gpt-3.5-turbo-instruct"
# 初始化对话链
conv_chain = ConversationChain(llm=llm)
# 打印对话的模板
print(conv_chain.prompt.template)
输出:
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
{history}
Human: {input}
AI:
这里的提示为人类(我们)和人工智能(text-davinci-003)之间的对话设置了一个基本对话框架:这是人类和 AI 之间的友好对话。AI 非常健谈并从其上下文中提供了大量的具体细节。 (The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. )
同时,这个提示试图通过说明以下内容来减少幻觉,也就是尽量减少模型编造的信息:
“如果 AI 不知道问题的答案,它就会如实说它不知道。”(If the AI does not know the answer to a question, it truthfully says it does not know.)
之后,我们看到两个参数 {history} 和 {input}。
{history} 是存储会话记忆的地方,也就是人类和人工智能之间对话历史的信息。
{input} 是新输入的地方,你可以把它看成是和ChatGPT对话时,文本框中的输入。
使用ConversationBufferMemory
在LangChain中,通过ConversationBufferMemory(缓冲记忆)可以实现最简单的记忆机制。
示例如下:
from langchain import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory
# 初始化大语言模型
llm = OpenAI(
temperature=0.5,
model_name="gpt-3.5-turbo-instruct")
# 初始化对话链
conversation = ConversationChain(
llm=llm,
memory=ConversationBufferMemory()
# 第一天的对话
# 回合1
conversation("我姐姐明天要过生日,我需要一束生日花束。")
print("第一次对话后的记忆:", conversation.memory.buffer)
# 回合2
conversation("她喜欢粉色玫瑰,颜色是粉色的。")
print("第二次对话后的记忆:", conversation.memory.buffer)
# 第二天的对话
# 回合3
conversation("我又来了,还记得我昨天为什么要来买花吗?")
print("/n第三次对话后时提示:/n",conversation.prompt.template)
print("/n第三次对话后的记忆:/n", conversation.memory.buffer)
有了记忆机制,LLM能够了解之前的对话内容,这样简单直接地存储所有内容为LLM提供了最大量的信息,但是新输入中也包含了更多的Token(所有的聊天历史记录),这意味着响应时间变慢和更高的成本。而且,当达到LLM的令牌数(上下文窗口)限制时,太长的对话无法被记住(对于text-davinci-003和gpt-3.5-turbo,每次的最大输入限制是4096个Token)。
使用ConversationBufferWindowMemory
ConversationBufferWindowMemory 是缓冲窗口记忆,它的思路就是只保存最新最近的几次人类和AI的互动。因此,它在之前的“缓冲记忆”基础上增加了一个窗口值 k。这意味着我们只保留一定数量的过去互动,然后“忘记”之前的互动。
示例如下:
from langchain import OpenAI
from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferWindowMemory
# 创建大语言模型实例
llm = OpenAI(
temperature=0.5,
model_name="gpt-3.5-turbo-instruct")
# 初始化对话链
conversation = ConversationChain(
llm=llm,
memory=ConversationBufferWindowMemory(k=1)
# 第一天的对话
# 回合1
result = conversation("我姐姐明天要过生日,我需要一束生日花束。")
print(result)
# 回合2
result = conversation("她喜欢粉色
## 二、链(Chain)
![need_search_image_by_title]()
### 3. Sequential Chain:顺序链
Sequential Chain可以把几个LLMChain串起来,形成一个顺序链。在一个示例中,我们通过Sequential Chain生成了一篇完美的文案,包括三个步骤:植物学家介绍鲜花、鲜花评论者评论鲜花和社交媒体运营经理写鲜花运营文案。
首先,我们导入所需的库,包括设置OpenAI API密钥、导入相关的模块和类。然后,依次添加三个LLMChain,分别用于生成鲜花的知识性说明、根据知识性说明生成评论以及根据介绍和评论写出自媒体文案。最后,添加SequentialChain,把这三个链串起来,并运行链,得到最终的输出结果。
具体实现步骤如下:
1. 导入库:
```python
# 设置OpenAI API密钥
import os
os.environ["OPENAI_API_KEY"] = '你的OpenAI API Key'
from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains import SequentialChain
- 添加第一个LLMChain,生成鲜花的知识性说明:
# 这是第一个LLMChain,用于生成鲜花的介绍,输入为花的名称和种类
llm = OpenAI(temperature=.7)
template = """
你是一个植物学家。给定花的名称和类型,你需要为这种花写一个200字左右的介绍。
花名: {name}
颜色: {color}
植物学家: 这是关于上述花的介绍:"""
prompt_template = PromptTemplate(input_variables=["name", "color"], template=template)
introduction_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="introduction")
- 添加第二个LLMChain,根据鲜花的知识性说明生成评论:
# 这是第二个LLMChain,用于根据鲜花的介绍写出鲜花的评论
llm = OpenAI(temperature=.7)
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。
鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate(input_variables=["introduction"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")
- 添加第三个LLMChain,根据鲜花的介绍和评论写出一篇自媒体的文案:
# 这是第三个LLMChain,用于根据鲜花的介绍和评论写出一篇自媒体的文案
template = """
你是一家花店的社交媒体经理。给定一种花的介绍和评论,你需要为这种花写一篇社交媒体的帖子,300字左右。
鲜花介绍:
{introduction}
花评人对上述花的评论:
{review}
社交媒体帖子:
"""
prompt_template = PromptTemplate(input_variables=["introduction", "review"], template=template)
social_post_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="social_post_text")
- 添加SequentialChain,把前面三个链串起来:
# 这是总的链,我们按顺序运行这三个链
overall_chain = SequentialChain(
chains=[introduction_chain, review_chain, social_post_chain],
input_variables=["name", "color"],
output_variables=["introduction","review","social_post_text"],
verbose=True)
# 运行链,并打印结果
result = overall_chain({"name":"玫瑰", "color": "黑色"})
print(result)
最终的输出如下:
Entering new chain...
Finished chain.
{'name': '玫瑰', 'color': '黑色',
'introduction': '\n\n黑色玫瑰,这是一种对传统玫瑰花的独特颠覆,它的出现挑战了我们对玫瑰颜色的固有认知。它的花瓣如煤炭般黑亮,反射出独特的微光,而花蕊则是金黄色的,宛如夜空中的一颗星,强烈的颜色对比营造出一种前所未有的视觉效果。在植物学中,黑色玫瑰的出现无疑提供了一种新的研究方向,对于我们理解花朵色彩形成的机制有着重要的科学价值。',
'review': '\n\n黑色玫瑰,这不仅仅是一种花朵,更是一种完全颠覆传统的艺术表现形式。黑色的花瓣仿佛在诉说一种不可言喻的悲伤与神秘,而黄色的蕊瓣犹如漆黑夜空中的一抹亮色,给人带来无尽的想象。它将悲伤与欢乐,神秘与明亮完美地结合在一起,这是一种全新的视觉享受,也是一种对生活理解的深度表达。',
'social_post_text': '\n欢迎来到我们的自媒体平台,今天,我们要向您展示的是我们的全新产品——黑色玫瑰。这不仅仅是一种花,这是一种对传统观念的挑战,一种视觉艺术的革新,更是一种生活态度的象征。\n这种别样的玫瑰花,其黑色花瓣宛如漆黑夜空中闪烁的繁星,富有神秘的深度感,给人一种前所未有的视觉冲击力。这种黑色,它不是冷酷、不是绝望,而是充满着独特的魅力和力量。而位于黑色花瓣之中的金黄色花蕊,则犹如星星中的灵魂,默默闪烁,给人带来无尽的遐想,充满活力与生机。\n黑色玫瑰的存在,不仅挑战了我们对于玫瑰传统颜色的认知,它更是一种生动的生命象征,象征着那些坚韧、独特、勇敢面对生活的人们。黑色的花瓣中透露出一种坚韧的力量,而金黄的花蕊则是生活中的希望,二者的结合恰好象征了生活中的喜怒哀乐,体现了人生的百态。'}
通过这个示例,我们可以看到Sequential Chain能够将多个LLMChain按照特定的顺序组合起来,实现复杂的自然语言处理任务,生成一篇连贯且富有逻辑的文案。
三、记忆(Memory)
1. 使用ConversationChain
ConversationChain最主要的特点是,它提供了包含AI前缀和人类前缀的对话摘要格式,这个对话格式和记忆机制结合得非常紧密。通过打印ConversationChain中的内置提示模板,可以看到模板为人类和人工智能之间的对话设置了基本框架,强调AI健谈且能从上下文中提供具体细节,同时如果不知道答案就如实说不知道。其中,{history}参数存储会话记忆,即对话历史信息,{input}是新输入的地方。这使得记忆机制能够将历史对话信息存储在提示模板中,并在新一轮对话中传递给模型,从而让LLM了解之前的对话内容。
2. 使用ConversationBufferMemory
通过ConversationBufferMemory可以实现最简单的记忆机制。在对话链中引入ConversationBufferMemory后,每次对话都会被记录下来并存储在内存中,作为后续对话的参考。例如,当用户说“我姐姐明天要过生日,我需要一束生日花束。”后,程序会记录下这句话,然后当用户再次输入新内容时,之前的对话历史会被作为提示模板的一部分传递给模型。这样,LLM就能够根据之前的对话内容进行更有针对性的回复。
3. 使用ConversationBufferWindowMemory
ConversationBufferWindowMemory是缓冲窗口记忆,只保存最新最近的几次人类和AI的互动。通过设置窗口值k,可以控制保存的互动数量,从而限制使用的Token数量。例如,设置k=1时,只保留上一次的人类回应和AI的回应。在实际应用中,如果只需要记住最近的互动,缓冲窗口记忆是一个很好的选择,但它会完全遗忘掉K轮之前的对话内容,对于需要混合远期和近期互动信息的场景不是最佳选择。
4. 使用ConversationSummaryMemory
ConversationSummaryMemory将对话历史进行汇总,然后再传递给{history}参数,以节约Token数量。此方法由另一个LLM驱动,对之前的对话进行汇总,适合长对话。但对于较短的对话可能会导致更高的Token使用,且对话历史的记忆完全依赖于中间汇总LLM的能力,还需要为汇总LLM使用Token,这增加了成本,且并不限制对话长度。
5. 使用ConversationSummaryBufferMemory
ConversationSummaryBufferMemory结合了上述各种记忆机制的特点,通过max_token_limit参数,在对话文字长度在300字之内的时候,记忆原始对话内容;当对话文字超出了这个参数的长度,就把所有超过预设长度的内容进行总结,以节省Token数量。例如,在对话过程中,如果前两轮的对话内容未超过300字,记忆机制会完整地记录对话内容,但当第三轮对话使总对话内容超出300字时,它会把早期的对话加以总结,以节省Token资源。这种记忆机制具有灵活性,既能回忆起较早的互动,又有缓冲区确保不会错过最近的互动信息,但对于较短的对话也会增加Token数量。
四、代理(Agent)
1. 代理的作用
代理在 LangChain 中就像一个多功能的接口,能够接触并使用一套工具。它根据用户的输入来决定调用哪些工具,不仅可以同时使用多种工具,还能将一个工具的输出数据作为另一个工具的输入数据。
在 LangChain 中使用代理,需要理解三个关键元素:
- 大模型:提供逻辑的引擎,负责生成预测和处理输入。
- 与之交互的外部工具:可能包括数据清洗工具、搜索引擎、应用程序等。
- 控制交互的代理:调用适当的外部工具,并管理整个交互过程的流程。
2. ReAct 框架
- ReAct 框架的灵感来自“行动”和“推理”之间的协同作用,引导模型生成一个任务解决轨迹:观察环境 - 进行思考 - 采取行动,也就是观察 - 思考 - 行动。具体来说,就是引导模型生成一个任务解决轨迹:观察环境,进行思考,采取行动。其中,Reasoning 包括了对当前环境和状态的观察,并生成推理轨迹,使模型能够诱导、跟踪和更新操作计划,甚至处理异常情况。Acting 在于指导大模型采取下一步的行动,如与外部源(如知识库或环境)进行交互并且收集信息,或者给出最终答案。
- LangChain 通过 Agent 类,将 ReAct 框架进行了完美封装和实现,赋予了大模型极大的自主性,使其从一个只能通过自己内部知识进行对话聊天的 Bot,飞升为了一个有手有脚能使用工具的智能代理。ReAct 框架会提示 LLMs 为任务生成推理轨迹和操作,这使得代理能系统地执行动态推理来创建、维护和调整操作计划,同时还支持与外部环境(例如 Google 搜索、Wikipedia)的交互,以将额外信息合并到推理中。
3. Agent 的关键组件
- 代理(Agent):这个类决定下一步执行什么操作。它由一个语言模型和一个提示(prompt)驱动。提示可能包含代理的性格、任务的背景以及用于激发更好推理能力的提示策略(例如 ReAct)。LangChain 中包含很多种不同类型的代理。
- 工具(Tools):工具是代理调用的函数。需要让代理能访问到正确的工具,并以最有帮助的方式描述这些工具。如果没有给代理提供正确的工具,它将无法完成任务。如果没有正确地描述工具,代理将不知道如何使用它们。LangChain 提供了一系列的工具,同时也可以定义自己的工具。
- 工具包(Toolkits):工具包是一组用于完成特定目标的彼此相关的工具,每个工具包中包含多个工具。比如 LangChain 的 Office365 工具包中就包含连接 Outlook、读取邮件列表、发送邮件等一系列工具。当然 LangChain 中还有很多其他工具包供使用。
- 代理执行器(AgentExecutor):代理的运行环境,调用代理并执行代理选择的操作。执行器也负责处理多种复杂情况,包括处理代理选择了不存在的工具的情况、处理工具出错的情况、处理代理产生的无法解析成工具调用的输出的情况,以及在代理决策和工具调用进行观察和日志记录。
4. 深挖 AgentExecutor 的运行机制
- 通过深入 LangChain 源代码的内部,揭示了 AgentExecutor 是如何通过计划和工具调用,一步一步完成 Thought、Action 和 Observation 的。
- 分析了代理每次给大模型的具体提示,以及代理执行器是如何按照 ReAct 框架来调用模型,接收模型的输出,并根据这个输出来调用工具,然后又根据工具的返回结果生成新的提示的。
具体来说,代理执行器的运行机制如下:
- 首先,代理执行器接收用户的输入问题,并将其传递给大模型。大模型根据提示信息,生成一个初步的思考和行动方案。
- 然后,代理执行器根据大模型的输出,解析出需要调用的工具和工具的输入参数。如果大模型返回的行动是搜索,代理执行器就会调用搜索工具,并将搜索结果作为观察结果返回给大模型。
- 大模型根据观察结果,进行进一步的思考和推理,生成新的行动方案。如果大模型认为任务已经完成,就会返回最终答案;如果任务尚未完成,就会继续调用工具,直到任务完成。
- 最后,代理执行器返回大模型的最终答案。
例如,在一个查询玫瑰花市场价格并计算加价后价格的任务中,代理执行器首先将问题传递给大模型。大模型根据提示信息,判断自己无法直接回答问题,需要调用搜索工具来获取玫瑰花的平均价格。代理执行器调用搜索工具,并将搜索结果返回给大模型。大模型根据搜索结果,计算出加价 15%后的价格,并返回最终答案。
五、总结
- 本文深入介绍了 LangChain 中的链、记忆和代理三个主题,为读者构建强大的语言模型应用程序提供了有力的指导。
链作为 LangChain 的核心组件,将多个处理步骤链接在一起,使复杂应用程序的实现更加模块化和易于管理。其中 LLMChain 围绕语言模型推理功能添加了功能,整合了 PromptTemplate、语言模型和 Output Parser,通过多种调用方式使代码结构更加简洁。Sequential Chain 则可以将多个 LLMChain 串起来,形成一个顺序链,实现复杂的自然语言处理任务。
记忆机制在聊天机器人的构建中非常重要,LangChain 提供了多种记忆机制,如 ConversationChain、ConversationBufferMemory、ConversationBufferWindowMemory、ConversationSummaryMemory 和 ConversationSummaryBufferMemory。这些记忆机制各有优缺点,在实际应用中需要根据具体情况选择合适的记忆机制。
代理在 LangChain 中就像一个多功能的接口,能够接触并使用一套工具。它由大模型、外部工具和控制交互的代理组成,通过 ReAct 框架赋予大模型极大的自主性,使其成为一个有手有脚能使用工具的智能代理。Agent 的关键组件包括代理、工具、工具包和代理执行器,它们共同协作完成任务。
- 同时,也指出了这三个主题在实际应用中的优缺点和注意事项。
链的优点是使处理流程清晰且易于管理和扩展,但在处理复杂任务时可能需要多个链的组合,增加了代码的复杂性。记忆机制的优点是能够让 LLM 了解之前的对话内容,提高回复的针对性,但可能会增加响应时间和成本,并且受到 LLM 的令牌数限制。代理的优点是能够根据用户的输入决定调用哪些工具,实现更复杂的任务,但需要正确地描述工具和提供正确的工具,否则代理将无法完成任务。
在实际应用中,需要根据具体情况选择合适的链、记忆机制和代理,以实现最佳的性能和效果。同时,也需要注意处理好响应时间、成本和令牌数限制等问题,以确保应用程序的稳定性和可靠性。