# 确定性边界：LLM 应用的架构第一原则

> LLM 是概率部件，而生产应用需要工程保证。本文提出一条经过实证的架构原则——凡代码能回答的不问模型，模型只产最小片段，片段必经确定性关卡——并给出五个机制的设计细节、量化收益与适用边界。实证基座是一个带 351 个测试的生产级 AI 写作 agent。

- Canonical (HTML): https://kaguc.com/blog/deterministic-boundary-zh/
- Date: 2026-07-29


*English version: [The deterministic boundary: a first principle for LLM application architecture](/blog/deterministic-boundary/)。“LLM 应用工程”系列第 11 篇——前十篇各机制在此汇成一条统一原则。本系列以一个生产级 AI 写作 agent（FastAPI + React + Tauri，351 个测试用例）的实现与实测数据为实证基座，讨论可迁移的工程方法。*

## 问题：概率部件，工程责任

把 LLM 嵌进生产应用后，工程上真正的难点不是"模型不够聪明"，而是它作为一个**概率部件**，会以几种可预期的方式破坏系统性质：

1. **作用域失控。**让模型"改一段"，它重写整篇。对整篇重写式修订的实证测量（arXiv:2601.13217）显示，修订平均回退 16–27% 已覆盖的要点——不是模型失误，是开放式重写的固有统计性质。
2. **循环坍塌。**长上下文下，agent 会退化为以相同参数重复调用同一工具。公开案例（qwen-code issue #4695）记录了单次会话 43 次连续 `git status`、消耗 8.9M token；SDK 层的重试机制对这类故障无效，因为每次调用在 API 层都是"成功"的。
3. **成本漂移。**如果每轮交互都携带全量上下文，一个"把关键词改一下"的请求也要支付整篇文档加全部素材的 token——我们实测这类请求的输入开销约 6–7k token，而其中信息量约为 1.5k。
4. **回归不可测。**输出非确定，传统断言失效；提示词一改，质量静默漂移，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 都会失效。解法是把这些区域替换成**等长空格**：

```python
def _mask(content: str) -> str:
    """把注释与逐字环境替换成等长空格（保持所有字符 offset 不变），
    使其中的 \section 不被当成真标题——但 splice 仍用原文 offset。"""
```

在掩码文本上定位标题，在原文同一 offset 上读取内容。解析与拼回共用一套坐标系，没有换算，也就没有换算错误。

**双锚点。**每个节点同时携带位置型锚（`n0`、`n1`，同一版本内稳定）和内容指纹键（`L1:误差分析`，由层级+标题派生）。位置锚在章节增删后会漂移，内容键跟着标题走、跨版本更稳；定位时内容键优先。这是为"跨版本引用同一节"付出的最小代价——不需要 diff 算法，两个字符串键就够。

失败模式也是确定性的：解析不出结构就返回 `editable=False`，调用方回退到全文修订。**边界机制自身绝不抛异常阻断主流程**——它的职责是收窄模型的作用域，不是制造新的故障点。

## 机制二：作用域生成与确定性拼回

定位之后，发给模型的提示词只含目标节（外加防越界的铁律），模型返回的也只是该节的替换片段。片段回到主文档的路径全部是代码：

1. `strip_fragment`：清洗——去 Markdown 围栏；若模型违规返回了整篇文档，切出从首个标题到 `\end{document}` 之前的部分；
2. `check_fragment`：体检——花括号配平、环境配平、无 `\documentclass`（软信号，提示不硬拦）；
3. `splice(content, start, end, new_text)`：一行拼回，**区间外字节级不变**。

字节级不变是可测试的承诺，不是形容词：对应的断言直接写在测试里。一个实现层的教训：片段必须保持被替换跨度内的顶层 `\section` 数量，否则同一轮里后续编辑项的位置锚会整体漂移、改错节——这个 bug 是内部对抗评审发现的，修复后成为固定测试项。

## 机制三：零 token 预路由

"用户这条意见想改哪里"是一个路由问题。默认做法是问模型，但相当一部分意见带有**无歧义的显式目标**：“参考文献格式不对”“摘要太长”“表2 的单位错了”“§3.1 重写一下”。这类请求用规则直接判定：

```text
信号类别 = { 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 占位……）；
- 引用 `\cite` key 集合变小；
- 章节数减少；
- 插图或数据表减少。

任一触发即回退上一版并终止迭代。选择确定性判据而不是再调一次 LLM 评委，理由写在模块注释里：这个失败模式（丢内容）**本身可确定性检测**，而配对评委在该领域的效度未经验证且每轮多付一次调用——更对，且更省。

闸门自身也有边界 case：参考文献生成排在反思之后，bib 为空时"引用闭合"检查对任何含 `\cite` 的稿件恒为 False，会把"新增了一处引用"误判成劣化。修复方式是 bib 为空时把该项移出比分、引用是否丢失改由"key 集合非减"单独判定。**闸门是代码，代码有 bug；但闸门的 bug 可以用单测钉死，模型的漂移不能——这正是把关卡放在确定性一侧的理由。**

## 机制五：把边界本身纳入 CI——prompt 形状回归门

离线测试跑不了真实 LLM，测不了生成质量，但可以测**提示词的形状**：作用域提示词是否仍然要求"只改这一节"；无缺口输入下的组装函数输出是否与 golden 文件逐字节一致。有人把提示词改回"输出整篇修订稿"，普通 `pytest` 立刻红——确定性边界最脆弱的部分（自然语言写成的约束）由此获得了和代码同级的回归保护。配套的还有评分器自身的判别力门：金标准好报告 ≥0.75、坏报告 ≤0.35、分差 ≥0.4，先证明评分器真的编码了质量，才允许它当门。

## 全景与收益

```mermaid
flowchart TD
    A[修改请求] --> B{确定性预路由}
    B -->|命中显式目标| S[作用域提示词：仅目标节]
    B -->|无信号或跨类冲突| L[LLM 路由] --> S
    S --> G[LLM 生成节片段]
    G --> C[确定性清洗与体检]
    C --> P[splice 拼回：区间外字节不变]
    P --> H{在环者}
    H -->|对话式编辑| U[候选台账，用户接受才落库]
    H -->|自主反思环| R{零 token 回归门}
    R -->|通过| V[落为新版本]
    R -->|劣化| X[回退上一版，终止迭代]
```

| 机制 | 替代做法 | 实测收益 |
|---|---|---|
| 分节器 + splice | 让模型整篇重写 | 非目标区域字节级不变；规避 16–27% 内容回退风险 |
| 零 token 预路由 | 每条意见都调路由模型 | 显式目标请求 0 token 判定；输入 6–7k → ~1.5k |
| 回归门 | 无条件接受修订 / LLM 评委 | 每轮省一次评委调用；劣化即回退，收敛即停 |
| prompt 形状门 | 人工 review 提示词改动 | 351 个测试中的确定性部分全部离线可跑、可进 CI |

## 适用边界

按惯例，说清楚这条原则**什么时候不成立**：

1. **前提是领域有可解析的结构。**LaTeX 有 `\section`，代码有 AST，Markdown 有标题层级；自由散文没有。没有结构锚点，分节器无从谈起——此时确定性边界退化为只剩清洗和闸门两层。
2. **边界自身是代码，代码有维护成本。**分节器要处理 starred 标题、可选短标题参数、注释与 verbatim 掩码这些角落，我们为这一层写了几十个单测。如果产品形态还在快速变化，过早建边界会让每次变化付两遍成本。
3. **它保护存量，不创造增量。**边界的价值随"已有资产的价值"增长：防的是第 N 版被改坏，对从零生成第 1 版没有贡献。创造性主导、无既有资产的任务不需要它。
4. **确定性判据只测得到可测的。**回归门防"变差"，不证明"变好"；偏好层的质量仍然需要配对评委或人的判断——那是另一篇文章的主题（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）。

