资讯动态

ReAct框架2.5:AI Agent的推理行动观察反思循环实战

发布时间:2026/10/17 2:27:28 来源:尧图企业网站定制
ReAct框架全称是Reasoning and Acting也就是推理与行动。做AI Agent开发的朋友对这个词应该不陌生。标题里的“2.5 ReAct框架 推理行动观察反思循环到搞定为止”核心就是把ReAct循环拆成推理、行动、观察、反思四个环节让Agent从碰到任务开始一路“想一步、做一步、看一步、回头想一想”反复迭代直到任务收敛出最终结果。这篇内容我会围绕这套框架讲讲我在项目迭代中总结的实现经验包括循环机制的拆解、核心代码实现、一个完整任务的走查演示以及一路踩过的坑和优化思路。无论你是刚接触Agent开发的新手还是在多步任务调优上卡过壳的老手这篇文章应该都能给你一些能直接搬走的干货。1. ReAct框架到底是什么2.5版本到底改了些什么ReAct这个思路在Agent领域里不算新鲜但真正把它用出效果的人其实不多。我最早接触这个概念时也觉得“推理加行动”无非就是模型想一想再调个工具有什么难的可真到业务里跑起来才发现循环能不能稳定收敛、模型会不会在一个动作上来回空转、工具返回的结果怎么喂给模型不跑偏每一步都是坑。我这边维护的Agent项目从1.0版本一路迭代到现在的2.5版本核心循环始终是ReAct但每个版本解决的问题完全不同。先聊聊1.0版本。那时我的做法很简单让模型针对用户问题直接输出一句话然后我手动去后台执行对应的工具最后把结果塞回来再让模型生成答案。这其实就是一个“伪ReAct”因为模型并没有连续的推理过程它只是在一次次被调用时做局部判断。结果就是任务稍复杂就容易断链比如用户问“帮我查一下A产品的价格再对比B产品然后告诉我哪个更适合预算5000以内的人”模型第一步查完价格就直接开始回答了根本没走后面的对比和推荐。到了2.5版本我把循环拆成四个强制环节并且给每个环节定义了清晰的输入输出格式推理Thought模型分析当前状态总结已知信息和下一步计划。行动Action模型选择一个工具并给出结构化参数。观察Observation系统执行工具把真实结果返回给模型。反思Reflection模型对比预期与实际结果明确是否继续、换方向还是结束。这个闭环的威力在于模型不再是“凭记忆答题”而是变成了“动手查证、错了改、再查证”的执行者。2.5版本相比早期版本最重要的一处改动是把反思从“可有可无”变成了“强制项”每一步行动后都必须输出反思没反思就视为本轮作废下一轮不携带这个反思结果。1.1 为什么选择ReAct而不是直接让模型一步到位有人会问现在不少大模型已经自带工具调用能力了为什么不直接用非要自己搭一套循环我在几个项目里实测下来的结论是拆开做循环和让模型自己一口气搞定差别非常大。直接调用模式相当于把任务交给模型自由发挥模型内部怎么拆解你看不到中间某一步出错你也没法干预。简单任务比如“查询明天的天气”还好模型直接调用天气API就回来了。但一旦任务变成“先查三家供应商的报价→对比运费政策→再根据预算推荐方案”这种多步逻辑模型很容易在中间丢步骤或者把上一步的真实结果记错然后一本正经地编下去。ReAct框架的做法是把任务拆成一个一个显式的步骤每一步都记录在上下文中模型必须参照上一步的实际观察结果来决定下一步动作。好处是每一步都可审计、可干预、可回滚出问题能准确知道卡在哪一环。坏处也很明显多轮交互带来的令牌消耗高响应速度慢。但以稳定性优先的生产场景里慢一点换可控性非常划算。1.2 2.5版本的整体架构整体架构上我把Agent拆成了三层。底层是工具层维护了一批小而专的工具比如查询价格、搜索口碑、读取文件、数学计算等等。每个工具都有独立的名称、用途描述、参数Schema和回调函数。工具层不参与任何循环逻辑只管执行。中间层是循环调度器也是ReAct框架的心脏。它负责维护推理-行动-观察-反思的循环状态拼装每次请求的上下文解析模型输出执行工具调用并判断循环是否应该结束。上层是任务入口和结果汇总。任务入口接收自然语言描述把它包装成系统提示词结果汇总模块则把循环过程中沉淀下来的中间结果整理成最终交付答案。2.5版本在架构上的一个显著变化是给观察结果加了一道“标准化过滤器”。所有工具返回的数据先经过截断、去重、结构化三个处理步骤再送入模型上下文。这一步后面会展开讲因为它直接关系到循环的稳定性。2. 循环四阶段拆解推理、行动、观察、反思ReAct循环四个环节每个都有它的设计讲究不是随便拼在一起就能跑通的。下面逐个拆开说重点讲我在实际实现里遇到的设计取舍。2.1 推理Thought先想清楚再动手推理阶段的任务是让模型输出一段简短的思维过程说明“我现在掌握了什么、还缺什么、下一步打算做什么”。很多开发者在这里犯的典型错误是让模型自由思考结果模型洋洋洒洒写了一大段分析真正有用的信息和行动计划反而淹没在长篇大论里。我在提示词里对推理格式做了硬性约束输出必须符合这样的结构Thought: 我现在知道什么下一步应该做什么为什么这样做。 Action: 工具名(参数值)这里最关键的其实是第一句“我现在知道什么”。这句话看起来多余实际上决定了循环的“记忆质量”。因为大模型本身没有状态它唯一的记忆来源就是对话历史上已经发生的内容。模型把已知信息说出来调度器才能判断它到底有没有正确吸收前面的观察结果。如果模型在推理里复述出的已知信息和上一轮观察结果对不上说明模型已经产生了事实漂移调度器需要介入纠正。我还在推理阶段做了一个轻量的防重复检测。把每轮Thought和上一轮Thought做向量相似度对比相似度超过阈值就认为模型陷入原地打转下一次请求时会在提示词里追加警告要求模型“必须提出与之前不同的行动计划”。这个手段虽然粗糙但对某些爱绕圈子的模型非常有效。2.2 行动Action工具要小而专命名要一眼懂行动环节的关键不在模型而在工具设计。2.5版本里我坚持“小工具原则”一个工具只做一件事命名直白参数尽可能少。举个反例。早期版本里我做过一个“all_in_one_search”工具参数有十几个能搜商品、能搜新闻、能查天气、还能翻译。结果模型每次调用都要在不同参数之间纠结参数填错率极高工具内部还要做一堆条件分支。后来我把它拆成了current_weather、product_price、news_search等五六个独立工具每个工具的参数不超过三个模型的选择准确率明显改善。工具接口的格式是文本化的模型输出如下形式Action: product_price(product产品A)循环调度器拿到这段文本后先走正则解析匹配“工具名(参数值)”的格式再把参数值做类型转换最后调用对应的注册函数。解析器的容错能力很重要模型经常输出多余空格、引号错配、甚至中文逗号。我在解析器里做了一层宽松匹配正则解析失败时尝试从整段文本中提取工具名和参数键值对实在解析不了就返回一个“工具调用格式错误”的观察结果让模型自己纠正。这个过程虽然多消耗一轮循环但比解析器硬抛异常强得多。2.3 观察Observation别把垃圾数据喂给模型观察阶段是把工具返回的真实结果回填给模型。这个环节看似简单实则是最容易翻车的地方。原因是工具返回的内容往往是原始数据尤其是搜索类的接口一返回就是几百条摘要信息量大但噪声也多。如果把原始结果直接塞给模型第一浪费令牌第二模型很容易被无关信息带偏忘了主任务是什么。我在2.5版本里给观察结果加了标准化处理管线按顺序做三件事截断设置最大长度2000字符超出部分从中间切掉只保留头部和尾部。这个策略是试出来的太短的截断会丢掉关键内容太长的截断又起不到减负作用2000个字符在当前模型上下文窗口下算是一个平衡点。去重工具返回内容经常有多个条目重复比如不同渠道同一条新闻的摘要全部发给模型没有意义合并成一条就够了。结构化把纯文本结果改造成关键字段的形式比如搜索结果是“标题—来源—摘要”的三元组列表价格数据整理成表格行。结构化这一步的收益特别明显。模型面对干净、有序的信息时推理的连贯性和准确性都会提升。我对比过同一个Agent任务在“原始返回”和“结构化返回”两种模式下的表现前者的平均循环轮数高出大概1.5轮最终答案的完整度还不如后者。2.4 反思Reflection循环能不能收敛全看这一步反思环节是ReAct框架区别于普通工具调用的灵魂。没有反思的循环本质上还是一个“打完就收工”的流水线有了反思Agent才具备“自我纠错”的能力。我在反思环节给模型规定了三个必答问题观察到的结果和行动前的预期一致吗如果不一致最可能的原因是什么下一步该怎么做继续推进、换一条路径、还是直接终止这个设计是踩过不少坑才定下来的。早期版本的反思只是一句“请评价上一步的结果”模型十次里有八次给出“结果合理继续执行”的废话即使工具返回的是空数据或者明显错误的内容它也不愿承认。后来我在提示词里加了强约束“如果结果与预期不一致你必须明确指出并承认错误同时给出至少一条具体的替代方案。”加了这条之后反思才真正产生修正价值。反思的另一个作用是给循环收敛提供依据。当反思里明确出现“已获得所有必要信息可以结束”时调度器会降低最大迭代轮数提前跳出循环。这在长任务场景里能省不少令牌。3. 实操实现从零搭一个可用的ReAct Agent理论说清楚了下面看代码。我用的是一套简化但可以直接跑的Python实现核心循环大概一百行左右工具注册部分加上两个示例工具也不到五十行。3.1 环境准备与工具集设计运行环境我用的Python 3.10以上依赖就两个一个模型推理客户端这里我抽象成了统一接口方便后面切换不同的模型服务一个工具注册模块用来管理工具清单和调用入口。工具注册模块的核心逻辑是“装饰器注册”这样每新增一个工具只需要写一个普通函数并加上注册标记不需要改动循环调度器。TOOL_REGISTRY {} def register_tool(name, description, params_schema): def decorator(func): TOOL_REGISTRY[name] { func: func, description: description, params_schema: params_schema } return func return decorator register_tool( nameproduct_price, description查询指定产品的当前市场售价返回价格和近期促销信息。, params_schema{product: string} ) def product_price(product: str): # 实际项目里这里调用第三方价格数据接口 return {product: product, price: 2999, promo_price: 2699} register_tool( nameproduct_review, description查询指定产品的用户口碑评分和主要评价摘要。, params_schema{product: string} ) def product_review(product: str): return {product: product, score: 4.2, summary: 做工扎实性价比一般}工具注册信息的description和params_schema会被放进系统提示词作为模型的“工具使用说明书”。建议在description里写清楚工具的适用场景不要只写“查询产品价格”要写“当用户询问某产品卖多少钱、近期有没有降价时使用”。模型对场景化描述的接受度远比干巴巴的接口说明要高。3.2 核心循环代码实现循环调度器是Agent的中枢。它每一轮做四件事调用模型生成推理与行动、解析模型输出、执行工具调用、强制生成反思。代码逻辑相当紧凑class ReActAgent: def __init__(self, llm, max_iterations8): self.llm llm self.max_iterations max_iterations def run(self, task: str, history: list None) - str: history history or [] for step in range(self.max_iterations): response self.llm.generate( system_promptself._build_system_prompt(task, history), user_prompt请继续执行任务。 ) parsed self._parse_response(response) if parsed[type] finish: return parsed[answer] obs self._execute_action(parsed[action_input]) reflection self._reflect(parsed, obs) history.append({ thought: parsed[thought], action: parsed[action], observation: obs, reflection: reflection }) # 历史裁剪只保留最近三轮的完整记录 history self._trim_history(history) return 任务未能在限定步数内完成请调整任务描述或扩大工具集。这段代码里有几个值得展开的细节。第一个是history的维护。大模型没有状态每一轮对话都必须把之前所有有用的历史拼回去模型才知道自己已经查过什么、得到过什么结果。但history越长令牌成本越高。2.5版本里我的策略是只保留最近三轮的完整记录更早的内容被压缩成一个简短的摘要比如“第1轮查询了X的价格结果为2999元”。这个裁剪策略在多数任务上损失不大却能显著控制上下文长度。第二个是finish类型的识别。模型在确认任务完成时必须输出“Final Answer: xxx”这样的格式。调度器识别到finish标记就立刻跳出循环不再额外生成内容。如果模型学不会主动结束循环就会一直跑到max_iterations既浪费时间又浪费令牌。所以我在提示词里专门写了一句话“一旦你确认所有必要信息都已获得请直接输出Final Answer不再输出Action。”第三个是_tri_history函数里同时会做重复动作检测。对比上一轮Action字段的文本如果发现本轮和上轮动作相似度极高就给模型追加警告避免重复执行相同操作。这是防止循环空转的兜底手段。3.3 提示词模板与关键参数提示词模板是ReAct循环的“操作系统”。我用的模板结构如下你是一个任务执行助手。你可以使用以下工具 {tools_json} 执行任务时必须严格遵循以下流程 1. Thought: 简要说你当前的状态和下一步计划。 2. Action: 调用工具格式为 ToolName(参数值)。 3. Observation: 观察工具返回的结果。 4. Reflection: 评价结果是否符合预期并说明下一步调整。 5. 当任务完成输出 Final Answer: 最终答案。 约束 - 每轮只能调用一个工具。 - 如果观察结果与预期不符必须在Reflection中承认错误并给出替代方案。 - 不要重复执行相同的Action。 - 如果已有信息足以完成回答立即输出Final Answer。关键参数方面从实测经验看温度设为0.1。温度过高模型在工具选择上会“天马行空”0.1能让它的决策相对稳定。最大迭代轮数设为8轮。简单任务3到4轮就能结束8轮是一个比较保守的上限再多轮的任务往往说明任务本身拆解得不够细。观察截断长度为2000字符这是过载保护和信息完整度之间的折中。历史保留三轮完整记录更早的压缩成摘要。这套配置不是绝对的不同模型对格式的敏感度和对长上下文的处理能力不一样。建议先跑一个测试集观察平均轮数和失败率再做针对性调整。4. 完整任务演示从“查竞品资料”到“输出对比报告”只看代码还是有点抽象我把一个真实场景完整走一遍。任务设定是某部门想了解三款同类产品的售价和口碑最终产出一份简要对比表。这个任务设计成必须走多轮工具调用正好能展示ReAct循环的工作过程。4.1 第一轮初始推理与首次行动模型拿到任务后的初始推理大致是Thought: 用户要求对比产品的价格和口碑。我目前不知道具体是哪三款产品需要先获取产品列表。Action: product_search(keyword主流同类型产品)Observation: 返回产品A、产品B、产品C均为当前市面常见型号。第一轮的关键在于模型能否正确裁决“先搜索产品名单而不是直接猜价格”。如果模型这个环节就跳步后面的所有数据都会建立在幻觉之上。ReAct提示词里的“每一步都要有依据”约束对这种场景有很强的约束力。4.2 第二轮和第三轮依次查询价格拿到产品列表后模型继续行动Thought: 已获取产品名单。现在分别查询三款产品的售价先从产品A开始。Action: product_price(product产品A)Observation: 产品A售价2999元促销价2699元。之后模型又对产品B做了一次查询Thought: 产品A价格已确认继续查询产品B。Action: product_price(product产品B)Observation: 产品B售价3499元。真实运行中每一轮之间都会插入一个反思环节。比如模型在拿到产品A价格后会反思“价格查询结果正常与预期一致。继续查询下一条。”这个反思虽然看起来像废话但它承担着一个重要职责确认“上一步操作确实有效”。如果模型跳过反思直接连查三款产品一旦中间某次查询返回了错误数据就不会被发现。4.3 中途救场反思环节如何修正路径假设在查询产品B口碑时工具返回了异常结果Action: product_review(product产品B)Observation: 查询结果为空可能未找到对应产品。如果没有反思机制模型很可能会硬着头皮继续查产品C最后拿着残缺数据强行生成报告或者干脆编造一个评分。有了反思约束模型的输出会变成Reflection: 产品B口碑查询失败与预期不符。可能原因是使用了产品全称“产品B”而工具只支持型号简称“B型号”。下一步改用简称重新查询。这个反思的含金量很高它明确指出了失败原因还给出了具体的修正操作。接下来模型执行Action: product_review(productB型号)Observation: 返回12条评价综合评分4.2分。这一步如果靠传统的一次性工具调用根本不可能实现自我修正。同样的任务让模型直接生成答案它在产品B口碑这一项上大概率靠训练记忆编数据而ReAct循环通过反思把这个错误中途拦截并纠正了。4.4 循环收敛与最终输出三款产品数据都齐了之后模型会进入汇总阶段Thought: 产品A、B、C的价格和口碑数据均已获得足以生成对比报告。Final Answer: 产品A售价2999元促销2699元口碑评分4.5分产品B售价3499元口碑评分4.2分产品C售价2599元口碑评分4.6分。根据预算和口碑综合来看产品C性价比最高适合预算敏感型采购。整个任务用了大概6轮符合预期。这个案例说明一个设计得当的ReAct循环能让模型利用工具和反思机制完成复杂多步调研而不需要为每一种任务单独训练模型。5. 常见问题与排查技巧实录实战里踩过的坑比文档里写的多得多。下面列几个最高频的问题每个都附上排查思路和解决方法。5.1 循环卡死模型反复执行同一步表现连续四五轮日志里模型的Action完全一样Observation也高度相似反思环节形同虚设。原因模型不认为自己的操作有错或者它认为重复执行总会等到想要的结果。尤其是面对查询结果为空、工具返回数据不完整这类情形模型容易“死磕”。排查思路先看Action文本是否逐字相同再看Observation内容是否一致。如果两个都是重复的基本可以确定是循环卡死。解决办法我在2.5版本里加了三道防线。第一是最大轮数封顶8轮强制截停第二是重复动作检测对比相邻两轮的Action相同就给模型警告第三是对反思内容做关键词监控如果反思里出现“结果不符但继续尝试”之类的表达调度器会自动追加一段更强的纠偏提示词。这三道防线加下来循环卡死的概率可以降到很低。5.2 观察信息过载模型被长文本带偏表现工具返回上万字内容模型在下一轮推理时开始大量复述原文完全忘记原来的任务目标。原因发布工具时没做返回内容管控把未经过滤的原始数据直接塞给了模型。模型面对超长文本时注意力很容易被前后部分的内容带走导致推理偏离主任务。排查思路统计每一轮的Observation字符长度如果经常超过上下文窗口的三分之一说明工具返回内容没有做标准化处理。解决办法是给每个工具的输出统一接上标准化管线。截断到2000字符、合并重复条目、结构化核心信息。另外从工具设计层面也要改让工具返回的是“摘要”而不是“全文”。比如搜索工具不返回网页全文只返回标题和摘要查询数据库的工具只返回结果条目数和关键字段。从源头减少数据量比事后截断更健康。5.3 工具调用格式解析失败表现模型的Action字段不是标准格式比如输出Action: 帮我查一下产品A的价格而不是Action: product_price(product产品A)。原因可能是提示词约束不够严格也可能是模型本身对格式的指令遵循能力偏弱。不同模型之间的差异特别大有的模型你给了示例它还会变着花样输出。排查思路统一记录解析失败的日志统计失败率。如果失败率超过10%就需要在提示词里加入“失败率较高”的专项提醒并提供一到两个正向示例和反向示例。解决办法是给解析器加一层宽松解析。先用正则匹配标准格式如果失败再尝试从整段文本里用工具名做模糊匹配同时提取参数键值对。宽容解析只能作为兜底不能成为常态否则模型会越来越放飞。我在2.5版本里对宽容解析命中的样本加了标记隔一段时间检查一次一旦发现某个模型频繁触发宽容解析就针对这个模型做专门的格式强化提示。5.4 反思失效模型拒绝承认错误表现反思输出“虽然结果与预期不符但整体上还是合理的”然后继续执行原计划等于没反思。原因大模型在训练阶段被大量优化为“乐于助人”面对任务中的不完美结果模型倾向于“粉饰太平”而不是主动认错。这其实是反思环节最常见也最难解决的卡点。排查思路看反思段落里有没有明确的关键词比如“失败”“错误”“不符”“替代方案”。如果一段反思里找不到任何负面措辞同时下一轮Action和上一轮完全相同基本可以判定反思失效。解决办法是在提示词里用强约束“如果观察结果与预期不一致你必须承认错误并列出至少一条具体的替代方案。”这条约束实测能明显提升反思的可用率。另外一个备选方案是二次校验让模型把反思内容连同观察结果一起重新评估一次代价是令牌消耗增加适合对可靠性要求极高的场景。6. 优化方向与个人经验最后聊几个我认为值得花精力打磨的方向也算是做ReAct循环这么久以来的一点心得。6.1 控制迭代轮数与令牌消耗ReAct循环开销最大的地方在于每次请求都要把历史上下文完整回传。轮数一多令牌消耗会快速增长。2.5版本里我用的历史裁剪加摘要压缩已经能控制不少成本但还有更激进的做法只保留对当前决策有直接影响的最近一轮观察和反思更早的内容全部丢弃。实际操作中我发现多数任务的决策依赖窗口其实很窄真正过去第六轮的信息很少被用到。代价是如果任务出现长距离依赖比如最后汇总时需要引用第一轮的某个细节可能会丢信息。所以我建议在裁剪策略上保留一个“关键记忆字段”专门存放所有已经完成的查询结果每次请求重新整理成摘要放回上下文。6.2 让反思更有效的小技巧反思环节的改进空间最大。我在2.5版本里试过“双轮观点校验”就是让模型先反思一次再把反思结果和原始Observation一起发回去做二次确认。效果确实好反思的准确率提升明显但令牌消耗也翻倍了。生产环境里如果对成本敏感建议用关键词监控代替二次校验把反思内容里是否包含具体原因和替代方案作为生效标准不满足就回炉重写。这个方案虽然粗但成本低能挡住大部分“假反思”。另外一个细节是反思的格式要结构化。不要只让模型写一段自由文字而是固定输出“预期是否达成—失败原因—下一步操作”三个字段。结构化的反思内容在裁剪、检索、可视化时都更友好。6.3 后续扩展方向ReAct循环还有一些挺有意思的扩展空间。比如在反思环节引入置信度评分让模型对自己每一步的操作打个分低于阈值就触发重新规划。再比如把历史上下文交给独立的记忆管理模块用向量检索代替简单的截断裁剪这样Agent能做更长周期、跨多个会话的复杂任务。我个人在多次迭代中体会最深的一点是ReAct框架真正难的不是搭出一个能跑的循环而是让循环在真实业务场景里稳定收敛。每一轮推理都必须产生信息增益每一次行动都必须有明确目的每一段反思都必须带来实际修正。做到这三点哪怕循环显得有些“笨”任务最终都能被可靠地搞定。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑