虽然原型阶段可能运行良好,但在更真实、更广泛的应用场景中,它们往往容易出错。 为什么它们会出错? 当智能体出错时,是因为其内部调用的大语言模型(LLM)出错了。
而大语言模型出错,通常源于以下两个原因之一:
- 底层大语言模型本身能力不足;
- 未向大语言模型提供“正确”的上下文。
这是AI工程师(或任何从事AI系统开发的人)的首要工作。
缺乏“正确”上下文,是阻碍智能体更可靠的最大瓶颈。因此,LangChain的智能体抽象层专门为此设计,以促进上下文工程的实现。
核心智能体循环
要理解应在何处访问或更新上下文,首先需理解核心智能体循环。 核心智能体循环非常简单:- 获取用户输入;
- 调用大语言模型,要求其直接响应或调用工具;
- 若决定调用工具,则执行这些工具;
- 重复步骤2和3,直到决定结束。
但最终起决定性作用的,是实际传递给大语言模型的上下文——即最终的提示词(或消息列表)以及它可访问的工具集合。
模型选择
你所使用的模型(包括具体模型参数)是智能体循环中的关键组成部分,它驱动整个智能体的推理逻辑。 智能体出错的一个原因,可能是你所选模型本身能力不足。为了构建可靠的智能体,你需要能够访问所有可能的模型。
LangChain通过标准化的模型接口支持这一点——我们已集成超过50种不同的模型提供商。 模型选择也与上下文工程密切相关,体现在两个方面: 第一,你向大语言模型传递上下文的方式,可能取决于你所使用的具体模型。
有些模型提供商更擅长处理JSON,有些则更擅长XML。
你所做的上下文工程可能需针对特定模型进行调整。 第二,在智能体循环中选择合适的模型,也可能取决于你希望传递给它的上下文。
一个显而易见的例子是:不同模型具有不同的上下文窗口大小。
如果智能体中的上下文不断累积,你可能希望在上下文较小时使用某个模型提供商,而当上下文超出该模型的窗口限制时,再切换到另一个支持更大上下文的模型。
上下文类型
有几种不同类型的上下文可用于构建最终传递给大语言模型的完整上下文: 指令(Instructions):开发者提供的基础指令,通常称为系统提示词(system prompt)。可以是静态的,也可以是动态生成的。 工具(Tools):智能体可访问的工具集合。
这些工具的名称、描述和参数,与提示词文本同等重要。 结构化输出(Structured output):智能体应以何种格式响应。
输出格式的名称、描述和参数,与提示词文本同等重要。 会话上下文(Session context):我们在文档中也称之为“短期记忆”。
在对话场景中,最直观的理解就是组成对话的消息列表。
但通常也可能包含其他更结构化的信息,你希望智能体在会话过程中能够读取或更新。
智能体可读写此类上下文,且通常会直接将其纳入传递给大语言模型的上下文中。
示例包括:消息、文件等。 长期记忆(Long term memory):应跨会话(对话)持久保存的信息。
示例包括:提取的用户偏好等。 运行时配置上下文(Runtime configuration context):这不是智能体的“状态”或“记忆”,而是特定智能体运行时的配置信息。
它不会被智能体修改,通常也不会传递给大语言模型,而是用于引导智能体行为或查找其他上下文。
示例包括:用户ID、数据库连接等。
为支持上下文工程,我们的智能体需具备的功能
现在我们已理解了基本的智能体循环、模型选择的重要性,以及存在的不同上下文类型。那么,我们的智能体需要支持哪些功能?LangChain的智能体又是如何支持这些功能的?
自定义系统提示词
你可以使用prompt 参数 传入一个函数,该函数返回字符串作为系统提示词。
使用场景:
- 利用会话上下文、长期记忆或运行时上下文中的信息,个性化系统提示词。
在调用模型前,显式控制“消息生成”
你可以使用prompt 参数 传入一个函数,该函数返回消息列表。
使用场景:
- 动态在发送的消息末尾追加额外的系统消息,以强化指令,而不修改状态。
在“消息生成”/自定义系统提示词中访问运行时配置
你可以使用prompt 参数 传入一个函数,该函数返回消息列表或自定义系统提示词。通过调用
get_runtime 可访问运行时配置。
使用场景:
- 利用传入的
user_id查询用户资料,并将其放入系统提示词中。
在“消息生成”/自定义系统提示词中访问会话上下文
你可以使用prompt 参数 传入一个函数,该函数返回消息列表或自定义系统提示词。会话上下文通过
state 参数 传入。
使用场景:
- 在系统提示词中使用用户在运行时传入的更结构化信息(如偏好设置)。
在“消息生成”/自定义系统提示词中访问长期记忆
你可以使用prompt 参数 传入一个函数,该函数返回消息列表或自定义系统提示词。通过调用
get_store 可访问长期记忆。
使用场景:
- 从长期记忆中查询用户偏好,并将其放入系统提示词中。
在模型调用前更新会话上下文
你可以使用 pre_model_hook 更新状态。 使用场景:- 若消息列表过长,则过滤部分消息,将过滤后的列表保存至状态中并仅使用该列表;
- 每N条消息后生成对话摘要,并保存至状态中。
在工具中访问运行时配置
你可以在工具中调用get_runtime 来访问运行时配置。
使用场景:
- 在工具调用中使用
user_id查询相关信息。
在工具中访问会话上下文
你可以在工具中通过 InjectedState 参数访问会话上下文。 使用场景:- 将状态中的消息传递给子智能体。
在工具中访问长期记忆
你可以在工具中调用get_store 来访问长期记忆。
使用场景:
- 从长期记忆存储中查询记忆信息。
在工具中更新会话上下文
你可以从工具中返回包含状态更新的 Command,以更新会话上下文。 使用场景:- 使用工具更新“虚拟文件系统”。
在工具中更新长期记忆
你可以在工具中调用get_store 访问长期记忆,然后在工具内部更新它。
使用场景:
- 使用工具更新存储在长期记忆中的用户偏好。
在模型调用前更新工具
你可以向model 参数 传入一个函数,该函数附加自定义工具。
使用场景:
- 强制智能体首先调用某个特定工具;
- 仅在智能体调用某些工具后,才赋予其访问其他工具的权限;
- 在N次迭代后移除工具访问权限(强制智能体直接响应)。
在模型调用前更新所用模型
你可以向model 参数 传入一个函数,该函数返回自定义模型。
使用场景:
- 当消息历史变长时,切换为支持更长上下文窗口的模型;
- 当原始模型陷入困境时,切换为更智能的模型。