一、先说结论:三个决定是对的,两个是纯踩坑
下面把"为什么"讲清楚。
二、架构长什么样(三层)
┌──────────────────────────────────────────┐
│ 控制层:8 环节状态机 │
│ C14 → DEEPEN → SEED → PLAN → OUTLINE → │
│ DRAFT → REVIEW → FINAL │
│ 职责:知道"现在第几步"、上一环缺字段不放行 │
├──────────────────────────────────────────┤
│ 约束层:26 张规则卡(按环节加载) │
│ 职责:告诉模型"这一步该怎么想" │
├──────────────────────────────────────────┤
│ 记忆层:四本台账(人物/事实/伏笔/摘要) │
│ 职责:让程序记住模型记不住的东西 │
└──────────────────────────────────────────┘
分层的关键不在"分了三层",在于三层的失效方式是独立的:
- 控制层坏了 → 你会一直卡在某一步,看得见;
- 约束层坏了 → 文风开始漂,要几十章才看得出来;
- 记忆层坏了 → 事实开始打架,要等读者骂你才发现。
所以三层里,记忆层最需要自动化校验,因为它最晚暴露。
三、坑 1:状态机写成了"if-else 瀑布"
我一开始的写法:每个环节写完,直接调用下一个环节——本质上是一条递归的函数链。
问题:递归下去就回不来了。用户在 DRAFT 环节想改 SEED 的人物,没有任何入口。而且一旦某一步抛异常,整个调用栈没了, "现在在第几步"这个信息就丢了。
改法:状态机只负责两件事——"现在允许进入哪一步"和"进入之前缺什么",它不负责跑流程。跑流程是外层的事。
两个要点:
- 闸门必须返回"缺哪个字段"。 返回一句"校验未通过",前端只能显示"失败了",人会一直点重试,而重试是最贵的动作;
- 状态必须落库,不能只存在内存里。 状态存在内存里,进程一重启,"第几步"就没了——这就是最初版本重启后要重跑一遍的原因。
四、坑 2:把台账交给模型写,废稿进了长期记忆
我一开始的写法:DRAFT 每生成一版,就顺手把这一版里的新事实写进台账。
结果:改稿改掉的设定(比如"主角其实没杀他")照样进了事实台账。台账污染是静默的——它不报错,只是在几十章之后让剧情出现一个说不通的转折。
改法:写回是单向 + 延迟 + 可拒绝的。
写回的四个约束:
- 只允许定稿环节写——初稿的废稿内容不能进长期记忆;
- 入库成功之后才写——入库失败但台账写了,账实不符;
- 撞了"不可推翻"的条目就抛错转人工——有些事实推翻一次,全书就废了;
- 写之前留一份快照——改坏了能退到上一章。
四个约束一句话:只允许定稿写、入库之后写、撞了不可变量就抛错、写前留快照。
这四个约束的价值不在"严格",在"污染是静默的"。 台账写错了不报错,它只是让几十章之后的剧情出现一个说不通的转折——那时候你已经找不到是哪一次写回造成的。
五、坑 3:摘要让模型自由发挥,误差滚雪球
我一开始的写法:让模型"用一段话概括本章"。
结果:写到 40 多章,后文开始引用"摘要里没有、但正文里有"的细节——因为摘要把它压掉了,模型只能"补"。而摘要又是下一章输入的一部分,所以误差会链式累积。
改法:摘要由程序按固定字段拼装——章号、字数、本章出场人物、发生地点、新增事实、埋设与回收的伏笔。模型只负责"结尾状态"那一句。
为什么只留一句给模型? 因为这一句是唯一需要判断的("本章停在什么状态"),其余都是可以被程序数出来的事实。能被数出来的东西,不要让模型去概括。
代价:摘要可读性变差,人工回看要多读两行。收益:误差不再复利。
六、坑 4:检索用了向量相似度,召回"很像对的"的错东西
我一开始的想法:既然有向量库,那就把正文切块、语义检索,多省事。
结果(跑了 30 章就停):主角查一个已死角色的旧部,检索召回了"该角色生前的段落",模型据此写出"他回来了"。
根因:向量检索没有"这件事发生在第几章"这个维度。而长篇里,时间恰恰是最重要的一维。
改法:检索键换成确定性字段(人物、章号、事实条目)。如果一定要用向量,至少把章号做成元数据过滤条件——但那已经半个台账了。
七、坑 5:预算裁剪裁掉了不该裁的
我一开始的写法:超预算了?按体积排序,从最大的块开始裁。
结果:锚点块正好最大,被裁了。那一章的语言风格和前后完全不像。
改法:裁剪顺序写死成一条业务规则——先裁前情摘要,再裁事实条目,最后才动本章框架;锚点和规则卡不在候选列表里。
顺序是业务规则,不是体积排序。 按体积排,最大的块一定先被裁,而最大的块通常正是锚点。
再加一条断言:裁完还超预算,直接报错。 如果裁完还超,说明不是"内容太多",是"固定开销本身设计错了"——这种错误早点炸出来比晚点炸好。 否则它会表现为"这一章莫名变短了",而你要花很久才能定位到预算配置上。
八、实测条件(不带条件的数字没意义)
都是内部实测,不代表别人复现同样结果。
九、失败案例还有一个:状态机卡死
现象:某环节上游条件反复不满足,流程原地循环,用户没有任何办法往下走。
根因:只设计了"确认"和"重试",没设计"绕过"。没有逃生通道的状态机,会把一次可恢复的失败变成不可恢复的失败。
改法:三个键的语义分开——
- 确认:这一步看过了,放行;
- 修改:只改当前环节,不跳转;
- 自由输入:权限最高,专门处理"突然要倒回去改前一章剧情"这种非常规情况,平时用得很少。
这条教训我后来写在代码注释里:状态机不是越严越好,是越"可解释地放宽"越好。
十、边界:这套架构别乱抄
不适合:
- 单轮任务(翻译、摘要、分类)——没有环节,状态机是纯开销;
- 1 万字以内的短篇——四张台账的固定开销(约 6 万字符)摊不平,直接对话更便宜;
- 流程还没稳定的项目——流程一天改三次时,状态机会变成维护负担,先稳定再建模;
- 不能接受"停下等确认"的场景——那这条路的负体验会很明显。
已知没解决的:
- 换题材时台账要清空重建,目前手动,误操作会把上一本的设定带进新书;
- 1000 章以上没测过;
- 规则卡没有版本管理,改坏了只能人工回滚;
- 多线并行的状态分支没有通用模式。
十一、三条回头看最有用的建议
- 先定义"这一步算完成",再定义"这一步做什么"。 大多数人反过来,写得出 prompt,说不出验收条件,于是流程永远推不动。
- 闸门返回"缺什么",不返回"不通过"。 可修的报错才叫报错。
- 记忆层一定要能打印出来看。 台账的价值一半在"存",一半在"能人工核对"。不可审计的记忆,等于没有记忆。
常见疑问(Q&A)
Q1:为什么不用现成的 workflow 编排框架? A:可以,而且省事。但框架管的是"调用顺序与重试",这里真正要管的是"上一环的产出是否满足下一环的准入条件"**——那是业务语义,框架不提供,还是得自己写。
Q2:状态存哪? A:不重要。文件、数据库、对象存储都行。唯一要求是进程重启后能恢复"当前环节 + 该环节字段"。 我早期就是 JSON 文件,够用很久。
Q3:8 个环节是拍脑袋定的吗? A:不是,但这也不是通用答案。判断标准是:每个环节是否有一个独立的、可验收的产出。 没有独立产出的环节应该合并。
Q4:这套东西跑起来慢不慢? A:慢,而且这是明码标价的。做法是把"慢"放在环节边界(等人确认),把"快"放在环节内部(连续生成) 。不能容忍任何停顿的场景别用。
Q5:为什么不用更长的上下文窗口一劳永逸? A:容量解决"塞得下",不解决"顾得上"。我自己的观察是:输入还在窗口内,早期设定已经被明显忽略。**窗口是容量,注意力才是带宽。
本文由 AI图鉴自动抓取并翻译,版权归原作者所有;如需查看原文请点下方链接。