Branches
A step constrained to a single typed decision. The conditional in your if statement.
branch is a step shaped for one job: produce one decision, no tools. The default schema is bool, which makes it drop straight into an if.
Signature
from bridle import branch
branch(
prompt: str,
*,
schema: type[T] = bool,
context: Any = None,
label: str | None = None,
) -> TReturns a Call typed as T. Resolves on truthiness, attribute read, or bridle.resolve.
The common case: a yes/no
if branch("is the evidence sufficient?", context=sources):
return write_brief(sources)
return gather_more(sources)The model produces a single bool. The if resolves the call as part of evaluating the condition — no bridle.resolve needed.
Multi-way: enums and Literal
For more than two outcomes, pass an Enum or a Literal[...]:
from enum import Enum
class Verdict(Enum):
APPROVE = "approve"
REJECT = "reject"
ESCALATE = "escalate"
verdict = branch("decide on this case", schema=Verdict, context=case)
match verdict:
case Verdict.APPROVE: return approve(case)
case Verdict.REJECT: return reject(case)
case Verdict.ESCALATE: return escalate(case)Use enums when the values are referenced from elsewhere in the codebase. Use Literal[...] when the set is small and local:
from typing import Literal
priority: Literal["low", "med", "high"] = branch(
"rank the priority",
schema=Literal["low", "med", "high"],
context=ticket,
)Why not just step?
Three reasons branch exists separately.
Tools are forbidden. A decision should not need to call out — if the call needs evidence, gather it first, then branch over the result. The signature enforces this.
One-shot, no schema-retry stew. A bool is hard to get wrong. Branches almost never trip the schema-retry path.
The trace reads better. A call_kind: "branch" event signals "decision point" without you having to label it.
Pitfalls
Putting the gathering inside the branch. Don't ask branch "find sources, then decide." It cannot — branch has no tools. Run a step to produce the evidence, then a branch to read the verdict.
Using branch with a complex schema. A schema with many fields belongs in a step. branch is for one decision; if the result has structure, it isn't a branch.
Reading the result without using it as a condition. branch returns a Call. Inside an if, that's fine. Anywhere else, resolve explicitly:
verdict = bridle.resolve(branch("decide", schema=Verdict, context=case))A complete example
import bridle
from bridle import agent, branch, step
from bridle.models.anthropic import install
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 cautious_brief(topic: str) -> Brief:
sources: list[Source] = bridle.resolve(step(
"find three reputable sources",
schema=list[Source],
context=topic,
))
if not branch("is the evidence sufficient?", context=sources):
# Recurse with a sharper prompt instead of writing on weak ground.
return cautious_brief(f"{topic} — find better-documented angles")
return step("write the brief", schema=Brief, context=(topic, sources))
install()
brief = bridle.resolve(cautious_brief("the weather on Mars"))
print(brief.headline)