step(prompt, *, schema, context=None, tools=())
Ask the model for one typed value. It can call tools along the way. You get back a validated instance of schema.
step docsBridle gives you three calls — step, branch, loop — that ask the model for a typed answer. Your code handles the rest: ifs, loops, function calls. No new framework to learn.
from bridle import agent, branch, loop, step
from pydantic import BaseModel
class Source(BaseModel):
url: str
summary: str
class Brief(BaseModel):
headline: str
body: str
@agent(input=str, output=Brief, model="claude-sonnet-4-6")
def brief_writer(topic: str) -> Brief:
sources = loop(
f"gather sources on {topic}",
schema=Source,
until=lambda acc: len(acc) >= 3,
)
if not branch("is the evidence sufficient?", context=sources):
return brief_writer(f"{topic} — go deeper")
return step("write the brief", schema=Brief, context=(topic, sources))
@agent declares what goes in and what comes out. The model's reply is validated against Brief before your function returns it.
loop calls the model until your Python predicate is true. Here it gathers sources until there are three.
branch returns a real Python value — a bool by default, or any Enum or Literal you pass. Use it in a normal if.
step(prompt, *, schema, context=None, tools=())
Ask the model for one typed value. It can call tools along the way. You get back a validated instance of schema.
step docsbranch(prompt, *, schema=bool, context=None)
Ask the model for a decision. Defaults to True or False. Pass an Enum or Literal for more than two answers. Use it in an if.
branch docsloop(prompt, *, schema, until, tools=(), max_iterations=32)
Call the model in a loop until your predicate returns true. Hits the cap and raises LoopExhaustedError if the predicate never holds.
loop docsWrap a call to change how it runs. Stack wrappers in any order — the result is still a call.
cache(retry(timeout(step("..."), seconds=10), attempts=3))