让 AI 改一段话,其实比想象中难
给 agent 加个改写功能,本以为顺手,结果掉进 str_replace、Fuzzy Patch、Fast Apply 三条路里。没有最佳实践,只有 A/B test。
最近想给 agent 加个功能:让它能改一段已经写好的代码(或文字)。听起来简单得不像话——你都能让模型从零写一整篇了,改一段还不是顺手的事。
结果一猛子扎进去,发现事儿多了,根本没有什么一招鲜的"最佳实践"。目前圈子里其实就三大流派。
第一派:str_replace,拿 token 换稳定
先说最直觉的做法,Anthropic 官方推荐,也是 Claude Code 在用的:str_replace(字符串替换)。
让模型把"要改的老代码"原样吐出来作为锚点,再吐一段"新代码",本地拿个脚本做唯一匹配和查找替换。干净,纯 API 就能跑,不依赖什么底层设施。
但这招的代价是极其浪费 token。为了保证匹配的唯一性,你常常得让模型把一整段甚至整个函数体当 old_str 抄一遍。Claude 能玩得转,是因为 3.5 Sonnet 脑子好使、上下文够大且相对便宜。官方的底层逻辑很明白:既然 LLM 这种自回归模型天生数不准绝对行号,那干脆拿 token 消耗换取 100% 稳定的上下文定位。
第二派:Fuzzy Patch,靠模糊搜索兜底
那不想费那么多 token 怎么办?这就到了第二派,OpenAI 早期和现在当红开源神器 Aider 走的路:容错补丁(Fuzzy Patch)。
很多人有个误区,以为输出 Diff 就是在后台静默调用系统的 patch 命令。真用过就知道,标准的 GNU patch 极其脆弱,模型但凡多吐一个空格或者把缩进搞错一点,系统命令就直接报错退出。
所以 Aider 它们搞的是一套"变种":用自定义的 SEARCH/REPLACE 块来描述变更,刻意不依赖精确的行号。模型只管输出局部变更,拿到本地后,用 Python 脚本去做带一定宽容度的"模糊匹配"。就算模型把上下文锚点抄错了一点,脚本也能强行揉进去。这派主打一个"用本地工程算法,给模型的粗心兜底"。
第三派:Fast Apply,砸钱改底层
上面这俩,都是纯靠调 API 就能玩的。但你要去看大厂 Cursor,人家走的是降维打击的第三条路:投机解码(Fast Apply)。
Cursor 能秒改上千行,网上常有人猜它是在本地跑了什么牛逼的 AST(语法树)对齐算法。错,真正的大杀器在云端。它把投机解码(Speculative Decoding)玩出了花:本来这技术是用小模型猜 token 让大模型验证,Cursor 根本不让小模型猜,直接把用户硬盘里的"原文件"当草稿喂给云端微调过的大模型。
于是过程就成了这样:引擎拿原文一段段往大模型嘴里送,模型一看,"对对对这就我写的",唰一下几十上百个 token 全过,飞快;直到碰上你要改的那块,模型说"不对,这儿我要写点别的",它才停下来,老老实实逐字生成新内容。改完,再找到和原文重新对上的位置,继续狂飙。
听着完美,但这招普通人走不通——你得自己能改推理引擎,能直接碰底层的 KV Cache。纯靠调 API 的人,连门都摸不着。
那个被淘汰的盲狙派
至于以前那种"把文档抽象成坐标,告诉模型改第三行"的纯盲狙做法,真跑起来极其吃具体场景,容错率太低,主流落地方案里反而见得少。
三板斧,纯 API 玩家只能选前两个
所以看下来,底层其实就三板斧:要么像 Claude 拿 token 换绝对稳定,要么像 Aider 靠模糊搜索擦屁股,要么像 Cursor 砸钱改底层基建。纯 API 玩家只能在前两个里选。那就是骡子是马,拉出来遛遛,跑 A/B test 吧。