1. 从提示词到系统
  2. 第六篇 落到一个真实项目上
  3. 第 25 章 把旧提示词改造成系统

第 25 章 把旧提示词改造成系统

上一章是从零开始,那是幸运的情况。

这一章面对的是更常见的局面:手上已经有一份跑在生产上的提示词,接近两千行,没人敢轻易动。 本章采用的是较早审计快照,精确值为 1925 行。


一、那份不敢动的提示词

先看看这类提示词长什么样。下面引用的是第 1 章同一条演化线中较早的一次审计快照

规模        1925 行
优先级标记  18 处标题写着【最高优先级】
概念分布    "单位"出现 284 次,"手机号" 291 次,"不得" 195 次
重复闸门    至少两处独立的"回看硬闸门",各自判断历史里有没有某个字段

第 1 章展示的是较晚快照:2196 行、15 处“最高优先级”、“单位”299 次、“手机号”309 次。两组数字没有使用同一时间点:局部上,优先级标记从 18 处降到 15 处;整体上,规模和概念重复度仍在增长。第 22 章把这两个快照放在同一组版本指标中比较。

生产观察 这两组数字只支持同一系统在两个版本快照之间的结构变化,不支持把行数、关键词次数或优先级标记数量当作跨项目阈值。迁移时应先为自己的系统建立基线。

第一个诊断结论就在第二行:

当十八个章节都自称“最高优先级”时,没有一个是。

这不是写作水平的问题,而是一种可预测的演化结果:每次出事故,最直接的修法就是加一条规则,并且把它标成最高优先级——因为上一条“最高优先级”没起作用。补丁循环持续得越久,“最高优先级”这类标记就越容易通货膨胀(第 1 章)。

第四行更值得注意。两处“回看硬闸门”做的是同一类判断——在问某个字段之前,先回头看历史里有没有。它们分别为不同的字段写了一遍,条件表述略有差异。这就是第 13 章那个问题的现场:同一个业务许可有两个权威实现,等于没有权威实现。

二、下刀之前:先补上评测与可观测性

在动任何一条规则之前,先做三件事。前两件完全不改变生产行为,第三件只增加一条隔离的诊断能力,同样不该影响正常业务路径。没有它们,后面每一步都是盲改。

第一,建回归集。 把历史事故、当前必须成立的行为、以及你最担心的场景,写成可判分的用例(第 19 章)。

第二,打基线。 用这份回归集把当前版本跑一遍,记录结果。

这一步最容易被跳过,代价也最大。第 1 章那次重构就是在改造前夕才整理回归表的,结果新版跑出四十八条通过,却说不出“比旧版提升了多少”——因为旧版从来没有在同一口径下跑过。

改造之前,是获取可信基线成本最低、条件最完整的时刻。 越往后补,模型、配置、数据和运行环境越可能已经变过,最终可能再也拿不到真正同口径的比较。

第三,建立版本可观测性。 改造期间你会频繁发版,“我改的东西到底生效没有”这个问题会反复出现,而它应该是一个可以直接查到的事实,不该靠猜。

首选做法是把当前生效版本记进追踪、运行配置或内部健康检查(第 20 章)——这些都在应用层,最完整也最安全。只有在平台托管、根本读不到应用层状态的情况下,才退而使用一条受约束的版本探针。

这三件事做完,你才具备下刀的条件。

三、迁移七步

第一步,冻结增量。 改造期间不接新需求,或者约定新需求一律走新结构。一边改地基一边加楼层,最后谁也说不清是哪一步坏的。

第二步,画现状图。 把提示词从头到尾读一遍,给每一段标注它承担的是第 2 章那九项职责中的哪一项:

任务   上下文   证据   状态   控制   行动   表达   验证   治理

这一步的产物不是架构图,是一张分布表——你会立刻看到哪一层被塞满了,哪一层根本不存在。旧提示词的典型特征是:抽取与表达占了绝大部分,而“状态”这一层是空的——字段只有“有值”和“没值”,没有未知、拒绝、冲突(第 10 章)。

标注时会遇到一类段落:一段同时承担了三四项职责——一边解释怎么理解用户的话,一边判断能不能推进,一边规定该怎么说。这本身就是重构信号,而且这些段落通常就是第四、五步要动的地方。

第 2 章的九项职责在这里变成一套标注框架:逐段标出一份将近两千行的旧提示词到底在承担哪些职责。

第三步,找重复。 用最笨的办法:搜索关键概念,看它在几个地方被独立判断过。较早快照里“手机号”出现 291 次,其中真正做判断的有多少处、条件是否一致,一查就清楚。

第四步,定唯一权威。 每一个业务许可判断,只保留一处权威计算(第 13 章)。在本书的贯穿案例里,这一步砍掉的行数最多,风险也相对最低——删掉的是重复实现,不是功能;具体收益取决于重复逻辑的占比和回归保护的覆盖。

要说准一层:“只保留一处”指的是只保留一个权威计算实现,不是取消纵深防御。 闸门唯一地算出这一轮允许做什么,动作不变量检查计划动作在不在这个集合里,验证器检查文本有没有越过已批准的动作——这三者不是三套业务规则,而是一个权威结论加两层一致性检查(第 15、22 章)。

第五步,分离确定性逻辑。 先把格式、计数、状态和许可判断从自然语言补丁中分开,收进明确的规则表和唯一权威位置;具备应用层条件时,再把能确定执行的部分迁往代码、存储或工作流节点(第 16 章),下一节详述。

第六步,收成运行内核。 把散落在各处的执行顺序,收成一段明确的“每轮实际怎么跑”:先做什么、再做什么、什么时候判定、什么时候渲染。

当时的重构版做法是把它单列成一节,并声明它最后执行、覆盖前文所有生成习惯。

但这里要说清楚它的性质:这是迁移期的脚手架,不是目标架构。 本章开头刚刚批评过十八处“最高优先级”,而“覆盖前文”本质上仍然是一条声明式优先级规则——只是把十八个并列的最高优先级,换成了一个更大的最终优先级。

它当时的真实价值不在“最后执行”这四个字,而在于建立了单一的编排入口:改造期的提示词里必然还留着旧写法,与其逐条删除、每删一条跑一次回归,不如先让所有执行路径收敛到一处,再慢慢清理。

最终目标仍然是把旧的冲突实现删掉或外置,让运行权威来自明确的代码路径、状态和控制层,而不是靠更重的措辞去压住旧文本。

第七步,加输出检查与最终提交。 发出前逐项核对,然后把状态与文本分层提交(第 18 章)。那份重构后的提示词,把它们写成两个规则区段:一节是发出前的逐项自检清单,一节是最终提交。

这同样是过渡形态。 按本书前面的架构,能机械判定的检查应当逐步迁进一个独立的验证器,由代码执行——第 18 章那条纪律是“验证器是一道开关,不是一份模型自审清单”。把提示词尾部的自检条目当成最终模板照搬,只会得到一个更长的提示词加一次自我反思,而不是一道真正拦得住的关。

适用边界 贯穿案例的重构版仍然主要是一份提示词:槽位、闸门、运行内核和发送前检查以代码式结构写在提示词内部,并没有整体迁入外部代码和持久化存储。本章所说的“外置”,是从这个真实停点继续向目标运行时迁移;配套仓库用于演示目标形态,不是原项目的历史代码副本。

每一步之后都要跑回归。 七步不是七天,是七个可以独立验证、独立回滚的批次。

四、复杂度判断树:操作版

第 5 章那棵树是路线图版,回答“要不要往下走”。这里是操作版,回答“先动哪一层、花多少钱、什么时候可以停”。

表 25-1 复杂度判断树的操作版

症状 该动哪一层 成本 退出条件
同一规则在多处出现,口径还不一致 合并到唯一权威 同一业务判断只剩一个权威计算位置
格式校验、计数写在提示词里 先集中权威位置;具备条件时外置给代码 过渡期只保留一个提示词内实现;目标形态下,权威计算与最终校验移出模型
字段只有“有值/没值” 引入状态枚举 未知、拒绝、冲突可区分
判断散布在多处,结果互相打架 建唯一闸门 一个业务许可只有一个入口
说了“已提交”但实际没提交 工具与观测结果 完成语都有回执支撑
状态对但流程错(阶段混乱) 引入状态机 阶段与控制模式分开且互斥

第二行的退出条件要留意措辞:移出的是权威计算与最终校验,不是说提示词里连“请输出 JSON”都不能写。告诉模型目标格式仍然有用,只是最终由谁判定合不合格,必须是代码。

用法有一条顺序纪律:

一般情况下先做低成本的那两行。 它们能立刻把规模降下来,而规模一降,剩下的问题才看得清。带着一堆重复条款去设计状态机,你会把重复也一起设计进去。

但有一条例外必须优先,它来自第 16 章:如果发现权限、作用域、合规约束或不可逆动作仍然由模型自由决定,这类缺口不能排队等。 一个“模型自己判断能不能查别人订单”的系统,不该被安排在“先把手机号格式校验搬出去”之后。风险不按成本排序。

五、优先外置清单

第 16 章给过判据,这里给一个建议顺序。它描述的是从提示词内结构化继续走向外部运行时时可以采用的迁移次序,不是贯穿案例已经完成的步骤,也不是普遍排序——具体项目要按自己的风险、平台能力和依赖关系调整(尤其是上一节那条安全例外):

表 25-2 优先外置的职责清单

顺序 外置什么 在提示词里为什么不可靠 外置后怎么验证
1 格式校验(位数、日期、编码) 模型会数错位数,且每次都可能不同 单元测试
2 计数(问过几次、重试几次) 需要跨轮累加,模型没有可靠的计数器 单元测试
3 状态持久化 提示词里的“记住”依赖上下文,会被截断 回放验证
4 真值表判定 条件一多,组合覆盖不全 覆盖检查
5 权限与作用域 可被文本影响,是安全单点 越权测试(第 21 章)
6 可机械识别的敏感值与禁止模式 没必要指望模型记住“不说” 确定性匹配与验证器(第 18 章)

顺序本身有讲究:

在没有高风险缺口需要优先处理时,通常可以从数据与状态开始,再逐步下沉控制逻辑。 先把值、格式、计数这些数据侧的东西挪出去,状态才干净;状态干净了,判定才可能正确;判定正确了,权限才有意义。反过来先搬权限,你会发现它依赖的状态还在模型手里。

这是一条迁移经验,不是普遍定律——上一节那条安全例外优先于它。

还有一条第 16 章的提醒要在这里重复一次,而且要说得更准:外置不是把所有东西都交给代码,但该外置的也不只是“值”。

要分开的是两件事:

模型负责   "别给我打电话"  →  deny(channel_permission.PHONE_CALL)
                             这是语义识别,搬走它规则就没了

代码负责   PHONE_CALL=DENIED  →  不得产生任何电话联系动作
                                这是业务执行,留在提示词里就不可靠

模型识别拒绝,代码执行拒绝约束。 把整条规则都留给模型,约束会时灵时不灵;把语义识别也交给代码,你会退回到关键词匹配。

六、改造期的回归保护

改造期最大的风险不是改错,而是一次改太多,导致失败无法归因

四条保护:

一,一步一验。 每完成一步跑一次回归,不要攒着几步一起验。合并三步之后回归红了,你就得回头把三步都查一遍。

二,规模和方差一起看。 只看通过率会漏掉重要信号。在本书的贯穿案例里,规模上升与跨通过/不通过边界的不稳定同时出现,曾经是一次有效的重构预警——第 1 章那次就是(第 22 章)。这是一次项目内的经验,不是普适规律。

三,保留可回滚的版本指针。 改造期发版频繁,回滚必须能在业务可接受的窗口内完成,且不依赖一次完整重新发布(第 22 章)。

四,灰度而不是全量切。 尤其是第五、六、七步——它们改的是执行路径,影响面比前几步大得多。

七、什么时候应该重写,而不是迁移

这是本章最需要判断力的一节。

迁移适用于:结构还辨认得出来、大部分规则各有理由、业务本身还在正常演进。第 1 章那次属于这一类,准确说是骨架重构——保留业务规则,重建结构。

重写更合适的信号有四个

规则之间大量互相冲突,你已经无法确定哪一条是当前口径
相当一部分规则没有人说得清它为什么存在(来源全丢)
提示词描述的是一个已经不存在的旧业务
继续改造的成本,已经超过按新架构重做一遍

第二条最容易被低估。一条没人知道为什么存在的规则,你既不敢删,也不敢改——它会永久占据成本,并且在每一次改造中都被绕开。第 22 章建立追溯链,就是为了避免这种失忆:留下来源,是为了将来有人能安全地删掉它。

但重写有一个不能商量的前提:

必须先有回归集。 没有回归集的重写,只是把一个不可验证的系统换成另一个不可验证的系统——而且新的那个还没有经过生产检验。

还有一句要补上:“重写”指重新实现,不等于一次性全量替换。 新旧两版仍然要走同口径评测、影子运行与灰度,并保留可回滚的切换(第 21、22 章)。判断“该重写”和“可以直接切过去”是两件事。

八、它会重新长回来

改造成功之后会发生一件事,第 1 章已经记录过:到项目接近半年时,那份从约一百七十五行重新起步的提示词已经涨回了七百多行。

而且这次涨上来的东西不一样。第一次膨胀是散布的禁令和重复的口径;第二次涨的是结构化机制——一致性断言、字段真值表、发送前检查清单,每一条都能说出自己防的是哪一次真实事故(第 22 章)。

所以这一章讲的事情有一个必须承认的边界:

改造是一次性的,治理是持续的。 把规模从两千行压到一百七十五行,这张牌只能打一次;如果不同时把治理装上,它会以更体面的形式重新长回来。

改造完成的那一刻,正是该装第 22 章那套东西的时候:每条规则登记来源、规模与方差纳入常规观察、退役有明确判据和流程。 否则过一段时间,你可能还会需要再读一遍这一章。

九、两条路都走完了

旧系统迁移时,抓住三件事:

改造前先留下基线;这是判断重构价值成本最低、条件最完整的一次机会。

先消除重复权威,再把确定性责任移到更适合它的层——但安全、权限和不可逆动作不能排队等。

改造是一次性的,治理是持续的——没有退出机制,系统迟早还会长回来。

到这里,这本书的两条路都走完了:从零搭一个系统,和把一个旧系统改造成系统。

这二十五章讲了很多“应该怎么做”:写契约、管上下文、建状态、设闸门、拆调用、加验证、做评测、上治理。

但还有一个问题一直没有正面回答,而它其实是这整本书的题目:

这些机制到底该做到哪一步?什么时候,应该停止写提示词?