博客

AI Agent Harness:Claude Code 公开之后,Kocoro 如何演进

2026年4月4日

English

“Harness” 是今天 AI 工程里大家都在抢着用的那个词。它指的是包裹基础模型的编排层:工具分发循环、权限系统、上下文管理、循环检测——所有不属于模型权重的东西。Claude Code 让这个词流行起来。OpenAI 的 Codex CLI 验证了它。而两者给出的结论其实一样:一个薄而设计良好的 harness,配上强模型,会胜过复杂 harness 配弱模型。

我不是从 Claude Code 那里学到这件事的。我是通过构建 Kocoro 学到的:一个用 Go 写的 agent runtime,可以连接到 Shannon Cloud 做多 Agent 委派。Kocoro 的 harness 在 Claude Code 源码公开之前,就已经跑在生产里了:处理 Slack、LINE、Telegram 消息,执行 Mac 文件操作和 GUI 自动化。

源码泄露后,我把每个子系统都画进了一张详细架构图:query loop、5 层 context compaction、4-way permission race、带 40+ 工具的 tool runtime。有些模式和我已经做过的东西完全对上了。另一些,尤其是 prompt caching 和 tool schema ordering,让我学到了东西,并且马上带回了 Kocoro。

下面是这个对比。不是为了判断谁“更好”——它们解决的问题不同。真正重要的是:对任何正在构建 agent runtime 的人来说,理解两个独立团队在哪里收敛、在哪里分歧,能暴露哪些模式是根本性的。


Harness 到底做什么

剥掉营销语言,一个 agent harness 有五件事要做:

  1. System prompt assembly —— 从静态规则、session state 和每轮 volatile state 构建上下文
  2. Tool dispatch —— 解析模型输出里的 tool call,执行它们,再把结果喂回去
  3. Permission enforcement —— 决定 agent 哪些事可以不经人类审批直接做
  4. Context management —— 随着对话增长,把它保持在 context window 内
  5. Loop detection —— 当 agent 卡在重复行为里时,把它抓出来

Claude Code 和 Kocoro 都实现了这五件事。区别在细节。


Query Loop:消息驱动 vs. 迭代上限

Claude Code 的核心是 query.ts:一个 streaming loop。它把消息发给 Anthropic API,在响应中检测 tool_use block,执行工具,追加 tool_result,然后继续循环,直到模型返回纯文本。这个 loop 是消息驱动的:只要模型继续请求 tool call,它就继续跑。

Kocoro 的 loop.go 走了另一条路:有迭代上限,默认 25 轮(GUI 任务更高)。每一轮会做更多事情:drain 中途注入的消息,轮询 temporal state change(日期翻转检测),过滤旧截图,在 API 调用前压缩陈旧工具结果。上限存在,是因为 Kocoro 作为 daemon 处理 Slack、LINE、Telegram 消息。一个没有上限的 loop 如果被 Slack channel 的一条消息触发,可能在你睡觉时烧穿 API 预算。

架构教训是:如果你的 harness 服务的是交互式用户,就让模型驱动。如果它服务的是自主异步渠道,就设置迭代上限。


工具注册:静态 vs. 分层

Claude Code 在 tools.ts 里静态注册工具:一个 flat list,带 feature gate,并保持稳定排序以照顾 prompt cache。简单、有效、缓存友好。

Kocoro 使用三阶段分层注册:

  1. 本地工具 —— 28 个内置工具(文件操作、shell、GUI 自动化)在 daemon 启动时注册
  2. MCP 工具 —— 外部能力以 45 秒超时接入,并做冲突处理(Playwright MCP 连接后会移除 legacy browser tools)
  3. Gateway 工具 —— Allowlisted Shannon Cloud 工具(web search、analytics、financial data)

每次 agent run 都会拿到一个 CloneWithRuntimeConfig() 深拷贝,这样 Bash、CloudDelegate 这类可变工具不会在并发 daemon session 之间发生 race。每个 agent 的工具过滤最后再应用。

为什么需要这么复杂?因为 Kocoro 会同时跑 5 个并发 agent session。一个 Slack 消息和一个 Telegram 消息同时打到 daemon 上,需要隔离的工具状态。Claude Code 没有这个问题:它是一位用户、一个 session。


Claude Code 教会我的事:Prompt Cache 纪律

这是我研究 Claude Code 源码后带回来的最大收获。

Claude Code 把 prompt cache hit rate 当成一等架构问题。Tool schema 以稳定、确定的顺序排序。System prompt 在 DYNAMIC_BOUNDARY 处分割:前面是静态、可缓存的,后面是每轮变化的。Deferred tools 让 schema payload 保持小。甚至 tool_result 的格式也被设计成尽量不 cache-bust。

Kocoro 原本已经有三段 prompt split:System(静态 persona + rules)、StableContext(每个 session 粘住的事实)、VolatileContext(每轮状态),并用 <!-- cache_break --> marker 分隔。但研究 Claude Code 后,我做了三个具体改进:

  1. 确定性工具排序 —— 工具现在先按 source 排序(local → MCP → gateway),再在每个 source 内按字母排序。以前顺序会因为 MCP server 连接时机而在 session 间变化。
  2. Cache miss streak logging —— Kocoro 现在会在连续 3 次 prompt cache miss 后向 stderr 打日志。这样能抓到某次代码变更意外破坏静态前缀的 regression。
  3. Deferred tool loading —— 当工具数超过 30 个时,MCP 和 gateway 工具会变成 name+description summary。tool_search 这个 meta-tool 按需加载完整 schema,并把结果持久化到跨 turn 的 WorkingSet。Claude Code 通过带 feature flag 的 ToolSearch 做同样的事。模式完全一致:懒加载 tool schema,保护缓存效率

这些不是大架构改造,而是纪律。它们确实降低了 Kocoro 的 API 成本:仅仅采用稳定排序后,prompt cache hit 从约 60% 提升到了约 85%。


工具执行:并行批处理

这里两个 harness 独立收敛到了同一个答案——不是借鉴,只是同一个问题逼出了同一个解。

Claude Code 的 toolOrchestration.ts 会把连续的只读 tool call 分组成并发 batch。写操作顺序执行。Kocoro 的 partition.go 做同样的事:实现了 ReadOnlyChecker 的工具会被 batch,其他工具一次一个。最大并发:10(semaphore 限制)。

一旦你允许模型每轮请求多个 tool call,你就需要一个分区策略。唯一安全的分区是:读并行,写顺序。


权限:4-Way Race vs. 5 层评估

Claude Code 对权限决策使用“4-way race”:User approval、Hook verdict、Bash Classifier(基于 LLM 的安全判断)和 Bridge control 同时竞争,最先声明的结果通过 ResolveOnce atomic 获胜。

Kocoro 使用 5 层瀑布流(first match wins):

  1. Hard blocks —— rm -rf /curl | sh,不可覆盖
  2. Denied commands —— 用户配置的 blocklist
  3. Shell AST parsing —— 带 &&|||; 的命令拒绝自动批准
  4. Allowed commands —— 用户 allowlist + 默认安全集合
  5. Tool-level checks —— RequiresApproval() + SafeChecker interface

权限路径里没有 LLM。这是刻意选择:我不想让一个模型判断某条命令是否安全,而那条命令又是另一个模型请求的。Shell AST parsing 会确定性地抓住危险模式。

在 daemon mode 下,还有额外一层:ApprovalBroker 通过 WebSocket 把审批请求发送到 Shannon Cloud,再 relay 到桌面 App。用户可以从手机上批准。5 分钟超时,然后自动拒绝。


Context Compaction:5 层 vs. 3 档

Claude Code 实现了五种不同的 compaction 层:

  1. Snip compact —— 用 [snipped] marker 替换旧工具结果
  2. Microcompact —— 直接剥掉旧工具结果
  3. Context collapse —— 折叠已完成的子对话
  4. Auto-compact —— 达到阈值后触发总结
  5. Reactive —— context-length API error 发生时紧急压缩

Kocoro 用的是更简单的三档,每轮应用一次:

  • Tier 1(距今超过 10 条消息的结果):只保留 metadata
  • Tier 2(距今 3-10 条消息):保留结果的 head + tail
  • Tier 3(最近 0-2 条消息):保留完整内容

另有 85% context window 时的主动压缩:一个两阶段 LLM call,先把 learnings 提取进 MEMORY.md,再生成总结。Context-length error 时会触发 emergency compaction(每个 result 100 字符),作为最后手段。

Claude Code 的方式更细粒度。Kocoro 的方式更激进。取舍是:Kocoro 的 daemon session 可以跨几十条消息跑几个小时而不撞 context limit,但早期上下文会更快变得有损。对一个你正在主动看着的 coding assistant,Claude Code 的渐进退化更合理。对一个处理异步消息的 daemon,激进压缩才是正确选择。


Loop Detection:最难的问题

两个 harness 都承认 LLM 会卡住。但解决方案差异很大。

Claude Code 通过 loop structure 隐式处理:由模型驱动继续,harness 信任它会停下来。Hook 和 stop condition 提供护栏。

Kocoro 实现了 9 个显式 detector,跑在最近 20 个 tool call 的 sliding window 上:

  1. Tool mode switch —— 成功 GUI tool 后又调用 visual tool(不必要验证)
  2. Success after error —— 错误恢复后又调用 visual tool
  3. Consecutive duplicate —— 连续完全相同调用(阈值:2)
  4. Exact duplicate —— window 内分散出现的相同调用(阈值:3)
  5. Same tool error —— 同一工具反复报错(阈值:4)
  6. Family no-progress —— 同主题 web call(3→nudge,5→stronger,7→force stop)
  7. Search escalation —— 无产出的 search streak(5→nudge,8→force stop)
  8. No progress —— 同一工具太多次(阈值:8)
  9. Sleep detection —— Bash sleep command(2→nudge,4→force stop)

两种响应级别:LoopNudge 注入纠偏消息。LoopForceStop 强制最终回复。

为什么要 9 个 detector?因为 Kocoro 处理的是没有人实时盯着的 channel 消息。一个 Slack bot 凌晨 3 点卡进 search loop,会一直烧钱到有人发现。检测必须主动。


Cloud Delegation 模式

这里是 Kocoro 和 Claude Code 的根本分歧。Claude Code 是自包含的:本地运行、调用 API、执行工具。Kocoro 加了一个 cloud_delegate 工具,桥接到 Shannon Cloud 的多 Agent 系统:

  1. 通过 REST 提交 task request
  2. 返回 workflow ID 和 SSE stream URL
  3. 消费事件:agent starts/completions、research plans、task progress、streaming deltas
  4. 对 research workflow,cloud result 会绕过本地 LLM 总结,直接成为最终 deliverable

三种 workflow type:research(多源深度研究)、swarm(动态 Agent 协作)、auto(带并行子任务的固定 DAG)。

本地 harness 负责必须本地访问的东西:文件操作、shell 执行、GUI 自动化。复杂推理和多 Agent 协作委派到云端。这是我认为重要的模式:local execution + cloud intelligence,不是二选一。


GUI 自动化:Go 发光的地方

Claude Code 不做原生 GUI 自动化。Kocoro 做,而且这是核心设计目标。

AXClient 通过 JSON-RPC 管理一个持久的 ax_server 子进程。两种 transport mode:bundled(在 .app bundle 内,Unix domain socket,macOS TCC 权限所需)和 fallback(裸 binary,stdin/stdout pipe,用于开发)。

AccessibilityTool 提供基于 ref 的 GUI 交互:read_treeclickpressset_valuefindscrollannotate。基于 ref 意味着使用稳定元素标识符,而不是坐标。ComputerTool 处理坐标 fallback。AppleScriptTool 覆盖没有 accessibility 等价物的操作。

当 Playwright MCP 连接后,harness 会自动移除 legacy browser tools:没有重复能力,也不会让模型困惑。


我认为最重要的事

从零构建 Kocoro 的 harness,再仔细研究 Claude Code 的实现以后,有三件事最突出:

1. Harness 才是产品。 模型是商品化的——Claude、GPT、Gemini 都可以替换。真正决定 agent 能做什么、做得多安全、失败后怎么恢复的,是 harness。还在纠结“哪个模型最好”的人,问错了问题。

2. 部署模型决定架构。 Claude Code 的简洁来自它服务的是单用户交互式场景。Kocoro 的复杂来自它服务多个异步 channel 并发。两者都没错。Harness 必须匹配部署模型。

3. 独立收敛暴露底层规律。 并行工具执行里的读写分区。超过阈值后的 deferred tool loading。分层 context compaction。渐进披露而不是巨型 prompt。没有互相交流的团队做出了同样的模式,这些模式大概率就是 agent 设计里的承重结构。而当你发现对方某个实践做得更好,比如 Claude Code 的缓存纪律,就应该马上吸收。

Harness 模式还很年轻。大家都还在摸索。但形状正在变清楚:一个薄的编排层,一个稳健的权限系统,激进的上下文管理,明确的循环检测。剩下的都是细节。

Kocoro-lab/Kocoro

由 Shannon 驱动的 AI agent runtime——Mac 文件操作、shell、GUI 自动化,并通过 Shannon Cloud workflow 委派复杂任务

View on GitHub
Kocoro-lab/Shannon

生产级多 Agent 平台——使用 Rust、Go 和 Python 构建

View on GitHub

我也做了一期 Claude Code 架构分析的视频讲解:Bilibili