上一篇我们聊了 Agent 动态路由——任务交接时怎么把控流向。这次换个方向,聊一个大家问得最多的问题:怎么让 AI 能回答你自己公司的文档、产品手册、内部 Wiki?
你可能试过直接把文档塞进 System Prompt,结果 token 超限了。你也可能试过 Fine-tuning,但数据更新一次就得重新训练。两条路都走不通——这就是 RAG 存在的原因。
RAG 是什么:一句话+类比
RAG(Retrieval-Augmented Generation)= 先检索,再生成。
类比:RAG 就像开卷考试。模型本身是那个能写文章的学生,知识库是那一堆参考书。考试时不靠死记硬背,而是先翻书找到相关段落,再用自己的理解写答案。
没有 RAG 的 AI 是闭卷考——它只能答它训练时见过的内容。
为什么不直接 Fine-tuning?
这是大家最常问的问题。Fine-tuning 训练的是「风格和能力」,不是「知识」。
| 维度 | RAG | Fine-tuning |
|---|---|---|
| 知识更新 | 改向量库,秒级生效 | 重新训练,几小时到几天 |
| 成本 | 低(API + 向量DB) | 高(GPU 算力) |
| 幻觉风险 | 可溯源,能引用原文 | 模型可能「记错」 |
| 适用场景 | 私有知识、频繁更新 | 专业语气、特定格式输出 |
结论:知识库类需求,首选 RAG;想让模型说话更像你们品牌,才考虑 Fine-tuning。
RAG 完整流程拆解
RAG 分两个阶段:索引阶段(离线)和查询阶段(在线)。
索引阶段(一次性/更新时): 文档 → 分块(Chunking) → 向量化(Embedding) → 存入向量数据库 查询阶段(每次对话): 用户提问 → 向量化 → 相似度检索 → 取出 Top-K 段落 → 拼进 Prompt → LLM 生成回答第一关:文档分块(Chunking)
分块策略直接决定检索质量,但大多数人第一次都搞错了。
固定长度分块(最常见,但有问题)
fromimport# 最常见写法:按字符数切分1000# 每块最多1000字符200# 相邻块重叠200字符(防止语义断裂)"\n\n""\n""。""!""?"" """❌ 常见错误:chunk_overlap=0
→ 一个完整句子被切成两半,检索时两半都不完整,模型无法理解
✅ 正确做法:chunk_overlap设为chunk_size的 10%-20%
→ 语义完整,相邻块有重叠保护
语义分块(效果更好,稍复杂)
fromimportfromimport# 按语义相似度自动切分,不按字符数硬切"percentile"# 超过85%相似度阈值才切分85# 输出的每个 chunk 语义上都是完整的✅ 语义分块在技术文档、法律合同这类强结构文本效果明显更好
❌ 但速度更慢(每次都要调用 Embedding),适合离线批量处理
第二关:向量化(Embedding)
Embedding 是把文本变成一串数字向量,语义相近的文本向量距离更近。
类比:把每段文字映射到一个 1536 维的空间里,「苹果手机」和「iPhone」在这个空间里距离很近,和「橙子」距离远。
选 Embedding 模型
# 方案A:OpenAI text-embedding-3-small(性价比最高,推荐)fromimport"text-embedding-3-small"# 1536维,比 ada-002 便宜5倍# model="text-embedding-3-large", # 精度更高,贵3倍,一般用不到# 方案B:本地模型(零成本,但精度稍差)fromimport"BAAI/bge-m3"# 多语言,中文效果好"device""cpu"# 测试一下:两段近义句,向量距离应该很小"如何重置密码""忘记密码怎么办"# 这两个向量的余弦相似度应该 > 0.9关键原则:索引时用什么 Embedding,查询时必须用同一个——不能混用。
向量数据库选型
| 数据库 | 适用场景 | 特点 |
|---|---|---|
| Chroma | 本地开发、原型验证 | 零配置,纯 Python |
| Qdrant | 生产环境推荐 | 性能好,支持过滤 |
| Pinecone | 云服务,快速上线 | 全托管,按量付费 |
| pgvector | 已有 PostgreSQL | 不用新增基础设施 |
# Chroma 本地版(开发用)fromimport"./chroma_db"# 本地持久化"my_knowledge_base"# Qdrant 生产版fromimportimport"http://localhost:6333""my_knowledge_base"第三关:检索策略
大多数 RAG 系统检索效果差,不是因为 Embedding 模型不好,而是检索策略太简单。
基础检索:相似度搜索
# 最基础:返回最相似的4个chunk"如何申请年假"4# 带分数:能看到每个 chunk 的相似度(0-1,越高越相关)"如何申请年假"4forinprintf"相似度: {score:.3f} | 内容: {doc.page_content[:50]}..."进阶检索:MMR(最大边际相关性)
❌ 纯相似度搜索的问题:Top-4 可能都是在说同一件事(高度重复)
✅ MMR 在保证相关性的同时,最大化结果多样性:
# MMR 检索:相关 + 不重复"如何申请年假"4# 返回4个20# 先取20个候选,再从中选4个最多样的0.7# 0=最多样,1=最相关,0.5-0.7 效果最好混合检索:向量 + 关键词(生产推荐)
fromimportfromimport# 关键词检索(BM25,对专有名词、型号特别有效)4# 向量检索"k"4# 混合:各取 50%0.50.5# 可调,专有名词多时提高 BM25 权重"iPhone 14 的电池容量是多少"# BM25 精准匹配「iPhone 14」,向量找到语义相关段落,两者互补第四关:完整 RAG Chain 搭建
把前面所有环节串起来,搭一个可以直接上生产的 RAG Chain:
fromimportfromimportfromimportfromimportfromimport# 1. 初始化组件"gpt-4o-mini"0"text-embedding-3-small""./chroma_db""my_knowledge_base""mmr""k"4"fetch_k"20# 2. RAG Prompt(关键:要求模型基于上下文回答)"""你是一个专业的知识库助手。请根据以下检索到的上下文回答用户问题。**规则:**- 只基于提供的上下文回答,不要编造- 如果上下文中没有相关信息,直接说「根据现有资料,我找不到这个问题的答案」- 回答要简洁直接,引用原文时用引号**检索到的上下文:**{context}**用户问题:**{question}"""# 3. 格式化检索结果(多个 chunk 拼在一起)defformat_docsdocsreturn"\n\n---\n\n"f"[来源: {doc.metadata.get('source', '未知')}]\n{doc.page_content}"forin# 4. 组装 Chain(LCEL 写法)"context"# 检索 → 格式化"question"# 问题直接传入# 5. 使用"我们公司的年假政策是什么?"print带来源引用的版本
fromimport# 同时返回答案和来源文档"answer""source_documents"# 保留原始 chunk"年假怎么申请?"print"答案:""answer"print"\n引用来源:"forin"source_documents"printf" - {doc.metadata.get('source', '未知')}: {doc.page_content[:80]}..."第五关:文档入库(工程化)
把文档批量处理入库,这才是生产中最麻烦的部分:
importfromimportfromimportfromimportdefload_documentsdocs_dir: strlist"""支持 PDF、Word、TXT、Markdown 混合入库"""".pdf"".docx"".txt"".md"forin"*"ifinstr# 给每个 chunk 打上来源标记forin"source""file_path"strprintf"✅ 已加载: {file_path.name} ({len(docs)} 段)"returndefbuild_knowledge_basedocs_dir: str, persist_dir: str"""一键构建知识库"""# 加载文档printf"\n共加载 {len(raw_docs)} 个文档片段"# 分块800150"\n\n""\n""。""!""?"printf"分块后共 {len(chunks)} 个 chunk"# 向量化入库(分批处理,避免 API 限流)"text-embedding-3-small"# 批量处理,每批 100 个100Noneforinrange0lenifisNone"knowledge_base"elseprintf"进度: {min(i+batch_size, len(chunks))}/{len(chunks)}"printf"\n✅ 知识库构建完成,共 {len(chunks)} 个向量"return# 使用"./docs""./chroma_db"常见坑(踩过才知道)
坑1:Chunk 太大,检索噪音多
❌chunk_size=3000:一个 chunk 包含了太多无关内容,检索出来的段落「离题」
✅ 推荐chunk_size=600-1000,回答简单问题用小 chunk,需要完整上下文时用k=6
坑2:相同文档重复入库
# ❌ 每次启动都重新入库,向量越来越多# ✅ 检查是否已有数据,有就直接加载ifandprint"加载已有向量库"elseprint"新建向量库"坑3:提问语言和文档语言不一致
❌ 文档是中文,用英文查询 → 相似度打分错乱
✅ 用多语言 Embedding(BAAI/bge-m3)或在检索前把提问翻译成文档语言
坑4:Top-K 太少,关键信息检索不到
❌k=2:覆盖太少,问题涉及多个段落时漏答
✅ 生产环境推荐k=4~6,token 允许的情况下宁多不少
坑5:Prompt 没有「只基于上下文回答」约束
❌ 没加限制 → 模型结合自己训练知识和检索结果混答,无法区分哪些是你的文档里有的
✅ 明确写:「只基于以下上下文,没有就说没有」——这一句能把幻觉降低 80%
发布前自查清单
- Embedding 模型索引和查询时一致
chunk_overlap≥chunk_size的 10%- 每个文档 chunk 打了来源 metadata
- Prompt 中有「只基于上下文」约束
- 检索数量
k≥ 4 - 重复入库已做幂等检查
- 混合检索(BM25 + 向量)用于专有名词多的场景
总结
这篇我们从零搭了一套完整的 RAG 私有知识库方案:
- 分块决定上限:
chunk_size=800,overlap=150,语义分块效果比固定分块好 20-30% - Embedding 选型:开发用
text-embedding-3-small,中文内容用bge-m3 - 检索策略分层:基础用相似度,去重用 MMR,专有名词多用混合检索
- Prompt 约束是关键:「只基于上下文」这一句能把幻觉降低 80%
- 工程化必做:入库幂等检查,文档打 metadata 来源,批量处理防限流
理解 RAG 的核心是:检索质量 > 生成质量——答案已经在文档里了,问题是能不能找对。
学AI大模型的正确顺序,千万不要搞错了
🤔2026年AI风口已来!各行各业的AI渗透肉眼可见,超多公司要么转型做AI相关产品,要么高薪挖AI技术人才,机遇直接摆在眼前!
有往AI方向发展,或者本身有后端编程基础的朋友,直接冲AI大模型应用开发转岗超合适!
就算暂时不打算转岗,了解大模型、RAG、Prompt、Agent这些热门概念,能上手做简单项目,也绝对是求职加分王🔋
📝给大家整理了超全最新的AI大模型应用开发学习清单和资料,手把手帮你快速入门!👇👇
学习路线:
✅大模型基础认知—大模型核心原理、发展历程、主流模型(GPT、文心一言等)特点解析
✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑
✅开发基础能力—Python进阶、API接口调用、大模型开发框架(LangChain等)实操
✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用
✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代
✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经
以上6大模块,看似清晰好上手,实则每个部分都有扎实的核心内容需要吃透!
我把大模型的学习全流程已经整理📚好了!抓住AI时代风口,轻松解锁职业新可能,希望大家都能把握机遇,实现薪资/职业跃迁~