一、凭感觉调 Prompt,就是在赌
Prompt Engineering 被讲了两年,很多团队的日常还是很原始:写一版模板,让 AI 跑跑看,人读一读输出,觉得不对就改两句,再跑再改。
demo 阶段这么玩没问题,一进真实业务就撞墙。三个症状很典型:
- 不知道改动到底有没有用——改了一句话,下次产出看着顺眼一点,是真变好了还是今天运气好?
- 不知道往哪个方向改——产出不如意,是冗余多了?路径错了?还是粒度拆错了?凭阅读感受分不开
- 改着改着就跑偏——为修一个 case 改了模板,又引入三个新 case
根本矛盾是:**”感觉”不是一个可优化目标**。没有客观信号,就没有闭环;没有闭环,就不是优化,是押运气。
我做过一个多语言后台项目的 AI 代码生成工具链,跑了 24 轮迭代之后,把 AI 产出的冗余率从 81% 打到了 2%。

二、把它看成 RL 问题,把环境搭起来
我一开始也觉得换下一代模型就能解决。结果 81% 的冗余率摆在面前,幻想碎得很干净。
换个视角看,这件事本来就有一套现成框架。
把场景抽象一下:有一个”智能体”(AI + 当前模板)在做决策,它不知道决策好坏,要靠环境反馈(真实代码作对照)校准。我们想让它的策略越来越好,但模板是离散的自然语言,梯度下降不了。
这几个特征凑一起,正好是强化学习要解决的场景。RL 就是为”黑盒策略 + 可观测奖励”发明的:无需可导,用 reward 驱动策略迭代。
有人会说这是”反向传播式优化”。不准确——反向传播的前提是连续可微的计算图能把 loss 传回参数,自然语言模板没有这个性质。RL 才是对的框架。
这套映射的价值在于,它把一个模糊问题变成了一个工程问题:

视角一换,几件事立刻变清楚:
- 优化目标可量化:
reward = 1 − loss,有明确数字 - 改动可验证:A/B 对照替代主观感受
- 方向有层次:reward 可拆成多个维度,分别优化
- 长期有积累:高 reward 的轨迹能沉淀成 few-shot 记忆
环境:廉价可重复是底线
RL 的第一件事是准备一个能反复试错的环境。对”AI 写代码”这个问题,环境的最小形态就两样东西:历史数据 + 自动化脚本。
我搭的沙箱用了三样数据:几百个历史工单(覆盖 Go / PHP / Vue 多栈的真实需求)、数百个带完整 diff 的 MR(这是”标准答案”,最关键的监督信号)、二十多份完整需求文档(作为输入的”状态 S”)。
数据凑齐只是第一步。真正要紧的是把”生成 → 对比 → 打分”做成自动化流水线,否则 20 轮迭代能把人拖垮。
三个脚本承担全部工作:一个负责把工单灌给 AI 产出 spec + plan + code;一个负责把 AI 产出和真实 MR 做行级 diff 算 loss;一个负责对比两版模板的胜负表。
环境设计的底线就一条:必须廉价可重复。RL 要跑几十上百轮才有意义,每轮都要半天做数据准备就崩了。
这套沙箱从触发到看到 reward 分数不到 10 分钟。真实 MR 只读,避免训练集泄漏;对比前剥掉空白和注释,减少假阳假阴;每次产出都存档,方便回看错例。
环境就位了,下一个关键决策是用什么信号作为 reward。
三、三个 Reward 维度 + 一句话打掉 81% 冗余
让 AI 改两行代码,它给你 44 行——完整 package 声明、完整 import 块(20 行)、整个函数体(30 行其中只有 2 行变化)。这是”过度生成”:AI 默认把交付物当作”可运行的完整文件”,哪怕 95% 是你原本就有的。
要治这个病,得先能测它。我把 reward 拆成三个维度,对应三种不同的”不好”:

前两维 coverage_loss(漏改了多少)和 precision_loss(多写了多少)是主战场。关键洞察是:它们完全独立。
AI 可以同时漏改 + 多写,也可以只犯其中一种。把它们拆成两个 reward 信号,才能对症下药:漏改治不好精准度,多写治不好覆盖率。
第三维 granularity_loss 看 AI 把任务拆得太粗还是太细。一个大 task 该切三刀却只切一刀,就会在这里扣分。这个维度用得少一些,这里不展开。
血泪教训:reward 选错,所有迭代白做
最初 3 轮我用的是”文件清单级” reward——比较 AI 改了哪些文件 vs MR 改了哪些文件。跑 8 个样本总 loss 26.7%,”还行”。后来换成行级 reward,数字瞬间变脸:precision_loss 从 30% 飙到 86%。
为什么?因为文件级只测”改了哪些文件”,不测”文件内具体改了哪些行”。AI 贴 1000 行完整代码 vs 真实只改 2 行,在文件级下都算命中。
选对测量粒度比调模板重要,这和 RL 里”选对 reward function 比调超参重要”完全同构。就像分类任务只看 accuracy 会掩盖类别不平衡,换成 F1 / precision / recall 才看到真实表现。先验证你的尺子,再测量你的目标——这是所有优化工作的通用教训。
一句话约束,81% → 2%
行级 reward 就位后,跑 6 个真实样本一测,数字让我意外:coverage_loss ≈ 0%(该改的 AI 都改了),但 precision_loss = 81%。
AI 不是”能力不够”,是”话太多”。
根因就一句话:AI 默认把交付物当作”可运行的完整文件”。要改这个默认假设,在模板里加一条硬约束:
“Legacy 修改时只输出 diff 行,禁止重建完整函数或 import 块。AI 的任务是产出 git patch,不是从零写文件。”
就这一句话。然后跑 A/B:同一个样本,两版模板各跑一次,对比 reward。别凭感觉拍脑袋。
扩大到 6 个覆盖不同栈的样本,结果稳得让人发指:
| 场景类型 | 规模 | 旧模板 | 新模板 | 降幅 |
|---|---|---|---|---|
| Go 风控回退 | 1 文件 6 行 | 86.4% | 0.0% | -86.4pt |
| PHP 证件校验 | 2 文件 21 行 | 79.4% | 11.8% | -67.6pt |
| Go 批量告警 | 9 文件 44 行 | 77.3% | 0.0% | -77.3pt |
| Go 白名单 | 7 文件 59 行 | 63.5% | 0.0% | -63.5pt |
| 全栈 Go+PHP+Vue | 9 文件 149 行 | 95.2% | 0.0% | -95.2pt |
| Go FuncSwitch | 2 文件 10 行 | 85.4% | 0.0% | -85.4pt |
| 平均 | — | 81.2% | 2.0% | -79.2pt |
6/6 全部胜出,覆盖纯 Go、纯 PHP、全栈混合。这不是运气。
这个策略为什么泛化性这么强?因为它改的不是具体知识,而是 AI 的”交付形态假设”。
交付形态是跨语言、跨场景的。Go / PHP / Vue 里,”只输出 diff 行”都是同一个约束。
这里有一条 reward shaping 的核心经验:越抽象的约束越容易泛化,越具体的知识越需要字典兜底。前半句解决了 precision 的问题,后半句是下一节要讲的事。
四、另一半问题:知识问题得查字典
precision 解决后再看 coverage。在真实场景(AI 不知道答案)下,coverage_loss 经常飙到 80%+。原因简单粗暴:AI 猜的文件路径根本不对。
拿一个 PHP 员工证件校验需求举例。AI 预测了两个文件:app/Logics/employee/IdCardValidator.php、app/Http/Controllers/Api/employee/ModifyInfoController.php。
真实 MR 改的是另外两个文件:EmployeeController.php 和一个 Proxy Client。一个路径都没猜对。
这揭示了一个不同性质的问题——能力问题 vs 知识问题:
- 能力问题(上一节的过度生成)——AI 有能力做对,只是默认选错。靠 reward + 模板约束解决
- 知识问题(这里的路径预测)——AI 没办法凭空知道你们项目的路径约定。靠外部字典解决
归因的两个陷阱
先讲一个元方法:怎么判断某个 loss 该归到能力,还是归到知识。24 轮迭代里踩过两个坑:
- 把能力问题当知识问题,或反过来——自检方法是问自己一句:”给 AI 看正确答案,它能不能做对?”能 → 约束问题,不能 → 知识问题。两种的解法完全不同,归因错了就会往错的方向使劲
- 归因归到一个不能改的地方——比如归到”Claude 本身不够聪明”。Stop。只改你可控的策略(模板、字典、few-shot),不要把锅甩给模型
字典沉淀:查表比推理靠谱
我把百来个历史需求的 MR 路径统计出来,沉淀成一份”路径字典”:每个业务场景对应哪个典型路径族。
比如状态机类需求改 internal/app/logic/{area}_fsm/ 和 internal/app/enum/{area}_status.enum.go;定时任务类改 internal/app/worker/cron/{name}.go 加上配置文件;审批流类改 internal/app/infra/audit/client.go。
更有意思的是 i18n 方言陷阱。”新增文案”听起来最简单,但同一个项目里不同栈有七种不同路径惯例:
- Go 微服务用 gettext
ll_CC长码:locale/{zh_CN,en_US}/LC_MESSAGES/*.po - Laravel 后端居然用
cn不是zh:resources/lang/{cn,en}/*.php——这是框架历史包袱 - Yii 后端走
Yii::t()机制:messages/{zh,en}/*.php - 前端管理端用短码:
web/app/common/i18n/lang/{zh,en}/*.js - 前端员工端还要支持繁体:
client/common/config/i18n/lang/{en-us,zh-cn,zh-hk}/*.js
这些知识 AI 不可能凭空知道。你让它”加个中文文案”,它十有八九猜错路径,coverage_loss 就起飞了。
字典沉淀完之后跑 dry-run 自动比对 21 个需求的 865 个真实路径 vs 字典,迭代 4 轮把覆盖率从 78% 推到 99.4%。AI 按字典查表而不是凭空猜,coverage_loss 从源头降低。
查表比推理可靠 10 倍,这是知识问题的通用解法。
到这里,reward shaping 分出两种形态:
- 约束式:在模板里加一句话,改变 AI 默认行为
- 字典式:给 Agent 一本可查的手册,补上它没有的知识
两者不是互斥关系,而是针对不同根因的 loss 分工合作。
五、通用模板之上要有业务专家
单一模板打到 81% → 2%、路径字典覆盖 99.4%,听着已经很好。但真实业务不是单一问题:有人要加红点,有人要搭审批流,有人要接定时任务。
再通用的模板,在具体场景下都会欠拟合。
一开始我也抗拒分那么多专家 skill,维护成本看着不低。但通用模板每次撑到第 27 行就崩:要么把所有场景的细节都往模板里塞,最后撑爆、互相干扰;要么都不塞,AI 每次都问”审批流怎么搭?”。
解法是 RL 里的分层策略:通用模板做路由(判断这是什么场景),专家子策略做执行(该场景下怎么做)。
十几个业务专家
从历史样本里识别出十几个高频业务场景,各自成为一个独立的子 skill。举几个代表性的:
i18n-add(命中最多)——覆盖上节讲的 7 栈方言state-machine——枚举扩展 / FSM 重构 / 状态细化三种 variantapproval-flow——业务类型注册 + 撤销扩展,踩过的坑最多cron-task——幂等、分片、expr 跨环境、时区陷阱
另外还有批量异步、权限校验、消息发送、列表筛选、公司级灰度、导入导出、操作日志、字段加密、红点提醒等,每一个对应历史上反复出现的需求模式。
子策略内部的四件套
每个业务专家是一个目录,四个文件构成一套完整行动指南:SKILL.md 放触发条件和 variant 选择表,variants.md 放代码骨架,anti-patterns.md 放反模式(比如状态机类的”switch 无 default”、”直接绕 FSM 改状态”),examples.md 放历史样本作为 few-shot。
AI 被问”给离职任务加个新状态”时,会先读 SKILL.md 判断匹配哪个 variant,再读 variants.md 选方案,接着扫 anti-patterns.md 避坑,最后参照 examples.md 找相似实现。
再下一层是原子操作:sql-migration、rpc-field-add、enum-extend 这类不含业务语义的机械动作,专家子策略按需调用。
整个策略系统分三层:通用模板决定”哪件事”,业务专家决定”怎么做”,原子操作决定”最底层怎么动手”。这和 RL 里的 hierarchical policy 完全一致。
Experience replay:零成本获得记忆
分层完之后还有一层红利——RL 训练里的经典技巧 experience replay:把高回报的轨迹存下来,下次决策时优先参考。对应到这里就是 few-shot 样本库。
跑完 A/B 之后手上一批 reward 很高的 (input, output) 对,挑出来放进对应 codegen 的 examples.md,下次 AI 遇到相似需求就能看到”前人怎么做的”。
这一步 ROI 最高:不改模板、不重跑样本,只是把已有轨迹组织好。
在 RL 里这叫 offline data 利用,在业务里叫知识沉淀。做完这步,策略系统就有了”记忆”,不会每次都从零开始推理。
六、三条经验,还有一步没自动化
24 轮迭代踩过的坑最后抽成三条锋利的经验:
- 测量粒度决定一切——行级 diff 比文件清单级靠谱,A/B 对照比主观判断靠谱,dry-run 比完整 forward-pass 便宜。选对尺子,后面所有优化才有意义
- 知识外置,别让 AI 凭空猜——字典和业务专家本质是一件事:把 AI 不具备的知识显式喂给它。查表比推理靠谱 10 倍
- 交付形态约束 > 模型升级——一句”只输出 diff 行”让 81% 冗余消失。这比等下一代模型更快、更可持续、更可复制
最后一条最反直觉也最重要。AI 行业的普遍预期是”下一代模型会解决一切”,但我的体感是:同样一套基础模型,不同 Prompt 策略能拉开 80 个百分点的质量差。与其等下一代,不如把现有模型放进一个能反复试错 + 稳定拿反馈的闭环里。
还剩一个没自动化的环节
回过头看整个 RL 循环——reward 的计算已经自动化(脚本),但策略更新那一步还是靠人:人看错例、人归因、人改模板条款。这是整个闭环里唯一不可复制的环节。
学术界和工业界在摸索几条路径:DSPy 把 Prompt 当作可”编译优化”的参数,自动 bootstrap few-shot;RLAIF 用另一个 LLM 当 reward model 或 prompt 改写器;APE 和 OPRO 把 Prompt 空间当组合优化问题,用进化算法或贝叶斯优化搜。
全自动化不会一夜实现。务实的过渡路径是:短期人工 Policy Update + 自动 reward 计算(现状);中期 meta-LLM 读错例自动提议模板修改,人做 safety check;长期做 N 版候选模板 × M 个样本的自动搜索,取 reward 最高的策略。
不管走哪条路径,环境和 reward 定义都不用重做——这是整套流水线最长寿的资产。上层策略是快消品,下层环境 + reward 是传家宝。
这个框架能用在更多地方
把眼镜摘下来看——这套方法不只能用在”AI 写代码”上。任何满足三个条件的工作流都能套上来:有可量化的标准答案、有清晰的 Agent 产出、有可计算的 reward。
AI 写文档,可以对照真实文档的 diff;AI 做测试用例,可以对照真实用例的覆盖;AI 做 code review,可以对照真实评审意见的召回;AI 做运维预案,可以对照真实事故的处置动作。
每一个都可以按这套路子搭一个 RL 优化闭环。
核心观点就一句:提升 AI 产出质量的杠杆不在”换更强的模型”,而在”把 AI 放进一个能反复试错 + 稳定拿到反馈”的闭环里。
把 Prompt 当作文写,改一版看一版,是在赌。把 Prompt 当 RL 策略调,有环境有 reward 有 A/B,才是在优化。
差别就这么大。
参考资料:
- Stanford DSPy——自动化 Prompt 编译优化框架
- Anthropic Constitutional AI——RLAIF 的代表工作
- APE、OPRO——算法化 Prompt 搜索
- Richard S. Sutton《Reinforcement Learning: An Introduction》——RL 基础理论