CSDN 原文镜像
本文为作者 CSDN 博客的全文镜像,原文发布于 2026-05-19。为适配本站结构,仅补充了站内元数据与来源说明,正文主体保持原文内容。
- 原文链接:https://blog.csdn.net/m0_63309778/article/details/161193006
- 站内分区:Agent / Provider 工具调用适配

摘要
在构建 AI Agent 框架时,我们经常会遇到一个工程问题:不同大模型厂商虽然都支持 Function Calling / Tool Use,但它们的消息格式并不一致。
例如 OpenAI、OpenRouter、vLLM 等生态通常采用一种接近 OpenAI Chat Completions 的工具调用格式,我们可以称之为 OpenAI-wire;而 Claude / Anthropic 则采用另一套基于 content blocks 的工具调用格式,我们可以称之为 Anthropic-wire。
这两种格式的核心差异包括:
- 工具调用放置位置不同;
- 工具参数格式不同;
- 工具执行结果的消息 role 不同;
- 工具调用与工具结果的关联字段不同;
- 多工具并行调用的表达方式不同;
- 消息 content 的组织方式不同。
对于 Agent 框架来说,一个非常重要的设计原则是:不要让核心 Agent 循环直接依赖某一个模型厂商的协议格式。
以 Hermes 这类 Agent 框架为例,它通常会在内部统一使用一种格式,比如 OpenAI-wire,然后在调用 Anthropic API 时,在适配层将 OpenAI-wire 转换成 Anthropic-wire。这样做可以让 Agent 主循环只维护一套逻辑,避免 provider 差异扩散到整个系统。
本文将详细介绍 OpenAI-wire 与 Anthropic-wire 的差异,并结合 Agent 框架的设计,讲清楚为什么需要做统一中间表示,以及如何实现格式转换。
1. 什么是 wire format?
在讨论 OpenAI-wire 和 Anthropic-wire 之前,首先要理解一个概念:wire format。
所谓 wire format,可以理解为:
LLM API 在网络传输时使用的 JSON 消息格式。
也就是说,当我们的程序调用大模型时,最终都会把上下文消息、工具定义、工具调用结果等内容组织成一个 JSON 请求体,然后通过 HTTP API 发送给模型服务。
例如:
Agent 代码
↓
内部统一消息结构
↓
转换成不同模型厂商要求的 JSON
↓
发送给 OpenAI / Anthropic / OpenRouter / vLLM
↓
解析模型返回结果
↓
继续执行工具或返回最终答案不同模型厂商虽然都支持“让模型调用工具”,但它们对消息格式的要求不同。
同样是让模型调用一个 read_file 工具:
- OpenAI 风格会把工具调用放到
assistant.tool_calls字段中; - Anthropic 风格会把工具调用放到
assistant.content数组中的tool_useblock 里。
所以,Agent 框架要解决的不是简单的“工具怎么执行”,而是:
如何让同一个 Agent 工具调用循环,兼容不同 LLM provider 的通信协议。
2. Function Calling 在 Agent 中的基本流程
在 Agent 系统中,Function Calling / Tool Use 的基本流程通常是这样的:
用户提出任务
↓
模型判断是否需要调用工具
↓
模型返回工具调用请求
↓
程序解析工具名和参数
↓
程序执行本地工具 / 外部 API
↓
将工具结果返回给模型
↓
模型基于工具结果继续推理
↓
返回最终答案,或者继续调用工具例如用户说:
读取 README.md 文件模型可能不会直接回答,而是输出一个工具调用:
调用 read_file 工具,参数为 {"path": "README.md"}程序收到之后执行:
read_file<span class="token punctuation">(</span>path<span class="token operator">=</span><span class="token string">"README.md"</span><span class="token punctuation">)</span>然后把文件内容再发回给模型,让模型继续总结或回答。
这个过程听起来简单,但一旦接入多个模型厂商,就会遇到消息格式差异。
3. OpenAI-wire 的工具调用格式
OpenAI-wire 是目前很多 Agent 框架和开源模型服务采用的格式。
OpenAI、OpenRouter,以及很多 OpenAI-compatible API,包括部分 vLLM 服务,都会采用类似结构。
一个典型请求如下:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"model"</span><span class="token operator">:</span> <span class="token string">"gpt-4"</span><span class="token punctuation">,</span>
<span class="token string-property property">"messages"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"读取 README.md"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_calls"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"README.md\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_call_id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"# Hermes Agent..."</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"Read a file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"parameters"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string-property property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>3.1 OpenAI-wire 的核心特点
OpenAI-wire 有几个非常重要的特点。
第一,工具调用放在 assistant.tool_calls 中
模型如果想调用工具,会返回一条 assistant 消息:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_calls"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"README.md\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>也就是说,工具调用不是放在 content 文本里,而是放在结构化字段 tool_calls 中。
第二,tool_calls 是数组
因为 tool_calls 是数组,所以模型一次可以返回多个工具调用。
例如:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_calls"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"README.md\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_2"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"package.json\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>这意味着模型可以并行请求多个工具调用。
Agent 框架在处理时,需要遍历 tool_calls:
<span class="token keyword">for</span> tool_call <span class="token keyword">in</span> message<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
name <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span>
args <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
result <span class="token operator">=</span> execute_tool<span class="token punctuation">(</span>name<span class="token punctuation">,</span> args<span class="token punctuation">)</span>第三,arguments 是 JSON 字符串
这是 OpenAI-wire 中一个非常容易踩坑的地方。
工具参数不是对象,而是一个 JSON 字符串:
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"README.md\"}"</span>所以程序执行工具前,需要先做反序列化:
<span class="token keyword">import</span> json
arguments_str <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span>
arguments <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>arguments_str<span class="token punctuation">)</span>也就是说:
OpenAI-wire:
arguments 是字符串,需要 json.loads而不是:
arguments <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span>如果直接把字符串当对象使用,就会报错。
第四,工具执行结果使用独立的 tool role
OpenAI-wire 中,工具执行结果会以一条独立消息追加回上下文:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_call_id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"# Hermes Agent..."</span>
<span class="token punctuation">}</span>其中 tool_call_id 非常重要,它用于关联:
哪个工具调用请求
对应
哪个工具执行结果完整链路如下:
user:
读取 README.md
assistant:
tool_calls = [
read_file({"path": "README.md"})
]
程序执行工具:
read_file("README.md")
tool:
tool_call_id = call_1
content = "# Hermes Agent..."
assistant:
根据 README.md 的内容继续回答4. Anthropic-wire 的工具调用格式
Anthropic-wire 是 Claude 使用的工具调用格式。
它和 OpenAI-wire 的最大不同在于:Claude 的消息内容采用 content blocks 结构。
Claude 不会把工具调用放到 assistant.tool_calls 字段,而是放在 assistant.content 数组中。
一个典型请求如下:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"model"</span><span class="token operator">:</span> <span class="token string">"claude-xxx"</span><span class="token punctuation">,</span>
<span class="token string-property property">"messages"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"读取 README.md"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_use_id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"# Hermes Agent..."</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string-property property">"tools"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"Read a file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input_schema"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string-property property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>4.1 Anthropic-wire 的核心特点
第一,工具调用放在 assistant.content 数组中
Claude 的 assistant 消息可能是这样的:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>这里的工具调用是一个 tool_use block。
结构如下:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>第二,input 是对象,不是 JSON 字符串
这是 Anthropic-wire 和 OpenAI-wire 的关键区别之一。
Claude 工具调用参数是:
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>而不是:
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\": \"README.md\"}"</span>因此,解析 Claude 的工具参数时不需要 json.loads:
args <span class="token operator">=</span> block<span class="token punctuation">[</span><span class="token string">"input"</span><span class="token punctuation">]</span>对比一下:
<span class="token comment"># OpenAI-wire</span>
args <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token comment"># Anthropic-wire</span>
args <span class="token operator">=</span> block<span class="token punctuation">[</span><span class="token string">"input"</span><span class="token punctuation">]</span>第三,工具执行结果放在 user role 中
这也是一个容易让人疑惑的地方。
在 OpenAI-wire 中,工具结果是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_call_id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"工具执行结果"</span>
<span class="token punctuation">}</span>但是在 Anthropic-wire 中,工具结果是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_use_id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"工具执行结果"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>也就是说:
OpenAI-wire: 工具结果使用 role = tool
Anthropic-wire: 工具结果使用 role = user这并不是说工具结果真的是用户输入,而是 Anthropic 的消息协议把“客户端提供给模型的工具结果”归在 user 消息中。
第四,content 可以混合文本和工具调用
Claude 的 content 是一个数组,里面可以同时包含文本和工具调用。
例如:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"我先读取 README 文件。"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>所以解析 Claude 返回结果时,不能简单认为 content[0] 一定是工具调用。
更稳妥的写法是:
<span class="token keyword">for</span> block <span class="token keyword">in</span> response<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"text"</span><span class="token punctuation">:</span>
collect_text<span class="token punctuation">(</span>block<span class="token punctuation">[</span><span class="token string">"text"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token keyword">elif</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"tool_use"</span><span class="token punctuation">:</span>
collect_tool_call<span class="token punctuation">(</span>block<span class="token punctuation">)</span>5. OpenAI-wire 与 Anthropic-wire 对比表
下面用一张表总结两种格式的差异:
| 对比项 | OpenAI-wire | Anthropic-wire |
|---|---|---|
| 工具定义字段 | tools[].type = function,工具信息放在 function 内 | 直接使用 tools[].name、description、input_schema |
| 工具参数 Schema | function.parameters | input_schema |
| 工具调用位置 | assistant.tool_calls | assistant.content[] 中的 tool_use block |
| 工具调用参数 | function.arguments | input |
| 参数格式 | JSON 字符串 | JSON 对象 |
| 是否需要 parse | 需要 json.loads(arguments) | 不需要 |
| 工具结果 role | role: "tool" | role: "user" |
| 工具结果关联字段 | tool_call_id | tool_use_id |
| 多工具调用 | tool_calls 数组 | 多个 tool_use block |
| 文本和工具调用关系 | content 和 tool_calls 分开 | content 数组可混合 text 与 tool_use |
| 解析重点 | 解析 tool_calls | 遍历 content blocks |
6. 为什么 Agent 框架需要统一内部格式?
如果我们只接入一个模型厂商,其实可以直接按照该厂商的协议写代码。
但是 Agent 框架一般不会只支持一个模型。
常见的接入对象可能包括:
OpenAI
Anthropic Claude
OpenRouter
vLLM
Ollama
Gemini
Qwen
DeepSeek
本地私有化模型服务如果每个 provider 的格式都直接写进 Agent 主循环,代码会变得非常混乱。
例如:
<span class="token keyword">if</span> provider <span class="token operator">==</span> <span class="token string">"openai"</span><span class="token punctuation">:</span>
<span class="token comment"># 解析 assistant.tool_calls</span>
<span class="token comment"># 工具结果用 role=tool</span>
<span class="token keyword">elif</span> provider <span class="token operator">==</span> <span class="token string">"anthropic"</span><span class="token punctuation">:</span>
<span class="token comment"># 遍历 assistant.content</span>
<span class="token comment"># 工具结果用 role=user + tool_result</span>
<span class="token keyword">elif</span> provider <span class="token operator">==</span> <span class="token string">"gemini"</span><span class="token punctuation">:</span>
<span class="token comment"># 又是另一套格式</span>
<span class="token keyword">elif</span> provider <span class="token operator">==</span> <span class="token string">"qwen"</span><span class="token punctuation">:</span>
<span class="token comment"># 可能还有特殊格式</span>这样的问题是:
- Agent 主循环越来越复杂;
- 每新增一个模型 provider,都要修改核心逻辑;
- 工具执行逻辑和 provider 协议耦合;
- 多工具调用、错误处理、消息历史管理变得难维护;
- 后期调试困难。
所以更好的设计是:
Agent Core 内部统一使用一种标准格式,Provider Adapter 负责做格式转换。
7. Hermes 的适配思路
以 Hermes 这类 Agent 框架为例,它的设计思路可以理解为:
内部统一使用 OpenAI-wire
调用 OpenAI / OpenRouter / vLLM 时:基本原样发送
调用 Anthropic / Claude 时:转换为 Anthropic-wire
拿到 Anthropic 返回后:再转换回 OpenAI-wire也就是说:
Agent Core 只认识 OpenAI-wire
Provider Adapter 负责翻译不同模型厂商的格式架构可以抽象成:
┌──────────────────────────────┐
│ Agent Core │
│ │
│ - 维护 messages │
│ - 判断 tool_calls │
│ - 执行工具 │
│ - 回填工具结果 │
│ │
│ 内部统一使用 OpenAI-wire │
└───────────────┬──────────────┘
│
▼
┌──────────────────────────────┐
│ Provider Adapter │
│ │
│ - OpenAI: 原样发送 │
│ - Anthropic: 转换成 Claude 格式│
│ - Gemini: 转换成 Gemini 格式 │
│ - 其他模型: 转换成对应格式 │
└───────────────┬──────────────┘
│
▼
┌──────────────────────────────┐
│ LLM Provider │
│ │
│ OpenAI / Claude / OpenRouter │
│ vLLM / Ollama / Gemini 等 │
└──────────────────────────────┘这本质上是一个典型的 Adapter Pattern,适配器模式。
8. Agent Core 的核心循环
Agent 的主循环大致可以写成这样:
<span class="token keyword">def</span> <span class="token function">run_agent</span><span class="token punctuation">(</span>user_input<span class="token punctuation">,</span> tools<span class="token punctuation">)</span><span class="token punctuation">:</span>
messages <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> user_input
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token keyword">while</span> <span class="token boolean">True</span><span class="token punctuation">:</span>
response <span class="token operator">=</span> call_model<span class="token punctuation">(</span>messages<span class="token punctuation">,</span> tools<span class="token punctuation">)</span>
<span class="token comment"># 如果模型没有请求工具调用,说明可以直接返回最终答案</span>
<span class="token keyword">if</span> <span class="token keyword">not</span> has_tool_calls<span class="token punctuation">(</span>response<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">return</span> response<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token comment"># 如果模型请求了工具调用,则执行工具</span>
<span class="token keyword">for</span> tool_call <span class="token keyword">in</span> response<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
tool_call_id <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span>
tool_name <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span>
tool_args <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
result <span class="token operator">=</span> execute_tool<span class="token punctuation">(</span>tool_name<span class="token punctuation">,</span> tool_args<span class="token punctuation">)</span>
messages<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string">"tool_call_id"</span><span class="token punctuation">:</span> tool_call_id<span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> result
<span class="token punctuation">}</span><span class="token punctuation">)</span>注意,这段主循环假设所有模型返回的都是 OpenAI-wire:
response<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span>
tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span>
tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span>
role <span class="token operator">=</span> <span class="token string">"tool"</span>
tool_call_id <span class="token operator">=</span> xxx如果底层是 OpenAI-compatible provider,那么可以直接处理。
如果底层是 Anthropic,则需要在 _call_anthropic() 里做格式转换,让 Agent Core 仍然看到 OpenAI-wire。
9. OpenAI-wire 转 Anthropic-wire
下面看一下核心转换逻辑。
9.1 转换 assistant tool_calls
Hermes 内部的 OpenAI-wire 消息可能是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token keyword">null</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_calls"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\":\"README.md\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>发送给 Claude 前,需要转换成:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>对应转换代码:
<span class="token keyword">import</span> json
<span class="token keyword">def</span> <span class="token function">openai_assistant_to_anthropic</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">:</span>
content <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token comment"># 如果原 assistant 消息里有普通文本,也需要保留</span>
<span class="token keyword">if</span> message<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"content"</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
content<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">for</span> tool_call <span class="token keyword">in</span> message<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"tool_calls"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
content<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> tool_call<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"name"</span><span class="token punctuation">:</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"input"</span><span class="token punctuation">:</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> content
<span class="token punctuation">}</span>核心映射关系:
OpenAI tool_calls[].id
→ Anthropic content[].id
OpenAI tool_calls[].function.name
→ Anthropic content[].name
OpenAI tool_calls[].function.arguments
→ json.loads(...)
→ Anthropic content[].input9.2 转换 tool result
Hermes 内部的 OpenAI-wire 工具结果是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_call_id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"# Hermes Agent..."</span>
<span class="token punctuation">}</span>发送给 Claude 前,需要转成:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_use_id"</span><span class="token operator">:</span> <span class="token string">"call_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"# Hermes Agent..."</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>对应转换代码:
<span class="token keyword">def</span> <span class="token function">openai_tool_result_to_anthropic</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">return</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string">"tool_use_id"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"tool_call_id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>核心映射关系:
OpenAI role = tool
→ Anthropic role = user
OpenAI tool_call_id
→ Anthropic tool_use_id
OpenAI content
→ Anthropic tool_result.content9.3 转换 tools 定义
OpenAI-wire 的工具定义是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"Read a file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"parameters"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string-property property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>Anthropic-wire 的工具定义是:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"description"</span><span class="token operator">:</span> <span class="token string">"Read a file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input_schema"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"object"</span><span class="token punctuation">,</span>
<span class="token string-property property">"properties"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"string"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>转换代码:
<span class="token keyword">def</span> <span class="token function">openai_tool_to_anthropic_tool</span><span class="token punctuation">(</span>tool<span class="token punctuation">)</span><span class="token punctuation">:</span>
function <span class="token operator">=</span> tool<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span>
<span class="token keyword">return</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> function<span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"description"</span><span class="token punctuation">:</span> function<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"description"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">"input_schema"</span><span class="token punctuation">:</span> function<span class="token punctuation">[</span><span class="token string">"parameters"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>映射关系:
OpenAI function.name
→ Anthropic name
OpenAI function.description
→ Anthropic description
OpenAI function.parameters
→ Anthropic input_schema10. Anthropic-wire 转 OpenAI-wire
当 Claude 返回工具调用时,Hermes 还需要把 Anthropic-wire 转回内部 OpenAI-wire。
Claude 可能返回:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"我需要先读取 README 文件。"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>Hermes 内部希望看到:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"我需要先读取 README 文件。"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_calls"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string-property property">"function"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"arguments"</span><span class="token operator">:</span> <span class="token string">"{\"path\":\"README.md\"}"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>转换代码如下:
<span class="token keyword">import</span> json
<span class="token keyword">def</span> <span class="token function">anthropic_assistant_to_openai</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">:</span>
texts <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
tool_calls <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">for</span> block <span class="token keyword">in</span> message<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"content"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"text"</span><span class="token punctuation">:</span>
texts<span class="token punctuation">.</span>append<span class="token punctuation">(</span>block<span class="token punctuation">[</span><span class="token string">"text"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token keyword">elif</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"tool_use"</span><span class="token punctuation">:</span>
tool_calls<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"id"</span><span class="token punctuation">:</span> block<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string">"function"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> block<span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"arguments"</span><span class="token punctuation">:</span> json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span>block<span class="token punctuation">[</span><span class="token string">"input"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
openai_message <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> <span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>texts<span class="token punctuation">)</span> <span class="token keyword">if</span> texts <span class="token keyword">else</span> <span class="token boolean">None</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> tool_calls<span class="token punctuation">:</span>
openai_message<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span> <span class="token operator">=</span> tool_calls
<span class="token keyword">return</span> openai_message这里的关键点是:
Anthropic input 对象
→ json.dumps(...)
→ OpenAI arguments 字符串11. 一个完整的 Provider Adapter 示例
下面给出一个简化版的适配器设计。
<span class="token keyword">class</span> <span class="token class-name">ProviderAdapter</span><span class="token punctuation">:</span>
<span class="token keyword">def</span> <span class="token function">call</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> messages<span class="token punctuation">,</span> tools<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">raise</span> NotImplementedError
<span class="token keyword">class</span> <span class="token class-name">OpenAIAdapter</span><span class="token punctuation">(</span>ProviderAdapter<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">def</span> <span class="token function">call</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> messages<span class="token punctuation">,</span> tools<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token comment"># OpenAI-wire 内部格式可以基本原样发送</span>
<span class="token keyword">return</span> openai_client<span class="token punctuation">.</span>chat<span class="token punctuation">.</span>completions<span class="token punctuation">.</span>create<span class="token punctuation">(</span>
model<span class="token operator">=</span><span class="token string">"gpt-4"</span><span class="token punctuation">,</span>
messages<span class="token operator">=</span>messages<span class="token punctuation">,</span>
tools<span class="token operator">=</span>tools
<span class="token punctuation">)</span>
<span class="token keyword">class</span> <span class="token class-name">AnthropicAdapter</span><span class="token punctuation">(</span>ProviderAdapter<span class="token punctuation">)</span><span class="token punctuation">:</span>
<span class="token keyword">def</span> <span class="token function">call</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> messages<span class="token punctuation">,</span> tools<span class="token punctuation">)</span><span class="token punctuation">:</span>
anthropic_messages <span class="token operator">=</span> self<span class="token punctuation">.</span>convert_messages_to_anthropic<span class="token punctuation">(</span>messages<span class="token punctuation">)</span>
anthropic_tools <span class="token operator">=</span> self<span class="token punctuation">.</span>convert_tools_to_anthropic<span class="token punctuation">(</span>tools<span class="token punctuation">)</span>
response <span class="token operator">=</span> anthropic_client<span class="token punctuation">.</span>messages<span class="token punctuation">.</span>create<span class="token punctuation">(</span>
model<span class="token operator">=</span><span class="token string">"claude-xxx"</span><span class="token punctuation">,</span>
messages<span class="token operator">=</span>anthropic_messages<span class="token punctuation">,</span>
tools<span class="token operator">=</span>anthropic_tools
<span class="token punctuation">)</span>
<span class="token keyword">return</span> self<span class="token punctuation">.</span>convert_response_to_openai<span class="token punctuation">(</span>response<span class="token punctuation">)</span>
<span class="token keyword">def</span> <span class="token function">convert_messages_to_anthropic</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> messages<span class="token punctuation">)</span><span class="token punctuation">:</span>
result <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">for</span> message <span class="token keyword">in</span> messages<span class="token punctuation">:</span>
role <span class="token operator">=</span> message<span class="token punctuation">[</span><span class="token string">"role"</span><span class="token punctuation">]</span>
<span class="token keyword">if</span> role <span class="token operator">==</span> <span class="token string">"tool"</span><span class="token punctuation">:</span>
result<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string">"tool_use_id"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"tool_call_id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">elif</span> role <span class="token operator">==</span> <span class="token string">"assistant"</span> <span class="token keyword">and</span> message<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"tool_calls"</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
content <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">if</span> message<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"content"</span><span class="token punctuation">)</span><span class="token punctuation">:</span>
content<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string">"text"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">for</span> tool_call <span class="token keyword">in</span> message<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
content<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string">"id"</span><span class="token punctuation">:</span> tool_call<span class="token punctuation">[</span><span class="token string">"id"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"name"</span><span class="token punctuation">:</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"input"</span><span class="token punctuation">:</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
result<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> content
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">else</span><span class="token punctuation">:</span>
result<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> role<span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> message<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> result
<span class="token keyword">def</span> <span class="token function">convert_tools_to_anthropic</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> tools<span class="token punctuation">)</span><span class="token punctuation">:</span>
result <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">for</span> tool <span class="token keyword">in</span> tools<span class="token punctuation">:</span>
function <span class="token operator">=</span> tool<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span>
result<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> function<span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token string">"description"</span><span class="token punctuation">:</span> function<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">"description"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token string">"input_schema"</span><span class="token punctuation">:</span> function<span class="token punctuation">[</span><span class="token string">"parameters"</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token keyword">return</span> result
<span class="token keyword">def</span> <span class="token function">convert_response_to_openai</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">:</span>
texts <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
tool_calls <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">for</span> block <span class="token keyword">in</span> response<span class="token punctuation">.</span>content<span class="token punctuation">:</span>
<span class="token keyword">if</span> block<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">"text"</span><span class="token punctuation">:</span>
texts<span class="token punctuation">.</span>append<span class="token punctuation">(</span>block<span class="token punctuation">.</span>text<span class="token punctuation">)</span>
<span class="token keyword">elif</span> block<span class="token punctuation">.</span><span class="token builtin">type</span> <span class="token operator">==</span> <span class="token string">"tool_use"</span><span class="token punctuation">:</span>
tool_calls<span class="token punctuation">.</span>append<span class="token punctuation">(</span><span class="token punctuation">{<!-- --></span>
<span class="token string">"id"</span><span class="token punctuation">:</span> block<span class="token punctuation">.</span><span class="token builtin">id</span><span class="token punctuation">,</span>
<span class="token string">"type"</span><span class="token punctuation">:</span> <span class="token string">"function"</span><span class="token punctuation">,</span>
<span class="token string">"function"</span><span class="token punctuation">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"name"</span><span class="token punctuation">:</span> block<span class="token punctuation">.</span>name<span class="token punctuation">,</span>
<span class="token string">"arguments"</span><span class="token punctuation">:</span> json<span class="token punctuation">.</span>dumps<span class="token punctuation">(</span>block<span class="token punctuation">.</span><span class="token builtin">input</span><span class="token punctuation">,</span> ensure_ascii<span class="token operator">=</span><span class="token boolean">False</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
message <span class="token operator">=</span> <span class="token punctuation">{<!-- --></span>
<span class="token string">"role"</span><span class="token punctuation">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string">"content"</span><span class="token punctuation">:</span> <span class="token string">"\n"</span><span class="token punctuation">.</span>join<span class="token punctuation">(</span>texts<span class="token punctuation">)</span> <span class="token keyword">if</span> texts <span class="token keyword">else</span> <span class="token boolean">None</span>
<span class="token punctuation">}</span>
<span class="token keyword">if</span> tool_calls<span class="token punctuation">:</span>
message<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span> <span class="token operator">=</span> tool_calls
<span class="token keyword">return</span> message这个例子只是用于说明思路,真实项目中还需要处理:
- streaming;
- error handling;
- tool result 顺序;
- 多工具并行调用;
- content block 中的图片、文件等多模态内容;
- token usage;
- stop reason;
- provider-specific 参数;
- 工具调用失败后的错误消息;
- JSON 参数解析失败的 fallback 策略。
12. 为什么选择 OpenAI-wire 作为内部统一格式?
Hermes 选择 OpenAI-wire 作为内部统一格式,通常有几个原因。
12.1 生态兼容性更强
很多模型服务都提供 OpenAI-compatible API,例如:
OpenRouter
vLLM
Ollama 的部分 OpenAI-compatible 接口
DeepSeek API
Qwen API 的兼容接口
一些企业内部私有化模型网关因此,如果内部使用 OpenAI-wire,接入大量模型服务会更方便。
12.2 Agent 主循环更简单
Agent Core 只需要处理一种格式:
assistant_message<span class="token punctuation">[</span><span class="token string">"tool_calls"</span><span class="token punctuation">]</span>
tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"name"</span><span class="token punctuation">]</span>
tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span>
role <span class="token operator">=</span> <span class="token string">"tool"</span>
tool_call_id <span class="token operator">=</span> xxx不用在主循环里写大量 provider 分支。
12.3 后续扩展更容易
如果将来要支持 Gemini,只需要增加:
OpenAI-wire ↔ Gemini-wire如果要支持 Qwen 原生格式,只需要增加:
OpenAI-wire ↔ Qwen-wire如果要支持其他私有模型网关,只需要增加:
OpenAI-wire ↔ PrivateModel-wireAgent Core 不需要改。
这就是统一中间表示的价值。
13. 典型踩坑点
13.1 OpenAI 的 arguments 是字符串
错误写法:
args <span class="token operator">=</span> tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span>
execute_tool<span class="token punctuation">(</span>name<span class="token punctuation">,</span> args<span class="token punctuation">)</span>这样拿到的是字符串:
<span class="token string">"{\"path\": \"README.md\"}"</span>正确写法:
args <span class="token operator">=</span> json<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>tool_call<span class="token punctuation">[</span><span class="token string">"function"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token string">"arguments"</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
execute_tool<span class="token punctuation">(</span>name<span class="token punctuation">,</span> args<span class="token punctuation">)</span>13.2 Anthropic 的工具结果不是 role=tool
很多人第一次接 Claude tool use 的时候,会下意识这么写:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"tool"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_call_id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"工具结果"</span>
<span class="token punctuation">}</span>这是 OpenAI-wire 的写法,不适用于 Anthropic-wire。
Anthropic-wire 应该写成:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_use_id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"工具结果"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>13.3 Claude 的 content 可能同时包含 text 和 tool_use
不能只取第一个 content block:
block <span class="token operator">=</span> response<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>因为第一个 block 可能是文本:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"text"</span><span class="token punctuation">,</span>
<span class="token string-property property">"text"</span><span class="token operator">:</span> <span class="token string">"我需要先读取文件。"</span>
<span class="token punctuation">}</span>正确方式是遍历:
<span class="token keyword">for</span> block <span class="token keyword">in</span> response<span class="token punctuation">[</span><span class="token string">"content"</span><span class="token punctuation">]</span><span class="token punctuation">:</span>
<span class="token keyword">if</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"text"</span><span class="token punctuation">:</span>
handle_text<span class="token punctuation">(</span>block<span class="token punctuation">)</span>
<span class="token keyword">elif</span> block<span class="token punctuation">[</span><span class="token string">"type"</span><span class="token punctuation">]</span> <span class="token operator">==</span> <span class="token string">"tool_use"</span><span class="token punctuation">:</span>
handle_tool_use<span class="token punctuation">(</span>block<span class="token punctuation">)</span>13.4 多工具调用必须用 id 关联结果
不要用工具名关联工具结果。
错误思路:
read_file → 工具结果因为同一个工具可能被调用多次:
read_file("README.md")
read_file("package.json")
read_file("src/main.py")必须使用 id:
OpenAI: tool_call_id
Anthropic: tool_use_id例如:
call_1 → read_file("README.md")
call_2 → read_file("package.json")
call_3 → read_file("src/main.py")工具结果必须分别对应:
tool_call_id = call_1
tool_call_id = call_2
tool_call_id = call_313.5 Anthropic 的 tool_result 顺序要注意
Claude 对工具结果的顺序比较敏感。
一般来说,模型返回 tool_use 后,下一条用户消息中应该尽快提供对应的 tool_result。
推荐结构:
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"assistant"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_use"</span><span class="token punctuation">,</span>
<span class="token string-property property">"id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"name"</span><span class="token operator">:</span> <span class="token string">"read_file"</span><span class="token punctuation">,</span>
<span class="token string-property property">"input"</span><span class="token operator">:</span> <span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"path"</span><span class="token operator">:</span> <span class="token string">"README.md"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"role"</span><span class="token operator">:</span> <span class="token string">"user"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token punctuation">{<!-- --></span>
<span class="token string-property property">"type"</span><span class="token operator">:</span> <span class="token string">"tool_result"</span><span class="token punctuation">,</span>
<span class="token string-property property">"tool_use_id"</span><span class="token operator">:</span> <span class="token string">"toolu_1"</span><span class="token punctuation">,</span>
<span class="token string-property property">"content"</span><span class="token operator">:</span> <span class="token string">"文件内容"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">]</span>
<span class="token punctuation">}</span>不要在工具调用和工具结果之间插入无关消息。
14. 对 Agent 框架设计的启发
OpenAI-wire 和 Anthropic-wire 的差异,本质上反映了一个 Agent 框架设计问题:
框架内部到底应该直接使用某个 provider 的格式,还是定义自己的统一中间格式?
对于一个长期维护的 Agent 框架来说,推荐做法是:
内部统一格式
外部适配转换
核心逻辑不依赖 provider也就是:
Agent Core
↓
Canonical Message Format
↓
Provider Adapter
↓
LLM Provider这种设计的好处是:
- 主循环简单;
- 工具执行逻辑统一;
- 消息历史管理统一;
- provider 扩展方便;
- 测试更容易;
- 不同模型之间切换成本低;
- 适合构建企业级 Agent 平台。
15. 总结
OpenAI-wire 和 Anthropic-wire 都是在表达同一件事:
模型需要调用工具,程序执行工具,然后把工具结果返回给模型。
但是它们的表达方式不同。
OpenAI-wire 的特点是:
assistant.tool_calls
function.arguments 是 JSON 字符串
工具结果使用 role=tool
通过 tool_call_id 关联工具结果Anthropic-wire 的特点是:
assistant.content 中包含 tool_use block
input 是 JSON 对象
工具结果使用 role=user
通过 tool_use_id 关联工具结果
content 可以混合 text 和 tool_use对于 Hermes 这类 Agent 框架来说,最合理的设计是:
内部统一使用 OpenAI-wire
调用 Anthropic 时转换成 Anthropic-wire
拿到 Anthropic 返回后再转换回 OpenAI-wire这样 Agent Core 只需要维护一套逻辑。
最终可以把整个设计总结成一句话:
OpenAI-wire 是内部统一语言,Anthropic-wire 是 Claude 的外部方言,Provider Adapter 是两者之间的翻译器。
这个设计看起来只是消息格式转换,但对 Agent 框架的可维护性、可扩展性和多模型兼容能力非常关键。对于需要支持多模型、多工具、多轮工具调用的企业级 Agent 系统来说,统一 wire format 几乎是必须要考虑的基础架构设计。