为什么选择 ReAct + Single-Agent,而不是直接上 RAG?——医师助手 Agent 决策记录

张开发
2026/4/13 20:50:26 15 分钟阅读

分享文章

为什么选择 ReAct + Single-Agent,而不是直接上 RAG?——医师助手 Agent 决策记录
一、先说结论在正式开始写代码之前我们团队花了将近一周时间在争论一个看起来不大的问题AI 模块到底用什么架构最终我们选定的方案是Single-Agent ReAct 工作流 GraphRAG 工具调用。这篇博客想完整记录这个决策的推导过程——我们考察了哪些方案、为什么否定了它们、最终的架构长什么样。纯粹的技术介绍网上很多我不想重复只想说清楚为什么是这个方案而不是别的。二、从问题本身出发用药安全检查表面上是一个查询-回答问题但细想之后有几个关键约束约束一答案必须可溯源不能靠 LLM 自由发挥。布洛芬和华法林能不能一起吃这个问题的答案是确定的有明确的药理机制依据。如果 LLM 答错了后果不是体验差而是医疗事故。所以我们不能接受LLM 凭记忆回答的方案必须有一个绝对可靠的事实来源。约束二用户输入是非结构化的自然语言质量很差。医生端可能输入患者高血压在用拜新同最近又开了阿司匹林。患者端可能输入我在吃感冒药还想吃布洛芬可以吗。这些描述杂乱药名可能是商品名、通用名或俗称混用患者标签孕妇肾病可能根本没提到。约束三信息不完整时系统不能崩也不能硬性拒绝。真实用药场景里患者经常不知道自己有没有某种过敏史或者描述不清楚。如果系统遇到信息缺失就直接返回无法判断实用性就大打折扣但如果靠猜测给出结论又不安全。有了这三个约束架构选型就有了清晰的评判标准。三、我们考察过的方案方案 A直接调用 LLM用 Prompt 包含知识最简单的做法把常见的配伍禁忌规则全部写进 System Prompt然后让 LLM 根据用户输入直接回答。为什么否定这违反了约束一。LLM 的参数记忆是概率性的不是查表。同一个问题问两次可能得到不同答案而且 LLM 没有办法证明它的答案来自哪条规则。更致命的是随着知识库更新我们不可能每次都重新训练或调整 Prompt——知识维护成本太高。我们做了一个简单测试把布洛芬 华法林这对经典的高风险配伍问题用不同措辞问了几次 GPT 类模型结论的严重程度描述差异明显。这对医疗场景来说是不可接受的。方案 B向量 RAG把药物文献向量化用语义检索向量 RAG 是目前最流行的 LLM 增强方案把医药知识文档切片、向量化用户提问时先检索最相关的文本片段再让 LLM 基于这些片段回答。为什么否定或者说为什么不够用用药安全检查的核心问题是药物之间的关系而不是关于单一药物的描述。举个具体例子问布洛芬 华法林是否有冲突向量检索可能分别找到布洛芬的药代动力学和华法林的注意事项两段文字但这两段文字不一定包含它们之间相互作用的明确表述——特别是当这种相互作用是间接的A 影响代谢酶 → 影响 B 的血药浓度时文本里往往是分散描述的语义检索很难把它们关联起来。图谱天然适合表达关系边向量适合表达相似性距离。对于这个场景我们需要的是前者。方案 CMulti-Agent多智能体协同有人提议做多智能体一个分诊 Agent负责理解用户输入一个药师 Agent负责查询知识库一个审核 Agent负责最终裁决各自有专属能力。为什么否定多智能体的优势在于并行处理复杂任务、各自专注不同领域。但我们的核心流程是串行的先理解输入再查知识图谱再整合输出没有需要并行的子任务。更实际的问题是复杂度。Agent 之间的通信协议、状态传递、异常处理在一个三个月的学期项目里会把大量时间花在框架搭建上真正的业务逻辑反而做不完。申请书里写的是 Single-Agent我赞同这个判断——在这个规模和时间约束下Single-Agent 足够Multi-Agent 是过度设计。四、最终方案为什么是 ReAct Single-Agent GraphRAG把上面的分析综合起来最终方案的设计逻辑如下核心思路是用 LLM 做理解推理用知识图谱做事实裁决两者通过 Tool Calling 连接。ReAct 解决了什么问题ReActReason Act的关键在于它让 LLM 的推理过程显式化。普通的 LLM 调用是黑盒——你问它答中间发生了什么你不知道。ReAct 强制 LLM 在每一步先写出Thought我需要做什么再决定是否触发Action调用哪个工具最后处理Observation工具返回了什么。这对我们的场景有两个直接好处可调试性。当系统给出错误结论时我们能看到是哪一步出了问题——是实体提取错了还是工具调用参数错了还是结果整合时推理出错了。工具调用是按需触发的。LLM 不会每次都盲目调用所有工具。它先判断这个问题需要查图谱吗需要查哪些药物对再有针对性地发起请求。这对控制延迟和 API 消耗都有好处。GraphRAG 解决了向量 RAG 的关系盲区我们把药物配伍禁忌、药食冲突、人群禁忌建模成图谱节点是实体药物名、疾病、食物、人群标签边是关系配伍禁忌、增强毒性、影响吸收等每条边带有属性危险等级、机制描述。Agent 提取出用药清单后把药物名作为参数传给图谱查询工具。工具在图数据库里做路径遍历找到两个药物节点之间是否存在禁忌边。这个过程是确定性的——图谱里有就是有没有就是没有不存在概率性的大概是。LLM 的角色变成了理解非结构化输入 → 提取标准化实体 → 把图谱返回的结构化规则翻译成面向用户的自然语言解释。这个分工是合理的LLM 擅长的事语言理解和生成归 LLMLLM 不擅长的事精确事实判断归知识图谱。Single-Agent 在这里的定位整个系统只有一个 Agent 实例但它被赋予了两个角色的职责意图解析理解用户在问什么和安全审核判断用药方案是否有问题。这两个职责通过 System Prompt 的设计和不同的工具配置来区分而不是拆成两个独立的 Agent。在我们目前的规模下这样做足够清晰也省去了多 Agent 之间状态同步的麻烦。五、架构示意用文字描述一下整个请求的流转过程便于理解各模块的分工用户输入自然语言 ↓ FastAPI 后端接收 ↓ ReAct Agent 启动 ├── Thought: 识别药物实体、患者标签 ├── Action: 调用图谱查询工具Tool Calling │ ↓ │ 知识图谱路径遍历 │ ↓ │ 返回冲突规则 / needs_clarification 状态码 └── Thought: 根据图谱结果生成最终结论 ↓ 如果 needs_clarification → 前端触发追问弹窗Human-in-the-loop 如果信息充足 → 生成红色阻断 / 蓝色建议 / 降级免责医嘱 ↓ Vue3 前端状态机渲染结果三个层次的职责是清晰分离的LLM 负责语言层知识图谱负责事实层状态机负责前端的交互逻辑层。这三层之间通过定义好的接口通信任何一层的实现细节变化不会传染到其他层。六、这个方案的代价任何技术选型都有代价我觉得有必要诚实地说出来。代价一图谱的覆盖范围决定了系统的天花板。如果某个用药冲突没有被收录进知识图谱系统就无法发现它——不管 LLM 有多聪明。所以图谱的质量和覆盖率是这个项目最大的风险点之一。在学期项目的规模里我们只能覆盖最常见的处方药、OTC 及典型冲突场景无法做到全量。代价二实体对齐是一个脏活。用户说拜新同图谱里的节点是硝苯地平控释片用户说999 感冒灵图谱里的活性成分是对乙酰氨基酚 扑尔敏。中间这个对齐过程商品名 → 通用名 → 成分需要额外处理否则图谱查询会频繁漏报。这一块我们目前还没有特别好的解决方案是后续博客会继续讨论的问题。代价三ReAct 的循环次数在某些情况下难以控制。如果 LLM 在 Thought 阶段判断不够果断可能反复触发工具调用导致延迟增加。需要在 System Prompt 层面加约束或者在工程层面设置最大循环轮次的硬限制。七、小结做完这个架构选型之后我的直观感受是技术选型本质上是在约束条件下做取舍不存在最好的方案只有在这个场景下最合适的方案。向量 RAG 不是不好在文档问答、知识检索类场景里它非常合适Multi-Agent 不是过度设计在需要并行处理多个复杂子任务的系统里它有明确价值。但对于我们这个项目——核心诉求是关系型事实判断、信息来源必须可溯源、三个月内要做完——ReAct Single-Agent GraphRAG 是目前最合理的组合。本文为山东大学软件学院 2023 级创新实训博客项目面向全场景用药安全的医师助手 Agent团队ColdX。

更多文章