gogo-agent

为什么问题改写/意图识别不用ReAct Agent?

问题改写和意图识别,我们是需要借助大模型的,依靠他的语言理解能力和总结能力帮我们做意图识别和问题改写。但是这两个工作我们没有使用ReAct Agent,而是选择了其他的方式实现。这也是有意为之的。 (除了这两个Agent,其实还有标…

TL;DR

问题改写和意图识别,我们是需要借助大模型的,依靠他的语言理解能力和总结能力帮我们做意图识别和问题改写。但是这两个工作我们没有使用ReAct Agent,而是选择了其他的方式实现。这也是有意为之的。 (除了这两个Agent,其实还有标…

问题改写和意图识别,我们是需要借助大模型的,依靠他的语言理解能力和总结能力帮我们做意图识别和问题改写。但是这两个工作我们没有使用ReAct Agent,而是选择了其他的方式实现。这也是有意为之的。 (除了这两个Agent,其实还有标题生成、问题推荐也都是类似的做法。) 主要是因为:ReActAgent 为「开放式问题求解」而设计。问题改写和意图识别属于「确定性文本变换」,两者的任务特征完全不同: | 特征 | ReActAgent 适合的任务 | 问题改写 / 意图识别 | | --- | --- | --- | | 需要工具? | 需要查数据库、调 API、读文件 | 不需要——纯文本进、文本出 | | 需要多步? | 一步做不完,需要迭代 | 一步就够——改写就是一句话的事 | | 结果开放? | 不确定要几步才能完成 | 高度确定——输入问题,输出 JSON | | 需要记忆? | 多轮工具调用之间需要上下文 | 不需要——每次都是无状态转换 |

用 ReActAgent 做改写/识别,就像用挖掘机拧螺丝——能拧,但全是多余开销。

完整 ReActAgent 会带来的多余开销

ReActAgent 每轮推理后要判断:「我需要调用工具吗?还是已经有答案了?」 对于改写任务:答案永远是「不调工具,直接输出」。这个判断本身就浪费了 token(system prompt 里要写 ReAct 格式说明、工具列表)和一次解析开销。 完整 Agent 会初始化 Memory、加载历史、执行 AutoContext 压缩逻辑。但改写/识别是无状态的单次任务——不需要知道「上一次我是怎么改写的」,只需要当前上下文。 完整 Agent 会触发全套 Hook(熔断、记忆持久化、内容折叠等)。改写/识别不调工具,这些 Hook 全部空转。 ReAct 循环可能出现: - Agent 认为需要工具但没工具可用 → 幻觉循环 - 输出格式不符合 ReAct 约定 → 解析失败重试 - maxIters 没到但 Agent 陷入反思 → 浪费时间 单次 LLM 调用:一进一出,没有这些风险。 ReAct 模式下,即使 maxIters=1,框架仍要走完整的「准备输入 → 推理 → 解析是否有 tool_call → 确认是最终回答 → 输出」流程。直接 model.stream() 少了中间解析层。 Agent 的价值在于「思考-行动-观察」的迭代循环。当任务不需要行动(无工具)、不需要迭代(一步到位)、不需要观察(无外部反馈)时,Agent 就是纯粹的架构冗余。 问题改写和意图识别就是这样的任务:给定上下文 → 输出结构化结果,中间不需要「做任何事」,所以直接一次 LLM 调用是最合理的选择。

不用ReAct用什么?

如果不用ReAct Agent的话,那么有几个做法: 1、直接使用model调用llm 2、集成AgentBase实现一个新的Agent 这里,我们选择的是第二个方案,这样可以在后面的其他agent做更好的编排,并且能够统一的注册hook,实现统一的session管理、进度回显等。 这里先提一句,前面我们也提到了,标题生成、问题推荐这两个我们没用ReAct Agent,甚至也没用agent,用的是上面的第一个方案,主要是因为这两个流程其实都不是主链路的,对整个对话任务没啥影响,不需要做session存储,也不用回显进度。

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。