一份长文档到底是怎么写出来的
四十页的报告不是一次很长的回复。它是一条带状态机的后台作业——提纲、提问、调研、逐节写——每一步都能在崩溃之后重跑而不会重复劳动、也不会白烧预算;改写的偏移由代码算而不是模型挑;每一版都留着。
一次普通回答是对模型的一次请求。文档不是:它要跑几分钟到几十分钟,要扛得住你关掉标签页,还要在四十页里保持一致——而这四十页装不进任何一次请求。所以它是一条带状态机的后台作业,而下面几乎每一个设计决定,都是从同一个问题推出来的:这一步死在半路会怎样?
这几步
| 步骤 | 做什么 | 死在半路会怎样 |
|---|---|---|
| 挑类型 | 把请求对上你定义的某一种文档类型 | 重跑,覆盖写 |
| 提纲 | 产出各节 | 建行时冲突即跳过 |
| 提问 | 开写之前问你一次它需要的东西 | 等着——你的回答和唤醒作业是同一个事务 |
| 调研 | 按节收集事实,分轮进行 | 从下一轮接着跑 |
| 写 | 一次写一节 | 认领一节、写完、再下一节 |
| 收尾 | 统计、通知、可导出 | 幂等 |
最能说明这套设计的是调研这一步,因为它是唯一真正做不到幂等的:同一轮跑两次,搜索结果可能不同,抽出来的句子也可能不同。所以它靠的是另外两样——事实按 (文档, 节, 事实) 归一化之后唯一,重复的落不进第二行;轮次计数与那一轮的事实在同一个事务里递增。
结论值得直说:一个反复在第 3 轮崩溃的作业,恢复之后从第 4 轮接着跑。没有这一条,每次重启都从第 1 轮开始,而重启这件事本身没有上限。崩溃循环烧不掉你的预算。
为什么一次只写一节
每一节由一次比较并置换(ready → writing)认领,正文、版本、计数在同一个事务里写完。这就是"服务器写到一半重启了"不成为事件的原因:写完的节一个字都不动,正在写的那一节被重新认领,而不是被写成两个半截。
它同时让请求保持小。一节是由它自己的写作要求、它自己的事实和一小段上下文写出来的,不是由整篇文档。这件事比听上去重要:我们实测过材料变多时工具调用的可靠性怎么塌——从小体量下 8 次里成功 7 次,到 2/8,再到 5000 字上下的 0/8。把所有东西都塞进提示词不会产出更好的一节,只会产出一节复述材料而不是按要求写的文字。
改写:偏移由代码给
在画布里选中一段,说出你要改什么,只有那一段会被替换。这条机制刻意做得很窄:
- 客户端给出一个字符区间
[start, end)。 - 服务端取
body[start:end]作为要改的片段。 - 模型收到的是那个片段加一段有界上下文,只回替换文本。
- 服务端拼回去:
body[:start] + 替换文本 + body[end:]。
模型永远不被问区间,也从不回 diff。一个被要求给出改写区间的模型,在没把握时不会报错——它会给出一个看起来完全合理的区间,然后你的文档里悄悄少了一句话。区间来自渲染这段文字的切块器,所以它按构造就是准的。
如果这一节在你编辑期间被改过了——比如智能体那一刻正在重写它——这次编辑返回冲突,而不是覆盖。留哪一份由你决定;这是你有权做的决定,而替你决定的那种做法,人是在几天后才发现的。
事实,以及一个没有出处的数字会怎样
各节是从一张事实表写出来的,不是从模型的记忆里。每条事实都连着它的来源,所以成品文档里的任何一个数字都能回溯到某一页或某一段。
改写之后,结果由代码扫一遍:数值、代号、专名,凡是不在引用材料里的都标出来。发现了就重生成一次;还在,这段就带着标记回来、并且不自动应用。特别要说的是,这道检查不是靠问模型自己——把矛盾的材料摆在它面前问"这段有依据吗",它照样回答有。
版本
一节的每一次变化都是一个版本:初稿、你的手改、每一次要求的改写、每一次还原。还原旧版本是新增一个版本,而不是删掉什么,于是历史留下的是真实发生过的事,而不是幸存下来的那一份。
对话里会出现什么
写作会在对话里留下三种消息,而没有一种是模型写的:
- 开工——标题和共几节,在作业真正开始烧 token 的那一刻发出,而不是在请求被提出时。
- 已更新——哪几节被改了、各自发生了什么,在某一轮真的改动过文档时发出。
- 完工——节数、字数、用时。
三种消息里的数字都是从数据库算出来的。这不是审美偏好:一个被要求汇报自己刚做了什么的模型,会给出一份自信、具体、偶尔为假的说明——包括在文档一个字没变的那一轮里说"我已经把那张图加进文档了"。系统做过什么,这件事归系统说。
承诺之前该知道的限制
- 每个数据库跑一个写作 worker。作业用
FOR UPDATE SKIP LOCKED认领,认领语义本身已经容得下多个 worker,但目前的部署只跑一个。 - 取消有两半:进程内的那一半让当前这一步很快停下,作业行上还有一个持久标记,所以活过进程的作业照样停得掉。
- 如果你的智能体关掉了公开来源,调研这一步根本不联网——是跳过,不是降级。许可只能更严。