如果你是产品经理、运营、技术支持,大概率有过这种痛点:公司内部文档散落在 Confluence、飞书、Notion、Google Drive、邮件附件里,新人问个问题,要么翻半天找不到答案,要么找到的答案是过时版本。如果能让 AI 直接基于公司内部知识回答问题,效率会大幅提升。
RAG(Retrieval-Augmented Generation,检索增强生成),正是解决这个问题的核心技术。2026 年,RAG 已经从 AI 工程领域的”前沿技术”变成”必备技能”——大多数企业 AI 应用都是 RAG 系统:智能客服、内部知识库、销售助手、文档摘要、合同审查、HR 答疑。本质上,90% 的”AI + 企业”项目都是 RAG。
这篇文章就是带你”从零到一”——跟着走一遍,30 分钟你能搭出一个能用的企业私有知识库问答系统,能查你的 PDF、能查飞书文档、能查 Notion 页面,回答问题时引经据典、不胡说八道。
一、RAG 到底是什么:从名字开始讲
RAG 全称是 Retrieval-Augmented Generation,中文叫”检索增强生成”。这个名字其实已经把原理讲清楚了——三步走:
- 检索(Retrieval):用户提问时,先从知识库里搜出最相关的几段内容
- 增强(Augmented):把搜出来的内容作为上下文,塞给 LLM
- 生成(Generation):LLM 基于这些上下文,生成最终答案
为什么需要 RAG?因为 LLM 有两个根本局限:
- 不知道公司内部信息:LLM 是用公开数据训练的,不可能知道你们公司的产品文档、内部制度、技术方案
- 知识有时间戳:LLM 的训练数据有截止时间,2025 年之后的事件它不知道,公司新发布的产品它也不知道
RAG 的核心思想是:LLM 不需要”记住”所有信息,只需要给它”对的资料”,它就能”现场学习”然后回答。
一个直观的比喻:RAG 就像给 LLM 配了一个”开卷考试”——LLM 本来要靠记忆答题,有了 RAG,可以让它”翻书”找答案,然后基于找到的内容作答。这种模式让 LLM 从”死记硬背”变成”活学活用”。
RAG vs Fine-tuning:为什么选 RAG 不是微调
你可能听说过另一种给 LLM 喂专有知识的方式——微调(Fine-tuning)。这两种方案经常被拿来对比。简明区分:
| 维度 | RAG | Fine-tuning |
|---|---|---|
| 知识更新成本 | 重新摄入文档,几小时 | 重新训练,几天 + 几千美元 |
| 适合场景 | 文档常变、量大、要求可追溯 | 固定领域术语、风格转换 |
| 可追溯性 | 每条答案能追溯来源 | 黑盒,LLM 内部權重变化 |
| 成本 | 中(检索 + LLM 调用) | 高(训练 + 推理) |
| 起步门槛 | 低(几小时跑通) | 高(需要数据 + 训练) |
对多数企业内部知识库场景,RAG 是首选:文档会持续更新、知识量大、要求”每个答案有出处”。只有少数场景(比如客服语言风格统一、特定专业术语)Fine-tuning 价值更高。
二、环境准备:5 分钟搞定
我们用 Python + LangChain + Chroma 这套成熟组合搭一个 RAG 系统。这套组合的优势是:
- 纯 Python,跨平台
- 不依赖云服务,本地跑通
- 生态丰富,扩展容易
步骤 1:装依赖
python -m venv venv
source venv/bin/activate
pip install langchain langchain-community langchain-openai \
chromadb pypdf tiktoken
依赖说明:
langchain:RAG 编排框架langchain-community:各种文档加载器(Notion、飞书、PDF 等)langchain-openai:OpenAI 模型(也支持 Claude 等)chromadb:本地向量数据库(免登录、纯本地)pypdf:读 PDFtiktoken:Token 计数
需要注意的安装踩坑:如果你在中国,OpenAI embedding API 可能在网络上是连不上的。这种情况有两个选择:换 Sentence-Transformers 这种开源 embedding 模型(pip install sentence-transformers),或者用国内提供的云服务(阿里云 DashScope、智谱、DeepSeek 都有 embedding 接口)。本教程后文的 OpenAI 代码都可用国产模型的 embedding 接口替换,只需改一行 embeddings = HuggingFaceEmbeddings(...) 即可。
步骤 2:配 API key
建 .env 文件:
后面会用到 embedding 和 LLM 两个 OpenAI 接口,一个 key 搞定。
步骤 3:准备一份文档
放一个 docs/ 目录,把你公司的产品介绍、用户手册、技术文档都丢进去。RAG 系统会扫描这个目录,把所有文档处理成”知识库”。
到这里准备就绪。下面写代码。
三、最简单的 RAG:30 行核心代码
我们的第一版 RAG 系统,极简但已经能用。新建 simple_rag.py:
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 1. 加载文档
loader = TextLoader("docs/product_intro.txt")
documents = loader.load()
# 2. 切分文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, chunk_overlap=50
)
chunks = text_splitter.split_documents(documents)
# 3. 向量化并存到向量数据库
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(chunks, embeddings)
# 4. 创建 LLM
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 5. 创建 RAG 链
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
return_source_documents=True,
)
# 6. 提问
result = qa.invoke({"query": "我们的产品有什么特点?"})
print("答案:", result["result"])
print("\n来源:")
for i, doc in enumerate(result["source_documents"]):
print(f" [{i+1}] {doc.page_content[:100]}…")
跑一下 python simple_rag.py,你会看到:
- 系统读取你的 product_intro.txt
- 自动切分成多个段落
- 每段生成向量
- 用户提问时,先向量搜索找到最相关的 3 段
- 把这 3 段塞给 GPT-4o-mini
- LLM 生成答案
这就是 RAG 最基础的形态。整个代码 30 行,已经能做企业内部知识问答。
进阶:多文件摄入 + 增量更新
上面代码只吃一个 txt。真实场景里,你会吃整个目录下的所有文档,并且会增量更新(而非重头跑一遍)。这是企业级 RAG 的标配:
import hashlib
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
vectorstore = Chroma(persist_directory="./chroma", embedding_function=OpenAIEmbeddings())
def file_hash(path: Path) -> str:
"""计算文件 hash,作为是否更新过的依据"""
return hashlib.md5(path.read_bytes()).hexdigest()
existing_meta = {m["hash"]: m["id"] for m in vectorstore.get()["metadatas"] if m and "hash" in m}
for path in Path("docs").rglob("*"):
if path.suffix not in [".pdf", ".txt", ".md", ".docx"]:
continue
h = file_hash(path)
if h in existing_meta:
print(f"跳过(未改动): {path}")
continue
# 读取、切除、入库
docs = load_document(path)
vectorstore.add_documents(docs, metadatas=[{"source": str(path), "hash": h}])
print(f"已入库: {path}")
第二次跑这个脚本时,改动过的文件会自动重入库,没动过的自动跳过。这是一个企业文档高频更新的常见模式。
四、加文档摄入:让 RAG 能查真实业务文档
上面的示例只能读纯文本。真实企业场景里,文档是多种格式的——PDF、Word、Notion、飞书文档。LangChain 提供了几十种文档加载器,挑几个常用的:
加载 PDF
loader = PyPDFLoader("docs/manual.pdf")
pages = loader.load_and_split()
pages 是个列表,每个元素是一页的内容。这是 LangChain 把整本 PDF 切成多页的方式。
加载 Notion
loader = NotionDBLoader(
integration_token="secret_xxx",
database_id="你的 Notion DB ID",
)
docs = loader.load()
需要 Notion Integration Token。
加载飞书文档
loader = FeishuLoader(
app_id="your_app_id",
app_secret="your_app_secret",
dir_token="文件夹 token",
)
docs = loader.load()
批量加载多个文件
真实场景里,文档会分散在不同目录、不同格式。我们写一个 batch loader:
def load_directory(directory: str):
"""批量加载一个目录下所有支持的文档"""
documents = []
for file_path in Path(directory).rglob("*"):
if file_path.suffix == ".pdf":
loader = PyPDFLoader(str(file_path))
elif file_path.suffix in [".txt", ".md"]:
loader = TextLoader(str(file_path))
elif file_path.suffix == ".docx":
from langchain_community.document_loaders import UnstructuredWordDocumentLoader
loader = UnstructuredWordDocumentLoader(str(file_path))
else:
continue
documents.extend(loader.load())
return documents
documents = load_directory("docs/")
print(f"加载了 {len(documents)} 个文档块")
把这个函数封装好,以后任何目录的文档都能批量入库,不需要一行一行写加载代码。
五、优化 RAG:从”能用”到”好用”
最基础的 RAG 系统能跑,但有 4 个常见问题:
- 答非所问——问 A,答 B
- 答得太泛——答案没针对性
- 检索不到——文档里有相关信息,但搜不到
- 重复/矛盾——不同文档说法不一致
下面 4 个优化技巧,每个都能解决其中一类问题。
优化 1:更好的文档切分策略
最简单的 RecursiveCharacterTextSplitter 是按字符硬切的,这会切断语义。更好的方式是按”语义边界”切分:
# 按 Markdown 标题切分
splitter = MarkdownTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_text(markdown_text)
或者用 SemanticChunker(2026 年新出的工具),它会自动根据语义相似度切分:
splitter = SemanticChunker(
embeddings,
breakpoint_threshold_type="percentile",
)
chunks = splitter.split_documents(documents)
这种切分不会切断句子内部结构,检索时更精准。
优化 2:混合检索(向量 + 关键词)
向量检索擅长”语义相似”,但对精确关键词、专业术语不敏感。比如用户问”RefundPolicy.pdf 第 3 条”,纯向量检索可能搜不到,关键词检索能搜到。
# 关键词检索
bm25_retriever = BM25Retriever.from_documents(chunks)
# 向量检索
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 混合(70% 向量, 30% 关键词)
ensemble_retriever = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.7, 0.3],
)
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=ensemble_retriever,
)
这种混合检索兼顾”语义理解”和”关键词精确匹配”,是企业 RAG 系统的标准做法。
优化 3:重排序(Reranking)
第一轮检索可能召回了 10 个文档,但真正相关的只有前 3 个。重排序让 LLM(或专门的 rerank 模型)再过一遍,挑出最相关的:
from langchain.retrievers.document_compressors import CohereRerank
# 召回 10 个文档
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
# 重排到前 3
compressor = CohereRerank(top_n=3)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=base_retriever,
)
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=compression_retriever,
)
重排序能显著提升答案的”精准度”,但会引入额外的 API 调用(成本增加)。建议在”答案质量要求高”的场景才用。
优化 4:评估与持续改进
RAG 系统上线后,你需要持续评估它的”答案质量”。最简单的方法是写一个测试集。
优化 5:Query 改写 (Query Rewriting)
用户提问往往口语化、不精准。原始查询”代理能干嘛?”对检索不友好;改成”代理软件有哪些主流使用场景”会好得多。Query 改写用 LLM 把口语化查询重写成检索友好的形式:
rewrite_prompt = PromptTemplate.from_template(
"将以下用户问题改写为更适合检索关键字词汇库的形式。仅输出改写后的查询,不要其他内容。\n\n原始问题: {question}\n改写后:"
)
def rewrite_query(question: str, llm) -> str:
return llm.invoke(rewrite_prompt.format(question=question)).content
# 使用
original_q = "那个能看代码的东西怎么用?"
rewritten_q = rewrite_query(original_q, llm)
print(f"原始: {original_q}")
print(f"改写后: {rewritten_q}")
# 可能输出: "开发者使用 AI 代码代理的方法是什么"
改写后的查询送进检索器,准确率明显提高。这个技巧特别适合”用户提问质量参差不齐”的场景(比如客服、业务答疑)。
{"q": "产品价格是多少?", "expected_source": "pricing.pdf"},
{"q": "如何申请退款?", "expected_source": "refund_policy.pdf"},
{"q": "客服电话?", "expected_source": "contact.md"},
]
def evaluate(test_cases):
correct = 0
for case in test_cases:
result = qa.invoke({"query": case["q"]})
sources = [doc.metadata.get("source") for doc in result["source_documents"]]
if case["expected_source"] in sources:
correct += 1
print(f"准确率: {correct}/{len(test_cases)} = {correct/len(test_cases)*100:.0f}%")
evaluate(test_cases)
跑这个评估,你能看到 RAG 系统的”检索命中率”。如果准确率低,可以从以下方面改进:
- 文档切分粒度(chunk_size 太大或太小)
- embedding 模型选型
- 加更多上下文(比如文档元数据)
- 改写用户问题(用 LLM 把口语化问题改成更”搜索友好”的形式)
评估这件事必须做——不评估的 RAG 系统,上线后没人知道它工作得怎么样。
六、完整项目实战:做一个 HR 知识库问答
接下来我们用一个完整实例走通一遍。这次做”HR 知识库问答”,用户可以问”年假怎么休””加班费怎么算””工资什么时候发”,系统基于公司 HR 文档回答。
项目结构
├── .env
├── docs/
│ ├── 员工手册.md
│ ├── 年假政策.pdf
│ └── 薪资福利.docx
├── ingest.py # 文档摄入
├── query.py # 查询入口
└── evaluate.py # 评估
ingest.py:摄取所有文档
from langchain_community.document_loaders import (
PyPDFLoader, TextLoader, UnstructuredWordDocumentLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from dotenv import load_dotenv
load_dotenv()
def load_documents(directory: str):
docs = []
for path in Path(directory).rglob("*"):
if path.suffix == ".pdf":
docs.extend(PyPDFLoader(str(path)).load())
elif path.suffix in [".txt", ".md"]:
docs.extend(TextLoader(str(path)).load())
elif path.suffix == ".docx":
docs.extend(UnstructuredWordDocumentLoader(str(path)).load())
return docs
def main():
documents = load_documents("docs")
print(f"加载 {len(documents)} 个文档")
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(documents)
print(f"切分成 {len(chunks)} 个 chunk")
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
chunks, embeddings, persist_directory="./chroma"
)
vectorstore.persist()
print(f"向量库已保存到 ./chroma")
if __name__ == "__main__":
main()
跑 python ingest.py,HR 文档会被处理并存到本地 Chroma 数据库。
query.py:查询入口
from langchain_community.vectorstores import Chroma
from langchain.prompts import PromptTemplate
from langchain.chains import RetrievalQA
from dotenv import load_dotenv
load_dotenv()
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma(persist_directory="./chroma", embedding_function=embeddings)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
# 自定义 prompt 模板
prompt_template = """基于以下参考资料回答问题。
参考资料:{context}
问题:{question}
答案要简洁、有依据。如果参考资料里没有答案,说"我不知道"。"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"],
)
qa = RetrievalQA.from_chain_type(
llm=llm,
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True,
)
def ask(question: str):
result = qa.invoke({"query": question})
print(f"\n问: {question}")
print(f"答: {result['result']}")
print(f"来源: {[doc.metadata.get('source', '?') for doc in result['source_documents']]}")
if __name__ == "__main__":
questions = [
"年假可以休几天?",
"加班费是怎么计算的?",
"工资什么时候发?",
"我刚入职,需要办理什么手续?",
]
for q in questions:
ask(q)
跑 python query.py,你会看到一个完整的 HR 问答系统跑起来。每一个回答都会附上”来源文档路径”,让用户知道答案来自哪个文件。
评估
跑一段时间后,你需要评估这个 RAG 系统的质量。建一个 evaluate.py,跑 50-100 个测试 case,统计准确率、命中率、用户满意度,看系统是否需要调优。
七、调试技巧:RAG 系统的常见坑
坑 1:答案”看着对”但其实不对
症状:LLM 给的答案流畅自然,但其实是”幻觉”——它没基于检索到的文档,而是基于自己的”常识”瞎编。
修复:
- 在 prompt 里明确”如果文档里没答案,说’我不知道’,不要瞎编”
- 把 temperature 设成 0(LLM 默认有随机性)
- 加”返回来源文档”,让用户能验证
坑 2:检索不到”该被搜到”的内容
症状:文档里有相关信息,但搜不到。
修复:
- 检查 chunk_size 是否合适(太大/太小都不好)
- 试不同的 embedding 模型(text-embedding-3-large 比 -small 更准)
- 用混合检索(向量 + 关键词)
- 加文档元数据(标题、日期、来源)进 embedding
坑 3:速度慢
症状:问一个问题要等 5-10 秒。
修复:
- embedding 用更快的模型
- LLM 用更快的模型(gpt-4o-mini 比 gpt-4o 快 5 倍)
- 用缓存:相同问题的答案不重跑
- 优化检索(limit k 值,减少不必要检索)
坑 4:成本高
症状:每月 API 费用超出预期。
修复:
- 监控 token 使用量
- 用更便宜的模型处理简单问题,贵的处理复杂问题
- 缓存高频问题的答案
- 限制文档 chunk 数量,避免无限膨胀
八、生产化:从 demo 到能用的 3 个步骤
写完 demo 后,要把 RAG 系统用到生产里,需要做 3 件事:
步骤 1:加权限控制
生产 RAG 必须区分用户权限:
- 普通员工:能查自己部门的文档
- 主管:能查所有部门
- 高管:能查机密文档
实现方式:在 retrieval 阶段先根据用户角色过滤文档 metadata。
步骤 2:加审计日志
每一轮问答都要记录:
- 用户问了什么
- 检索到了哪些文档
- LLM 给的答案是什么
- 用户是否点赞(满意)
这些日志是后续优化的关键数据。
步骤 3:加监控
监控几个关键指标:
- 响应时间
- 检索命中率
- 用户满意度
- 成本
一旦异常,立即告警。
九、下一步
学完这篇,你已经能搭一个能用的 RAG 系统了。接下来的几个方向:
方向 1:上 Agent(Agentic RAG)
普通 RAG 是”先检索再生成”。Agentic RAG 让 Agent 决定”要不要检索”、”检索几次”、”用什么关键词”——更灵活,适合复杂查询。LangGraph 配合 LLM 决策,能搭出企业级的 Agentic RAG 系统。
方向 2:接 MCP
把 Notion / 飞书 / Slack 等企业内部工具接成 MCP Server,RAG 系统就能”实时”获取最新文档,而不是靠定期 ingest。这是 2026 年最前沿的 RAG 模式。
方向 3:多模态 RAG
现在的 RAG 主要处理文字,2026 年开始支持图片、表格、PDF 图表等多模态内容。多模态 RAG 能回答”这张图里的公式是什么意思”这种问题。
方向 4:GraphRAG
把知识图谱(Knowledge Graph)融进 RAG,系统能理解实体之间的关联,适合”问关于人/事/物之间的关系”的问题。例如”我们公司哪个部门在 AI 项目上投入最多”——纯向量 RAG 答不好,GraphRAG 能答。
推荐学习路径
- 想搭生产级 RAG:补充评估体系、可观测性、成本控制
- 想理解 Agent 怎么用 RAG:看本系列《手把手搭建第一个 AI Agent:基于 LangGraph 实战》
- 想了解 RAG 怎么跟 MCP 集成:看《MCP 实战:给 Agent 接上 GitHub / 数据库 / 浏览器》
30 分钟前你不知道 RAG 是什么,现在你能搭一个能用的企业知识库系统。但这只是起点——真正的”厉害”在于把它用到能放心交给别人的程度。下周我会写一篇 GraphRAG 实战,讲知识图谱怎么融入 RAG,让系统回答”复杂关系类”问题。
先把今天的 demo 跑通,然后试着往里塞你公司真实文档。30 分钟后,你的 RAG 系统会变成你最常用的”知识助手”——这种成就感,只有动手做才能体会到。
(综合 LangChain 官方文档、Chroma 文档、LangGraph 文档、Cohere Rerank 文档、企业 RAG 实战项目整理)




我要评论