[AI小说] 如何用多 Agent 协作,全自动写完整本小说
如何用多 Agent 协作,全自动写完整本小说
ainovel-cli 是一个全自动 AI 长篇小说创作引擎:从一句话需求开始,Coordinator 调度 Architect、Writer、Editor 多个 Agent 完成设定、卷弧规划、逐章写作、七维审稿和自动重写,最终导出 TXT / EPUB。
项目真正有区分度的地方,不只是多 Agent 分工。它还支持 500+ 章上下文策略、相关章节推荐、Step 级断点恢复、用户实时干预、仿写画像、已有小说导入续写和诊断报告。这些能力共同指向一个目标:不是生成一章样稿,而是稳定写完整本小说。
长篇生成的难点在持续运行:规划要能滚动展开,角色和伏笔要能跨数百章保持一致,用户中途改稿要能进入返工队列,崩溃后要能从 plan / draft / check / commit 继续。ainovel-cli 的关键设计,是把长篇创作拆成多 Agent 协作,并把关键事实落到 Store,而不是让聊天历史承担全部记忆。
项目源码: https://github.com/voocel/ainovel-cli
底层基于:
agentcore:底层通用 Agent 框架,提供 Agent 编排、工具调用、子代理和运行上下文能力。
litellm:模型调用适配层,用统一接口接入不同 LLM Provider。

核心设计判断
ainovel-cli 的架构价值可以概括为四点:
| 架构判断 | 解决的问题 | 用户价值 |
|---|---|---|
| 多角色 Agent 协作 | 单个 Agent 难以同时承担规划、写作、审稿和调度 | 长篇生成有明确分工,质量控制不依赖一次性输出 |
| 控制面与创作面分离 | LLM 不适合承担所有确定性流程判断 | 流程稳定,用户干预仍可由模型理解 |
| 关键状态进入事实层 | 聊天历史不能作为长篇作品的唯一记忆 | 断点恢复、长期一致性、诊断都有依据 |
| 修改请求进入显式流程 | 中途改稿容易破坏终稿与进度边界 | 用户可调整作品,系统不绕过状态约束 |
这四点贯穿整个项目。
功能点与架构能力映射
本文不是 README 复述,但 ainovel-cli 的主要功能点都能落到对应架构能力上:
| 用户看到的功能 | 背后的架构机制 | 本文展开位置 |
|---|---|---|
| 多智能体协作 | Coordinator + Architect / Writer / Editor 子代理 | §2 |
| 长循环自动推进 | Host Flow Router + Coordinator 执行通道 | §1 |
| Step 级断点恢复 | 工具落盘 + checkpoint + PendingCommit | §4 / §5 |
| 卷弧双层滚动规划 | compass + layered outline + arc/volume boundary | §3 |
| 相关章节推荐 | novel_context 从伏笔、角色、状态、关系召回历史章节 | §6 |
| 自适应上下文策略 | ContextProfile + StoreSummaryCompact + restore pack | §6 |
| 七维质量评审 | Editor prompt + save_review 评分门禁 | §8 |
| 用户实时干预 | Coordinator steer 分类 + directive / review queue / reopen | §7 |
| TUI 统一入口 | Entry/TUI 只做输入展示,运行事实来自 Host 事件投影 | 总体架构 / §9 |
| 多 LLM 支持 | ModelSet + role config + provider failover | §2 |
| 诊断报告 | diag snapshot + 脱敏 export | §9 |
| 仿写画像 | simulation profile 进入 novel_context,作为参考上下文 | §6 补充点 |
| 导入已有小说 | import pipeline 反推 foundation 并落盘为已完成章节 | §10 补充点 |
| 导出 TXT / EPUB | exporter 从 Store 读取终稿并过滤内部工件 | §10 |
| 自定义规则 / 去 AI 味 | rules loader + commit 检查 + Editor 审阅约束 | §8 补充点 |
可以看到,项目的功能不是孤立堆叠,而是围绕同一条链路展开:输入 -> 规划 -> 写作 -> 审稿 -> 返工 -> 记忆 -> 恢复 -> 诊断 -> 导出。后文重点分析这些功能背后的可靠性设计。
总体架构
TUI / Headless
|
v
Host
|-- Flow Router:读 Progress / Checkpoint / Outline,计算下一步
|-- Observer / Diag:投影运行事件,生成诊断报告
|-- Usage / ModelSet:统计用量,支持角色级模型配置
|
v
Coordinator Agent
|-- 执行 Host 指令
|-- 处理用户干预、规划选型、异常裁定
|
v
SubAgents
|-- Architect:premise / characters / world_rules / outline / compass
|-- Writer:plan -> draft -> check -> commit
|-- Editor:review -> rewrite/polish queue -> arc/volume summary
|
v
Tools
|-- save_foundation / draft_chapter / commit_chapter / save_review / ...
|
v
Store
|-- Progress / Checkpoints / Drafts / Chapters / Summaries
|-- Characters / World / Foreshadow / Directives / Signals
各层边界清晰:
| 层 | 职责 | 边界 |
|---|---|---|
| Entry / TUI | 输入、展示、命令入口 | 不做创作决策 |
| Host | 启动、恢复、事实路由、观察、模型管理 | 不直接写正文 |
| Coordinator | 执行 Host 指令,理解用户干预 | 不直接读写 Store |
| SubAgents | 规划、写作、审稿 | 不绕过工具修改状态 |
| Tools | 原子落盘、推进进度、追加 checkpoint | 不做跨代理调度 |
| Store | 保存作品事实与运行事实 | 不做语义判断 |
这个分层让系统能把“模型判断”和“事实推进”拆开处理。
1. Hybrid Coordinator:事实路由交给 Host
在垂直 Agent 场景里,很多流程决策是确定性的:
- 返工队列不为空,下一步处理队列第一章。
- 弧末评审缺失,下一步派 Editor。
- 弧摘要已完成但下一弧仍是骨架,下一步派 Architect 展开。
- 规划阶段、用户干预、异常处理,则交给 Coordinator 裁定。
项目将确定性路由下沉到 internal/host/flow/router.go。Route 是纯函数,不读模型、不写状态:
func Route(s State) *Instruction {
p := s.Progress
if p == nil || p.Phase == domain.PhaseComplete {
return nil
}
if p.Phase != domain.PhaseWriting {
return nil
}
if len(p.PendingRewrites) > 0 {
ch := p.PendingRewrites[0]
return &Instruction{
Agent: "writer",
Task: fmt.Sprintf("重写第 %d 章", ch),
Reason: fmt.Sprintf("PendingRewrites 队列剩余 %d 章", len(p.PendingRewrites)),
Chapter: ch,
}
}
// 弧末评审、摘要、展开、续写...
}
nil 表示进入 LLM 裁定场景,而不是路由失败。
用户价值:写完章节后的推进不依赖模型“猜下一步”,减少长流程卡顿和误派;同时用户提出自然语言修改时,仍由 Coordinator 做语义判断。
docs/refactor-flow-driven.md 给出过这次重构的依据:Coordinator 9 类决策点中,6 类是纯路由,3 类需要 LLM 裁定;每章 Coordinator token 由约 3500-14000 降到约 1000-1500(项目设计文档,2026-04-20)。
2. 多角色 Agent:职责隔离与模型异构
长篇创作至少包含规划、写作、审稿、调度四类任务。ainovel-cli 将它们拆成不同 Agent:
| 角色 | 职责 |
|---|---|
| Architect | 生成前提、角色、世界规则、卷弧大纲、指南针 |
| Writer | 完成单章规划、正文、回读、自审、提交 |
| Editor | 阅读原文,做结构与审美审阅,产出返工建议和摘要 |
| Coordinator | 执行 Host 指令,处理用户干预和语义裁定 |
internal/agents/build.go 按角色装配模型:
architectModel := models.ForRoleWithFailover("architect", reportFailover)
writerModel := models.ForRoleWithFailover("writer", reportFailover)
editorModel := models.ForRoleWithFailover("editor", reportFailover)
coordinatorModel := models.ForRoleWithFailover("coordinator", reportFailover)
子代理之间不直接聊天,而是通过 Store 中的大纲、摘要、角色状态、伏笔台账和审稿结果协作。
用户价值:规划、写作、审稿不共享同一个混杂上下文,角色职责更稳定;不同角色可以配置不同模型,在质量和成本之间做更细粒度的权衡。
3. 卷弧滚动规划:避免长篇后半程空洞化
一次性规划几百章容易让后半程变成占位符。ainovel-cli 采用滚动规划:
- 初始只生成 2 卷。
- 第 1 卷有完整弧结构,第 1 弧有详细章节。
- 第 2 卷保留骨架弧。
- 到弧/卷边界时再
expand_arc 或 append_volume。
compass 保存终局方向、活跃长线和预估规模。
章节提交时,commit_chapter 返回后续路由所需的边界事实:
result := domain.CommitResult{
Chapter: a.Chapter,
NextChapter: a.Chapter + 1,
ReviewRequired: reviewRequired,
ArcEnd: arcEnd,
VolumeEnd: volumeEnd,
NeedsExpansion: needsExpansion,
NeedsNewVolume: needsNewVolume,
NextVolume: nextVol,
NextArc: nextArc,
}
这些字段进入 Flow Router,驱动评审、摘要、展开或续写。
用户价值:后续规划可以吸收已写章节摘要、角色快照、伏笔状态和用户干预,不必死守开局时的一次性大纲。长篇越往后写,规划越接近当前作品事实。
4. 工具事实层:完成状态由落盘定义
长流程 Agent 不能用“模型说完成了”作为完成标准。ainovel-cli 要求关键产物必须通过工具写入 Store:
| 角色 | 关键工具 |
|---|---|
| Architect | save_foundation |
| Writer | plan_chapter / draft_chapter / check_consistency / commit_chapter |
| Editor | save_review / save_arc_summary / save_volume_summary |
Writer 的流程由提示词规定,但由代码兜底。internal/agents/build.go 中 Writer 命中 commit_chapter 后结束本轮,StopGuard 还会检查本轮是否产生新的 commit checkpoint:
writer := subagent.Config{
Name: "writer",
MaxTurns: 30,
ToolsAreIdempotent: true,
StopAfterTools: []string{"commit_chapter"},
StopGuardFactory: func(_, _ string) agentcore.StopGuard {
return reminder.NewWriterStopGuard(store)
},
}
用户价值:章节真正写入文件、进度真正推进、checkpoint 真正追加后,系统才认为任务完成。断点恢复和导出都建立在真实文件状态上。
5. commit_chapter:章节提交是一条事务链
commit_chapter 是章节级状态提交,不只是保存正文。
| 提交动作 | 写入/更新 |
|---|---|
| 保存终稿 | chapters/XX.md |
| 保存摘要 | summaries |
| 更新时间线 | timeline |
| 更新伏笔 | foreshadow |
| 更新人物关系与状态变化 | relationships / state_changes |
| 更新配角名册 | cast_ledger |
| 推进进度 | progress |
| 追加 checkpoint | meta/checkpoints.jsonl |
由于提交跨多个工件,项目用 PendingCommit 记录中间阶段:
pending := domain.PendingCommit{
Chapter: a.Chapter,
Stage: domain.CommitStageStarted,
Summary: a.Summary,
HookType: a.HookType,
DominantStrand: a.DominantStrand,
StartedAt: now,
UpdatedAt: now,
}
if err := t.store.Signals.SavePendingCommit(pending); err != nil {
return nil, fmt.Errorf("save pending commit: %w: %w", errs.ErrStoreWrite, err)
}
internal/domain/commit.go 定义了 started、state_applied、progress_marked、signal_saved 等阶段。
用户价值:中断后系统能判断提交卡在第几章、哪个阶段,而不是只知道“运行失败”。同时,分层长篇会在任何写入前检查章节是否落在大纲范围内,避免大纲耗尽后继续裸写。
6. Store 上下文:长篇记忆来自结构化工件
聊天历史不适合作为长篇作品记忆。ainovel-cli 将作品事实沉淀到 Store,再按当前任务装配上下文。
| 记忆类型 | 来源 |
|---|---|
| 短期记忆 | 最近消息、前一章尾部 |
| 中期记忆 | 章节摘要、弧摘要、卷摘要 |
| 长期记忆 | 角色档案、世界规则、伏笔、关系、状态变化 |
| 恢复记忆 | checkpoint、草稿、restore pack |
internal/domain/runtime.go 根据章节规模选择上下文策略,50 章以上启用分层摘要:
func NewContextProfile(totalChapters int) ContextProfile {
switch {
case totalChapters <= 15:
return ContextProfile{SummaryWindow: 10, TimelineWindow: 10}
case totalChapters <= 50:
return ContextProfile{SummaryWindow: 5, TimelineWindow: 8}
default:
return ContextProfile{SummaryWindow: 3, TimelineWindow: 5, Layered: true}
}
}
Writer 压缩时优先使用 StoreSummaryCompact,从结构化工件重建上下文;只有不够时才走 LLM 摘要,并通过 WriterRestorePack 注入恢复包。
同一条上下文通道也承载扩展能力。仿写画像会以 compact profile 注入 novel_context,相关章节推荐也在这里完成:系统从伏笔、角色出场、状态变化、关系等维度召回历史章节,供 Writer 按需回读。
用户价值:写到后期时,模型拿到的是当前作品事实,而不是大量陈旧聊天记录。角色状态、伏笔、审稿待修项和风格规则可以跨压缩、跨重启持续生效。
7. 用户干预:修改请求进入显式流程
用户中途修改不是普通 prompt,而是按影响范围进入不同路径:
| 用户意图 | 处理路径 |
|---|---|
| 继续写 | 回到主线续写 |
| 查询状态或设定 | Coordinator 回答后继续推进 |
| 调整篇幅或后续方向 | Architect 更新 compass / outline |
| 修改已写章节 | Editor 写入返工队列 |
| 长效风格要求 | save_directive 落盘 |
| 完本后返工旧章 | reopen_book 重新打开并入队 |
修改已完成章节时,Coordinator 不直接派 Writer 覆盖终稿,而是让 Editor 通过 save_review 写入 PendingRewrites:
if finalVerdict == "rewrite" || finalVerdict == "polish" {
if len(affected) == 0 && r.Chapter > 0 {
affected = []int{r.Chapter}
}
flow := domain.FlowRewriting
if finalVerdict == "polish" {
flow = domain.FlowPolishing
}
if err := t.store.Progress.SetPendingRewrites(affected, r.Summary); err != nil {
return nil, fmt.Errorf("set pending rewrites: %w", err)
}
if err := t.store.Progress.SetFlow(flow); err != nil {
return nil, fmt.Errorf("set flow %s: %w", flow, err)
}
}
Host Router 随后优先处理返工队列,逐章派 Writer 重写或打磨。
用户价值:用户可以改旧章、调篇幅、加风格约束,但这些修改不会绕过状态机。终稿边界、返工队列和后续路由仍然可追踪。
8. Editor:审稿结果驱动返工
Editor 不只输出评论,而是生成可执行的质量反馈。它必须读原文,并从七个维度审阅:
- 设定一致性
- 人设一致性
- 节奏平衡
- 叙事连贯
- 伏笔健康
- 钩子质量
- 审美品质
save_review 会根据评分卡做门禁。关键维度不合格时,accept 会升级为 polish 或 rewrite,并更新 Progress.Flow 与返工队列。
自定义规则也会进入这条质量链路。用户在规则文件中配置的禁词、字数、疲劳词和偏好,会在 commit 检查与 Editor 审阅中被消费,避免质量控制只依赖模型自由判断。
用户价值:审稿意见可以变成后续写作流程,而不是停留在自然语言建议。严重问题进入重写,轻度问题进入打磨,合格内容继续推进。
9. 可观测性:诊断只观察,不接管控制流
/diag 和 diag-export 用于分析流程、质量、规划和上下文问题。导出报告移除小说正文、prompt 和思考内容,只保留工具名、参数类型、错误串、重复次数、phase/flow、checkpoint 等行为骨架。
internal/diag/export_test.go 中有一个典型测试:模型连续 14 次把 commit_chapter.chapter 传成字符串 "7"。导出报告需要保留 "7"、InputValidationError 和重复次数,但不能泄露正文。
观察层不自动修复、不续跑、不改流程。
用户价值:用户可以提交可诊断信息,同时不暴露小说正文;维护者可以根据行为骨架定位死循环、参数错误或流程卡点。
10. 导出:内部工件转换为读者产物
/export 支持 TXT 和 EPUB,且是只读操作。internal/host/exp/exporter.go 对失败语义处理明确:
- 没有已完成章节,返回错误。
progress 标记完成但章节文件缺失,返回错误。
- 输出文件已存在且未指定 overwrite,返回错误。
- 范围内未完成章节记录为 skipped。
导出器区分内部工件和读者产物:premise 不进入导出,长篇内部弧分隔不进入导出,读者拿到的是章节正文和卷结构。
导入则走相反方向:已有小说先被切章,再反推出 foundation、角色、世界观、分层大纲和指南针,原文以已完成章节落盘。它不是普通上下文参考,而是把外部文本转换为同一套 Store 事实,后续才能接力续写。
用户价值:系统生成的不只是中间过程,还能稳定交付可阅读、可保存、可迁移的作品文件。
边界与风险
当前仓库有一个明显文档边界:README 部分内容仍保留早期“Host 不介入调度”“Reminder 驱动”的表述,但代码和 docs/architecture.md 已经落到 Hybrid Coordinator。对新维护者来说,这会造成职责边界误判。
另一个风险是审稿阈值。Editor 的评分和 verdict 会影响返工频率。阈值过严会导致过度返工,过松会放过一致性问题。项目已把关键门禁下沉到 save_review,但阈值仍需要真实长篇运行数据校准。
总结
ainovel-cli 的核心设计不是“让大模型写小说”,而是把长篇创作建模成一个可恢复、可观测、可干预的 Agent 工作流。
它的架构价值在三处:
- 流程稳定性:Host Router 处理确定性路由,减少 LLM 在流程层的随机性。
- 状态可靠性:Tools 与 Store 定义完成、记忆、返工和恢复依据。
- 用户可控性:干预、改稿、长效要求和完本返工都有显式入口。
模型能力决定创作质量,事实层设计决定系统能否持续运行。对长篇内容生成 Agent 来说,后者是项目能从 demo 走向可用系统的关键。