上一章结束在一个令人不安的事实上:抽取、归并、闸门、状态机都可能算错,而系统里没有任何一个环节的职责是怀疑状态本身。于是错误会被当作合法输入,一路执行到底。
这一章补上那个缺失的环节——一层专门假设“前面可能出错”的防护:它不负责重新判断真相,只负责限制错误能继续造成什么后果。
一、每一步都合规,结果却是错的
先看两个场景。它们的共同点是:单看每一层都像是正常的,错误出在层与层组合之后。 这类问题不能归结成某一次“抽取错了”。
场景一:配置错了,但每一层都忠实执行。 某一版规则里,“后续处理意向”这个闸门条件被不小心配成了默认满足。于是一个从头到尾只是在咨询产品参数、压根没要求继续处理的用户,走完了完整流程:抽取正确、证据无误、归并如实、字段齐备、状态机正常转入已摘要——系统给一个只是来问问题的人,提交了一条服务请求。没有任何一层做错自己的事:归并的职责是折叠事件,不是判断“这个人是否要求进一步处理”;闸门的职责是检查配好的条件,不是怀疑这个条件配得对不对。
场景二:单看每一步都合法,组合起来却是禁止的。 用户明确说过“手机号先不留了”,phone 字段已经是“已拒绝”。但在某一轮,一条负责“补全缺失联系方式”的支路逻辑,因为只看到“渠道还没凑齐”,产生了一个“询问手机号”的计划动作。
拆开看每一步都说得通:字段状态是对的(已拒绝),那条支路的局部逻辑也按它自己的配置运行了。可是放在一起:
phone.status = 已拒绝
计划动作 = 询问手机号
这是一个绝不该出现的组合。 而没有任何单独一层会拦住它——字段那层不知道有人要询问,支路那层不知道这个字段已被拒绝。
两个场景指向同一件事:整条流水线里,每一层都只对自己那一格负责,没有一处的职责是回头检查“这些格子拼在一起,是不是一个合法的整体”。
这就是上一章末尾那个问题的具体形态。前面所有机制都在假设各层的输入、配置和彼此之间是可信且协调的,然后忠实处理。而当这个假设不成立时,忠实反而成了帮凶——系统会用完全正确的流程,执行一个不该执行的决定。
所以需要最后一层,它的假设正好相反:假设前面可能错了。
二、不变量:任何时候都必须成立的事
第一件工具是不变量。
不变量描述的是系统状态与动作必须满足的一致性约束。但要立刻说清一件事,否则它会和正常业务打架:
不变量不是“任何时刻都成立”,而是“在它适用的检查点上必须成立”。
为什么这个限定不能省?看一个再正常不过的过程:摘要发出时联系人是李工,用户下一轮改成王工。在“状态已更新、摘要还没来得及更新”的那一小段里,当前状态和已发摘要确实不一致——但这是合法的中间态,不是故障。若把“摘要必须永远和状态一致”当成无条件铁律,它会在每次正常修改时误报。
所以不变量要绑定检查点。它和闸门、转换规则不同:
闸门 在某个动作发生前,检查这个动作
转换规则 在某次阶段变化时,检查这次转换
不变量 在各自适用的检查点,检查状态或动作组合是否越过硬约束
上一章末尾已经点出这个区分:转换表管“能不能走过去”,不变量管“走到之后这个状态本身合不合法”。不变量不挂在某一条路径上,所以它能挡住那些绕过了主流程的支路——而支路正是最常出故障的地方。
一些典型的不变量,注意它们各自能在哪个阶段被检查:
候选状态刚算出时(状态不变量)
一个字段不能同时是"有效"又是"已拒绝"
已终止阶段,不得再存在"待索取"的字段
策略给出计划动作后(动作不变量)
已拒绝的字段,不得出现在"计划询问"的动作里
已转人工时,系统不得再持有主动作
提交前(跨层不变量)
提交服务请求的动作若声称依据了后续处理意向,就必须能追溯到对应的证据编号
标记为"当前有效"的摘要,其快照必须与当前状态一致
最后一条特意写成了条件式,因为它受前面那个限定约束:
若 摘要状态 = 当前有效
则摘要快照必须与相关状态一致
否则(摘要状态 = 已过期 / 待重新生成)
不一致是合法的
这样,用户改联系人导致的短暂不一致就不再是违规——它只是把摘要标成了“已过期”。只有“自称当前有效、内容却对不上”才算真正违规。 这和第 10、12 章的“已过期”是同一个思想:旧东西不是不能存在,而是不能再冒充当前有效的版本。
再回到第一节那个 已拒绝 + 询问手机号 的组合——它正好是一条动作不变量能抓住的违规:策略产生计划动作之后、动作发出之前,检查“计划询问的字段里有没有已拒绝的”,一撞上就拦。
不变量不是只在提交前检查一次。 不同不变量依赖不同阶段的数据,应该在最早能判定它的位置就检查,并在提交前做最后一道整体校验:
归并 → 候选状态 → 【状态不变量】
↓
闸门 → 策略 → 计划动作 → 【动作不变量】
↓
渲染 → 验证 → 【提交前跨层校验】 → 提交
越早检查,错误传播得越浅——一个自相矛盾的候选状态,没必要等到提交那一步才被发现。第 18 章讲提交时会接上这条链。
至于“检查失败了怎么办”,要分两类不变量来回答,不能一刀切。
第一类是安全关键的不变量。 比如“已拒绝的字段不得出现在计划询问里”“已转人工时系统不得再持有主动作”。这类一旦失败,正确的反应是拦下这个动作:不发出、不提交,降级到安全动作或转人工。因为它意味着系统正打算做一件明确不该做的事。
第二类是数据完整性的不变量。 比如“已发出的摘要,其内容必须与当前状态一致”。这类失败通常不是“要出事了”,而是“状态之间不同步了”,正确的反应是触发一个修复动作——就是第 14 章那个“标记需要重新生成摘要”。这不是停机,而是系统自愈。
区别在于失败意味着什么:
安全关键失败 系统正要做一件危险的事 → 拦下来
完整性失败 系统内部对不齐了 → 修复它
共同点是:不变量失败绝不能被无视然后继续。 要么拦、要么修,但不能假装没看见。还有一条容易漏的纪律:修复之后,必须从相应检查点重新验证一遍,而不是修完直接往下走——否则修复本身引入的新问题会溜过去。至于某条不变量属于哪一类、失败后怎么处理,应当在设计时就写清楚,而不是运行时临时决定。
还有一条边界要划清:不变量不是第二套闸门。 它不该把闸门那整套业务许可条件再实现一遍——同一个许可问题有两个权威实现,就等于没有权威实现(第 13 章)。不变量该管的是跨状态、动作、证据和提交边界的禁止组合与一致性:这个动作声称的依据能不能追溯到证据、已拒绝的字段有没有出现在计划询问里、自称当前有效的摘要和状态对不对得上。闸门回答“该不该做”,不变量回答“这样的组合能不能被提交”。
生产观察 这条边界在真实系统里被写进了小节标题。那份提示词里有一节叫“关键回归断言【只作约束,不重新计算】”——方括号里那六个字就是本节这条纪律。
节里列的是一批具体组合,形式都是“什么输入之后,必须得到什么状态、并且禁止出现什么话术”。其中一条是这样的:
任一诊断只要出现“联系人已有却仍在问联系人”“单位已有却仍在问单位”“信息收集未触发却在问联系字段”,该诊断本身即为无效,必须先按字段选择规则重算,禁止把矛盾状态展示给用户。
注意它的动作:发现矛盾时不是自己去挑一个正确答案,而是判定当前结果无效并要求重算。“只作约束,不重新计算”说的就是这个边界——不变量有权说“这不对”,没有权说“应该是这样”。
三、失败了怎么办:安全降级
停下来之后呢?总不能让用户对着一个卡住的系统。
这就是安全降级。但先要纠正一个常见的误解——安全降级不等于“退回去问一句”。
安全降级的准确定义是:
在当前状态允许的合法动作里,选那个万一错了、代价也最小的。
“问一句”只是其中一种可能,而且经常不是最安全的那种。看几个例子:
表 15-1 安全降级情形与动作
| 情形 | 最安全的动作 | 为什么不是“问一句” |
|---|---|---|
| 用户已拒绝手机号 | 不再索取 | 再问一句,就是纠缠已经拒绝的人 |
| 已经转人工 | 记录并同步,AI 不主动推进 | AI 这时候插话本身就是越界 |
| 工具结果未知(第 9 章) | 告知暂时无法确认结果 | 假装确认或重复提交都更糟 |
| 状态存在两个合理解释 | 澄清一句 | 这种情形才轮到“问一句” |
所以降级动作是从一个集合里选的——正常回答、澄清、不推进、告知无法确认、转人工——选哪个,取决于当前状态下什么是合法且风险最低的,而不是永远回到同一句追问。
核心原则仍然是一句话:
在不确定的时候,最坏的结果应该只是“没有推进”,而不是“做错了一件难以撤销的事”。
把可能的动作按“错了之后有多难收拾”排一下序,方向就清楚了。下面这个排序对很多咨询类系统适用,但它依赖具体业务,不是通用刻度——你需要按自己系统里各个动作真实的代价重排一遍:
更安全 什么业务动作都不做,只做一次普通回应或澄清提问
│ (错了,代价通常是多问一句)
↓
居中 推进到下一步、生成一份内部可见的草稿
│ (错了,多是内部可纠正的偏差)
↓
更危险 提交服务请求、下单、发通知、调用外部接口
(错了,产生难以撤销的外部副作用——第 9 章)
需要提醒的是:代价的排序本身要按业务判断,不能照搬。 在有的系统里,“发一条错误的外部通知”比“下一个可撤销的订单”后果严重得多。先问“这个动作错了,收拾起来有多难、影响多大”,再决定它在这个梯子上的位置。
大方向不变:越是难收拾的动作,动手前越需要确定。 安全降级的做法就是:当确定性不足时,往上退一格。宁可多问一句“我理解得对吗”,也不要把一个可能错误的状态直接提交给外部世界。
这也解释了为什么第九章那条“不可逆动作要格外小心”和这里是同一件事的两面:一个动作越不可逆,系统在做它之前就越应该怀疑自己。
生产观察 安全降级最容易被做反。一个常见的错误是:状态有疑问时,系统选择“先按最可能的情况推进,错了再说”。这在可逆动作上问题不大,在不可逆动作上就是事故。正确的默认方向是:疑问越大、动作越重,越应该退回到更低风险的合法动作——可能是澄清,也可能是不推进、告知无法确认,甚至直接移交人工。
四、把“不做”设计成一个体面的动作
安全降级有一个容易被忽视的前提:“不做”本身也得是一个像样的动作,否则没人愿意用它。
如果系统一遇到不确定就回一句“抱歉,我无法处理您的请求”,那么产品压力会逼着团队去缩小“不确定”的范围——最后又回到了“赌一把”。
所以“不做”要设计得体面。同样是不推进,下面两种天差地别:
差: 抱歉,我无法处理。
好: 我这边想跟您确认一下:您刚提到的预算,是这次需求的大致范围,
还是说暂时先不设预算上限?
第二种把“我不确定”转化成了一次自然的澄清。用户不会觉得系统卡住了,反而觉得它在认真对待。在适合澄清的场景里,好的兜底可以把不确定自然地转成一次合理的追问,而不是生硬地说“无法处理”。
这条和第 13 章决策表那条兜底行呼应:兜底行的动作必须明确且安全。这里补上另一半——它还应该对用户是友好的。 安全和体面不矛盾,只是需要专门设计。
五、询问预算:不要变成纠缠
第三篇里反复出现一个词:询问预算。到这里可以把它讲完整,因为它本质上也是一条“不越界”的约束——越的是用户的界。
系统需要信息,但索取信息是有社交成本的。同一个字段问第三遍,用户的体验是被纠缠。所以每个字段的追问要有上限。
预算不是一个简单的计数器,它至少要区分几种情况:
表 15-2 询问预算的处理
| 情况 | 处理 |
|---|---|
| 问了,用户答了 | 该字段不再需要主动询问;ask_count 保留这段历史,不清零 |
| 问了,用户岔开了话题 | 消耗一次预算,下一轮可换时机再问 |
| 问了,用户明确拒绝 | 不是预算问题,直接转“已拒绝”,停止追问 |
| 预算耗尽 | 进入暂停(第 14 章),不再主动索取 |
这里要澄清一个第 10 章就埋下的区分:ask_count 和“剩余预算”不是一回事。 ask_count 是元数据,记录“这个字段被主动问过几次”,它是历史事实——问过两次就是两次,不因为用户后来回答了就改回零。而“还能不能再问”是策略基于 ask_count、字段状态和当前阶段临时算出来的判断。把这两个概念混在一起,就会出现“用户答了之后计数清零、于是系统以为自己从没问过”这种错。
关键是把“没得到”和“被拒绝”分开
还有一种情况要专门处理:用户持续绕开某个请求。 聊天里真正可观测的不是抽象的“沉默”,而是“用户继续在回复别的内容,却连续几轮都没有回答这个字段”。这本身就是一种信号,应该消耗预算并考虑换个时机或换个方式,而不是每一轮都机械重复同一句请求。
六、拒绝之后:一条需要持续执行的约束
拒绝值得单独说,因为它是所有约束里最需要“跨越时间”的一个。
先要拆开一组经常被混在一起的东西,因为它们是两个不同的维度:
"不方便留手机号" → phone.status = DECLINED (拒绝提供这个字段)
"别给我打电话" → channel_permission.PHONE_CALL = DENIED (拒绝这种联系方式的授权)
这两者完全可能同时是:
phone.status = VALID 手机号系统已经有了
channel_permission.PHONE_CALL = DENIED 但用户不允许用它打电话
系统拥有一个联系方式,不等于拥有使用它的许可。把“别打电话”错误地落成“phone 字段拒绝”,会得出荒谬的结论——要么把已经拿到的号码丢掉,要么无视用户“别打电话”的明确要求照打不误。
这里值得立一条很强的原则:
拒绝什么,就约束什么。拒绝提供联系方式,不等于拒绝一切联系;系统拥有联系方式,也不等于拥有使用它的许可。
不论是字段拒绝还是授权拒绝,共同点是:这个约束应该在它适用的作用域和有效期内持续执行;其中一部分授权或撤回类的约束,按业务与合规要求可能需要跨会话保存——而不是过了几轮、切换话题之后就被淡忘。
这在实现上有两个要求:
第一,拒绝是状态,不是一次性事件。 两类拒绝都必须落成可持久的状态——字段拒绝进入第 10 章那个“已拒绝”的字段状态,授权拒绝进入对应的许可状态(如 channel_permission.PHONE_CALL)——持久保存,而不是“这一轮不问了”这种临时处理。
第二,拒绝要能挡住所有索取路径。 这又是一个不变量的用武之地——“已拒绝的字段不得出现在任何计划询问里”,写成一条全局约束,而不是指望每一个可能发起询问的分支都记得检查。因为只要有一条支路忘了检查,用户就会再次被问到他已经明确拒绝的东西,而这是最伤信任的错误之一。
适用边界 “永久”要有合理的边界,而且这条边界往往由合规而非产品决定。拒绝提供电话,不等于拒绝一切;一次会话里的拒绝,是否延续到之后的新会话,取决于业务与数据合规规定——在一些法域,用户的拒绝或撤回同意是必须跨会话长期保存并执行的,这不是可选项。
这里的原则是“拒绝必须被可靠地记住并执行”,而不是“任何拒绝都永远不可更改”——用户当然可以后来主动改变主意。关键是这个改变必须由用户明确发起,而不是被系统悄悄“忘记”掉。跨会话的拒绝要怎么存、存多久、如何撤销,涉及隐私与合规,第 23 章会再讨论。
七、安全降级的终点:把控制权交出去
前面几节的安全降级,都还是“AI 少做一点”:不推进、不索取、只做最低风险的合法动作。但有些时候,当前状态下最低风险的合法动作已经不是 AI 继续处理了。
这时安全降级的终点是移交:停下来,把控制权交给人。
转人工不是把对话扔出去,是一次带交接物的控制权移交。
什么时候该移交
四类触发,判据来源各不相同:
表 15-3 人工移交的触发类型
| 触发类型 | 典型情形 | 判据来自哪里 |
|---|---|---|
| 用户要求 | “我要找人工” | 用户表达,抽取层识别 |
| 能力边界 | 闸门反复拦下无法推进、状态冲突无法自动化解 | 系统内部信号 |
| 风险升级 | 投诉、涉及法律或赔付、金额超阈值、情绪激化 | 预先定义的风险规则 |
| 策略移交 | 报价审批、签约——业务规定这一步必须人做 | 业务策略 |
四类里模型的角色是同一个:
模型可以识别“发生了什么”,但不能独自决定“控制权交给谁”。
识别“这句话是不是在要求人工”、识别用户情绪激化,都是语义解释;但转不转、转给谁、按什么优先级排队,是控制层的决定。能提前写清楚的判据就写成规则——这也是第四篇会正式展开的模型与代码分工原则。
请求移交,不等于已经移交
这里有一层最容易被跳过的区分:“用户要求转人工”和“控制权已经转过去了”,不是同一件事。
用户说“给我转人工”,产生的只是一个触发信号。从它到 control_mode = HUMAN_HANDOFF,要走完第 9 章那条有外部副作用的路——计划动作、执行、观测结果;第四篇还会讲它在提交阶段怎么落地:
HANDOFF_REQUESTED → 控制层判定 → 计划动作 = 转人工
→ 执行(路由到人工队列)
→ 观测结果 SUCCESS / FAILURE / UNKNOWN
→ 只有 SUCCESS,才更新 control_mode
跳过这一段的后果很具体:客服平台路由失败,或者超时没有回执,而系统已经把自己标成“已转人工”、按本章那条不变量交出了主动作。于是 AI 放弃了控制权,人工却根本没有接到,用户对着一个不再回应的会话一直等下去。
这里的 SUCCESS 还需要定义清楚:它必须证明控制权到达了预先定义的那个转移点——有的系统里“进入人工队列”就算数,有的要“人工已接单”才算。观测结果能证明到哪一步,状态就只能更新到哪一步(第 9 章)。
请求转人工是一个触发;只有控制权实际完成移交之后,系统才进入
HUMAN_HANDOFF。
移交包:不能只交聊天记录
最省事的做法是把聊天记录甩过去。但这等于让人工重新做一遍抽取和归并——自己从原文里读出用户说了什么、哪些还有效、哪些被改过。系统刚做完的工作全部作废。
真正该交的是:
会话标识与追踪 ID ── 让人工能查到完整链路
当前阶段与控制模式
已提交状态快照 ── 每个字段的值、状态、来源、时间
各字段问过几次 ── ask_count
用户明确拒绝了什么 ── 并区分拒绝的是什么
未解决的冲突与拦截 ── blockers 与原因码
最近若干轮原文
转出原因与触发类型
其中两项最容易被漏掉,而且会直接造成本节开头那种翻车。
第一,问过几次。 人工不知道这个字段已经问过两次,就会很自然地再问第三次。换了个人问,纠缠的性质并没有变。
第二,拒绝的到底是什么。 上一节的区分到了移交场景仍然不能丢。移交包里如果只写一行 phone: DECLINED,人工的合理理解是“他没留电话”,接下来自然想去问一句。但用户实际说的可能是“别给我打电话”:
phone.status = DECLINED ← 不提供这个字段的值
channel_permission.PHONE_CALL = DENIED ← 不接受电话联系这种方式
第二行是一条持续约束:在它的适用作用域和有效期内,它同样约束人工。拒绝什么,约束什么。
交接不能只甩聊天记录;核心是系统承认什么、问过什么、用户拒绝过什么,以及它为什么停下来。
人工也是一个证据来源
移交之后人工会做事:补一个字段、纠正一个信息、达成一个结论。这些怎么回到系统?
最干净的做法是走同一套链路——人工在工作台上的每一次修改,和用户输入一样先成为证据,再形成事件:
人工操作 → 证据 → 事件 → 归并 → 候选状态 → 验证 → 提交
这条链背后是一条原则:
人工也是一个证据来源,不是一个特权写入口。
第 11 章那份证据里有 source 字段,人工操作标成对应来源即可。人工来源可以在特定字段或特定场景被赋予较高的权威级别,但这必须由字段和策略明确规定,而不是默认建立一个“人工永远压过其他来源”的全局排序(第 12 章)——人工录错了号码、用户随即纠正,这个纠正不该因为来源排名就被压住。
反过来说,如果允许人工直接改写已提交状态而不产生事件,第 11、12 章那条“证据 → 事件 → 状态”的追溯链就会在人工入口这里断掉:事后回放会看到状态莫名其妙地变了,而没有任何事件能解释它。
接回:转出可以自动,接回必须显式
转出可以由策略自动触发;接回必须由人工操作或明确的工作流事件显式交还。
两个方向的风险不对称。转错了的最坏结果是多占用一次人工;接错了的最坏结果,是人还在处理时系统抢过话头开始自动回复,用户同时收到两种声音。
这里的“显式”不一定是有人手动点按钮——工单关闭后由工作流发出一个交还事件,同样是显式的。它要排除的是另一种情况:仅凭一个普通超时就自行恢复。
接回时必须重新读取最新的已提交状态,不能沿用移交之前那份快照。如果人工期间的事件已经走完归并并提交,那份最新状态本身就是结果;只有还存在尚未归并的事件时,才以它为基准重新折叠(第 12 章)。
接回之后有几件事明确禁止:
- 不能重问人工期间已经拿到的信息;
- 不能重问用户已经拒绝过的东西——由
DECLINED与联系许可约束决定;ask_count也不在接回时重置。DECLINED回答“还有没有资格再问”,ask_count回答“已经问过几次”,两者不是一回事; - 不能静默推翻人工作出的结论。后续如果出现用户的明确纠正、新的工具结果或授权变更,仍然可以形成新的证据与事件,经归并产生新的状态版本——这和第 11、12 章的纠正事件是同一个语义:可以通过新的证据与事件显式改变,不能直接改写已经提交的历史;
- 不能让用户重新复述。接回时问一句“我们刚才聊到哪了”,等于把交接工作推给用户;
- 完成语仍然受原来的约束。人工在对话里说“我已经帮您提交了”,这句话对系统而言只是一条证据,不是一个成功的观测结果。系统要说“已提交”,仍然需要匹配的结果支撑(第 9 章)。人说过,和事情真的发生了,是两件事,哪怕说话的是自己人。
八、第三篇:六层合起来是什么
这一章讲的不变量、安全降级、兜底、询问预算、持续约束和控制权移交,其实是同一条原则的不同侧面:系统必须为“我可能错了”和“这件事我不该做”预留处理方式——既提高做对的概率,也限制做错的代价。 这也是本书核心判断在系统层的落点:提示词工程的终点,是知道哪些事不该让提示词做——也包括知道哪些事,系统在没把握的时候就不该做。
至此,第三篇完整了。回头看,这六章其实是在一层一层地回答同一个问题:面对一句用户的话,一个可靠的系统内部到底发生了什么?
第 10 章 字段 系统现在承认什么,以及每个承认处于什么处境
第 11 章 证据 这些承认凭什么成立,原始观察是什么
第 12 章 归并 新旧观察怎么折叠成唯一的候选状态
第 13 章 闸门与策略 此刻允许做哪些动作,这一轮先做哪一件
第 14 章 状态机 系统走到了哪个阶段,下一步能去哪里
第 15 章 兜底防护 前面出了错,怎么限制错误继续扩大
把它们串起来——这里只画第三篇负责的状态与控制链;工具执行后的观测结果回流、以及它触发的重新折叠,沿用第 9、10 章那张母图,这里不重复展开,以免制造第二份“正式架构”:
用户说了一句话
↓
记录原始证据,机械核验可判的部分 〔证据〕
↓
解释成事件:这是提供、纠正,还是拒绝 〔证据→事件〕
↓
折叠进上一份已提交状态,得到候选状态 〔归并〕
↓
候选状态先过状态不变量:它自身合法吗 〔兜底防护〕
↓
闸门判断:此刻哪些动作被允许 〔闸门〕
↓
策略选出这一轮唯一的主动作 〔策略〕
↓
计划动作过动作不变量:这个组合合法吗 〔兜底防护〕
↓
状态机:若本轮已有触发事件,按当前阶段
与守卫条件检查该转换是否合法 〔状态机〕
↓
确定性不足或不变量失败时,安全降级:
选当前状态下最低风险的合法动作 〔兜底防护〕
↓
(把决定交给下一步——渲染、验证、提交)
这条链有三个贯穿始终的性质,它们比任何单个机制都重要:
第一,每一步都只做自己的事。 归并不判断对错,闸门不挑动作,策略不改状态。职责一旦交叉,系统就会重新长出第 1 章那种“同一个判断散落在多处”的毛病。
第二,语义判断尽量前移,越往后越确定。 原始证据只负责忠实记录观察,需要语言理解的语义绑定与解释集中在事件形成阶段;一旦进入归并、闸门、状态机和不变量,就都是确定性的规则,可测试、可复现、可回放。
第三,每一层都保留了“我不确定”和“我不能”的表达。 字段有未知和冲突,归并有冲突不硬选,闸门有原因码,兜底有安全降级。一个不能表达“我拿不准”的系统,会把每一次拿不准都变成一次硬猜。
第三篇最值得记住的,是下面三句话:
不变量不能保证系统永远理解正确,但可以规定哪些状态和动作组合绝不能被提交。
安全降级不是固定退回澄清,而是在当前约束下选择最低风险的合法动作。
生产系统不仅要提高做对的概率,还必须限制做错时能造成的最大损害。
第三篇完成的是一套“明确状态与控制”的设计。下一篇继续回答工程化问题:这些机制怎么落成运行时,什么该交给模型、什么必须交给确定性执行层,一次模型调用又该怎么拆开。
第四篇,从提示词到运行时系统。