AI 开发

AI Coding Agent 的 Agent Harness 是什么?从 Model、Harness、Tools 到 JSON State 深入解析 2026 Agent 架构

阅读约 16 分钟

DeepSeek 专篇写过一家产品的宿主。本篇只拆通用四层:Model、Harness、Tools、JSON State。模型填参数。宿主跑循环。工具过 Schema。状态能回放。

阅读前速览

层 锁什么 谁执行
Model tool_use / arguments 模型填 JSON,不跑副作用
Harness 循环 / 权限 / 压缩 宿主装配上下文再执行
Tools inputSchema Schema 过门,宿主再调
State session / turn / JSONL 能重建,才算有状态

四层都是对象。Prompt 不是架构。

Harness 是环,不是模型

2026 的 Coding Agent 看起来像「一个模型在改仓库」。拆开以后是四张对象。模型只产出下一次动作的 JSON。真正装上下文、过 Schema、评权限、跑工具、写回结果、在窗口将满时压缩历史的,是 Harness。没有这一环,模型只是聊天。Claude Code 官方把同一件事写成循环:收提示,评估,执行工具,再评估,直到没有工具调用。Codex 把连续性写进 Thread 和会话记录。Cursor 同样是宿主循环,不是模型自己碰磁盘。

  • Model:只填 lookup_order 和 A-1042。看不见套接字,也不 spawn 进程。
  • Harness:系统提示、工具表、权限、压缩、预算、max_turns。这些是宿主状态,不是模型权重。
  • Tools 与 State:工具有 Schema。会话有可检查的 JSON。聊天文本不是状态。

内置工具怎么填 JSON,见 AI Coding Agent 与 JSON 工具调用。MCP 那一跳,见 Coding Agent 如何调用 MCP。Schema 为什么是门,见 AI 为什么需要 JSON Schema。某一家产品的插件宿主,见 DeepSeek Harness 专篇。本篇不再讲那一家,只写四层怎么叠。

第一层:Model 只填 JSON

模型看见的是宿主给的工具表和到目前为止的消息。它的输出是文本,或一段工具调用,或两者。虚构查单,不要贴真实密钥:

{
  "type": "tool_use",
  "id": "call_3c91",
  "name": "lookup_order",
  "input": { "order_id": "A-1042" }
}

这一段不是 HTTP,也不是 MCP 的 tools/call。它是模型对宿主说「请查 A-1042」。宿主校验形状,再决定执行还是拦住。把模型当成执行器,会漏掉中间那一跳。

第二层:Harness 跑循环

一次会话是状态机,不是单次补全。官方循环可以收成一张对象:不可变的是模型名和工具表;可变的是消息数、待执行工具、预算。轮次从 2 走到 8,是宿主在计数,不是模型在记日记:

{
  "session_id": "sess_7f2a",
  "turn": 2,
  "max_turns": 8,
  "immutable": {
    "model": "coding-agent",
    "tools": ["lookup_order", "Read", "Bash"]
  },
  "mutable": {
    "messages": 6,
    "pending_tool": "lookup_order",
    "budget_tokens": 128000
  }
}

Claude Code 的一轮:模型产出带工具的回复,宿主执行,把 tool_result 写回,再问模型。没有工具调用时循环结束,并交出用量和 session_id。钩子可以在执行前拦截。Codex 在审批之后选沙箱,再执行。权限怎么写成规则,见 Coding Agent 权限模型。Harness 调用权限,并不等于权限。

第三层:Tools 过 Schema

工具表是模型的菜单。每一行必须有名字、说明、一份 JSON Schema。宽 object 等于请模型发明键。查单不要带退款字段:

{
  "name": "lookup_order",
  "description": "Look up an order by ID. Confirm before any refund.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

过门之后,宿主执行,再把结果写成对象喂回去。模型读的是回写,不是终端滚动条。失败也要是对象,不要只丢一句散文:

{
  "type": "tool_result",
  "tool_use_id": "call_3c91",
  "content": "{\"order_id\":\"A-1042\",\"status\":\"paid\"}"
}

第四层:JSON State 能回放

状态不是「刚才聊了什么」。状态是消息、工具结果、会话编号、压缩边界。Codex 把会话追加进 JSONL,resume 从记录重建。一行就该能指出是哪一次查单:

{"ts":"2026-09-28T01:12:04.221Z","type":"tool_result","id":"call_3c91","name":"lookup_order","ok":true,"order_id":"A-1042"}

上下文会满。Claude Code 在接近上限时自动压缩,流里会出现 compact_boundary。早期细节可能被摘要吃掉。项目根的 CLAUDE.md 会再注入,所以稳定规则写进文件,不要只写在第一轮提示里。压缩是状态变迁,不是静默丢记忆:

{
  "type": "system",
  "subtype": "compact_boundary",
  "session_id": "sess_7f2a"
}

五步把 Harness 收成对象

不要从「换一个更强的模型」开始。按对象写:

  1. 先列工具表。每条工具有 Schema。additionalProperties: false。没有 Schema 的工具不要交给模型。
  2. 写出循环的停止条件。max_turns 或预算。开口径任务不要裸跑。
  3. 权限写成规则。只读查单 allow。写盘 ask。退款 deny。不要把「请自动批准」当默认。
  4. 状态落成 JSON。至少留下 tool_use、tool_result、session_id。能按 id 对上,才算接通。
  5. 压缩当事件。看到 compact 就知道摘要替换了旧消息。必须留下的规则放磁盘文件。

Schema 怎么当门,见 Agents API + JSON Schema。本篇只要求:四层都有对象,而且由宿主执行副作用。

复盘 Agent,先摊开四份 JSON

一次翻车,多半是工具表、权限、状态没对齐,不是模型「不够聪明」。

  • 把 inputSchema 和 input 格式化,看缺字段和多余键。
  • 用 JSONPath 提取 抽出 $.name、$.input.order_id、$.session_id。
  • 一次成功回写和一次被拒 JSON Diff。先改 Schema 和规则,再改提示。
  • 核对 call_3c91 在 tool_use 和 tool_result 是否同一 id。
  • 会话 JSONL、令牌、仓库路径不要贴进公共格式化站。JSONSort 在浏览器本地跑。

四份对象都能按 Schema 重建,你才是在收架构,不是在收「它好像会改文件」。

常见误区

  • 把 Harness 当成新模型。模型换代。环、门、状态应该先稳定。评测错位从这里开始。
  • 以为更长的 system prompt 就是架构。提示会被压缩吃掉。工具表、权限、会话记录不会。
  • 跳过 Schema,直接执行。Harness 管循环,不替你免除二次校验。宽 object 会把发明键送进副作用。
  • 把聊天窗口当成状态。窗口是渲染。状态是 JSON。resume 读的是记录,不是截图。
  • 压缩当无事发生。官方会发边界事件。嵌套规则可能要重新读文件才会回来。
  • 用某一家产品的插件词当通用词。dsh、Capability Seam 是专篇对象。通用四层是 Model / Harness / Tools / State。

FAQ

Harness 和 Agent Framework 是一回事吗?

不是同一层。Framework 常在业务仓库里编排节点。Harness 是宿主循环:装上下文、过门、执行、落状态。你可以在 Harness 上写业务 Agent,不要把两层糊在一起。

模型会自己改文件吗?

不会。模型只填工具 JSON。读写、Shell、MCP 都由宿主执行。没有宿主,就没有仓库变更。

JSON State 必须是 JSONL 吗?

不必。Codex 用会话 JSONL 做权威记录。Claude Code 用会话文本和事件流。要求是能按 id 重建模型当时看见的对象,不是文件后缀。

压缩会丢掉 Schema 吗?

工具定义通常作为稳定前缀再注入。丢掉的是早期对话细节。必须留下的规则写进项目文件,不要只写在第一句用户消息里。

换模型要重写 Harness 吗?

不该。模型适配器可换。工具 Schema、权限表、状态形状应该先稳住。换模型是换填 JSON 的那一层,不是换四层架构。

结论

Coding Agent 的架构不是「一个更大的模型」。是 Model 填 JSON,Harness 跑循环,Tools 过 Schema,State 能回放。

先写清 A-1042 会进哪一个工具、哪一轮、哪一份状态记录。能指到四层对象,才算有 Harness。模型会换。这四层应该先稳定。

延伸阅读

更新记录: 首次发布