Structured Output

Constrain agent responses to a JSON Schema and run policy checks on the result.

Overview

By default, an ArchAgents agent responds with free-form text. That's the right shape for conversation, support, and most chat-style interactions.

When you need the agent to produce data (a triage decision, a customer summary, a structured action plan), you want the response to conform to a known shape every time. That's what an AgentMessageSchema does. It's a JSON Schema that tells the LLM exactly which fields to produce, with which types, and which are required.

Use a structured message schema when:

  • the agent's output will be consumed by other code or systems
  • a downstream automation depends on specific fields being present
  • you want to enforce content policies on individual fields (field guards)
  • you want the response to be deterministic in shape, even if the values vary

Schemas don't replace prompts, they constrain them. Your routine still tells the agent what to do; the schema tells the platform what shape to enforce on the result.


A minimal example

kind: AgentMessageSchema
id: customer-summary
schema:
 type: object
 properties:
 summary:
 type: string
 sentiment:
 type: string
 enum: [positive, neutral, negative]
 next_action:
 type: string
 required: [summary, sentiment]

This config tells the platform: "When this schema is in effect, the agent must produce an object with at least summary and sentiment fields, where sentiment is one of three exact values."

Save it as a config and deploy it like any other config:

archagent describe configsamples agent_message_schema --to-file ./schemas/customer-summary.yaml
# edit the file
archagent validate configs -k agent_message_schema -f ./schemas/customer-summary.yaml
archagent deploy configs

Linking a schema to a routine

Reference the schema from an agent routine using structured_message_template_refs:

routines:
 - name: customer-triage
 handler_type: preset
 preset_name: do_task
 preset_config:
 instructions: |
 Read the customer's most recent message in the thread and summarize it.
 Decide the sentiment and propose the next action.
 structured_message_template_refs:
 - "#/schemas/customer-summary"
 status: active

When the routine runs, the agent generates a response that matches the schema. The platform validates the shape automatically and rejects malformed responses before they reach your other code.

You can attach more than one schema to a routine when the agent has several response modes. The agent picks the schema appropriate to what it's doing, and the platform validates against that one.


Inline vs. referenced schemas

For small, single-use schemas, declare them inline on the routine:

routines:
 - name: ticket-triage
 handler_type: preset
 preset_name: do_task
 structured_message_template_refs:
 - kind: AgentMessageSchema
 id: ticket-triage-output
 schema:
 type: object
 properties:
 priority:
 type: string
 enum: [low, medium, high, urgent]
 category:
 type: string
 required: [priority, category]
 status: active

For schemas you want to reuse across multiple agents or routines, deploy them as standalone configs and reference by ID:

structured_message_template_refs:
 - ref!: "#/schemas/customer-summary"

Inline is fine for one-off shapes. Use a deployed config when the schema is a contract that more than one place depends on.


Field guards

Schemas don't only validate shape, they can also enforce content policies on the resolved field values. Add field_guards to a schema to require that fields pass content checks before the response is accepted:

kind: AgentMessageSchema
id: customer-summary
schema:
 type: object
 properties:
 summary: { type: string }
 sentiment: { type: string, enum: [positive, neutral, negative] }
 required: [summary, sentiment]
field_guards:
 - kind: ContainsString
 fields: ["summary"]
 value: "CONFIDENTIAL"
 on_match: redact
 - kind: LLMJudge
 fields: ["summary"]
 prompt: >
 Does this summary mention any specific customer's full name
 or financial account number?
 on_match: reject

Guards can reject (block the response), redact (rewrite the field), or warn (record a violation but let it through). See Field Guards for the full guard catalogue and how to design good policies.


What the schema controls

A schema constrains the shape of the response, types, required fields, enums, nesting. It does not constrain the content unless you also attach field guards. The agent's instructions and tools still drive what the values mean; the schema makes sure those values arrive in a known structure.

For free-form fields (a string summary, a string description), the LLM still generates whatever text fits the prompt. Pair the schema with field guards when content rules matter.


Validation

Validate schemas before deploying them:

archagent validate configs -k agent_message_schema -f ./schemas/customer-summary.yaml

The validator checks:

  • the schema is a valid JSON Schema
  • all referenced field paths in any attached field guards exist
  • guard configs are valid for their kind

The portal also validates schemas when you save them through the visual editor.


Best practices

  1. Make the schema as tight as the work requires. A schema with five required fields is easier to consume than one with twenty optional ones.
  2. Use enums for categorical fields. priority: low | medium | high is much more useful than priority: string with conventions in the prompt.
  3. Pair structured output with field guards when the response will be shared with humans or external systems. The schema enforces shape; guards enforce content.
  4. Reuse schemas across routines when the same response shape applies in more than one place. Deploy once, reference by ID.
  5. Test schemas with real prompts. Run the routine in a sandbox and inspect the resulting messages to confirm the agent is producing useful values, not just the right shape.

Where to go next

  1. Field Guards: content policies that run on schema fields.
  2. Agents: how routines reference schemas.
  3. Activity Feed: where validation failures and guard violations are recorded.
  4. Configs: the file-backed deploy workflow for schemas.