我把 7 个 Coding Agent 挨个解剖了一遍

我原以为是产品的错,测完发现是模型的错;再测一遍才发现——「模型」这个词里藏着三样东西,而只有一样是模型。

观测基准日:2026 年 7 月 26 日。本文涉及的产品版本、数据、源码行号均以此日为准。
这类文章三个月就会过期,请把它当成一张快照,而不是一本手册。


楔子:一次差点丢人的「重大发现」

事情是这样的。

我每天用 Coding Agent 干活,少则七八个小时,多则十几个。

K3 发布,我想试试这个模型能到什么程度。要试就试最强形态——那多半是官方自己做的客户端,于是我装了 Kimi Code。用了一阵,一个挥之不去的感觉冒出来:我总是不知道它在干嘛。屏幕上一个月亮 emoji 转啊转,转了半分钟,然后哗啦一下吐出一堆结果。而用 Claude Code 的时候,它会先说一句「我先看看这个文件」,再动手——我能在它跑歪之前喊停。

我觉得我发现了一个真问题。于是我去读了 Kimi Code 的源码(它是完全开源的),花了大半天,找到了「铁证」:它的运行时明明给每一次工具调用都生成了一句人话(比如 Reading src/foo.ts),这句话也明明送到了界面层,界面层还明明把它存了下来——然后从来不显示

我甚至把 issue 都写好了,标题是《tool.call.started 带着人类可读的 description,但 activity spinner 忽略了它》。

然后我做了一件事:打开 Kimi Code,真的跑了一次。

界面上清清楚楚写着:● Using Read (src/foo.ts)

我要提的问题,人家早就显示了。 我查了 992 条事件记录、两个引擎的代码、仓库近两个月的合并率,唯独没有看一眼界面。

这篇文章就是从那一刻开始的。后来我把七个主流 Coding Agent 挨个扒了一遍——五家开源的读源码,两家闭源的读官方文档加实测——连同我自己两万多次工具调用的记录,得出了一个和最初直觉完全不同的结论。中间我还错了两次,一次被数据打脸,一次被我自己打脸。

我会把这些错误也写进来。因为在这个领域,「我怎么发现自己错了」比「我发现了什么」更值钱

戴 AR 眼镜、穿白大褂的角色单膝跪在一台平躺的机器人旁边,机器人胸口的挡板被掀开,他用镊子从里面夹出一颗橙色的小球;左上角写着「它到底在干嘛?」,下面一排是今天要拆的七位选手的 logo


第一部分:基础课

已经懂 tool use 和 agent loop 的,直接跳 1.4——主线埋在那一节。

1.1 先说一个反直觉的事实:大模型什么都干不了

GPT、Claude、Kimi 这些大模型,说到底只会做一件事:你给它一段文字,它接着往下写一段文字。它不能读你的文件,不能运行你的代码,不能上网,不能改任何东西——一个被关在盒子里的大脑,只有一根「输入文字」的管道和一根「输出文字」的管道。

那为什么 Claude Code 能帮你改代码、跑测试、提交 git?

1.2 「工具调用」:给大脑装上手

答案是一个非常朴素的把戏,叫 tool use(工具调用)。我不需要改造模型,只需要在给它的文字里加一段说明:

你可以使用工具 Read(读取文件内容),参数 path。
要用它就按这个格式回答:{"tool": "Read", "path": "..."}

然后模型可能就会回答:{"tool": "Read", "path": "src/foo.ts"}

注意:模型并没有读文件,它只是「说」它想读。 真正读文件的是外面的程序——它看到这段格式化的文字,就去磁盘上把文件读出来,再把内容当作新的一段文字喂回去。模型接着往下写,此时它「知道」文件内容了,可以据此决定下一步。

戴 AR 眼镜、穿白大褂的角色把一张纸送进玻璃罩上的管道,罩子里封着发光的脑球,另一根管道把结果送出来,左边够不到的文件夹用虚线相连

这就是全部的魔法。 所谓 AI Agent 就是这个循环——模型说要用工具 → 程序执行 → 结果喂回 → 模型接着说,直到它不再要求用工具,而是直接给出答案。这个循环有个名字,叫 Agent Loop:本文剩下的一切,都是它在现实世界里被撞出来的形状。

Agent Loop 四步循环示意图:模型说要读文件 → 程序真的去执行 → 把结果当文字喂回去 → 模型接着往下想,顺时针循环

1.3 一个最小的 Agent 只要 50 行

不是打比方。一个能跑的 Coding Agent,核心代码真的就这么点:

messages = [系统提示词, 用户的问题]
while True:
    回复 = 调用大模型(messages, 可用工具列表)
    messages.append(回复)
    if 回复里没有工具调用:
        break                      # 模型说完了,收工
    for 每个工具调用 in 回复:
        结果 = 真的去执行(工具调用)
        messages.append(结果)      # 结果喂回去,下一轮继续

这就是 Claude Code、Codex、Cursor 的共同内核——一个 while 循环。既然核心只有 50 行,这些公司几千个文件、几十万行代码,到底在写什么?

1.4 全文主线:一个 while 循环撞上现实世界,冒出五个问题

答案是:在处理这个循环撞上真实世界时冒出来的一大堆麻烦事。 收拢起来就是五个问题——它们构成本文的主线,后面五个大部分(我称之为「五刀」)一刀对一个:

问题一句话说清
第一问谁来决定下一步这个 while 循环由谁驱动?它跑起来之后,你还能不能插一句话?
第二问怎么精确地改一行模型只会吐字,怎么让它精准地改掉第 47 行而不重写整个文件?
第三问记忆满了怎么办模型的「记忆」有上限,聊两小时就撑爆,然后呢?
第四问危险操作放不放行它要跑 rm -rf,你拦不拦?全问太烦,全放太险。
第五问你看得见它在干嘛吗循环跑五分钟,你在屏幕前干瞪眼。你怎么知道它没跑偏?

七个产品的所有区别,都在这五个问题的答案里。 而每个问题通常只有两三类解法——产品是解法的例子,不是分类本身。所以后面每一刀都按同一个结构讲:问题长什么样(一个你能代入的场景)→ 为什么难 → 有哪几类解法 → 各自的代价 → 对你意味着什么。

先剧透两句,好让你带着悬念读下去:

基础课的结论:Agent 的核心是一个 while 循环,50 行就能写完;产品价值的 99.9% 在于回答上面那五个问题。你评估一个 Coding Agent,评估的不是「它会不会调工具」,而是「它在这五个问题上替你做了什么选择」。


第二部分:七位选手的家底

解剖对象,观测日 2026-07-26:

谁做的开不开源语言形态
Claude CodeAnthropic❌ 闭源TypeScript命令行
CodexOpenAI✅ 开源Rust命令行 + IDE + 云端
CursorCursor❌ 闭源VS Code 改造编辑器 + 命令行 + 云端
Kimi CodeMoonshot✅ 全栈开源TypeScript命令行 + 网页
OpenCode社区✅ 开源TypeScript命令行(客户端/服务端分离)
Grok BuildxAI✅ 开源Rust命令行
Pi两位独立开发者✅ 开源TypeScript命令行

七个 Coding Agent 的家底对比图:Claude Code 与 Cursor 闭源,其余五家开源;Codex 与 Grok Build 用 Rust,其余用 TypeScript;只有 Cursor 走编辑器路线

两条伏笔:两家用 Rust 不是炫技(第四刀见分晓);Cursor 是唯一的编辑器路线(第五刀引爆)。


第三部分:第一刀 —— 谁来决定下一步

第一问:这个 while 循环由谁驱动?它跑起来之后,你还能不能插一句话?

3.1 先给你一个场景

你说「把登录接口的超时改成 30 秒」,回车。它读文件、跑 grep、改了两处,开始跑测试。这时你突然想起来:测试环境那份配置也得改,但生产那份千万别动。

你有三个动作:按 Esc 打断、直接把这句话打进输入框、或者等它跑完。这三个动作在七个产品里的行为完全不同——有的推倒重来,有的排队等它忙完,有的会在下一次调用模型之前把你的话塞进去。差别由「循环归谁管」决定。

3.2 一个必须先分清的概念:turn 和 step

你说「帮我修这个 bug」,实际发生的是:

turn 开始
  step 1:「读 foo.ts」  → 执行 → 结果回灌
  step 2:「读 bar.ts」  → 执行 → 结果回灌
  step 3:「改这三处」   → 执行 → 结果回灌
  step 4:「跑一下测试」 → 执行 → 结果回灌
  step 5:「修好了,原因是……」← 没有工具调用了
turn 结束

一个 turn 跑了 5 步、调了 4 次工具。你在屏幕前等的那几分钟,就是这些 step 在滚。

turn 与 step 的关系示意图:一个 turn 里有五个 step,前四步各调用一次工具,第五步不再调用工具,循环退出

记住这个区分,因为你能插话的位置,只有 step 与 step 之间的缝隙——一个产品把这条缝隙留在哪里,决定了你那三个动作会发生什么。

3.3 为什么难

写那个 50 行的 while 循环时,「下一步做什么」根本不是个决定:模型调了工具就继续,一行 if 搞定。但真实世界会追问三件事:怎么判断该不该继续(模型会声明「停止原因」,但它有时说谎:声明自己说完了,同一条回复里却还挂着工具调用)、多个工具能不能同时跑(读三个文件可以并行,一边写一边读同一个文件就会出事)、用户插话算什么(打断?插队?排队?)。

3.4 解法类型一:循环自己驱动(简单,够用)

循环自己看模型的输出,自己决定要不要转下一圈。 Kimi Code 的第一代引擎、Pi 属于这一类;从可观测的行为看,Claude Code 也是。

Kimi 的 v1 引擎是教科书式的写法:模型这一步调了工具,就 continue 转下一圈;不再调工具,turn 结束。整条逻辑长在一处,任何人二十分钟能读懂。

Claude Code 把这一类写到了极致(这段来自社区流传的一份早期源码,当作野史参考):整个循环是一个函数写完的,turn 是一等公民,连 step 这个抽象都没有。最妙的是它判断该不该继续的方式:不看模型声明的「停止原因」,只看这一轮输出流里有没有真的出现过工具调用——因为模型有时会说谎,声明自己说完了,同一条回复里却还挂着工具调用。

不信声明,信事实。 这是本文里我最喜欢的一行设计——它承认「模型是不可靠的合作者」,然后把判据挪到了模型说谎也改不掉的地方。

Pi 把这一类推到极简:README 直接把「不做 MCP、不做子 agent(子 agent,大白话:agent 派出去并行干活的分身)、不做计划模式、不做权限弹窗」当卖点写,还把自己的文档路径塞进系统提示词(系统提示词:每次对话开始前,产品塞给模型的那份固定说明书),让 agent 自己去读文档来了解自己。这是一种赌注:赌模型足够聪明,框架越少越好。

代价:逻辑全长在一个函数里,任何人二十分钟就能读懂;但要加「压缩上下文」「插入子任务」「注入插话」这些横切能力时,只能往同一个函数里塞,越塞越挤。

3.5 解法类型二:队列驱动(复杂,能长大)

循环自己不做决定,只负责把队列排干;下一步做什么由另一个模块说了算。 Codex、Kimi Code 的第二代引擎、OpenCode 的新引擎属于这一类。

Codex 是最重的那个,它对并发的答案很漂亮:用一把读写锁表达「哪些工具能并行、哪些必须独占」——能并行的拿读锁,不能的拿写锁。这比「维护一张白名单再手写排队」优雅得多:并发规则成了语言原语,而不是一堆 if。

Kimi Code 让我们看到这个转变正在发生:命令行跑 v1(模型调了工具就继续),网页版跑 v2(队列空了才算完,下一步由另一个服务决定),两代同时在产——你能看到一个产品从「能跑就行」长成「要长期维护」的中间态OpenCode 则做到了真正的客户端/服务端分离,状态落在数据库里、主循环每轮重读,于是崩了能接着跑,多设备也能接管同一个会话。

代价:理解成本高得多,换来的是横切能力可以独立演进,不用每次都去动主循环。

3.6 那你插的那句话到底去了哪

回到 3.1 的场景。「排队 / 插话 / 中断」分不分得清,差别很大:Kimi Code 分得最清——回车 = 排队(turn 结束才发)、Ctrl+S = 插话(塞进当前这一轮)、Esc = 中断;Pi 同样两个注入点,且 Esc 中断时会把排队消息退还输入框Claude Code 的默认语义是「加一句话」而不是「推倒重来」(我的会话记录里排队注入 491 次,显式中断只有 155 次);OpenCode 还没有显式插话入口,只有一个 QUEUED 徽标。

Codex 最讲究:中断时先温和等 100 毫秒再强杀,然后往历史补一条「被打断」标记,并给每个被杀的工具合成一条「被用户中止」的结果。看着啰嗦,治的是个真问题:历史里留下「调用了工具却没有结果」的悬空记录,下一次请求会被 API 直接拒绝。

对你意味着什么:习惯边看边纠的人,「插话有独立按键」的产品更顺手。另记一条通用经验:打断一个正在跑的 agent,永远比让它跑完再纠正更贵

这一刀的结论

  1. 循环有两类——自驱型(逻辑集中,好读,难扩展)和队列型(逻辑分散,难读,能长大),有两家正在从前者迁往后者。
  2. 「该不该继续」的最佳判据是事实而非声明。而你能插话的缝隙只存在于 step 边界——把「排队 / 插话 / 中断」分成三个动作还是混成一个,直接决定了你的干预是「补充」还是「重来」。

第四部分:第二刀 —— 怎么让 AI 精确地改一行

第二问:模型只会吐字,怎么让它精准地改掉第 47 行,而不是重写整个文件?

4.1 场景,以及为什么难

你有一个 2000 行的文件,想把某个函数的超时从 10 秒改成 30 秒。模型只会吐字,最朴素的办法是让它把改好的完整文件吐出来覆盖原文件。试试就知道有多糟:贵且慢(改一行要吐 2000 行)、会丢东西(模型很容易「顺手」删掉它觉得不重要的注释、空行、甚至整个没看懂的函数,而你很难第一时间发现)、无法审查(拿到一个全新的 2000 行文件,看不出它改了什么)。

所以所有产品都在做同一件事:让模型只描述「改动」,而不是「结果」

难点在于,这一挪就把难题从模型挪到了格式上——而模型对格式的遵守是概率性的。标准 diff 尤其不友好:它要求精确行号,模型经常数错;错一个字符补丁就打不上去,得重试,重试又要重发一遍上下文。

于是三类解法出现了,区别在于:你把「保证格式正确」这个责任压在谁身上。

三种让 AI 精确改文件的路线对比图:精确替换靠唯一性(把句中高亮的词换掉)、补丁格式靠语法(文档上罩一层严格的格线)、专用小模型靠另一个模型(大机器人说个大概,小机器人精确缝进文件)

4.2 压在「唯一性」上(Claude Code、Pi)

模型给出「原文」和「新文」两段字符串,程序在文件里找到原文换掉。没有行号,不用数数。Claude Code 加了几道锁(都是你用的时候能亲眼撞见的):原文必须在文件中唯一出现(出现两次直接报错,不然改错地方)、改之前必须先读过这个文件(防止模型凭空猜内容)、报错信息是结构化的,模型能据此自己修正。它的工具列表里没有 patch 工具,只有这一条路。

代价:唯一性是个真实的摩擦。当你要改的那行是 return null;——文件里有十七个——模型就得多抄几行上下文凑唯一,抄多了又容易抄错。

4.3 压在「语法」上(Codex)

Codex 的招牌是 apply_patch:它定义了一种对模型更友好的类 diff 格式,然后用形式语法约束模型的输出(大白话:在生成阶段就规定「下一个字符只能是这几种之一」),让模型在物理上不可能吐出格式错误的补丁。同一个解析器还守着两条路径:正经调工具时走它,模型在 shell 里偷偷用时也被拦下来走它——后门也堵上了。

代价:它依赖「模型服务端支持语法约束」这个能力,不是所有供应商都提供。把可靠性建立在基础设施上,就会被基础设施绑定。

4.4 压在「另一个模型」上(Cursor)

大模型只负责说「这里改成这样」(可以很潦草,甚至写 // ... 其余不变),由一个专门训练的小模型把它精确贴进文件——Cursor 早年的技术博客把这套「fast apply」讲得很细(用推测解码加速:让小模型先猜一串结果、批量验证,而贴代码大部分内容原样照抄,猜中率极高);如今的实现细节外界无从验证,但「改动由专门模型来贴」这个路线从产品行为看没有变。

代价(从可观测行为推断):它引入了第二个概率性组件。前两条路线出问题会报错,这条可能是贴歪了但没报错——所以 Cursor 必须配一个强制的 diff 审查界面(diff:两版文件逐行对比的差异清单,红删绿增那种)。这两件事是一套的,不能只抄一半。

对你意味着什么:agent 改文件反复失败,八成不是它「笨」,而是唯一性约束失效了。最有效的干预不是换更强的模型,而是把位置说得更唯一(「改 LoginService 类里那个」)。

这一刀的结论

  1. 没人让模型重写整个文件。分歧在于「格式正确」的责任压在谁身上:唯一性约束(简单,重复代码会卡)、语法约束(最稳,绑定基础设施)、专用小模型(最灵活,必须配强制审查)。
  2. 这也是最快会被抹平的一刀:Grok Build 干脆把 Codex 和 OpenCode 的工具实现移植过来,做成六套可切换工具集——工具层正在从「产品差异化的地方」变成「不该重复发明的地方」。

第五部分:第三刀 —— 为什么 AI 会「失忆」

第三问:模型的「记忆」有上限,聊两小时就撑爆,然后呢?

5.1 先给你一个场景

你和 agent 聊了两小时,一起重构完一个模块。中途你明确说过「这个目录下的文件别动」。然后你让它加个小功能——它动了那个目录。你会本能地觉得它「不听话」,但真相更朴素:那句话已经不在它的记忆里了。

5.2 上下文是什么,为什么会满

模型每次只是「接着往下写」,这意味着每一次调用都要把之前所有的对话重新发一遍给它:第 500 次调用时,前面 499 次的全部内容(每个文件、每条命令的输出)都要重新发过去。这个「全部内容」就是上下文(context),它有大小上限。而 Coding Agent 是上下文的吞金兽:读一个文件几千 token,跑一次测试上万 token,几十轮下来就爆了。

关键在这里:模型没有「记忆」,它只有「这次请求里带了什么」。 所谓失忆不是它忘了,是有人在它看到之前就把那段话删掉了。删的人是谁?就是下面这些机制。

5.3 解法类型一:压缩(所有人都在用)

所有产品的第一解法都叫压缩(compaction):让模型把之前的对话总结成一段摘要,丢掉原文,只留摘要继续。 这就是你偶尔看到的「正在压缩对话」——之后它会有点失忆,因为细节真被扔了。

我从自己的会话记录里统计了 105 次压缩事件:效果是 40.7 万 token → 9,839 token,压掉 97.6%;代价是中位耗时 159.8 秒、最长一次 394 秒——这是整个使用过程中最长的一次停顿。

更有意思的是:这 105 次里 103 次是我手动触发的,只有 2 次自动。现在的模型上下文动辄一百万 token(token:模型计量文字的单位,一个汉字约合一到两个),自动压缩的触发线显然没有跟着窗口等比放大——日常根本撞不到。也就是说:在百万上下文时代,自动压缩事实上已经退化成了一个手动工具。

压缩过程示意图:40 万 token 的纸堆经过标着「中位 160 秒」的压机,出来只剩 1 万 token 的一张小卡片;戴 AR 眼镜、穿白大褂的角色坐在旁边托腮等待,地上散落着几张被丢掉的纸

压缩的代价比「慢」严重得多:摘要是模型自由发挥写的,而下一次压缩是对摘要再摘要——复印件的复印件,几轮就糊了。5.1 里丢掉的「别动这个目录」,通常就死在第二次摘要。

5.4 解法类型二:让压缩变得可控(OpenCode)

既然自由摘要会糊,那就别让它自由。OpenCode 的摘要是固定的五段模板(目标 / 重要细节 / 工作状态 / 下一步 / 相关文件),更关键的是——下一次压缩不是重新摘要,而是「更新上一份摘要」:像维护文档,而不是复印复印件。它还有一条更轻的路径叫 prune只擦掉老工具调用的输出,保留调用记录本身——你还知道「我读过 foo.ts」,只是不记得内容了。

Claude Code 能观察到的思路是「能少扔就少扔」:超大的工具结果不进上下文,而是落盘存成文件、只在对话里留一段预览和一个指针(界面上你能直接看到这类提示),全量压缩留作最后的手段。

5.5 另外两条路:重放,和检索

Kimi Code 的特色是账本——它把每次操作记成一条不可修改的流水(术语叫事件溯源,大白话讲就是记账:恢复会话不是「读取状态」,而是把账本从头重放一遍算出状态)。但别误会:在「装不下」这个问题上,它照样靠压缩——上下文用到约八成五就阻塞式地压一次,让模型写一份第一人称的交接笔记;我自己的账本里就有 8 次压缩记录。账本管的是另一条轴:历史完整、可审计、恢复不丢账。这条轴的代价我亲身踩到了——第九部分讲。

Cursor 则干脆跳出这个框:很多东西根本不该进上下文。 它在云端建语义索引,代码切块加密后上传向量,用 Merkle 树增量同步(大白话:像 git 那样只传变了的部分),要用时检索,不用时不占位置。代价很清楚:代码要出本机。

5.6 还有一种记忆是跨会话的:AGENTS.md

上面四类都在管「这次会话的记忆」。还有一种要活得更久:项目根目录放一个 CLAUDE.mdAGENTS.md(Codex、Kimi、OpenCode、Pi 都认后面这个名字),写上「这个项目怎么跑测试」「不要动这个目录」,agent 每次启动都会读——它已经成了事实标准。Codex 更进一步:把 AGENTS.md.git.codex 强制设为只读,理由很硬——防止 agent 修改自己的行为准则来给自己提权。

对你意味着什么:5.1 那个场景的正确解法不是「再说一遍」,而是把它写进 AGENTS.md——凡是「我必须重复第二次」的约束,都该从对话搬到文件里。这是我从这次调查里拿到的最实用的一条习惯改变。

这一刀的结论

  1. AI 的「失忆」不是玄学,是有具体机制在删东西。三类容量解法:压缩(人人都有,慢且会糊)、结构化 + 增量更新(最稳)、外部检索(最省,但代码要出本机);Kimi 的事件账本解决的是另一条轴——恢复与审计,在容量问题上它照样要压缩,而账本的历史还会反咬你(第九部分)。
  2. 百万上下文让自动压缩事实上失效了,它现在更像一个你需要主动按的按钮;而重要约束别只说一遍——对话是易失的,文件是持久的。

第六部分:第四刀 —— 危险操作放不放行

第四问:它要跑 rm -rf,你拦不拦?全问太烦,全放太险。

如果说前面三刀是工程,这一刀就是哲学了。

6.1 先给你一个场景

你让 agent 清理构建产物。它生成了一条命令:

rm -rf ./build ./dist $CACHE_DIR

看起来没问题。但要是写成 rm -rf $CACHE_DIR/,而这个变量在你机器上恰好是空的呢?它会展开成 rm -rf /这就是这一刀的全部难处:危险不写在命令的字面上,它藏在执行环境里,你没法靠「读一遍命令」判断安全。

6.2 为什么难

两个方向都是死路:全部弹窗问一遍 → 你一小时点五十次「同意」,第五十一次就会闭着眼点,审批疲劳会把安全机制变成摆设全部放行 → 只要出一次事,可能就是不可逆的。

所以真正的问题不是「拦不拦」,而是:你把信任放在哪一层? 七个产品的答案摊开来正好是一条光谱——从「交给操作系统」一路滑到「交给你自己」,越靠前越硬,越靠后越灵活。

(先埋一个伏笔:逐条审批除了拦危险,还有一个没人写进文档的副作用——它逼着你看见每一条命令。第五刀会回来算这笔账。)

戴 AR 眼镜、穿白大褂的角色捧着发光的脑球,面前依次是保险库门、栅栏门、守卫机器人和一个没有门的空门框

6.3 光谱左端:交给操作系统(Codex、Claude Code)

思路是:别问了,直接关起来——让危险在结构上不可能发生。

Codex 走得最远:三个操作系统各一套沙箱(macOS 用 Seatbelt、Linux 用 bubblewrap + seccomp、Windows 用受限令牌),默认只读、无网络、fail-closed——最后这个词的意思是「出问题就拒绝」而不是「出问题就放行」,这个默认值的方向本身就是立场。有了这层,很多审批直接不必要:6.1 那条命令即使真展开成 rm -rf /,能删的也只有沙箱里那点东西。

Claude Code 是同一端的另一种走法:多档权限加沙箱模式,而且实际用起来有个明显的甜头——允许命令在沙箱里跑之后,审批弹窗明显变少。方向和 Codex 一致:隔离做得越硬,需要问人的就越少,专治「审批疲劳」。

代价:沙箱要为每个操作系统单独实现维护,而且总有正常工作被挡住(比如联网装依赖),于是你又得开口子——每开一个,「结构上不可能」就松一分。

6.4 光谱中段:交给规则和静态分析(Kimi Code、OpenCode)

不隔离进程,而是在执行前把命令看清楚。

Kimi Code 用一条十九条规则的有序链,从上往下跑,第一个给出明确结论的规则胜出。真正值得抄的是另一个细节:敏感文件(.env、SSH 私钥、证书)的拦截不在权限层,而在工具层硬编码——你就算开到最宽松那档也读不到自己的私钥,因为那不是权限问题,是安全边界问题。权限是可调的旋钮,安全边界是不该有旋钮的地方;混在一层,总有一天有人会把旋钮拧到底。

OpenCode 则把 bash 命令解析成语法树,算出它实际会碰到哪些目录,越界才要权限——看的是结构不是字面,但同样看不穿环境变量。没有一条路线能真正看穿运行时,这是这一刀无解的部分。它另有两个有创意的原语:

① Doom loop 权限——同一个工具、完全相同的参数连续调用三次就弹窗。它把「agent 在原地打转」建模成了需要人类批准的危险操作,治的恰恰是最费钱的一种失败:不报错,只是无限重试。

② 带理由的拒绝——你拒绝时它弹个文本框让你写原因,这个原因会作为一种特殊的错误回灌给 agent。拒绝不再是「失败」,而是一次带理由的纠偏——现实中你对同事说的也不是「不行」,而是「换个思路」。

代价:静态分析在猜「这条命令会干什么」,上限由分析器的精度决定,而它永远落后于人的创造力。

6.5 光谱右端:交给模型,或者交给你自己(Cursor、Pi)

Cursor 的自动审查是个三级漏斗:白名单 → 沙箱 → 让另一个 LLM 判断「这条命令危险吗」(它不是孤例,Grok Build 的自动档也走这条路),官方文档里坦白承认这只是「尽力而为的护栏,不是硬安全边界」——这一类方案的特点是会以概率失败,而且失败时是静默的。 Pi 则干脆不做权限弹窗,还写进 README 当卖点:你自己会看,你自己负责。这在「一个人、自己盯着」时完全成立,但不能放进 CI

对你意味着什么:选哪一档只取决于一个问题——出事之后谁来擦屁股,多久能发现? 你自己盯着跑,放哪层都行;一旦无人值守、或者仓库里有别人的东西,你要的就不是「更聪明的判断」,而是「结构上不可能」。

这一刀的结论

  1. 所有答案都在同一条光谱上:你把信任放在离操作系统多近的地方? 越近越硬,越远越灵活(也越容易静默失败)。
  2. 真正的敌人不是危险命令,是审批疲劳。另有两条值得抄走:权限和安全边界必须分层(旋钮拧到底也不能碰私钥)、拒绝要能带上理由(拒绝是纠偏,不是失败)。

第七部分:第五刀 —— 你看得见它在干嘛吗(本文核心)

第五问:循环跑五分钟,你在屏幕前干瞪眼。你怎么知道它没跑偏?

前四问,七家都算解决了。这一问,只有一家真正解决了。

现在回到楔子里那个问题。

7.1 先说清楚这一问为什么最难

前四刀都有明确的成功标准:文件改对了没有、上下文有没有爆、危险命令有没有拦住。这一刀没有——它的成功标准长在身上:你能不能在它跑偏的第三十秒发现,而不是第五分钟。更麻烦的是模型愿不愿意先说一句话,是它的性格,不是你的代码:你可以在提示词里请求,但请求不是保证。

所以这一刀的正确问法是:当一件重要的事只能靠请求模型配合时,你还有别的办法吗?

7.2 三次测量,我错了两次

我想量化的正是这件事:agent 在动手之前,会不会先说一句话告诉你它要干嘛。

这个指标我叫它「叙述率」。听起来简单,实际上我量了三次,前两次都错了。

第一次:我定义为「在同一次模型响应里,第一个工具调用之前有没有文字」。算出来 Claude Code 只有 15%,和 Kimi 的 21% 差不多。结论:三家趋同,没差别。

我把这个结果给自己看的时候,觉得不对劲——我每天都在用,我明明看到它经常先说话

第二次:我意识到问题了——文字经常在上一条响应里单独发出,下一条才是工具调用。按响应切片,会把用户明明看见的叙述算丢。于是我改成按「屏幕上出现的顺序」摊平来算。结果:Claude Code 94.1%,Kimi 31.6%。结论反转:差别巨大。

然后第二次也是错的。

我的脚本漏掉了一件事:没有把「工具执行结果」计入序列。于是被结果隔开的多次工具调用,被错误地合并成了「一批」,一个 turn 开头说了一句话,就被算成整轮都有叙述。

同一个会话文件,错误算法给 96.2%,正确算法给 36.7%。

第三次,我终于做了应该第一步就做的事:把真实序列打印出来,用眼睛看。

思考 → Read → 结果 → 思考 → Read → 结果 → 思考
文字「现在改 sync_service.py,共四处:」  ← 一句话
Edit → 结果 → Edit → 结果 → Edit → 结果   ← 连着改三处,没有解释
思考 → Bash → 结果 → Bash → 结果 ……

真相是:Claude Code 不是每次动作前都解释,而是「一段工作前说一句,然后连着干三四件事」。

三格漫画:戴 AR 眼镜、穿白大褂的角色先困惑地看着一张卡片,接着张开双臂欢呼,最后捂脸,卡片掉在脚边

7.3 最终数据

统一口径(把主链摊平成「文字 / 思考 / 工具 / 结果 / 用户」五类元素,逐个工具调用看它前面紧挨着的是什么;「思考」指模型输出前先生成的那段推理草稿,界面上通常折叠显示),约 2.4 万次工具调用:

严格叙述率含思考的「有解释」每句话覆盖几次工具开工前说话(turn 级)
Claude Code · opus-4.8(上一代)69.6%79.0%1.390.4%
Claude Code · opus-526.7%60.7%3.294.1%
Claude Code · fable-514.3%55.0%4.269.4%
Claude Code · sonnet-517.3%78.5%4.012.2%
Kimi Code · k317.7%55.2%3.988.7%
Kimi Code · deepseek-v415.9%27.1%4.966.7%
Codex99~100%

在你相信这张表之前,先看这几条

这张表是我一个人、一台机器、三个多星期的使用记录跑出来的。它能支撑的结论比它看起来的少,我把限制先摊开:

带着这几条,再看三个结论。

结论一:同代模型下,Claude Code 和 Kimi Code 没有区别

Claude Code 配 fable-5 是 14.3%,Kimi Code 配 k3 是 17.7%。算上思考流,「有任何解释」的比例是 55.0% vs 55.2%——几乎完全重合。

我最初那个「Kimi 不如 Claude Code」的直觉,在同代模型下不成立。

结论二:换模型的解释力,比换客户端大一个数量级

看第一行:上一代的 opus-4.8,严格叙述率 69.6%,平均每句话只覆盖 1.3 次工具调用——几乎是「说一句、干一件事」。当前这一代呢:主力三个(fable-5、sonnet-5、k3)掉到 14~18%,opus-5 居中在 26.7%,每句话覆盖三到五次工具。跨两家产品同步发生。

但注意两点,它们让这个结论比「模型集体变沉默」更微妙:

其一,前面说过,「模型」是个复合变量——那个直连 71%、过网关 0.3% 的反例说明,供应商下发的提示词和请求路径足以单独压过权重本身。所以准确的说法不是「模型的性格变了」,而是「模型 + 它的服务方式」这个整体变了

其二,看「含思考」那一列:sonnet-5 的解释率 78.5%,和上一代 opus-4.8 的 79.0% 几乎一样。这一代模型未必是变沉默了——更像是解释从可见正文迁进了思考流,而界面没有跟着迁。 你不是没被解释,你是得去折叠区里找。

我的「问题感」形成于用上一代模型的时期——我拿记忆中的 Claude Code,去比了当下的 Kimi。归因错了变量。 这条结论有一个很实际的推论:你读到的任何一篇「A 产品比 B 产品更会解释」的横评,如果没有固定模型(以及模型的服务路径),测的都不是产品。

结论三:只有 Codex 把这件事写进了协议

看最后一行:Codex 在「开工前说一句话」这个维度上是 99~100%

Claude Code 呢?配 opus-4.8 时是 90.4%,配 sonnet-5 时掉到 12.2%同一个产品,换个模型,塌了。

这就是 7.1 那个问题的答案:当一件事只能靠请求模型配合时,你确实还有别的办法——别请求它,改协议。

(先交代清楚这个数字的边界:我测到的 214 个 Codex turn 全部跑在 GPT-5 系模型上,而这套协议正是为它们设计的——协议的履约方终究还是模型。碰上一个天生不配合的模型,这套机制表现如何,我没有数据。所以准确的说法是:Codex 是七家里唯一把「过程说明」从提示词升格成协议的,并且在它自家的模型上,这个保证是兑现的。)

四种「开工前先说一句」做法的对比图:协议保证 99–100%、随模型漂 90% 到 12%、方言化 13 份提示词、状态差分只看 diff

7.4 解法类型一:写进协议(Codex)

先把「协议」这个词说明白:大白话讲,它是模型和客户端之间约定死的数据格式——输出分成哪几个盒子、每个盒子怎么处理,写在接口规范里,不靠商量。GPT-5 系模型有一个特性:输出分通道(分析 / commentary / 最终答案)。Codex 把「工具调用前的意图说明」绑定到 commentary 通道上,于是它成了协议的一等公民,而不是提示词里的一句请求。

为什么这很重要? 提示词里的请求只能被遵守或被忽略,而通道是有类型的——一旦某段输出被标成 commentary,整个系统都能对它区别对待:commentary 消息可以被打断而最终答案不行;派生子 agent 时只继承最终答案,过程播报不会污染子 agent;界面按「这个模型支不支持这个通道」走两套渲染分支——模型不配合时,产品还有第二套方案

最能说明问题的是一个细节:「preamble」这个词在 Codex 新版提示词里出现了 0 次。 老版本里有整整一节讲它,新版一个字没有。因为它不再需要靠提示词请求了——它已经变成了架构。

(一个反面注脚:Codex 界面上那个「思考」区域并不是模型真正的思维链——推理本体是加密回传的,我的记录里只有少数请求附带明文摘要,比例随模型从 0% 到 31% 不等。可观测性不是「有没有东西在滚」,是「滚的那个能不能让你做判断」。

7.5 解法类型二:把叙述风格写成模型族的方言(OpenCode)

OpenCode 给了一个我没想到的答案:它有 13 份不同的系统提示词,靠模型 ID 的字符串匹配来路由,而且这些提示词里的叙述规则互相矛盾——给 GPT-4/o1/o3 的那份要求「每次工具调用前都说一句」,给其他 GPT 的要求「不要叙述常规读取」,给 Gemini 等的默认那份要求「绝不 preamble」,而 Claude 系走的那份,一条叙述规则都没有

同一个产品,对不同模型下相反的指令。这条路线承认了一件事:提示词不是「产品的声明」,而是「对某个模型的调教」,本来就该按模型分家。代价是十三份文件各自维护。(Codex 更彻底,它把提示词做成了可远程热更新的服务端资产——提示词能被热更新,本身就说明它有多不稳定。

于是三种形态齐了:协议保证(Codex,写进架构,稳)、随模型漂(Claude Code / Kimi Code,靠界面补偿,模型一换就变)、方言化(OpenCode,按模型族分别调教,用维护成本换稳定性)。

7.6 解法类型三:根本不看过程,只看差分(Cursor)

前面两条都在想办法「让用户看见过程」。Cursor 的答案是:不看过程,只看结果的差分。

在编辑器里你看到的是 diff:哪几行被改了,逐块 accept 或 reject,还能 checkpoint 回滚。审批单位不是「一次工具调用」而是「一个代码块」。这条路线有个前两条都没有的优势:它不依赖模型配合——模型再沉默,diff 都在那里,一行不少。

但它的适用区间有边界,而 2026 年最有意思的事实是双向收敛:Cursor 长出了命令行和云端 agent,因为它承认任务跑十分钟、改二十个文件时盯着 diff 看不经济;终端阵营则反过来在补 diff 预览。「diff 即可观测性」只在「同步、高频、人在环内」这个区间占优;自治度一拉高,两条路线都会收敛到「计划先行 + AI 审查 + PR 验收」。

7.7 为什么这个痛感只咬住一部分人(我就是那部分人)

到这里还缺一块拼图:如果同代模型下两个产品的叙述行为几乎一样,为什么的体感这么强烈?

答案在我自己的两个使用习惯里,它们叠在一起,正好把这个问题放大到最大。

第一,回到开头那句:我装 Kimi Code,是冲着 K3 这个模型去的,不是冲着这个产品去的。 这其实是绝大多数人选客户端的真实逻辑——你想用哪个模型,就装哪家的壳。于是「换产品」和「换模型」在体感里天然是同一个动作,而这两个变量的解释力差了一个数量级。我的归因错误不是偶然,它是这种选型方式的必然副产品。

第二,我用 agent 一律开完全访问。 我机器上那份 Kimi Code 配置的第一行就是 default_permission_mode = "auto"——我体验到的「默认」,本来就是全自动档。我从来不逐条审批。

第二条才是真正的关键,因为它触到了一个很少被说破的事实:

在逐步审批模式下,审批弹窗本身就是一种强制可观测性。 每一条命令在执行前都会被摊开在你面前,参数、路径、要动哪个文件,你想不看见都难。你不需要模型「愿意解释」,因为系统在替你按了暂停键。

而一旦你开了完全访问,这层可见性是你自己主动关掉的。 剩下能看见什么,完全取决于界面愿意主动展示什么、以及模型愿意主动说什么。

所以这是一笔明码标价的交易:你用可见性换速度。越自治,你就越依赖产品的主动叙述能力。

而这一代模型恰好把主动叙述从 69.6% 降到了 14~18%。

两件事叠在一起,痛感才被放大:我关掉了强制可观测性这一层,同时模型又把自愿可观测性这一层降到了历史低点。中间没有任何东西接住。这就是那个「不知道它在干嘛」的完整因果链——它不是某个产品的缺陷,是一个用法和一个模型代际迎头相撞。

顺便,这也解释了我第二次归因为什么会错成那样:我怀疑「auto 模式刻意隐藏、yolo 才显示」,可我的对比从头到尾都发生在两个全自动档之间。真正被关掉的那一档(逐步审批),我压根没进去过。

7.8 那我最初的直觉,到底对不对?

对了一半,而且错的那一半错得很有价值。

我的判断裁定
「执行期间看不到在跑什么」❌ 假。工具卡片在模型还在吐参数时就上屏了
「auto 模式刻意隐藏、yolo 才显示」❌ 假。逐帧实拍对比,两种模式渲染完全一致
「Claude Code 每步都解释,Kimi 不」❌ 对当前版本不成立(同代模型没差别)
「难以及早干预」这个痛感本身,但成因是模型代际 + 子 agent 折叠

关于最后一条:Kimi Code 派出子 agent 后,子 agent 内部的工具调用不会单独显示,只在父卡片上滚动显示最近 4 条。而 OpenCode 把子 agent 做成了可以进去的一等会话(能在父/上一个/下一个之间导航,各自的用量都能看)。

这才是我真正感受到的那个盲区——只是我一开始把它归因给了 spinner,第二次归因给了权限模式,两次都错。

对你意味着什么:如果你觉得「看不清它在干嘛」,先别换客户端——先看三件事:你在用哪一代模型(在客户端里输 /model,或看状态栏,都能看到当前模型名)、你开的是哪一档权限、你的任务里有没有子 agent。这三个变量的解释力,都比产品选择大。而如果你确实需要「必须能看见」(比如帮别人改代码、或者在生产仓库里干活),那么在终端阵营里,只有 Codex 把这件事写进了协议——至少在它自家模型上,这个保证是兑现的。

这一刀的结论

  1. 「让用户看见」是唯一一个架构管不到模型嘴里的问题。四类解法:协议保证(我测到的范围内最稳的)、随模型漂(最普遍的)、方言化(务实的)、只看差分(绕开的)。
  2. 实测 2.4 万次工具调用:产品间的差距,远小于「模型 + 提示词 + 服务路径」这个复合变量的差距。横评不固定这些,测的就不是产品。
  3. 可观测性的痛感是自治度的函数:逐步审批时弹窗替你强制看见,全自动时你只剩模型自愿说的那一点——开完全访问不是错,但你得知道自己交易掉了什么。而可观测性也不等于「屏幕上有东西在滚」,它等于「你能不能在跑偏的第三十秒做出判断」。按这个标准,行业整体还没及格。

第八部分:一条隐形的第一约束

前面五刀有个没解释的共同点:为什么大家都对「动上下文」这么小心?因为有一条约束凌驾在所有设计之上,而它很少被写进文档。

8.1 什么是 prompt cache

每次调用都要把全部历史重发一遍,这太贵了,所以有了缓存:如果这次请求的开头和上次完全一样,那部分就不用重新计算,价格通常只有原价的十分之一。

关键词是「完全一样」,而且是前缀匹配——只要开头有一个字节不同,从那往后的缓存全部失效。历史只能往后长,改中间等于全废。

8.2 这个约束有多强

我从自己的会话记录里统计:缓存命中的 token 占全部输入的 98.2%,平均每次请求输入 27 万 token。也就是说:缓存一旦失效,成本会变成十倍左右。

于是它成了架构的隐形老大。最直白的证据在开源的 Codex 里:工具明明是并行跑完的,结果却必须按固定顺序回灌历史——乱序回灌不影响正确性,但会让两次请求的前缀不再一致,缓存全废。一个纯粹为了缓存而存在的设计约束,写在代码里。

社区流传的那份 Claude Code 早期源码里还有一个我特别喜欢的细节(同样当野史参考):读文件的行号格式从 ' 1→' 改成了 '1\t',理由写在注释里——前者每行多 9 个字节,乘上全世界的读文件次数,估算占了全网未缓存输入的 2% 以上

一个看似纯属「审美」的格式选择,背后是一笔真金白银的账。

戴 AR 眼镜、穿白大褂的角色抱臂站在一小块露出水面的冰山尖上,水下是大得多的冰体

回头看前五刀,很多选择一下子就说得通了:为什么 Claude Code 宁可把大结果落盘换成一个指针,也不去重排上下文——同一个原因:前缀不能变。 这也解释了为什么同样长的对话有时便宜有时贵:贵的那次多半是你(或压缩机制)动了历史的前面部分


第九部分:一个真实的 bug,和它背后的道理

Kimi Code 曾经有个实验功能叫「微压缩」,会往事件账本里写一种特定类型的记录。后来功能下线了,代码删了,但老用户的账本里,那些记录还在。于是用新版本打开老会话、重放到那条记录,引擎在「我认识的记录类型」里查不到,就走进「未知记录,报错」的分支——有个用户报的 bug 里,这个报错出现在账本第 1035 条上,意味着一次恢复能刷出成片红字。而「跳过这条记录」本来就是正确行为,代码只是把「计划内的跳过」和「账本损坏」混为一谈了。我提了个十几行的修复(MoonshotAI/kimi-code#2211),在正式版上复现、打上补丁后同一个会话恢复干净。

这个 bug 讲了一个通用的道理:删除一个功能,比添加一个功能难,因为数据比代码活得久。你的历史数据是用旧词汇写的,而你的新代码只认识新词汇——这就是「事件重放」那条路线的真实账单:买到完整可审计的历史,就必须永远向后兼容自己写过的每一个词。


第十部分:所以我该用哪个

不排名,按场景说;括号里是你要付的代价。

选型路牌图:六块路牌分别写着——要安全边界选 Codex、要成熟生态选 Claude Code、要盯每一行选 Cursor、要能改全栈选 Kimi 或 OpenCode、要极简选 Pi、要兼容工具选 Grok Build

最后一句实话:这七个产品的差距,比你想象的小;模型代际的差距,比你想象的大。 纠结换哪个客户端之前,先确认你在用哪一代模型。


尾声:这篇文章错了三次

我把它们列出来,因为这才是这次调查里最值钱的部分。

第一次:我以为发现了 Kimi Code 的可观测性缺陷,写好了 issue,被一次两分钟的实际操作证伪。教训:用源码推断代替实际观察,是最容易犯也最尴尬的错。

第二次:我用「按 API 响应切片」的口径算出「三家没差别」,被自己的使用体感击穿。教训:统计结论和重度用户的体感打架时,先怀疑口径,而不是怀疑体感。

第三次:我以为修正过的口径就是对的,结果修正过头了——漏算工具结果,把 36.7% 算成了 96.2%。教训:修正后的口径同样需要验证。

而且即便是第三次的口径,也有它看不见的东西——7.3 里那几条局限,就是我现在还欠着的账。

三次里,终结争论的手段只有一个:把真实数据打印出来,用眼睛看一遍。

不是更复杂的统计,不是更多的源码,就是「打开看一眼」。

我想这也是使用 AI 的隐喻。它会给你看起来很完整的答案——有数据、有口径、有引用,无懈可击。你唯一能做的,就是去看一眼真实世界,然后问:这和它说的一样吗?


附:本文的数据与方法


张玉魁的头像

张玉魁 / Ktao

临床医学在读。白天学医,其余时间把真实企业的客服、内容、数据和报表变成每天在跑的自动化系统。

评论

0 / 200
评论

加载中…