从 0 到 1 构建个人知识库问答助手

中文 English

这个教程带你从 0 到 1 做一个个人知识库问答助手。目标不是堆满框架,而是让你真正理解一个 RAG 应用从用户提问到最终回答的完整链路。

完成后你应该能讲清楚:文档如何进入系统,为什么要 chunk,Embedding 和检索在做什么,Prompt 如何约束模型只基于资料回答,引用来源怎么做,以及如何用评测集持续改进。

最终效果

用户上传或放入一批 Markdown、PDF、网页笔记后,可以提问:

我的 30 天 AI 学习计划里,第 2 周应该做什么?

系统返回:

第 2 周建议重点完成 RAG 入门,包括阅读 RAG 文档、准备测试文档、实现文档切分、检索、RAG 最小版本、引用来源和 20 条测试问题。

来源:
- 30-day-plan.md / Week 2

推荐技术栈

模块推荐
语言Python
API 服务FastAPI
本地存储SQLite
向量检索先用内存或 SQLite,后续换 pgvector/Qdrant
文档格式先支持 Markdown,再支持 PDF
评测CSV/JSON + 简单评分脚本

总体架构

用户问题
  -> 后端 API
  -> 查询改写
  -> 检索相关 chunk
  -> 组装 Prompt
  -> 调用模型
  -> 解析答案和引用
  -> 记录日志和反馈

离线索引流程:

文档导入
  -> 文档解析
  -> 文本清洗
  -> Chunk 切分
  -> 生成 Embedding
  -> 保存 chunk + metadata + vector

Step 1:完成最小聊天调用

先不要做知识库。第一步只验证模型 API 能正常调用。你要做到:API Key 放在环境变量里,能发送用户问题,能打印模型回答,能记录耗时和错误。

Step 2:读取本地文档

先支持 Markdown 文件。读取文件后,把内容和用户问题一起放进 Prompt。

你是个人知识库助手。请只基于下面资料回答问题。

资料:
{document_text}

用户问题:
{question}

如果资料中没有答案,请说“资料中没有找到答案”。

这一步会很快遇到上下文太长的问题,所以不能长期把整篇文档塞给模型。

Step 3:实现 Chunk 切分

Chunk 是文档切分后的片段。新手可以先用简单策略:

{
  "chunk_id": "30-day-plan#week-2#001",
  "title": "30-Day Plan",
  "source": "checklists/30-day-plan.md",
  "section": "Week 2:RAG 入门",
  "text": "Day 8 阅读 RAG 文档..."
}

Step 4:先做关键词检索

在引入向量检索前,先做一个关键词检索版本。它不高级,但很适合帮助你理解检索流程。最低要求是输入用户问题,在 chunk 文本和标题中匹配关键词,返回 top-k chunk,并把 chunk 放进 Prompt 回答。

Step 5:加入 Embedding 和向量检索

Embedding 会把文本转换成向量,用来做语义相似度检索。

chunk text -> embedding model -> vector -> 保存
question -> embedding model -> query vector -> 相似度搜索 -> top-k chunks

Step 6:组装 RAG Prompt

把检索到的 chunk 作为“可用资料”,而不是让模型自由发挥。

你是个人知识库问答助手。

规则:
- 只能基于“可用资料”回答
- 不要编造资料中没有的信息
- 每个关键结论都要给出来源
- 如果资料不足,明确说明缺少什么

可用资料:
{retrieved_chunks}

用户问题:
{question}

输出格式:
答案:
{answer}

来源:
- {source title} / {section}

Step 7:加入引用来源

引用不是装饰,而是 RAG 的信任基础。每个回答至少要能追溯到文档名、章节或页码、chunk_id 和原文片段。

{
  "answer": "string",
  "sources": [
    {
      "title": "30-Day Plan",
      "source": "checklists/30-day-plan.md",
      "section": "Week 2",
      "chunk_id": "30-day-plan#week-2#001",
      "score": 0.82
    }
  ]
}

Step 8:处理无法回答

资料不足时,系统应该拒答。常见拒答条件包括:top-k 检索结果分数太低、检索结果和问题主题不相关、模型判断资料没有直接答案、用户请求超出知识库范围。

资料中没有找到这个问题的明确答案。当前检索到的内容主要和 RAG 学习计划有关,没有包含部署费用信息。

Step 9:建立最小评测集

准备 30 条问题:15 条资料中有明确答案,5 条需要综合多个 chunk,5 条应该拒答,5 条历史 bad case。

{
  "id": "kb_001",
  "question": "第 2 周学习什么?",
  "expected_behavior": "回答 RAG 入门任务并引用 30-day-plan",
  "required_sources": ["checklists/30-day-plan.md"],
  "tags": ["rag", "easy"]
}

Step 10:上线前检查

更完整的检查见 GitHub 仓库中的 RAG Production Checklist。

常见迭代方向

问题优化方向
检索不到答案优化 chunk、metadata、query rewrite
检索到了但答错改 Prompt、加引用校验、加强拒答
引用不准保存更细 metadata,限制模型引用来源
成本高减少 top-k、压缩上下文、缓存 embedding
延迟高异步索引、缓存、选择更快模型

最终你应该交付什么

这比只写“我会 RAG”有说服力得多。