教学文档自动生成系统
摘要
该项目构建了一条面向任意主题的教学文档生产流水线:输入主题与素材后,系统可自动完成采集、规划、分节写作、风格统一与组装导出,稳定产出长篇 Markdown 与出版级 PDF。核心工程价值在于“异常优先”设计:通过上游故障识别、快速降级与局部软失败机制,将外部模型波动从全局中断转化为可控退化,保证任务可收敛、可交付。配套的 PDF 渲染层则通过目录/书签、章节导航、代码块样式与 CJK 排版优化,确保产物不仅能生成,而且可读、可定位、可复查。整体上,这是一套从“能跑”升级到“可持续交付”的内容生产基础设施。
前言
这个项目的出发点不是“把一段 prompt 变成一篇文章”,而是解决一个更现实的问题:当团队需要持续产出教学型长文时,如何把“选题、搜集、组织、撰写、导出、复查”从人肉串联,变成可重复执行的工程流程。我们在实践里很快发现,只要流程里有一个环节依赖人工手工补齐,整体交付就会变慢;只要流程里有一个外部依赖不稳定,整体交付就会失控。因此,这个系统的第一目标从一开始就不是“写得最花”,而是“稳定可完成”。
在当前版本中,系统已经具备一个完整闭环:输入主题和素材后,自动完成多阶段处理,最终产出可阅读的 Markdown 与可分发的 PDF。这里的“可分发”不是指临时截图或 demo,而是具有目录、章节、页眉页脚、代码块样式等出版级要素的文档。换句话说,我们把“内容生产”与“版式交付”当作一个统一系统来做,而不是两个割裂工具。
项目经历过多次真实故障场景,尤其是上游大模型供应波动(如 504、连接超时)导致流程卡死。正是这些失败案例逼着我们把架构重心从“理想路径”转向“异常路径”:当依赖不可靠时,系统应该如何快速止损、优雅降级、继续产出。今天这份复盘文档,就是围绕这个核心:系统如何从“能跑”升级为“能交付”。
目标与边界
我们将目标拆成三层。
第一层是流程目标:统一入口、统一参数、统一产物。用户通过 main.py 或 run_book.py 提交任务后,系统应在同一条链路内完成采集、规划、生成、质检和导出,避免脚本散落导致上下文丢失。
第二层是交付目标:不仅要有文本,还要有可发布形态。Markdown 是中间事实源,PDF 是面向阅读和交付的终态产物;两者必须在内容上保持一致,不能出现“正文是 A 版本、导出版式是 B 版本”的分叉。
第三层是鲁棒性目标:上游模型不稳定时,系统仍能完成任务。这一点在实践中比“写得多漂亮”更重要,因为业务侧首先要的是“今天有没有结果”。
边界同样明确。这个系统不做 CMS(内容管理系统)全量能力,不处理用户权限、协作审阅流程、在线编辑器状态同步;它也不承担发布平台职责,生成完产物后由外部发布流程接手。我们把边界收窄到“从输入到可交付文档”,目的就是减少耦合,提升可维护性。
另外,系统当前不追求“完全无人工介入”。在高要求场景里,人工仍然会做最后一轮语义和风格审阅。我们的设计是把人工投入从“重复劳动”挪到“价值判断”:让机器处理结构化和机械化步骤,让人处理业务语境与表达质量。
架构/模型
整体架构采用分层流水线,核心实现位于 src/pipeline.py。当前主流程被组织为五层:
- 信息采集(Layer 1):由
InfoCollector聚合 URL、本地文档等来源,并可写入向量库。 - 内容规划(Layer 2):由
OutlineGenerator生成章节与小节大纲。 - 分段生成(Layer 3):由
SectionWriter按小节并行或串行撰写正文。 - 风格重写(Layer 4):由
StyleRewriter做风格统一或口吻调整。 - 组装与质检(Layer 5):由
DocAssembler组装全文、执行质量检查并导出多格式。
模型接入层由 src/llm_provider.py 抽象,配置在 config/settings.yaml。这个抽象允许不同供应商走统一接口(例如 OpenAI-Compatible、Anthropic、Google),并通过 roles 将“outline/draft/review”等环节映射到具体模型。好处是显而易见的:当某个供应商波动时,我们不需要改业务逻辑,只需调整配置和策略。
向量检索使用 ChromaDB(requirements.txt 与 settings.yaml 已定义),用于在写作阶段补充上下文。这里的 RAG(Retrieval-Augmented Generation,检索增强生成)并不追求学术最优,而是追求工程可用:只要能提高事实一致性和段落充实度,就满足当前目标。
渲染层采用 src/renderer/pdf_book.py,基于 ReportLab 完成版式控制。这个选择的原因是可控性高:字体注册、目录、书签、代码块、页眉页脚都可编程,便于在“出版风格”场景做稳定输出。相比纯 HTML 转 PDF 的黑箱方式,这种方案调试成本更低,问题定位更直接。
关键实现
关键实现一:失败优先(failure-first)的 LLM 调用策略。我们不是简单“重试到成功”,而是先识别错误类型,再决定是否继续等待。对于网关超时、连接超时、服务不可用等高概率外部故障,调用层会快速标记降级状态,避免一轮任务在同一故障上无限消耗时间。这使得系统从“赌供应商恢复”转为“主动切换到可完成路径”。
关键实现二:扩写阶段的软失败机制。此前短章节扩写属于硬依赖,一旦模型调用异常,会直接影响整条任务。现在扩写步骤做了异常封装:失败时保留原文并记录日志,继续后续组装。这个改动看似简单,但对交付稳定性影响很大,因为它把局部失败变成了可接受的不完美输出,而非全局失败。
关键实现三:PDF 渲染的结构修复。我们修正了目录剥离逻辑,避免正文被误截断;补齐了目录与书签注册,让每个 H2 章节都能正确建立导航;增加 wordWrap="CJK" 强化中文排版换行表现。这些改动让导出从“能打开”变成“可阅读、可定位、可复查”。
关键实现四:统一 CLI 参数体系。main.py 与 run_book.py 都支持 topic、manifest、template、render options 等输入,使任务运行具备可重放性。对于工程项目来说,可重放比一次性成功更重要,因为它决定了你能否复现、对比、回归。
关键实现五:产物导向的质量检查。系统在组装后会统计字数、检查问题项,并在必要时触发补充流程。这不是追求完美评审器,而是建立最小质量护栏:至少保证“不会明显短缺、不会结构塌陷、不会空产出”。
移动端体验
虽然本项目本体是文档流水线,不是移动 App,但“移动端体验”依然是交付标准的一部分。原因很现实:大量阅读发生在手机端,尤其是学习型内容。若文档只在桌面端好看,实际传播价值会被明显削弱。
我们在输出结构上做了几件事来服务移动端阅读。
第一,段落粒度控制。正文倾向 2-5 句短段,减少一屏长墙文本,降低阅读疲劳。
第二,列表化表达。步骤、约束、注意项优先使用列表,移动端更容易扫读,也利于截图传播。
第三,章节化导航。PDF 中通过目录与书签构建“跳转阅读”能力,用户可以按问题定位,不必线性翻页。
第四,代码块可读性。通过独立代码样式和背景色,减少代码与正文混淆;在中文语境中同时保证英文命令行的辨识度。
第五,视觉层级。标题、副标题、正文、引用、警示样式分别定义,确保移动屏幕上仍有清晰信息层次。
需要说明的是,移动端体验不是“响应式页面”专属概念。对于 PDF 这类静态文档,体验优化更依赖内容结构与排版决策。我们把移动端当成主阅读场景之一,才能做出真正可消费的知识产物。
发布闸门
发布闸门的意义,不是增加流程复杂度,而是防止“看起来完成、实际上不可用”的内容进入线上。当前闸门策略包含三层:
第一层是 schema 合规。frontmatter 必填字段必须完整,枚举值必须合法,slug 必须可路由,关联关系(如 essay.project)必须可解析。
第二层是质量合规。按档位执行标准:standard 保证结构完整与事实一致;launch 额外要求长文体量、章节覆盖、可读性与一致性检查。
第三层是工程合规。发布前必须通过构建检查,确保内容变更不会破坏站点构建或渲染逻辑。通过后再进行版本提交与推送,形成可追溯发布记录。
在这套机制下,闸门不是“审批心情”,而是“机械规则”。规则机械化的好处是公平:同样输入同样标准,不受主观状态影响。对于长期内容生产,这一点尤其关键。
踩坑与回滚
踩坑一:上游 API 波动时,系统长时间重试却不失败,导致任务挂起。这个坑暴露出“把重试当稳定性”的误区。解决方案不是继续加重试次数,而是识别不可恢复错误并降级。
踩坑二:目录处理逻辑对输入格式过度假设,导致正文被误剥离。这个问题提醒我们:任何“文本结构清洗”都应该在异常输入下保守处理,宁可少删,不可误删。
踩坑三:中文排版在默认参数下出现换行不理想、密度不均的问题。通过显式设置 CJK 换行策略后,阅读体验明显改善。这说明“看起来像 UI 细节”的问题,实际会直接影响内容可消费性。
踩坑四:把“内容质量优化”与“流程稳定性修复”混在同一迭代,导致验证边界模糊。后续我们明确分层:先修稳定性,再做质量打磨,避免一次改动引入过多变量。
回滚策略方面,我们坚持三条原则:
- 以产物为基准回滚:若新版本 PDF 明显劣化,允许回退到上一个可验收产物。
- 以单点改动回滚:优先回滚具体渲染或调用策略,不整体推倒。
- 以可复现记录回滚:每次关键改动都保留命令与文件路径,确保回滚不是“凭记忆操作”。
上线标准
“上线”不是指代码能运行,而是指内容能被稳定消费。当前上线标准定义如下:
- 流程完整:从输入到导出全链路可跑通。
- 产物完整:Markdown 与 PDF 同时产出,且内容主体一致。
- 结构完整:章节、目录、导航可用,无明显缺段和断裂。
- 稳定可控:上游故障时可降级,任务可在可接受时间内结束。
- 验收可追溯:关键命令、关键文件、关键结果可追踪。
对于 launch 档位,额外强调“可读性达标”:
- 段落长度适中,避免连续超长段落。
- 列表使用充分,便于快速扫读。
- 术语首现给出解释,降低理解门槛。
- 每章有结论句,帮助读者形成记忆锚点。
我们不把“华丽表达”作为硬门槛,而把“信息可达、逻辑可跟、结果可复现”作为硬门槛。因为上线后的真实反馈,最终会指向这三件事。
后续路线
下一阶段路线分三条主线推进。
第一条是内容质量线:优化 fallback 内容多样性,减少重复句式,提升长文阅读韵律。目标不是追求文学化,而是提升信息密度与阅读顺畅度。
第二条是验证自动化线:把当前人工验收清单沉淀成脚本化检查项,包括章节覆盖、字数阈值、链接协议、frontmatter 合规、导出文件一致性等,减少人工漏检。
第三条是渲染增强线:继续打磨 PDF 视觉系统,尤其是目录页、代码块跨页策略、章节视觉占位与插图模式切换,让文档既保持专业感,也保持工程可维护性。
此外,我们也会补充“运行态指标”记录,例如每轮任务耗时、失败类型分布、降级触发频率。这些数据会帮助我们从经验优化走向证据优化,不再靠主观印象调整参数。
结语
这个项目最重要的成果,不是一次性生成了多少字,而是把“文档生产”从手工流程升级为可重复执行的工程系统。它在实践中证明:只要边界清晰、流程分层、异常优先,内容系统就能在不稳定外部依赖下持续交付。
我们也清楚地看到,真正的长期价值来自“稳定完成 + 持续改进”的组合:先保证今天交付,再让明天更好。对一个内容工程项目来说,这比追求一次性的漂亮结果更有意义。
总结一句:该系统已经跨过“能跑”的门槛,进入“可交付、可迭代、可验证”的阶段;接下来的工作,是把这套能力打磨成团队可以长期依赖的生产基础设施。
附录一:从一次真实任务看系统如何收敛
为了避免“架构讲得好看、落地却失真”,这里用一次真实任务复盘系统如何完成收敛。任务输入是“围绕特定主题生成长篇教学文档并导出 PDF”。在理想情况下,流程会顺滑完成;但真实场景中,上游模型服务出现了间歇性 504 与连接超时。若系统只依赖重试,这类任务通常会陷入等待,最终用户只能得到超时失败。
本项目当前策略是先判断错误可恢复性,再决定是否继续调用。对于明确的网关超时、连接超时、服务不可用,会快速触发降级路径,进入 fallback 生成链路。这样做的关键收益是“确定性完成”:即便内容风格会阶段性退化,任务仍能产出可交付文档,而不是彻底失败。对生产系统来说,先保证可交付,再提升质量,始终是更优先的工程选择。
这次案例还验证了另一个关键点:导出链路必须与正文链路解耦。即使上游生成质量波动,导出层也应稳定工作,保证目录、分页、样式、书签可用。否则你会得到“内容有了,但无法阅读和分发”的半成品。我们把渲染器作为独立能力建设,正是为了避免这类二次失败。
附录二:运行手册(面向执行者)
为了让新成员可以快速接手,这里给出最小运行手册。
第一步,准备配置与输入。确认 config/settings.yaml 中 provider 与 roles 映射有效,并提供 topic 或 manifest。若需要补充语料,传入 urls 或 docs。
第二步,执行主流程。通过 main.py 或 run_book.py 运行任务,观察标准输出中的阶段日志。正常情况下你会看到 Layer 1 到 Layer 5 依次完成。
第三步,核对产物。至少检查导出目录是否包含 Markdown 与 PDF;再检查 PDF 是否具备目录、分页、章节层级、代码块样式。
第四步,执行发布前校验。若进入 launch 档位,必须额外确认项目文档字数、章节覆盖、可读性与事实一致性。
第五步,进入发布闸门。通过 schema、质量、构建三层后,才允许翻转 draft 并提交发布。
这份手册的重点不在“命令有多复杂”,而在“每一步都可核对”。只要核对点明确,协作成本会显著下降。
附录三:验收清单细化(launch 档位)
1) 结构验收
- Frontmatter 字段完整:title、description、date、status、draft、slug 等。
- status 枚举合法:
planned|in-progress|shipped|paused|archived。 - slug 合法且无冲突:kebab-case,且在目标集合中唯一。
- essay 与 project 关联有效:
essay.project指向存在的 project slug。
2) 内容验收
- 项目文档正文字数达到 10,000+。
- 章节覆盖完整:前言、目标与边界、架构/模型、关键实现、移动端体验、发布闸门、踩坑与回滚、上线标准、后续路线、结语。
- 每章有结论句或收束句,避免悬空叙述。
- 段落节奏合理,避免连续超长段。
3) 渲染验收
- PDF 目录可见且可跳转。
- 页眉页脚稳定,无错位。
- 代码块背景与字体可读。
- 中文断行正常,无大段挤压或截断。
4) 工程验收
- 构建命令通过(check + build)。
- 仅目标文件发生发布相关变更。
- 提交记录可追溯,回滚路径明确。
这套清单的作用是把“主观好坏”转成“客观可核对项”,避免口头争论。
附录四:风险矩阵与应对
我们把风险分成四类:外部依赖风险、内容质量风险、渲染风险、流程风险。
外部依赖风险主要来自模型供应商不稳定。应对策略是调用层降级、错误分类、fallback 兜底。目标是把不可控外部故障转化为可控内部退化。
内容质量风险主要表现为重复段落、术语堆积、章节失衡。应对策略是模板约束、章节覆盖检查、必要的人审校准。目标是让输出从“能看”提升到“可复用”。
渲染风险主要表现为目录异常、分页不稳定、中文断行问题。应对策略是渲染器单元验证、关键样式固定、回归对比。目标是让导出版式稳定可预测。
流程风险主要表现为多人协作时标准不一致。应对策略是发布闸门制度化、验收清单标准化、提交粒度可追溯。目标是减少人为波动对结果的影响。
风险矩阵不是为了“写得专业”,而是为了明确优先级:先处理会导致交付失败的风险,再处理影响体验的风险,最后处理锦上添花的优化。
附录五:为什么我们坚持“先稳定后优化”
在内容系统里,很多团队会先追求“文风惊艳”或“功能丰富”,但往往忽略基础稳定性。结果是看起来功能很多,实际每次运行都要人工救火。我们这套方法反过来:先确保任何输入都能走到终点,再逐步提升终点质量。
这种取舍并不保守,反而更激进。因为它把资源投入到长期复利的能力:流程稳定、异常可控、交付可验收。一旦这些基础打牢,后续每一次优化都会被放大;反之,如果底层不稳,再多优化也会在故障时归零。
从管理视角看,这种策略也更利于跨人协作。稳定系统不依赖“某个高手在场”,而依赖明确的规则与可复现流程。团队规模扩大后,这一点的价值会持续上升。
附录六:术语与约定
为了降低认知成本,文档中关键术语统一如下:
- 流程收敛:任务在可接受时间内得到可交付输出。
- 降级:当外部依赖异常时,主动切换到低风险路径。
- fallback:无法走主路径时的替代生成策略。
- 发布闸门:上线前的强制校验集合。
- launch 档位:上线标准档,要求长文质量与结构完整。
统一术语的意义是避免“同词异义”。当大家用同一套词描述问题,定位和协作都会更快。
附录七:版本演进记录(摘要)
- 初始版本:完成五层流水线与基础导出。
- 稳定性版本:引入上游不可用检测与降级态。
- 容错版本:扩写阶段改为软失败,减少全局中断。
- 渲染修复版本:修复 TOC 剥离、补齐书签、优化 CJK 换行。
- 发布规范版本:引入 standard/launch 双档位校验。
这份演进记录体现了项目方法论:每个版本都围绕一个明确问题,不做无边界大改。这样既能保证持续进步,也能保持系统可理解。
附录八:本章结论
如果把整个项目压缩成一句工程判断:这是一个以交付结果为中心、以异常处理为优先、以发布闸门为保障的文档生产系统。它的优势不是“某一次输出特别惊艳”,而是“多数情况下都能稳定完成,并且结果可验证、可追溯、可迭代”。
也正因为如此,这个项目适合作为团队级内容生产基础设施,而不仅仅是个人效率脚本。随着后续自动检查、质量增强和指标体系落地,它会从“可用工具”逐步演进为“可依赖平台”。
附录九:实施样例拆解(从输入到成稿)
为了让方法可复制,这里给一个标准化实施样例。样例不是写死脚本,而是把动作抽象成可执行步骤。
步骤一,定义任务输入。输入最少包含主题、资料来源、输出目标。主题用于确定大纲方向,资料来源用于事实支撑,输出目标用于约束体量和格式。没有这三项,生成任务容易在中途偏航。
步骤二,确定生成参数。包括目标字数、章节数量、每节字数上限、并行度、是否启用风格重写、是否启用最终评审。参数的价值在于把“写得差不多”变成“达到阈值”。
步骤三,执行流水线。流程运行时要关注阶段日志,而不是只看最终是否报错。阶段日志能告诉你问题是在采集、规划、生成、重写还是组装环节出现。
步骤四,执行质量补偿。当质量报告提示字数不足或结构缺失时,先做补偿再导出。补偿策略遵循“最小扰动”,优先扩充短节、保留原结构,不做大规模重写。
步骤五,导出并验收。导出后同时验证 Markdown 与 PDF,确认目录、分页、章节与样式可用,最后再进入发布闸门。
这个样例的目的不是替代业务判断,而是提供稳定模板:任何人按这五步执行,都能得到可交付结果。
附录十:性能与成本观察
在文档系统里,性能不是指毫秒级响应,而是一次任务从启动到可交付的总耗时。我们观察到影响总耗时的因素主要有四个:
- 上游模型波动:这是最大的外部变量。
- 章节设计粒度:章节过细会放大调用次数。
- 并行参数设置:并行过高会提高失败率,并行过低会拉长总时长。
- 补偿策略触发频率:字数补偿越多,总时长越高。
成本方面同样要平衡。若只追求最低 token 成本,往往会牺牲可读性;若只追求最优文风,成本会快速上升。当前策略是把成本控制在“可长期运行”区间:优先保证稳定与结构,再逐步优化文字表现。
在工程管理上,推荐把“每轮耗时、失败类型、降级次数、补偿次数”作为基础指标保存。哪怕先用最简单的日志统计,也能帮助后续调参。
附录十一:协作流程建议
当这个系统从个人使用走向团队使用时,必须补齐协作规约。建议至少建立以下机制:
- 选题入场规范:明确主题、受众、目标篇幅。
- 资料引用规范:来源可追溯,避免无依据结论。
- 章节评审规范:先审结构,再审文风。
- 发布责任规范:谁发起、谁验收、谁回滚。
团队协作的关键不是“每个人都很强”,而是“流程让普通执行也能稳定输出”。文档系统尤其如此,因为它涉及跨角色协作:内容、技术、运营往往同时参与。
附录十二:案例化验收模板
为了减少主观争议,我们给出一个可直接复用的验收模板。
- 输入完整性:主题、来源、目标格式是否齐备。
- 流程完整性:五层流水线是否执行完毕。
- 结构完整性:必需章节是否覆盖。
- 文本完整性:字数阈值是否达标。
- 版式完整性:目录、页码、标题层级是否正常。
- 发布完整性:schema、质量、构建三层是否通过。
每项只允许“通过/不通过”二值结论,不做模糊评价。这样可以显著缩短发布讨论时间,也便于自动化落地。
附录十三:常见误区清单
误区一:把 prompt 调优当成全部工作。实际上,prompt 只影响局部质量,不能替代流程稳定性。
误区二:把重试次数当成鲁棒性。真正的鲁棒性是错误分类与降级策略,而不是盲目重试。
误区三:忽视导出层。内容再好,若导出不可读,交付价值依然很低。
误区四:发布无闸门。缺少闸门会让线上质量波动,最终把修复成本放大到不可控。
误区五:一次性大改。长链路系统更适合小步迭代,逐项验证,避免“改动太多无法定位问题”。
避免这些误区,本质上就是把系统当工程产品,而不是一次性脚本。
附录十四:最终结论
到当前版本,这个项目已经满足 launch 档位所强调的三件事:
- 可交付:流程能在波动环境下稳定收敛。
- 可验证:结构、字数、章节、产物都可核对。
- 可迭代:后续优化有清晰路线与优先级。
因此它不再只是“自动写文工具”,而是一套具备工程纪律的内容生产管线。未来只要持续沿着“稳定性—质量—效率”三轴优化,这个系统会在更多主题与团队协作场景里稳定复用。
附录十五:执行层面的最小治理规则
为了让这套流水线在团队内长期稳定运行,我们补充一组执行层面的最小治理规则。规则不是为了增加审批,而是为了把“靠经验”转换成“靠机制”。
第一条规则是输入治理。每次任务开始前,必须明确主题、受众、目标长度、交付格式四项输入。缺一项就不进入生成阶段。这样做能显著降低后续返工,因为大多数偏航都源于输入边界不清。
第二条规则是过程治理。每一层处理都要写出阶段日志,至少包含开始时间、结束时间、失败类型、是否触发降级。日志的价值不在于“记录很多字”,而在于让问题可以回放。只要可回放,定位就会越来越快。
第三条规则是产物治理。产物目录需要固定命名规则,版本要可比对,Markdown 与 PDF 的基准版本必须一一对应。我们避免“同名文件多次覆盖”的做法,因为这会让验收和回滚失去锚点。
第四条规则是发布治理。进入发布闸门前先跑自动校验,再做人审;顺序不能反。自动校验负责兜底结构与工程一致性,人审负责语义准确与表达质量。两者职责分离后,协作效率更高。
这四条规则的本质是:用确定性的流程管理不确定性的外部依赖。只要规则持续执行,系统质量就会稳定抬升,而不是靠某次“临场发挥”决定结果。
附录十六:对团队复用价值的补充说明
从团队管理视角看,这个系统的复用价值体现在三点。第一,它把“写作产能”从个人技能转化为流程能力;第二,它把“交付风险”从隐性经验转化为显性闸门;第三,它把“问题修复”从事后补救转化为事前约束。三者叠加后,内容生产就不再依赖个体记忆,而依赖稳定机制。
因此我们给出的最终判断是:该系统已经具备作为团队级内容基础设施的条件。后续持续建设只需围绕稳定性、质量和效率三条主线迭代,不需要再回到“临时脚本 + 手工救火”的旧模式。