Blank¶
The smallest agent the template supports: one output type, one prompt file, no tools.
Blank is a starting point, not a pattern. It is one agent module with a typed output model, a dependencies dataclass, a prompt in agent/prompts/<name>.txt, and a run_* function that returns a RunResult, wrapped in the template's guardrails: limits on requests and tokens, and an error when a provider blocks a response. Unlike the other examples, every symbol in it is renamed to the name you choose: --name newsletter gives you newsletter_agent, NewsletterOutput, NewsletterDeps and run_newsletter_agent. You also get a smoke test and an eval starter for it, as with any agent you add.
Use it when
- You know what you want to build and none of the patterns matches its shape yet.
- You would rather grow an agent from nothing than delete what you don't need from a worked example.
- You want the scaffolding (tests, evals, limits, tracing) around an agent you write yourself.
Look elsewhere when
- One of the patterns fits. Start from it.
singleis the same agent with a worked example and recipes for tools, dynamic instructions and multi-turn history, and Which pattern should I use? helps you choose.
What it shows
- A structured output type (
BlankOutput) and an injectable dependencies dataclass (BlankDeps) - Instructions loaded from a prompt file (
prompts/blank.txt) USAGE_LIMITSon every run, and theRaiseContentFilterErrorcapability- A commented recipe for dynamic instructions, for when the prompt depends on runtime state
See it run: sample_run.md is a recorded run against a real model: what each agent was asked, which tools it called, and what it returned.
Source¶
All of it is in examples/blank/.
"""Blank agent — an empty starting point with one agent, one output type, one prompt.
To use:
1. Define your output type (or use str for unstructured output)
2. Set your instructions in agent/prompts/blank.txt
3. Add tools if needed
4. Call run_blank_agent() and read `.output` from the RunResult it returns
"""
from __future__ import annotations
from dataclasses import dataclass
from pydantic import BaseModel
from pydantic_ai import ( # noqa: F401 — RunContext used in commented tool example below
Agent,
RunContext,
)
from pydantic_ai.capabilities import RaiseContentFilterError
from pydantic_ai.usage import UsageLimits
from agent.config import settings
from agent.logging import agent_label, configure_logging, get_logger
from agent.prompts.templates import load_prompt
from agent.runs import Flow, RunResult
logger = get_logger(__name__)
LABEL = agent_label(__name__) # names this agent's run spans in Logfire traces
# Guardrail against runaway agentic loops. A run that exceeds any limit
# raises UsageLimitExceeded instead of silently burning tokens. Tune per task:
# request_limit caps model round-trips (each tool-call iteration is one
# request), total_tokens_limit caps overall tokens. Set AGENT_COST_LIMIT (USD) to
# add a spend cap — optional, off by default, and only useful for models with
# known pricing (see Settings.cost_limit).
USAGE_LIMITS = UsageLimits(
request_limit=10, total_tokens_limit=100_000, cost_limit=settings.cost_limit
)
# --- Output type ---
# Replace with your actual output schema, or use str for unstructured output.
class BlankOutput(BaseModel):
"""Replace with your actual output schema."""
result: str
# --- Dependencies ---
# Use a dataclass to inject runtime dependencies (DB connections, API clients, etc.)
# Remove if this agent needs no external dependencies.
@dataclass
class BlankDeps:
"""Runtime dependencies injected into the blank agent."""
# example_client: SomeAPIClient # Add your dependencies here
pass
# --- Agent definition ---
blank_agent: Agent[BlankDeps, BlankOutput] = Agent(
settings.model,
name=LABEL,
output_type=BlankOutput,
deps_type=BlankDeps,
# Fail fast when the provider filters a response, instead of retrying a
# refused request or returning partial text.
capabilities=[RaiseContentFilterError()],
instructions=load_prompt("blank"), # loads agent/prompts/blank.txt
)
# --- Tools ---
# Add tools here. See agent/tools/example.py for the full pattern.
# @blank_agent.tool
# async def my_tool(ctx: RunContext[BlankDeps], query: str) -> str:
# """Tool description — this docstring is sent to the LLM."""
# return "result"
# --- Dynamic instructions (optional) ---
# Use @blank_agent.instructions for instructions that depend on runtime state.
# @blank_agent.instructions
# async def dynamic_instructions(ctx: RunContext[BlankDeps]) -> str:
# return f"Today is {date.today()}."
async def run_blank_agent(user_input: str, deps: BlankDeps | None = None) -> RunResult[BlankOutput]:
"""Run the blank agent with the given user input.
Args:
user_input: The user's message or task description.
deps: Runtime dependencies. Created with defaults if not provided.
Returns:
A RunResult: `.output` is the validated BlankOutput, `.usage` the total usage, and
`.steps[0].result` the native Pydantic AI result (messages, run id, ...).
"""
if deps is None:
deps = BlankDeps()
logger.info("Running blank agent", extra={"user_input": user_input})
flow = Flow(USAGE_LIMITS)
result = await flow.run(blank_agent, user_input, deps=deps)
logger.info("Blank agent run complete", extra={"output": result.output})
return flow.finish(result.output)
if __name__ == "__main__":
import asyncio
configure_logging()
result = asyncio.run(run_blank_agent("Hello, what can you do?"))
print(result.output)
title = "Blank"
pattern = "blank"
summary = "An empty agent: one output type, one prompt, no tools. Start here and build from scratch."
smoke_input = "Hello, what can you do?"
templated = true # `blank` / `Blank` in the source are replaced by the name you choose
[entrypoint]
deps = "BlankDeps"
run = "run_blank_agent"
"""Live check: the blank agent runs against the real model. Run with `pytest -m eval`."""
import pytest
from evals.trace import traced_run
from examples.blank import agent as module
from examples.live_support import assert_every_agent_ran, run_as_script
pytestmark = pytest.mark.eval
async def test_the_agent_answers():
traced = await traced_run(module.run_blank_agent, "Hello, what can you do?")
assert isinstance(traced.result.output, module.BlankOutput)
assert traced.result.output.result.strip()
assert [step.agent for step in traced.result.steps] == ["blank"]
assert traced.result.usage.requests >= 1 # more if the model retried its output
assert_every_agent_ran(module, traced.agents_ran)
async def test_the_demo_script_runs():
assert "result=" in await run_as_script("examples.blank.agent")
Recorded run · gemini-3.1-flash-lite · 1 step · $0.0003
Recorded 2026-10-07 with google:gemini-3.1-flash-lite · 1 step · 261 tokens · $0.0003 · 1.4 s.
Model output varies between runs. Regenerate with uv run python scripts/record_example.py blank.
Input
Hello, what can you do?
Steps
1. blank
261 tokens · $0.0003
Prompt
Hello, what can you do?
Output
{
"result": "I am a versatile AI assistant. While I don't have a specific set task yet (I am currently a \"blank\" agent), I am capable of:\n\n* **Writing and Editing:** Drafting emails, essays, creative stories, or refining your existing text.\n* **Information and Analysis:** Answering questions, summarizing documents, or explaining complex topics.\n* **Problem Solving:** Helping with logic puzzles, brainstorming ideas, or providing step-by-step guidance.\n* **Coding/Technical Help:** Writing code, debugging, or explaining technical concepts.\n* **Organization:** Creating schedules, lists, or helping to structure your thoughts.\n\n**How can I help you today?** If you have a specific goal or task in mind, let me know, and I can adapt my approach to help you best."
}
Result
run_blank_agent(...).output
{
"result": "I am a versatile AI assistant. While I don't have a specific set task yet (I am currently a \"blank\" agent), I am capable of:\n\n* **Writing and Editing:** Drafting emails, essays, creative stories, or refining your existing text.\n* **Information and Analysis:** Answering questions, summarizing documents, or explaining complex topics.\n* **Problem Solving:** Helping with logic puzzles, brainstorming ideas, or providing step-by-step guidance.\n* **Coding/Technical Help:** Writing code, debugging, or explaining technical concepts.\n* **Organization:** Creating schedules, lists, or helping to structure your thoughts.\n\n**How can I help you today?** If you have a specific goal or task in mind, let me know, and I can adapt my approach to help you best."
}