ReActAgent 推理循环
思考、行动、观察的循环是怎么跑起来的:一轮里发生了什么,什么时候停,额度用完怎么收场。
依据 agent-core 0.1.20 · develop@fbe6a0d9(2026-11-13)核实
ReAct 是什么
ReAct 是让大模型"边想边做"的套路:模型先想下一步该干什么,需要外部信息就调用工具,看到工具结果后再想,直到能回答为止。openJiuwen 里实现这个套路的类叫 ReActAgent,后面讲到的 DeepAgent、团队协作,底下跑的都是它。
搭一个最小的智能体
官方示例的骨架是四步:身份卡、配置、工具、运行。
from openjiuwen.core.single_agent import AgentCard, ReActAgentConfig, ReActAgent
from openjiuwen.core.runner import Runner
card = AgentCard(id="promise_agent", description="订单交期助手")
config = ReActAgentConfig(
model_client_config=model_client, # 模型服务地址、密钥
model_config_obj=model_config, # 模型名、温度
prompt_template=[{"role": "system", "content": "你负责回答客户的交期询问。"}],
max_iterations=5,
)
agent = ReActAgent(card=card).configure(config)
Runner.resource_mgr.add_tool(check_inventory) # 工具注册到全局资源管理器
agent.ability_manager.add(check_inventory.card) # 再告诉这个智能体它能用
result = await Runner.run_agent(agent=agent, inputs={"query": "1000 件 A-100 什么时候能交?"})
这段按官方示例 build_react_agent.ipynb 改写,check_inventory 是你自己定义的工具;本站没有连接真实模型运行它。
注意卡片(Card)和配置(Config)是分开的:卡片描述"它是谁",可以序列化、跨进程传递;配置是运行时的东西,比如模型连接。工具也一样,先在 Runner.resource_mgr 里登记实现,再把卡片加给智能体。
一轮里发生了什么
主循环是一个 for 循环,次数上限是 max_iterations(默认 5)。每一轮按这个顺序走:
| 步骤 | 做什么 |
|---|---|
| 1 | 看有没有 Rail 请求了强制结束,有就停 |
| 2 | 把运行中插进来的引导指令(steering)写进对话 |
| 3 | 调用模型,前后触发 BEFORE_MODEL_CALL / AFTER_MODEL_CALL |
| 4 | 模型没要工具:把回复作为答案返回,循环结束 |
| 5 | 模型要了工具:执行这次要的全部工具,每个工具前后触发 Rail 事件 |
| 6 | 工具里有需要人工确认的中断,就挂起保存现场 |
| 7 | 整轮成功,触发 AFTER_REACT_ITERATION,进入下一轮 |
几个细节:
- 同一轮里的多个工具默认并行执行(
parallel_tool_calls=True)。工具之间有先后依赖时,模型自然会分到不同轮里去要。 - 模型回了空内容,框架会追加一条提示让它重试一次,再空就返回错误,不会把空字符串当答案。
- 宿主可以钉死上限:
max_iterations_hard_limit由宿主设置,实际上限取两者较小值,智能体自己的配置改不大它。
额度用完怎么收场
循环跑满还没得到答案时,由 on_max_iterations 决定怎么收场:
| 取值 | 结果 |
|---|---|
"error"(默认) | 返回 result_type: error、termination_reason: max_iterations,不带已查到的数据 |
"return_observations" | 同样标为 error,但附上本轮所有工具的原始结果,交给调用方处理 |
"final_answer" | 在循环外再调一次模型,不给工具,让它用已有证据给出尽力答复,并标注被截断 |
面向客户的场景一般选 "final_answer";面向程序的场景(比如上游还要再加工)选 "return_observations"。默认的 "error" 最保守,适合宁可失败也不要给半成品的场合。
动手:调一调循环
三个任务分别对应迭代上限、额度用完的收场方式、并行工具。
小结
- 一轮 = 一次模型调用 + 这次要的全部工具;模型不再要工具,循环就结束。
max_iterations要留出"最后作答"的那一轮。- 额度用完的默认行为是返回错误,想要尽力答复要显式设
on_max_iterations="final_answer"。 - 循环里的每个关键点都有 Rail 事件,下一步的 DeepAgent 和 Rail 关卡都建立在这个循环上。
源码出处
- agent-core/openjiuwen/core/single_agent/agents/react_agent.py:563
class ReActAgent(BaseAgent)ReActAgent 本体 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:274
class ReActAgentConfigmax_iterations、on_max_iterations、parallel_tool_calls 等配置 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3169
for iteration in range(start_iteration主循环 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:3388
async def _finish_max_iterations额度用完后的三种收场 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:2431
async def _execute_tool_call工具批量执行 - agent-core/openjiuwen/core/single_agent/agents/react_agent.py:1761
async def _railed_model_call被 Rail 包住的模型调用 - agent-core/examples/react_agent/build_react_agent.ipynb官方示例:搭一个带工具的 ReActAgent
链接指向固定提交,仓库后续更新不影响这里的行号。
过关测验
1. ReActAgent 的"一轮"指的是什么?
2. max_iterations 用完了,on_max_iterations 是默认值,调用方会拿到什么?
3. on_max_iterations="final_answer" 时,最后那次模型调用能不能再调工具?