上一页显示了中间件如何使用交叉关注点(日志记录、护栏、错误处理)包装代理的执行管道,而无需触摸代理的核心逻辑。 中间件处理的是代理如何运行,而不是代理知道的内容。 到目前为止,代理的知识来自两个来源:其训练数据以及用户在当前对话轮次中说的任何内容。
这是个问题。 有用的代理不仅仅需要这些。 它需要回忆一下用户三年前所说的内容,了解用户的偏好,或从知识库拉取相关事实,这一切都在开始生成响应 之前 。 工具可以提取信息,但它们是反应性的:模型必须决定调用它们。 如果模型没有意识到它需要上下文,它不会要求它。
上下文提供程序 可解决此问题。 它们是在每个代理调用之前和之后运行的组件,主动将相关信息注入上下文窗口,并选择性地从要存储的响应中提取状态以供将来使用。 它们提供代理内存、个性化设置和对外部知识的访问权限,而无需更改代理的说明或代码。
何时使用此项
在以下情况下向代理添加上下文提供程序:
- 代理需要 对话历史记录 , 它应该记住以前轮流中所说的, 而不仅仅是当前的消息。
- 你想要注入 用户特定的数据 (配置文件、首选项、帐户详细信息或会话状态),以便代理可以个性化其响应。
- 你需要 检索扩充生成 (RAG) - 在每个响应之前自动从知识库提取相关文档或事实。
- 代理需要 动态说明 - 根据一天中的时间、用户的位置或其他运行时条件在调用之间进行更改的上下文。
- 你希望 从代理逻辑分离数据溯源 — 代理不需要知道上下文来自 何处 ,只有上下文可用。
为什么不用工具?
工具和上下文提供程序都允许代理访问外部信息,但它们的工作方式基本不同:
| 方面 | 工具 | 上下文提供程序 |
|---|---|---|
| 触发器 | 反应 - 模型决定何时调用工具 | 主动 — 在每次调用之前自动运行 |
| 控制 | 模型驱动:模型选择哪个工具、何时使用以及使用哪些参数 | 开发者驱动:由你决定哪些上下文始终可用 |
| 能见度 | 模型必须知道存在一个工具并判断它是否相关 | 上下文以透明方式注入 — 模型将其视为提示的一部分 |
| 用例 | 按需操作和查找:“搜索 Web”、“查询数据库” | 始终存在的上下文:对话历史记录、用户配置文件、预加载的知识 |
| 令牌成本 | 仅当调用该工具时才花费的令牌 | 用于每次调用的令牌(上下文始终在提示中) |
两者都不是更好的。 许多代理都使用:应 始终 存在的信息的上下文提供程序(历史记录、用户配置文件、核心知识),以及代理应 按需 提取的信息工具(实时搜索结果、数据库查询、API 调用)。
小窍门
一个很好的经验法则:如果代理 每次 运行时都应该有此信息,请使用上下文供应商。 如果代理 应仅在相关时进行提取,请使用工具。
上下文提供程序的工作原理
上下文提供程序在每次代理调用的整个生命周期中参与两个阶段:
┌──────────────────────────────────────────────────────────────┐
│ Caller: agent.run("What's the return policy?") │
└──────────────┬───────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ BEFORE RUN — each context provider injects context │
│ │
│ • History provider loads past conversation messages │
│ • Memory provider retrieves relevant facts/preferences │
│ • RAG provider searches knowledge base and adds results │
│ • Custom provider injects user profile, time, location │
└──────────────┬───────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ Agent core — model sees original input + all injected │
│ context and generates a response │
└──────────────┬───────────────────────────────────────────────┘
▼
┌──────────────────────────────────────────────────────────────┐
│ AFTER RUN — each context provider processes the response │
│ │
│ • History provider saves the new messages │
│ • Memory provider extracts facts to remember for later │
│ • Custom provider updates session state │
└──────────────────────────────────────────────────────────────┘
要点:
- 上下文提供程序自动运行。 创建代理时,您只需注册一次。 之后,他们将参与每次调用,而无需您编写任何额外的代码。
- 多个提供商集成在一起。 可以注册多个上下文提供程序(历史记录提供程序、RAG 提供程序和自定义提供程序),它们都参与同一上下文窗口。 其贡献按注册顺序合并。
- 提供程序有两个挂钩。 前面的挂钩将上下文(消息、指令、工具)注入到提示中。 后钩子处理响应 - 存储消息、提取内存或更新状态。
- 提供程序具有会话感知性。 上下文提供程序接收当前会话,以便它们可以加载和存储限定为特定会话的数据。 有关会话管理的工作原理,请参阅 会话 。
小窍门
有关上下文提供程序与中间件和聊天客户端位于完整代理执行管道中的位置的详细视图,请参阅 代理管道体系结构。
管理上下文窗口
注入的每个上下文都使用模型上下文窗口中的令牌。 历史随每个轮次而增长。 RAG 结果添加文档区块。 用户配置文件会添加元数据。 如果总数超过模型的限制,则最旧或最不相关的信息将被截断,这可能会丢失重要的上下文。
使用上下文提供程序时,上下文窗口管理是一个关键考虑因素: 压缩 策略汇总或修整较旧的历史记录,以在保留密钥信息的同时保持在令牌限制范围内。 请参阅 压缩。
小窍门
有关内存和上下文提供程序的实践体验,请参阅入门教程中的 步骤 4:内存 。
重要
不建议保留很长的上下文窗口,因为随着上下文窗口的增长,模型的性能可能会下降。 如果代理开始遇到性能下降的情况,请考虑使用压缩策略来减小上下文大小。
注意事项
| 注意事项 | 详细信息 |
|---|---|
| 令牌预算 | 每个注入的上下文都会消耗令牌。 请仔细监视总上下文大小,尤其是在结合多个提供商时。 如果上下文无限增长,重要信息将会被悄然截断。 |
| 检索延迟 | 查询外部服务(数据库、搜索索引、API)的上下文提供程序会为每个调用添加延迟。 使用缓存、连接池和异步操作以保持检索速度。 |
| Relevance | 注入不相关的上下文不只是浪费令牌 , 它可以通过稀释信号来积极降低模型的响应。 确保提供商提供专注且准确的信息。 |
| 陈旧性 | 缓存或预加载的上下文可能会过时。 设计提供程序以适当间隔刷新数据,并考虑您的用例是否可以接受略微过时的上下文。 |
| 可组合性 | 当多个供应商参与同一上下文窗口时,其贡献可能会产生意外的相互作用。 将提供程序一起测试,而不仅仅是单独测试,以确保组合上下文有意义。 |
后续步骤
现在,您的代理具有工具、技能、中间件和上下文提供程序,下一步是 代理作为工具 ,即通过将一个代理用作另一个代理的工具来构建代理,实现专门化和任务委派。
更深入: