English version: The deterministic boundary: a first principle for LLM application architecture。“LLM 应用工程”系列第 11 篇——前十篇各机制在此汇成一条统一原则。本系列以一个生产级 AI 写作 agent(FastAPI + React + Tauri,351 个测试用例)的实现与实测数据为实证基座,讨论可迁移的工程方法。
问题:概率部件,工程责任
把 LLM 嵌进生产应用后,工程上真正的难点不是"模型不够聪明",而是它作为一个概率部件,会以几种可预期的方式破坏系统性质:
- **作用域失控。**让模型"改一段",它重写整篇。对整篇重写式修订的实证测量(arXiv:2601.13217)显示,修订平均回退 16–27% 已覆盖的要点——不是模型失误,是开放式重写的固有统计性质。
- **循环坍塌。**长上下文下,agent 会退化为以相同参数重复调用同一工具。公开案例(qwen-code issue #4695)记录了单次会话 43 次连续
git status、消耗 8.9M token;SDK 层的重试机制对这类故障无效,因为每次调用在 API 层都是"成功"的。 - **成本漂移。**如果每轮交互都携带全量上下文,一个"把关键词改一下"的请求也要支付整篇文档加全部素材的 token——我们实测这类请求的输入开销约 6–7k token,而其中信息量约为 1.5k。
- **回归不可测。**输出非确定,传统断言失效;提示词一改,质量静默漂移,CI 无从拦截。
针对这些失效模式,我们在项目里收敛出一条架构原则,本文称之为确定性边界(deterministic boundary):
凡确定性代码能回答的问题,不问模型;模型只产最小必要片段;片段必经确定性关卡才能进入系统状态。
这条原则与业界现有的两类手段正交:structured outputs / function calling 解决的是格式确定性(输出长什么样),guardrails 类框架解决的是内容合规(输出能不能说);确定性边界处理的是作用域与状态确定性(输出被允许改动系统的哪一部分、以什么条件落库)。代码编辑器领域的 str_replace / fast-apply 模式(模型产 diff、应用是确定性的)是同一原则在另一个域的表达。
下文以文档修订场景为例,拆解五个机制。技术栈:Python/FastAPI,文档格式为 LaTeX,但机制本身与格式无关——任何有可解析结构的领域都适用。
机制一:确定性定位——分节器
作用域编辑的前提是能确定性地回答"目标节的字符区间是什么"。我们的分节器(约 260 行,零 LLM、零依赖)把 LaTeX 源码按 \section / \subsection / \subsubsection 解析成节点树,每个节点给出 char_start / char_end。两个设计点值得展开:
屏蔽而不位移。注释和 verbatim 环境里的 \section 是假标题,但如果先删掉它们再解析,所有字符 offset 都会失效。解法是把这些区域替换成等长空格:
def _mask(content: str) -> str:
"""把注释与逐字环境替换成等长空格(保持所有字符 offset 不变),
使其中的 \section 不被当成真标题——但 splice 仍用原文 offset。"""
在掩码文本上定位标题,在原文同一 offset 上读取内容。解析与拼回共用一套坐标系,没有换算,也就没有换算错误。
**双锚点。**每个节点同时携带位置型锚(n0、n1,同一版本内稳定)和内容指纹键(L1:误差分析,由层级+标题派生)。位置锚在章节增删后会漂移,内容键跟着标题走、跨版本更稳;定位时内容键优先。这是为"跨版本引用同一节"付出的最小代价——不需要 diff 算法,两个字符串键就够。
失败模式也是确定性的:解析不出结构就返回 editable=False,调用方回退到全文修订。边界机制自身绝不抛异常阻断主流程——它的职责是收窄模型的作用域,不是制造新的故障点。
机制二:作用域生成与确定性拼回
定位之后,发给模型的提示词只含目标节(外加防越界的铁律),模型返回的也只是该节的替换片段。片段回到主文档的路径全部是代码:
strip_fragment:清洗——去 Markdown 围栏;若模型违规返回了整篇文档,切出从首个标题到\end{document}之前的部分;check_fragment:体检——花括号配平、环境配平、无\documentclass(软信号,提示不硬拦);splice(content, start, end, new_text):一行拼回,区间外字节级不变。
字节级不变是可测试的承诺,不是形容词:对应的断言直接写在测试里。一个实现层的教训:片段必须保持被替换跨度内的顶层 \section 数量,否则同一轮里后续编辑项的位置锚会整体漂移、改错节——这个 bug 是内部对抗评审发现的,修复后成为固定测试项。
机制三:零 token 预路由
“用户这条意见想改哪里"是一个路由问题。默认做法是问模型,但相当一部分意见带有无歧义的显式目标:“参考文献格式不对”“摘要太长”“表2 的单位错了”“§3.1 重写一下”。这类请求用规则直接判定:
信号类别 = { bib 类词, 摘要/关键词, 节引用(表N/图N/§编号/唯一节标题) }
命中恰好一类 → 直接路由(0 token)
无信号 / 跨类冲突 / 含"全文""整体"字眼 → 退给 LLM 路由
设计立场是保守优先:宁可退 LLM,绝不硬猜。歧义处理也是确定性的——“数据处理"和"数据处理与结果"两个标题同时命中时,保留更长的那个。命中路由后配合机制二的窄提示词,实测把"改关键词"一类请求的输入 token 从约 6–7k 降到约 1.5k;路由本身零成本、零延迟,且不需要 API key,离线测试可以全速跑。
机制四:零 token 回归门
自主 agent 的反思环(草稿 → 自我修订 × N)是作用域失控的重灾区。除了把修订约束成结构化编辑计划(schema 强制 changes[],空数组即视为收敛、提前终止)加作用域 splice,落库前还有最后一道闸门 regression_check——六项全部是确定性判据:
- 丢失可编译骨架(
\documentclass/\end{document}); - 篇幅骤缩到上一版 60% 以下(截断或"其余不变"式省略);
- 结构分下降(八项等权检查:章节数、误差分析、数据表、公式、无 TODO 占位……);
- 引用
\citekey 集合变小; - 章节数减少;
- 插图或数据表减少。
任一触发即回退上一版并终止迭代。选择确定性判据而不是再调一次 LLM 评委,理由写在模块注释里:这个失败模式(丢内容)本身可确定性检测,而配对评委在该领域的效度未经验证且每轮多付一次调用——更对,且更省。
闸门自身也有边界 case:参考文献生成排在反思之后,bib 为空时"引用闭合"检查对任何含 \cite 的稿件恒为 False,会把"新增了一处引用"误判成劣化。修复方式是 bib 为空时把该项移出比分、引用是否丢失改由"key 集合非减"单独判定。闸门是代码,代码有 bug;但闸门的 bug 可以用单测钉死,模型的漂移不能——这正是把关卡放在确定性一侧的理由。
机制五:把边界本身纳入 CI——prompt 形状回归门
离线测试跑不了真实 LLM,测不了生成质量,但可以测提示词的形状:作用域提示词是否仍然要求"只改这一节”;无缺口输入下的组装函数输出是否与 golden 文件逐字节一致。有人把提示词改回"输出整篇修订稿”,普通 pytest 立刻红——确定性边界最脆弱的部分(自然语言写成的约束)由此获得了和代码同级的回归保护。配套的还有评分器自身的判别力门:金标准好报告 ≥0.75、坏报告 ≤0.35、分差 ≥0.4,先证明评分器真的编码了质量,才允许它当门。
全景与收益
| 机制 | 替代做法 | 实测收益 |
|---|---|---|
| 分节器 + splice | 让模型整篇重写 | 非目标区域字节级不变;规避 16–27% 内容回退风险 |
| 零 token 预路由 | 每条意见都调路由模型 | 显式目标请求 0 token 判定;输入 6–7k → ~1.5k |
| 回归门 | 无条件接受修订 / LLM 评委 | 每轮省一次评委调用;劣化即回退,收敛即停 |
| prompt 形状门 | 人工 review 提示词改动 | 351 个测试中的确定性部分全部离线可跑、可进 CI |
适用边界
按惯例,说清楚这条原则什么时候不成立:
- **前提是领域有可解析的结构。**LaTeX 有
\section,代码有 AST,Markdown 有标题层级;自由散文没有。没有结构锚点,分节器无从谈起——此时确定性边界退化为只剩清洗和闸门两层。 - **边界自身是代码,代码有维护成本。**分节器要处理 starred 标题、可选短标题参数、注释与 verbatim 掩码这些角落,我们为这一层写了几十个单测。如果产品形态还在快速变化,过早建边界会让每次变化付两遍成本。
- **它保护存量,不创造增量。**边界的价值随"已有资产的价值"增长:防的是第 N 版被改坏,对从零生成第 1 版没有贡献。创造性主导、无既有资产的任务不需要它。
- **确定性判据只测得到可测的。**回归门防"变差",不证明"变好";偏好层的质量仍然需要配对评委或人的判断——那是另一篇文章的主题(LLM-as-Judge 的效度工程)。
参考
- Beyond Single-shot Writing: Deep Research Agents are Unreliable at Multi-turn Report Revision(arXiv:2601.13217)——整篇重写式修订回退 16–27% 已覆盖内容的实证测量;结构化编辑计划与 scoped 修订正是该文点名的解法。
- PaperOrchestra: A Multi-Agent Framework for Automated AI Research Paper Writing(arXiv:2604.05018)——结构化多 agent 流水线大幅胜过自主基线(+50–68% / +14–38%);其"改好才收、改差就撤"精修闸门报告 79–81% 胜率、0% 变差(该细节取自正文,未独立核验)。
- Self-Refine: Iterative Refinement with Self-Feedback(arXiv:2303.17651)——反馈必须具体可执行;泛化反馈显著掉分,错误难自检的任务需接入外部信号。
- qwen-code issue #4695——工具循环坍塌的公开案例(43 次重复调用 / 8.9M token)。