AI 小说产线(二):领域数据需要一个家的服务端
上一篇写了小说产线的编排器”李白“:它自己一个字不写,只管问询、委派和纪律。留了一个问题没展开——编排器声称”本地零领域数据”,那这些数据到底住在哪?
答案是住在一个独立的写作服务端上:题材卡、文风库、平台字数标准、项目状态、约束包组装,全部归服务端;大纲与正文的生成也由服务端执行。这篇从编排器的视角,讲这层服务契约是怎么设计的,以及三条花代价换来的取舍。
契约的核心:编排器只传”指针”,不传”内容”
正文生成那一步,编排器发给服务端的东西少得惊人:章节 ID 加作者当场的一句话指令,完事。
为什么约束包(8 问结果、题材卡、字数标准这些生成上下文)不由编排器组装好再发过去?第一版方案就是这么想的,被否了。原因有两个:一是往返冗余——服务端本来就有全部数据,编排器拉回来再发回去,纯属路由费;二是更隐蔽的双份组装逻辑漂移——一旦编排器本地有一份组装代码,服务端也有一份,两边对”生成上下文该长什么样”的理解就会慢慢分叉,而且没有任何报错,只是生成质量悄悄变怪。
所以契约定为:数据住哪,组装就在哪。编排器拿到的是结果,不是过程的中间件。
能力对照表:什么该委派,什么该自建
服务端不是万能的,编排层也不是空壳。两者边界的判定工具是一张能力对照表——把”服务端有什么”逐行列出来,再和编排需求对:
| 服务端已有 | 服务端没有(编排层自建) |
|---|---|
| 大纲/正文生成、题材卡、文风库 | 评审量表的网文加权(爽点密度、章末钩子) |
| 项目状态、导出、备份 | 黄金三章的高阈值流程 |
| 约束包组装、字数标准 | 长篇每卷的卷纲自动刷新 |
右列全是编排层的需求,服务端没有对应能力——这些就老老实实留在编排器本地,做成校验脚本和流程控制。这张表的价值在反方向:每当想给编排器加个功能,先查左列,服务端有的绝不重造。这条纪律是编排器不膨胀的保命符。
对照表还是活的。服务端排期开发新能力(比如题材与故事火花的匹配度路由),上线那天,编排层里对应的手工逻辑就该”回家”——自建是缺口期的临时方案,不是永久领地。每次服务端升级,把表重新对一遍,看有没有功能该搬家了。
并发与一致性:一本书一个大脑
服务端要面对一个尴尬现实:可能有多个会话同时操作同一本书。解法是标准的 Web 手段加一条领域规则——乐观锁(版本号比对,冲突即拒绝)加带 TTL 心跳的锁机制防死锁,外加编排器开工前的”一书一活跃会话”校验:同一本书同时只有一个编排会话在工作。
领域规则比技术手段更重要。锁解决的是”别写坏”,一书一会话解决的是”别写乱”——两套大纲思路交替注入同一本书,就算每次都写入成功,成品也是精神分裂的。
流式生成的取舍:断流即作废
正文生成走流式接口,字是往外蹦的,中途可能断。断流怎么办?断点续传听起来很工程正确,我们选了相反的方案:整章作废,重新生成,连续两次断流就报错转人工。
计算的是另一笔账:半截残章一旦混进章节库存,它看起来是完整的——格式齐全、语句通顺,直到某个伏笔在”后半章”里根本不存在。发现和清理残章的成本,远高于重新生成一章的成本。数据污染的治理成本永远高于数据重建,这是做服务端设计时要主动算的一笔账。
模型归属也能看出这层契约的清晰度:生成用哪颗模型是服务端的事——编排器发指令、收成稿,不需要知道也不需要关心服务端背后接的是哪一家;而评审用哪颗模型是编排层的事,因为它定义的是”验收标准”。生产权归服务端、验收权归编排器,异构评审的原理就这样落在了架构上,而不是靠自觉。
一条容易忽略的纪律:数据源标注
编排器的 8 问开书流程里,每一问都必须标注数据来源:情感内核是固定选项,题材列表来自服务端接口,故事概要可以调 AI 生成候选。标注本身不是目的,目的是让”服务端有这份能力”和”编排器本地写死了一份副本”随时可查——凡是本地写死的,早晚和服务端的版本打架。
有了标注,升级服务端时 grep 一下来源标注,就知道哪些编排逻辑要跟着改。
写在最后
服务不可达时,编排器直接停——重申一遍这个设计,因为它最反直觉也最重要。一个”服务挂了也能凑合写”的编排器,等于把领域数据偷偷复制了一份在本地,前面所有的契约、对照表、标注纪律,都会被这份私藏的副本慢慢腐蚀。
服务端不是编排器的”后端”,它是领域数据的唯一住所。编排器是来看门的,不是来收留数据的。
附录:服务契约速查
给想理解这层契约具体长什么样的读者,把编排器真正会碰到的接口和机制浓缩成一张表。
接口面(编排器视角的全部依赖):
| 动作 | 服务端接口 | 说明 |
|---|---|---|
| 建书 | POST /api/novels |
携带 premise/大纲/实体三元组 |
| 出大纲 | POST /api/novels/:id/outline/generate |
编排器取回后只做完整性核对 |
| 章节生成 | POST /api/chapters/:id/generate |
SSE 流式;约束包由服务端组装 |
| 章节状态回写 | PUT /api/chapters/:id |
乐观锁版本号在此生效 |
| 导出归档 | GET /api/novels/:id/export |
前置李白的一致性清单核对 |
| 8 问数据源 | /api/genres、/api/styles、/api/picks、/api/ai/premise |
题材过滤/文风/月度推荐/概要候选 |
改稿循环的精确规则(这是花最多轮次打磨的部分):评审得分低于阈值即进入定向修改——阈值按长度分两档(短故事/长篇各一档,不用单一阈值跨平台);每轮只按低分维度重写该章,不做全量重生成;每轮产出保留 candidate 版本,循环结束后择优回滚;3 轮仍不达标:取最优 candidate、标记未达标、交作者裁决——不强行通过,也不无限重试。把”不达标”变成一个合法的出口,是防止循环空转的关键设计。
两个工程细节:其一是 SSE 断流策略——流式中断即整章作废重生成,不做断点续传;连续断流两次封顶后转人工。半截残章混进库存的排查成本远高于重生成一次。其二是一书一活跃会话——服务端加乐观锁和带 TTL 心跳的锁,编排器开工前先校验这本书没有别的会话在写;两套大纲思路交替注入同一本书,比并发写坏数据的后果更难修。
本系列:(一)一个不管写作、只管纪律的编排器 | (二)本文 · 服务端契约设计