Your first agent
From zero to a running, typed, traceable agent in five minutes.
You will write an agent that takes a topic and returns a structured Brief. It uses two primitives — step and branch — and the @agent decorator.
Prerequisites
- Bridle installed (
pip install bridle-ai) ANTHROPIC_API_KEYset in your environment
If you have not done either, see install.
The whole agent
import bridle
from bridle import agent, branch, step
from bridle.models.anthropic import install
from pydantic import BaseModel
class Outline(BaseModel):
angle: str
points: list[str]
class Brief(BaseModel):
headline: str
body: str
@agent(input=str, output=Brief, model="claude-sonnet-4-6")
def brief_writer(topic: str) -> Brief:
outline = step(
"draft an outline for a 200-word brief",
schema=Outline,
context=topic,
)
if not branch("is the outline specific enough to write from?", context=outline):
outline = step(
"tighten the outline — concrete claims only",
schema=Outline,
context=outline,
)
return step(
"write the brief from the outline",
schema=Brief,
context=(topic, outline),
)
install()
brief = bridle.resolve(brief_writer("the weather on Mars"))
print(f"# {brief.headline}\n\n{brief.body}")Save it as first_agent.py, then:
python first_agent.pyYou should see a headline and a short body printed to stdout.
What just happened
Read the code top to bottom.
Schemas first. Outline and Brief are Pydantic models. They are the contract between your code and the model — the only shape values cross.
The @agent decorator. Marks brief_writer as a typed, traceable unit. input=str validates the first argument; output=Brief validates the return value; model="claude-sonnet-4-6" becomes the default for steps inside the body. See agents for the full surface.
Three primitives. step runs one model turn (or several, if it calls tools) until it produces a value matching the schema. branch is a step constrained to a single typed decision — bool by default. Pass either as a value into if or as the input to the next call. See calls for why this works.
bridle.resolve(). Forces a Call to evaluate. You can also evaluate by reading any attribute or using it in a boolean — but resolve is explicit and reads well at the call site.
What you got for free
-
Typed values.
outlineis anOutlineinstance, not a dict.briefis aBrief. -
Validation and recovery. If the model produces malformed output, Bridle feeds the validation error back as a corrective tool result and retries (up to three times by default).
-
A trace. Every call, every model turn, every retry was captured. Inspect it:
from bridle import Trace from bridle.trace import set_active_trace trace = Trace() set_active_trace(trace) bridle.resolve(brief_writer("the weather on Mars")) print(trace.to_jsonl())See traces.