我把 Claude Code 的模型换成了扩散模型:28 次运行全过,但它的卖点花不出去

把 Claude Code 的模型后端换成 Inception 的 Mercury 2 扩散模型,跑 9 个带隐藏测试的编码任务、共 28 次运行全过。真正的发现是一笔账:agent 负载的输入输出比是 82:1,输出侧只占总成本 4.2%——扩散优化的那一侧,在这里花不出去。

昨天那篇 自回归不是物理定律 算的是一笔理论账:扩散语言模型省下的解码步数,是从哪里借的。

今天我把账拿到真实工作负载上验。做法是把 Claude Code 的模型后端整个换掉——它还是那个 harness,Bash/Read/Edit 工具一个没变,但每一次 LLM 推理都打到 Inception 的 Mercury 2(一个在售的扩散语言模型)上。然后跑 9 个带确定性校验的编码任务,重复到 28 次运行。

28 次全过。而这不是重点。 重点是账单:最后一轮完整跑下来,输出 token 只占总成本的 4.2%。扩散模型花大力气优化的那一侧,在 agent 场景里根本不是成本和延迟的主项——它的核心卖点,在这里没有花出去的地方。

名词速查

术语一句话解释
dLLM(扩散语言模型)不按从左到右逐个吐字,而是并行地把一整段文字从”模糊”改到”清晰”的语言模型,详见 扩散语言模型那篇
harness包在模型外面那层代码:给它工具、喂它上下文、接住它的输出。同一个模型换个 harness 性能能差几倍,见 什么才是好的 Harness
prefill / decode推理的两个阶段:一次性读完整段输入 vs 逐段生成输出。扩散优化的是后者
Messages APIAnthropic 的对话接口格式,和 OpenAI 的 chat completions 格式不兼容
SSEServer-Sent Events,服务器通过一条长连接持续推送片段的协议,流式输出用它
tool_use / tool_calls模型”我要调这个工具”的结构化输出。Anthropic 叫 tool_use 块,OpenAI 叫 tool_calls 数组
reasoning token模型内部推演消耗、但不出现在最终答案里的 token。要付钱,且要占 max_tokens 额度

为什么值得这么折腾

想知道一个模型”能不能干活”,最有信息量的测法不是问它题目,是把它塞进一个已经调好的生产级 harness 里,看它能不能在真工具、真文件系统、真多轮循环里把活干完

Claude Code 恰好是个很硬的测试台:它的系统提示词长、工具多(这次实测每轮带 24 个工具定义)、循环深。一个模型在这里能跑完,说明的东西比刷榜多。

而它支持 ANTHROPIC_BASE_URL 指向自定义端点——理论上可以换后端。理论上。

第一道坎:协议不兼容

Claude Code 说 Anthropic Messages API,Inception 说 OpenAI chat completions。本机没装任何现成的翻译层(litellmclaude-code-router 都没有),所以我写了一个约 500 行的 Python 标准库代理。

翻译的主体部分是机械的,值得写下来的是三个把我卡住的坑——它们都不在文档里。

坑 1:SSE 流没有分帧,客户端干等 600 秒

第一次跑最小任务,verify 显示文件已正确创建,但 claude 进程又挂了整整 600 秒,最后报 API Error: The operation timed out

根因是 HTTP 层:我用 HTTP/1.1 返回 SSE,但既没有 Content-Length,也没做 chunked 分帧。

# 伪代码,对应代理里的流式响应分支
# 错的:客户端读完最后一个 SSE 事件后,不知道响应结束了,继续等
send_header("Connection", "keep-alive")

# 对的:SSE 无法预知长度、又不分帧时,必须用"关闭连接"来界定响应边界
self.close_connection = True
send_header("Connection", "close")
# ... 推完所有事件后主动 flush 并让连接关闭

可迁移的教训HTTP/1.1 下响应边界只有三种界定方式——Content-Length、chunked 分帧、关闭连接。流式接口用不了第一种,那就必须明确选后两种之一,不能什么都不选。症状会伪装成”上游模型很慢”,而实际是你自己的传输层没收尾。

坑 2:max_tokens 被内部推理吃光,返回空内容

最早的连通性测试我给了 max_tokens: 20,结果拿回:

{"choices":[{"message":{"content":null,"tool_calls":[]},"finish_reason":"length"}],
 "usage":{"completion_tokens":0,"reasoning_tokens":19}}

19 个 token 全花在内部推理上,一个字都没轮到输出。reasoning token 和输出 token 抢的是同一个 max_tokens 额度。 我在代理里加了 4096 的下限。

这个坑对任何”带内部推演的模型 + 老的 max_tokens 直觉”都成立:一个在非推理模型上够用的额度,换到推理模型上会变成静默的空响应——finish_reasonlength 而不是报错,很容易被当成模型抽风。

坑 3:流式响应默认不回 usage,成本失明

第一轮 226 次调用跑完,我去统计成本,发现 token 数全是 0——Inception 的流式响应里根本没有 usage 块。非流式有,流式没有。

解法是 OpenAI 的标准可选参数,加上就有了:

{"stream": true, "stream_options": {"include_usage": true}}

顺带还多了个 server_timing.server_latency_ms,让我能把服务端延迟和网络往返分开看。

教训:自建 LLM 代理时,成本可观测性不是自动来的。流式路径要单独确认 usage 有没有被上报,否则你会在完全不知道花了多少钱的情况下跑完整轮评测——我就是。

第二道坎:怎么设计一个不骗自己的评测

模型自己写实现、又自己跑测试判断”我做完了”,这就是 独立验证真空 那篇讲的问题:写与测同源时,测试从独立证据退化成自我确认。

所以每个任务我都加了 harness 之外的、模型看不到的校验:

  1. 确定性 verify.sh:跑在 agent 结束之后,不由 agent 触发。
  2. 防篡改检查grep 确认它没改测试文件。
  3. 通用性检查:用测试里没出现过的输入独立验证,防止它硬编码测试值。
  4. 隐藏测试套件(L8 专用):可见的只有 3 个 smoke test,判分用的 37 个用例放在工作区之外,agent 全程看不到。而且我先用一份参考实现跑过隐藏套件确认全绿——评分标准本身也要被验证,否则你测的是自己写错的题。

九个任务按难度递增:

编号任务考什么
L0写一个内容精确的文件链路连通性
L1按 7 个测试实现 3 个函数单文件编码
L2跨文件找出折扣分档的边界 bug定位非本地的根因
L3--json 参数,且不许改受保护文件、要更新 changelog多约束指令遵循
L4两个互相耦合的 bug(可变默认参数 + 缓存未失效)会不会修一个就交卷
L5跟随现有代码库约定新增一个配置项(9 个测试)多文件导航 + 模仿既有模式
L6重构去重,但禁止使用正则反直觉约束下的重构
L712 个文件里长程改名,且必须放过一个同名的线协议字符串常量长程一致性 + 陷阱识别
L8按规格实现解析函数,用隐藏测试判分抗”拟合可见测试”

L4–L8 各重复跑 3 次,L0–L3 各 1 次,加上最后一轮 9 个任务的完整重跑,共 28 次计入统计的运行(协议代理修好之前的调试运行不计入)。

结果:28/28

全部通过。这是我亲手跑出的数字,校验脚本和运行记录都在本地。

任务通过轮次范围耗时范围
L02/223.0–5.2s
L12/28–914.4–14.8s
L22/28–1011.6–24.9s
L32/27–1113.8–27.6s
L44/46–810.0–20.6s
L54/414–1525.1–33.9s
L64/47–1314.2–27.9s
L74/423–4052.5–93.5s
L84/44–1010.9–28.8s

代码质量不是勉强及格。L8 那份实现连”数字和单位之间不许有空格”这条藏在规格里的隐含规则都处理对了——它是靠”解析数字时遇到空格就停下,此时下一个字符必须是单位”这个结构自然满足的,不是特判出来的。L6 在禁用正则的前提下老实写了字符级循环,并把公共逻辑抽成了带 fallback 参数的私有函数。

先接住一个合理的怀疑:28/28 说明我的题目没有触到它的上限,不说明它没有上限。这套评测测出的是”能不能在生产级 harness 里稳定干完中等难度的活”,答案是能;它测不出”和前沿模型比差多少”。想测后者需要难得多的题目,那是另一篇的工作。

真正的发现:这笔账里,输出侧只占 4.2%

拿到 usage 之后,最后一轮 9 个任务的完整开销是这样(一手实测,官方定价来自 API 的 /v1/models 响应:输入 0.25/M、输出0.25/M、输出 0.75/M、缓存读 $0.025/M):

数值
LLM 调用次数107
累计输入 token1,593,388(其中命中缓存 302,391,19%)
累计输出 token19,329(其中 reasoning 11,903,占 61.6%)
单次调用平均输入 14,891 tok,输出 181 tok
输入 : 输出82.4 : 1
总成本0.3448(每任务0.3448(每任务 0.0383)
输出侧占总成本4.2%
服务端延迟p50 801ms / p90 2821ms / max 7038ms

这个 82:1 是这篇文章的核心。它是结构性的,不是我这套任务的偶然:agent 每轮都要重发整段对话历史、24 个工具定义、长系统提示词,而模型一轮通常只回一个工具调用——一百多个 token。轮次越多,这个比例越极端。

于是扩散模型的价值主张在这里遇到了尴尬:

  • 成本上:它优化的输出侧只占 4.2%。就算解码免费,账单也只降 4.2%。
  • 延迟上:181 个输出 token,即使解码时间归零,能砍掉的也只是延迟里对应这 181 个 token 的那一小段。p50 的 801ms 主要花在读那 14,891 个输入 token(prefill)和网络往返上。
  • 而 prefill 本来就是并行的——自回归模型读输入时也是一次算完整段,扩散在这一侧没有结构性优势。

昨天那篇算出扩散省下的步数是”从质量预算里借的”。今天这笔账补上另一半:在 agent 负载下,它省下的那个东西本身就不值钱。 输出快 5–10 倍(这是 Inception 在自家模型描述里的说法,我没有独立复现速度对比)作用于一个只占 4.2% 成本的项。

这里必须说清一个我的口子:我的代理把 Claude Code 发来的 prompt cache 断点全丢掉了,只吃到了 Inception 自己的自动缓存,命中率 19%。如果把缓存做好,输入侧成本会明显下降,4.2% 这个比例会相应上升。所以 4.2% 应当读作”输出侧占比”的下限估计。但要注意:token 数量比 82:1 不受缓存影响——缓存改变的是价格,不是你得往模型嘴里塞多少东西。延迟和轮次的结论也一样成立。

失败模式:两个值得记下来的

一、工具名幻觉,4/98 ≈ 4.1%,且全部集中在最长的任务里。

L7(12 文件长程改名)中,它连续做了三次越界调用:

Search({"query": "fetch_user", "path": "", "max_results": 20})
WebSearch({"query": "fetch_user python function"})
web_search({"query": "fetch_user python function"})

Searchweb_search 不是 Claude Code 的工具名(对应的真名是 GrepWebSearch)——它编了工具。更有意思的是中间那次:它想上网搜一个只存在于本地代码库里的函数名。这不是格式错误,是把”我需要找东西”错误地映射成了”我需要上网”。

这三次都发生在同一个任务、上下文最长的时候。我的判断(观点,非实测因果):这是长上下文下工具选择退化的表现,而不是随机抽风——因为在短任务里 0 次发生。要确认这一点需要更多样本。

harness 在这里救了它:不存在的工具名被拒绝,它换回 Grep 继续干活,最终照样通过。这正好是 判断归模型、物理归代码 那篇的意思——harness 里的约束是物理法则,模型犯错也撞不穿。

二、长程任务的轮次方差极大。

L7 四次运行的轮次是 23 / 28 / 30 / 40。最后那次吃满了我设的 40 轮上限,耗时 93.5 秒(是最快那次的 1.8 倍),才刚好在上限前完成。

这意味着:给这类模型做长程任务时,轮次上限是个危险参数。 短任务 2–10 轮很稳,长任务的尾部会甩得很远。如果我把上限设成 30,那次运行就会被截断而失败——同一个模型、同一道题,成功率取决于我拍的那个数。

另外,第一轮 226 次调用里有 3 次网络层瞬时失败(1.3%),被 Claude Code 自己的重试吸收了,没有影响任何任务结果。

那什么时候该用扩散模型

不是”不该用”,是用在输出侧真的占大头的地方

  • 值得:长文生成、批量翻译或改写、代码补全(输入短、输出长、延迟敏感),以及任何输入输出比接近 1:1 或反过来的负载。
  • 不值得(按这次实测):多轮 agent 循环。输入主导,扩散优化的那一侧占比太小。
  • 需要另测才知道:输入很长但输出也长的场景,比如”读一大段代码然后重写整个文件”。我这套任务里模型多数走的是 Edit(小改),没怎么走整文件重写。

一个更一般的提炼(这是我的观察,不是定律):评估一个模型优化方向对你有多大价值,先量一下你自己负载的输入输出比。 这个比值决定了厂商宣传的那个倍数会被乘上多小的一个系数。我在动手之前完全没想到它是 82:1。

诚实的提醒

  • 亲手测出的:28/28 通过率、各任务轮次与耗时、107 次调用的 token 与成本、p50/p90/max 服务端延迟、4.1% 越界工具调用、1.3% 网络失败率。都来自本地运行记录。
  • 来自厂商自述、我没有独立复现的:“比 Claude 3.5 Haiku、GPT-4o Mini 快 5–10 倍""Copilot Arena 速度第一、质量并列第二”——这两句来自 Inception 在 /v1/models 响应里给 Mercury 2 写的模型描述,我没做速度横评。
  • 我的观点,非实测因果:工具名幻觉集中在长上下文是”长程退化”而非随机。
  • 明确的局限:只测了一个模型(mercury-coder 系列在这个账号上已不可用);任务是我自己出的,没有触到能力上限;没有和自回归模型做同 harness 对照——那才是最该补的实验。

成本最低的亲手验证实验(约半小时):不用写代理。直接对你平时用的 agent 工具抓一次真实会话,把每次请求的 prompt_tokenscompletion_tokens 加起来,算出你自己的输入输出比。然后拿这个比值去重新读任何一份”输出速度提升 N 倍”的宣传——你会得到一个和宣传数字很不一样的、属于你自己负载的预期收益。

参考来源

工程实践

  • Inception Labs API(/v1/models/v1/chat/completions,含 Mercury 2 的定价、上下文长度与厂商自述性能描述)
  • Anthropic Messages API 与 OpenAI chat completions 的流式事件格式差异(本次翻译代理的实现依据)
  • Claude Code 的 ANTHROPIC_BASE_URL / CLAUDE_CONFIG_DIR 自定义端点与配置隔离机制

站内相关