从零搭一个中文电子书 RAG:为什么先做最小闭环

目录

从零搭一个中文电子书 RAG:为什么先做最小闭环

系列定位:这是一个从 MVP 开始打磨 RAG 系统的实战记录,不是“企业级最终方案”的包装稿。
阅读时间:8 分钟
适合读者:想自己搭 RAG、正在做知识库问答、希望理解工程取舍的开发者
关键词:RAG、中文电子书、MVP、Spring Boot、PostgreSQL、pgvector

先把预期说清楚

我最开始也很容易把 RAG 系统想大:多租户、权限体系、异步队列、监控告警、缓存、对象存储、模型网关、自动评测,最好再配一个漂亮的后台。

但真正动手之后会发现,如果第一版就奔着“企业级完整架构”去,很快会被细节淹没。RAG 的核心价值其实很朴素:

  1. 用户能上传资料。
  2. 系统能把资料解析成可检索的文本块。
  3. 文本块能被向量化并持久化。
  4. 用户提问时,系统能召回相关片段。
  5. 大模型回答时,能带上来源,避免凭空编造。

所以 my-rag 的第一阶段没有追求大而全,而是先做一个中文电子书 RAG 的最小闭环:上传、解析、切块、向量化、检索、问答、引用来源

这篇文章讲的不是“我设计了一个完美架构”,而是“我为什么先把系统压到这个范围,以及这个范围里的关键取舍是什么”。

一、RAG MVP 的边界

1.1 先解决一个具体场景

泛泛地说“企业知识库”太宽了。不同企业的文档来源、权限模型、更新频率、合规要求都不一样,第一版很难一次吃下。

我选择的起点是中文电子书和文档资料:

  • 文档通常比较长,能真实暴露分块和上下文构建问题。
  • 中文内容多,对检索和 token 估算更敏感。
  • 用户问题往往需要引用出处,适合验证 RAG 的可信度。
  • 文件上传、解析、索引、问答可以形成完整闭环。

这个场景足够小,可以落地;也足够复杂,不至于只做一个玩具 demo。

1.2 第一版只保留必要能力

当前 my-rag 的能力可以概括为七件事:

  • 上传文档并持久化元数据。
  • 文档先解析并切块,进入 CHUNKED 状态。
  • 用户确认 embedding 费用后,再生成向量,进入 READY 状态。
  • 使用 PostgreSQL + pgvector 做相似度检索。
  • 可选启用关键词索引,和向量召回做混合排序。
  • Chat 问答只基于召回片段回答,并返回来源引用。
  • 记录 Chat log,方便复盘“召回错了还是回答错了”。

这里故意没有把权限、多租户、对象存储、消息队列、Redis 缓存放进第一版。它们都重要,但不是证明 RAG 闭环成立的前置条件。

1.3 一个容易踩的坑:过早企业级

“企业级”这个词很诱人,但它也很容易让系统在还没验证核心价值之前就变复杂。

比如消息队列当然可以提升文档处理的可靠性;但如果还不知道 chunk 的质量是否够用,先引入队列只会让问题更难定位。Redis 缓存也很有用;但如果召回策略本身不稳定,缓存只是在加速错误结果。

所以第一版的原则是:先让问题暴露在简单链路里,再决定哪里值得加复杂度

二、整体链路长什么样

my-rag 的主流程可以拆成两段。

第一段是文档进入知识库:

flowchart LR A[上传文档] --> B[保存文档元数据] B --> C[解析文本] C --> D[按章节和段落切块] D --> E[估算 token 与 embedding 成本] E --> F[用户确认] F --> G[生成向量] G --> H[文档进入 READY]

第二段是用户提问:

flowchart LR A[用户提问] --> B[问题向量化] B --> C[召回相关 chunk] C --> D[构建上下文] D --> E[调用大模型] E --> F[返回答案和来源] F --> G[写入 Chat log]

这两个流程共同回答一个问题:系统是否真的能把“我的资料”变成“可追溯的回答”。

三、技术选型:够用比漂亮更重要

3.1 后端:Spring Boot + Maven 多模块

后端选择 Java 17 和 Spring Boot 3。原因很简单:这套技术栈成熟、调试方便,适合把业务状态流转写清楚。

项目拆成几个模块:

  • rag-api:请求和响应 DTO。
  • rag-common:公共响应、错误码等基础能力。
  • rag-service:核心业务逻辑,包括文档、切块、embedding、检索和 chat。

这不是为了显得架构复杂,而是为了让 API 契约和业务实现分开,后面前后端联调时会舒服很多。

3.2 数据库:PostgreSQL + pgvector

第一版没有引入专门的向量数据库,而是使用 PostgreSQL 加 pgvector

这个选择背后的考虑是:

  • 文档、chunk、chat log 本来就需要关系型数据。
  • MVP 阶段的数据规模可控,PostgreSQL 足够承载。
  • pgvector 可以直接在数据库里完成向量相似度排序。
  • 迁移和本地开发都比较简单。

数据库里最核心的三类表是:

  • rag_document:文档元数据和状态。
  • rag_document_chunk:解析后的文本块。
  • rag_chunk_embedding:chunk 对应的向量。

这个结构比“把所有东西塞进一个 JSON”麻烦一点,但它让状态追踪、错误排查和后续统计都更清楚。

3.3 前端:React + Ant Design

前端没有做复杂的视觉设计,重点是把关键操作做完整:

  • Dashboard 看整体状态。
  • Documents 上传和管理文档。
  • Document Detail 查看文档、chunk、索引进度。
  • Chat 进行问答。
  • Chat Logs 回看历史回答和来源。
  • Settings 配置模型参数。

RAG 产品里,前端不是只有一个聊天框。文档处理状态、费用确认、来源引用、失败提示,都会直接影响用户是否信任系统。

3.4 模型:embedding 和 chat 分开配置

系统把 embedding 和 chat 拆成两类模型配置。

embedding 用于把文档 chunk 和用户问题转成向量;chat 用于根据召回上下文生成回答。这样做的好处是,后续可以单独替换其中一个模型,而不影响另一条链路。

本地开发默认可以走 mock provider,避免每次启动都依赖真实 API Key。接真实服务时,再通过本地 secret YAML 或环境变量配置。

四、最重要的设计不是架构图,而是状态流转

RAG 系统最容易混乱的地方,是文档状态。

如果一个文档刚上传,还没解析,能不能被检索?如果解析成功但还没 embedding,能不能进入 Chat?如果 embedding 失败,用户应该看到什么?

我把文档流程拆成几个明确状态:

  • UPLOADED:文件已上传。
  • PARSING:正在解析。
  • PARSED:解析完成。
  • CHUNKED:已经生成 chunk,可以估算 embedding 成本。
  • EMBEDDING:正在生成向量。
  • READY:可以参与检索和问答。
  • FAILED:处理失败,需要展示错误原因。

这个状态机不花哨,但非常关键。它让前端可以明确提示用户下一步该做什么,也让后端避免把半成品文档混进检索结果。

五、为什么要让用户确认 embedding 成本

RAG 的费用常常不是来自聊天,而是来自批量 embedding。

如果用户上传一本很长的电子书,系统直接开始向量化,既不透明,也容易让调试成本失控。因此 my-rag 选择把索引拆成两步:

  1. 先 parse + chunk,得到 chunk 数和本地 token 估算。
  2. 前端展示预估费用,用户确认后再调用 embedding API。

这个交互看起来多一步,但它让系统从一开始就有成本意识。

当然,本地 token 估算不会和模型服务账单完全一致,所以界面也应该明确提示:预估只用于决策,最终费用以服务商账单为准。

六、第一版架构的取舍

第一版架构的核心取舍,是先让闭环稳定、可解释,再决定哪些地方值得增加复杂度。文档处理确实适合异步化,但第一阶段先用简单链路跑通状态流转和错误处理,等文档处理量上来后,再把 parse、chunk、embedding 拆到队列里会更稳;本地文件系统也已经足够支撑 MVP,等需要多实例部署、备份、跨机器访问时,再接 MinIO 或云对象存储;权限、多租户、审计日志都应该做,但它们应该建立在知识库问答闭环稳定之后;检索也是同样的原则,向量检索、关键词检索、RRF、rerank 都可以逐步加入,但每加一个环节,都要能回答:它解决了哪个召回问题?它让结果更好,还是只是让链路更复杂?

七、这一版真正想验证什么

第一版 my-rag 不是为了证明“我能搭一个完整企业知识库平台”,而是验证三个更基础的问题:

  1. 文档处理是否稳定:长文档能不能解析、切块、索引,并在失败时给出清晰状态?
  2. 检索是否可调试:回答不好时,能不能看到到底召回了哪些 chunk?
  3. 答案是否可信:大模型是否只根据资料回答,并带上来源引用?

这三个问题如果没解决,后面加再多企业级能力也只是外壳。

结语:先让闭环跑起来

RAG 系统最怕一开始就被“大架构”带偏。真正值得先做的,是一条能被用户亲手验证的闭环:上传自己的资料,提出自己的问题,看到回答和来源,然后判断这个系统是否值得继续打磨。

my-rag 的第一阶段就是围绕这个目标展开。下一篇我会继续拆文档处理流水线:一个文件从上传到 READY,中间到底经过了哪些步骤,哪些地方最容易翻车。


下一篇:《RAG 文档处理流水线:从上传文件到可检索 chunk》