ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

RAG智能问答效果优化实战:检索、提示词、工具三管齐下

RAG智能问答效果优化实战:检索、提示词、工具三管齐下 我对“超体”这个项目代号很有感情。它是我参与搭建的一个企业内部智能问答系统——把产品文档、历史工单、FAQ、技术公告全部收进知识库用户以自然语言提问系统直接给出有依据的答案。前六篇系列文章聊了架构、数据管道、部署这些“从0到1”的事这一篇我们不得不说最难啃的部分效果优化。说实话模型接口调通只需要一个下午但要把回答质量从“偶尔靠谱”打磨到“稳定可靠”靠的是检索、提示词、工具这三个环节一起发力。我把它叫“三管齐下”不是修辞是真的同一时间需要动三条线。这篇就把我们最近这一轮完整的优化过程复盘出来适合正在做RAG应用、智能客服、知识库问答的开发者参考。1. 整体思路拆解为什么必须三管齐下做AI应用的同学应该都有过这种经历模型回答不准第一反应是改提示词改来改去发现效果时好时坏又怀疑是模型不行换个更强的模型结果还是答非所问最后才醒悟过来问题可能出在喂给模型的内容压根不对。我们早期就吃过这种亏。团队里几个人围着提示词调了两个星期把系统提示词写得像法律条文那么严密但回答质量提升仍然有限。后来做了一次完整的问题归因分析把线上回答错误的样本一个个拉出来看发现真正的根因分散得很开有的是知识库压根没召回相关文档有的是检索到了但排序不对有的是模型没理解输出格式要求还有的是需要调外部工具才能回答的问题但模型根本不会调工具。这次分析之后我认识到一个问题对于知识问答类应用效果优化天然由三个维度构成维度解决的问题侧重点检索质量模型能否拿到正确资料召回率、排序准确性、分块合理性提示词设计模型能否正确使用资料指令清晰度、约束强度、输出格式工具调用模型能否完成系统之外的动作工具定义质量、调度逻辑、容错机制这三个维度其实是层层递进的关系。检索是粮草提示词是战术工具是装备。粮草没送到战术再精妙也只能瞎比划粮草送到了但战术混乱拿到正确材料也讲不出正确答案而工具调用则决定了系统能不能从“只会说”进化到“能做事”。我建议每个遇到效果问题的团队都先做一次归因分析不要急着改东西。把出错样本分三类是检索不到是指令理解偏了还是功能上需要工具分类清楚再动手效率会高很多。后面几个章节我详细说我们在每个维度做了什么、踩了什么坑。2. 检索优化从“找得到”到“找得准”检索是RAG系统的地基。模型再聪明如果知识库没有把该用的资料送上来一切都是白搭。我们围绕检索做了四件事混合检索、分块策略调整、重排链路、查询改写。2.1 混合检索选型向量检索和关键词检索必须配合做知识库问答的时候很多人以为向量检索就够了这是个常见误区。向量检索擅长捕捉语义相似比如用户问“电脑开不了机怎么办”知识库里有一条“设备无法正常启动的排查步骤”语义上确实相近向量能匹配上。但用户还常常直接用产品名称、型号、错误码这些精确词提问比如问“EB-502错误怎么解决”这时候关键词检索的精确匹配往往比向量更可靠。我们的做法是同时跑两条召回链路向量召回和BM25关键词召回然后把两路结果合并。早期版本只用了向量召回结果就是很多含具体型号、编号的问题明明知识库里有答案系统却说“未找到相关信息”。后来加上关键词召回这类问题的召回率肉眼可见地提升。两路结果合并之后排序就变成了一个问题。可以直接按分数加权也可以把合并后的结果交给重排模型。我们当时的做法是向量和BM25各自取Top 20合并去重然后统一进入重排环节。2.2 分块策略过大过小都会出问题分块是我认为检索优化里最容易被低估的环节。很多团队把文档随便切一切就扔进知识库我建议拿回答错误样本倒推检查分块是否合理。我们测试过三种分块大小256 token、512 token、1024 tokenoverlap取50到100不等。结论是过大的分块1024 token以上虽然上下文信息完整但噪音太多检索时容易因为一块中包含太多无关内容而拉低相关性分数过小的分块256以下则容易把一句话的逻辑截断比如表格、代码块被切得七零八落。最终我们采用了一种半结构化的方式先按文档本身的章节标题切分再对切出来的长段落做二次分块每个块控制在350到500 token之间。同时保留元数据比如文件名、章节路径、页码这些信息在后续重排和引用展示中非常有用。另一个经验表格类内容必须特殊处理。把表格原样塞进向量库检索效果很差。我们把表格转成“键值对描述文本”比如“型号X300最大负载500公斤电源AC 220V”这样语义向量才能有效编码。处理之后关于设备参数类问题的准确率提升非常明显。2.3 重排Rerank打通“最后一公里”召回阶段拿回来的Top 20结果相关性排序是粗糙的。向量分数高不等于真的匹配用户问题。我们接了一个重排模型对Top 20结果重新打分再取前5到8条作为上下文送入模型。这一步是投入产出比最高的一个改动。举个实际例子用户问“设备在低温环境下能不能正常工作”向量召回的第一名是一条关于设备工作湿度范围的文档相关但不完全对题重排模型把关于温度适应性的文档提到最前面最终回答质量立刻不一样了。重排模型通常比嵌入模型的区分能力更强它能更好地捕捉“问题与文档之间到底是不是真的对口”这种关系。当然重排也有成本。多一层模型调用时长和费用都会增加。我们的处理是只在Top 20结果上做重排控制候选规模另外离线把重排模型量化部署线上延迟增加控制在可接受范围内。2.4 查询改写让检索理解用户的真实意图用户提问题不一定规范有的人说“怎么登录”有的人说“我去登不进去了”还有的人一句话里糅杂了好几个问题。直接拿原始问题去检索效果经常不理想。我们加了一个查询改写层在检索之前先让模型对用户问题做一次轻量加工。加工动作包括这几类。第一是意图归一化比如把“你们这个怎么用”改写成“XX产品功能使用说明”。第二是拆解复合问题比如“怎么登录和改密码”拆成两个子查询分别检索再合并结果。第三是补充业务词比如把“那个东西坏了”补充成“设备故障报修流程”这需要结合产品上下文。查询改写还有一个隐秘的好处能帮助处理同义词和口语化表达。关于改写模型我们用的是轻量模型在一次搜索链路里增加几十毫秒延迟但检索准确率提升很明显。如果不用查询改写就必须在检索端建同义词表和别名库维护成本更高。3. 提示词工程把“会说话”变成“说准确的话”检索优化之后模型拿到的资料基本是靠谱的了下一个瓶颈就在提示词。很多团队对提示词的理解停留在“把要求写详细一点”这不够。提示词需要像软件一样结构化设计、版本管理、回归测试。3.1 系统提示词的结构化设计我们的系统提示词经历了从“一段话”到“一个模块化模板”的演进。最早的提示词是一大段自然语言描述后来发现改起来非常痛苦今天加一个格式要求明天加一个语气要求全挤在一段话里模型很容易忽略细节也很容易把不同要求搞混。重构之后我们把系统提示词拆成四个模块角色定义明确系统是什么身份面对什么用户群体。任务说明告诉模型在本次交互中要完成什么任务。输出约束给出硬性要求比如不能编造、必须引用参考内容、不知道就直说。输出格式定义答案的JSON结构或文本样式。每个模块之间用明确的标记分隔。下面是我们模板的简化版你是某产品知识库的智能助手面向终端用户解答产品使用与故障排查问题。 任务 1. 基于以下参考内容回答问题。 2. 如果参考内容不足以作答明确回复“当前知识库中暂无相关信息”。 硬性约束 - 不得编造参考内容中没有的事实。 - 回答必须包含至少一条参考内容来源编号。 - 语气简洁专业不使用网络流行语。 参考内容 [1]内容... [2]内容... 用户问题 用户输入...纯文本模板会导致输出格式变化为了稳定解析我们最终把输出部分改成了JSON格式用类似的指令让模型固定输出answer和citations两个字段。这样下游就可以直接用结构来对接不用再猜模型输出的边界。结构化改造之后上游应用侧的解析故障基本清零。3.2 事实清单法一根降低幻觉的强心针这是我这轮优化里最推荐的一个技巧。与其让模型直接生成回答不如让它先提取“支持性事实”再基于这些事实组织回答。具体做法是在提示词里要求模型第一步先阅读参考内容列出所有与问题相关的事实条目每条事实必须带上来源编号第二步基于列出来的事实完成最终回答。这样强制模型在生成答案之前先做一轮“证据确认”。从心理学角度说这个技巧的本质是让模型先建立一块“工作记忆板”后续生成时会围绕这些事实来组织语言而不是漫无边际地发挥。我们做了AB对比测试未使用事实清单前虚构内容占错误样本比例挺高的使用后这部分错误明显降低。代价就是响应稍微变慢一点多了一次中间推理但正确率提升了这个代价是值得的。3.3 参数调优温度、TopP、最大长度都有讲究提示词不只是文字采样参数也归属于这个维度。不同场景对随机性的需求不一样。问答场景我们用的温度是0.2TopP是0.6左右这能保持回答的稳定性同时不至于把话说得太死。如果是头脑风暴类工具温度可以开到0.8甚至1.0。但知识库问答一定不要追求“花哨”稳定准确是第一位的。最大长度也值得关注。如果设置太短模型可能答到一半就截断了太长又增加延迟。我们是根据历史回答的平均长度来设定的留了50%的冗余同时配合“回答尽量精简”的指令把大部分回答控制在400字以内。另外一个细节是不要在提示词里堆砌“非常重要”“一定要”这类程度副词。模型对这些词的敏感度并不稳定过度使用反而降低指令的系统性权重。用明确的硬性约束词和编号条目比情绪化强调管用得多。3.4 提示词的版本管理与回归测试提示词和代码一样需要版本管理。我们一开始用文本文件直接改后来经常出现“这个版本效果好但改不回去了”的尴尬情况。后来我们把提示词纳入统一配置中心每个模板都有版本号、修改人和变更记录。更重要的是维护一套回归测试集。我们整理了一批覆盖不同难度的测试问题包括简单检索题、复合推理题、拒答类问题、需要工具联动的问题总共60多条。每次修改提示词先跑一遍回归测试集对比答案质量评分再决定是否上线。虽然不能保证发现所有问题但能拦住大部分明显退化。在实际操作中我们还会把回归测试的评分做成一个简单的看板记录每次改动的得分变化这样一段时间后翻看记录能够判断哪些提示词改动是真正有效的。4. 工具调用让模型从“会说”进阶到“能做事”检索和提示词解决的是“怎么把话说对”但很多真实用户需求不只是问答——他们需要系统查出订单状态、查询实时数据、触发流程工单。这类问题必须靠工具调用解决。我们在工具调用上的优化踩的坑也不少核心是四项工具定义质量、容错重试、多工具编排、可观测性。4.1 Function Calling的工具定义参数描述才是灵魂大模型平台都支持函数调用定义看起来很简单一个函数名、一段描述、几个参数。但实际效果差异非常大关键在于描述写得够不够细。我们最早的工具定义很粗糙比如查订单工具就写“根据订单号查询订单详情”模型经常传错参数格式。后来我们参考了一个很朴素的原则把工具描述当作写在操作手册里的说明来写参数必须包括类型、取值范围、示例值。比如订单号参数明确写“18位数字字符串示例202408280001234567”模型传参的准确率立刻上去了。下面是我推荐的一种工具定义结构{ name: query_order_status, description: 根据用户提供的订单号查询订单当前状态。仅在用户明确给出订单号时调用订单号通常是18位数字串。, parameters: { type: object, properties: { order_id: { type: string, description: 订单号18位数字例如202408280001234567, minLength: 18, maxLength: 18 } }, required: [order_id] } }还有一个细节如果你的模型服务商支持工具调用的“强制禁用”开关一定要在非必要场景里关掉工具调用否则模型有时候会自作主张发明工具参数。我们有过一次模型虚构了一张未存在的电子券编码去调验证接口的教训后来加了参数校验和强制校验开关才堵住漏洞。4.2 工具调用结果的解析与容错重试机制工具调用是模型行为不是程序代码它天生有不确定性。所以要按“可能会出错”来设计整套流程。我们设计了三层防线。第一层是参数前置校验在发给工具之前先用代码校验参数格式不符合预期就直接返回“参数缺失或格式错误”不走工具逻辑。第二层是工具返回结果的统一包装解析不管下游系统返回什么格式都转成统一JSON结构防止后续提示词拼接时报错。第三层是重试机制如果工具返回错误或模型第一轮产生幻觉我们允许整个链路重试一次第二次调用时把第一次的错误信息作为反馈传给模型。举一个真实场景用户问“帮我查一下订单7123到哪了”模型第一次调用工具时把订单号识别成“7123”前置校验发现长度不足系统将错误信息反馈给模型模型重新提取出完整订单号“7123889900123456”第二次调用成功。如果没有这个重试机制用户得到的就是一次失败的答复。容错重试整个流程成本不高但对用户体验改善相当大。4.3 多工具编排不要让模型一张嘴同时调十个工具当系统里有十几个工具时模型经常出现选择困难。特别是同时收到“查天气”和“查温度”这种功能近似的工具时模型可能会选错或者干脆把上一个工具的结果拿来胡答。我们做了一次工具数量收敛和归类。把底层相似的API合并成一个大工具比如“查询业务数据”统一收口内部再做路由分发。同时引入一个意图分类前置工具先让模型判断用户的问题属于哪一类场景再只暴露这一场景下需要的候选工具这样大大减少了误选概率。还有一种情况一个用户问题需要连续调用多个工具比如“我家设备离线了帮我查一下设备信息并且重新下发指令”需要先查设备列表再调用下发指令工具。这种多跳工具调用我们用了一个简单的编排逻辑在系统层定义一个极简的状态机仅允许“查询类工具”成功后才可调用“操作类工具”并且操作类工具必须引用查询结果中的真实设备ID。千万别让模型自由发挥。4.4 日志与全链路追踪没有观测就没有优化工具调用链路比纯问答复杂得多没有日志和追踪基本等同于抓瞎。我们把一次完整的会话请求串起来加上一个request_id从用户问题、检索结果、重排结果、模型提示词、工具调用参数到工具返回结果全部写入结构化的追踪日志里。这个日志的价值在于每次线上回答质量出问题我们都能按request_id把整条链路拉出来逐段排查快速判断是检索错了、提示词没约束住还是工具侧返回了脏数据。我们甚至把日志做成一张简单的看板能看到工具调用的成功率、平均耗时、最常见的失败原因用数据驱动下一步优化。5. 常见问题与排查技巧实录结合我们这一轮实战整理了5类出现频率最高的问题及排查思路。问题现象可能根因建议排查手段模型回答“知识库中无信息”但知识库里明明有检索召回失败向量与关键词都没命中检查分块大小、检索TopK数量打印召回结果逐条看相关性模型答非所问内容跟问题不完全搭边检索到了相关文档但重排未把真正对题的排到前面人工核对Top 5里是否存在正确文档检查重排模型是否生效同一个问题多次问答案不一致温度过高或上下文构造不稳定降温度到0.2以内检查检索结果是否每次稳定模型编造工具参数调用错误工具工具描述不清晰、参数约束缺失细化工具描述和参数示例加入参数前置校验工具调用成功但用户仍不满意工具返回结构过于复杂模型没能正确解析统一工具返回格式返回结果里只保留模型需要的字段先说第一个问题这也是最迷惑人的。一次用户问“怎么申请发票”知识库里有明确的发票申请流程文档但系统说查不到。拉日志一看发现文档被分块之后发票流程被切碎在两段内容里单看任何一段都不算和“申请发票”高度相关两条的向量分数都偏低重排后也没进Top 5。最后把那个文档改成按章节标题做结构化分块问题就消失了。这个案例告诉我们知识库文档的分块质量直接影响检索结果不能只调参数。第二个排查技巧是“看Top 5”。我觉得排查RAG类问题最有效的手段之一就是直接看检索链路返回的Top 5内容到底是什么。如果Top 5里压根没有正确答案那是召回问题如果正确答案在Top 5里但重排后掉出去了那是重排问题。这招能把问题定位范围缩小一大半。第三个问题是版本灰度。修改提示词或检索参数后不能全量上线直接改最好先让新配置只作用于部分流量。我们用了一个简单的AB分流方案线上保留新旧两个配置版本各跑一部分流量观察一两天的用户反馈和错误率再决定是否全量切换。6. 关于效果优化的几点体会这一轮实践下来我最大的体会是效果优化没有银弹必须系统化推进。只调提示词、只加工具、只优化检索都不能覆盖所有问题类型。三个维度是互相咬合的齿轮缺一个整体转不动只有一个转得再快也没用。另外建议每个团队都沉淀一套“效果评测集”。不要等到线上出问题才临时找几个问题测试平时就要把典型问题、边界问题、易错问题积累起来每次改动自动回归。没有评测集的优化基本是在碰运气。最后分享一个小技巧优化过程中每个改动最好只改一个变量。我见过一些团队一次改了分块、重排、提示词三个地方结果效果变好了但根本说不清是哪个改动起了作用。我们后来严格自律一次只改一个维度跑完回归看效果再改下一个。虽然慢一点但每一步都走得明白积累的经验也能沉淀下来复用。下一轮我们打算在这套体系上加更多画像类的问答策略、多轮对话记忆优化。这次先写到这里如果你也在做类似系统希望这些踩坑记录能帮你少走一些弯路。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进