跳到主要内容

Agent 的端口与适配器架构:为什么你的工具 Schema 应该比供应商更长寿

· 阅读需 11 分钟
Tian Pan
Software Engineer

这是一个在每一个发布 Agent 的团队中都会重复发生的迁移故事。你基于某个供应商的 SDK 构建了你的 Agent。工具定义以该供应商预期的 JSON Schema 形式存在。工具结果被格式化为该供应商的消息结构。接着,某些原因迫使你做出改变 —— 竞争对手发布了更好的模型,采购部门为了冗余要求引入第二个供应商,或者一个 MCP 服务器取代了手写的集成 —— 然后你发现迁移的真正工作量:它不仅仅是一个 API 客户端。它是代码库中的每一个工具定义、每一个结果格式化器、每一个重试处理器以及每一个测试固件。

失败的原因并不是你选错了供应商。而是你让别人的序列化格式变成了你的内部架构。针对这个问题,早在二十年前就有了一个答案 —— Alistair Cockburn 的六边形架构,更广为人知的名字是“端口与适配器” —— 而 Agent 系统是它多年来最引人注目的新用例。

工具层才是锁定的真正所在

当工程师担心 LLM 的供应商锁定(Vendor Lock-in)时,他们通常想到的是 Prompt。Prompt 确实会偏向某个模型的惯用表达,但它们只是字符串 —— 重新调整虽然烦人,但隔离成本很低。工具层则本质上不同。一个生产环境的 Agent 可能会暴露几十个工具,而每一个工具至少在四个地方触及供应商特定的格式:

  • Schema 声明 —— 工具的名称、描述和参数如何被序列化到请求中。
  • 调用解析 —— 模型决定调用工具时,如何返回(一个专门的消息角色、一个内容块、或者一个流式增量)。
  • 结果格式化 —— 你在下一轮对话中如何将工具的输出交还给模型。
  • 错误与重试语义 —— 当模型生成了格式错误的参数或工具执行失败时会发生什么。

这些格式确实存在差异,而且不仅仅是表面上的。OpenAI 通过专门的 tool 角色返回工具结果;Anthropic 将它们作为结构化内容块映射到用户消息中。Anthropic 要求系统 Prompt 作为顶级字段,而 OpenAI 则在消息数组中接受它们。Google 的 API 会拒绝带有未类型化数组项的 JSON Schema,而这些在其他地方完全有效,并且没有主流供应商接受顶级的 $ref

这些怪癖比序列化更深入。如果你在启用扩展思考(Extended Thinking)时强制使用工具,Anthropic 会报错。而流式处理(Streaming)比这些都要糟糕:一个供应商发出结构化的多阶段事件,另一个发出生成片段(Completion Chunks),在两者之间转换需要在流的过程中维护状态。

这些差异如果只处理一次并不难。问题在于团队在哪里处理它们。当供应商的格式就是你编写工具的格式时,每一个怪癖都要在每一个工具定义处处理,你在迁移过程中必须修改的地方的数量随工具数量而扩展,而不是随供应商数量而扩展。

端口:你拥有的 Schema

六边形架构的核心举措是将依赖箭头指向内部。应用核心定义了端口(Ports) —— 用领域自身的词汇表达的稳定接口 —— 而混乱的外部世界通过实现这些端口的**适配器(Adapters)**进行连接。核心从不导入适配器;而是适配器导入核心。

应用到 Agent 上,端口就是你的工具契约:一个名称、一个人类和模型均可读的描述、一个参数 Schema、一个结果类型和错误语义 —— 编写一次,采用你版本化并拥有的中立表示。既不是 “OpenAI 函数调用格式”,也不是 “MCP 服务器发布的格式”,即使你的中立格式碰巧看起来与其中之一相似。区别不在于语法,而在于所有权。当你拥有契约时,供应商改变其序列化方式只是一个适配器 Bug。当供应商拥有契约时,同样的改变就是一个波及整个代码库的事件。

基于这个单一契约,薄薄的适配器在两个方向上进行机械转换:

  • 供应商适配器 (Provider Adapter) 将你的工具契约序列化为每个厂商的请求格式,并从每个厂商的响应格式中解析出工具调用决策 —— 包括流式变体。
  • 后端适配器 (Backend Adapter) 将契约的执行侧连接到实际执行工作的任何地方:一个本地函数、一个内部 REST 服务、一个 MCP 服务器、或者一个队列。

这与 LLM 网关(Gateway)的形式相同,网关是购买现成的供应商适配器部分的好方法。但网关只规范了面向模型的一侧。后端一侧 —— “search_orders” 今天是一个 Postgres 查询,下个季度是一个由另一个团队拥有的 MCP 服务器 —— 这是你的领域,没有任何供应商产品会为你定义这些契约。

真正重要的边界不是 Agent 与工具之间

加载中…
References:Let's stay in touch and Follow me for more thoughts and updates