文章目录
-
- [1. 引言:大模型很强,但不是"万能知识库"](#1. 引言:大模型很强,但不是“万能知识库”)
- [2. RAG 是什么](#2. RAG 是什么)
- [3. RAG 的核心架构与工作流程](#3. RAG 的核心架构与工作流程)
- [4. 企业级知识库落地的完整流程](#4. 企业级知识库落地的完整流程)
-
- [4.1 需求与数据盘点](#4.1 需求与数据盘点)
- [4.2 文档解析与清洗](#4.2 文档解析与清洗)
- [4.3 文本切分(Chunking)](#4.3 文本切分(Chunking))
- [4.4 向量化与索引构建](#4.4 向量化与索引构建)
- [4.5 检索与重排序](#4.5 检索与重排序)
- [4.6 Prompt 组装与答案生成](#4.6 Prompt 组装与答案生成)
- [4.7 评估与持续优化](#4.7 评估与持续优化)
- [5. 一个可运行的最小实现](#5. 一个可运行的最小实现)
- [6. 常见问题与优化经验](#6. 常见问题与优化经验)
- [7. 总结](#7. 总结)
1. 引言:大模型很强,但不是"万能知识库"
大语言模型(LLM)让自然语言处理迈入了全新阶段:写文案、写代码、做摘要、翻译、对话,表现都远超预期。但在企业真实业务中,单纯依赖 LLM 构建知识型应用,很快就会遇到几个绕不开的问题。
大模型的典型局限:
- 知识时效性问题:模型训练有截止时间,无法回答训练后发生的新事件、新政策、新产品信息。
- 私有知识缺失:企业内部的制度、流程、产品手册、客户资料没有进入公开语料,模型天然"不知道"。
- 幻觉问题:模型在不确定时会"一本正经地胡说八道",给出看似合理但完全错误的答案。
- 缺乏可追溯性:模型直接生成答案,无法告诉用户"这个结论来自哪份文档、哪一段"。
- 成本与可控性:每次把所有知识都塞进上下文并不现实,既贵又容易超出上下文窗口。
RAG(Retrieval-Augmented Generation,检索增强生成)正是为了解决这些问题而生的:让模型在生成答案之前,先从外部知识库中检索相关信息,再基于检索到的内容回答问题。它把"知识的存储与检索"和"语言的理解与生成"解耦,让 LLM 不再依赖记忆,而是依赖可管理、可更新的知识库。
2. RAG 是什么
RAG 的核心思想可以概括为一句话:
遇到问题时,先查资料,再回答。
普通的 LLM 问答是闭卷考试,模型只能靠训练时记住的东西作答;RAG 则是开卷考试,模型可以参考实时检索到的资料来组织答案。
从架构上看,RAG 把系统拆成两条链路:
- 离线链路(离线索引):把企业文档切分、向量化,并写入向量数据库,构建可检索的知识库。
- 在线链路(在线问答):用户提问后,先把问题向量化并检索相关知识片段,再把"问题 + 检索结果"一起交给 LLM 生成最终答案。
这种方式带来了显著收益:知识可以随时更新而无需重新训练模型;答案可以给出出处来源;模型幻觉大幅降低;企业私域数据也能被安全、可控地利用起来。
3. RAG 的核心架构与工作流程
一次典型的 RAG 问答会经过以下几个阶段:
#mermaid-svg-M1JNMmbPnYhXRlmR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-M1JNMmbPnYhXRlmR .error-icon{fill:#552222;}#mermaid-svg-M1JNMmbPnYhXRlmR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-M1JNMmbPnYhXRlmR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-M1JNMmbPnYhXRlmR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-M1JNMmbPnYhXRlmR .marker.cross{stroke:#333333;}#mermaid-svg-M1JNMmbPnYhXRlmR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-M1JNMmbPnYhXRlmR p{margin:0;}#mermaid-svg-M1JNMmbPnYhXRlmR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster-label text{fill:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster-label span{color:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster-label span p{background-color:transparent;}#mermaid-svg-M1JNMmbPnYhXRlmR .label text,#mermaid-svg-M1JNMmbPnYhXRlmR span{fill:#333;color:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR .node rect,#mermaid-svg-M1JNMmbPnYhXRlmR .node circle,#mermaid-svg-M1JNMmbPnYhXRlmR .node ellipse,#mermaid-svg-M1JNMmbPnYhXRlmR .node polygon,#mermaid-svg-M1JNMmbPnYhXRlmR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-M1JNMmbPnYhXRlmR .rough-node .label text,#mermaid-svg-M1JNMmbPnYhXRlmR .node .label text,#mermaid-svg-M1JNMmbPnYhXRlmR .image-shape .label,#mermaid-svg-M1JNMmbPnYhXRlmR .icon-shape .label{text-anchor:middle;}#mermaid-svg-M1JNMmbPnYhXRlmR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-M1JNMmbPnYhXRlmR .rough-node .label,#mermaid-svg-M1JNMmbPnYhXRlmR .node .label,#mermaid-svg-M1JNMmbPnYhXRlmR .image-shape .label,#mermaid-svg-M1JNMmbPnYhXRlmR .icon-shape .label{text-align:center;}#mermaid-svg-M1JNMmbPnYhXRlmR .node.clickable{cursor:pointer;}#mermaid-svg-M1JNMmbPnYhXRlmR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-M1JNMmbPnYhXRlmR .arrowheadPath{fill:#333333;}#mermaid-svg-M1JNMmbPnYhXRlmR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-M1JNMmbPnYhXRlmR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-M1JNMmbPnYhXRlmR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M1JNMmbPnYhXRlmR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-M1JNMmbPnYhXRlmR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M1JNMmbPnYhXRlmR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster text{fill:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR .cluster span{color:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-M1JNMmbPnYhXRlmR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-M1JNMmbPnYhXRlmR rect.text{fill:none;stroke-width:0;}#mermaid-svg-M1JNMmbPnYhXRlmR .icon-shape,#mermaid-svg-M1JNMmbPnYhXRlmR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-M1JNMmbPnYhXRlmR .icon-shape p,#mermaid-svg-M1JNMmbPnYhXRlmR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-M1JNMmbPnYhXRlmR .icon-shape .label rect,#mermaid-svg-M1JNMmbPnYhXRlmR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-M1JNMmbPnYhXRlmR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-M1JNMmbPnYhXRlmR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-M1JNMmbPnYhXRlmR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 用户提问
问题向量化
向量检索 Top-K
召回相关文档片段
构建增强 Prompt
LLM 生成答案
返回答案与引用来源
各阶段说明:
- 问题向量化:使用与离线索引相同的 Embedding 模型,把用户问题转成向量。
- 相似度检索:在向量数据库中按余弦相似度等指标检索最相关的 Top-K 片段。
- 重排序(可选):对初步召回结果使用重排序模型进一步精排,提升相关性。
- Prompt 组装:把检索到的原文片段作为上下文,与用户问题、系统指令一起拼装成 Prompt。
- 生成答案:LLM 基于参考资料生成回答,并尽量遵循"只依据给定材料回答"的约束。
- 来源引用:把答案对应的文档片段、章节或链接返回给用户,实现可追溯。
一个简化版的 Prompt 模板如下:
text
你是一名企业知识助手。请只根据下面的参考资料回答用户问题,
如果参考资料中没有相关信息,请明确回答"资料中未找到相关内容"。
参考资料:
{context}
用户问题:
{question}
4. 企业级知识库落地的完整流程
在企业场景中,从零搭建一个可用的 RAG 系统,通常包括以下几个步骤。
4.1 需求与数据盘点
先明确以下几个问题:
- 系统要解决什么业务问题?是客服问答、内部制度查询,还是销售辅助?
- 知识来源有哪些?PDF、Word、网页、表格、工单、数据库记录还是接口数据?
- 知识更新的频率如何?是每天更新,还是需要准实时更新?
- 答案的准确性和可追溯性要求有多高?
这一步决定了后续的文档处理策略、索引方案和评估标准。
4.2 文档解析与清洗
企业文档格式繁杂,解析质量直接影响最终效果。常见处理包括:
- 格式转换:将 PDF、Word、HTML 等统一转换为纯文本或结构化文本。
- 布局还原:保留标题、段落、表格、列表的层级关系。
- 噪音清洗:去除页眉页脚、水印、乱码、重复内容和不相关广告。
- 信息抽取:必要的场景下抽取关键字段,如标题、日期、编号、责任人等。
4.3 文本切分(Chunking)
切分是 RAG 中最容易被低估、却非常关键的环节。切分粒度过大会导致检索不精确、上下文冗余;过小又会丢失上下文语义。
常用的切分策略:
- 固定长度切分:按字符数或 Token 数切分,简单但可能切断句子。
- 基于分隔符切分:按段落、标题、换行等语义边界切分。
- 递归切分:优先按大分隔符切分,长度不足时递归使用更小分隔符。
- 结构感知切分:保留 Markdown 标题、代码块、表格等结构。
同时建议保留一定长度的重叠区间(Overlap),避免关键信息恰好落在两个片段之间被切断。
4.4 向量化与索引构建
选择 Embedding 模型时,需要考虑语言、领域、维度、成本和性能。对于中文企业场景,应优先选择对中文支持良好、且在通用语义检索上表现稳定的模型。
索引构建的核心要素:
- 向量数据库:如 Milvus、Qdrant、Weaviate、Elasticsearch 的向量能力等。
- 元数据存储:除向量外,还应保存文档名、来源、页码、更新时间等元数据,便于过滤和引用。
- 混合检索:向量检索擅长语义匹配,关键词检索擅长精确命中,两者结合通常效果更好。
- 多路召回与融合:结合向量、关键词、规则等多路结果,通过 RRF 等方式融合排序。
4.5 检索与重排序
初步召回后,可以引入重排序模型提高前几条结果的相关性。重排序模型通常基于 Cross-Encoder,比双塔 Embedding 的精度更高,但计算成本也更高,因此先粗排再精排是常见的工程方案。
4.6 Prompt 组装与答案生成
将检索到的片段、用户问题、系统指令、历史对话等内容按模板组织,调用 LLM 生成答案。企业场景中通常还需要:
- 限制答案范围,避免脱离资料编造内容。
- 要求引用来源,如"依据《差旅报销制度》第三章"。
- 设置输出格式,如摘要、步骤、表格或结构化字段。
- 对敏感内容做过滤和脱敏处理。
4.7 评估与持续优化
RAG 系统上线不是终点,而是持续优化的起点。需要建立评估体系,跟踪以下指标:
- 检索命中率:正确答案是否出现在检索结果中。
- 生成准确率:最终答案是否与参考资料一致。
- 引用完整性:每条关键结论是否都有来源。
- 用户反馈:点赞、点踩、人工复核结果等。
根据评估结果,再持续优化切分、Embedding、检索策略和 Prompt。
5. 一个可运行的最小实现
下面用一个简化的 Python 示例,演示从文档加载、切分、向量化到检索问答的完整链路。示例基于 LangChain,便于读者快速理解。
python
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# 1. 准备文档
document = """
企业员工差旅报销制度:
员工出差前需在 OA 系统提交出差申请,经部门负责人审批后方可出行。
出差期间住宿标准:一线城市每人每晚不超过 500 元,其他城市不超过 350 元。
出差结束后 5 个工作日内,需提交报销单及发票,逾期不予受理。
"""
# 2. 文本切分
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=200,
chunk_overlap=30,
separators=["\n", "。", ",", " "]
)
chunks = text_splitter.split_text(document)
# 3. 向量化并写入向量数据库
embeddings = OpenAIEmbeddings()
vectorstore = Chroma.from_texts(chunks, embedding=embeddings)
# 4. 构建检索问答链
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(temperature=0),
retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)
# 5. 提问
answer = qa_chain.run("一线城市的住宿报销标准是多少?")
print(answer)
实际生产环境中,还需要把 document 替换为真实文件解析结果,把 OpenAIEmbeddings 替换为企业可用的中文嵌入模型,把 Chroma 替换为可持久化的向量数据库,并补充元数据、权限控制、监控和缓存等能力。
6. 常见问题与优化经验
随着系统数据量和复杂度上升,很多团队会在相同的地方踩坑,以下经验可供参考。
召回不准
- 检查切分粒度是否合适,过大的片段会稀释语义。
- 尝试混合检索,补充关键词检索作为补充。
- 引入重排序模型,对 Top-20 再做精排。
答案仍然幻觉
- 检查 Prompt 是否明确要求"只依据参考资料回答"。
- 降低模型温度,减少随机性。
- 对低置信度问题设置拒答策略,而非强行生成。
多文档、多来源冲突
- 在 Prompt 中要求列出矛盾点,并说明信息来源。
- 按照时效性、权威性对来源做加权,优先使用最新、可信度高的文档。
更新不及时
- 将文档更新事件接入离线索引流水线,支持增量更新。
- 重要数据采用准实时索引,非关键数据定期批量更新。
成本与延迟过高
- 合理控制 Top-K 数量,避免无意义地塞入过多上下文。
- 对高频问题做缓存,对长文档做摘要索引,减少 Token 消耗。
7. 总结
RAG 的价值在于:它把"企业私有知识"和"大模型通用能力"连接了起来。通过检索,企业可以让 LLM 的回答建立在真实、可更新、可追溯的资料之上;通过生成,企业又能把检索结果转化为自然、易读的答案。
一个成熟的 RAG 系统,涉及文档解析、文本切分、向量化、混合检索、重排序、Prompt 工程、评估反馈等多个环节。它不是简单的"向量库 + 大模型",而是一条需要围绕数据质量与业务目标持续迭代的工程流水线。
对大多数企业来说,不必一开始就追求复杂的多路召回和重排序,建议先以"最小可用系统"跑通闭环,再根据真实反馈逐步优化。先让回答"有据可查",再让回答"更准、更快、更稳"。