企业级RAG落地避坑指南:从0到1拆解全流程优化方案,解锁高效生产级应用!
文档切片不科学导致信息丢失?向量模型选型不当影响召回精度?大模型调参混乱难以稳定输出?本文基于百家企业落地验证,,直击开发瓶颈,提供!✅ 从Token切片到语义切片的✅ Embedding选型与向量数据库的✅ 检索前问题改写&标签增强、检索后Rerank的✅ 大模型Prompt工程与参数调优的:🔹 企业技术负责人:构建高可用RAG架构,降低试错成本🔹 开发者:
为什么你的RAG效果总差强人意?
文档切片不科学导致信息丢失?向量模型选型不当影响召回精度?大模型调参混乱难以稳定输出?
本文基于百家企业落地验证,系统性拆解RAG五大核心阶段(文档准备→解析切片→向量存储→检索召回→生成优化),直击开发瓶颈,提供可复用的框架级解决方案!
📌 你将获得
✅ 从Token切片到语义切片的文档解析最佳实践
✅ Embedding选型与向量数据库的性能-成本平衡策略
✅ 检索前问题改写&标签增强、检索后Rerank的双重精度提升方案
✅ 大模型Prompt工程与参数调优的工业级适配技巧
适合人群:
🔹 企业技术负责人:构建高可用RAG架构,降低试错成本
🔹 开发者:快速定位问题环节,掌握全链路调优方法论
🔹 技术团队:复制标准化开发范式,加速项目交付
前面已经对RAG的基础原理做了介绍,不清楚的可以先看RAG的基础原理-CSDN博客
这里我们还是先回顾 RAG 的工作流程
RAG(Retrieval Augmented Generation,检索增强生成)是一种结合了信息检索和生成式模型的技术,能够在生成答案时利用外部知识库中的相关信息。它的工作流程可以分为几个关键步骤:解析与切片、向量存储、检索召回、生成答案等。

基于上面的工作流程,我们将从 RAG 中的每一个环节入手,尝试优化 RAG 的效果。
1 文档准备阶段
首先,你需要确保文档包含员工需要的全部信息,提前做好准备:
-
包含相关知识:你需要验证文档中是否包含了用户可能提出的问题。 例如,公司新增了一个"数据分析部",但知识库中没有相关文档,导致无法回答"数据分析部的职责是什么?"的问题。解决方案是补充该部门的职责文档。
-
表达要直接和清晰:你需要改进文档的表达方式,确保信息能被快速检索和准确理解,从而更好匹配用户的提问需求,避免隐藏关键信息。 例如,员工提问"张伟的工作职责是什么?“,但文档中只提到"张伟负责IT支持”,没有明确职责描述。可以在文档中补充更详细的职责描述。
这两项准备工作是保证 RAG 应用优化效果的前提,如果你已经做好准备,就可以开始深入了解和优化 RAG 应用的各个环节了。
2 文档解析与切片阶段
首先,RAG 应用会解析你的文档内容,然后对文档内容进行切片。
大模型在回答问题时拿到的文档切片如果缺少关键信息,会回答不准确;如果拿到的文档切片非关联信息过多(噪声),也会影响回答质量。即过少或过多的信息,都会影响模型的回答效果。
因此,在对文档进行解析与切片时,需要确保最终的切片信息完整,但不要包含太多干扰信息。
在这个阶段你可能会遇到以下问题:
| 类别 | 细分类型 | 改进策略 | 场景化示例 |
| 文档解析 | 文档类型不统一,部分格式的文档不支持解析 比如前面用到的 SimpleDirectoryLoader 并不支持 Keynote 格式的文件 | 开发对应格式的解析器,或转换文档格式 | 例如,某公司使用了大量的 Keynote 文件存储员工信息,但现有的解析器不支持 Keynote 格式。可以开发 Keynote 解析器或将文件转换为支持的格式(如 PDF)。 |
| 已支持解析的文档格式里,存在一些特殊内容 比如文档里嵌入了表格、图片、视频等 | 改进文档解析器 | 例如,某文档中包含了大量的表格和图片,现有解析器无法正确提取表格中的信息。可以改进解析器,使其能够处理表格和图片。 | |
| ... | ... | ... | |
| 文档切片 | 文档中有很多主题接近的内容 比如工作手册文档中,需求分析、开发、发布等每个阶段都有注意事项、操作指导 | 扩写文档标题及子标题 「注意事项」=>「需求分析>注意事项」 建立文档元数据(打标) | 例如,某文档中包含多个阶段的注意事项,用户提问“需求分析的注意事项是什么?”时,系统返回了所有阶段的注意事项。可以通过扩展标题和打标来区分不同阶段的内容。 |
| 文档切片长度过大,引入过多干扰项 | 减少切片长度,或结合具体业务开发为更合适的切片策略 | 例如,某文档的切片长度过大,包含了多个不相关的主题,导致检索时返回了无关信息。可以减少切片长度,确保每个切片只包含一个主题。 | |
| 文档切片长度过短,有效信息被截断 | 扩大切片长度,或结合具体业务开发为更合适的切片策略 | 例如,某文档中每个切片只有一句话,导致检索时无法获取完整的上下文信息。可以增加切片长度,确保每个切片包含完整的上下文。 | |
| ... | ... | ... |
2.1文档解析
LLM大模型交互由于是文字交互,为了尽可能保证格式,所以采用markdown的格式。
对于一些纯文本文件相对简单,但是在实际工作中,编写代码将 PDF 妥善地转为 Markdown 并非易事。
由于pdf/docx等多种文件格式来源的多样性,文件解析到markdown过程中可能存在一些格式上的小问题,比如 PDF 里的跨页表格行可能被解析为多行、表格中的信息解析、图片内容解析等。
不具备较强编程能力的团队建议优先借助第三方的解析平台,它们可以使用大模型对生成的markdown文本进行润色,修正目录层级、缺失信息等。
例如:阿里百炼提供的 DashScopeParse 来完成 PDF、Word 等格式的文件解析。或者是腾讯的相关服务。他们都有不错的效果,当然你也可以使用开源方案来进行文件解析,多费点时间,也能取得不错的效果。
2.2 文档切片
在文档切片的过程中,切片方式会影响检索召回的效果。让我们通过具体例子来了解不同切片方法的特点。
首先创建一个通用的示例文本: “LlamaIndex是一个强大的RAG框架。它提供了多种文档处理方式。用可以根据需选择合适的方法。”
接下来,让我们看看各种切片方法的特点和示例:
2.2.1 Token 切片
适合对 Token 数量有严格要求的场景,比如使用上下文长度较小的模型时。
使用Token切片(chunk_size=10)后可能的结果:
-
切片1: “LlamaIndex是一个强大的RAG”
-
切片2: “框架。它提供了多种文”
-
切片3: “档处理方式。用户可以”
2.2.2 句子切片
这是默认的切片策略,会保持句子的完整性。
同样的文本使用句子切片后:
-
切片1: “LlamaIndex是一个强大的RAG框架。”
-
切片2: “它提供了多种文档处理方式。”
-
切片3: “用户可以根据需求选择合适的方法。”
2.2.3 句子窗口切片
每个切片都包含周围的句子作为上下文窗口。
示例文本使用句子窗口切片(window_size=1)后:
-
切片1: “LlamaIndex是一个强大的RAG框架。” 上下文: “它提供了多种文档处理方式。”
-
切片2: “它提供了多种文档处理方式。” 上下文: “LlamaIndex是一个强大的RAG框架。用户可以根据需求选择合适的方法。”
-
切片3: “用户可以根据需求选择合适的方法。” 上下文: “它提供了多种文档处理方式。”
2.2.4 语义切片
根据语义相关性自适应地选择切片点。
示例文本: “LlamaIndex是一个强大的RAG框架。它提供了多种文档处理方式。用户可以根据需求选择合适的方法。此外,它还支持向量检索。这种检索方式非常高效。”
语义切片可能的结果:
-
切片1: “LlamaIndex是一个强大的RAG框架。它提供了多种文档处理方式。用户可以根据需求选择合适的方法。”
-
切片2: “此外,它还支持向量检索。这种检索方式非常高效。” (注意这里是按语义相关性分组的)
2.2.5 Markdown 切片
专门针对 Markdown 文档优化的切片方法。
示例 Markdown 文本:
# RAG框架
LlamaIndex是一个强大的RAG框架。
## 特点
- 提供多种文档处理方式
- 支持向量检索
- 使用简单方便
### 详细说明
用户可以根据需求选择合适的方法。
Markdown切片会根据标题层级进行智能分割:
-
切片1: “# RAG框架\nLlamaIndex是一个强大的RAG框架。”
-
切片2: “## 特点\n- 提供多种文档处理方式\n- 支持向量检索\n- 使用简单方便”
-
切片3: “### 详细说明\n用户可以根据需求选择合适的方法。”
在实际应用中,选择切片方法时不必过于纠结,你可以这样思考:
-
如果你刚开始接触 RAG,建议先使用默认的句子切片方法,它在大多数场景下都能提供不错的效果
-
当你发现检索结果不够理想时,可以尝试:
-
处理长文档且需要保持上下文?试试句子窗口切片
-
文档逻辑性强、内容专业?语义切片可能会有帮助
-
模型总是报 Token 超限?Token 切片可以帮你精确控制
-
处理 Markdown 文档?别忘了有专门的 Markdown 切片
-
没有最好的切片方法,只有最适合你场景的方法。你可以尝试不同的切片方法,观察 Ragas (下一篇文章会分享)评估结果,找到最适合你需求的方案。学习的过程就是不断尝试和调整的过程!
3 切片向量化与存储阶段
文档切片后,你还需要对其建立索引,以便后续检索。一个常见的方案是使用嵌入(Embedding)模型将切片向量化,并存储到向量数据库中。
在这一阶段,你需要选择合适的 Embedding 模型以及向量数据库,这对于提升检索效果至关重要。
3.1 了解 Embedding 与向量化
Embedding 模型可以将文本转换为高维向量,用于表示文本语义,相似的文本会映射到相近的向量上,检索时可以根据问题的向量找到相似度高的文档切片。
平面坐标系中的有向线段是 2 维向量。例如,从原点 (0, 0) 到 A (xa, ya) 的有向线段可以称为向量 A。向量 A 与向量 B 之间的夹角越小,也就意味着其相似度越高。

3.2 选择合适的 Embedding 模型
不同的 Embedding 模型对相同的几段文字进行计算时,得到的向量可能会完全不同。通常越新的 Embedding 模型,其表现越好。例如开始使用的是 text-embedding-v2。如果换成更新的版本 text-embedding-v3 你会发现即使不去做前面的优化,检索效果也会有一定的提升。
3.3 选择合适的向量数据库
在构建 RAG 应用时,你有多种向量存储方案可以选择,从简单到复杂依次是:
3.3.1 内存向量存储
最简单的方式是使用 LlamaIndex 内置的内存向量存储。只需安装 llama-index 包,无需额外配置,就能快速开发和测试 RAG 应用。
优点是快速上手,适合开发测试;缺点是数据无法持久化,且受限于内存大小。
3.3.2 本地向量数据库
当数据量增大时,可以使用开源的向量数据库,如 Milvus、Qdrant 等。这些数据库提供了数据持久化和高效检索能力。
优点是功能完整、可控性强;缺点是需要自行部署维护。
3.3.3 云服务向量存储
对于生产环境,推荐使用云服务提供的向量存储能力。阿里云提供了多种选择:
-
向量检索服务(DashVector):按量付费、自动扩容,适合快速启动项目。
-
向量检索服务 Milvus 版:兼容开源 Milvus,便于迁移已有应用。
-
已有数据库的向量能力:如果已使用阿里云数据库(RDS、PolarDB等),可直接使用其向量功能
云服务的优势在于:
-
无需关注运维,自动扩容
-
提供完善的监控和管理工具
-
按量付费,成本可控
-
支持向量 + 标量的混合检索,提升检索准确性
选择建议:
-
开发测试时使用内存向量存储
-
小规模应用可以使用本地向量数据库
-
生产环境如果运维团队经验不足,推荐使用云服务,可根据具体需求选择合适的服务类型
4 检索召回阶段
检索阶段会遇到的主要问题就是,很难从众多文档切片中,找出和用户问题最相关、且包含正确答案信息的片段。
从切入时机来看,可以将解法分为两大类:
-
在执行检索前,很多用户问题描述是不完整、甚至有歧义的,你需要想办法还原用户真实意图,以便提升检索效果。
-
在执行检索后,你可能会发现存在一些无关的信息,需要想办法减少无关信息,避免干扰下一步的答案生成。
| 时机 | 改进策略 | 示例 |
| 检索前 | 问题改写 | 「附近有好吃的餐厅吗?」=> 「请推荐我附近的几家评价较高的餐厅」 |
| 问题扩写 通过增加更多信息,让检索结果更全面 | 「张伟是哪个部门的?」=> 「张伟是哪个部门的?他的联系方式、职责范围、工作目标是什么?」 | |
| 基于用户画像扩展上下文 结合用户信息、行为等数据扩写问题 | 内容工程师提问「工作注意事项」=> 「内容工程师有哪些工作注意事项」 项目经理提问「工作注意事项」=> 「项目经理有哪些工作注意事项」 | |
| 提取标签 提取标签,用于后续标签过滤+向量相似度检索 | 「内容工程师有哪些工作注意事项」=> 标签过滤:{"岗位": "内容工程师"}向量检索:「内容工程师有哪些工作注意事项」 | |
| 反问用户 | 「工作职责是什么」=> 大模型反问:「请问你想了解哪个岗位的工作职责」 实现反问的提示词 | |
| 思考并规划多次检索 | 「张伟不在,可以找谁」 => 大模型思考规划: => task_1:张伟的职责是什么, task_2:${task_1_result}职责的人有谁 => 按顺序执行多次检索 | |
| ... | ... | |
| 检索后 | 重排序 ReRank + 过滤 多数向量数据库会考虑效率,牺牲一定精确度,召回的切片中可能有一些实际相关性不够高 | chunk1、chunk2...、chunk10 => chunk 2、chunk4、chunk5 |
| 滑动窗口检索 在检索到一个切片后,补充前后相邻的若干个切片。这样做的原因是:相邻切片之间往往存在语义联系,仅看单个切片可能会丢失重要信息。 滑动窗口检索确保了不会因为过度切分而丢失文本间的语义连接。 | 常见的实现是句子滑动窗口,你可以用下方的简化形式来理解: 假设原始文本为:ABCDEFG(每个字母代表一个句子) 当检索到切片:D 补充相邻切片后:BCDEF(前后各取2个切片) 这里的BC和EF是D的上下文。比如:BC可能包含解释D的背景信息EF可能包含D的后续发展或结果这些上下文信息能帮助你更准确地理解D的完整含义通过召回这些相关的上下文切片,你可以提高检索结果的准确性和完整性。 | |
| ... | ... |
4.1 执行检索前的优化方向
4.1.1 问题改写
🤔 为什么需要问题改写?
想象一下你在搜索"找张伟"或者"张伟 部门"这样的关键词。看似简单,但对于RAG系统来说,这样零散的搜索词可能不太好回答。因为在真实场景中,可能存在多个叫张伟的同事,而且用户输入的关键词往往过于简单,缺少必要的上下文信息。
✨ 问题改写能带来什么?
问题改写就像是帮助系统更好地理解用户意图。比如当你问"找张伟"时,系统可以把问题改写为更完整的形式,比如"请告诉我公司中所有叫张伟的员工及其所在部门"。这样的改写不仅能提高检索的准确性,还能让回答更加全面。
接下来,你可以通过实际案例来体验不同的问题改写策略。在这个案例中,你将使用以下配置:
-
文档:Markdown格式
-
切片:默认句子切片策略
-
模型:text-embedding-v3
-
存储:默认向量存储
【方法一:使用大模型扩充用户问题】
你可以让大模型充当一个问题改写助手。它会帮你把简单的问题改写得更加完整和清晰。比如,它不仅会考虑到可能存在多个张伟的情况,还会把相关的上下文信息都补充进去。看看具体怎么做:
例如:
系统角色设定:
你是一个专业的问题改写助手。你的任务是将用户的原始问题扩充为一个更完整、更全面的问题。
规则:
1. 将可能的歧义、相关概念和上下文信息整合到一个完整的问题中
2. 使用括号对歧义概念进行补充说明
3. 添加关键的限定词和修饰语
4. 确保改写后的问题清晰且语义完整
5. 对于模糊概念,在括号中列举主要可能性
原始问题:
{query}
请生成一个综合的改写问题,确保:
- 包含原始问题的核心意图
- 涵盖可能的歧义解释
- 使用清晰的逻辑关系词连接不同方面
- 必要时使用括号补充说明
输出格式:
[综合改写] - 改写后的问题
【方法二:将单一查询改写为多步骤查询】
除了改写问题,你还可以尝试另一种思路:把复杂的问题拆解成简单的步骤。LlamaIndex 提供了两个强大的工具来实现这个功能:
-
StepDecomposeQueryTransform: 这个工具可以帮你把一个复杂问题分解成多个子问题。比如对于"张伟是哪个部门的?",它会先分解为:
-
“公司里有几个叫张伟的员工?”
-
“这些张伟分别在哪些部门?”
-
这样可以更全面地获取所有张伟的信息。
-
MultiStepQueryEngine: 这个查询引擎会按顺序处理这些子问题。它会先获取公司所有张伟的信息,然后再查询每个张伟的部门信息,最终将答案整合成一个完整的回应,告诉用户"公司有三名张伟,分别在教研部、课程开发部和IT部"。
这种方法就像解决数学题一样 - 把大问题分解成小问题往往更容易得到准确的答案。不过要注意,这种方法会多次调用大模型,所以会消耗更多的token。
【方法三:用假设文档来增强检索(HyDE)】
前面的方法都是在调整问题本身,现在让我们换个思路:如果我们先假设一个可能的答案会怎样?这就是HyDE(Hypothetical Document Embeddings)方法的独特之处。
它的工作方式很有趣:
-
先让大模型基于问题编一个"假想的答案文档"
-
用这个假想文档来检索真实文档
-
最后用检索到的真实文档来生成实际答案
这就像你在找一本书时,心里已经有了一个大致的内容轮廓,然后用这个轮廓去图书馆匹配相似的书籍。让我们看看具体怎么实现:
有趣的是,虽然这个"假想文档"完全是AI编造的,但它的结构和风格与真实的公司员工信息非常相似。LlamaIndex提供了灵活的控制机制来优化这个过程:
HyDEQueryTransform类允许我们通过以下方式精确控制假想文档的生成:
-
自定义LLM:通过llm参数传入不同的大模型配置,可以选择更适合的语言模型来生成假想文档
-
提示词模板:通过hyde_prompt参数自定义提示词模板,精确控制输出的格式和内容
-
查询策略:使用include_original参数决定是否将原始查询与假想文档结合使用
TransformQueryEngine则作为查询引擎的包装器,它会:
-
先调用HyDEQueryTransform生成假想文档
-
使用假想文档进行向量检索
-
最后返回查询结果
这种架构让我们能在不修改底层查询引擎的情况下,通过调整HyDEQueryTransform的参数来优化检索效果。即使假想文档的具体内容可能不够准确,但通过精心设计的配置,它可以帮助系统更准确地检索相关信息。
4.4.2 提取标签增强检索
在向量检索的基础上,我们还可以添加标签过滤来提升检索精度。这种方式类似于图书馆既有书名检索,又有分类编号系统,能让检索更精准。
标签提取有两个关键场景:
-
建立索引时,从文档切片中提取结构化标签
-
检索时,从用户问题中提取对应的标签进行过滤
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("DASHSCOPE_API_KEY"), base_url="https://dashscope.aliyuncs.com/compatible-mode/v1")
system_message = """你是一个标签提取专家。请从文本中提取结构化信息,并按要求输出标签。
---
【支持的标签类型】
- 人名
- 部门名称
- 职位名称
- 技术领域
- 产品名称
---
【输出要求】
1. 请用 JSON 格式输出,如:[{"key": "部门名称", "value": "教研部"}]
2. 如果某类标签未识别到,则不输出该类
---
待分析文本如下:
"""
def extract_tags(text):
completion = client.chat.completions.create(
model="qwen-turbo",
messages=[
{'role': 'system', 'content': system_message},
{'role': 'user', 'content': text}
],
response_format={"type": "json_object"}
)
return completion.choices[0].message.content
当我们建立索引时,可以将这些标签与文档切片一起存储。这样在检索时,比如用户问"张伟是哪个部门的",我们可以:
-
从问题中提取人名标签 {"key": "人名", "value": "张伟"}
-
先用标签过滤出所有包含"张伟"的文档切片
-
再用向量相似度检索找出最相关的内容
这种"标签过滤+向量检索"的组合方式,能大幅提升检索的准确性。特别是在处理结构化程度较高的企业文档时,这个方法效果更好。
4.2 在执行检索后
4.2.1 Rerank(重排序)
Rerank模型的工作原理
Rerank模型的核心任务是在召回阶段的结果基础上,进一步计算查询(Query)与每个候选之间的细粒度相关性。其工作原理通常基于交叉编码器(Cross-Encoder),直接建模Query和候选文本的交互,从而计算出相关性分数并进行重新排序。与双编码器(Bi-Encoder)相比,Rerank模型的计算量更大但精度更高,适合小规模候选集。
Rerank模型在RAG中的作用
-
重新排序和筛选:Rerank模型能够对检索到的文档进行再次排序和筛选,优先展示与问题更相关的文档。这样,当LLM开始生成回答时,它会优先考虑这些排名靠前的、更加相关的文档,从而提高生成回答的准确性和质量。
-
提高精度:Rerank模型通过更复杂的模型对候选结果重新打分和排序,消除召回阶段的噪声,提升结果精度。它结合上下文信息和用户意图,提供更相关的结果。
例如先从向量数据库中检索召回 20 条文档切片,再借助rerank模型进行重排序,并且筛选出其中最相关的 5 条参考信息。运行代码后你可以看到,同样是 5 条参考信息,这次大模型能够准确回答问题了。
现在,大模型会根据你的问题和检索召回的内容,生成最终的答案。
5 生成答案阶段
做完以上的优化动作,最终这个答案可能还是不及你的预期。你可能会遇到的问题有:
-
没有检索到相关信息,大模型捏造答案。
-
检索到了相关信息,但是大模型没有按照要求生成答案。
-
检索到了相关信息,大模型也给出了答案,但是你希望 AI 给出更全面的答案。
为了解决这些问题,你可以从以下角度着手分析与解决:
5.1 选择合适的大模型
-
如果只是简单的信息查询总结,小参数量的模型足以满足需求。
-
如果你希望答疑机器人能完成较为复杂的逻辑推理,建议选择参数量更大、推理能力更强的大模型。
-
如果你的问题需要查阅大量的文档片段,建议选择上下文长度更大的模型。
-
如果你构建的 RAG 应用面向一些非通用领域,如法律领域,建议使用面向特定领域训练的模型。
5.2 充分优化提示词模板
-
明确要求不编造答案:大模型可能会产生一些不真实的内容,通常称为幻觉。你可以通过提示词要求大模型:「如果所提供的信息不足以回答问题,请明确告知"根据现有信息,我无法回答这个问题。"切勿编造答案。」,来减少大模型产生幻觉的几率。
-
添加内容分隔标记:检索召回的文档切片如果随意混杂在提示词里,人也很难看清整个提示词的结构,大模型也会受到干扰。建议将提示词和检索切片明确地分开,以便大模型能够正确地理解你的意图。
-
根据问题类型调整模板:不同问题的回答范式可能是不同的,你可以借助大模型识别问题类型,然后映射使用不同的提示词模板。比如有些问题,你希望大模型先输出整体框架,然后再输出细节;有些问题你可能希望大模型言简意赅的给出结论。
5.3 调整大模型的参数
-
如果你希望大模型输出在相同的问题下,输出的内容尽可能相同,你可以在每次模型调用时传入相同的seed值。
-
如果你希望大模型在回答用户问题时不要总是用重复的句子,你可以适当调高 presence_penalty 值。
-
如果你希望查询事实性的内容,可以适当降低 temperature 或 top_p 值;反之,查询创造性的内容时,可以适当增加它们的值。
-
如果你需要限制字数(如生成摘要、关键词)、控制成本或减少响应时间的场景,可以适当降低max_tokens的值,但是若max_tokens过小,可能会导致输出截断,反之,需要生成大段文本时,可以提高它的值。
5.4 调优大模型
如果上述方法都做了充分的尝试,仍然不及预期,或者希望有更进一步的效果提升,你也可以尝试面向你的场景调优一个模型。在后续的章节中,你将学习和实践这一点。
更多推荐



所有评论(0)