跳转至

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

这一次发生了什么

旅游助手不是一次性回答,而是:

  1. get_weather:发现下午有雨。
  2. search_places:按兴趣和天气找地点。
  3. estimate_route:估路线顺序和耗时。
  4. 输出 final:给出计划和打包建议。

这就是 ReAct 风格的 Agent Loop。

为什么这是 Agent

关键不是工具数量,而是谁决定下一步:

形态 谁决定路径
Workflow Python 代码提前写死
Agent Loop 模型根据当前状态动态决定

旅游助手看到“下午下雨”后,下一步变成“找室内友好的地点”。这就是 Agent Loop 的价值。

但 Agent 不是终点

Agent Loop 会带来新问题:

  • 它可能无限循环。
  • 它可能选错工具。
  • 它可能伪造工具结果。
  • 它可能给出看似合理但实际很差的路线。

所以后面的模式才会出现:可靠性验证、检索、规划、多 Agent、权限护栏、评测。

下一步可以读 ReAct 模式页,再去 选择模式 看完整地图。