// WHY THIS MATTERS
大多数人的 prompt 是这样活着的:硬编码在某个 f"""...""" 里,散落在十几个文件,谁改的、改了啥、上线哪版全凭记忆;模型一升级,行为悄悄变了也没人知道。这在 demo 阶段没事,一旦 prompt 进了生产、有真实用户依赖它,它就是系统里最脆弱、却最没被工程化的那块 ——一个逗号的改动可能让格式遵从崩掉,而你没有任何 CI 拦得住。本期把软件工程那套「版本 → 测试 → 发布 → 回滚 → 监控」搬到 prompt 上,但要时刻记住一个本质区别:prompt 不是确定性代码 。它的「正确」是分布性的(同一输入不同输出)、是对特定模型的 (换模型就变)、退化是无声的 (不报错,只是质量悄悄掉)。所以照搬 CI/CD 不够,得为这三个特性专门设计。核心心法:把 prompt 当成有版本、有 eval、能一键回滚的工件,而不是一段字符串。
// 01
Prompt 即工件:从散落的字符串到 registry
论断:内联硬编码的 prompt 没有版本、不可追溯、和代码强耦合。第一步是把它抽成有 id@version 的工件 ,代码只引用,不内联。
背景与原理
把 prompt 从代码里抽出来,做成 prompt registry 里的一等工件:每条有 id、version、metadata(用途、目标模型、负责人、关联 eval set)。代码里写 get("triage", "v7"),而不是把 600 字的 system prompt 内联进 f-string。三个直接收益:可追溯 (生产此刻跑的是哪版,一查即知)、可独立迭代 (改 prompt 不必改代码、不必重发版)、可被非工程同事编辑 (产品 / 运营改文案,走 registry 而不是提 PR 动源码)。registry 本身进 git——拿到代码评审、diff、历史。但要正视一个 §4 会展开的难点:prompt 的 diff 不像代码 diff 那样「逻辑对错一眼可判」 ,所以 git 只解决「留痕」,「改得好不好」要靠 §2 的 eval。
实战示例
# registry:prompt 是带版本的工件,不是内联字符串
from registry import prompts
p = prompts.get("ticket-triage" , version="v7" ) # 钉版本,不取 latest
msg = p.render(ticket=text, lang="zh" ) # 模板 + 变量
# registry 条目(进 git,可 review / diff / 回滚)
# id: ticket-triage version: v7 model: claude-opus-4-8
# owner: bigcat eval_set: triage-golden-120 created: 2026-06-15
核心 mental model:prompt 是代码引用的「外部资源」,不是代码的一部分。 就像你不会把数据库 schema 内联进业务逻辑——prompt 同理,它有自己的生命周期。
失败模式 :(1)prompt 内联硬编码、散落各处——「改了但说不清改了哪、上线哪版」。(2)无版本号,只有「最新的那个」——回滚时无从找回上一版。(3)prompt 和代码强绑死一起发版——改一个字也要走完整发布流程,迭代被拖死。
// 02
测试与发布:prompt 的 CI(但它不确定)
论断:prompt 改动要像代码一样过门——但不能用 assert ==。在 eval set 上看聚合,新版不得比 baseline 差;上线走灰度,不全量替换。
背景与原理
LLM 非确定性,单条 case「试了一下 OK」毫无统计意义。正确做法是回归门 :维护一个 eval set(黄金样本 + LLM-judge + 规则断言,见 Day 6),新 prompt 必须在整个 eval set 上的聚合指标不低于 当前生产版(这是 Day 33 棘轮思想在 prompt 上的投影——只准变好)。发布同样不能「直接换」:shadow (影子运行,不影响用户,只离线比对新旧输出)→ canary / 灰度 (先切 5% 真实流量,盯指标)→ 全量,任一环节指标掉就停。关键认知:prompt 的「正确」是分布性的 ——一两条 case 过不代表整体好,必须在 eval set 上看聚合分布。
新 prompt v8 ──▶ ┌─── EVAL 门(离线,对 baseline v7)───┐
│ golden set · LLM-judge · 规则断言 │
│ 聚合指标 < v7 ? ──▶ 拒绝,不发 │
└──────────────┬───────────────────────┘
通过 ──▶ SHADOW:影子跑,只比对不影响用户
──▶ CANARY:5% 真实流量,盯线上指标
指标掉 ──▶ 自动停 + 回滚 v7
──▶ 全量切到 v8(v7 仍可一键回滚)
prompt 的「对」是分布性的:看 eval set 聚合,不看单条 case
实战示例
def eval_gate(new, baseline, eval_set):
s_new = run_eval(new, eval_set) # LLM-judge + 规则断言的聚合分
s_base = run_eval(baseline, eval_set)
if s_new.score < s_base.score: # 棘轮:不得比生产版差
raise Reject(f"{s_new.score} < baseline {s_base.score}" )
return "shadow" # 过门 → 进影子,不是直接全量
deploy = { "stage" : "canary" , "traffic" : 0.05, "rollback_to" : "v7" }
失败模式 :(1)拿一两条 case 试着 OK 就全量上线——分布性效果被单点蒙蔽。(2)无 baseline 对比,凭「感觉变好了」发布——没有 ratchet 就会悄悄退化。(3)直接全量替换、无灰度无回滚路径——出问题时全体用户一起中招。
// 03
回滚与可观测:prompt 会「无声退化」
论断:prompt 的退化通常不报错 ——只是质量悄悄掉(更啰嗦、漏 edge case、tone 漂移)。所以必须:生产钉版本、一键回滚、线上指标盯着。
背景与原理
代码 bug 会抛异常,prompt 退化只是「答得差了一点」,没有红灯——这是它最危险的地方。两道防线。其一,版本钉死 + 一键回滚 :生产引用具体 v7,绝不引用 latest;像 feature flag 一样,发现不对秒回上一版,而不是现场改 prompt 救火。其二,线上指标 :每个 prompt version 关联一组可量化信号——下游任务成功率、格式 / schema 合规率、用户反馈(点踩 / 重试率)、长度、cost、latency——指标掉到阈值下就告警、必要时自动回滚。这和 Day 34 HITL 的「从生产数据回归阈值」是同一套思路:让 prompt 的质量变成可监控、可回归的量,而不是凭感觉。
实战示例
# 生产钉版本 + 指标守门 + 自动回滚
LIVE = "v7" # 永远钉具体版本,不要 latest
def on_metrics(ver, m):
if m.schema_ok < 0.95 or m.thumbs_down > 0.08: # 无声退化的可见代理
alert(f"{ver} 质量掉了" ); rollback(to=prev(ver)) # 秒回上一版
# 每版留指标,供对比与回归(呼应 Day 34 审计回归)
# v6: schema 0.97 / 👎 0.04 v7: schema 0.93 / 👎 0.09 ← 退化
失败模式 :(1)生产引用 latest——上游一改,你的系统跟着无声变,且无法回滚到「改之前」。(2)从没演练过回滚——真出事时手忙脚乱现场改 prompt,越救越乱。(3)只看离线 eval、不看线上指标——eval set 过了不等于真实流量上没退化。
// 04
跨模型漂移:同一个 prompt 在不同模型上不是同一个 prompt
论断:prompt 是写给特定模型 的程序。换模型、甚至模型小版本升级,等于运行时变了——同一 prompt 行为可能漂移。prompt × model 是个矩阵,每格独立 eval。
背景与原理
最反直觉、也最常踩的坑:把 prompt 当成可移植的。但「在 Opus 上调到完美的 prompt」搬到另一个模型(或同模型的下个版本)上,格式遵从、CoT 深度、拒答边界、tone 都可能变——因为你换掉的是解释这段 prompt 的运行时 。治理上有三条。其一,把模型升级当依赖升级 :上游出新版,跑一遍回归 eval 再切,别「省事直接升」。其二,多模型路由场景 (你有 fallback / 成本路由,见 Day 23)——每条路由路径上的 prompt 都要在对应模型上测,别假设 A 上好的 B 上也行。其三,漂移哨兵 :定期用固定 eval set 跑生产模型,检测「同 prompt 同输入、输出分布是否漂移」——抓上游静默更新(provider 不通知你就换了权重)。
claude-opus-4-8 gpt-class 本地 8B
triage eval ✓ eval ? eval ✗ ← 每格独立测
summarize eval ✓ eval ✓ eval ?
extract eval ✓ eval ✗ eval ✗
· 模型升级 = 依赖升级:整列重跑回归再切
· 固定 eval set 定期跑 → 抓「上游静默漂移」
失败模式 :(1)换模型 / 升版本不重测——「省事直接切」,行为漂移到生产才发现。(2)假设 prompt 可移植——在 A 上的调优默认搬到 B。(3)上游模型静默更新无监控——provider 换了权重,你的输出分布悄悄变了却毫无察觉。
进阶资源 · Anthropic
Prompt engineering overview (针对模型调优),
docs.anthropic.com ·
本站 Day 23
Personal AI Infra (多模型路由)
// 综合实战 · 给你的 prompt 资产建一条 prompt-as-code 流水线
一周计划:把你最关键的那个生产 prompt,从「埋在代码里的字符串」升级成「有版本、有门、能回滚、被监控」的工件。
抽 registry :把硬编码 prompt 抽成 id@version 工件,代码改成 get(id, version) 引用。
建 eval set :攒 50–100 条黄金样本 + LLM-judge + 规则断言,作为回归基线。
Eval 门 :新版必须在 eval set 上聚合分不低于生产版(棘轮),否则不发。
灰度发布 :shadow 影子比对 → canary 5% → 全量,每步盯指标。
Pin + 回滚 :生产钉具体版本,真演练一次 一键回滚(别等出事才第一次用)。
线上指标 :每版关联 schema 合规 / 点踩率 / cost / latency,掉了告警。
跨模型矩阵 :列出 prompt × model,把下次模型升级当依赖升级跑回归;加一个定期 drift 哨兵。
做完这套,你再看到「我们调了个更好的 prompt」会本能地问:对比的 baseline 是什么、在多大的 eval set 上、灰度了吗、能回滚吗、换模型测了吗——而不是相信「感觉变好了」。Prompt 是软件的一部分,就该有软件的纪律——只是要为它的「不确定、依赖模型、无声退化」额外设防。
// ENGLISH GLOSSARY
Prompt Registry 集中管理 prompt 工件的库,每条带 id / version / metadata,代码按版本引用。
Prompt-as-Code 把 prompt 当成需版本、测试、发布、回滚的工程工件来治理。
Eval Set / Golden Set 用于回归的黄金样本集,配 LLM-judge + 规则断言衡量聚合质量。
Regression Gate 新版本必须在 eval set 上不低于 baseline 才放行的门(棘轮)。
Shadow Run 影子运行新版,不影响用户,仅离线比对新旧输出。
Canary Release 先切一小部分真实流量验证指标,再全量。
Pinning 生产引用具体版本号而非 latest,避免上游变更无声传导。
Rollback 一键退回上一已知良好版本,像 feature flag。
Prompt Drift 同一 prompt 因模型变更 / 上游静默更新而行为漂移。
Prompt × Model Matrix prompt 与模型的组合矩阵,每格独立 eval,模型升级当依赖升级。
// 深入思考
prompt 进 git 像代码一样 review——但 reviewer 怎么判断一个 prompt 改动「更好」?代码有逻辑对错,prompt 只有分布性效果。 这正是 prompt-as-code 和真·代码的根本分歧:prompt 的 diff 不可靠地人工判优 。所以 review 的重心要转移——人审 prompt diff 主要看「有没有引入明显风险」(注入面、越权指令、PII、与 policy 冲突),「改得好不好」不交给人眼,交给 eval 门 。换句话说,代码 review 里「正确性」那部分,在 prompt 这边被 §2 的 eval set 接管了;人只保留「安全 / 合规 / 风格」的判断。如果你发现自己在 PR 里争论「这个措辞是不是更好」,那是该让 eval 数据说话的信号,不是该靠 reviewer 直觉的。
eval set 是整套治理的根基,但它会过拟合——你对着它调 prompt,它就不再代表真实分布。怎么防 eval set 腐化? 真实且严重,机制等同 ML 的训练/测试集泄漏。三个对策:(1)eval set 持续从生产采样补充 ,尤其把线上出问题的 case 回流进来——让它跟着真实分布走,而不是一个静态化石。(2)留一个 holdout :日常调优用的 dev set 和最终把关的 test set 分开,test set 不参与迭代、只在发布前看一次。(3)定期审视 eval set 的覆盖 :它是否还代表当前用户在问的东西?过拟合的根因往往是 eval set 太小太旧——它该像数据集一样有自己的版本和更新节奏,而不是建一次用一年。
灰度 / 回滚是从代码部署借来的,但 prompt 的「指标掉了」常常滞后且嘈杂,等你发现已经服务了很多用户。怎么更快发现退化? 关键是把检测前移到上线之前 + 用更快的代理信号 。前移:shadow 阶段就能离线发现大部分退化(不影响用户、立刻可比对),别等 canary 的线上反馈。代理信号:用户点踩 / 留存这类「真指标」滞后又嘈杂,但可机器即时算的代理 不滞后——schema/格式合规率、输出长度分布、拒答率、是否调用了预期 tool、与旧版输出的语义距离。这些能在请求级实时告警。真指标用来「确认」,代理信号用来「抢跑」。canary 的流量也该按风险调——高风险场景 1% 起、设更紧的自动回滚阈值。
跨模型矩阵 prompt × model,模型一多组合爆炸。个人 / 小团队负担得起吗?哪里该省? 负担不起全矩阵,也不需要。省的办法是按风险和流量裁剪 :只对「高流量 × 高风险」的 prompt 做全模型矩阵;长尾 prompt 只测主力模型,fallback 模型留一个小 smoke set 兜底即可。另一个省法:别养太多模型 ——prompt×model 爆炸的根因常是模型选型太散,收敛到 1 主 1 备能砍掉大半组合。真正不能省的是主力模型的升级回归 :那一列每次 provider 出新版都必须重跑,因为它覆盖你绝大多数流量。把预算花在「高流量列 + 升级回归」上,其余抽样。
这套和 Day 33 代码治理、Day 34 HITL、Day 6 eval 是什么关系?prompt-as-code 是不是就是把软件工程搬到 prompt? 它确实是「软件工程纪律 → prompt」的迁移,但有三处不能照搬 ,正是本期的存在理由。和 Day 6 eval 的关系:eval 是这里的「测试框架」,本期是围绕它的版本 / 发布 / 回滚 外壳。和 Day 33 :同是治理工件(代码 vs prompt),ratchet / provenance / 风险分级的思想共用,但对象的「正确性可判定性」不同——代码能静态分析,prompt 只能统计 eval。和 Day 34 :HITL 的「指标回归阈值」和这里的「prompt 指标回归」是同一引擎。三处本质不同:非确定性 (用分布而非断言)、模型依赖 (prompt×model 矩阵,代码没有这维度)、无声退化 (要主动监控,不像 bug 自己冒出来)。把握住这三点,你就不会把 prompt 当普通配置文件糊弄过去。
BigCat · Super Individual · Day 35 · 2026-06-15