2645 字
13 分钟
以 OpenCode 为目标优化 CodingAgent 成本

以 OpenCode 为目标优化 CodingAgent 成本#

这次优化的目标是让自研 CodingAgent 的运行方式逐步向 OpenCode 靠近:更少的闲聊输出,更清晰的工具协议,以及更少由工具失败带来的重复 model call。MBPP-53 的分数只是观察优化效果的一个结果,真正关注的是运行成本和执行路径。

MBPP-53 是一个很适合暴露 Agent 运行成本问题的测试集。它有 53 个相似但不完全相同的 Python 编程任务,每个任务都需要读题、写 solution.py、运行公开测试,并在失败后继续修复。这个过程天然会放大几类成本:重复读文件、重复解释、重复测试、过细计划导致的串行执行、长上下文反复进入模型请求,以及文件编辑失败后的再尝试。

因此,这次优化更像一次围绕真实任务的 Agent 工程调优。OpenCode 在这里是一个参照目标:它更强调短输出、工具驱动、权限规则、任务清单和长会话管理。我的优化过程就是不断把本地 CodingAgent 从“自由探索式运行”收敛到“受约束工具调用式运行”。

一、先看成本问题在哪里#

早期运行中,Agent 虽然能完成不少任务,但过程非常昂贵。以一次旧 trace 为例:

指标旧运行 20260603-2237
model requests342
total tokens16,448,556
prompt tokens16,181,874
completion tokens266,682
自动压缩次数6

这里最明显的问题是请求次数太多。大量请求来自重复探索:模型不断输出解释、尝试额外试探、读取和编辑文件、再运行测试。即使 prompt cache 命中率很高,累计 total tokens 仍会迅速膨胀。

这类问题需要回到 Agent 行为本身解决:让它少说、少绕、少串行、少失败、少携带无效上下文。

二、优化一:收紧提示词,减少自然语言 content#

早期系统提示词对“如何协作”“如何解释”“如何总结”保留了较多空间。模型在执行任务时容易把思考过程、推导过程和阶段总结写进普通 assistant content。对于命令行或 Web Chat 里的 coding agent 来说,这些内容大多不会直接推动任务完成,却会进入后续上下文。

对应优化是收紧系统提示词,把回复风格改成更接近 CLI agent 的短输出策略:

  • 保持回复简短。
  • 可以用 1 到 3 句话回答时就不展开。
  • 工具调用之外的文本只保留用户真正需要看的内容。
  • 完成文件处理后直接停止,避免额外解释。

相关提交是 74552a1 chore(prompt): 收紧 agent 输出与失败处理规则

这一步降低的是 completion token 和后续上下文污染。少输出一段解释,既能省掉当前这段输出,也能减少后续每一次请求都会携带的历史内容。

三、优化二:提示词和工具描述都强调并行化#

MBPP-53 里大量任务彼此独立。早期 Agent 会倾向于“读一个任务、写一个任务、测一个任务、再进入下一个任务”。这种串行策略稳定但昂贵,因为每个小动作都可能触发一次 model call。

优化方向是在系统提示词和工具描述里同时强化并行化:

  • 多个独立信息请求可以一次返回多个工具调用。
  • 多个可并行读取的文件应该批量读取。
  • 多个独立任务的公开测试可以在一次 assistant turn 中返回多个测试工具调用。
  • 工具调度层把安全工具分块并行执行,把独占工具单独执行。

这类优化的关键是让模型在一次返回中提出更多独立 tool call。模型调用次数减少,工具执行仍然由运行时调度层控制安全边界。

四、优化三:提高单次最大输出,减少长文本续写#

在长任务里,模型可能需要一次性生成较长的代码、压缩摘要或修复说明。如果 API 的单次输出上限过低,就会出现 finish_reason=length,随后系统需要追加续写请求。续写请求不只增加一次 model call,也会增加截断恢复逻辑的复杂度。

因此我把模型输出上限统一提高到 32000,并让普通请求、自动压缩摘要请求和手动 compact 请求共享这个配置。相关提交是 8607494 将模型输出上限设为 32000

这项优化解决的是长输出被迫拆成多轮的问题。对于 MBPP 这种需要大量写代码的任务,减少一次续写就意味着减少一次完整上下文请求。

五、优化四:从 plan 迁移到 todowrite,减少过细计划带来的串行#

早期 plan 工具更像一个完整的任务状态机,包含 goalcurrent_item_idnote、失败状态、当前项更新等字段。这个设计对复杂任务很清晰,但在 MBPP-53 这种重复型任务里容易产生副作用:模型会把每个小任务、每次测试、每次失败都写成计划项,然后围绕计划项逐个推进。

结果是原本可以并行的动作被计划状态串起来。模型需要反复更新计划、查看当前项、标记失败或通过,model call 数量随之增加。

对应优化是迁移到 OpenCode / Claude Code 风格更接近的 todowrite

  • todo 输入只有整张 todos 表。
  • 状态更少,只保留 pendingin_progresscompletedcancelled
  • 工具描述强调只在 3 个以上概念步骤或非平凡任务中使用。
  • 不为每个文件、每个测试、每次重复尝试创建 todo。
  • 后续又把 todo 更新改成阶段级主动更新,减少细粒度状态抖动。

相关提交是 3fe5b65 feat: replace plan tool with todowrite46c33fa fix(todowrite): 改为阶段级主动更新

这一步的核心收益是减少计划管理本身消耗的 model call。任务清单应该帮助 Agent 保持方向,避免变成阻止并行执行的锁。

六、优化五:增强文件编辑工具,减少编辑失败重试#

MBPP 任务看起来只是写 solution.py,但文件编辑仍然会带来大量重复尝试。早期编辑工具如果要求精确匹配,模型生成的 old_text 只要有缩进、空行、引号或换行差异,就可能匹配失败。失败后模型通常会重新读取文件、重新生成修改片段、再次调用工具。这会增加 model call,也会让上下文里堆积大量错误信息。

另一个问题是读写顺序不清。覆盖已有文件前,如果没有明确的“先读后写”约束,模型可能直接覆盖文件,或者在不知道当前内容的情况下生成错误替换。

对应优化分成三步:

  • write_file 增加 guarded 行为:新文件可以直接创建,覆盖已有文件前必须先成功 read_file
  • edit_file 增加读前置校验,编辑已有文件前也需要先读过同一路径。
  • edit_file 增强匹配能力,支持换行归一、tab/space 归一、缩进整体偏移、行首尾空白、外层空行、转义文本和中英文引号差异等场景。

相关提交是 9f88157 feat(tools): add guarded write_file54b4207 feat(edit): 添加读前置校验519c950 feat(edit): 增强编辑匹配能力

这个优化对 coding agent 很重要。文件编辑工具越可靠,模型越少因为工具失败进入“读文件、解释错误、再编辑”的循环。

七、优化效果:从自由探索到受约束执行#

经过这些优化后,新运行的行为已经明显收敛。

指标旧运行 20260603-2237新运行 20260605-2039
model requests34237
total tokens16,448,5561,759,698
prompt tokens16,181,8741,693,902
completion tokens266,68265,796
自动压缩次数62

从调用次数看,优化后的运行把模型请求压到了原来的约一成。工具调用总量也下降了,但下降幅度小于模型请求,说明优化重点在于让 Agent 用更少的模型轮次批量提出更多有效工具调用。

下面四张图来自 Monitor 的 Step Analytics 页面,同一套图表分别切换到旧运行 20260603-2237 和新运行 20260605-2039 后截图。

工具调用曲线对比#

20260603-2237 工具调用曲线

图:旧运行 20260603-2237 的工具调用曲线。

20260605-2039 工具调用曲线

图:新运行 20260605-2039 的工具调用曲线。

从 token 结构看,主要下降来自 prompt tokens。completion tokens 也明显减少,但总成本的大头仍然是每轮请求反复携带的历史内容。因此,减少 model call 是这次优化里最直接的成本收益来源。

Token 曲线对比#

20260603-2237 token 曲线

图:旧运行 20260603-2237 的 token 曲线。

20260605-2039 token 曲线

图:新运行 20260605-2039 的 token 曲线。

这组数据属于工程现场对比,代码和工具协议本身在变化,任务现场也有差异。但它足以说明成本来源已经发生变化:旧运行的主要问题是自由探索和重复 model call,新运行的主要行为已经变成批量读写、规范测试和少量失败修复。

从工程角度看,这比单纯追求一次高分更重要。Agent 的成本优化来自系统性收敛:

flowchart TD
A["成本过高"] --> B["减少自然语言输出"]
A --> C["推动独立工具并行"]
A --> D["提高单次输出上限"]
A --> E["替换过细计划工具"]
A --> F["增强文件编辑工具"]
B --> J["更少 completion token"]
C --> K["更少 model call"]
D --> K
E --> K
F --> L["更少编辑失败重试"]

八、总结#

这次优化可以概括为一句话:把 Agent 从自由探索式运行,逐步收敛为受约束、可并行、少重试的工具调用系统。

提示词收紧减少了无效 content;并行描述减少了串行 model call;更高输出上限减少了长文本续写;todowrite 降低了计划管理成本;文件编辑工具增强减少了编辑失败重试。

这也是我理解中 OpenCode 风格最值得借鉴的地方:优秀的 coding agent 需要模型能力、工具协议和任务执行策略一起工作。成本下降来自整条运行链路的收敛。

以 OpenCode 为目标优化 CodingAgent 成本
https://blog.huangnv.online/posts/2665/1/agent------以-opencode-为目标优化-codingagent-成本/
作者
huangnv
发布于
2026-06-05
许可协议
CC BY-NC-SA 4.0