Structured Output and Loop State Management
The Risk of Free-Text Parsing
Section titled “The Risk of Free-Text Parsing”When an agentic loop passes output from one stage to the next, and that output is free text, parsing is required. But free-text parsing is brittle. If the model expresses the same information in a slightly different format, parsing fails — and that failure can either halt the loop or drive it in the wrong direction.
Free-Text Parsing Failure Scenario───────────────────────────────────────────────────────────Expected output: "Result: success, file: /tmp/output.txt, lines: 42"
Actual output (model may phrase it differently): "I have successfully written 42 lines to /tmp/output.txt." "Task complete. output.txt (42 lines)" "Done → /tmp/output.txt | lines=42"
If the parser only handles the first format → failure───────────────────────────────────────────────────────────Structured output solves this. Constraining the model to return a specific JSON structure via JSON Schema nearly eliminates parsing failures.
JSON Schema-Based Structured Output
Section titled “JSON Schema-Based Structured Output”Structured output is defined the same way as the input_schema in tool definitions. The model is instructed to return JSON conforming to a given schema and is constrained not to deviate from it.
This is illustrative pseudocode; actual API signatures differ.
# Define the output schematask_result_schema = { "type": "object", "properties": { "status": { "type": "string", "enum": ["success", "failure", "partial"], "description": "Completion status of the task" }, "output_path": { "type": "string", "description": "Absolute path of the generated file (null if none)" }, "line_count": { "type": "integer", "description": "Number of lines in the output file" }, "error_message": { "type": "string", "description": "Error message on failure (null on success)" } }, "required": ["status"]}
# Apply the schema to the model callresponse = model.generate( messages=messages, tools=tools, output_schema=task_result_schema, # enforce structured output)
# response.structured_output always conforms to the schemaresult = response.structured_outputif result["status"] == "success": process_file(result["output_path"])Components of Loop State
Section titled “Components of Loop State”An agentic loop must maintain state across iterations. That state falls into three layers:
Agent State Layers───────────────────────────────────────────────────────────┌─────────────────────────────────────────────────────────┐│ AGENT STATE ││ ││ ┌─────────────────────────────────────────────────┐ ││ │ Message History │ ││ │ [user, assistant, tool, assistant, tool, ...] │ ││ │ ↑ short-term memory inside the context window │ ││ └─────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────┐ ││ │ Metadata │ ││ │ - iteration_count: int │ ││ │ - start_time: datetime │ ││ │ - total_tokens: int │ ││ │ - goal: str (original goal — read-only) │ ││ └─────────────────────────────────────────────────┘ ││ ││ ┌─────────────────────────────────────────────────┐ ││ │ Completion Flags │ ││ │ - is_complete: bool │ ││ │ - final_result: str | None │ ││ └─────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────┘The most important invariant is: the goal field is never modified. As the context grows over many iterations, the original objective can be diluted or distorted — a failure mode called goal drift. Preserving the original goal in a dedicated field and re-injecting it into the system prompt at each iteration significantly reduces this risk.
State-Passing Patterns
Section titled “State-Passing Patterns”Several patterns exist for carrying state between iterations, each with different trade-offs:
| Pattern | Mechanism | Advantage | Disadvantage |
|---|---|---|---|
| Full history | All messages in context | Simple to implement | Context grows linearly |
| Summary compression | Older messages replaced by summary | Saves context budget | Information loss in summary |
| Structured notes | Key facts saved to a JSON file | Selective, precise | Requires upfront schema design |
| Hybrid | Recent N messages + summary + structured notes | Balanced | More complex to implement |
Production systems most commonly use the hybrid approach: only the most recent messages stay in the context window; long-term state is stored in structured files outside the window.
Structured Output in Agent-to-Agent Communication
Section titled “Structured Output in Agent-to-Agent Communication”In multi-agent systems, one agent’s output becomes another agent’s input. This is where structured output becomes especially critical. When Agent A delegates a task to Agent B, defining both the task specification and the expected result format as JSON Schemas turns the interface between agents into a typed contract — unambiguous and machine-verifiable.
# Agent-to-agent communication specsubtask_spec = { "task_id": "analyze_security_001", "type": "security_analysis", "input": { "file_path": "/workspace/auth.py", "focus_areas": ["sql_injection", "auth_bypass"] }, "expected_output_schema": { "vulnerabilities": [ { "type": "string", "severity": "high|medium|low", "line": "integer", "description": "string", "recommendation": "string" } ], "overall_risk": "high|medium|low|safe" }}When spec and result format are defined in advance, the receiving agent can consume the delegated agent’s output directly, without any fragile string parsing. The next chapter assembles everything covered so far into a minimal working agent loop in under 50 lines of Python.
References
- Anthropic — Tool use documentation — accessed 2026-06-30
- Anthropic — Building Effective AI Agents — accessed 2026-06-30