Skip to content

05: Agent Loop

Now we finally need an agent loop: the model chooses the next action, Python executes it, appends the result, and the model decides again.

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()

Run:

uv run python examples/21_react_loop.py

The travel assistant checks weather, searches places, estimates a route, and only then returns a final plan.

The key difference:

Shape Who controls the path
Workflow Python code predefines it
Agent Loop The model decides from current state

Next: ReAct and Choose a Pattern.