构建自己的AI图书生成器
我的孩子有非常具体的兴趣和信息需求。他会让我找关于金属或信号警报器等狭窄主题的书籍,而大多数时候,这样的书根本不存在。或者即使存在,也不是儿童读物。
所以我构建了一个系统来为他生成这样的书。这一切始于一次ChatGPT的实验。当我验证了对更自动化、更复杂方法的需求后,这个项目演变成了一个使用AI生成整本书的自动化流水线。
在本文中,我将分享:
- 第一本关于金属的书是如何启动这个项目的
- 为什么直接使用ChatGPT生成不够用
- 我如何使用编程代理来创建第一批书
- 为什么我后来构建了一个专门的图书生成器
- 当前的流水线是如何规划、编写、格式化和打包图书的
- 音频、EPUB和Amazon KDP输出是如何生成的
- 当我尝试在Amazon上出版其中一本书时发生了什么
1、第一个请求:一本关于金属的书
他的第一个请求是一本关于金属的书。他想知道金属是什么以及它们如何被使用。他只关心一个特定的列表:钯、锡、镁、钛、铍、锂和钨。他确切地告诉我应该描述哪些属性。ChatGPT建议添加一些其他金属来完善整体,我儿子同意了。我们计划每种金属一章,涵盖其历史、提取和用途。
我们找不到这样的书,而他想在睡前听。我首先尝试用ChatGPT直接生成这本书。我们反复讨论了目录。我让模型写第一章,但结果非常差。仅靠ChatGPT并不奏效。
然后我考虑了编程代理。它们擅长规划和执行。我构建应用的常规方法是与ChatGPT迭代设计,然后将实现委托给编程代理。我认为同样的模式在这里也应该适用。我可以用ChatGPT生成目录,然后让编程代理写实际的书。
2、与编程代理迭代编写金属书
我和儿子最终确定了大纲。我将计划交给了编程代理。第一个版本很粗糙。代理写了要点而不是散文。编程代理可能更倾向于文档写作而非叙事写作。
金属书目录的一部分,显示每种金属一章,带有描述性的副标题,如"金:法老和海盗的金属"或"钨:最高熔点"。
我收紧了提示,明确要求写成正常的散文。我像管理常规编程项目一样管理这个过程。我创建了一个GitHub issue,让代理去处理,审查结果,然后创建下一个issue。这与我在智能手机上编程时使用的方法相同。我在之前的通讯中描述过:
另一个问题是上下文限制。代理没有完成任务。如果我要求它用正确的散文重写整本书,它会重写前五章然后停下来,因为它"累了"。我不得不反复推动代理完成整个文本。
与此同时,我让代理设置自动化。我首先想把书作为网站发布,这样我就可以在浏览器中阅读。后来我添加了EPUB输出,只是因为我觉得很有趣。自动化效果很好。金属书的文本需要大量手动指导,但我的孩子喜欢最终的结果。
这本已发布的书位于 https://alexeygrigorev.com/little-book-of-metals-ru/。源代码:github.com/alexeygrigorev/little-book-of-metals-ru。
3、镓和钾合金
在金属项目进行到一半时,我儿子决定他只对两种特定的金属感兴趣:镓和钾。他特别想了解它们的合金,因为它具有不寻常的性质。我们为这个主题单独做了一本书。
到那时,我已经理解了这个工作流程。我把想要的内容直接放进了第一个提示中。从金属书获得的经验立即得到了回报。这本书在第一次尝试时就做得很好。整个项目只需要两个GitHub issue。一个issue是写书,第二个issue是添加EPUB和MOBI发布。
镓-钾书籍仓库中的两个已关闭issue:"Написать книгу"(写书)和"epub and mobi generation"。
源代码:github.com/alexeygrigorev/gallium-kalium-book-ru。
4、针叶树
后来,我儿子在学校学习了针叶树后,想了解更多关于它们的知识。到那时,工作流程已经很标准化了。我们与ChatGPT讨论主题,制定计划,创建GitHub issue,让Copilot处理它,然后收到完成的书。
源代码:github.com/alexeygrigorev/conifers-book-ru。
5、构建专门的图书代理
在第三本书之后,我意识到像Copilot这样的通用编程代理不是最好的工具。我决定构建一个专门的程序,原因有两个。首先,我需要为我的课程参与者提供一个用例,展示如何构建专门的代理系统。其次,我怀疑专门的代理会产生比针对代码调优的模型更好的叙事文本。
我构建了这个程序来比较GPT、Claude和新发布的Gemini模型。我的评估是非正式的。我只是和孩子一起读这些书,然后挑选最好的文本。Gemini产生了最好的结果。
这个假设在实践中得到了验证。专门的代理写的散文比通用编程代理更好。
工作流程确定为一个标准的"先规划后执行"模式,与我在编程中教授的相同:
- 在聊天界面中迭代以最终确定目录。
- 将聊天输出转换为包含章节、部分和要点的结构化YAML计划。
- 使用
for chapter in plan: generate_chapter(...)循环遍历计划。每次迭代接收之前章节的压缩摘要,以在不传递整个文本的情况下保持上下文。
6、此后的许多书
我首先重新制作了金属书,只是为了测试新的流水线。从那以后,该系统成功处理了几个非常具体的请求。这些包括一本关于警报警报器的书、一份关于烟花工作原理的指南,以及一本完全专注于缆索驱动机构(如缆车)的书。
7、图书生成器的工作原理
这个仓库提供了一个端到端的规划和生成图书的流水线。它从一个交互式规划步骤开始,将计划转换为类型化的YAML文件,从该计划生成章节,然后生成音频、EPUB和Amazon Kindle Direct Publishing印刷成品。
你可以通过Streamlit界面或命令行脚本使用该系统。主要的设计模式是先规划,然后执行。书籍结构是预先确定的,后续的生成和发布步骤使用该结构作为其事实来源。
7.1 计划生成
第一阶段是交互式的,因为系统将书籍结构视为在文本生成开始之前需要审查的内容。
你使用 make ui 启动Streamlit界面。
运行 make ui 后可以看到的Streamlit应用界面
在界面中,你输入一个主题并选择书籍大小,如小型、中型或大型。然后你开始与Gemini 3 Pro Preview聊天。模型将草稿书籍计划流式传输到界面中。
你可以通过聊天来完善计划。每次完善调用都会将当前计划连同你的新反馈一起发送回Gemini,因此模型会更新现有大纲而不是从头开始。
当大纲看起来不错时,你点击:"Ready - Create Structured Plan"。这将工作流程从自由格式的规划转移到结构化的执行。对于无头运行,有一个命令行替代方案。你运行 uv run python -m chapter_based.plan -p books/mybook/input.txt 来从文本文件而不是Streamlit聊天构建计划。
7.2 结构化计划创建
自由格式的聊天计划对人类有用,但生成器需要一个可预测的结构。下一步将大纲转换为执行脚本可以读取的类型化计划。
系统将最终确定的计划再次发送给Gemini,并使用 response_mime_type="application/json" 和从Pydantic BookPlan 模型生成的schema来请求JSON输出。结构化输出保存到 books/<slug>/plan.yaml。
以下是YAML结构的一个片段:
name: My Book
slug: my-book
book_language: ru
parts:
- name: Part One
introduction: ...
chapters:
- name: Chapter Name
bullet_points:
- point one
- point two
这个 plan.yaml 文件是规划、编写、音频生成和发布之间的契约。编写脚本使用它来生成章节。发布脚本使用它来获取元数据和结构。封面流水线使用它来获取封底文本。
这种分离很重要,因为它允许规划界面在不重写执行流水线的情况下进行更改,前提是它仍然产生相同的结构化计划。
每个部分的引言和封底文本也来自计划中已有的字段,在生成过程中不需要额外的模型调用。
7.3 逐章执行
一旦 plan.yaml 存在,编写脚本就不再需要聊天历史。它们使用结构化计划作为事实来源。
如果你使用命令行,你运行 uv run python -m chapter_based.execute mybook。还有一个Makefile命令:make generate-book。
仓库包含两种生成实现:
book_generator/:基于部分的生成。它每章使用更多的LLM调用,并提供更细粒度的控制,这对较长的书很有用。chapter_based/:基于章节的生成。它每次调用生成一整章,通常每章产生3000到5000个词。
基于章节的路径更清晰地展示了流水线。
脚本 chapter_based/execute.py 加载YAML文件,将部分扁平化为单个章节规范列表,并逐一遍历它们。
对于每一章,脚本构建一个包含完整章节列表的书籍进度字符串:
- 已完成的章节标记为
[x]。 - 当前章节标记为箭头。
- 剩余章节标记为
[ ]。
Gemini接收这个大纲以及当前章节的要点。它不会接收之前章节的完整文本。连贯性来自于预先制定的计划,而不是将实际的先前章节文本传递到每个生成调用中。
这使得初始计划的质量变得重要。计划需要足够的结构来保持全书各章的一致性。
7.4 音频生成
在markdown章节存在之后,同一书籍文件夹可以用于生成内容的音频版本。我添加了这部分是因为我儿子有时想听书而不是阅读。实现使用Gemini文本转语音,因为它的语音质量比之前在单独的AI睡前故事项目中使用的设置更好。
你可以使用 make tts BOOK=... 启动文本转语音生成。脚本 book_generator/tts.py 调用 models/gemini-2.5-flash-preview-tts 并使用默认语音 Charon。脚本将返回的PCM音频封装到WAV文件中,并直接上传到S3存储桶。
生成使用 ThreadPoolExecutor 并行运行。它还使用成本锁和跳过已生成的检查,因此现有音频文件不会不必要地重新生成。
一个单独的脚本 scripts/convert_wav_to_mp3.py 将WAV文件转换为MP3。它使用 ffmpeg 并生成用于分发的MP3衍生文件。
8、发布流程
发布脚本也使用生成的markdown和相同的 plan.yaml,因此EPUB和印刷输出不依赖于文本是如何生成的。
有三个主要的发布命令。
make ebook BOOK=...将书发布为EPUB。scripts/convert_to_ebook.py聚合markdown文件并调整标题级别,使章节标题适合EPUB结构。然后它调用Pandoc,传入标题、作者、语言元数据和可选的封面图片。输出是EPUB文件。make kdp-interior BOOK=...将书发布为KDP内部PDF。scripts/create_kdp_interior.py聚合markdown文件,并通过XeLaTeX在Docker镜像中渲染以实现可重现性。输出是KDP就绪的内部PDF,具有6 x 9英寸裁切尺寸、镜像边距、书脊、用于西里尔文的DejaVu字体和生成的目录。生成的文件是 kdp_interior.pdf。make kdp-cover BOOK=...将书发布为KDP封面PDF。scripts/create_kdp_cover.py使用ReportLab构建环绕式印刷封面。它布局封底、书脊、封面图片和出血区域。封底描述来自 plan.yaml。书脊宽度使用页数 × 0.0025英寸计算。生成的文件是 kdp_cover.pdf。
9、成本追踪和操作约定
生成器在规划和编写过程中追踪成本,因为书籍长度的生成可能涉及长上下文、多次章节调用、重试和可选的资产生成。
函数 calculate_gemini_3_cost 使用2025年11月的Gemini定价层级,包括标准层级和超过200k上下文层级。它还将"思考"token作为输出token计费。
CostTracker 在生成过程中累积成本。在Streamlit UI中,在起草和完善计划时会实时显示运行总计。在文本生成过程中,终端输出也会在生成章节和部分时报告增量成本。
因为生成需要时间和金钱,系统将每个产物写入可预测的位置,并在重新生成之前检查它是否已经存在。
章节输出写入类似 books/<slug>/part_01/01_chapter.md 的路径。
一个典型的书籍文件夹包含:
books/<slug>/
plan.yaml
back_cover.md
cover.jpg
part_01/
01_chapter.md
02_chapter.md
part_02/
01_chapter.md
book.epub
kdp_interior.pdf
kdp_cover.pdf
脚本根据命名约定(如 books/<slug>/、part_XX/ 和可预测的产物名称)推断读写文件的位置。
生成脚本检查章节文件是否已存在。如果存在,脚本在重新运行时跳过该章节。书籍文件夹中的 _ready 哨兵文件将书籍标记为完成,并告诉系统不要再修改它。每步的 *_exists 检查防止系统重新生成已存在的产物。
所有后续步骤使用相同的书籍文件夹和相同的 plan.yaml。规划、编写、音频生成和发布通过文件而不是通过一个大型进程连接在一起。这种设计选择和操作约定使流水线具有幂等性和可恢复性:如果一次运行被中断,它可以从缺失的部分继续,而不是从头开始。
Makefile将工作流程连接在一起:
make ui
-> chat and create structured plan
-> make generate-book
-> make tts BOOK=...
-> make ebook BOOK=...
-> make kdp-interior BOOK=...
-> make kdp-cover BOOK=...
10、示例生成运行
在生成过程中,终端输出显示系统正在处理什么以及每个步骤花费多少。这使得生成器在长时间运行期间更容易监控。它还使成本在书籍仍在生产过程中就可见,而不是仅在完成后才可见。
一个例子是使用这个系统生成的烟花书。它产生了21章,花费了大约45分钟的挂钟时间,使用Gemini 3 Pro花费了大约4美元。
这个数字作为基准是有用的,但不应被视为固定估计。最终成本取决于模型、书籍长度、上下文大小、重试次数以及包含哪些可选步骤。Gemini Flash尚未在此工作流程中测试过,因此不清楚使用更便宜的模型会在多大程度上改变质量或成本。
11、成本和质量
一本完全生成的书一次运行成本不到5美元。具体数字取决于书籍和生成设置,但它提供了一个实用的数量级。系统目前使用Gemini 3 Pro进行书籍生成。这比使用更小或更快的模型更昂贵,但在这些实验中,输出质量证明了成本的合理性。对于一本要反复阅读的个人书籍,这个成本可能是可以接受的。
从早期实验开始,质量就足够好,值得继续这个项目。关于金属和警报器的书在家里很有用,可读性也足够好。这个观察导致了发布实验。如果这些书在私下有用,那么测试它们是否也可以为外部读者打包就是合理的。
12、发布实验和项目价值
这个项目最初是为了生成用于私人阅读的书籍。随着时间的推移,质量足够好,引发了另一个问题:这些书能否为实际出版做准备?
这改变了流水线的范围。仅仅生成markdown章节已经不够了;它需要产生符合真实出版要求的文件:用于电子书的EPUB、用于印刷的格式化内部PDF,以及用于Amazon KDP的全包裹封面PDF。
这就是为什么我添加了上面提到的发布脚本 convert_to_ebook.py、create_kdp_interior.py 和 create_kdp_cover.py,以端到端地测试完整的发布流程。这个测试验证了生产流水线,但没有验证需求。
技术流水线可以产生一个书籍包,但发布文件与销售一本书不是一回事。我测试的书已经在Amazon上大约五个月了。到目前为止,它售出了零本。
将生成的书上传到Amazon本身是不够的。销售需要一层单独的工作:市场研究、利基选择、定位、搜索优化、元数据、评论和推广。
这个项目仍然有用,但其当前价值主要是个人和技术性的。它作为一个为私人阅读生成书籍的系统,以及作为一个自动化发布的实际实验而运作。将其转变为商业工作流程需要生成器之外的受众研究和营销工作。
原文链接: Building an AI Book Generator with a Plan-Then-Execute Pipeline
汇智网翻译整理,转载请标明出处