Structured Output
The platform supports enforcing a structured JSON schema on agent responses. When an agent blueprint declares output_schema, every invocation produces a response that validates against a Pydantic model — or the call fails with a clear error rather than returning freeform text.
How it works
Structured output uses the Strands native forced-tool path on all four providers. The Strands SDK passes a structured_output_model kwarg to the Agent constructor, which internally uses a forced tool-call pattern to make the model produce output conforming to the schema.
This is provider-agnostic: the same path works for bedrock, anthropic, litellm, and vertex.
Minimum Strands version
The native forced-tool path requires strands-agents>=1.41.0. Earlier versions of the Strands SDK had a forced-tool/finish_reason incompatibility with OpenAI-compatible providers: when the model returned a stop_reason: end_turn signal without invoking the forced tool, Strands raised StructuredOutputException on the second retry attempt instead of recovering gracefully. These incompatibilities were resolved in strands-agents 1.41.0. The agent-core dependency constraint (strands-agents[otel]>=1.0.0,<2) satisfies this automatically when installed fresh from a current index — but if you pin or vendor Strands at an older version you will hit these issues.
Prior to
strands-agents 1.41.0, a workaround hook calledStructuredOutputEnforcer(backed by theinstructorlibrary) was used to handle non-Bedrock providers. That workaround was removed in full when the upstream bugs were resolved. Thestructured_output_enforcer.pyfile still exists in the codebase as historical reference but is not wired by the loader and has no effect.
Blueprint configuration
Declare output_schema at the top level of the agent blueprint:
id: extraction-agent
name: Extraction Agent
version: "1.0.0"
prompt_ref: extraction-agent-system-v1
model:
provider: litellm
model_id: claude-sonnet-4-6
temperature: 0.1
max_tokens: 4096
base_url: https://your-litellm-proxy.example.com
api_key_env: LITELLM_API_KEY
output_schema: ExtractionResult # Name of a Pydantic model in your schema_registry
The value of output_schema is a string key looked up in the schema_registry dict passed to BlueprintLoader:
from pydantic import BaseModel
from agent_core.blueprints.loader import BlueprintLoader
class ExtractionResult(BaseModel):
entities: list[str]
summary: str
confidence: float
loader = BlueprintLoader(
blueprints_dir="blueprints/",
schema_registry={
"ExtractionResult": ExtractionResult,
},
)
If output_schema is set but schema_registry is None or does not contain the named key, the loader raises BlueprintLoadError at startup.
Runtime behavior
When output_schema is configured:
- The loader resolves the Pydantic model class from
schema_registry. - The resolved class is passed as
structured_output_modelto the StrandsAgentconstructor. - The Strands agent forces the model to produce a JSON response matching the schema’s tool-call format.
- On success,
agent_result.outputis an instance of the Pydantic model. - On failure (the model does not conform after retries), the agent raises
StructuredOutputException.
Example: full structured output agent
id: classifier-agent
name: Classifier Agent
version: "1.0.0"
prompt_ref: classifier-system-v1
model:
provider: litellm
model_id: ${ACTIVE_MODEL:-claude-sonnet-4-6}
temperature: 0.0
max_tokens: 2048
base_url: https://your-litellm-proxy.example.com
api_key_env: LITELLM_API_KEY
output_schema: ClassificationResult
gateway:
auth_type: aws_iam
runtime:
type: agentcore
max_iterations: 3 # Structured output agents typically need fewer iterations
idle_timeout_minutes: 10
network_mode: PRIVATE
protocol: HTTP
execution_modes:
simulation: true
staging: true
production: true
With the corresponding Pydantic model registered in schema_registry:
from pydantic import BaseModel
class ClassificationResult(BaseModel):
category: str
subcategory: str | None
confidence: float
reasoning: str
Provider notes
Bedrock
The Strands forced-tool path was originally designed around Bedrock’s Converse API and is fully reliable. No additional configuration is needed.
Anthropic
Works via the native forced-tool path. Temperature is not forwarded to the AnthropicModel constructor (it is a schema-required field but not passed through for this provider). For structured output with Anthropic, temperature in the blueprint has no effect on the enforced output call.
LiteLLM
Works via the native forced-tool path for any OpenAI-compatible endpoint. Ensure your proxy supports tool-call / function-calling mode (tool_choice="required") — most production-grade proxies (LiteLLM server, vLLM with OpenAI adapter, Ollama ≥ 0.3) do.
Vertex (Gemini)
Works via the native forced-tool path. max_tokens and temperature are not forwarded to GeminiModel, so schema enforcement is not affected by those blueprint fields.
The StructuredOutputEnforcer — historical context
Before strands-agents 1.41.0, the upstream forced-tool path had reliability issues for non-Bedrock providers: when the model returned stop_reason: end_turn without invoking the forced tool, Strands raised StructuredOutputException on the second retry attempt instead of recovering.
The StructuredOutputEnforcer hook (backed by the instructor library) was introduced as a workaround: it skipped the Strands forced-tool path entirely, ran the agent normally, and post-processed the final message through instructor with automatic retry-on-validation-error.
With the forced-tool/finish_reason incompatibilities resolved in strands-agents 1.41.0, the enforcer was removed from the loader wiring. The file core/src/agent_core/hooks/structured_output_enforcer.py remains in the repository as a historical reference but is dead code — it is never registered by the loader and has no effect on running agents.
If you see references to
StructuredOutputEnforcerin older documentation, blog posts, or community discussions about this platform, those references describe behavior that no longer applies. The current behavior is:output_schema→ Strands nativestructured_output_model→ works on all four providers withstrands-agents>=1.41.0.
Troubleshooting
BlueprintLoadError: schema_registry does not contain 'MySchema' The output_schema value in the blueprint does not match any key in the schema_registry dict passed to BlueprintLoader. Check the casing and spelling of both.
StructuredOutputException: The model failed to invoke the structured output tool The model did not conform to the schema after the configured number of retries. Consider: lowering temperature (closer to 0.0 tends to improve schema conformance), simplifying the Pydantic schema, or adding explicit JSON formatting instructions to the system prompt.
Strands version too old If you see the enforcer-related error paths or repeated StructuredOutputException on non-Bedrock providers, check pip show strands-agents. Upgrade to >=1.41.0.
Related: LiteLLM — proxy configuration for OpenAI-compatible endpoints. Agent Blueprint — output_schema field.