经典RAG方案做到一定规模之后,很多人会陷入一种“检索结果看着没问题,但生成质量就是上不去”的胶着状态。我见过不少团队,向量库、Embedding、Prompt模板都按教程搭好了,Hit Rate明明也不差,可真到了回答复杂问题、跨文档串知识、处理同义概念的时候,答案就是差口气。这其实是撞上了经典RAG的隐形天花板——不是哪里坏了,而是架构本身对知识组织方式的理解太单薄。这一章咱们把高级RAG的技术栈拆开揉碎,聊聊索引、检索、重排序、知识建模和Agent化这五个方向到底是怎么把那些“差口气”给补上的。
先说清楚一个前提:高级RAG不是某一个模型或者某一个工具,而是一整套对检索质量、知识结构化程度和生成控制力的升级思路。它解决的不是“能不能搜到”,而是“能不能搜准、能不能关联、能不能让模型按人类理解知识的方式去用知识”,尤其是知识库数据规模上去之后,这些能力直接决定了项目的天花板。
1. 经典RAG的墙,到底撞在哪
1.1 命中率只是及格线,真正的问题是“语义假匹配”
很多团队评估RAG方案时,眼睛只盯着一个指标——Hit Rate,也就是“正确答案是否出现在检索出的Top-K片段里”。我见过不少项目,Hit Rate能到90%以上,但用户实际问答效果依然拉胯。为什么?因为**“正确内容被召回”和“生成答案使用正确内容”是两回事**。
经典RAG埋了一个隐患:相似度检索天然偏向“语义形状接近”的文本,而不是“逻辑上有用”的文本。举个例子,用户问“公司对员工远程办公的报销政策是什么”,文档里有两段:一段讲差旅报销流程(提到了远程办公四个字),一段讲远程办公的设备补贴细则(没有出现“报销”这个词,但内含费用承担规则)。纯向量检索极可能把第一段排在前面,因为它的向量距离更近,而真正决定答案内容的第二段反而落到后面。这就是语义假匹配。
另一个被低估的问题是分块粒度。固定按512个token切片,容易把一段完整的业务规则拦腰截断,而按标题或段落切,又可能让一个超大表格变成孤立文本。检索是拿到对的东西了,但模型看到的是残缺信息,生成效果自然打折。高级RAG的第一件事,就是对“什么是有用的上下文”重新定义,这涉及索引重构和检索后处理,后面会展开。
1.2 知识割裂:向量检索天然分块,但知识是网状结构
经典RAG的第二个致命伤,是它把文档切成碎片之后,每一条碎片之间的逻辑关系就全部丢失了。向量索引本质上是一个“口袋里的碎纸片”集合,每片纸单独看都讲得通,但你要问一个需要跨三张碎片才能回答的问题,模型就只能靠猜。
举个非常典型的场景。公司知识库里有三份文档:一份是《数据安全管理办法》,一份是《供应商合作协议模板》,一份是《外部系统对接SDK文档》。用户问“外包团队要接入我们的接口,需要签什么安全协议?”。人类知道要把三份文档里的条目关联起来:先看SDK的接入条件,再查供应商协议里的安全条款,最后核对数据安全办法里的合规要求。但经典RAG在向量空间里只能各自召回,模型拿到的上下文里没有“这三条规则从属于同一个业务场景”的线索,于是生成答案要么遗漏关键条件,要么把不相关的条款强行组合。
高级RAG在这一点上的核心思路,不是把文档切得更细,而是引入知识的结构化维度。要么用GraphRAG把实体和关系抽出来建成图,要么用Ontology约束概念的层级和属性,或者至少做Parent-Child Chunk,让“检索时用小片段,喂给模型时带出所属大段落”。知识从线性的碎片,变成可跳转的网,模型才有机会做真正的推理,而不是做文字接龙。
1.3 从离线走向在线的三道坎:延迟、成本、可解释性
任何技术方案只要进了生产环境,工程指标就比模型指标更先刺到你的神经。经典RAG架构在线上的表现通常会遇到三道坎。
第一道坎是延迟。用户发出一个查询,你要经历查询改写(如果做了)、向量化(一次大模型调用)、向量检索、然后是把Top-K片段拼进Prompt再让生成模型跑一遍。理想状况下链路控制在3秒以内没问题,但检索索引一旦上了百万级向量,或者嵌入模型选得偏大,延迟很容易涨到5秒以上。高级RAG里的混合检索、重排序、Cache等技术,很多本质上都是在延迟上讨价还价。
第二道坎是成本。Token调用是RAG最大的隐性成本——不只生成要花钱,检索前如果做查询改写、上下文压缩,每一步都在燃烧Token。有的团队把Top-K设成10,每段400个token,一次回答光上下文就吃了4000token,即使生成模型只用1000token回复,账单也照样记5000token。高级RAG几乎每一项优化,最终都要能换算成Token成本的变化才有意义。
第三道坎是说服力。用户(尤其是企业内部用户)问了一个问题,你给了一个答案,他问“依据在哪”。经典RAG如果只是把向量召回结果塞进Prompt,你根本说不清楚模型为什么优先采纳A片段而不是B片段。可解释性弱,在业务场景里是真会被一票否决的,因为业务方不敢拿着一个无法溯源的结果去决策。高级RAG里的每一个环节——重排序、引用标注、知识图谱路径——都强调让决策链条可追溯。
2. 检索升级:混合检索 + 重排序 + 上下文压缩
2.1 混合检索:向量负责语义,BM25负责精确
很多RAG项目的检索只有一路:把用户问题向量化,然后和文档向量做余弦相似度。这种单路检索在开放域聊天里挺能打,但在垂直领域知识库里必然翻车——因为垂直领域有大量“精确关键词才有效”的内容,比如产品型号、错误码、法律条款编号、化学分子式、人名。
我做过一个真实的维修知识库项目,用户问“EOS 200D 拍照时显示Err 70是什么问题”,向量检索结果里全是“佳能单反相机常见故障”这种泛泛的页面,而精确包含“Err 70”报错代码的维修记录被挤到了第五页。问题就出在Embedding模型对罕见符号、数字短编码的语义理解很弱,它觉得“Err 70”和“相机故障”意思接近,但没法把“精确等于Err 70”这个限定当回事儿。
解决办法就是混合检索(Hybrid Search):一路用向量检索做语义召回,另一路用BM25或全文索引做关键词精确匹配,最后把两路结果合并去重再统一排序。这个方案的性价比极高,因为实现成本很低——开源的Elasticsearch/Meilisearch自带全文检索,向量库(如Qdrant、Weaviate)也多数内置了混合检索能力,你要做的只是调参数。
参数上有一点建议:合并结果时不要简单交替取前N个,常用的做法是**用RRF(Reciprocal Rank Fusion,倒数排名融合)**重新打分,公式是score = Σ 1/(k + rank_i),k是一个平滑常数(经验值60)。这样做的好处是,某一路排名低的结果不会完全被吞掉,而两路都命中的结果排名会被显著抬高。混合检索上线后,我那个维修项目里的Top-5命中率直接涨了约20%,而且用户反馈“回答明显更对味了”。
2.2 重排序:别让向量库一锤定音
从检索库里捞出来Top-50,只能说是“候选”,不能直接送进Prompt。因为向量相似度和“用户这段问答是否真正引用该片段”之间的相关性,通常只有0.6左右的相关系数,鲁棒性不够。这里就轮到重排序(Rerank)发挥作用——用一个专门的交叉编码器模型(Cross-Encoder)对候选片段和查询逐对打分。
为什么交叉编码器更准?因为双塔式嵌入(比如把查询和文档分别编码成向量)为了效率牺牲了交互。查询和文档在编码阶段是互相独立的,最后才用余弦相似度比较,等于两个人各自写完作文,再对对看像不像。而交叉编码器把查询和文档拼成一个序列喂进同一个模型,注意力机制能充分捕捉两者的细粒度交互信号。代价也直观:计算量高,没法提前把文档向量化存库,所以只能对少量候选做重排。这就是为什么标准流程是“双塔粗排 + 交叉精排”。
选择重排序模型有几个实操细节。一是模型规模和速度要平衡,主流选择是bge-reranker-base或bge-reranker-large,Base模型在CPU上处理100条候选大约耗时几百毫秒到1秒,可以接受。二是注意重排序的输入不能太长,通常限制在512个token。三是重排序后有奇效的调参:不要只取重排后的第1名,建议保留Top-3到Top-5,因为有些问题的答案分布于多段,需要给生成模型一些冗余。
我踩过一次坑:在某个金融场景,Embedding模型是中文微调过的,但重排序模型用的是纯英文底座的交叉编码器,中文表现稀烂,重排之后准确率反而比粗排低。后面统一换成了多语言模型,问题才解决。所以记住,重排序模型的训练数据和目标任务语言分布要匹配,别只看榜单分数。
2.3 上下文压缩:给大模型“投喂”前先做减法
检索召回Top-5,每段有几百上千token,直接全塞进Prompt,模型注意力会被大量无关细节分散,而且Token成本爆炸。这就是“上下文过长但信息密度不足”的典型困境。高级RAG在生成前会加一个**上下文压缩(Context Compression)**步骤。
这个环节有两种做法。一种叫提取式压缩,用一个小模型或语言模型对每个片段做“相关性判断 + 关键句抽取”,只保留和查询最相关的句子。另一种叫摘要式压缩,让LLM把多个片段融合成一个更紧凑的段落,再拼进Prompt。实际项目里我建议先提取后摘要:先用较轻量的模型把片段裁到只保留核心句,再把这批清理过的片段交给生成模型。
LangChain里有现成的ContextualCompressionRetriever,可以包装任何检索器,也可以自定义压缩器。但我更愿意分享一个简单的启发式:压缩时尽量保留数字、专有名词、日期、版本号这些硬信息句,因为它们是“答案的锚点”,就算用正则抽也比让模型自由发挥靠谱。做完压缩之后,我常见的效果是生成质量提升、幻觉下降,单次请求Token成本下降30%~50%,这在批量跑批的场景里是相当可观的数字。
3. 索引侧重构:Parent-Child、图与本体
3.1 Parent-Child Chunk:小块检索,大块回答
先抛出经典RAG里最拧巴的一个矛盾——分块大了检索不准,分块小了上下文不够。你切成200token的小段,检索精度确实高,但模型看到的内容太碎,逻辑线接不上;你切成2000token的大段,逻辑是完整了,但向量语义空间被拉得过于宽泛,查询和片段的相关度被稀释,而且塞进Prompt的干扰信息也变多了。
Parent-Child Chunk 的思路是把这个矛盾转移到两个不同层级去解决:检索用Child Chunk(小块,比如256-512 token),喂给模型时自动带出它所属的Parent Chunk(大块,比如整个小节,甚至整个Markdown标题下的内容)。因为小块数量大、粒度细,召回准确;而大块保留了上下文逻辑,模型读起来顺。
实现时我建议用“结构化切块 + 父子映射”的组合:先把文档按层级结构(如Markdown的##、###标题,或PDF的章节标题)切成Parent块,再把每个Parent块进一步切成多个有重叠的Child块。每一条Child向量对应存储一个parent_id元数据,检索时命中的是Child,返回时替换成对应的Parent内容。这样既控制了向量索引的规模,又保住了回答的信息量。
有个容易踩的坑是一个大Parent对应多个Child,检索Top-K时可能选到同一个Parent的多个Child,等于重复信息占掉了多个上下文位置。解决方式是在取Top-K后做一次按parent_id的去重或限额,保证同一个大块最多占一个名额,这样可以用有限的上下文窗口覆盖更多知识来源。
3.2 GraphRAG:把文本变成图,索引不再只是“口袋里的碎纸片”
如果你问一个跨文档关联型问题,前面讲的混合检索再怎么做,都只是在“碎片堆里挑好碎片”,依然是盲人摸象。要真正解决知识割裂,就得从根本上换一种知识组织形态——把文档里的实体(公司、项目、协议、人)和关系(负责、签署、违返)抽出来,建成一张知识图谱。这就是GraphRAG的底层思路。
微软开源的GraphRAG方案让我印象最深的一点,不是它构建图的过程有多酷,而是它定义了一个叫**社区检测(Community Detection)**的步骤:把整个图按关联密度划分成若干个社区,再用LLM为每个社区生成摘要。查询时先定位查询词命中了图里的哪些实体,再顺着这些实体激活对应社区摘要。相当于先看“组织概览图”,再决定要去哪个部门查细节。
这套方案适合什么场景?数据量大且关系密集的知识库,比如企业内部规章制度体系、行业研报库、医疗文献库。但不得不说,构建和更新图的成本是实实在在的——每个文档要做实体抽取、关系抽取、实体消歧(同一个“苹果”到底是公司还是水果),这些都要消耗LLM调用。一次全量建图,上百万字文档烧掉的Token量是向量索引方案的几十倍,而且图结构更新麻烦。
我的建议是:不要一上来就全量GraphRAG,可以先用一个更轻的折中方案——只针对“高频核心主体”建图,比如公司的重要产品线、关键岗位职责、常用流程制度,其余长尾内容继续走向量检索。检索时两者并行,按命中类型加权合并结果。既能缓解知识割裂,又不至于让成本和维护复杂度失控。
3.3 Ontology RAG:给知识装个“骨架”,解决概念对齐问题
GraphRAG解决了“实体有关联”,但没解决“概念有层级、属性和规则”。举个知识库里的常见状况:文档里一会儿说“客户”,一会儿说“甲方”,一会儿说“合作伙伴”,它们在不同上下文里语义有重叠但不等同。向量检索根本分不清这些概念在业务体系里的位置,而人可以凭业务常识知道“甲方”和“乙方”是合同关系,客户和合作伙伴是不同层级的概念。
**Ontology RAG(本体增强RAG)**的做法,是预先定义一份领域本体(Ontology),里面写明:有哪些概念类(Class)、概念之间有哪种关系(Object Property)、每个概念有哪些属性(Data Property),再加一点业务规则(比如“一个合同必须有且仅有一个甲方的企业主体”)。然后把这个本体以两种方式融入RAG流程:一种是在索引阶段用它做语义标注,让知识图谱里的边更精确;另一种是在查询阶段把本体片段和检索结果一起送进Prompt,让模型“按规矩”回答。
OpenAI官方文档里推荐过一个做法叫“提示词中的本体”,就是直接把本体定义写进System Prompt,例如:“你是企业知识问答助手,本系统中的数据安全等级定义为:L1公开 / L2内部 / L3机密 / L4绝密,所有回答中涉及密级判定时,必须严格对应此分级。”效果立竿见影,因为很多RAG幻觉不是不知道事实,而是没理解术语在本系统内的精确含义。
Ontology RAG在本地知识库落地时的痛点也很实在:本体建模是个纯人工活,要让业务专家、技术负责人坐在一起把词表、树状分类、属性约束定义出来,跑一次可能需要一个月。我见过比较务实的做法是先跑100条高频问题,从问题里反推本体结构,建成个“8成够用”的版本,先跑起来,后续再迭代。别试图一开始就把本体做成行业标准级别,那种方案往往是“建得很漂亮,半年推不上线”。
4. Agentic RAG:从“搜索一遍”到“检索规划”
4.1 Agentic RAG的本质:让模型决定“下一步搜什么”
传统RAG流程是固定流水线:查询来了,直接检索,直接生成,一步到位。Agentic RAG把这个流程从“直线”改成了“决策循环”——一个Agent先理解用户的模糊意图,然后自己规划要搜几轮、分别搜什么、是否需要基于第一轮结果改变方向。
举个例子,用户问“我们上季度在华东区的销售到底达标没有,如果没有,原因可能出在哪里?”。经典RAG会把它当成一次查询去检索,结果只会搜到一堆“华东区销售数据”曝光率高的文档,答得最可能的形态是“销售额下滑,原因详见报告”。而Agentic RAG会先拆出子任务:第一轮查“华东区上季度销售额”,拿到数值后判断是否达标,未达标再生成第二轮查询,例如“华东区Q2渠道政策变化”和“华东区大客户流失情况”,最后把多轮证据拼出链条式的回答。
这种能力的底层是ReAct(Reasoning + Acting)范式:Agent在每次检索前,先输出一段思考(Reaxoning),说明当前已知什么、还缺什么、因此要查什么。思考文本不仅让Agent的搜索路径有据可循,回溯排查时也能看清它为什么调用了某些工具。工程实现上,一般借助LangChain的create_react_agent或LlamaIndex的AgentWorkflow来编排,检索工具则封装成Tool供Agent调用。
4.2 一个最小可落地的Agentic RAG流程
我不建议新手直接上复杂的多Agent框架,可以先用一个最朴素的ReAct循环跑通,再逐步加功能。最小版本长这样:
- 用一个函数调用(Function Calling)型大模型作为核心,定义两个工具:
search_knowledge_base(query)(向量检索)和search_document_by_metadata(filters)(按元数据过滤,比如“公司名=X”,查指定文档)。 - 用户提问后,模型先输出意图判断:是否需要拆解、是否需要多轮检索、是否需要限定数据范围。
- 进入循环:思考 → 调用工具 → 得到观察结果 → 再思考,直到Agent认为证据链足够,或者达到最大轮数(我给的建议是上限3轮,太多轮既烧Token又容易跑偏)。
- 最终把整个思考过程和检索到的片段拼进一个完整Prompt,生成最终回答。
这个流程在跑“需要跨文档/跨条件组合”的问题上优势明显,但也要接受它的代价:一次回答的延迟可能从2秒涨到8秒以上,Token消耗翻倍甚至翻三倍。我不建议把所有查询都走Agentic流程,可以先做一层路由:简单事实性问题走轻量RAG快速链路,复杂推理型问题走Agentic链路。路由判断本身也可以交给一个小模型,这种“分级处理”在生产里是控制成本的关键。
4.3 Agentic RAG的代价与适用边界
聊到成本与适用性,我自己的感受是,Agentic RAG是那种“性能上限高但下限也低”的方案——做得好体验惊艳,做得糙会变成灾难。最典型的翻车场景是Agent在检索循环里反复搜出相似结果却不知道终止,最终把自己绕进死循环,或者连续调用工具导致Token账单爆炸。
一个深坑是工具调用返回的结果格式不稳定。比如你的知识库检索工具返回的是JSON数组,里面某些字段为空,Agent可能把空字符串当成“没有找到”,于是一直生成无效的子查询,白白浪费几轮调用。解决办法是给工具定义严格的返回Schema,并且在工具描述里写清楚“当结果为空时,直接返回固定标记NO_RESULT,不要再尝试生成更多查询”。这类细节写进工具描述后,Agent的失败率会明显下降。
另一个边界是在可用性要求极高的场景,Agent的推理链不可控反而让人庆幸“还好没上”。比如后台系统的工单字段问答,用户只想快速知道“这张工单是谁提交的”,根本不需要Agent规划三轮去查。所以我的边界判断是:具备模糊意图、需要多源比对、信息互相交叉的问题才适合Agentic RAG;否则,干脆用固定流程,省钱又稳定。
5. 工程落地:本地知识库与全链路评估
5.1 零基础也能复现:Ollama + Embedding + 向量库
高级RAG听着唬人,但本地跑一套最小可用的流程并不难,硬件门槛也没想象中高。我以“零基础可复制”的方式推荐一套组合:Ollama负责本地大模型推理,nomic-embed-text或bge-m3做嵌入,向量库用Chroma/Faiss先顶着,检索和重排序逻辑用LangChain/LlamaIndex搭起来。
落地细节如下:
- 安装Ollama后拉取模型:
ollama pull qwen2.5:7b(生成用)+ollama pull nomic-embed-text(嵌入用)。这套组合在16GB内存的Mac或普通单卡GPU机器上就能跑。 - 用
LangChain里的OllamaEmbeddings接嵌入模型,Chroma.from_documents把切好的Chunk入库。 - 检索阶段用
ensemble(BM25 + 向量融合)实现混合检索,配合ContextualCompressionRetriever做上下文压缩。 - 如果想加重排序,本地可以用
BAAI/bge-reranker-base,通过FlagEmbedding库加载,或者用Ollama兼容接口调用跨编码器模型。
我第一次跑这套流程时碰到的“第一个坎”是中文Embedding效果不如预期。后来发现根因是nomic-embed-text对中文的支持确实一般,换成bge-m3之后就通达了不少。另外本地小模型(7B级别)的指令遵循能力弱,Prompt里要尽量把“你只需要依据上下文回答,不要自行补充”写清楚,否则它背景知识一多,就容易“放飞自我”生成“表面上合理但库里根本没有”的内容。
5.2 别只用Hit Rate,评估体系要覆盖生成侧
很多团队评估高级RAG升级有没有效果时,还在只看Hit Rate,这是远远不够的。因为高级RAG动的不只是召回,还有重排序、上下文压缩和知识结构化——这些步骤里,任何一个出现问题,影响都会传导到最终生成质量。我建议至少关注三个维度:
- 检索质量:Hit Rate(Top-K内是否包含标准答案)、MRR(正确答案排第几)、NDCG(排序质量)。这些指标只看检索环节对不对。
- 生成质量:Faithfulness(生成内容是否忠于上下文,有没有无中生有)、Answer Relevance(回答是否回答了用户的问题、有没有答非所问)。这两个指标是RAG幻觉的“照妖镜”。
- 引用准确度:回答里引用的来源片段是否真的支撑了对应句子。这个指标在企业知识问答里至关重要,没有它,用户不敢盲信结果。
实操上有两个评价工具比较好用:RAGAS(开源框架,通过LLM自动评估检索与生成质量)和TruLens,其中RAGAS的faithfulness和answer_relevance两个指标,可以用很轻量的方式跑起来。这里涉及一个注意点:LLM-as-a-Judge本身也有偏差,建议评估模型用更聪明的模型(比如GPT-4级别,或至少是本地70B级别),用7B模型去Judge另一个7B模型的回答,基本等于“让运动员当自己的裁判”,误差大到没法参考。
5.3 LangChain4j Easy RAG:Java生态里的一条捷径
如果团队技术栈是Java,不想在Python里另起炉灶,LangChain4j是个很自然的选择。它的Easy RAG模式基本上把“文档切块→嵌入→入库→检索→生成”封装成了声明式配置,能在几分钟内跑出一个最小可用的RAG服务。
大致流程是:引入langchain4j-easy-rag依赖,配置好DocumentSplitter(比如按段落切分)、EmbeddingModel(可以接Ollama或OpenAI兼容接口)、ContentRetriever,然后创建AiServices就完成了。它还内置了“引用来源”的追踪能力,对需要展示依据的企业项目非常友好。
Java生态做RAG的另一个优势是能直接复用已有的Spring Boot基础设施和运维体系。我在一个老旧的政务系统里接RAG时,团队根本无法在服务器上跑Python环境,最后就是靠LangChain4j直接把RAG功能嵌进了原有的Java服务里。不过也要提醒一句,Java生态的RAG组件版本迭代速度比Python慢,一些高级特性(Agentic流程、GraphRAG)支持没那么顺手,如果目标是深度定制,Java只适合先跑垂直应用,核心算法还是要回Python侧验证。
6. 常见问题与排查技巧实录
6.1 检索结果一团糟,从哪几个环节排查
面对“检索结果不相关”这种老问题,我给你整理一条排查顺序,按性价比从高到低来:
- 查分块策略:如果每个Chunk超过1000token且切分不考虑语义边界,优先重写切分逻辑,改成语义段落切分 + Parent-Child结构。这一步带来的提升往往最显著。
- 查查询与文档的语言是否匹配:Embedding模型是多语言还是单语言?文档是中文还是英文混合?错误地混用模型,命中率会跌一半不止。
- 查检索方式:如果只有向量检索,先加一路BM25关键词检索做融合,再用RRF合并结果。这个改动成本低,收益明显。
- 查Top-K设置:Top-K太小可能漏掉正确答案,太大则引入噪声。经验值从4到8之间做AB测试,而不是跟着教程抄。
- 查重排序器:粗排质量不足时,交叉编码器重排能把“对但排得靠后”的候选捞回来,但前提是模型选对。
6.2 知识库能存图片吗?多模态的坑
很多团队问“RAG知识库能不能存图片”。理论上可以,但要分清楚是哪种存法。第一种是把图片当成元数据挂载到文档里,比如“这份PDF第三页有架构图”,检索时给模型返回一句对图片的文字描述——这不算真正的多模态RAG。第二种是真正把图片本身纳入检索,比如用户问“去年架构图中负载均衡在哪个层”,系统要去图片里找答案,这就必须有**多模态Embedding + 视觉语言模型(VLM)**参与。
实际工程落地时,第二种方案的代价比大多数人预想得高:一个视觉Embedding模型(如CLIP类)并不能理解图表里的业务关系,它只能判“图片画面相似”;而真要让模型读懂一张架构图,得把图片喂给Qwen-VL这类视觉语言模型来生成结构化解说,再对这个解说文本做索引和检索。我见过的可行做法是:图片进知识库前,先让VLM把图片转成结构化Markdown描述(哪些节点、哪些连线、哪些数据标注),再把这段Markdown作为文档块进入常规索引。这个方案把“多模态RAG”转化成“图文理解 + 文本检索”,成本和稳定性都更可控。
6.3 回答自信但错误——幻觉怎么压
这是RAG上线后用户抱怨最高频的问题:模型明明拿到了上下文,却依然给出上下文里没有的细节。压幻觉有四个在实操中确认有效的技巧:
- 写死指令约束:Prompt里明说“如果上下文中没有相关信息,直接回答‘知识库中未找到相关内容’,禁止编造”。这一步能立竿见影地减少幻觉。
- 提高生成模型对上下文的敏感度:把“参考依据”与“回答主体”显式分开,比如要求模型先列出依据编号,再围绕依据编号生成回答。不直接给依据,模型就倾向于用参数记忆补全。
- 限制回答边界:告诉模型只能引用提供的片段,不能使用自身预训练知识。本地小模型对这条的执行力弱一些,可以让检索器在输出片段前先做一轮硬性相关性过滤(比如相似度低于阈值直接丢弃)。
- 开启引用溯源:强制模型逐句标注引用来源(Source-A),应用层再校验这个来源确实存在且有对应文本。企业场景下,没有这一步,RAG很难获得用户信任。
6.4 性能账单爆表,先看这三个地方
RAG项目跑着跑着Token账单暴涨,几乎都是这三类原因:一是没做查询缓存,相同或高度相似的问题反复调用嵌入和生成模型,浪费严重。用语义缓存(对查询向量做相似匹配,命中就直接返回历史答案)能砍掉三成以上的重复调用。二是上下文太长,Top-K设置过大或没做压缩,一次问答吃掉几千Token。建议引入上下文压缩,配合“只保留关键句”的硬过滤。三是过度Agent化,所有问题都让Agent多轮规划,导致工具调用和思考本身消耗大量Token。把问题分流做好——简单问答走直通链路,只有复杂推理才进Agent——是最省钱的结构性优化。
7. 写在最后:从工具链回归业务本身
顺着这一章的思路走下来,你会发现高级RAG并不是一个“银弹模型”或“神仙框架”,它更像一条“检索质量螺旋上升”的实践路径:先混合检索把Recall拉满,再重排序把Precision做高,随后用压缩和重构解决上下文质量,最后才是用图和Agent解决跨文档推理。这每一步都要回归到业务场景里做取舍。我个人试过很多组合之后最大的体会是:先想清楚用户问的问题到底长什么样,再决定要不要上GraphRAG或者Agent。盲目堆高级特性,最后大概率是成本翻倍、效果原地踏步。先跑通基础链路,用评估数据说话,再往上一层层升级架构,这才是稳的路子。