Why LazyBridge¶
Five things LazyBridge does differently — each grounded in code, not marketing.
1. Recursive composition¶
In LazyBridge, Plan is an engine. An Agent(engine=Plan(...)) is a valid
Tool. That means pipelines nest with no special syntax at any depth — you
use the same tools=[...] parameter whether you're adding a plain function or
a ten-step sub-pipeline.
from lazybridge import Agent, LLMEngine, Plan, Step, Session, from_step
search = Agent(engine=LLMEngine("gpt-5.4-mini"), name="search")
summarise = Agent(engine=LLMEngine("gemini-2.5-pro"), name="summarise")
writer = Agent(engine=LLMEngine("claude-sonnet-4-6"), name="write")
research = Agent(
engine=Plan(Step("search"), Step("summarise")),
tools=[search, summarise], name="research",
)
article = Agent(
engine=Plan(Step("research"),
Step("write", context=from_step("research"))),
tools=[research, writer], session=Session(),
)
print(article("AI agents in 2026").text())
Cost, token counts, and OpenTelemetry spans roll up automatically across all
levels via Envelope.metadata.nested_*. You pay no composition tax.
Deep dive: Layered composition
2. Plans fail at construction¶
Other frameworks surface wiring errors at runtime — after spending money on
LLM calls. LazyBridge raises PlanCompileError the moment you construct a
Plan, before any inference happens.
from lazybridge import Agent, LLMEngine, Plan, Step, from_step
writer = Agent(engine=LLMEngine("claude-sonnet-4-6"), name="write")
# This raises PlanCompileError immediately — "search" is not in tools=[]
plan = Plan(
Step("search"),
Step("write", context=from_step("search")),
)
The compiler catches:
- Duplicate step names
- Tools referenced in
Step(target=...)that are not intools=[] - Forward references to steps not yet declared
- Sentinels (
from_step,from_parallel,from_parallel_all) pointing at incompatible neighbours in the same parallel band from_parallel_allon a step that is not a band-start- Type drift between consecutive steps (structural mismatch)
- Malformed
routes=/routes_by=declarations - Invalid
after_branches=references
All before a single token is generated.
Deep dive: Plan guide
3. One contract for everything¶
Tool.wrap() accepts anything that exposes a useful capability. There is one
interface whether the capability is a function, an agent, a Plan-backed
pipeline, an MCP server, or a native provider tool:
| Capability | How it enters | What the agent sees |
|---|---|---|
| Python function | Tool.wrap(fn) or tools=[fn] |
Tool |
Agent |
agent.as_tool() or tools=[agent] |
Tool |
Agent(engine=Plan) |
tools=[pipeline] |
Tool |
| MCP server | tools=[MCP.stdio(...)] |
Tool |
| Native provider tool | tools=[NativeTool(...)] |
Tool |
| Guarded variant | agent.as_tool(verify=judge) |
Tool |
The LLM engine sees the same schema regardless of what backs the tool. Swap a stub function for a full sub-pipeline without touching the outer agent.
Deep dive: Everything is a tool
4. Quality control at every node¶
Structured output, runtime validation, and cross-model verification compose at any level of the pipeline — not just at the top.
from pydantic import BaseModel
from lazybridge import Agent, LLMEngine
class Report(BaseModel):
title: str
summary: str
word_count: int
judge = Agent(engine=LLMEngine("gemini-2.5-pro"), name="judge")
writer = Agent(
engine=LLMEngine("claude-sonnet-4-6"),
output=Report, # schema-validated; re-prompts on failure
output_validator=lambda r: r.word_count > 50, # runtime check
verify=judge, # cross-model quality gate
max_verify=3, # up to 3 retry cycles
name="write",
)
output= validates the schema and re-prompts automatically on parse failure.
output_validator= runs an arbitrary Python check after parsing. verify=
passes the output to a second agent for quality assessment; feedback flows
via context, never onto the original task, and loops up to max_verify times.
All three stack.
Deep dive: verify= guide
5. Observability is native¶
Session and event logging are on by default. OpenTelemetry export with
GenAI semantic conventions (gen_ai.* attributes) requires one line:
from lazybridge import Agent, LLMEngine, Session
from lazybridge.ext.otel import OTelExporter
agent = Agent(
engine=LLMEngine("claude-sonnet-4-6"),
session=Session(exporters=[OTelExporter()]),
)
Every run emits spans carrying gen_ai.system, gen_ai.request.model,
gen_ai.usage.input_tokens, gen_ai.usage.output_tokens, nesting_level,
and parent-child span links. Cost rolls up across nested agents.
SQLite-backed Store gives you a local event log with back-pressure
policies and batching. Both are opt-out, not opt-in.
Deep dive: Session guide
Next steps¶
Quickstart → Comparison with other frameworks Layered composition deep-dive