AI 实战 / 已完成

AI 应用里的 RAG 知识库实战

理解文档上传、切分、向量化、检索、Prompt、引用来源和 RAG 效果调试。

返回文章积累

一句话:RAG 知识库就是先从你的资料里找出相关片段,再把片段交给大模型回答,减少模型胡说。

本篇学完你会什么:理解上传文档、切分、向量化、入库、检索、拼提示词、生成回答、引用来源和效果调试这一整条 RAG 实战链路。

1. 为什么 AI 应用需要 RAG

直接问大模型:

我们公司报销制度是什么?

模型可能不知道你的公司制度,也可能编一个听起来很像真的答案。

RAG 的做法是:

先查你的资料
再带着资料问模型
最后让模型根据资料回答

它解决的是:

问题RAG 怎么帮忙
模型不知道私有资料先从知识库检索
模型容易胡说要求根据资料回答
回答没有依据返回引用来源
文档太长塞不进 prompt只取最相关片段

2. RAG 完整流程

完整链路:

上传文档
  ↓
解析文本
  ↓
切分 chunk
  ↓
生成 embedding
  ↓
存入向量库
  ↓
用户提问
  ↓
问题生成 embedding
  ↓
检索相似 chunk
  ↓
把 chunk 放进 prompt
  ↓
大模型生成回答
  ↓
返回答案和引用来源

大白话:先把资料整理成一张可搜索的卡片库。用户问问题时,先从卡片库找相关卡片,再拿卡片去问模型。

3. 文档上传后先做什么

用户上传的可能是:

PDF
Word
Markdown
网页
纯文本
代码文件

第一步不是马上丢给模型,而是解析出干净文本。

你需要保存:

字段作用
document_id文档 id
filename原文件名
content解析后的全文
created_at上传时间
owner_id谁上传的

还要记录失败原因,比如:

文件太大
格式不支持
PDF 扫描件没有文字
解析超时

4. 切分 chunk 怎么理解

模型不能每次都读完整文档,所以要切成小段。

比如一篇制度文档:

第一段:报销适用范围
第二段:交通费标准
第三段:住宿费标准
第四段:审批流程

为了先理解,可以把“每一段就是一个 chunk”当成入门版说法。

真实项目里,chunk 不一定刚好等于自然段。切分方式要看文档长什么样:

切分方式适合注意
按自然段制度、文章这类段落清楚的文档段落太长要再拆
固定长度 + 重叠文档格式混乱、段落不稳定不要切断关键上下文
按标题层级Markdown、技术文档、说明书把标题保存到 metadata
表格/代码特殊处理参数表、API 文档、代码片段不要把一行表或一段代码拆散

chunk 要保存:

字段作用
chunk_id片段 id
document_id属于哪份文档
content片段正文
page来自第几页
title来自哪个标题
embedding向量

切太大,检索不准;切太小,上下文不完整。初学可以先用:

每段 500-800 个中文字符
相邻段落重叠 80-120 个字符

5. Embedding 和向量库是什么

Embedding 可以理解成:把一段文字变成一串数字,方便计算“意思像不像”。

比如:

“报销交通费需要什么材料?”

和:

“交通费报销需提供发票和审批单”

字面不完全一样,但意思很接近。Embedding 就是帮系统判断这种接近程度。

向量库就是专门存这些数字和文本片段的仓库。

常见选择:

选择适合
pgvector已经用 PostgreSQL,想少引入新组件
Chroma本地学习和小项目
Milvus数据量更大、向量检索更专业
Elasticsearch/OpenSearch需要关键词检索和向量混合

6. 检索怎么做

用户提问:

出差住宿费最多报多少?

系统做:

问题 -> embedding -> 向量库查 top_k 个相似 chunk

伪代码:

query_vector = embedding_model.embed_query(question)
chunks = vector_store.similarity_search_by_vector(query_vector, k=5)

top_k=5 的意思是:先找最相关的 5 个片段。

不要一开始取太多。取太多会让 prompt 变长、成本变高,还可能把不相关内容塞给模型。

7. Prompt 怎么拼

拿到片段后,把它们放进提示词:

你是知识库问答助手。
请只根据【资料】回答问题。
如果资料里没有答案,就说“资料中没有找到明确答案”。

【资料】
1. 交通费报销需要发票和审批单。
2. 住宿费标准为每晚不超过 500 元。

【问题】
出差住宿费最多报多少?

关键规则:

  1. 明确要求“只根据资料回答”。
  2. 明确资料不足时怎么说。
  3. 保留来源信息,方便引用。

8. 回答里为什么要带引用

没有引用,用户只能相信模型。

带引用后,用户能回到原文确认。

返回可以这样:

{
  "answer": "根据资料,出差住宿费标准为每晚不超过 500 元。",
  "sources": [
    {
      "document": "报销制度.pdf",
      "page": 3,
      "chunk_id": "chunk_102"
    }
  ]
}

引用不是装饰,它是 RAG 的信任基础。

9. 后端接口怎么设计

最小接口:

接口作用
POST /knowledge/documents上传文档
GET /knowledge/documents文档列表
POST /knowledge/documents/{id}/index解析、切分、向量化
POST /knowledge/chat基于知识库问答

问答请求:

{
  "question": "出差住宿费最多报多少?",
  "document_ids": [1, 2],
  "top_k": 5
}

问答响应:

{
  "answer": "出差住宿费标准为每晚不超过 500 元。",
  "sources": [
    {
      "document_id": 1,
      "filename": "报销制度.pdf",
      "page": 3
    }
  ]
}

10. RAG 效果差怎么调

效果差时,不要只怪模型。按这条链路查:

文档是否解析干净
chunk 是否切得太碎或太大
embedding 模型是否合适
top_k 是否太少或太多
检索结果是否真的相关
prompt 是否要求只根据资料回答
是否返回引用来源

常见调法:

问题调整
找不到答案增大 top_k,检查文档是否入库
答案不准优化 chunk,加入标题和页码 metadata
回答胡说prompt 要求资料不足就说明
成本太高减少 top_k,缩短 chunk,缓存结果
引用不对检查 chunk 的 document/page metadata

11. 常见错误

错误后果修正
把整份文档直接塞给模型超长、贵、不稳定先切分和检索
chunk 没保存来源无法引用保存 document/page/title
只做向量检索,不看结果不知道错在哪调试时打印 top_k chunk
prompt 没限制依据模型容易自由发挥要求只根据资料回答
文档更新后不重建索引答案用旧资料文档变更后重新向量化
用户权限没控制可能查到不该看的资料检索时按 owner/tenant 过滤

12. 检查清单

[ ] 文档能上传并记录 owner
[ ] 文档能解析成干净文本
[ ] chunk 保存了 document_id、page、title
[ ] embedding 已写入向量库
[ ] 问题能检索 top_k 片段
[ ] prompt 明确只根据资料回答
[ ] 资料不足时不会编答案
[ ] 回答返回 sources
[ ] 文档更新后能重建索引
[ ] 检索时有用户或租户权限过滤

13. 总结表

名词大白话
RAG先查资料,再让模型回答
chunk从文档切出来的小片段
embedding把文字变成可比较的数字
向量库保存 embedding 和片段的仓库
top_k找最相关的前几段
metadata片段来源信息,比如文档、页码、标题
sources回答引用的资料来源
hallucination模型胡说

上一篇建议:大白话讲解——FastAPI 项目配置、多环境和部署上线.md

下一篇建议:把 RAG 接到你的 AI 实验室 小接口里,先做“上传一份 Markdown 文档并问答”的最小版本。