我用 RAG 搭了一套个人知识库:让 AI 读完我所有笔记再回答问题
适合有大量笔记/文档/资料,想让 AI 基于你的私有知识库回答问题的人。
本文从零搭建一套 RAG 系统,附完整 Python 代码,500 行以内搞定。
为什么需要 RAG
直接问 ChatGPT 问题,它只能用训练数据回答。问它"我上周写的那篇关于 MCP 的文章总结一下",它不知道,因为那是你的私有数据。
RAG(Retrieval-Augmented Generation,检索增强生成)解决的就是这个问题:先从你的知识库里检索相关内容,再把检索结果喂给 AI 生成回答。
用户提问 → 检索知识库 → 找到相关片段 → 喂给 AI → AI 基于你的资料回答类比一下:RAG 就像给 AI 配了一个"私人图书馆",它回答问题之前先去图书馆查资料。
RAG 的核心流程
整个 RAG 分 3 步:
| 步骤 | 做什么 | 用什么 |
|---|---|---|
| 1. 索引 | 把文档切块、向量化、存入数据库 | Embedding 模型 + 向量数据库 |
| 2. 检索 | 用户提问时,找到最相关的文档块 | 余弦相似度搜索 |
| 3. 生成 | 把检索结果 + 用户问题一起喂给 AI | LLM(GPT-4/Claude 等) |
步骤 1:文档索引
文档切块
长文档不能整篇塞给 AI,需要切成小块(chunk)。每块 200-500 字,有重叠。
defsplit_text(text,chunk_size=300,overlap=50):"""将文本切块,每块 chunk_size 字,重叠 overlap 字"""chunks=[]start=0whilestart<len(text):end=start+chunk_size chunk=text[start:end]chunks.append(chunk)start=end-overlap# 重叠部分returnchunks# 示例text=open("my_article.md",encoding="utf-8").read()chunks=split_text(text,chunk_size=300,overlap=50)print(f"切成了{len(chunks)}个块")向量化
把每个文本块转成一个向量(一组数字),用于后续的相似度搜索。
fromopenaiimportOpenAI client=OpenAI()defget_embedding(text):"""用 OpenAI 的 Embedding 模型把文本转成向量"""response=client.embeddings.create(model="text-embedding-3-small",input=text)returnresponse.data[0].embedding# 示例vector=get_embedding("MCP 是 Anthropic 推出的开放协议")print(f"向量维度:{len(vector)}")# 通常是 1536 维存入向量数据库
我用的是 ChromaDB,纯 Python,不需要额外安装服务。
importchromadb# 创建数据库db=chromadb.PersistentClient(path="./knowledge_db")collection=db.get_or_create_collection("my_docs")# 索引文档defindex_document(file_path,doc_id):text=open(file_path,encoding="utf-8").read()chunks=split_text(text)fori,chunkinenumerate(chunks):vector=get_embedding(chunk)collection.add(ids=[f"{doc_id}_{i}"],embeddings=[vector],documents=[chunk],metadatas=[{"source":file_path,"chunk_index":i}])print(f"已索引{file_path},共{len(chunks)}个块")# 批量索引importglobforfileinglob.glob("knowledge_base/*.md"):index_document(file,doc_id=file.split("/")[-1].replace(".md",""))步骤 2:检索
用户提问时,把问题向量化,然后搜索最相似的文档块。
defsearch(query,top_k=5):"""搜索知识库,返回最相关的 top_k 个文档块"""query_vector=get_embedding(query)results=collection.query(query_embeddings=[query_vector],n_results=top_k)returnresults["documents"][0]# 示例results=search("MCP 协议怎么配置",top_k=3)fori,docinenumerate(results):print(f"--- 结果{i+1}---")print(doc[:200])步骤 3:生成
把检索结果和用户问题一起喂给 AI。
defrag_answer(question):"""基于知识库回答问题"""# 1. 检索relevant_docs=search(question,top_k=5)context="\n\n---\n\n".join(relevant_docs)# 2. 生成prompt=f"""基于以下参考资料回答问题。如果资料中没有相关内容,就说"我没有找到相关信息"。 参考资料:{context}问题:{question}回答:"""response=client.chat.completions.create(model="gpt-4",messages=[{"role":"user","content":prompt}])returnresponse.choices[0].message.content# 使用answer=rag_answer("MCP 协议的 mcp.json 怎么配置?")print(answer)完整项目结构
rag-system/ ├── index.py # 文档索引脚本 ├── search.py # 检索模块 ├── generate.py # 生成模块 ├── main.py # 主入口 ├── knowledge_db/ # 向量数据库(自动生成) ├── knowledge_base/ # 你的知识库文档 │ ├── article1.md │ ├── article2.md │ └── notes/ │ └── meeting.md └── requirements.txtrequirements.txt:
openai>=1.0 chromadb>=0.4效果对比
同一个问题,直接问 AI vs RAG 回答:
| 问题 | 直接问 AI | RAG 回答 |
|---|---|---|
| “我之前写的 MCP 文章说了什么?” | “我没有你之前文章的信息” | “你之前的文章讲了 mcp.json 配置、3 个实战案例…” |
| “我的知识库里有哪些关于 AI Agent 的资料?” | 无法回答 | “你有 3 篇相关文章:Agent Pipeline、Skill 体系、多 Agent 框架对比” |
| “我的选题打分标准是什么?” | 无法回答 | “你的四维度打分:热门度/匹配度/差异化/实操性,合格线 30 分” |
踩坑记录
坑 1:切块太碎,上下文丢失
症状:检索到的片段只有半句话,AI 无法理解。
原因:chunk_size 太小(比如 50 字),语义不完整。
解决:chunk_size 设 200-500 字,确保每块有完整的语义单元。
坑 2:切块太大,检索不精确
症状:检索到的块包含大量无关内容,AI 回答时被干扰。
原因:chunk_size 太大(比如 2000 字),一个块里混了多个话题。
解决:chunk_size 控制在 300 字以内,overlap 设 50 字保持上下文连贯。
坑 3:Embedding 模型选错
症状:中文搜索结果不相关。
原因:用的 Embedding 模型对中文支持不好。
解决:用text-embedding-3-small(OpenAI)或bge-large-zh(中文专用)。
坑 4:向量数据库占满磁盘
症状:索引了几千篇文章后,磁盘空间不足。
原因:每篇文章切出几十个块,每个块一个 1536 维向量,存储量很大。
解决:定期清理不再需要的旧版本索引,或者用压缩存储。
坑 5:检索结果重复
症状:top_k=5 返回的 5 个结果内容几乎一样。
原因:文档里有大段重复内容(比如复制粘贴的段落)。
解决:索引前去重,或者检索后做相似度过滤(相似度 > 0.95 的只保留一个)。
坑 6:AI 回答时编造知识库内容
症状:AI 说"根据你的知识库",但引用的内容实际上知识库里没有。
原因:AI 的幻觉问题,它会"脑补"知识库内容。
解决:在 prompt 里明确写"如果资料中没有相关内容,就说没找到",并且要求 AI 标注引用来源。
进阶优化
| 优化方向 | 做法 | 效果 |
|---|---|---|
| 混合搜索 | 向量搜索 + 关键词搜索结合 | 提升召回率 |
| 重排序 | 检索后用 reranker 模型重新排序 | 提升精确度 |
| 多轮对话 | 记住对话历史,连��检索 | 支持追问 |
| 增量索引 | 只索引新增/修改的文档 | 节省时间和成本 |
| 元数据过滤 | 按标签/日期/类型过滤检索范围 | 精准定位 |
总结
3 条核心经验:
chunk_size 是最关键的参数。太小上下文丢失,太大检索不精确。300 字 + 50 字重叠是我的最佳实践。
Embedding 模型决定搜索质量。中文场景用
text-embedding-3-small或bge-large-zh,别用通用模型。RAG 不是万能的。如果知识库本身质量差(过时、错误、不完整),RAG 只会让 AI 更自信地输出错误答案。先保证知识库质量,再搭 RAG。
你有搭过 RAG 系统吗?遇到过什么问题?评论区交流。