
如果让我用一个词总结现在 AI Agent 圈子里最“朴素但最管用”的套路那一定是 ReAct。很多人刚接触 AI Agent 时总以为它是什么黑魔法其实剥开来看核心就是一个循环让大模型先“想”怎么拆解问题再“做”一步动作拿到结果后继续“想”、继续“做”直到搞定任务。这个“想一步做一步”的模式就是 ReAct全称 Reasoning Acting。网上那些热门话题什么“基于 React 模式构建能思考与行动的 AI 智能体”“AI Agent 主流架构”追到源头几乎都能落到 ReAct 上。甚至有人问“AI Agent token 是什么意思”也和 ReAct 密切相关——因为每一次想和做的交替都在消耗 token理解了这个模式你才能搞明白为什么 Agent 烧 token 烧得飞快。我刚开始搭 Agent 时也是一头雾水后来手写了一个简单的 ReAct 循环才真正理解整套机制。这篇文章我就从零拆一遍讲清楚它怎么想、怎么做以及你实际动手时会踩到哪些坑。1. ReAct 模式的核心设计思路为什么非要“想一步做一步”1.1 从“一次性回答”到“分步行动”的思维转变先看传统的大模型调用方式。你把一个问题扔给模型它直接吐出一段答案。这在回答“中国有多少个省份”这种静态问题时没什么毛病但遇到“帮我查一下今天上海到北京的高铁选一个下午两点后出发的班次并告诉我票价最便宜的选项”这种需要外部信息、需要比较、需要判断的任务模型就抓瞎了。它没有实时数据没有执行动作的能力只能凭训练记忆瞎编最后给你一个看似合理实则无法验证的答案。ReAct 解决的问题恰恰在这里。它把整个任务化解成一个循环模型在每一步不是直接输出最终答案而是先产出一段推理文本Thought说明“我现在打算做什么、为什么这样做”接着产出一个动作Action比如“我要调用搜索工具查询上海到北京的高铁时刻表”工具执行完返回观察结果Observation模型再基于这个结果进行下一轮推理如此往复直到它有足够的信息拼出最终答案Final Answer。这个过程很像人类自己解决复杂问题的思路遇到问题不会一上来就写结论而是先拆解、收集信息、试错、修正。ReAct 不过是把这个内隐的思考过程显式地交给模型让每一步都可观察、可控制。这也是为什么大家常把 ReAct 类的 Agent 叫“能思考与行动的智能体”因为它同时具备了推理能力和外部工具调用能力而不只是一个会聊天的文本生成器。1.2 与 Chain-of-Thought 的关系和区别很多人容易把 ReAct 和 CoTChain-of-Thought思维链搞混。CoT 的理念是鼓励模型在回答前先展示中间推理步骤比如“第一步算出总价、第二步计算折扣”这确实能提升复杂推理的准确率。但 CoT 的推理只发生在模型的内部发生在它自己的上下文里它不跟外部世界互动。你让模型做一个需要实时数据的任务CoT 再强也白搭因为它无法获取新信息。ReAct 相当于是 CoT 的自然延伸保留逐步推理的同时引入一个“行动”的维度。它的推理过程不只是为了“算得更准”更是为了决定“下一步该做什么”。你把 Thought 和 Action 交替输出模型像一个拿着工具箱的修理工每拧一下螺丝都看一眼说明书然后根据实际情况决定继续拧还是换一个地方拧。具体到实现上CoT 只需要让模型输出推理过程而 ReAct 要求模型输出遵循一个固定的格式化协议。举个例子模型每一步要生成类似这样的内容Thought: 我需要查询明天的天气才能决定是否带伞。 Action: 调用 get_weather 工具参数为 {city: 北京, date: 明天} Observation: 明天北京晴转多云气温 20-28 度。这个格式的意义在于它不是给人类看的而是给代码解析的。你的 Agent 程序读到了 Action 字段就提取出工具名和参数真正去执行工具然后把工具返回的结果塞回 Observation 字段再喂给模型。整个循环就是靠这种“模型输出 - 代码执行 - 结果喂回”的节奏驱动起来的。1.3 为什么 ReAct 能成为主流 Agent 架构的事实标准你去翻主流的 Agent 开源项目、框架文档、热门的“AI Agent 主流架构”介绍文章会发现 ReAct 几乎是所有架构的地基。LangChain 中的AgentExecutorLlamaIndex 的AgentRunnerAutoGPT 那种更复杂的任务驱动态核心循环都离不开 React 的变体。原因很简单它简单、可控、效果好。简单体现在实现难度上。你不需要训练模型只需要设计好 Prompt 让大模型按照 Thought/Action/Observation 的格式输出然后用十来行代码写一个循环搞定。可控体现在每一步都有迹可循你可以把所有的 Thought、Action、Observation 打印出来像看监控日志一样追踪 Agent 的决策过程出问题了好调试。效果好则是经过大量实证的ReAct 论文里就展示过它比单纯的 CoT 排斥更少、决策更准确在各种需要推理和交互的任务上问答、事实验证、位图操作等表现都很亮眼。还有一个容易被忽略的点ReAct 天然适合模块化扩展。你想让 Agent 多一个能力就多注册一个工具比如加一个“计算器”、加一个“数据库查询器”模型知道工具的存在和用法就能自动调度。这也是为什么很多团队愿意用 ReAct 作为底座去构建五花八门的 Agent 应用。2. 核心细节解析ReAct 循环里到底发生了什么2.1 观察不到就不要行动Thought、Action、Observation 的铁三角ReAct 循环的四个核心字段是 Thought、Action、Action Input、Observation外加一个可选的 Final Answer。后半部分我详细拆一遍。Thought 是模型的推理文本它解释“我现在的计划是什么、为什么要调用这个工具、上一步结果告诉我什么”。优秀的 Thought 会让整个决策过程变得透明也方便你定位模型是不是出现了幻觉或跑偏。Action 是模型选择要调用的工具名称。Action Input 是传给工具的参数通常是一个 JSON 格式的字符串比如{query: 上海到北京高铁时刻表}。Observation 是你代码执行工具后返回的字符串结果比如搜索摘要、API 返回值、数据库查询行等。最后当模型认为信息足够时会停止输出 Action而是输出 Final Answer给出最终答案。这套“铁三角”的精髓在于模型永远是在“看到 Observation 之后”才做下一轮 Thought。它不能凭空行动每一次行动都基于上一次行动的实际结果。这防止了模型在信息不足时盲目猜测也给了开发者一个组织闭环的基础。我见过很多人刚手写 ReAct 时把 Prompt 写得很随意结果模型根本不按这个格式走直接输出一大段解释文字。所以 Prompt 里必须明确给出格式示例并且说明“如果不满足格式你的回答无效”。最好在系统提示词里写上请严格按以下格式输出 Thought: 你的思考 Action: 工具名来自工具列表 Action Input: JSON 格式的参数 Observation: 结果由系统提供你不需输出在向你提供了足够多的 Observation 后请直接输出 Final Answer: 最终答案有不少实现还会在代码里做“格式修正”比如如果模型输出缺少 Action 字段就把整段当作 Thought再让它重新输出一次实测下来能有效提高成功率。2.2 工具是怎么被“定义”和“发现”的工具不是凭空让模型会用。在调用之前你需要在 Prompt 中给模型一份工具清单包含每个工具的名称、功能描述、参数说明。模型从清单中选择合适的工具然后生成一个 Action 调用它。工具描述写得好不好直接影响 Agent 的智商。比如你有一个搜索工具如果描述只是“search”模型可能拿不准什么时候该用。更好的写法是search(query: str): 用于搜索网络上的最新信息。当用户问题涉及实时事件、数据、知识库未覆盖的内容时使用该工具。参数 query 是搜索关键词。描述里要写清楚“什么时候用”“参数是什么”“返回什么”。这相当于给模型一本说明书它阅读后才知道什么场景该拿起哪个工具。还有一种方式是让模型自己“生成”工具调用代码比如 ReAct 加 Code Interpreter 的模式模型可以输出一段 Python 代码由沙箱执行。这本质上也是工具调用只不过把 Action 从“选一个预定义工具”变成了“生成一段可执行代码”。这种模式灵活性更高但也带来了代码执行安全的问题后面我会在避坑部分详细说。2.3 Token 去哪了为什么 Agent 会疯狂烧 token热词里有个“AI Agent token 是什么意思”这问题放到 ReAct 上下文里特别实在。每一次循环你都需要把之前的全部对话历史重新塞给模型。假设你循环了十步每步生成的 Thought、Action、Observation 都有几百 token第十次请求时上下文的 prompt 长度可能已经上万 token。差的模型输出质量会明显下降而且费用随时间累积上去。更隐蔽的浪费在于无效循环。模型可能在某个问题上反复调用同一个工具、得到同样的结果、再输出一样的 Thought陷入死循环。这时候你的 token 就烧得毫无意义。解决办法有几种一是设置最大迭代次数达到上限就强制结束二是对 Observation 做截断只保留前若干字符避免把超大返回文本塞进上下文三是在 Prompt 里明确要求“如果上一次工具结果没有带来新信息就不要再重复调用同一个工具”。我自己在实际项目中会给 ReAct 循环加一个“观察压缩”步骤搜索结果可能很长但模型真正需要的只是其中和任务相关的几句话。让代码先做个简单截断或摘要再丢给模型能省不少 token同时减少模型被无关信息干扰的概率。2.4 从“想一步做一步”到“计划先行”ReAct 的进阶变体原始的 ReAct 是走一步看一步每一步都靠 Thought 现场决策。但遇到特别复杂的任务比如“帮我整理一份行业研究报告”需要拆成好几个阶段走一步看一步可能陷入局部最优做着做着就忘记最初的计划。所以很多工程实现都会在 ReAct 前面加一个“Plan”阶段让模型先输出一份分步计划再按步骤执行。这就是 Plan-and-Solve 或 ReAct with Memory 的思路。我常看到有团队在 ReAct 循环里维护一个全局计划列表模型的每个 Thought 都会把当前计划和已完成进度写进去确保后面步骤不会跑偏。这种“计划 行动”的混合模式本质上还是 ReAct只是因为增加了“显式计划”这个状态使得 Agent 的长期任务完成能力大幅提升。另外一个变体是多 Agent 协作。每个 Agent 都是一个 ReAct 循环分别负责搜索、总结、审查等不同职责再通过一个调度角色把它们串联起来。这种架构看起来高级但骨子里每个节点依然是 ReAct 那套“想一步做一步”的逻辑。3. 实操过程手写一个最简 ReAct Agent附 Python 代码3.1 环境准备与核心依赖先声明一下我下面的实现不会依赖 LangChain 这类重量级框架只用一个主流 LLM 的 API再加上标准库的json和requests。这样你就能把全部注意力放在 ReAct 循环本身上。选择 Python 是因为生态成熟、调试方便。有人说“基于 Rust 语言 ai agent”更酷后文我也会说几句 Rust 方向的事但今天这个示例先用 Python 讲透逻辑。你需要准备Python 3.10 以上环境openai库或者其他兼容 OpenAI 接口的 SDK一个 LLM API Key比如你常用的任意大模型服务商我会用一个 get_weather 的模拟工具和一个搜索工具来演示。工具实现如下import json def get_weather(city: str, date: str) - str: # 模拟天气查询实际使用时可替换为真实 API data { 北京: {2025-04-10: 晴20-28度, 2025-04-11: 多云18-26度}, 上海: {2025-04-10: 小雨15-22度, 2025-04-11: 阴16-23度}, } city_data data.get(city, {}) return city_data.get(date, f{city} {date} 暂无数据请更换关键词)工具函数本身没有任何魔法就是一个普通的 Python 方法。让 Agent 能调用它关键是在 Prompt 中描述清楚并在循环执行时把 Action Input 解析成 Python 参数。3.2 构建工具注册表与解析函数为了让 Agent 能动态扩展工具我习惯用一个字典把工具名和函数映射起来同时维护一份工具描述列表供 Prompt 填充。代码如下TOOLS { get_weather: { description: 查询指定城市在指定日期的天气情况。参数city城市名date日期格式YYYY-MM-DD, function: get_weather, }, search: { description: 通用搜索引擎用于查找实时信息。参数query搜索关键词, function: lambda query: f以下是与{query}相关的搜索结果1. 2024年AI Agent市场规模达数十亿美元。2. ReAct推理与行动模式是主流Agent架构的核心。, } } def call_tool(name: str, arguments: dict) - str: tool TOOLS.get(name) if not tool: return f错误工具 {name} 不存在。可用工具{, .join(TOOLS.keys())} try: result tool[function](**arguments) return str(result) except Exception as e: return f工具调用出错{e}call_tool是 ReAct 循环中的“做”动作的执行者。它接收模型输出的 Action 名称和参数将参数解包后传给对应函数。这里要注意模型输出的 Action Input 可能不是合法 JSON也可能是带 Markdown 代码块格式的 JSON所以解析时需要做容错处理。常见的一个坑是大模型喜欢输出带反引号包裹的 JSON比如{city: 北京, date: 2025-04-10}你的解析函数必须能容忍这种格式。我的做法是先把 Action Input 里的代码块标记去掉再用json.loads解析如果失败就尝试用正则提取花括号内的部分import re def parse_action_input(text: str) - dict: text text.strip() # 去掉 json ... 包裹 if text.startswith(): text re.sub(r^(?:json)?|$, , text, flagsre.S).strip() # 提取最外层的 JSON 对象 match re.search(r\{.*\}, text, re.S) if match: text match.group(0) try: return json.loads(text) except json.JSONDecodeError: # 尝试将单引号替换为双引号非严格场景 try: return json.loads(text.replace(, )) except Exception: raise ValueError(f无法解析 Action Input: {text})3.3 主循环让大模型与工具交替执行核心的 ReAct 循环其实很短。每一步调用模型传入一个包含历史消息的列表模型会输出完整的回应文本我们再提取其中的 Action 和 Action Input 字段。如果模型输出包含 Final Answer就结束循环。如果模型输出不包含 Action 字段说明它可能在解释而不是执行此时我们把它作为纯文本消息追加到历史中再让模型重新生成一次。下面是一个最小可用的循环实现from openai import OpenAI client OpenAI(api_keyYOUR_API_KEY, base_urlYOUR_API_BASE) SYSTEM_PROMPT 你是智能助手。请使用 Thought/Action/Observation 循环回答用户问题。 你有以下工具可用 {tool_descriptions} 每轮输出格式必须为 Thought: 你的思考 Action: 工具名 Action Input: JSON 格式的参数 当信息足够时直接输出 Final Answer: 最终答案 注意Action Input 必须是合法 JSON不要用 markdown 代码块包裹。不要重复调用相同参数的工具。 .format(tool_descriptions\n.join([f- {name}: {desc[description]} for name, desc in TOOLS.items()])) def run_react(query: str, max_steps: int 5) - str: messages [{role: system, content: SYSTEM_PROMPT}] # 用户指令也放入上下文 messages.append({role: user, content: query}) for step in range(max_steps): response client.chat.completions.create( model你的模型名称, messagesmessages, temperature0 ) text response.choices[0].message.content print(f--- Step {step1} ---) print(text) # 检查是否最终回答 if Final Answer: in text: return text.split(Final Answer:)[-1].strip() # 提取 Action action_match re.search(rAction:\s*(.)\s*Action Input:\s*(.), text, re.S) if not action_match: # 如果没有按格式输出将当前内容作为 assistant 消息然后重新请求 messages.append({role: assistant, content: text}) messages.append({role: user, content: 请严格按格式输出Thought/Action/Action Input或者输出 Final Answer。}) continue tool_name action_match.group(1).strip() action_input_raw action_match.group(2).strip() try: args parse_action_input(action_input_raw) except ValueError as e: observation str(e) else: observation call_tool(tool_name, args) print(fObservation: {observation}) # 将这一步的模型输出和 observation 追加到消息历史供下一步推理使用 messages.append({ role: assistant, content: text f\nObservation: {observation} }) return 达到最大步数任务未完成。 if __name__ __main__: result run_react(北京明天适合出门吗请查询天气后回答。) print(最终结果:, result)注意我在把 Observation 追加回上下文时是直接接在模型输出后面的有些实现用单独的 user 消息来表示 Observation效果类似但要注意消息角色的一致性。这里有一个经验最好把 Observation 放在 role 为tool或user的消息里并在 Prompt 里说明规则让模型区分哪些文本是它自己生成的哪些是外部返回的。如果你全部揉到 assistant 消息里模型可能混淆“这是我的思考”和“这是工具给我的结果”导致后续决策失真。3.4 实测效果看一次完整的“想一步做一步”假设用户问“北京明天适合出门吗请查询天气后回答”。运行上面的代码在理想情况下你会看到类似这样的循环--- Step 1 --- Thought: 用户想知道北京明天的天气是否适合出门我需要先查询北京的天气信息。 Action: get_weather Action Input: {city: 北京, date: 2025-04-11} Observation: 北京 2025-04-11 多云18-26度 --- Step 2 --- Thought: 北京明天天气多云气温18-26度风力未知适合出门但建议携带外套。信息已足够我可以给出最终回答。 Final Answer: 北京明天多云气温18-26度适合出门建议带一件薄外套。这是最标准的 ReAct 流程模型先想一嘴再调用一个工具看到结果后再想一嘴直接给出结论。整个过程只消耗了两次模型调用一次工具执行。但如果你的工具返回的信息不足比如用户想知道“空气质量”而 get_weather 没返回这个字段模型可能会在下一步继续调用搜索工具去补充。这时候循环就会多跑几步每一步的 Observation 都会给模型提供新的决策依据。你可以在本地把max_steps调成 10并在工具函数里打印调用时间观察 Agent 是如何根据反馈调整行动的。我第一次跑通这个循环时最大的感受就是它并不聪明它只是在老老实实地“看完上一步结果再决定下一步”。但恰恰是这种“老实”让它的行为变得可以预测、可以调试。3.5 Rust 能做 ReAct Agent 吗简单聊聊另一个方向热词里提到“基于 rust 语言 ai agent”其实 Rust 生态里也有不少 AI Agent 框架产物比如那些主打高性能、低资源占用的实现。Rust 的优势在于编译期安全、并发能力强、运行时开销小适合对延迟敏感或需要长期稳定运行的 Agent 服务。但劣势也很明显开发速度快不过 Python社区里的 LLM 基础设施绑定不如 Python 丰富。如果你想用 Rust 写 ReAct Agent一般不是从零实现而是用现成的 crate 对接 LLM API然后再手写循环。思路和 Python 版本完全一样向模型发请求 - 解析 Thought/Action - 执行工具 - 把 Observation 拼回上下文。真正的差异在于工具调用的实现更繁琐因为 Rust 没有 Python 那样的动态**kwargs传参你得用 serde_json 手动解析参数再 match 到具体函数。我个人建议如果想快速验证想法用 Python如果想上生产、压榨性能且团队里有 Rust 高手再用 Rust 重构。Agent 架构的价值根本不在于语言而在于你怎么设计推理循环和工具边界。语言只是实现细节。4. 常见问题与排查技巧实录4.1 问题一模型不按格式输出Action 一直解析不出来这是最常见的问题几乎每个手搓 ReAct 的人都经历过一遍。你给模型一个开放提问它可能就直接输出一大段回答根本不给 Action或者 Action 和 Action Input 之间多了几行解释正则匹配失败。排查方向有三个。第一检查 Prompt 是否提供了足够清晰的工具描述和格式示例最好把正确的输出格式和错误的输出格式都各写一个示例作为 few-shot。第二检查你是否明确说明了“不按格式的后果”比如“如果缺少 Action 字段将无法完成你的目标”。第三在代码中做格式容错比如用正则启发式提取 Action或者把模型输出再次包装为消息让模型重新格式化输出。我还试过一种很粗暴的办法在系统提示词里加入“永远不要解释为什么只输出 Thought/Action/Action Input 或者 Final Answer”。这个方法适合底层模型比较听话的场景但对于某些对话习惯很强的模型多试几次才知道哪种提示词语气最稳。4.2 问题二死循环同一个 Action 反复调用模型输出 Action 调用工具Observation 返回后模型决定再调同一个工具、同样的参数然后得到完全一样的 Observation又决定再调一次。这就是典型的死循环你的 token 在空转。触发原因可能是模型没理解 Observation 的内容或者工具返回的信息不够解决它的困惑它只能一遍遍重试。我的排查步骤打印完整的循环日志看每次 Observation 是否真的相同。如果相同说明模型没有从历史中找到新信息。尝试在 Prompt 里加上“如果上一次工具的返回没有新内容你应该换个工具或直接给出 Final Answer”。在代码层面加去重逻辑如果发现模型要调用的 Action 和 Action Input 与上一轮完全一致就直接把上一次的 Observation 塞回去并额外提示模型“这个结果你已经看到了请思考下一步”。死循环也常和 max_steps 设置有关我习惯默认给 5最长 10超过就强制终止并抛出“任务超时”给用户。永远不要无上限循环这既防呆也省钱。4.3 问题三Action Input 解析失败工具参数乱套大模型生成的 JSON 经常不标准比如单引号、尾逗号、反引号包裹、多余的说明文字。前面我已经给了 parse_action_input 的增强版这里再补充两个实用细节。第一非法 JSON 时不要直接返回一个生硬的错误串而是给模型一个“引导性”的 Observation。比如返回“工具参数解析失败请确保 Action Input 是标准 JSON不要包含其他文字。”这样模型看到后会自动修正。第二如果工具本身报错比如请求超时也要把错误信息写成 Observation 返回给模型让模型决定是重试还是换工具。这其实很符合 ReAct 的理念——模型是决策者工具是执行者执行过程的任何异常都应该反馈给决策者。4.4 问题四幻觉模型在没有工具结果的情况下编答案有时候模型会跳过工具调用直接在 Thought 后面给出 Final Answer其中包含看似真实但实际编造的内容。比如用户问某公司的最新财报模型直接回答“该公司营收 XX 亿”即使你并没有给它提供过任何相关的 Observation。这类幻觉在 ReAct 场景下尤其危险因为用户默认 Agent 会查询真实数据。我的对策是在系统提示词里强制声明“如果用户的问题涉及实时信息或知识库之外的内容你必须先调用工具否则最终答案将被判为错误”。此外在代码终端的校验上也做一层如果任务的预期结果需要工具支持但模型直接输出 Final Answer就要求它重新执行一次工具调用。还有一个经验让模型在 Final Answer 中注明“该信息来自工具返回值/未调用工具”这样你就知道哪些结论是可验证的、哪些是模型自行发挥的。对负责任的应用来说这个标注很重要。4.5 问题五Observation 过长把上下文撑爆工具返回的内容可能是几十页 PDF 的摘要也可能是完整数据库表。如果全量塞进上下文模型处理时间长、费用高还可能被无关信息干扰。我的做法是在 Observation 进上下文前做“瘦身”。简单粗暴的方式是只保留前 N 个字符比如截断到 1000 个字符。另一种更好的方式是做一个 extractive summarizer把长文本切成 chunk丢给模型做归并摘要再把摘要作为 Observation 给主 Agent。虽然多花一次模型调用但主循环的 token 消耗会大幅下降。在写 ReAct 框架时可以把“工具返回预处理”抽象成一层拦截器每个工具的结果先经过一个格式化函数让它变成“模型最省脑子的摘要”。好的 Observation 有时甚至比模型努力“想十步”更有效。5. 主流框架与学习路线如何在真实项目里落地 ReAct5.1 LangChain 里你躲不开的 AgentExecutor如果你去搜索“AI Agent 主流架构”大部分资料会提到 LangChain。LangChain 的 Agent 概念是基于 Agent Tools Memory 封装的而它内置的AgentExecutor在执行 Agent 时核心循环就是 ReAct——Agent 通过should_continue判断是否继续通过take_next_step执行工具最终调用finish_plan返回输出。刚开始学 LangChain Agent 时我建议不要直接load_tools一把梭而是逐步看它的 Prompt 模板。LangChain 为 ReAct 设计了一套著名的ZeroShotAgentPrompt里面通过tool_names和tools变量注入工具列表还写明了“Use the following format”来约束模型输出。你能清晰地看到它如何实现 Thought/Action/Observation跟手写版本的思路一模一样。读懂了这套模板你就掌握了 LangChain Agent 的里子。5.2 AutoGPT 和 BabyAGI 的启示从 ReAct 到目标管理AutoGPT 这类“半自主 Agent”看起来比 ReAct 复杂得多动不动就是任务列表、优先级、上下文记忆。但你把它的执行循环拆开依然能看到 ReAct 的影子。AutoGPT 里的goal目标对应 ReAct 里的 Task每个任务定义command对应 Action命令执行后的返回对应 Observation而反思Reflection则会生成新的思考并追加到记忆里。可以说 AutoGPT 是 ReAct 的延伸实现只是在 ReAct 外面套上了“任务规划”和“状态记忆”。学习这些框架时不要被表面的复杂名词带偏。先把 ReAct 的跑通了你会发现任何高阶 Agent 架构都是在一遍遍“想一步做一步”的基础上加策略怎么安排步骤、怎么压缩记忆、怎么并行多个循环。5.3 给你一条可执行的 ReAct 学习路线很多人在热词里搜“AI Agent 学习路线”我结合踩坑经验给一条我觉得最适合普通开发者的路线第一步用 Python 手写一个模拟工具比如天气查询、计算器加 LLM 的 ReAct 循环跑通到能连续调用两三个工具完成一个组合任务。这一步重点练 Prompt 格式化和循环控制。第二步把你手写的工具替换成真实的 API搜索 API、数据库查询、文件操作等。开始处理长 Observation、工具报错、上下文管理的问题。第三步引入状态记忆。可以是基于向量的长期记忆也可以是任务计划列表。让 Agent 能够处理多轮对话和跨会话信息。第四步学习主流的 Agent 框架比如 LangChain 或 LlamaIndex自己实现一遍 AgentExecutor 级别的封装对比你之前的写法思考它为什么要那么设计。第五步读 ReAct 原始论文以及几篇主流 Agent 综述。你会发现很多工程上的直觉在论文里都有对应的理论解释比如“配合 CoT 能提高决策准确率”“加入记忆可避免重复探索”等。整个路线不需要花很多钱大部分内容你用一个模型 API 就能跑通关键是动手写、动手调试。5.4 部署一个 ReAct Agent 到线上服务的要点热词里还有“AI Agent 部署”这里也说一下。如果你打算把一个 ReAct Agent 放进生产环境有几个点一定要注意。第一是并发控制。ReAct 的每一步都是串行的模型调用本身耗时又高所以单个 Agent 的吞吐量很有限。你可以用异步消息队列把所有请求排队用多 Worker 去执行 Agent 循环但每个 Worker 里跑的还是串行 ReAct。第二是状态管理。Agent 循环中积累的 Thought/Observation 历史都放在内存里一旦服务重启就丢了。建议将中间状态序列化到 Redis 或数据库以 session_id 为 key这样用户可以在多个请求之间续接同一个 Agent 任务。第三是工具鉴权与安全。ReAct 让模型有了执行工具的权限这非常强大也极其危险。必须对你的工具做白名单控制限制文件访问范围、API 调用频率和敏感操作权限。尤其是如果 Agent 能执行代码一定要在沙箱里跑不能直接暴露在宿主机上。第四是可观测性。我每次上生产版 Agent都会给每个循环步骤打日志包含 token 数、耗时、工具名、参数、返回摘要。配合一个简单的 Dashboard你能在用户投诉之前发现模型开始乱跑或工具频繁报错。这些部署经验总结起来就是一句话ReAct 循环怎么跑不重要重要的是你如何保证它在无人盯守时也能不跑偏、不失控。结尾最后再分享一个小技巧我在实际项目里用 ReAct 时最受益的一个小技巧是永远在 Prompt 里给模型留一个“我不知道”的出口。就是说如果模型在多次工具调用之后仍然没有找到可靠答案它应该坦率地输出“暂时无法获取该信息”而不是硬编一个。这个出口看似简单却极大减少了幻觉。你可以通过 few-shot 展示一个类似的失败案例让模型学习到“Observation 不足时不能强行下结论”的行为模式。另一个体会是ReAct 模式虽然底层是反复的“想一步做一步”但真正让 Agent 表现出“聪明”的其实是工具质量。两个相同的 ReAct 循环一个配了老旧的搜索工具一个配了实时精准的数据接口效果天差地别。所以不要一味迷信模型能力多花精力打磨你的工具集比调 Prompt 更划算。从我第一次跑通一个只有 20 行代码的 ReAct 循环到现在维护着包含多个工具的 Agent 服务我觉得这个模式最大的价值不是技术上的高深而是一种工程哲学把复杂任务的决策过程拆成可观察、可干预的小步每一步都留出修正空间。对于想入门 AI Agent 的人来说亲手实现一遍 ReAct胜过读十篇架构文章。现在你可以动手试试了先从天气查询开始下一步让 Agent 替你去查资料、发消息、写报告你会慢慢习惯它“想一步做一步”的节奏。