概述

构建智能体(或任何 LLM 应用)的难点在于使其足够可靠。虽然它们可能适用于原型,但在真实用例中常常失败。

为什么智能体会失败?

当智能体失败时,通常是因为智能体内部的 LLM 调用采取了错误的行动 / 没有达到我们的预期。LLM 失败有两个原因:
  1. 底层 LLM 能力不足
  2. “正确”的上下文没有传递给 LLM
很多时候,实际上是第二个原因导致了智能体不可靠。 上下文工程 是以正确的格式提供正确的信息和工具,以便 LLM 能够完成任务。这是 AI 工程师的首要工作。这种“正确”上下文的缺乏是阻碍构建更可靠智能体的首要因素,而 LangChain 的智能体抽象体在设计上专门用于促进上下文工程。
刚接触上下文工程?请从概念概述开始,了解不同类型的上下文及其适用场景。

智能体循环

典型的智能体循环包含两个主要步骤:
  1. 模型调用 - 使用提示词和可用工具调用 LLM,返回响应或执行工具的请求
  2. 工具执行 - 执行 LLM 请求的工具,返回工具结果
核心智能体循环图
这个循环会持续进行,直到 LLM 决定完成任务。

可控环节

要构建可靠的智能体,你需要控制智能体循环中每一步发生的事情,以及步骤之间发生的事情。

瞬时上下文

LLM 在单次调用中看到的内容。你可以修改消息、工具或提示,而不会改变保存在状态中的内容。

持久上下文

跨轮次保存在状态中的内容。生命周期钩子和工具写入会永久修改此内容。

数据源

在整个过程中,你的智能体会访问(读取/写入)不同的数据源:

工作原理

LangChain 中间件 是底层机制,使上下文工程对使用 LangChain 的开发者来说变得实用。 中间件允许你挂接到智能体生命周期中的任何步骤,并:
  • 更新上下文
  • 跳转到智能体生命周期中的不同步骤
在本指南中,你会看到频繁使用中间件 API 来实现上下文工程的目标。

模型上下文

控制每次模型调用的内容 - 指令、可用工具、使用哪个模型以及输出格式。这些决策直接影响可靠性和成本。

系统提示

开发者向 LLM 提供的基础指令。

消息

发送给 LLM 的完整消息列表(对话历史)。

工具

智能体可用于执行操作的实用程序。

模型

要调用的实际模型(包括配置)。

响应格式

模型最终响应的模式规范。
所有这些类型的模型上下文都可以来源于 状态(短期记忆)、存储(长期记忆)或 运行时上下文(静态配置)。

系统提示

系统提示设定 LLM 的行为和能力。不同的用户、上下文或对话阶段需要不同的指令。成功的智能体会利用记忆、偏好和配置来为当前对话状态提供正确的指令。
从状态中访问消息计数或对话上下文:

消息

消息构成了发送给 LLM 的提示。 管理消息的内容至关重要,以确保 LLM 拥有正确的信息来做出良好响应。
当与当前查询相关时,从状态注入上传文件的上下文:
瞬时与持久消息更新:上面的示例使用 wrap_model_call 进行瞬时更新 - 修改单次调用发送给模型的消息,而不改变保存在状态中的内容。对于会修改状态的持久更新,你可以:
  • 直接从 wrapModelCall 返回一个 Command,以从模型调用层注入状态更新。
  • 使用生命周期钩子,如 beforeModel、afterModel 或 wrapToolCall(用于工具返回)来更新对话历史。更多详情请参阅中间件文档。
更多信息请参阅状态更新。

工具

工具使模型能够与数据库、API 和外部系统交互。定义和选择工具的方式直接影响模型能否有效完成任务。

定义工具

每个工具都需要一个清晰的名称、描述、参数名称和参数描述。这些不仅仅是元数据 - 它们引导模型推理何时以及如何使用该工具。

选择工具

并非每个工具都适用于所有情况。工具过多可能会压垮模型(上下文过载)并增加错误;工具过少则会限制能力。动态工具选择会根据认证状态、用户权限、功能标志或对话阶段来调整可用工具集。
仅在某些对话里程碑后启用高级工具:
关于过滤预注册工具和在运行时注册工具(例如,从 MCP 服务器)的更多信息,请参阅动态工具。

模型

不同的模型有不同的优势、成本和上下文窗口。为手头的任务选择合适的模型,这可能在智能体运行过程中发生变化。
根据状态中的对话长度使用不同的模型:
更多示例请参阅动态模型。

响应格式

结构化输出将非结构化文本转换为经过验证的结构化数据。当需要提取特定字段或为下游系统返回数据时,自由格式的文本是不够的。 工作原理: 当你提供一个模式作为响应格式时,模型的最终响应将保证符合该模式。智能体运行模型/工具调用循环,直到模型完成工具调用,然后最终响应被强制转换为提供的格式。

定义格式

模式定义引导模型。字段名称、类型和描述精确指定了输出应遵循的格式。

选择格式

动态响应格式选择会根据用户偏好、对话阶段或角色来调整模式 - 在早期返回简单格式,随着复杂性增加而返回详细格式。
根据对话状态配置结构化输出:

工具上下文

工具的特殊之处在于它们既能读取也能写入上下文。 在最基本的情况下,当工具执行时,它接收 LLM 的请求参数并向模型返回一个工具消息。工具完成其工作并产生一个结果。 工具还可以为模型获取重要信息,使其能够执行并完成任务。

读取

大多数现实世界的工具不仅仅需要 LLM 的参数。它们需要用于数据库查询的用户 ID、用于外部服务的 API 密钥,或当前会话状态以做出决策。工具从状态、存储和运行时上下文中读取以访问这些信息。
从 State 读取以检查当前会话信息:

写入

工具结果可用于帮助智能体完成给定的任务。工具既可以将结果直接返回给模型,也可以更新智能体的记忆,以使重要上下文可用于后续步骤。
使用 Command 将数据写入 State 以跟踪会话特定信息:
有关在工具中访问状态、存储和运行时上下文的完整示例,请参阅工具。

生命周期上下文

控制在核心智能体步骤之间发生的事情——拦截数据流以实现横切关注点,如摘要、护栏和日志记录。 正如你在模型上下文和[工具上下文](#工具上下文中所见,中间件是使上下文工程实用的机制。中间件允许你钩入智能体生命周期的任何步骤,并可以:
  1. 更新上下文 - 修改状态和存储以持久化更改、更新对话历史或保存见解
  2. 跳转生命周期 - 根据上下文移动到智能体循环中的不同步骤(例如,如果满足条件则跳过工具执行,使用修改后的上下文重复模型调用)
智能体循环中的中间件钩子

示例:摘要

最常见的生命周期模式之一是在对话历史过长时自动进行压缩。与模型上下文中显示的瞬时消息修剪不同,摘要会持久化更新状态 - 用为所有未来轮次保存的摘要永久替换旧消息。 LangChain 为此提供了内置中间件:
当对话超过令牌限制时,SummarizationMiddleware 会自动:
  1. 使用单独的 LLM 调用对较旧的消息进行摘要
  2. 在 State 中用摘要消息替换它们(永久地)
  3. 保留最近的消息以维持上下文
摘要后的对话历史是永久更新的 - 未来的轮次将看到摘要而不是原始消息。
有关内置中间件、可用钩子以及如何创建自定义中间件的完整列表,请参阅中间件文档。

最佳实践

  1. 从简单开始 - 从静态提示和工具开始,仅在需要时添加动态内容
  2. 增量测试 - 一次添加一个上下文工程特性
  3. 监控性能 - 跟踪模型调用、令牌使用情况和延迟
  4. 使用内置中间件 - 利用 SummarizationMiddleware、LLMToolSelectorMiddleware 等。
  5. 记录你的上下文策略 - 清楚地说明传递了哪些上下文以及原因
  6. 理解瞬时与持久:模型上下文的更改是瞬时的(每次调用),而生命周期上下文的更改会持久化到状态

相关资源