简介:2024年智能体(Agent)与检索增强生成(RAG)融合应用的专题PDF文档,面向具备一定技术基础的研发人员与技术管理者,系统展现大模型在游戏娱乐、金融科技、语音助手、办公支持等前沿领域的落地探索。资源为单个PDF文件,共146页,压缩包大小12.43MB,目前已有544人学习浏览。内容汇集八大实际案例:网易伏羲实时语音交互AI队友、蚂蚁集团泛金融多智能体应用、ModelScope开源框架加速智能体开发、小米语音助手中的复杂任务处理、RAG在办公领域解决幻觉与信息老旧问题、Elasticsearch 8落地实践、西门子通用智能助理,以及阿里云PAI大语言模型微调训练。各案例既呈现多智能体协作与决策方式的深层改变,也剖析了RAG在数据检索、信息整合与安全性方面的优势,并附有技术实现讲解与业务背景分析。整份资料基于真实生产经验,能帮助开发者快速掌握Agent与RAG结合大模型的方法论,理解前沿趋势,为研发与创新提供有力参考。
1. Agent+RAG 是什么:三个要素,谁能直接上手
2024 年之后的 Agent+RAG,不再等于“一个向量库加一段提示词”。RAG 解决的是“让大模型基于自有资料回答”,Agent 解决的是“让大模型按步骤完成几件事”,二者组合起来才是完整形态。标题里这份 146 页的资料,名义上是八个案例,实际上是在反复演示同一个融合骨架:路由谁来做、检索谁来做、生成谁来做、不满意之后回不回头。这篇笔记就把这四件事拆开,给出一套可以跑通的最小链路、八类案例的共性参数表,以及五条会翻车的真问题。适合那些已有 RAG 基础、想把手上的知识库问答升级为可编排任务的人,不适合还停留在“向量检索结果直接拼进提示词”阶段的读者。
2. RAG 与 Agent 的分工线:为什么增强生成不负责判断
2.1 单轮 RAG 的作用边界:能提供证据,不负责决策
RAG 的完整链路是文档解析、切片、embedding、向量检索、拼装上下文、交给大模型生成。它擅长的是“给定一个明确问题,从库里找出相关片段,再回答”。这句话拆开看,有两个隐性前提:问题本身已经明确,库里有现成答案的片段。一旦问题需要拆解,例如“对比 A 和 B 的条款差异,给出一版费用说明”,单轮检索往往只拿到 A 的片段或 B 的片段,生成结果就会各说各话。这不是召回质量差,而是链路结构不支持多步求证。
很多团队在 2024 年初踩过同一个坑:觉得命中率低就换 embedding 模型,换成 bge-m3,加上重排,命中率上去了,端到端的效果还是不行。原因是 RAG 的召回目标由 Agent 层决定,问题没被拆成合适的检索子问题,召回再准也答不到点子上。单轮 RAG 是“一次检索、一次生成”,它不负责判断该问几次、该信哪份材料、生成结果是否自相矛盾。这些判断是 Agent 的职责,也是二者组合后价值最大的部分。
2.2 融合的三种编排模式:串行、并行与反思回环
从工程结构看,Agent 给 RAG 只做三件具体的事:决定何时检索,决定检索目标,决定什么时候终止。按照这三件事的执行方式,可以把融合应用分成三种编排模式。
| 编排模式 | 执行方式 | 典型场景 | 延迟与成本 |
|---|---|---|---|
| 串行模式 | 先改写问题,再检索,最后生成 | 售后知识库问答、产品文档答疑 | 低,一轮检索即可收敛 |
| 并行模式 | 一个问题拆成多个子查询,同时检索多个索引,合并结果 | 多文档综述、竞品对比 | 中,查询数翻倍,权重乘在查询数上 |
| 反思回环 | 生成初步答案后,把答案送回检索器做二次求证,不一致则重写 | 合同审查、投资分析、医疗建议等高风险问答 | 高,通常 2 到 4 轮循环 |
选型时先看问题的失败成本。同样是回答错误,产品使用说明答错可以接受,合同条款答错不可接受。反思回环比串行模式多出的延迟通常在 3 到 8 秒之间,这个代价换的是对结论的二次确认。2024 年大模型 API 的单位成本下降后,回环模式才真正具备了工业可用性,这也能解释为什么反思去噪成了当年融合应用的主要卖点。
2.3 Agent 补上的四种控制力:路由、规划、反思与工具调用
Agent 层对 RAG 的四个控制点,正好对应四类工程判断。第一是路由:问题要不要进知识库,进哪个索引;第二是规划:一个复杂问题要拆成几步,每步各自检索什么;第三是反思:生成的结果是否和召回片段矛盾,需不需要重试;第四是工具调用:检索之外是否需要查数据库、算指标、调接口。
这四个控制点有一个容易被忽略的次序问题。路由一定要先于检索执行,规划可以先于检索执行,也可以穿插在多轮检索之间;反思一定在生成之后;工具调用可能发生在任意位置。不要把所有判断都交给一段大 prompt 完成——常见做法是把路由单独抽成一个 LLM 调用,输出结构化字段,再用代码判断后续分支,而不是让大模型自己一路“想”到底。这样每个环节都可观测、可回滚、可单独换模型,排查问题时能直接定位是路由错了还是检索错了。
3. 最小可复现的 Agent+RAG 链路:路由、召回、裁决三段式
3.1 链路总览与数据准备:先拆任务,再开代码
不管最终面对的是八大案例里的哪个场景,落地的第一步都是把任务拆成三个模块:路由模块判断要不要检索、检索哪个索引;召回模块负责真正查库并排序;裁决模块负责判断生成结果是否可信。下面这段代码按这个顺序展开,对应一条最小可复现链路。
数据准备阶段唯一要强调的点是索引划分。不要把所有资料灌进一个索引,至少按“产品文档”“政策条款”“历史工单”三个维度切分,路由输出直接定位到索引名。这样一份资料出了问题,不会污染全库,也是后面讲八大案例时的统一前提。
3.2 模块一:意图路由与 query 改写
路由模块用一次结构化输出完成“判断要不要检索”和“改写检索语句”两件事。这是我通常能用的最短版本:
from pydantic import BaseModel, Field from openai import OpenAI client = OpenAI(base_url="http://localhost:8000/v1", api_key="local") class RouteResult(BaseModel): need_kb: bool = Field(description="是否需要进入知识库检索") kb_name: str = Field(description="目标索引名,不检索则为空串") rewrite: str = Field(description="改写后的检索语句,保留实体和限定词") route_prompt = """你是路由引擎。判断用户问题是否需要检索资料库。 不需要检索的情况:寒暄、请求说明自身能力、与业务无关闲聊。 检索时需要把问题改写为适合向量检索的单句,不要丢失数字、型号、时间。""" def route_question(query: str) -> RouteResult: resp = client.beta.chat.completions.parse( model="qwen2.5-14b", messages=[ {"role": "system", "content": route_prompt}, {"role": "user", "content": query}, ], response_format=RouteResult, ) return resp.choices[0].message.parsed逻辑说明:路由用response_format强制输出结构化结果,而不是让模型自由生成文本,这样后续代码可以直接读取字段。rewrite是容易被省略却最影响效果的一步,用户口语“那款便宜的能不能干这个”必须改写成带型号和场景的检索句,否则向量召回全跑偏。
参数说明:模型选择上,可以用参数量偏小但指令遵循强的 7B 到 14B 模型,路由不需要太强的推理能力。如果路由错误率偏高,把need_kb的判断条件写得更具体,例如枚举哪些业务词必须走检索。不要在这里加太多 few-shot,三个例子就够,多了模型容易照抄模板。
3.3 模块二:双路召回与重排
召回阶段我一般用“稠密向量 + 稀疏关键词”双路召回,再做一次重排。单独用向量召回对专有名词不敏感,单独用 BM25 又扛不住语义改写,融合是更稳的做法:
from FlagEmbedding import FlagModel from pyserini.search.lucene import LuceneSearcher embedder = FlagModel("BAAI/bge-m3", query_instruction_for_retrieval="为检索") bm25 = LuceneSearcher.from_prebuilt_index("company_policy_index") def dual_recall(query: str, top_k: int = 8): dense_vec = embedder.encode_queries([query])[0] dense_hits = vector_db.search(dense_vec, top_k=top_k) bm25_hits = bm25.search(query, top_k=top_k) merged = {hit["doc_id"]: hit for hit in dense_hits + bm25_hits} return list(merged.values())[: top_k * 2]逻辑说明:dense_hits负责语义相近但表述不同的片段,bm25_hits负责保证型号、编号这类精确词不丢。合并时按doc_id去重,同一份材料被两种方式命中只保留一次。
参数说明:重排不能省。双路召回返回的候选集有大量噪声,直接用会污染生成质量。我会在后面接一个 cross-encoder 重排,例如bge-reranker-v2-m3,对候选集中 score 排序,取前 3 到 5 条进上下文。这里的top_k不是越大越好,超过 8 条之后,上下文里噪声比例上升,生成的答案反而变差。小库场景下top_k=8、重排保留 3 条是不错的起点,大库场景可以放宽到top_k=20、重排保留 5 到 6 条。
3.4 模块三:裁决与输出控制
召回完成,生成答案之后,还需要一个裁决步骤。RAG 链路里最常见的虚假繁荣是:召回结果根本没有支撑信息,模型仍然编了一个合理答案。裁决模块的任务就是判断生成结论能否由召回片段支撑起来:
def verify_and_answer(query: str, passages: list[dict]) -> str: numbered = "\n\n".join( f"[{i}] {p['text']} (来源: {p['source']})" for i, p in enumerate(passages) ) final_prompt = f"""基于资料回答用户问题。要求: 1. 每个直接结论后标注资料编号,如[1][2]; 2. 资料中没有依据的判断,明确写出“资料未覆盖”; 3. 资料之间有冲突时,不要自行折中,列出冲突双方及编号。 资料如下: {numbered} 用户问题:{query}""" resp = client.chat.completions.create( model="qwen2.5-30b", messages=[{"role": "user", "content": final_prompt}], temperature=0.2, max_tokens=800, ) answer = resp.choices[0].message.content if "资料未覆盖" not in answer and _coverage_check(answer, passages) < 0.5: return "资料不足,无法给出可靠结论。" return answer逻辑说明:_coverage_check是一个启发式校验,检查答案中出现的实体有多少出现在召回片段里。低于阈值就拒绝输出,而不是让模型硬答。这相当于给生成结果加了一个最后的闸门。
参数说明:temperature必须压低,这类任务 0.2 以下比较合适。max_tokens没必要给太大,答案越长越容易滑向编造。裁决失败时的回退动作也很重要,可以把原始问题再送进路由模块,重新生成改写语句再做一次召回,而不是直接失败返回,这样能兜住改写不佳的情况。
4. 八大案例的拆法:按交互形态分类,而不是按行业分类
4.1 八个案例背后的同一套骨架
标题里这八个案例,横向覆盖客服、风控、办公、数据分析等方向,但纵向拆开,它们用的其实是同一套骨架。每个案例的差异只体现在三个变量上:路由要不要拆多步、检索要不要接外部工具、生成之后要不要加反思回环。把这个变量关系想清楚,八个案例的逐案分析就可以压缩成一张参数表,读到任何新案例也能直接套进去。
4.2 从案例形态推导 Agent+RAG 配置
常见案例大致落在四个形态上。第一种是单轮知识问答,对应售后答疑、政策查询,最简单的串行模式,两个模块就够,不需要反思。第二种是多轮上下文问答,对应在线导购、故障排查,需要把对话历史里的指代对象写进 rewrite,每个问题先做指代消解再路由。第三种是跨文档综合求证,对应竞品分析、多合同条款对比,必须做并行检索与结果汇总,反思回环基本不能省。第四种是检索接计算,对应经营分析、数据看板提问,检索到的片段不能直接作为答案,要转成 SQL 或代码片段去执行,返回值再生成最终结论。
| 案例交互形态 | 是否拆多步 | 是否需要工具调用 | 是否需要反思 | 路由要点 |
|---|---|---|---|---|
| 单轮知识问答 | 否 | 否 | 否 | 直接判断索引 |
| 多轮上下文问答 | 否 | 否 | 可省 | 先消解指代再路由 |
| 跨文档综合求证 | 是 | 否 | 必须 | 拆子查询并行出发 |
| 检索接计算 | 是 | 是 | 建议 | 判断要先查数还是先算数 |
这四列配置,就是八案例和未来新案例真正的工程量所在。交互形态判断对了,后面代码基本不用大改,改的只是索引名、工具函数和反射轮上限。
4.3 把 RAG 抽象成服务:八个案例复用一个检索层
八个案例如果每个都各自写一套检索代码,维护成本很快失控。更常见的做法是把检索能力抽象成独立服务,Agent 层不关心索引背后是向量库还是 ES,只调一个统一的检索接口。几个案例共享同一套知识库服务,只在 Agent 编排层区分行为,这就是 RAG as a Service 的落地位置。
实践上,我把这个服务设计成只暴露两个接口:一个是retrieve(index_name, query, top_k),返回带 score 和 source 的片段;另一个是feedback(doc_id, helpful),把用户的显式反馈写回索引的权重表。所有 Agent 都通过接口调用,不直接访问数据库。这个抽象带来的直接收益是,案例之间新增或下线一个知识库不影响 Agent 代码,只影响一个服务配置。
5. 避坑清单:Agent+RAG 高发的五类故障与排查路径
5.1 改写吞掉实体:检索命中率低,答案频繁说“资料未覆盖”
现象:用户问题里的型号、日期、合同编号在 rewrite 之后消失,向量检索找不到精确目标,回答质量骤降。
原因:路由模块的改写太自由,模型把“BGP-2024-071 号合同”简化成了“合同”。这类专有名词恰恰是知识的索引键,被压缩后检索必然落空。
解决:改写阶段引入实体保护。把问题中匹配到的型号、编号、产品名先抽取出来,改写时用占位符替换,检索完成后再恢复。一个正则或者一个小的实体识别模型就能完成,不要依赖大模型自觉保留。
5.2 路由过度自信:问题与知识库无关,仍然硬编答案
现象:用户问“你用的什么模型”,Agent 没有走“无需检索”分支,反而从一个不相关的索引里召回内容,生成了完全不相干的答案。
原因:路由模块的need_kb判断只给了一个布尔值,模型倾向默认走检索路径。这与大模型的指令遵循偏差有关,模型倾向于执行更复杂的路径。
解决:给路由加一个显式的反例段,把“寒暄、闲聊、自我介绍、与业务无关”的示例写进去。更稳的做法是加一个前置校验,对路由输出的kb_name做一次索引存在性校验,索引里没有对应领域就强制走无需检索分支。两处一起做,路由错误率能压到可接受水平。
5.3 反思回环死循环:二次求证永远失败,接口成本翻三倍
现象:反思模式下,生成结果与召回片段不一致,系统重写后再次检查,仍然不一致,循环直到超时。账单先爆了。
原因:反思循环的退出条件只判断“是否一致”,没有判断“重试了几次”和“召回片段是否变化”。如果第一次召回就不充分,重试只会用同样的片段不断生成同样的结果。
解决:死循环必须用代码规则而不是模型自觉来终止。我在反思循环外加上三个硬限制:最大迭代次数固定为 3;同一轮重试时把上一轮的答案拼进检索条件,让“原文支持”成为检索目标;若两次迭代召回结果完全一致,直接退出循环。
5.4 上下文塞满导致幻觉反弹:召回越多,越不准确
现象:为提高命中率把top_k调到 30,重排后保留 12 条,生成长度变长,答案里开始出现片段拼接带来的虚假信息。
原因:RAG 的生成阶段对上下文长度存在一个敏感区间。片段数量超过 8 条后,模型难以同时参考所有片段,更容易受中间位置噪声片段影响。
解决:召回数量与生成模型窗口匹配。长上下文模型也不意味着可以无限塞,保底做法是把重排后的片段按与 query 的相关度排序,一次只保留前 5 条,并在 prompt 中明确声明“优先参考编号靠前的资料”。如果担心信息遗漏,把 5 条之外的片段单独做一次摘要,再把摘要附在末尾,而不是全部平铺。
5.5 验证阶段走错指标:命中率很高,答案正确率依然垫底
现象:本地评测显示检索命中率超过 90%,上线后用户投诉率居高不下,反馈“回答看着像资料,结论是错的”。
原因:命中率只验证了“相关片段被召回”,没有验证“生成结论是否基于该片段”。RAG 的生成阶段完全可能忽略资料引用,模型凭自身记忆回答,答案看起来正确但事实上超出资料范围。
解决:评测拆成两条线并行。一条线测召回,看 hit rate 和 MRR;另一条线测生成,用“答案中每个关键结论能否在给定片段中找到出处”作为判定口径。第二条线人工成本高,可以通过 LLM 作为裁判,但裁判 prompt 要明确要求逐句对齐,防止裁判模型同样产生幻觉。
6. 验证口径与可观测性:一次排查记录胜过十次猜测
Agent+RAG 应用上线后,最难的不是调路由,而是出了问题不知道在哪一环。我现在的习惯是在链路每一跳都打结构化日志,日志字段固定为:用户原始问题、路由结果、改写后 query、召回片段 id 与 score、重排后的顺序、生成答案、裁决结论。一条请求对应一条完整记录,存进时序表,问题复现时先按链路回放,而不是靠抓现场。
{ "session_id": "s-8f2a", "ts": "2024-11-03T10:22:11Z", "route": {"need_kb": true, "kb_name": "contract", "rewrite": "BGP-2024-071 合同履行期限"}, "retrieval": {"hits": [{"doc_id": "d-331", "score": 0.91}, {"doc_id": "d-287", "score": 0.83}]}, "answer": "合同履行期限为 2025 年 3 月 1 日[1]", "verify": {"passed": true, "coverage": 0.86} }日常验证用三组数字就够了:路由错误率、召回 hit rate、端到端答案可溯源率。路由错误率低于 5%,hit rate 不低于 70%,可溯源率不低于 90%,这三个数同时达标,再谈优化效果。如果可溯源率低,先改裁决模块;如果 hit rate 低,先查数据切片和 embedding;如果路由错误率高,先补路由的示例。条理清晰,比反复调模型参数有效得多。这套日志和指标的习惯,也是我从那八个案例里学到的最值钱的部分,希望帮到你。
本文还有配套的精品资源,点击获取