LangChain Agentic RAG:让 Agent 自己决定要不要检索、检索几次
个人主页 for_ever_love__ 欢迎各位大佬莅临其他栏目: 大模型开发从0到1 其他栏目: iOS项目总结大全 其他栏目: 我想学python了 其他栏目: iOS UI 文章目录LangChain Agentic RAG让 Agent 自己决定要不要检索、检索几次一、两种 RAG 的结构对比二、最小实现把 retriever 包成工具三、进阶加上反思节点四、什么时候该用 Agentic RAG五、成本对比六、让 Agent 知道检索不到就别硬找七、完整示例带反思的客服 Agent八、常见坑九、小结LangChain Agentic RAG让 Agent 自己决定要不要检索、检索几次传统 RAG 是问一次检索一次答一次的固定流程。但很多问题根本不是一次检索能解决的“对比一下 2023 和 2024 年的差旅制度有什么变化”——这需要检索两次还得分别定位两个版本。Agentic RAG 把检索变成一个工具让 Agent 自己决定要不要查、查什么、查几次、够了没有。一、两种 RAG 的结构对比传统 RAG固定链问题 → 检索 → 拼 prompt → 生成 → 答案流程写死无论什么问题都走一遍。Agentic RAG问题 → [Agent 思考] → 需要查吗 ├─ 不需要 → 直接回答你好、谢谢这类 ├─ 需要 → 调用检索工具 → 结果满意吗 │ ├─ 不满意 → 换个查询再查 │ └─ 满意 → 生成答案核心区别检索变成了 Agent 可支配的一个动作。二、最小实现把 retriever 包成工具fromlangchain_core.toolsimporttoolfromlanggraph.prebuiltimportcreate_react_agentfromlangchain_openaiimportChatOpenAItooldefsearch_knowledge_base(query:str)-str:在企业知识库中搜索相关内容。 当需要查找公司制度、流程、产品规格、历史记录等信息时使用。 query 应该是简洁的检索词不要写完整句子。 如果没找到有用内容返回空字符串即可。 docsretriever.invoke(query)ifnotdocs:return没有找到相关内容。return\n\n.join(f[{i}]{d.page_content}fori,dinenumerate(docs,1))llmChatOpenAI(modelgpt-4o-mini,temperature0)agentcreate_react_agent(llm,[search_knowledge_base],prompt你是企业知识库助手。 规则 1. 需要查资料时必须调用 search_knowledge_base不要凭记忆回答 2. 可以多次调用每次用不同的检索词 3. 检索不到时就说没有找到相关信息不要编造 4. 回答时在句末标注 [编号]。 ,)resultagent.invoke({messages:[(user,对比 2023 和 2024 年的差旅制度变化)]})Agent 会自己跑出这样的轨迹 需要分别查两个版本 search_knowledge_base(2023 差旅制度) [1] 2023版一线城市 500 元… 还需要 2024 版 search_knowledge_base(2024 差旅制度) [1] 2024版一线城市 600 元… 信息够了 主要变化一线城市从 500 元上调到 600 元[2]…传统 RAG 做不到这一点——它只有一次检索机会。三、进阶加上反思节点让 Agent 显式判断检索到的内容够不够fromtypingimportTypedDict,Annotated,Literalfromlanggraph.graphimportStateGraph,START,ENDfromlanggraph.graph.messageimportadd_messagesclassState(TypedDict):messages:Annotated[list,add_messages]documents:listretries:intdefretrieve(state:State):questionstate[messages][-1].content docsretriever.invoke(question)return{documents:docs}defgrade_documents(state:State)-Literal[generate,rewrite]:判断检索结果是否和问题相关docsstate[documents]ifnotdocs:returnrewrite# 让 LLM 打分scoresgrade_chain.batch([{question:state[messages][-1].content,doc:d.page_content}fordindocs],config{max_concurrency:5})relevant[dford,sinzip(docs,scores)ifsyes]iflen(relevant)2:returngenerateifstate.get(retries,0)2:returngenerate# 重试够了硬着头皮答returnrewritedefrewrite_query(state:State):换个说法再检索new_qrewrite_chain.invoke({question:state[messages][-1].content})return{messages:[HumanMessage(new_q)],retries:state.get(retries,0)1}defgenerate(state:State):ctxformat_docs(state[documents])ans(prompt|llm|StrOutputParser()).invoke({context:ctx,question:state[messages][0].content,})return{messages:[AIMessage(ans)]}graphStateGraph(State)graph.add_node(retrieve,retrieve)graph.add_node(rewrite,rewrite_query)graph.add_node(generate,generate)graph.add_edge(START,retrieve)graph.add_conditional_edges(retrieve,grade_documents,{generate:generate,rewrite:rewrite,})graph.add_edge(rewrite,retrieve)graph.add_edge(generate,END)appgraph.compile()三个关键节点节点作用retrieve检索grade_documents判断相关性够就生成不够就改写重查rewrite换个查询词回到 retrieveretries上限很重要否则会无限循环。四、什么时候该用 Agentic RAG场景传统 RAGAgentic RAG单跳问答“报销标准多少”✅ 更快更省过度设计多跳问答“A 和 B 有什么区别”❌✅需要跨多个数据源❌✅需要先查再判断再查❌✅简单闲聊“你好”浪费一次检索✅ Agent 会跳过检索延迟要求 1 秒✅❌ 至少多一轮判断依据你的问题是否需要多步。如果 90% 的问题都是单跳问答别上 Agentic RAG那 10% 单独处理就行。五、成本对比传统 RAGAgentic RAGLLM 调用1 次生成2~4 次思考 检索判断 生成检索1 次1~3 次延迟2~3 秒5~15 秒token1 份上下文多份上下文累积Agentic RAG 明显更贵更慢。所以只在需要多跳的场景用。一个折中方案路由——简单问题走传统链复杂问题走 Agentdefroute(question:str):ifis_multi_hop(question):# 规则或轻量分类returnagent.invoke({messages:[(user,question)]})returnfast_chain.invoke(question)六、让 Agent 知道检索不到就别硬找这是 Agentic RAG 最容易出问题的地方Agent 可能连续换 5 个查询词反复搜最后还是编一个答案。防护三招① 工具返回明确的空结果tooldefsearch_knowledge_base(query:str)-str:...docsretriever.invoke(query)ifnotdocs:return知识库中没有找到相关内容。请不要反复重试不同的检索词\直接告诉用户没有找到。...在工具返回值里直接下指令比在 system prompt 里说更有效因为它出现在模型刚看到的位置。② 限制步数agent.invoke({messages:[...]},config{recursion_limit:8})③ 显式的放弃出口prompt ... 4. 如果两次检索都没找到直接回答没有找到相关信息 并说明已尝试过哪些检索词。不要编造不要继续重试。 七、完整示例带反思的客服 Agentfromlangchain_core.toolsimporttoolfromlanggraph.prebuiltimportcreate_react_agenttooldefsearch_faq(query:str)-str:搜索常见问题库。用于查找退款、物流、发票等标准问题的答案。return\n.join(d.page_contentfordinfaq_retriever.invoke(query))or无结果tooldefsearch_order(order_id:str)-str:根据订单号查询订单状态和物流。订单号形如 SO20240115。returnapi.query_order(order_id)tooldefescalate_to_human(reason:str)-str:转人工客服。当无法解决用户问题或涉及退款/投诉时使用。create_ticket(reason)return已创建人工工单客服将在 1 个工作日内联系用户。agentcreate_react_agent(ChatOpenAI(modelgpt-4o-mini,temperature0),[search_faq,search_order,escalate_to_human],prompt你是电商客服助手。 流程 1. 先判断问题类型标准问题 → search_faq具体订单 → search_order 2. 找不到答案时最多再换一次检索词 3. 仍然找不到 → 调用 escalate_to_human 转人工 4. 涉及退款、投诉的直接转人工不要自行处理 不要编造物流信息、退款金额、政策条款。 ,)forchunkinagent.stream({messages:[(user,我的订单 SO20240115 什么时候到)]},stream_modevalues,config{recursion_limit:8},):...给 Agent 一个转人工的工具是很实用的设计——它给模型一个明确的我搞不定的出口比让它硬答好得多。八、常见坑现象原因解法反复检索同一个意思没有放弃出口工具返回里下指令限制步数延迟很高多轮调用简单问题路由走传统链简单问题也去检索没区分prompt 里写明打招呼不用检索上下文爆炸多轮检索结果都留着只保留相关的或用裁剪节点明明有答案却说没有工具返回无结果太早检查检索阈值是否过严九、小结Agentic RAG 把检索变成工具Agent 自己决定查不查、查几次最小实现tool包住 retriever create_react_agent工具描述要写清楚什么时候用进阶加反思节点retrieve → grade → (generate | rewrite → retrieve)必须设重试上限适合多跳问答、跨数据源单跳问答用传统链更快更省成本是 2~4 倍可以用路由做快慢分离防反复检索 硬编三招工具返回值里下指令、限制步数、给一个 escalate 类的明确出口。下一篇讲 Corrective RAG——检索质量不好时怎么补救。