📚 Harness 学习计划 / 第1周 / Day03_补充_Claude与Hermes接口格式对比

Day 3 补充 · Claude Messages API vs Hermes ChatML 接口格式逐层对比

创建时间:2026-08-20(Day 6 学习时补充)
关联:Day 3 对比表已覆盖功能维度,本文档专攻接口格式——请求/响应的 JSON 结构逐层对比


一、术语定义

术语 性质 说明
Claude Messages API 结构化 JSON API 协议 工具定义、调用、结果都是 API 的独立字段,类型安全
Hermes ChatML 文本标记型对话格式 工具定义、调用、结果都嵌入 prompt 文本中,靠标签区分

两者是同一层级(大模型对话交互协议)的两种不同实现范式。


二、顶层结构:分字段 vs 全塞一个字符串

这是两者最本质的区别。

Claude Messages API —— 3 个顶层参数,各司其职

POST /v1/messages
{
  "model": "claude-sonnet-5",
  "system": "You are a helpful assistant.",   ← 独立字段:系统指令
  "messages": [                                ← 独立字段:对话历史
    {"role": "user", "content": "..."}
  ],
  "tools": [                                    ← 独立字段:可用工具
    {"name": "get_weather", ...}
  ]
}

三个参数各司其职,互不干扰。API 后端收到后,内部拼接成模型能理解的格式,但 Harness 开发者不需要关心拼接细节

Hermes ChatML —— 全塞进 messages,一个字符串串到底

POST /v1/chat/completions
{
  "model": "hermes3:8b",
  "messages": [
    {"role": "system", "content": "
      You are a helpful assistant.
      <tools>{...}</tools>          ← 工具定义也塞这里
    "},
    {"role": "user", "content": "..."},
    {"role": "assistant", "content": "
      <tool_call>...</tool_call>    ← 工具调用也塞这里
    "}
  ]
}

所有东西都在 messages 数组里,最终拼成一个巨大的 prompt 字符串:

<|im_start|>system
You are a helpful assistant.
<tools>{...}</tools>
<|im_end|>
<|im_start|>user
What's the weather in Paris?
<|im_end|>
<|im_start|>assistant
<tool_call>...</tool_call>
<|im_end|>

一图对比

┌──────────────────────────────────────────┐
│       Claude(结构化分离)                │
│                                          │
│  system ──→ 独立字段                     │
│  tools  ──→ 独立字段                     │
│  messages ──→ 独立字段                   │
│                                          │
│  API 内部拼接,Harness 只操作 JSON 对象   │
└──────────────────────────────────────────┘

┌──────────────────────────────────────────┐
│       Hermes(文本拼接)                  │
│                                          │
│  messages ──→ 一个数组                    │
│    ├── system(含 <tools>)               │
│    ├── user                              │
│    ├── assistant(含 <tool_call>)        │
│    └── tool(含 <tool_result>)           │
│                                          │
│  最终拼成一个大字符串,Harness 负责拼接    │
└──────────────────────────────────────────┘

这对 Harness 意味着什么?

Claude Hermes
传工具定义 tools: [...] 直接传 自己拼 <tools>{...}</tools> 塞进 system content
传对话历史 messages: [...] 追加即可 自己维护 ChatML 全文本拼接顺序
解析工具调用 content[].type === "tool_use" 正则匹配 <tool_call> 标签
回传工具结果 tool_result block 带 ID 匹配 <tool_result> 按顺序追加
判断是否结束 stop_reason 字段 正则判断有没有 <tool_call>

Claude 的 Harness 是"操作 JSON 对象",Hermes 的 Harness 是"拼字符串 + 解析字符串"。 这就是为什么 Day 3 结论说"Claude 集成更快,Hermes 开发成本更高"——多的不是理解成本,是字符串处理代码。


三、工具定义:怎么告诉模型"你有什么能力"?

Claude Messages API —— tools 是 API 顶层参数

{
  "model": "claude-sonnet-5",
  "max_tokens": 1024,
  "system": "You are a helpful assistant.",
  "messages": [
    {"role": "user", "content": "What's the weather in Paris?"}
  ],
  "tools": [
    {
      "name": "get_weather",
      "description": "Get the current weather for a city",
      "input_schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string",
            "description": "City name"
          }
        },
        "required": ["city"]
      }
    }
  ]
}

关键特征:

Hermes ChatML —— tools 嵌在 system prompt 里

<|im_start|>system
You are a helpful assistant.

<tools>
{"type": "function", "function": {"name": "get_weather", "description": "Get the current weather for a city", "parameters": {"type": "object", "properties": {"city": {"type": "string", "description": "City name"}}, "required": ["city"]}}}
</tools>
<|im_end|>
<|im_start|>user
What's the weather in Paris?
<|im_end|>
<|im_start|>assistant

关键特征:


四、工具调用:模型怎么告诉 Harness"我要调工具"?

Claude —— 结构化 content block

{
  "id": "msg_xxx",
  "model": "claude-sonnet-5",
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_01A09B3C...",
      "name": "get_weather",
      "input": {
        "city": "Paris"
      }
    }
  ]
}

关键特征:

Hermes —— 文本标签

<tool_call>
{"name": "get_weather", "arguments": {"city": "Paris"}}
</tool_call>

关键特征:


五、工具结果回传:怎么把执行结果还给模型?

Claude —— 结构化 tool_result block

{
  "role": "user",
  "content": [
    {
      "type": "tool_result",
      "tool_use_id": "toolu_01A09B3C...",
      "content": "Paris: 22°C, partly cloudy"
    }
  ]
}

关键特征:

Hermes —— 文本标签

<|im_start|>tool
<tool_result>
{"temperature": 22, "condition": "partly cloudy"}
</tool_result>
<|im_end|>

关键特征:


六、并行工具调用

Claude

{
  "stop_reason": "tool_use",
  "content": [
    {"type": "tool_use", "id": "toolu_001", "name": "get_weather", "input": {"city": "Paris"}},
    {"type": "tool_use", "id": "toolu_002", "name": "get_weather", "input": {"city": "London"}}
  ]
}

一次返回多个 tool_use block,Harness 并行执行后一次性回传两个 tool_result。

Hermes

<tool_call>
{"name": "get_weather", "arguments": {"city": "Paris"}}
</tool_call>
<tool_call>
{"name": "get_weather", "arguments": {"city": "London"}}
</tool_call>

理论上可以输出多个 <tool_call>,但没有 stop_reason 信号,Harness 需要自己判断"模型是否还在输出"还是"这轮全部输出完了"。


七、核心差异总结

┌──────────────────────────────────────────────────────────────────┐
│                      Claude Messages API                        │
│                                                                  │
│   tools[]  ──→  API 顶层字段(独立)                              │
│   tool_use ──→  content block(type + id + input)                │
│   tool_result ──→ content block(type + tool_use_id + content)    │
│   stop_reason ──→ 明确信号(end_turn / tool_use / max_tokens)     │
│                                                                  │
│   Harness 只需操作 JSON 对象,不需要解析文本                       │
└──────────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────────┐
│                      Hermes ChatML                               │
│                                                                  │
│   tools ──→ <tools> 标签嵌在 system prompt 里                     │
│   tool_call ──→ <tool_call> 标签嵌在 assistant 回复里              │
│   tool_result ──→ <tool_result> 标签嵌在 tool 角色消息里           │
│   停止判断 ──→ 无内置机制,靠文本解析                              │
│                                                                  │
│   Harness 必须:正则提取 + JSON.parse + 格式错误处理               │
└──────────────────────────────────────────────────────────────────┘
维度 Claude Messages API Hermes ChatML
工具定义位置 API 顶层 tools 字段 system prompt 内的 <tools> 文本
工具调用返回 结构化 content[].type="tool_use" 文本 <tool_call> 标签,需正则解析
工具调用 ID 有唯一 id(如 toolu_01xxx 无 ID,Harness 自行管理
工具结果回传 结构化 tool_result block + tool_use_id 匹配 文本 <tool_result> 标签,按顺序拼接
停止判断 stop_reason 字段(API 保证正确) 无内置机制,靠解析文本判断
并行调用 原生支持,一次返回多个 tool_use 标签可并列,但无明确信号
参数校验 strict: true 保证 100% 符合 schema 无,模型可能输出格式错误
Harness 解析难度 低——JSON 直接取字段 中——正则 + JSON.parse + try-catch
解析出错风险 极低(API 层保证) 存在(模型输出不稳定)

八、对 Harness 设计的影响

如果 Harness 对接 Claude API:
  tools 字段直接传给 API → response.content 直接取 block
  → Harness 代码干净,不需要文本解析层

如果 Harness 对接 Hermes(通过 Ollama):
  Harness 必须自己:
    1. 把 tools 拼成 <tools> 标签塞进 system prompt
    2. 解析 <tool_call> 提取 name + arguments
    3. 处理格式错误(正则不匹配、JSON 解析失败)
    4. 按顺序拼接 <tool_result> 回传
    5. 管理对话历史(ChatML 全文本拼接)

结论:Claude 的 Tool Use 是 API 原生支持的"一等公民",Hermes 的 FC 是 prompt 层面的"约定"。Harness 对接 Hermes 需要额外写一个解析层。


📎 关联文件:Day03_对比ToolUse机制.md — 功能维度对比表
📎 关联记忆:[[hermes-day3-claude-tool-use]] — Tool Use 四层讲解+速查卡