Pydantic AI System Prompts: Instructions, Context, and Tests
Pydantic AI supports both static and dynamic system prompts. Static prompts set stable rules; dynamic prompts inject context at runtime. Here is how to define them, separate rules from request data, and test the behavior.
Updated 4 min read
Pydantic AI lets you set system prompts in two ways: as a static string in the Agent constructor, or as a decorated function that runs at the start of each request and can read from the current context. Knowing when to use each one, and how to separate stable rules from request data, makes the agent's behavior easier to test and easier to change.
Tools like fenbs store the project decisions that go behind those instructions, so when a system prompt encodes a business rule, the reason for that rule is recorded in one place any connected AI or team member can read.
How Pydantic AI handles system prompts
In Pydantic AI, system prompts accumulate: you can combine a static string with one or more dynamic functions, and all of them are joined before the model sees the message. This makes it straightforward to separate the stable part of the instruction (the role, the output format, the standing rules) from the dynamic part (the current user, the request context, the retrieved data).
The full documentation for Pydantic AI system prompts is at https://ai.pydantic.dev (opens in a new tab). Check that source before building on any behavior described here, as the API can change between releases.
Defining a static system prompt
Pass the system_prompt argument to the Agent constructor for instructions that never change between requests:
from pydantic_ai import Agent
agent = Agent(
'anthropic:claude-sonnet-4-6',
system_prompt=(
'You are a research assistant. '
'Answer only from the documents provided. '
'If the documents do not address the question, say so.'
),
)Use the static prompt for rules that are the same for every call: the agent's role, its output format, and any constraints that apply regardless of the request. Keep this part short and specific: every word here is included in every call.
Adding dynamic context at runtime
The @agent.system_prompt decorator marks a function that runs before each request and returns a string that is appended to the static prompt. The function receives a RunContext object, which provides type-safe access to the dependencies you defined for the agent.
from pydantic_ai import Agent, RunContext
from dataclasses import dataclass
@dataclass
class ResearchDeps:
user_name: str
available_documents: list[str]
agent = Agent(
'anthropic:claude-sonnet-4-6',
deps_type=ResearchDeps,
system_prompt='You are a research assistant. Answer only from the documents provided.',
)
@agent.system_prompt
def add_user_context(ctx: RunContext[ResearchDeps]) -> str:
docs = ', '.join(ctx.deps.available_documents)
return f'User: {ctx.deps.user_name}. Documents available: {docs}.'The dynamic function keeps request-specific facts out of the static prompt and makes them available only when they are relevant. The static prompt stays lean; the dynamic one adds only what this specific request needs.
Separating stable rules from request data
A common mistake is mixing standing rules and request data in the same prompt string. When the rules change, you have to find them inside a string that also contains runtime values. When the request data changes, you have to edit code that also holds the rules.
- Static prompt: role, output format, constraints that apply to all requests.
- Dynamic prompt: current user, session context, retrieved documents, request-specific scope.
- Dependencies: values the dynamic prompt reads from, typed as a dataclass for safety.
If a rule is a business decision, store the reason behind it in fenbs Decisions and rules so it is not buried in the code. The code enforces the rule; fenbs records why it was chosen and who approved it. When the rule changes, the decision is updated in one place rather than hunted across a codebase.
One practical test: if you removed a dynamic prompt function and the agent still behaved correctly on a sample request, the function is not adding anything. A function that always returns the same string should be moved to the static prompt. A function that reads from context and produces different output per request belongs in the decorator.
Testing system prompt behavior
Pydantic AI supports testing agents without live model calls using TestModel or FunctionModel. Use these to verify that system prompt content is present and that the right values are injected from context.
from pydantic_ai import models
def test_system_prompt_includes_user():
captured = []
def capture_prompt(messages, info):
captured.append(str(messages))
return models.ModelResponse.from_text('ok')
with models.override(capture_fn=capture_prompt):
agent.run_sync('summarize', deps=ResearchDeps(
user_name='Alice',
available_documents=['doc1', 'doc2'],
))
assert 'Alice' in captured[0]
assert 'doc1' in captured[0]Test each constraint in the static prompt separately. A constraint that works in a manual run can fail under edge-case inputs that you only discover in a test. Write at least one test per standing rule.
When a constraint in the static prompt appears to be ignored, check the dynamic functions first. A dynamic function that injects a contradicting instruction can override a static rule. Reading the accumulated prompt as a whole, before debugging the static part in isolation, often reveals the source of the conflict faster.
How to do this with fenbs
- Create a free account and add a project for the agent you are building.
- Add the agent's instruction template to Skills and prompts in fenbs so the team can see the current approved version.
- Record business rules that drive the static prompt in Decisions and rules in fenbs, with the reason and the person who approved them.
- Connect your AI app over MCP from Integrations so team members working on the project can read the current decisions before changing the prompt.
Keep the rules behind the prompt separate from the prompt itself
In fenbs, record the decisions behind each standing rule in the system prompt: what the rule is, why it was chosen, and who approved it. When the rule changes, update the decision and the code. Any connected team member or AI app reads the current decision before working on the prompt, so changes are made with the full context available.
Create a free accountFree for one person · No credit card · Works with Claude, ChatGPT, Cursor and more
Where to find the Pydantic AI documentation
The Pydantic AI documentation at https://ai.pydantic.dev (opens in a new tab) covers the current API for system prompts, RunContext, dependencies, and the test models. The framework updates regularly; check the changelog before relying on any specific method signature.
For general system prompt patterns that apply across frameworks, system prompt examples covers the instruction structures that produce reliable results regardless of which library you use.