05:Agent Loop:当下一步取决于工具返回
现在终于到 Agent 了。
Agent 不是“更长的 Prompt”,而是一种控制流:模型每一轮决定下一步动作,Python 执行动作,把结果写回状态,再让模型继续决定。
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
from agent_patterns_lab.patterns.react import run_react
from agent_patterns_lab.runtime import MockLLM, RunLimits, Tool, ToolRegistry, Tracer
def as_json(value: dict[str, Any]) -> str:
return json.dumps(value, ensure_ascii=False)
def main() -> None:
tracer = Tracer()
def get_weather(args: dict[str, Any]) -> str:
city = args["city"]
date = args["date"]
return as_json(
{
"city": city,
"date": date,
"forecast": "light rain after 15:00",
"temperature_c": "18-23",
"packing_hint": "umbrella and light jacket",
}
)
def search_places(args: dict[str, Any]) -> str:
return as_json(
{
"city": args["city"],
"matches": [
"West Lake: best before the afternoon rain",
"China National Tea Museum: indoor-friendly and good for tea lovers",
"Hefang Street: local snacks and easy evening walk",
],
}
)
def estimate_route(args: dict[str, Any]) -> str:
places = args["places"]
return as_json(
{
"route": places,
"total_transit_minutes": 55,
"note": "Keep West Lake first, move indoor after rain starts.",
}
)
tools = ToolRegistry(
[
Tool(
name="get_weather",
description="Get a simple weather forecast for a city and date",
handler=get_weather,
),
Tool(
name="search_places",
description="Find travel places based on city, interests, and constraints",
handler=search_places,
),
Tool(
name="estimate_route",
description="Estimate travel time for a short list of places",
handler=estimate_route,
),
]
)
model = MockLLM(
[
as_json(
{
"type": "tool",
"tool": "get_weather",
"args": {"city": "Hangzhou", "date": "tomorrow"},
}
),
as_json(
{
"type": "tool",
"tool": "search_places",
"args": {
"city": "Hangzhou",
"interests": ["tea", "local food", "easy walking"],
"constraint": "light rain after 15:00",
},
}
),
as_json(
{
"type": "tool",
"tool": "estimate_route",
"args": {
"places": [
"West Lake",
"China National Tea Museum",
"Hefang Street",
]
},
}
),
as_json(
{
"type": "final",
"answer": (
"Plan: West Lake in the morning, China National Tea Museum after "
"the rain starts, then Hefang Street for snacks. Pack an umbrella, "
"a light jacket, and comfortable shoes."
),
}
),
]
)
out = run_react(
model,
task=(
"Plan a relaxed one-day Hangzhou trip for tomorrow. "
"I like tea, local food, and easy walking. Tell me what to pack."
),
tools=tools,
limits=RunLimits(max_steps=6),
tracer=tracer,
)
print(out)
trace_path = tracer.export_jsonl(Path(".traces") / "21_react_loop.jsonl")
print(f"[trace] {trace_path}")
if __name__ == "__main__":
main()
运行:
uv run python examples/21_react_loop.py
这一次发生了什么
旅游助手不是一次性回答,而是:
- 调
get_weather:发现下午有雨。 - 调
search_places:按兴趣和天气找地点。 - 调
estimate_route:估路线顺序和耗时。 - 输出
final:给出计划和打包建议。
这就是 ReAct 风格的 Agent Loop。
为什么这是 Agent
关键不是工具数量,而是谁决定下一步:
| 形态 | 谁决定路径 |
|---|---|
| Workflow | Python 代码提前写死 |
| Agent Loop | 模型根据当前状态动态决定 |
旅游助手看到“下午下雨”后,下一步变成“找室内友好的地点”。这就是 Agent Loop 的价值。
但 Agent 不是终点
Agent Loop 会带来新问题:
- 它可能无限循环。
- 它可能选错工具。
- 它可能伪造工具结果。
- 它可能给出看似合理但实际很差的路线。
所以后面的模式才会出现:可靠性验证、检索、规划、多 Agent、权限护栏、评测。