如果你搜过“ReAct Agent”大概率会先撞见一堆前端 React 面试题和 React Native 启动白屏的技术帖。别笑ReAct 跟前端那个 React 几乎没有关系它全称是Reasoning Acting来自 2022 年的一篇论文《ReAct: Synergizing Reasoning and Acting in Language Models》。简单说它给大模型的回答过程加了一个“推理—行动循环”模型在一个循环里反复做三件事——思考当前需要什么信息、调用工具去拿这个信息、观察工具返回的结果直到信息足够拼出最终答案。这个模式看起来朴素却是当下大多数 Agent 产品的内核。尤其你在搜索“agent 框架”“agent 开发”相关话题时最后都会绕回 ReAct。这篇文章想聊清楚两件事ReAct 这个循环到底是怎么运作的以及把它搬进生产环境实践时你会踩到哪些文档里不写的坑。不管你是准备手写一个最小 Agent 练手还是已经在团队里负责 Agent 项目的落地这篇应该都能给你一些参考。1. 别把 ReAct 当魔法它只是一套“思考—调用—观察”的循环协议1.1 那篇论文到底解决了什么问题要理解 ReAct得先回到它出现之前的 LLM 应用状态。当时有两条技术路线并行一条是 Chain-of-Thought简称 CoT让模型把推理过程一步步写出来把“答案”变成“推导过程 答案”。这个方法的缺陷很明显——它是闭卷考试模型在脑子里推演全程不接触外部事实。比如它可能满怀信心地告诉你“北京今天气温 25 度”但它根本不知道昨天是不是真的下过雪。另一条路线是让模型直接调用工具比如接一个搜索引擎 API。这条路的缺陷是工具调用之间没有逻辑链条模型可能一会儿搜这个、一会儿搜那个行为非常碎片化你很难判断它到底在追什么信息。ReAct 做的事情本质上是把这两条路线合并成一套协议模型在每一步先输出自己的推理轨迹再从推理中决定调用哪个工具然后观察工具返回的真实结果把结果吸收进下一步推理。这样一来推理不再是闭卷考试而是带着实时反馈的即时决策循环。我在生产环境实践里对这点感触很深ReAct 最大的贡献不是某一步的准确率而是让模型的行为有了可解释的过程轨迹。你可以审计它每一轮在想什么、为什么调用这个工具、看到结果后做了什么调整这种透明度对于调试和人工介入非常重要。1.2 三步循环的完整拆解Thought / Action / ObservationReAct 的循环可以浓缩成三个关键词Thought、Action、Observation。Thought模型描述当前对任务的理解和下一步计划相当于它的“内心独白”。Action模型从工具清单里选一个具体工具并给出所需参数。Observation工具执行后的返回值作为新的外部事实喂回给模型。循环往复直到模型认为信息足够输出Final Answer。用一个实际例子说明。假设 Agent 收到一个用户问题“北京今天比昨天冷多少度”第一轮模型输出Thought: 我需要知道北京今天和昨天的气温才能计算温差。 Action: get_weather Action Input: {city: 北京}工具返回Observation: {today: 8, yesterday: 12}。模型看到这个结果进入第二轮Thought: 今天 8 度昨天 12 度今天比昨天冷 4 度。 Final Answer: 北京今天比昨天冷 4 度。注意一个关键细节循环的轮次不固定可能三两步结束也可能几十步还在探索。这是它灵活性的来源也是生产环境里麻烦的根源——后面我会专门讲怎么给这个循环装刹车。1.3 为什么有了“观察”幻觉问题才真正有解经常有人问我ReAct 能治大模型的幻觉吗我的回答是不能根治但能显著降低幻觉落到最终答案里的概率。原因是 ReAct 强迫模型把每一步结论建立在外部 Observation 上。如果没有 Observation模型只能靠训练时的模糊记忆生成答案而有了 Observation模型必须沿着“我看到数据说 X所以我认为 Y”的链路输出。这相当于从闭卷考试变成了开卷考试而且考场上还坐着实时更新试卷的老师。但注意这里有一个非常大的陷阱Observation 本身也可能是错的。工具可能有 bug、外部 API 可能返回脏数据、模型解析 Observation 时可能理解偏。所以在生产环境里我从不把 Observation 当成“事实锚点”而是把它当成“需要纳入决策的输入之一”。后面第 4 章我会讲一个特别常见的故障模型“Observation 明明报错模型却引用错误信息继续前进”这种问题在 ReAct 实践里出现的频率远比你想的高。1.4 被当成替代方案的 Plan-and-Execute先规划完再执行讨论 ReAct 时总绕不开另一套模式——Plan-and-Execute。它的逻辑是先让模型生成一份完整计划把任务拆成多个步骤然后按计划逐步执行中间不重新思考大方向。先规划再执行的优点是 token 成本低、流程可控适合结构化明确的任务缺点是环境一旦变化、或者计划本身有漏洞模型不会中途修正容易一路错到底。ReAct 则相反它每走一步都会重新审视现状适应突发情况的能力强代价是每一步都要重新调用模型成本更高。我个人的实践结论是大部分生产系统不是二选一而是混合。高层级用 Plan-and-Execute 定大方向每个具体执行步骤内部用 ReAct 做推理—行动循环。比如一个行业调研 Agent可以用 Plan-and-Execute 规划要查哪几个维度而每个维度内的信息抓取、数据校验、交叉验证则用 ReAct 逐步完成。这样既控制整体流程又保留单步的灵活性后面第 5 章我还会从选型角度再聊这个问题。2. 手写最小 ReAct 循环不靠框架也能跑通推理—行动闭环不少人一上来就选 LangChain 或各类 Agent 框架结果框架封装的抽象太多底层循环反而不清楚。我的建议是先手写一个最小的 ReAct Agent把主循环走通再决定要不要引入框架。这里给一份相对精简但完整的 Python 示例你跑通之后再去看框架源码会顺畅得多。2.1 第一步把工具变成 Agent 能理解的名录ReAct Agent 能调用的所有能力都要提前注册成一个“工具名录”。每个工单元数据包含名字、描述和可执行函数。工具描述极其重要——模型是靠描述来决定调用哪个工具的描述不清晰它就会乱选。import json import re # 演示工具 1简单计算器 def calculate(expression: str) - str: # 生产环境请用 ast.literal_eval 或表达式解析库不要直接用 eval try: return str(eval(expression)) # 仅作演示 except Exception as e: return f计算失败: {e} # 演示工具 2天气查询mock 实现 def get_weather(city: str) - str: mock_data { 北京: {today: 8, yesterday: 12}, 上海: {today: 15, yesterday: 14}, } data mock_data.get(city, {error: 未知城市}) return json.dumps(data, ensure_asciiFalse) TOOLS { calculate: { desc: 四则运算计算器参数 expression 是一个算术表达式字符串, fn: calculate, }, get_weather: { desc: 查询城市今天和昨天的气温参数 city 是城市名, fn: get_weather, }, }注意工具描述里最好把参数格式也写清楚比如“expression 是一个算术表达式字符串”这能避免模型往参数里塞多余内容。我在实际项目中见过太多“Action Input 传了一整个句子”的案例根因就是描述太含糊。2.2 第二步用一条约束力足够的系统提示词锁死输出格式ReAct 的成败有一半取决于 Prompt。论文里用的是纯文本格式Thought/Action/Action Input/Observation这个格式上手简单也适合演示。生产环境我建议换成结构化的 JSON 输出但手写循环时文本格式更直观。SYSTEM_PROMPT 你是一个具备工具调用能力的智能体。用户会给你一个任务你需要通过反复调用工具来收集信息、验证假设最终给出答案。 每一步你必须严格按以下格式输出 Thought: 你当前的推理和计划 Action: 要调用的工具名 Action Input: 传给该工具的 JSON 参数 输出后等待 Observation 返回再继续下一步。 当你已经获得足够信息时输出 Thought: 我已得到最终答案 Final Answer: 你给用户的最终回复 当前可用的工具 {available_tools} def make_system_prompt(tools: dict) - str: lines [f{name}: {meta[desc]} for name, meta in tools.items()] return SYSTEM_PROMPT.format(available_tools\n.join(lines))这条 Prompt 的精髓在于“你必须严格按以下格式输出”。如果少了这句或者没有给出明确的解析规则模型很容易自由发挥输出大段前言。生产环境里你甚至可以在 Prompt 里加 few-shot 示例——比如用一个完整的三步调用过程作为示范让模型仿写。2.3 第三步主循环里的解析、执行与回填核心循环的逻辑并不复杂调用大模型得到输出 → 判断是否包含 Final Answer → 解析出 Action 和 Action Input → 执行工具拿到 Observation → 把 Observation 拼回消息列表 → 再次调用模型。循环往复直到出现 Final Answer 或达到最大步数。def call_llm(messages): # 这里接入你实际使用的模型服务OpenAI / 国产大模型 / 本地部署均可 # 重点是支持多轮 chat 格式消息 pass def parse_action(output: str): thought re.search(rThought:\s*(.), output, re.S) action re.search(rAction:\s*(\w), output) act_input re.search(rAction Input:\s*(\{.?\}), output, re.S) if not action: return None return { thought: thought.group(1).strip() if thought else , action: action.group(1).strip(), action_input: json.loads(act_input.group(1)) if act_input else {}, } def run_agent(query: str, max_steps: int 8): messages [ {role: system, content: make_system_prompt(TOOLS)}, {role: user, content: query}, ] for step in range(max_steps): raw_output call_llm(messages) print(f--- step {step} ---) print(raw_output) if Final Answer in raw_output: m re.search(rFinal Answer:\s*(.), raw_output, re.S) return m.group(1).strip() parsed parse_action(raw_output) if parsed is None: # 格式崩了把错误反馈给模型让它重新输出 messages.append({role: assistant, content: raw_output}) messages.append({role: user, content: 格式错误请重新按 Thought / Action / Action Input 的格式输出。}) continue tool TOOLS.get(parsed[action]) if tool is None: observation f错误不存在名为 {parsed[action]} 的工具 else: try: observation tool[fn](**parsed[action_input]) except Exception as e: observation f工具执行失败: {e} messages.append({role: assistant, content: raw_output}) messages.append({role: user, content: fObservation: {observation}}) return 达到最大步数任务未完成这个主循环里有一个容易被忽略的细节每一步执行完成后要把模型的原始输出和 Observation 都放进下一条消息。Observation 不是替代 assistant 输出而是额外追加的信息。这样模型既能回顾自己刚才说了什么也能看到工具反馈才能做下一步推理。很多新手写 Agent 时只丢一句“Observation: xxx”给模型不给它看之前的 Thought 和 Action模型很容易迷失上下文。2.4 第四步给循环装上限位器防止它停不下来max_steps是最基本的急刹车。为什么必须设这个值因为模型对“任务完成”的感知非常模糊。你问它一个简单问题它可能回答完 Final Answer 还要继续“补充说明”你让它查一个数据它可能查完目标数据还要“再多验证一下”其他维度。如果不限制最大步数一个原本三步能完成的任务可能被模型自觉扩展成十五步。在生产环境里我更推荐再加两层限制总 token 预算给整个任务设定一个 token 上限比如 20000超过就直接终止并返回兜底答案。重复动作检测如果模型连续两次调用同一工具且参数完全一致说明它大概率在空转强制中断。这些限位器以后都会成为你监控指标的一部分。第 4 章讲“空转循环”时你会看到它们到底有多重要。3. 从能跑到能用ReAct Agent 进生产前的工程化改造清单手写 Demo 只需要把循环跑通但生产环境完全不同。我自己带过多个 Agent 项目最大的感受是Demo 能跑和你敢让它接真实流量中间差了一个完整的工程团队。即使你是个人开发者也要提前补上这些心智否则上线一周内就会遇到各种“看起来像玄学”的问题。3.1 成本不再是技术指标而是产品生死线先算一笔账。一个 ReAct Agent 执行一次任务假设需要 5 轮循环每轮生成约 800 token再加上每次调用都要把之前所有轮次的消息重新发送一次实际 token 消耗会呈倍数膨胀。比如第 1 轮 1000 token第 2 轮变成 3000第 5 轮可能上万。一次复杂任务烧掉几十万 token 的情况在真实项目里并不罕见。所以生产环境必须有预算漏斗至少三层单轮输出 token 上限max_tokens防止模型在一轮里长篇大论。总轮数上限max_steps限制循环次数。累计费用预算每步累加 token 数超过阈值立即返回兜底答案。我见过一个团队上线 Agent 后一周收到上千美元账单原因是他们只设置了max_steps20但每轮 context 越来越长20 轮的 token 总量远超预期。记着ReAct 的循环次数不是成本的关键context 的累积长度才是。3.2 工具层是能力边界白名单、超时与审计Agent 一旦能调用工具工具就是它能力的放大器也是最大的攻击面和事故源。我在自己项目里的第一个教训是给 Agent 开放了一个“文件搜索”工具结果一次对话里它把服务器上的日志目录扫了个遍。不是模型有恶意只是工具的权限边界没有约束好它的探索欲被一个含糊的 Prompt 激发了。生产环境里我至少会做四件事工具白名单只暴露当前任务真正需要的工具不把整个内部 API 的全部能力都暴露给 Agent。超时控制每个工具调用都要有独立 timeout外部 API 慢的时候不能让 Agent 无限等。参数校验与作用域限制用户输入可能被带进工具参数必须做白名单校验。比如文件类工具要限制可访问的目录范围。审计日志完整记录“用户在什么会话里、基于什么上下文、调用了哪个工具、传了哪些参数、拿到了什么结果”这是事后排查事故的唯一依据。还有一条很容易被忽略工具函数本身要健壮。前面演示代码里的eval就是反面教材生产环境请用ast.literal_eval或真正的表达式解析库否则一个恶意表达式就能让你服务器变得很忙。3.3 上下文窗口不够用滑动窗口与摘要记忆ReAct 每走一步都要把之前所有历史塞进上下文这在长任务里会迅速撑爆窗口。你当然可以换成更大上下文的模型但 token 成本会跟着涨而且长下文会让模型的注意力漂移——它会逐渐“忘记”早期 Observation 里的关键信息。我的标准做法是分两层滑动窗口只保留最近几轮消息更早的历史不直接进 Prompt。这个方案简单粗暴适合大多数短任务。摘要记忆每一轮结束后用一个小模型把前面全部对话压缩成一段“任务进展摘要”放进 system prompt。这样 Agent 既不会超窗口又能保留全局信息。再进阶一点可以做向量检索式记忆把关键 Observation 写入向量库每轮开始前检索与当前问题最相关的几条拼进上下文。但我不建议第一个版本就上向量记忆它的工程复杂度会掩盖你真正要解决的问题。3.4 没有 tracing 的 ReAct Agent 等于闭眼开车普通后端服务调试看日志和 Stack Trace 就够了。但 ReAct Agent 是“多轮决策”系统你不仅要看最终结果还要看每一步的 Thought、Action、Observation才能定位是哪一环节出了问题。我最开始做 Agent 项目时只记录了最终答案结果一遇到坏案例完全不知道模型在想什么。后来强制自己把每一步都打印成结构化日志包括轮次、token 数、工具名、工具参数、工具返回状态、调用耗时这才有了可调试的抓手。如果你用的是成熟框架可以考虑标准的 tracing 工具比如 Langfuse、LangSmith 这类平台它们能自动记录整个 ReAct 循环轨迹。但即使不用这些自己写结构化日志也完全够用——关键是你必须能看到“模型在哪一步基于什么信息做了什么决策”。3.5 稳定性三板斧格式回退、版本锁定、回归样本集ReAct Agent 的生产环境瓶颈往往不是模型的“智商”而是输出的“纪律性”。为了提升纪律性我有三件事是必做的格式回退机制一旦解析器失败不是直接报错而是把“格式错误”作为 Observation 回喂给模型让它重新输出一个合规格式。这套机制能把格式失败率从百分之几十压到个位数。模型版本锁定生产环境绝对不能使用“浮动版本”的模型比如额度到期的自动切换到新版。不同版本对格式遵从能力差异巨大今天跑得好好的任务明天可能因为模型版本更新而全线崩坏。锁定版本是 ReAct 项目的第一条稳定性铁律。回归样本集筛选 50 到 100 个典型业务问题每次改动 Prompt 或工具描述后都跑一遍对比正确率和格式失败率。没有这个样本集你很难判断一次 Prompt 修改到底是变好了还是变坏了。3.6 Prompt 与工具描述的一致性维护最后这条听起来很基础却是生产环境里翻车率极高的问题工具注册表、系统 Prompt、解析逻辑三者不一致。比如你改了TOOLS里某个工具的名字却忘了更新系统 Prompt 里的工具列表或者往工具描述里加了新的参数示例但解析器没适配这些都是低级但致命的错误。我的做法是把工具注册表当作唯一事实源系统 Prompt 里的工具列表由make_system_prompt()自动生成不允许手写维护。谁要是想加新工具只需要改TOOLS字典Prompt 和文档就会自动更新从源头杜绝不一致。4. 踩坑复盘ReAct Agent 最常见的四种死法与抢救手段这章我直接讲生产环境里最常遇到的四类故障。这些不是理论推演而是我在真实项目里一例一例踩出来的每一个背后都有对应的排查思路和修法。4.1 死法一模型不按格式输出解析器当场挂掉现象模型输出了一大段话但里面没有Action:或者 Action 后面跟的不是工具名而是自然语言描述甚至整个输出是一段 Markdown 格式的自言自语。你的parse_action()返回None循环重试几次后直接失败。根因大模型天然有“自由表达”的偏好。你给的格式约束不够强硬或 few-shot 示例太少、太单一它就倾向于按照训练语料里的自然风格输出了。抢救手段解析失败不是硬报错而是把“格式错误请重新按格式输出”作为 Observation 回喂。这通常能救回一两次。在 few-shot 里放一个“反面例子”——先展示一段错误格式再展示同一内容的正确格式模型对对比样本的吸收效果很好。如果模型怎么教都学不会那就别用自由文本格式改用结构化输出。很多模型服务支持 JSON 模式直接约束输出为 JSON解析稳定度会大幅提升。经验补充格式崩坏率是 ReAct Agent 最重要的稳定性指标之一。如果这个指标超过 5%别急着改 Prompt先怀疑模型版本或解析器逻辑通常问题出在底层而不是表层。4.2 死法二连续空转它看起来在思考其实在原地打转现象模型每轮都在调用同一个工具比如反复调get_weather参数也一样Observation 也每次一样但它的 Thought 永远在写“我需要再次确认天气信息”。既没有新信息也没有 Final Answer循环一直耗到max_steps。根因模型对“何时算完成”缺少强约束。它的训练目标决定了它倾向于多探索几步尤其是当它感觉到任务有“风险”——比如用户问题里包含数字、日期这类信息时它会害怕给错答案于是不断重复验证。抢救手段在 Prompt 里写死完成条件当你已经能从已有 Observation 推导出答案时必须立刻输出 Final Answer不得继续调用工具。实现重复动作检测如果连续 N 次 Action 和 Action Input 完全相同强制中断并返回当前最可能的结论。给 Observation 附加“信息增量”标记比如在 Observation 里额外加一行“注意此数据与上一轮相同”让模型意识到没有新增信息。经验补充这类空转在长上下文模型里更常见因为模型“觉得自己还没充分利用上下文”。实际排查时一定要把整段 messages 打出来逐轮看你会惊讶地发现模型在第三轮就开始重复只是你没有从第三轮就看到它。4.3 死法三Observation 明明报错模型却引用错误信息继续前进现象工具返回的是Observation: {error: 未知城市}模型的下一轮 Thought 却写“根据查询结果北京的天气是……”完全无视了错误标记。又或者工具返回了 A 结果模型却把它当成 B 结果继续推理。根因这是模型对外部事实的 faithfulness 不足。尤其是在上下文很长的时候模型处理太多历史信息对最新一条 Observation 的注意力会下降宁可依赖训练记忆里的先验知识也不愿意认真读报错信息。抢救手段把 Observation 格式化得更醒目比如用Observation [ERROR]: 未知城市用明确的标签把异常情况区分出来。在 few-shot 里专门安排一个“Observation 报错后模型修正计划”的正例强化模型对错误的敏感度。对于关键数字结果在 Observation 里给出显式的语义化文本而不仅仅是 JSON。比如返回的是“今天的最高气温为 8 摄氏度”比{today: 8, unit: C}更不容易被误解。经验补充这个问题很难 100% 消除所以在生产环境里我通常会加一步“答案一致性校验”——当 Agent 给出最终答案后用一个小模型或规则引擎校验最终答案是否与最关键的 Observation 一致。不一致就打回重做或者走人工兜底。4.4 死法四一个任务烧掉四十次调用账单先破产现象任务很复杂Agent 每步都输出了 token但一直没有得到满意的最终答案。它不断尝试新的工具组合你设置了max_steps20但每轮 context 都在增长实际 token 消耗远超预期最终账单比你预估的高了好几倍。根因前面说过的成本漏斗不完善。很多人只设了max_steps却没设累计 token 上限。ReAct 的 context 是随轮次指数膨胀的10 轮的总 token 量可能比 5 轮多出 4 倍以上。抢救手段设置累计 token 预算比如整个任务最多消耗 50000 token超过就终止并返回兜底答案。每轮调用前压缩历史用摘要记忆替代完整历史让 context 保持近似恒定而不是线性增长。对工具调用做“去重缓存”如果相同的工具和参数已经被调用过一次直接复用上一次的 Observation不再触发真实工具执行。经验补充成本不是上线后才考虑的问题而是设计阶段就要算清楚的。我建议每个 Agent 任务上线前都跑一个 20 个样本的成本测试统计每任务平均 token 数然后乘以你的预期用户量基本能预判账单量级。如果成本超标先压缩循环轮次而不是去换更贵的模型。4.5 排查这类问题的通用流程固定版本做最小复现逐轮审问模型前面四种死法它们的排查流程其实是通用的。我自己摸索出来的固定流程是固定模型版本和生成参数。优先排查是否是模型版本漂移导致的行为变化。拿到失败案例的完整 messages。把每一轮的 user 和 assistant 消息都打印出来不要只看最终答案。用最小复现集收敛问题。如果 Agent 接了 8 个工具先砍到只剩 2 个看问题是否依然存在。如果消失大概率是工具描述之间的互相干扰。一次只改一个变量。要么加 few-shot要么改 Prompt 格式要么换工具描述不要同时调多处否则出了问题根本没法定位是哪个改动引入的。这套流程看起来朴素但真的能解决 80% 以上的 ReAct 稳定性问题。它本质上和普通后端调试没有区别只不过你调试的对象不是代码而是“模型在特定上下文里的决策行为”。5. 该不该用 ReAct边界判断、记忆接入与多 Agent 协作形态5.1 我判断“要不要上 ReAct”的三个问题ReAct 不是银弹。我在接手新项目时至少会先问三个问题这个任务真的需要外部信息吗如果答案是单纯的“知识问答”接一个知识库检索即可没必要引入 ReAct 循环。ReAct 的价值在于外部信息会动态影响推理走向如果信息流向是固定的用传统代码调用 API 更稳更省。工具调用顺序是固定的吗如果一个任务里 90% 的情况下工具调用序列是一样的——比如“查库存 → 算价格 → 下单”那你需要的是一个确定性流程而不是 ReAct。ReAct 适合的是调用序列不确定、需要根据观察结果动态决策的任务。你能接受多轮调用的延迟和成本吗ReAct 一次任务可能 3 到 10 轮哪怕用最快的小模型也要几秒到几十秒。如果产品对响应延迟敏感ReAct 的长循环会让你很头疼这时候可以考虑把推理和工具调用分成两级或者用更轻量的模式替代。5.2 记忆接入把一次循环变成可持续的智能体状态ReAct 本身是“一次性任务”模式任务结束上下文清空Agent 不记得上次对话的任何事。但在真实产品里用户期望 Agent 有连续性这就需要把记忆分层接入。我的做法是三层工作记忆当前 ReAct 循环里的 messages这就是它短期的“工作台”。情景记忆任务结束时把关键结论和工具执行记录写入外部存储。下次遇到同类型任务时用检索把相关内容拼进新循环的上下文。技能记忆记录哪些工具组合在哪些任务类型下表现好后续在新任务里作为 few-shot 示例的候选池。这一层做得好Agent 会像有经验一样越用越顺手。需要特别提醒的是记忆不等于“把历史全塞给模型”。不加筛选地塞历史最终又是一次 token 灾难。所以我的原则是能检索到相关片段才拼进上下文检索不到就什么都不加。5.3 多 Agent 协作把另一个 Agent 当作“工具”复用同一套循环基建多 Agent 协作是现在很热的方向。很多人上来就搞复杂的 Agent 编排但我的经验是不用重新发明一套协议因为一个 Agent 的 ReAct 循环对另一个 Agent 来说完全可以当成“一个工具”。举个例子Manager Agent 的 Action 可以是“调用数据分析 Agent”Observation 就是数据分析 Agent 返回的结果。这样Manager 的 ReAct 循环基建——白名单、超时、审计、格式回退——全部能够复用。你只需要把子 Agent 封装成带描述和入参的工具接口即可。这套思路能大大降低多 Agent 系统的工程复杂度也让整个系统的行为更容易审计。当然多 Agent 也有它特有的坑比如循环依赖Agent A 调 Agent BB 又调 A、上下文串扰、状态一致性问题。但至少从 ReAct 的角度来看子 Agent 即工具这个视角能够很自然地兼容现有的循环控制机制。5.4 一点个人体会在这个领域折腾了几年我的核心体会是ReAct 不是一个“功能”而是一个运行时机制。用户感知到的 Agent 智能往往来自产品层面的记忆管理、工具质量和对失败路径的兜底而不是那几行循环代码本身。所以我给团队成员的要求也很简单能不用 ReAct 就不用非要用的时候必须把循环的步数、成本、格式失败率、重复调用率当成核心监控指标。这些数字比任何模型升级都能更快地告诉你系统是否健康。先把手写循环跑通理解每一环的代价和脆弱点再去拥抱更复杂的框架和 Agent 协作模式是我觉得最稳妥的路线。
