目录
- [1. 大模型的"阿克琉斯之踵"](#1. 大模型的"阿克琉斯之踵")
- [2. RAG 是什么](#2. RAG 是什么)
- [3. RAG 的技术架构](#3. RAG 的技术架构)
- [3.1 离线索引阶段](#3.1 离线索引阶段)
- [3.2 在线检索与生成阶段](#3.2 在线检索与生成阶段)
- [4. 企业级 RAG 系统设计](#4. 企业级 RAG 系统设计)
- [4.1 多源异构数据接入](#4.1 多源异构数据接入)
- [4.2 文档解析的深度挑战](#4.2 文档解析的深度挑战)
- [4.3 检索质量优化](#4.3 检索质量优化)
- [4.4 用户权限与安全](#4.4 用户权限与安全)
- [4.5 可观测性与评估](#4.5 可观测性与评估)
- [5. 落地实践:从 0 到 1 搭建企业知识库](#5. 落地实践:从 0 到 1 搭建企业知识库)
- [5.1 技术选型](#5.1 技术选型)
- [5.2 核心代码示例](#5.2 核心代码示例)
- [5.3 进阶:多路召回 + 重排序](#5.3 进阶:多路召回 + 重排序)
- [5.4 评估体系](#5.4 评估体系)
- [6. 常见踩坑与最佳实践](#6. 常见踩坑与最佳实践)
- [6.1 切分策略选择](#6.1 切分策略选择)
- [6.2 避免"中间丢失"问题](#6.2 避免"中间丢失"问题)
- [6.3 成本控制](#6.3 成本控制)
- [6.4 回答质量保障](#6.4 回答质量保障)
- [7. 总结与展望](#7. 总结与展望)
1. 大模型的"阿克琉斯之踵"
大语言模型(LLM)在近年取得了惊人的突破,无论是 GPT-4、Claude 还是 DeepSeek,都能流畅地回答问题、撰写代码、翻译文本。然而,在实际企业应用中,它们暴露出了几个致命短板:
知识截止日期:模型训练数据有明确的时间边界,无法回答训练截止后发生的事件。比如一个 2024 年初训练的模型,对 2025 年的新技术、新政策一无所知。
幻觉问题:当模型遇到知识盲区时,它不会坦率地说"我不知道",而是会编造看似合理但完全虚构的内容。这在法律、医疗、金融等严肃场景中是不可接受的。
领域知识缺失:通用大模型对企业内部的私有文档、业务流程、产品手册一无所知,无法直接成为企业的"内部专家"。
无法溯源:模型给出的答案无法提供引用来源,用户无法验证信息的真实性,这在企业合规审计中是个硬伤。
上下文窗口限制:尽管上下文窗口在不断扩展,但将海量企业文档全部塞进 prompt 既不经济也不高效,且长上下文下模型容易"迷失"在中间信息中。
2. RAG 是什么
RAG(Retrieval-Augmented Generation,检索增强生成) 是一种将信息检索与文本生成相结合的技术架构。它的核心思想非常简单:先检索,再回答。
打个比方:传统的大模型就像一个闭卷考试的学生,只能凭记忆作答;而 RAG 给了这个学生一本"开卷参考书"------当用户提问时,系统先从知识库中检索出最相关的文档片段,然后将这些片段与问题一起交给大模型,让模型基于"参考资料"来生成答案。
用户提问 → 检索相关文档 → 将文档+问题拼成 Prompt → 大模型生成回答
这个看似简单的思路,一举解决了大模型的多个痛点:
| 痛点 | RAG 的解决方式 |
|---|---|
| 知识截止日期 | 知识库可随时更新,模型"即查即用" |
| 幻觉问题 | 强制模型基于检索到的真实文档回答 |
| 领域知识缺失 | 将企业私有文档索引化,变成可检索知识 |
| 无法溯源 | 检索结果天然带有来源,可在回答中标注引用 |
| 上下文窗口限制 | 只将最相关的片段送入模型,不浪费 token |
3. RAG 的技术架构
一个完整的 RAG 系统分为离线索引 和在线检索两个阶段。
3.1 离线索引阶段
这是"建库"的过程,发生在用户提问之前:
文档加载与解析:从 PDF、Word、Markdown、网页、数据库等多种来源加载文档,并解析为纯文本。这里需要处理表格、图片、代码块等复杂内容。
文档切分(Chunking):将长文档切分成适当大小的文本块。切分过大会降低检索精度,切分过小会丢失上下文。常见策略包括:
- 固定大小切分:按字符数或 token 数切分,简单但可能截断语义
- 语义切分:基于嵌入向量的相似度变化来判断段落边界
- 递归切分:先用大分隔符(如段落),不够再细化到句子
- 文档结构切分:保留 Markdown 标题层级、表格结构等
向量化(Embedding):将每个文本块通过嵌入模型(如 text-embedding-3-large、bge-large-zh)转换为高维向量。语义相近的文本,其向量在空间中距离也更近。
向量存储:将向量存入向量数据库(如 Milvus、Pinecone、Weaviate、Qdrant),并建立索引以支持高效近似最近邻搜索。
#mermaid-svg-AjWysNN2ySyatrYc{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-AjWysNN2ySyatrYc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AjWysNN2ySyatrYc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AjWysNN2ySyatrYc .error-icon{fill:#552222;}#mermaid-svg-AjWysNN2ySyatrYc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AjWysNN2ySyatrYc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AjWysNN2ySyatrYc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AjWysNN2ySyatrYc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AjWysNN2ySyatrYc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AjWysNN2ySyatrYc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AjWysNN2ySyatrYc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AjWysNN2ySyatrYc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AjWysNN2ySyatrYc .marker.cross{stroke:#333333;}#mermaid-svg-AjWysNN2ySyatrYc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AjWysNN2ySyatrYc p{margin:0;}#mermaid-svg-AjWysNN2ySyatrYc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AjWysNN2ySyatrYc .cluster-label text{fill:#333;}#mermaid-svg-AjWysNN2ySyatrYc .cluster-label span{color:#333;}#mermaid-svg-AjWysNN2ySyatrYc .cluster-label span p{background-color:transparent;}#mermaid-svg-AjWysNN2ySyatrYc .label text,#mermaid-svg-AjWysNN2ySyatrYc span{fill:#333;color:#333;}#mermaid-svg-AjWysNN2ySyatrYc .node rect,#mermaid-svg-AjWysNN2ySyatrYc .node circle,#mermaid-svg-AjWysNN2ySyatrYc .node ellipse,#mermaid-svg-AjWysNN2ySyatrYc .node polygon,#mermaid-svg-AjWysNN2ySyatrYc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AjWysNN2ySyatrYc .rough-node .label text,#mermaid-svg-AjWysNN2ySyatrYc .node .label text,#mermaid-svg-AjWysNN2ySyatrYc .image-shape .label,#mermaid-svg-AjWysNN2ySyatrYc .icon-shape .label{text-anchor:middle;}#mermaid-svg-AjWysNN2ySyatrYc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AjWysNN2ySyatrYc .rough-node .label,#mermaid-svg-AjWysNN2ySyatrYc .node .label,#mermaid-svg-AjWysNN2ySyatrYc .image-shape .label,#mermaid-svg-AjWysNN2ySyatrYc .icon-shape .label{text-align:center;}#mermaid-svg-AjWysNN2ySyatrYc .node.clickable{cursor:pointer;}#mermaid-svg-AjWysNN2ySyatrYc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AjWysNN2ySyatrYc .arrowheadPath{fill:#333333;}#mermaid-svg-AjWysNN2ySyatrYc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AjWysNN2ySyatrYc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AjWysNN2ySyatrYc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AjWysNN2ySyatrYc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AjWysNN2ySyatrYc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AjWysNN2ySyatrYc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AjWysNN2ySyatrYc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AjWysNN2ySyatrYc .cluster text{fill:#333;}#mermaid-svg-AjWysNN2ySyatrYc .cluster span{color:#333;}#mermaid-svg-AjWysNN2ySyatrYc 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-AjWysNN2ySyatrYc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AjWysNN2ySyatrYc rect.text{fill:none;stroke-width:0;}#mermaid-svg-AjWysNN2ySyatrYc .icon-shape,#mermaid-svg-AjWysNN2ySyatrYc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AjWysNN2ySyatrYc .icon-shape p,#mermaid-svg-AjWysNN2ySyatrYc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AjWysNN2ySyatrYc .icon-shape .label rect,#mermaid-svg-AjWysNN2ySyatrYc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AjWysNN2ySyatrYc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AjWysNN2ySyatrYc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AjWysNN2ySyatrYc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 📄 多源文档
解析与清洗
文档切分
向量化 Embedding
向量数据库
索引构建完成
3.2 在线检索与生成阶段
这是"问答"的过程,实时响应用户请求:
查询重写与扩展:用户原始问题可能表述模糊或过于简短,需要通过查询改写、HyDE(假设文档嵌入)等技术优化查询质量。
向量检索:将用户问题向量化,在向量数据库中检索 top-K 个最相似的文档片段。
重排序(Rerank):向量检索的精度有限,通过重排序模型(如 Cohere Rerank、bge-reranker)对初检结果进行精细排序,进一步提升相关性。
上下文组装:将检索到的文档片段按模板拼接,加入系统提示词、历史对话等,形成最终 prompt。
生成回答:大模型基于拼接好的 prompt 生成最终答案,并可附带引用来源。
#mermaid-svg-B2YcC9O2heqvlpzv{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-B2YcC9O2heqvlpzv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-B2YcC9O2heqvlpzv .error-icon{fill:#552222;}#mermaid-svg-B2YcC9O2heqvlpzv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-B2YcC9O2heqvlpzv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-B2YcC9O2heqvlpzv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-B2YcC9O2heqvlpzv .marker.cross{stroke:#333333;}#mermaid-svg-B2YcC9O2heqvlpzv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-B2YcC9O2heqvlpzv p{margin:0;}#mermaid-svg-B2YcC9O2heqvlpzv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-B2YcC9O2heqvlpzv .cluster-label text{fill:#333;}#mermaid-svg-B2YcC9O2heqvlpzv .cluster-label span{color:#333;}#mermaid-svg-B2YcC9O2heqvlpzv .cluster-label span p{background-color:transparent;}#mermaid-svg-B2YcC9O2heqvlpzv .label text,#mermaid-svg-B2YcC9O2heqvlpzv span{fill:#333;color:#333;}#mermaid-svg-B2YcC9O2heqvlpzv .node rect,#mermaid-svg-B2YcC9O2heqvlpzv .node circle,#mermaid-svg-B2YcC9O2heqvlpzv .node ellipse,#mermaid-svg-B2YcC9O2heqvlpzv .node polygon,#mermaid-svg-B2YcC9O2heqvlpzv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-B2YcC9O2heqvlpzv .rough-node .label text,#mermaid-svg-B2YcC9O2heqvlpzv .node .label text,#mermaid-svg-B2YcC9O2heqvlpzv .image-shape .label,#mermaid-svg-B2YcC9O2heqvlpzv .icon-shape .label{text-anchor:middle;}#mermaid-svg-B2YcC9O2heqvlpzv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-B2YcC9O2heqvlpzv .rough-node .label,#mermaid-svg-B2YcC9O2heqvlpzv .node .label,#mermaid-svg-B2YcC9O2heqvlpzv .image-shape .label,#mermaid-svg-B2YcC9O2heqvlpzv .icon-shape .label{text-align:center;}#mermaid-svg-B2YcC9O2heqvlpzv .node.clickable{cursor:pointer;}#mermaid-svg-B2YcC9O2heqvlpzv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-B2YcC9O2heqvlpzv .arrowheadPath{fill:#333333;}#mermaid-svg-B2YcC9O2heqvlpzv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-B2YcC9O2heqvlpzv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-B2YcC9O2heqvlpzv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B2YcC9O2heqvlpzv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-B2YcC9O2heqvlpzv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B2YcC9O2heqvlpzv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-B2YcC9O2heqvlpzv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-B2YcC9O2heqvlpzv .cluster text{fill:#333;}#mermaid-svg-B2YcC9O2heqvlpzv .cluster span{color:#333;}#mermaid-svg-B2YcC9O2heqvlpzv 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-B2YcC9O2heqvlpzv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-B2YcC9O2heqvlpzv rect.text{fill:none;stroke-width:0;}#mermaid-svg-B2YcC9O2heqvlpzv .icon-shape,#mermaid-svg-B2YcC9O2heqvlpzv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-B2YcC9O2heqvlpzv .icon-shape p,#mermaid-svg-B2YcC9O2heqvlpzv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-B2YcC9O2heqvlpzv .icon-shape .label rect,#mermaid-svg-B2YcC9O2heqvlpzv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-B2YcC9O2heqvlpzv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-B2YcC9O2heqvlpzv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-B2YcC9O2heqvlpzv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 🔍 用户提问
查询重写/扩展
向量检索 Top-K
重排序 Rerank
上下文组装 Prompt
大模型生成回答
带引用的最终答案
4. 企业级 RAG 系统设计
从 Demo 到生产环境,企业级 RAG 需要解决一系列工程化挑战。
4.1 多源异构数据接入
企业数据散落在各处:Confluence 文档、飞书文档、Notion、GitLab Wiki、传统文件服务器、数据库等。一个好的 RAG 系统需要支持统一的数据接入层,通过连接器(Connector)对接各类数据源,并实现增量同步、定时更新。
4.2 文档解析的深度挑战
实际文档远比 Demo 复杂:
- 表格:不能简单把表格转为纯文本,否则语义全丢。需要保留表格结构,或使用专门的表格理解模型。
- 图片:技术文档中的架构图、流程图包含关键信息,需要多模态模型(如 GPT-4V、Qwen-VL)进行图文理解。
- 公式:学术论文中的 LaTeX 公式需要正确解析。
- 混合布局:PDF 中的双栏、图文混排需要版面分析。
4.3 检索质量优化
Naive RAG 的检索准确率往往不到 60%,以下策略能显著提升:
| 策略 | 说明 | 效果 |
|---|---|---|
| 混合检索 | 向量检索 + 关键词检索(BM25)结合 | 提升召回率 |
| 多路召回 | 用不同粒度/不同嵌入模型多路检索后合并 | 覆盖更多相关片段 |
| 重排序 | 初检后用精细模型重排 | 显著提升精度 |
| 父文档召回 | 检索小粒度片段,但返回其所属的大粒度父文档 | 保留更多上下文 |
| 元数据过滤 | 按时间、来源、分类等元数据预过滤 | 缩小检索范围,提升精度 |
4.4 用户权限与安全
这是企业场景最核心的需求之一。不同用户对不同文档有不同权限------HR 能看薪资文档,但普通员工不能。RAG 系统必须在检索时就过滤掉用户无权访问的文档,而不是在生成答案后再做"掩码",否则可能造成信息泄露。
4.5 可观测性与评估
企业级系统需要完善的监控体系:
- 检索质量评估:定期采样,人工或自动评估检索的命中率、MRR@K、NDCG@K
- 生成质量评估:答案的忠实度(是否忠于检索文档)、相关性、无害性
- 性能监控:端到端延迟、各环节耗时、QPS、错误率
- 成本追踪:Embedding 调用量、LLM Token 消耗
5. 落地实践:从 0 到 1 搭建企业知识库
5.1 技术选型
当前主流技术栈组合:
文档解析:Unstructured / LlamaParse / MinerU
嵌入模型:text-embedding-3-large / bge-large-zh-v1.5 / jina-embeddings-v3
向量数据库:Milvus / Qdrant / Weaviate / Elasticsearch
检索框架:LangChain / LlamaIndex / Haystack
重排序:Cohere Rerank / bge-reranker-v2-m3
大模型:GPT-4o / Claude 3.5 / DeepSeek-V3 / Qwen-Max
应用框架:FastAPI / Dify / FastGPT / LangServe
5.2 核心代码示例
以下是一个基于 LangChain 的简化版 RAG 实现:
python
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Chroma
from langchain_openai import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. 加载文档
loader = DirectoryLoader("./docs/", glob="**/*.md")
documents = loader.load()
# 2. 文档切分
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", ",", " ", ""]
)
chunks = text_splitter.split_documents(documents)
# 3. 向量化并存入向量库
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 4. 构建 RAG 问答链
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt_template = """你是一个专业的企业知识库助手。
请基于以下参考资料回答用户问题。如果参考资料中没有相关信息,请明确说"根据现有资料无法回答"。
参考资料:
{context}
用户问题:{question}
请回答:"""
QA_PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 5}),
chain_type_kwargs={"prompt": QA_PROMPT},
return_source_documents=True
)
# 5. 提问
result = qa_chain.invoke({"query": "公司年假政策是什么?"})
print(f"答案:{result['result']}")
print(f"参考来源:{result['source_documents']}")
5.3 进阶:多路召回 + 重排序
python
from langchain.retrievers import BM25Retriever, EnsembleRetriever
from langchain_community.retrievers import CohereRerankRetriever
# 构建 BM25 关键词检索器
bm25_retriever = BM25Retriever.from_documents(
chunks, k=10
)
# 构建向量检索器
vector_retriever = vectorstore.as_retriever(
search_kwargs={"k": 10}
)
# 混合检索:向量 + 关键词
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.3, 0.7] # 权重分配
)
# 加上重排序
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
compressor = CohereRerank(top_n=5)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
# 用压缩后的检索器构建问答链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=compression_retriever,
return_source_documents=True
)
5.4 评估体系
搭建 RAG 系统后,需要建立评估闭环:
python
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_recall,
context_precision,
)
from datasets import Dataset
# 准备评估数据:问题、答案、上下文、参考答案
eval_dataset = Dataset.from_dict({
"question": ["年假怎么申请?", "加班费怎么算?"],
"answer": [rag_answer_1, rag_answer_2],
"contexts": [retrieved_contexts_1, retrieved_contexts_2],
"ground_truth": ["OA系统提交年假申请...", "工作日加班1.5倍工资..."]
})
# 运行评估
result = evaluate(
eval_dataset,
metrics=[faithfulness, answer_relevancy, context_recall, context_precision]
)
print(result)
6. 常见踩坑与最佳实践
6.1 切分策略选择
- 技术文档:推荐 500-800 token 的块大小,保留代码块的完整性
- 法律合同:推荐按条款切分,块大小可以更大(1000-2000 token)
- FAQ 场景:每个 QA 对作为独立块,不做切分
- 通用场景:500 token + 50 token 重叠是常见起点
6.2 避免"中间丢失"问题
当检索到的文档块放在 prompt 中间位置时,模型容易忽略中间信息。解决方案:
- 将最相关的文档放在 prompt 开头和结尾
- 控制送入模型的文档总长度
- 使用
Map-Reduce或Refine链式策略处理大量文档
6.3 成本控制
- Embedding 缓存:相同文本不重复向量化,节省 API 调用费用
- 检索结果缓存:热门问题的检索结果可缓存,减少重复计算
- 模型分层:简单问题用便宜的模型(如 GPT-4o-mini),复杂问题用强模型
- 本地部署:对安全要求高的场景,使用本地部署的嵌入模型和 LLM
6.4 回答质量保障
- 强制引用:在 prompt 中要求模型必须在回答中标注引用来源
- 拒答机制:当检索结果相关度低于阈值时,直接回复"无法回答"而非强行编造
- 人工审核回路:对关键场景(如法务、医疗),加入人工确认环节
7. 总结与展望
RAG 是目前让大模型"接地气"最成熟、最经济的技术方案。它不需要微调模型,不需要重新训练,只需将企业知识"外挂"到模型之外,就能让通用大模型摇身一变成为领域专家。
从技术演进来看,以下方向值得关注:
- Agentic RAG:RAG 不再是简单的"检索-生成",而是由 Agent 自主规划检索策略:拆解复杂问题、决定何时检索、检索什么、如何验证结果。
- Graph RAG:将文档知识构建为知识图谱,结合图结构进行推理式检索,理解实体间的关系而不仅是文本相似度。
- 多模态 RAG:检索对象从纯文本扩展到图片、表格、视频,生成结果也可以包含图表。
- Self-RAG:模型在生成过程中自我反思,判断是否需要检索、检索结果是否足够、生成的答案是否忠实于检索内容。
RAG 不是银弹,但它是一个务实且高效的起点。对于绝大多数企业来说,现阶段最重要的事情是:先把知识库建起来,让数据流动起来,在实践中迭代优化。毕竟,一个能跑通的 80 分系统,远比一个停留在 PPT 上的 100 分方案更有价值。