这是”AI Agent落地:业务系统智能化改造”系列的第七篇。第六篇讲了RAG全景——是什么、怎么演进、每个环节的作用。这篇讲我实际搭RAG系统时的设计决策:为什么这样选、哪些work、哪些还没验证。
不是”正确答案”,而是”决策过程”。
目录
引言:架构不是画出来的,是踩出来的
一、整体架构:从需求倒推
二、五个关键设计决策
- 分块策略 / 检索通道 / 融合策略 / 上下文组织 / 生成控制
三、踩坑清单
四、待优化的问题
五、上手的建议
总结
引言:架构不是画出来的,是踩出来的
第六篇讲了RAG的7层管线、四类索引、混合检索、Rerank。那些是”业界共识”,教科书上能查到。
这篇讲教科书上查不到的东西:面对具体业务,每一层怎么选、为什么选、选错了怎么办。
我搭的RAG系统服务的是乡村产业监测类的应用:800+政策文档、15900+分块、用户是政府管理人员。他们问的问题五花八门——”山东省2024年产业园申报情况”(精确词项)、”哪些产业发展比较好”(语义模糊)、”帮我做个对比分析”(多步推理)。
没有一种检索策略能覆盖所有场景。架构设计的本质是:在有限资源下,为你的业务场景做最优组合。
这篇文章讲五件事:整体架构怎么从需求倒推、五个关键设计决策怎么做取舍、六个真实踩坑、四个还没解决的问题、给读者的五条建议。每个决策讲三件事:选项有哪些、我选了什么、为什么。
一、整体架构:从需求倒推
业务需求分析
在画架构之前,先搞清楚用户怎么用这个系统:
| 用户行为 | 示例 | 对RAG的要求 |
|---|---|---|
| 精确查询 | “2024年山东省产业园名单” | 关键词匹配必须准 |
| 语义查询 | “哪些产业发展势头好” | 需要语义理解 |
| 表格查询 | “各省产业园数量对比” | 需要结构化数据检索 |
| 多步分析 | “对比山东和浙江的产业差异” | 需要多次检索+综合 |
| 报告生成 | “帮我写一份分析报告” | 需要大量上下文+防幻觉 |
五种行为,对检索的要求完全不同。单一检索通道不可能覆盖。
架构选择
已验证:最终的架构是”5通道检索 + 3级策略 + 防幻觉体系”:
用户提问 │ ▼ 查询分析(意图识别 + 查询改写) │ ├──→ FTS全文检索(BM25,精确词项) ├──→ 向量检索(Embedding,语义匹配) ├──→ 表格检索(结构化数据查询) ├──→ 事实表检索(预构建的事实对) └──→ 文档目录检索(按文档结构定位) │ ▼ RRF融合排序 │ ▼ Rerank精排 │ ▼ 上下文组织(裁剪 + 去重 + 分层) │ ▼ 生成控制(引用溯源 + 语料收缩 + answer_guard) │ ▼ 输出有把握:5通道的设计逻辑是对的——不同类型的查询需要不同的检索方式。
待验证:当文档量增长到2000+时,5通道的检索延迟是否还能保持在可接受范围内(目前800+文档时延迟约200-500ms)。
二、五个关键设计决策
决策1:分块策略——固定 vs 语义 vs 结构
选项:
| 策略 | 做法 | 优势 | 劣势 |
|---|---|---|---|
| 固定长度 | 按500字符切分 | 简单、可控 | 容易在句子中间截断 |
| 语义切片 | 按语义相似度断开 | 语义完整 | 计算量大,阈值敏感 |
| 按文档结构 | 以标题、章节为边界 | 保留文档逻辑 | 单节过长时需二次切分 |
| Parent-Child | 小块检索,大块生成 | 兼顾精度和上下文 | 需要维护两级索引 |
我选了什么:按文档结构切分 + 固定长度二次切分。
为什么:
我的文档主要是政策文件和案例报告,特点是章节结构清晰、段落逻辑完整。按标题切分能保留”一个块≈一个主题”的语义完整性。但有些章节太长(比如一个案例报告可能有3000字),所以超长的章节用固定长度二次切分,chunk_size=500,overlap=50。
没选语义切片的原因:计算成本太高。800+文档、15900+块,逐句向量化再比较相似度,离线索引时间会从分钟级变成小时级。对于政策文档这种结构化程度高的文本,结构切分的效果已经够用。
没选Parent-Child的原因:增加了系统复杂度(两级索引、检索时需要回查Parent),而我的场景中,500字的块已经能提供足够的上下文。如果后续发现上下文不够,再升级到Parent-Child。
踩过的坑:最初用固定长度500字符切分,不看文档结构。结果一个政策文件的”第三条”被切成了两半,前半段在块A,后半段在块B。用户问”第三条是什么”,检索命中了块A,但答案不完整。改成结构切分后解决了。
确定性:结构切分+二次切分的组合,在800+文档的场景下效果验证过。但不同文档类型(比如纯表格、纯文本)可能需要不同策略,还没做对比实验。
决策2:检索通道——为什么需要5通道
背景:大多数RAG教程只讲两种检索——BM25和向量。但我发现两种不够。
用户查询的真实分布:
| 查询类型 | 占比 | 只用BM25 | 只用向量 | 混合检索 |
|---|---|---|---|---|
| 精确词项(地名、年份、政策名) | ~30% | ✅ 能命中 | ❌ 容易漏 | ✅ |
| 语义模糊(”发展好的”) | ~25% | ❌ 命中不了 | ✅ 能命中 | ✅ |
| 表格数据(”各省数量”) | ~20% | ⚠️ 看分词 | ❌ 向量不懂表格 | ⚠️ |
| 事实查询(”谁负责”) | ~15% | ⚠️ | ⚠️ | ⚠️ |
| 结构导航(”第三章”) | ~10% | ❌ | ❌ | ❌ |
后三类查询,BM25+向量的混合检索覆盖不了。所以加了三个专用通道:
- 表格检索:专门处理结构化数据查询。表格在入库时就提取成结构化格式,检索时支持按列名、数值范围查询。
- 事实表检索:预构建”实体-属性-值”三元组。比如”山东省产业园数量→12个”,直接查事实表比从文档里检索快得多、准得多。
- 文档目录检索:按文档结构树定位。用户问”第三章讲了什么”,不需要语义匹配,直接按章节索引找。
已验证:5通道比2通道(BM25+向量)在120个评测查询上的Recall@5从72%提升到89%。
待验证:事实表的维护成本。每新增一批文档,需要重新构建事实表。目前是手动+半自动,还没做到全自动化。
决策3:融合策略——RRF vs加权
选项:
| 策略 | 做法 | 优势 | 劣势 |
|---|---|---|---|
| RRF | 按排名位置融合,score = Σ 1/(k+rank) | 不需要调权重,对不同分数尺度天然兼容 | 丢失绝对分数信息 |
| 加权融合 | 对各通道分数归一化后加权求和 | 保留分数信息,可调权重 | 需要归一化,BM25和向量分数的归一化本身引入误差 |
我选了什么:RRF,k=60。
为什么:
- RRF不需要调权重。5个通道的分数尺度完全不同——BM25的分数是一个体系,向量相似度是另一个体系,表格检索可能返回精确匹配的布尔值。归一化这些异构分数本身就是个难题。
- RRF的工程实现简单,几行代码就够。
- k=60是业界通用经验值,在我的场景下没有调过,效果已经够用。
代价:RRF只有相对排序,没有绝对置信度。也就是说,我知道”这个结果排第一”,但不知道”这个结果有多好”。如果需要设”置信度低于0.3就不返回”的阈值,RRF做不到。
待验证:k=60是否是最优值。理论上应该在评测集上做网格搜索(k=10,20,30,…,100),但还没做。
决策4:上下文组织——TopK直接塞 vs 压缩 vs 分层
问题:检索返回了10个块,每个500字,总共5000字。全部塞给模型?还是压缩后再塞?
选项:
| 策略 | 做法 | 优势 | 劣势 |
|---|---|---|---|
| TopK直接塞 | 取前K个块直接拼接 | 简单 | token消耗大,噪声多 |
| 上下文压缩 | 用LLM提取每个块中和查询相关的部分 | 减少噪声 | 多一次LLM调用,增加延迟 |
| 分层披露 | 先给摘要,需要时再给详情 | 省token | 实现复杂 |
我选了什么:TopK直接塞 + 简单去重。
为什么:在当前800+文档的规模下,简单方案已经够用。上下文压缩需要额外的LLM调用,延迟翻倍;分层披露需要设计摘要策略,工程量大。如果后续文档量增长到万级,再考虑升级。
效果:在800+文档的规模下,Top5(2500字)已经能覆盖大多数查询的上下文需求。token消耗在可接受范围内。
代价:有时候5个块里有2个是重复信息(同一个政策被不同分块包含),浪费了token。简单去重(按文本相似度>0.9过滤)能解决一部分,但不彻底。
待验证:上下文压缩是否能显著提升生成质量。理论上应该做A/B测试——同样的查询,一组用TopK直接塞,一组用压缩后的上下文,比较答案质量。还没做。
决策5:生成控制——防幻觉的三层机制
RAG最大的风险不是检索不到,而是检索到了但模型不用,反而自己编。
三层防幻觉机制:
第一层:Prompt约束
你是一个乡村产业数据分析助手。请严格基于以下检索结果回答问题。 如果检索结果中没有相关信息,请明确说"暂无相关数据"。 回答时请标注信息来源。已验证:Prompt约束能减少60%以上的幻觉。但不是100%——有些情况下模型还是会”创造性地”解读检索结果。
第二层:引用溯源
每个检索结果带元数据(文档名、页码、章节),生成答案时要求模型引用来源。用户可以点进去验证。
已验证:引用溯源本身不减少幻觉,但让幻觉变得可检测——如果引用的来源和答案对不上,用户能发现。
第三层:answer_guard
在答案返回前,用另一个LLM调用做一轮检查:
def answer_guard(question, answer, contexts):"""检查答案是否基于检索结果(简化示例,生产中用异步调用)""" prompt=f"""请判断以下回答是否完全基于提供的检索结果。 如果有任何信息不在检索结果中,请标记为"存在幻觉"。 问题:{question}回答:{answer}检索结果:{contexts}只返回:基于检索 / 存在幻觉 / 部分基于检索"""returnllm.invoke(prompt).content有把握:这个机制能捕获大部分幻觉。但有两个问题:(1)多一次LLM调用,延迟增加;(2)guard模型本身也可能误判。
待验证:guard的准确率。理论上应该用标注数据集测precision/recall,但还没做。
三、踩坑清单
这些是我实际踩过的坑,按严重程度排序。
坑1:分块大小选错了,检索精度直接废了
现象:用户问”2024年产业园申报条件”,返回的5个块里只有1个真正相关,其他4个是噪声。
原因:chunk_size=1000太大。一个1000字的块里可能包含3个不同主题的内容,向量检索命中了其中1个主题,但其他2个主题是噪声。
解决:chunk_size从1000改到500,overlap从100改到50。块小了,语义更纯,检索精度提升。
效果:Recall@5从65%提升到78%。
教训:不要网上抄一个”最佳chunk_size”就用。拿你自己的数据,准备50个真实查询,跑评测。chunk_size的选择和文档类型强相关——政策文档500字合适,FAQ可能200字更好。
坑2:向量检索对精确词项不稳定
现象:用户问”山东省2024年产业集群名单”,向量检索返回的结果里没有”山东”,而是”河北”——因为”山东”和”河北”在向量空间里很近(都是省份)。
原因:Embedding模型对专有名词(地名、年份、编号)的区分度不如关键词检索。”2024”和”2023”在向量空间里的距离可能很近,但在业务上完全不同。
解决:加了BM25全文检索通道。精确词项走BM25,语义查询走向量,两路并行。
效果:精确词项查询的准确率从60%提升到92%。
教训:向量检索不是万能的。它擅长”意思相近”,不擅长”字面精确”。大多数生产场景推荐混合检索。
坑3:Rerank延迟太高,用户体验差
现象:加了Rerank模型后,检索延迟从200ms变成了800ms。用户感觉”卡了一下”。
原因:Rerank是Cross-Encoder模型,需要把查询和每个候选结果一起输入模型计算相关性分数。10个候选就是10次推理。
解决:两个优化——(1)Rerank的输入从Top10减少到Top5(先粗排再精排);(2)Rerank模型从大模型换成小模型(bge-reranker-base),精度损失约3%,延迟减少60%。
效果:总延迟从800ms降到350ms。
教训:Rerank是”精度vs延迟”的trade-off。不是候选越多越好,也不是模型越大越好。找到你的业务能接受的平衡点。
坑4:多轮对话的指代消解
现象:
用户:山东省产业园有哪些? 系统:[返回12个产业园列表]用户:第三个的详细情况呢? 系统:[返回了一堆不相关的内容]原因:”第三个”指代的是上一轮返回的列表中的第三个,但检索模块收到的查询是”第三个的详细情况呢”,没有上下文。
解决:在检索前,用LLM把指代消解成完整问题。把对话历史和当前问题一起传给LLM,让它改写成独立可检索的问题。
def resolve_reference(history, current_query): prompt=f"""对话历史:{history}当前问题:{current_query}请将当前问题改写为一个独立的、不含指代的问题。只返回改写后的问题。"""returnllm.invoke(prompt).content# "第三个的详细情况呢" → "山东省兰陵县国家现代农业产业园的详细情况"有把握:这个方案能解决大部分指代问题。但增加了每次查询的LLM调用次数。
待验证:指代消解的准确率。有些复杂的指代(比如”那个去年说的政策”)可能消解不了。
坑5:评测集没覆盖边界场景
现象:系统在常规查询上效果不错,但遇到边界情况就翻车——比如空查询(用户只发了一个”?”)、超长查询(用户粘贴了一整段话)、多语言查询(中英混杂)。
原因:最初的评测集只有50个”正常”查询,没有覆盖边界场景。
解决:扩充评测集到120个查询,增加了30个边界场景:
- 空查询/极短查询(5个)
- 超长查询(5个)
- 多语言混杂(5个)
- 歧义查询(5个)
- 否定查询(”不包含山东的”)(5个)
- 多意图查询(”对比A和B”)(5个)
教训:评测集的质量决定了你能发现多少问题。50个”正常”查询的评测集,给你一种”效果很好”的错觉。真正的考验在边界场景。
补充一点:评测集本身的代表性也很重要。如果120个查询都是你自己想出来的,可能和真实用户的查询分布有偏差。更好的做法是从线上日志中采样真实查询,按场景分类后构建评测集。每月校准一次,确保评测集覆盖了新出现的查询模式。
坑6:表格数据检索是个大坑
现象:用户问”各省产业园数量排名”,系统返回了一堆文字描述,没有返回表格数据。
原因:表格在文档里是以图片或PDF表格形式存在的,切片后变成了纯文本,丢失了行列结构。向量检索把表格当普通文本处理,自然检索不到结构化信息。
解决:表格单独处理——在文档解析阶段提取表格为结构化格式(DataFrame),单独建索引,检索时支持按列名、数值范围查询。
有把握:这个方向是对的。但表格解析的准确率依赖文档质量——有些PDF的表格解析出来行列错位。
待验证:自动化的表格提取管线。目前部分表格还是手动处理的。
四、还没解决的问题
诚实地说,RAG在业务系统还有几个问题需要优化,跟着业务走很多时候需要优先满足核心需求:
问题1:跨文档推理
用户问”对比山东和浙江的产业发展差异”,需要从多个文档中分别提取两个省的数据,再综合对比。当前系统能分别检索到山东和浙江的信息,但没有”对比”的能力——它会把两个省的信息混在一起返回,而不是结构化对比。
可能的解法:Agent编排——先拆分为两个子查询(”山东产业情况”和”浙江产业情况”),分别检索,再用LLM综合对比。但还没实现。
问题2:实时数据接入
当前RAG的知识库是静态的——文档更新后需要重新索引。如果要接入实时数据(比如最新的产业统计数据),需要一个增量索引管线。
可能的解法:监听文档目录变化,触发增量索引。但需要处理文档版本冲突和索引一致性问题。
问题3:评测体系不完整
有评测集(120个查询),但没有自动化评测管线。每次改了Prompt或检索策略,都是手动跑几个查询看效果,没有系统化的指标对比。
可能的解法:用RAGAS框架搭建自动化评测,核心指标:Faithfulness、Answer Relevancy、Context Precision、Context Recall。但还没落地。
问题4:多模态检索
文档里有图片(地图、流程图、数据图表),当前系统只检索文本,图片被忽略了。用户问”产业园分布图”,系统找不到。
可能的解法:图片单独建索引(用CLIP或其他多模态Embedding),检索时文本和图片并行召回。但增加了系统复杂度。
五、上手的建议
看完这些,如果你正准备搭RAG系统,几条建议:
1. 不要照搬架构,理解取舍逻辑
我的5通道架构是在”800+政策文档、多种查询类型”这个场景下的选择。你的场景可能只需要2通道(BM25+向量就够了),也可能需要更多(比如加GraphRAG处理关系推理)。
关键是:先分析你的用户怎么用系统,再决定需要几条检索通道。
2. 先跑通最简单的,再逐步加通道
第一步:纯向量检索,跑通Demo。 第二步:加BM25混合检索。 第三步:加Rerank。 第四步:按业务需求加专用通道(表格、事实表等)。
每加一个通道,都要有评测数据证明”加了之后效果变好”。不要为了”架构好看”而加。
3. 评测集比架构更重要
一个2通道RAG + 100个高质量评测集,比5通道RAG + 10个随便写的测试问题强100倍。
评测集要覆盖:正常查询、精确词项、语义模糊、边界场景、多轮对话。每种至少10个。
4. 分块策略要拿自己的数据测
不要抄网上的”最佳chunk_size”。拿你自己的文档,准备50个真实查询,对比不同chunk_size的Recall@5。你会发现,最佳值和你的文档类型、查询模式强相关。
5. 防幻觉比检索优化更重要
用户能容忍”没检索到”(系统说”暂无数据”),但不能容忍”检索到了但答案是编的”。Prompt约束 + 引用溯源 + answer_guard,三层机制缺一不可。
6. 场景→方案速查
不确定自己需要几个通道?对号入座:
| 你的场景 | 推荐方案 | 原因 |
|---|---|---|
| 纯FAQ/知识问答 | 纯向量检索 | 查询模式单一,语义匹配够用 |
| 政策/法规文档 | BM25+向量混合检索 | 精确词项多(政策名、年份、编号),推荐加BM25 |
| 有大量表格数据 | 混合检索+表格专用通道 | 表格的行列结构向量检索处理不了 |
| 多轮对话 | 混合检索+指代消解 | “那个”“第三个”这类指代需要消解 |
| 关系推理(”对比A和B”) | 混合检索+Agent编排 | 需要拆分子查询分别检索再综合 |
| 小库(<100个文档) | 长上下文就够了 | 文档少直接塞进模型窗口,不需要检索 |
总结
架构设计的本质是取舍。没有”正确的RAG架构”,只有”适合你业务场景的RAG架构”。每个决策都有代价——5通道比2通道更准但更复杂,RRF比加权融合更简单但丢失绝对分数,TopK直接塞比上下文压缩更快但噪声更多。
踩坑是正常的。分块大小选错、向量检索精确词项漏召回、Rerank延迟太高、多轮对话指代消解——这些问题不是你的架构有问题,是RAG系统天然会遇到的挑战。关键是建立”发现问题→定位原因→验证修复”的闭环。
诚实面对不确定性。我的系统还有跨文档推理、实时数据接入、评测体系、多模态检索四个问题没解决。这不是失败,是工程的常态。知道”什么还没解决”比假装”什么都解决了”更有价值。
评测集是最被低估的武器。花在评测集上的时间,回报率比花在架构优化上高得多。没有评测集,你改了任何东西都不知道是变好还是变差。
欢迎评论区交流~
相关关键词:RAG架构、RAG实战、混合检索、BM25、向量检索、RRF、Rerank、分块策略、防幻觉、RAG评测、生产级RAG