Skip to content

02: Structured Output

Free-form text is pleasant to read, but hard for code to consume. A travel plan often needs a stable shape.

from __future__ import annotations

from pathlib import Path
from typing import Any

from agent_patterns_lab.runtime import Message, MockLLM, SchemaValidationError, Tracer, structured_complete


def main() -> None:
    tracer = Tracer()

    model = MockLLM(
        [
            '{"city":"Hangzhou","items":["West Lake","Tea Museum"]}',
            '{"city":"Hangzhou","morning":"West Lake","afternoon":"China National Tea Museum","evening":"Hefang Street","packing":["umbrella","light jacket","comfortable shoes"]}',
        ]
    )

    messages = [
        Message(
            role="system",
            content=(
                "Return ONLY JSON with keys: city, morning, afternoon, evening, packing. "
                "packing must be a list of strings."
            ),
        ),
        Message(role="user", content="Create a one-day Hangzhou itinerary."),
    ]

    def parse_itinerary(value: Any) -> dict[str, Any]:
        if not isinstance(value, dict):
            raise SchemaValidationError("expected a JSON object")
        required = ["city", "morning", "afternoon", "evening", "packing"]
        for key in required:
            if key not in value:
                raise SchemaValidationError(f'missing key "{key}"')
        if not isinstance(value["packing"], list) or not all(isinstance(x, str) for x in value["packing"]):
            raise SchemaValidationError('"packing" must be a list of strings')
        return value

    itinerary = structured_complete(
        model,
        messages,
        parser=parse_itinerary,
        schema_hint='{"city":"...","morning":"...","afternoon":"...","evening":"...","packing":["..."]}',
        tracer=tracer,
    )

    print(itinerary)
    trace_path = tracer.export_jsonl(Path(".traces") / "10_structured_output.jsonl")
    print(f"[trace] {trace_path}")


if __name__ == "__main__":
    main()

Run:

uv run python examples/10_structured_output.py

structured_complete(...) extracts JSON, validates it with a parser, and retries with a repair prompt when validation fails.

This gives code a reliable object. It does not guarantee the facts are true.

Next: 03: Tool Calling.