当前位置: 首页 > news >正文

RAG Agent 响应策略:文档与分析

=

1. 简介

在构建基于 RAG (Retrieval-Augmented Generation) 的问答系统时,如何准确、可信地向用户展示检索到的源文档(Source Chunks)是一个关键问题。

本文档对比了两种主要的技术方案:Explicit Retrieval (RAG Chain)Tool Calling Agent,并详细解析了本项目最终采用的方案(方案 2),以及其在代码层面的具体实现。

2. 方案对比

方案 1:显式检索 (Explicit Retrieval / RAG Chain)

流程

  1. Code: 接收用户 Query。
  2. Code: 调用 Retrieval Service 获取 Top K Chunks。
  3. Code: 将 Query + Chunks 组装成 Prompt。
  4. LLM: 根据 Prompt 生成 Answer。
  5. Code: 将Answer+Chunks拼接返回给用户。
流程图 (Scenario 1)
RetrievalServiceLLMApplication CodeUserRetrievalServiceLLMApplication CodeUserExplicit Retrieval PhaseGeneration Phase提问 ("VisionFive 2 CPU?")Search(Query)Return ChunksPrompt(Query + Chunks)AnswerReturn Answer + Attached Chunks

优缺点

  • 优点:实现简单,无幻觉(Sources 是代码直接附加的)。
  • 缺点缺乏灵活性。无论用户问什么(即使是闲聊),都会先去数据库检索,造成资源浪费。无法进行多步推理或根据上下文决定是否检索。

方案 2:工具调用代理 (Tool Calling Agent) -本项目采用

本方案包含三层架构细节:

  • KnowledgeBaseAgent (Wrapper): 应用程序层面的封装,负责后处理和结果组装。
  • Sub-Agent (LangGraph Runtime): 智能体编排引擎,负责状态管理和工具路由。
  • LLM (Model): 负责推理和生成。

流程

  1. Wrapper: 接收用户 Query,启动 Sub-Agent。
  2. Sub-Agent->LLM: 发送 Query,请求决策。
  3. LLM->Sub-Agent: 返回 Tool Call Request (如 “search”).
  4. Sub-Agent->Tool: 执行RetrievalService
  5. Tool->Sub-Agent: 返回ToolOutput(Raw Chunks)。
  6. Sub-Agent->LLM: 发送ToolOutput作为上下文。
  7. LLM->Sub-Agent: 返回最终 Answer。
  8. Sub-Agent->Wrapper: 返回完整状态(包含所有历史消息)。
  9. Wrapper: 解析历史消息,提取 Chunks 和 Answer,组装返回给用户。
流程图 (Scenario 2)
RetrievalServiceLLMSub-Agent (LangGraph)KnowledgeBaseAgentUserRetrievalServiceLLMSub-Agent (LangGraph)KnowledgeBaseAgentUserLangGraph Execution LoopPost-Processingask("VisionFive 2 CPU?")Invoke (Query)Predict (Query)Tool Call (name="search")Execute search(...)Return Raw Chunks (ToolMessage)Generate (Query + ToolOutput)Final Answer (AIMessage)Return State (History + Answer)Parse History ->> Extract AnswerParse History ->> Extract ToolOutput (Sources)Return {answer, sources}

优缺点

  • 优点
    • 无幻觉:Sources 同样是从工具执行结果中代码提取的,保证真实。
    • 智能决策:Agent 可以根据问题判断是否需要检索,以及如何检索(例如提取 Topic 参数)。
    • 多步推理:Agent 可以根据第一次检索结果决定是否需要再次检索。
  • 缺点:实现较复杂,需要解析 Message History。

3. 最终选择与实现 (方案 2)

本项目选择了方案 2,以确保引用的准确性同时保留 Agent 的智能特性。

3.1 代码结构

核心逻辑位于src/agents/knowledge_base_agent.py类中。

  • create_agent: 构建 LangChain Graph,负责编排 LLM 和 Tool 的交互。
  • ask: 执行 Graph,并负责后处理(提取结果)。

3.2 详细代码解析

Tool 返回的数据 (RetrievalService)

首先,src/services/retrieval_service.py负责生成 Tool 的输出字符串。这个字符串包含了 Chunk 的元数据和内容。

# src/services/retrieval_service.py# 格式化 Header,包含 Score 和 Metadataheader=f"[Source{i+1}]{page_info}(Score:{distance:.4f}){status_tag}"# 拼接 Header 和 Contentcontext_parts.append(f"{header}:\n{chunk.content}")
Agent 执行与解析 (KnowledgeBaseAgent)

src/agents/knowledge_base_agent.pyask方法中:

asyncdefask(self,query:str,topic:Optional[str]=None)->Dict[str,Any]:# ... (构建输入) ...# 1. 执行 Agent Graph# result["messages"] 包含了完整的对话历史,包括:# HumanMessage -> AIMessage (ToolCall) -> ToolMessage (Output) -> AIMessage (Final Answer)result=awaitself.agent_graph.ainvoke(inputs)messages=result["messages"]# 2. 提取 LLM 的回答 (Answer)# 通常是列表中的最后一条消息last_message=messages[-1]answer_text=str(last_message.content)# 3. 提取 Tool 的输出 (Sources)sources=[]formsginmessages:# 遍历历史消息,寻找 ToolMessageifisinstance(msg,ToolMessage)andmsg.name=="search_knowledge_base":tool_output=str(msg.content)# 4. 解析 Tool Output 字符串# 我们的 RetrievalService 返回格式是:# [Source 1] ... :# Content ...current_source=Nonecurrent_content=[]lines=tool_output.split('\n')forlineinlines:stripped_line=line.strip()ifstripped_line.startswith("[Source"):# 保存上一个 Sourceifcurrent_source:full_content=' '.join(current_content)# 保存完整内容sources.append(f"{current_source}\nContent:{full_content}")# 开始新 Sourcecurrent_source=stripped_line.rstrip(':')current_content=[]elifcurrent_sourceisnotNone:ifstripped_line:current_content.append(stripped_line)# 保存最后一个 Sourceifcurrent_source:full_content=' '.join(current_content)sources.append(f"{current_source}\nContent:{full_content}")# 5. 组装最终结果return{"answer":answer_text,"sources":sources}

3.3 关键点总结

  1. 为什么 ToolMessage 绝对可靠?

    • 框架保证ToolMessage的生成和格式不是由 LLM 决定的,而是由 LangChain/LangGraph 框架代码控制的。
    • 流程
      1. LLM 输出结构化的Tool Call Request(如search(query="..."))。
      2. 框架捕获请求,执行 Python 函数 (RetrievalService.search_knowledge_base)。
      3. 框架将 Python 函数的返回值(字符串)强制封装为ToolMessage对象。
      4. 框架将此对象追加到消息历史。
    • 结论:因为这是确定性的程序逻辑,不存在 LLM 的“幻觉”或“格式错误”问题。只要工具执行了,ToolMessage就一定存在且内容准确。
  2. 手动解析:我们编写了 Python 代码来解析ToolMessage.content。这让我们能够完全控制如何向用户展示来源(例如,我们选择了截断内容以保持界面整洁,但 LLM 看到的是全文)。

  3. 可靠性:即使用户问了一个无关问题,导致 LLM 拒绝回答,ToolMessage依然存在(只要搜索被执行了)。这让我们能够展示“虽然没回答,但我确实搜到了这些东西”。

http://www.jsqmd.com/news/179339/

相关文章:

  • 新手教程:理解USB3.0传输速度的协议基础
  • Terraform基础设施即代码:在云端快速创建CosyVoice3运行环境
  • 版权问题提醒:未经授权不得克隆他人声音商用
  • 纪念币预约终极方案:告别手忙脚乱的完整自动化工具指南
  • CDN加速内容分发:静态资源托管至各大云厂商
  • 3个被低估的NVIDIA显卡优化神器:告别卡顿的终极方案
  • 图解说明vivado2022.2安装界面操作的通俗解释
  • VS Fish Speech:CosyVoice3情感表达更自然的真实案例对比
  • Universal x86 Tuning Utility终极指南:解锁AMD/Intel设备完整性能潜力
  • 大模型Token购买通道开启:按需计费支持CosyVoice3语音生成调用
  • 教育领域应用前景:CosyVoice3为视障人士提供语音支持
  • 微信公众号推文规划:每周一篇深度技术文章
  • 中小学STEAM教育融合:让孩子体验AI语音魅力
  • 提高效率:OrCAD Capture与Pspice联合调试技巧总结
  • DownKyi视频下载工具完全指南:轻松获取B站高清视频
  • Python纪念币预约自动化:告别手动抢购的完整解决方案
  • 利用Multisim验证三极管开关电路导通条件通俗解释
  • 英文发音不准?CosyVoice3支持ARPAbet音素标注[M][AY0][N][UW1][T]修正发音
  • CAPL中时间同步与仿真时钟控制的技术细节
  • Telegram群组建立:国际用户沟通桥梁
  • downkyi视频方向修正终极教程:彻底告别竖屏视频横置问题
  • CosyVoice3是否支持实时录音上传?两种方式轻松完成prompt输入
  • 从GitHub拉取CosyVoice3最新代码:源码更新地址https://github.com/FunAudioLLM/CosyVoice
  • ChromeDriver下载地址分享:自动化测试CosyVoice3 WebUI界面操作
  • 日志分析技巧:定位CosyVoice3异常行为的根本原因
  • 官方文档之外的学习资源:B站教程与知乎专栏推荐
  • 稀疏化训练技术应用:减少不必要的参数计算
  • 电子书免费领取活动:《精通CosyVoice3》限时下载
  • 快速理解DDU工具:新手安装与使用核心要点
  • 数据中心选址考量:靠近用户减少延迟同时节能