上一页介绍了技能将可重用域专业知识(说明、参考资料和脚本)打包到任何代理可按需加载的自包含单元中。 但是,当你将代理部署到生产环境时,会出现一个新类别的问题:无论代理做什么,都会影响每个交互的问题。
需要记录每个请求和响应。 在模型看到有害内容之前,你需要实施防护措施以阻止这些内容。 需要强制实施速率限制、正常捕获异常并注入遥测数据 , 所有这些操作都无需触摸代理的核心逻辑。 将这些担忧复制粘贴到每个代理(或每个工具或每个技能)中无法扩展,并且会导致维护难题。
中间件 可以解决此问题。 中间件允许你使用可复用的行为包装代理的执行管道,从而在明确定义的节点拦截、检查和修改请求和响应。 将中间件视为代理周围的一系列同心层 , 每个层都有机会在进入代理之前对输入执行操作,在到达调用方之前在输出上执行操作。
何时使用此项
在以下情况下向代理添加中间件:
- 你需要防护措施,以在模型处理内容之前或之后阻止有害、偏离主题或违反策略的内容。
- 你希望对所有代理交互进行 集中日志记录或遥测 ,而无需单独修改每个代理。
- 需要 修改请求或响应 (扩充提示、转换输出或完全替换结果),而无需更改代理逻辑。
- 你想要 强制实施 适用于每次运行的速率限制、内容筛选或身份验证检查等策略。
- 需要一致、地处理异常 — 针对暂时性故障重试、返回正常回退响应,或记录错误日志以供诊断。
- 想要跨管道 共享状态 ,例如跟踪请求计时或累积多个中间件组件所需的指标。
小窍门
代理框架包括内置的用于跟踪和指标的工具。 有关详细信息,请参阅 可观测性 。
中间件管道的工作原理
调用代理的 run 方法时,请求不会直接转到模型。 相反,它会流经中间件层的管道,其中每个层都可以检查或修改请求,委托给下一层,然后在返回的路上检查或修改响应。
┌─────────────────────────────────────────────────────────┐
│ Caller: agent.run("What's the weather?") │
└──────────────┬──────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Middleware 1 (Logging) │
│ • Logs the incoming request │
│ • Calls next middleware │
│ • Logs the outgoing response │
└──────────────┬──────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Middleware 2 (Guardrails) │
│ • Checks input against content policy │
│ • If blocked → returns early with rejection message │
│ • If allowed → calls next middleware │
│ • Checks output against content policy │
└──────────────┬──────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ Agent core (model invocation, tool calls, etc.) │
└─────────────────────────────────────────────────────────┘
要点:
- 每个中间件决定是否继续。 中间件可以调用链中的下一层以正常方式继续,或者可以通过直接返回响应来使管道短路,例如,当护栏阻止请求时。
- 中间件能够识别这两个方向。 中间件在委托之前(检查或修改输入)和响应返回后(检查或修改输出)运行代码。 这是经典“洋葱”模式。
- 多个中间件链式连接。 注册多个中间件组件时,它们嵌套:第一个注册的中间件是最外部层,最后一个注册的中间件是最靠近代理的内部层。
小窍门
有关中间件如何适应完整的代理执行管道(包括上下文提供程序和聊天客户端层)的详细视图,请参阅 代理管道体系结构。
中间件可以执行的操作
代理框架在管道的三层(代理运行、函数调用和聊天客户端)上支持中间件,从而可以精细控制截获执行的位置。 常见模式包括:
| 图案 | 示例 | 引用 |
|---|---|---|
| 护栏与终止措施 | 阻止有害内容,限制聊天长度 | 终止和指导原则 |
| 异常处理 | 针对瞬态故障重试,返回备用响应 | 异常处理 |
| 结果替代 | 编辑敏感数据,扩充或替换代理输出 | 结果替代 |
| 共享状态 | 在中间件之间传递请求 ID 或计时数据 | 共享状态 |
| 运行时上下文 | 根据会话、用户或每次运行的配置改变行为 | 运行时上下文 |
| 范围 | 将中间件应用于所有运行或仅将中间件应用于单个运行 | 代理与运行范围 |
有关定义和注册中间件的完整演练,请参阅 “定义中间件”。 有关完整体系结构概述,请参阅 中间件概述。
注意事项
| 注意事项 | 详细信息 |
|---|---|
| 关注点分离 | 中间件使跨领域逻辑远离代理代码、工具和技能。 每个中间件组件都有一个责任(日志记录、防护栏、错误处理),你可以单独添加、删除或重新排序。 |
| 顺序依赖 | 中间件形成链。 注册中间件的顺序很重要:首先运行的日志记录中间件将看到原始输入,而上次运行的中间件将看到先前中间件已修改的输入。 有意识地规划流程顺序。 |
| 调试复杂性 | 当中间件修改输入或输出时,调试需要了解完整的管道。 响应可能看起来不正确,不是因为代理,而是因为中间件转换了它。 良好的日志记录中间件(位于链中早期)有助于诊断这些情况。 |
| 性能开销 | 每个中间件层都会向每个请求添加处理时间。 对于轻量级操作(如日志记录)而言,这是微不足道的。 对于昂贵的操作(例如调用外部内容审查 API),延迟会加起来,尤其是在链接了多个此类中间件时。 |
后续步骤
现在,代理具有工具、技能和中间件,下一步是 上下文提供程序 ,即在每次运行之前将内存、用户配置文件和动态知识注入代理的上下文窗口中的组件。
更深入: