三周、三个 Agent、一个读书工具——一个 PM 的架构决策实录
当8500行代码沉淀为Read-Box这个AI读书助手时,最珍贵的不是技术实现,而是那些架构决策的瞬间。从共享存储层的选择到LLM抽象层的坚守,本文揭秘三个AI Agent协作背后的关键抉择,以及为什么产品经理必须读懂这些技术权衡。

先给你看组数字。
79 个源码文件,8500 行代码,29 个单元测试,3 周开发周期,36MB 安装包,外加 1 个 AI Skill。
这是我最近一个项目——Read-Box——的最终统计。
Read-Box 是什么?一个桌面端的 AI 读书助手。你把电子书扔进去,它能自动提炼章节摘要、你可以跟它对话问书里的内容、它还能出题考你。背后是三个 AI Agent 在协作。
做完这个项目后我写了一份项目回顾,翻看那些”当时这么选的”记录时,我发现一件有意思的事:
让我收获最大的,不是代码写了多少,而是那些”选 A 还是选 B”的瞬间。
架构选共享存储还是事件总线?LLM 抽象层的边界划在哪里?配置存在环境变量还是数据库?这些决策当时觉得就是在几个选项里挑一个,回头来看,每一个都深刻影响了后面几周的开发体验。
所以这篇文章不聊技术教程,我就聊聊这些决策是怎么做的——以及为什么我觉得,产品经理也应该理解这些决策逻辑。
一、从”想法”到”范围”
项目是怎么开始的
最初的想法很简单:做一个读书辅助工具。
你导入一本电子书,AI 帮你提炼出摘要和关键概念,你可以问它书里的内容,还能让它出题帮你巩固记忆。
听起来不复杂对吧?但一拆解就发现,背后有三个 AI Agent 需要协作:
- 提炼 Agent:逐章生成摘要、关键概念、金句
- 问答 Agent:基于书籍内容回答问题
- 陪练 Agent:根据书籍内容出题、评判
核心问题来了:这三个 Agent 之间怎么协作?
提炼产出摘要和概念,问答需要这些信息来回答,陪练需要这些信息来出题。如果让它们直接互相调用,就会出现”提炼→问答→陪练”的链式依赖。耦合度高、扩展性差——哪天想加个”高亮标记”,得改三个 Agent 的代码。
三个方案摆在面前
我们讨论了三种架构方案:
方案 A:共享存储层。三个 Agent 都读写同一个 SQLite 数据库,不感知彼此存在。提炼写完数据就走,问答和陪练从数据库读需要的信息。
方案 B:Agent 编排器。一个 Orchestrator 串行调度 Agent,流程控制强,但编排器本身可能成为单点瓶颈。
方案 C:事件总线。提炼完成发出事件,问答和陪练订阅。最灵活,但对 v1.0 来说过度设计了。
我们选了方案 A。
为什么选共享存储层
理由其实就一句话:让 Agent 不感知彼此存在。
每一个 Agent 都可以独立开发、独立测试、独立部署。提炼 Agent 不需要知道问答 Agent 的存在,反之亦然。测试的时候只需要 mock SQLite,不用启动其他 Agent。
这个决策后来被反复验证是对的。加陪练 Agent 时,问答 Agent 完全不受影响。提炼调用 LLM 失败时,问答和陪练照样能工作。
我们还做了一个关键设计:问答和陪练共享同一个上下文管理器。原因也很简单——两者都涉及对话状态(当前书籍、章节、历史消息),分开管理一定会出现状态不一致。后来果然,因为共用了同一个管理器,切换书籍时状态同步、进度一致、历史完整。如果当时分了两个管理器,这些全得重新联调。
对 PM 来说,判断架构好坏的标准不是”多先进”,而是”加需求时要不要改已有的代码。”
二、项目架构拆解——为什么要把 LLM 抽成独立一层?
聊完了 Agent 怎么协作,来看看 Read-Box 的整体架构。

四层架构
前端展示层:Vue 3 + Tauri 桌面壳
↓ HTTP / SSE
API 路由层:FastAPI(接收请求 → 调度 Agent)
↓
Agent 层:提炼 Agent / 问答 Agent / 陪练 Agent
↓
存储层:SQLite(书籍数据 / Agent 产出 / 用户配置)
这就是 Read-Box 的四个层级。每层各司其职,上层不关心下层的实现细节。
那问题来了——LLM 放在哪一层?
它不在任何一层里。它是 Agent 层的”基础设施”——三个 Agent 都需要调用 LLM,但如果每个 Agent 各调各的,就会出问题。
为什么 LLM 必须独立出来
需求从一开始就明确了:支持多种模型。DeepSeek、Ollama……用户可以在界面上配置模型。
如果不做抽象,三个 Agent 各写一套 API 调用代码,后果是什么?
- 换模型要改三处
- 代码加一个新模型
- 要改三处API 参数
- 变化要改三处
- 出错处理逻辑复制三遍
所以我们在项目宪法里写了一条不可商议的原则:所有 AI 调用必须通过统一的 Provider 接口。
这个接口有多简单?核心就一句话:
async def chat(messages: list[dict]) -> str
发消息,收回复。没了。
不管底层是 DeepSeek 还是 Qwen 还是 Ollama,对 Agent 来说,调用方式始终不变。
LLM Provider 的结构是这样的:
LLMProvider(抽象基类)
├── OpenAIProvider(兼容 DeepSeek 等)
├── ClaudeProvider(预留)
└── OllamaProvider(预留)
三个 Agent 只跟基类打交道,不关心具体是哪个 Provider。加新的模型只需要写一个新类,然后在工厂方法里加一行。
配置方案的三次迭代
但这里有个有意思的故事——用户怎么配置 LLM?
v1:从环境变量读。用户手动编辑 .env 文件。开发者觉得方便,但用户要打开文件系统、找到文件、编辑、保存……体验极差。
v2:前端配置页存入 SQLite。用户在前端页面填写 API Key 和模型名称,保存到数据库。体验好了,但问题来了——Agent 启动时从环境变量读配置,用户保存到数据库的配置根本没生效。配置了等于没配。
v3(最终):路由层读取数据库配置。API 路由层在处理请求时,异步从数据库读取用户配置,创建 Provider 后直接注入 Agent。Agent 只管调用 provider.chat(),不关心配置怎么来的。
用户配置 → SQLite app_config 表
↓
路由层读取数据库 → 创建 ProviderConfig → 调用 create_provider() → 注入 Agent
↓
Agent 调用 provider.chat() —— 不关心配置来源
为什么最终方案能跑通?因为配置的存储位置和读取时机终于一致了——都存在数据库里,都在请求时读取。
对 PM 来说,这个迭代的启示是:用户配置看起来是一个小功能,但涉及”存哪里、什么时候读、谁负责读”三个问题。很多小功能做不好,不是因为逻辑复杂,而是因为这三个问题没想清楚。
这个抽象被验证是对的
后来我们加了一个”标注/高亮模块”。用户在阅读时标注重点内容,这些高亮在提炼时被加权处理。
改动量非常小:高亮数据存 SQLite,提炼 Agent 从数据库读取,问答和陪练完全不受影响。因为 Agent 之间本来就不直接调用,而是通过共享存储层交换数据。
宪法里写”LLM Provider 抽象层不可商议”那条,后来被验证是整项目最正确的决策之一。
判断”什么东西该抽象”的标准很简单:问自己”这个东西未来会不会变?变了需要改几处?”如果一个变化要改多处,那就该抽出来。
三、开发流程:Spec Kit 的第一手体验
说完了架构,聊聊怎么把想法落地成代码的。
这次项目试了 GitHub 上一个开源工具——Spec Kit,规格驱动开发。流程分五步:

宪法——5 条原则
项目宪法定了 5 条核心原则,印象最深的是两条:
“LLM Provider 抽象层不可商议。”这条前面已经讲过了。当时写它的时候觉得”哦,这种基础设施肯定要抽象的”,结果后面切换模型时,发现这条确实救了大命。
“核心逻辑测试优先,pytest 80% 覆盖。”实话实说,这个没做到。29 个测试覆盖了解析引擎的核心逻辑,但对 Agent 的测试因为需要 mock LLM,覆盖率不够。回头来看,应该先用 mock LLM 把 Agent 的测试写好,而不是后面补。
写规格——最花时间的步骤
每个功能写一个 Spec,从需求描述到验收标准到数据模型。
v1.0 写了 5 个 Spec:
1. 项目脚手架
2. 书籍解析引擎
3. AI 提炼 Agent
4. 问答 Agent
5. 陪练 Agent
写规格真的很花时间。比我想象的久。但它的价值在于——你把所有”我以为我想清楚了”的地方揪出来。写规格的时候发现的问题,比写代码的时候发现的问题,修复成本低很多。
我们还做了一个灵活的调整:小改动跳过流程。v1.1 的首页优化,改的是 CSS 样式和布局,不需要走完整五步,直接写代码就行。流程是工具,不是教条。
对 PM 来说,”规范”不是为了约束,是为了让团队在关键事情上不犯错。知道什么时候该严格、什么时候该灵活,比严格执行更重要。
Prompt 设计的演进
这次项目还有一条意外的收获——Prompt 设计也是一个迭代过程。
以提炼 Agent 为例:
初版:”给我总结这章。” 结果太长,结构随意。
第二版:加了 system prompt 约束长度和风格。”200-500 字,保留核心观点,不要写’本章介绍了’这类冗余开头。”
最终版:要求 LLM 输出 JSON 格式,便于解析入库。高亮内容在 prompt 中前置加权。
最有用的改进是哪个?明确在 prompt 里写”不要写’本章介绍了’这类冗余开头”——效果好到出乎意料。你看,跟模型沟通和跟人沟通有时候很像:不要只说”你要做什么”,也要说”不要做什么”。
额外的产出:一个 AI Skill
项目做完后,我还把 Read-Box 的核心能力提炼成了一个Claude Code / Codex 可用的 Skill。
结构很简单:一个统一的入口命令 /read-book,加上几个解析脚本和测试用例。不是零散的好几个 Skill,就一个——入口越少,用起来越顺手。
为什么要做这个?
因为项目的价值和项目的形态是两回事。Read-Box 是一个桌面 App,但它的核心能力(解析书籍 → 提炼 → 问答 → 陪练)同样可以在命令行里跑。Skill 让这些能力脱离了桌面 App 的束缚,在任何支持 Claude Code 的环境里都能用。
一个产品做完后,如果能把它核心能力提炼成可复用的”技能”,它的生命周期就不止于这个产品本身。
四、真实世界的”坑”——项目不只架构
架构聊完了,流程聊完了。说点更真实的——那些文档里不会写、但实际开发时花了一堆时间解决的事。
pnpm 版本升级
pnpm 升级到 11 之后,引入了新的安全机制,导致构建一直失败:
[ERR_PNPM_IGNORED_BUILDS] Ignored build scripts: esbuild@0.25.12
折腾了半天,试了 .npmrc 配置、package.json 配置,最后在 pnpm-workspace.yaml 里找到正确解法。
教训:开发前确认包管理器的版本兼容性。升级大版本前,先看 changelog。
CORS 配置
前后端分离架构下,CORS 配置反复改了三次:
1. 初版:`allow_origins=[“*”]` + `allow_credentials=True` → 部分浏览器报错
2. 改版:显式列出所有端口 → 端口一换就崩
3. 最终:`allow_origins=[“*”]` 不加 credentials → 开发阶段够用
教训:开发阶段怎么简单怎么来,生产环境再收紧。
打包 Python 后端是最难的
整个项目最大的技术难点不是 AI Agent,不是架构设计,而是——把 Python 后端打包进桌面应用。
一个桌面应用不能要求用户装 Python,所以要用 PyInstaller 把 Python 后端打包成 exe。这个过程经历了 4 次方案变更:

关键教训:–onefile 模式虽然生成单个 exe 看起来很整洁,但解压到临时目录后 sys.path 路径不对。–onedir 模式生成一个目录,路径问题少得多。
那些修过的 Bug

教训:异步编程中遗漏 await 是 Python 最常见的问题。SSE 流式输出要考虑换行符转义。前端 Axios 超时默认 5 秒太短,LLM 调用往往需要 10-30 秒。
花了多少时间?
如果你想知道这些”坑”占了多少时间——大概占了项目总时间的 30-40%。
功能性需求(解析引擎、Agent、对话、出题)大概 60%,剩下的全是在处理环境、配置、打包、兼容性问题。
对 PM 来说,排期时一定要给这些”非功能性”的事情留 buffer。它们往往比功能性需求更花时间,而且更不可预测。
写在最后
总结这个项目,有四点感触最深:
第一,架构决策的核心不是技术,是”未来怎么改”。
选共享存储层、做 LLM 抽象、定宪法原则——所有这些决策的出发点不是”现在多方便”,而是”以后加需求要改几处代码”。判断架构好坏的标准很朴素:三个月后你还能不能快速加功能?
第二,写规格是最花时间的,但也是最有价值的。
Spec Kit 的实践让我相信一点:写代码前把规格写清楚,不是浪费时间,是节省时间。你只是想得快,做得慢。前面想清楚,后面反而快。
第三,学会不断打磨 AI 工具的能力。
回头看我做 Read-Box 的过程,提炼 Agent 的 prompt 迭代了三版、问答 Agent 的 system prompt 反复调、SSE 流式输出的格式折腾了好几次——每一步都在”打磨”。项目做完后再把核心能力提炼成 Skill,本质上也是对 AI 工具能力的一次封装和升级。
打磨不只是”写更好的 prompt”,而是理解 AI 工作的方式,知道它擅长什么、不擅长什么,然后持续调整。这个能力和写代码没关系,纯靠动手试、迭代、总结。
第四,AI 时代的产品经理需要理解架构决策的逻辑。
不是为了自己写代码,是为了和工程师说同一种语言。当工程师说”耦合度高”、”扩展性差”、”抽象层不够”的时候,你知道他们说的是什么问题,知道该怎么权衡。
一个朋友跟我说过一句话,我觉得很对:
“你不会被 AI 取代,你会被更会用 AI 的人取代。”
但我想补一句——”会用 AI”不是指会用 ChatGPT 写周报,而是能理解 AI 应用是怎么搭起来的,知道架构决策背后的 trade-off。关于这个项目
Read-Box 已开源,你可以在 GitHub 上查看完整源码:
👉github.com/wenhui426/read-box
79 个源码文件、8500 行代码、29 个单元测试。如果你对 Agent 协作架构、LLM 抽象层实现、Tauri + FastAPI 桌面应用感兴趣,欢迎 star 和 fork。也欢迎提交 issue 和 PR——一个人写代码虽然快,但一群人写才更有意思。
本文由人人都是产品经理作者【王耑】,微信公众号:【职场产品人】,原创/授权 发布于人人都是产品经理,未经许可,禁止转载。
题图来自Unsplash,基于 CC0 协议。
- 目前还没评论,等你发挥!

起点课堂会员权益




