# Return structured output

Validate an <Agent /> result with a Standard Schema and use the typed value in a later AML step.
Canonical: https://agent-markup-language.com/docs/cookbook/structured-output/
Documentation index: https://agent-markup-language.com/docs/
Complete documentation: https://agent-markup-language.com/docs/llms.txt

**Deterministic**

## Goal

Turn an [`<Agent />`](https://agent-markup-language.com/docs/reference/primitives/agent/) response into a typed finding instead of passing unvalidated prose between workflow stages. This recipe follows the maintained [`structured.tsx`](https://github.com/we-are-singular/aml/blob/main/examples/src/core/structured.tsx) example.

## Prerequisites

- Node.js `>=26`;
- `@aml-jsx/sdk` and `zod` installed;
- no model credentials or network access for the deterministic example.

## Complete source

```tsx
import { Agent, AmlRuntime, evaluate } from "@aml-jsx/sdk"
import { DeterministicAgentProvider } from "@aml-jsx/sdk/testing"
import { z } from "zod"

const Finding = z.object({
  severity: z.enum(["low", "high"]),
  summary: z.string(),
})

const provider = new DeterministicAgentProvider({
  respond(request) {
    if (request.output?.type === "json") {
      return {
        structured: {
          severity: "high",
          summary: "authorization is checked after mutation",
        },
        text: "",
      }
    }

    return { text: `synthesized:` + request.prompt }
  },
})

async function Review() {
  const finding = await evaluate(<Agent provider={provider}>Inspect the change.</Agent>, Finding)

  return (
    <Agent provider={provider}>
      Explain this {finding.severity} finding: {finding.summary}
    </Agent>
  )
}

const result = await new AmlRuntime().evaluate(<Review />)
console.log(result)
```

## Run it

Save the complete source as `recipe.tsx` in a project configured as shown in [Getting started](https://agent-markup-language.com/docs/getting-started/), then run exactly that file:

```sh title="Terminal"
npx vite-node recipe.tsx
```

From an AML repository checkout, the smaller maintained example exercises the same structured-result contract:

```sh title="Terminal"
npm run example -- structured
```

## Expected output

```text
synthesized:Explain this high finding: authorization is checked after mutation
```

The deterministic provider returns a JSON-shaped value only when AML asks for JSON. The schema then validates that value before the typed `finding` is interpolated into the second `<Agent />`.

## Structured nested Agents

Use the Agent `schema` prop when a nested Agent should own validation without opening an imperative collection boundary:

```tsx
import { Agent, FollowUp } from "@aml-jsx/sdk"

const workflow = (
  <Agent provider={provider}>
    Specialist finding:
    <Agent provider={provider} schema={Finding}>
      Inspect the change.
      <FollowUp>Challenge the evidence, then submit the final finding.</FollowUp>
    </Agent>
    Synthesize the validated finding.
  </Agent>
)
```

The specialist's transformed result enters the parent prompt as canonical JSON text. FollowUps remain one ordered session; AML requests structured output only on the final authored turn. Use `evaluate(value, Finding)` instead when component code needs the schema-inferred value. The two forms are alternative schema owners and cannot be combined on one Agent.

## How it works

1. `evaluate(value, Finding)` requests structured output from the nearest Agent provider.
2. AML converts the application-owned schema to JSON Schema for the provider request. Zod is one implementation of the Standard Schema contract.
3. On the shared ACP path, the Agent submits candidates through AML's `aml_submit_result` MCP tool. AML immediately validates each candidate and returns validation errors to the Agent so it can correct the result in the same turn.
4. The first valid candidate is accepted. Later submissions are ignored. If the final authored turn omits a valid candidate, AML sends one repair prompt with the Tool instruction and complete JSON Schema; a second omission rejects the evaluation.
5. AML validates the accepted value at the application boundary before returning the schema's output type.
6. The second `<Agent />` remains text-producing. In this example, structured output applies to the specialist, not to the final response.

**Note — Structured output is a validation boundary**

A schema does not make an external model correct. It guarantees shape and validation behavior. Put semantic
checks—such as severity policy, allowed file paths, or required evidence—inside your schema or application code.

## Failure and security notes

- On the shared ACP path, invalid candidates are recoverable Tool errors. A final authored turn without an accepted candidate receives one schema-bearing repair prompt; missing output after that repair rejects the evaluation.
- After AML accepts the first valid candidate, repeated submissions cannot replace it. With tracing enabled, submissions appear as `agent.output` events with `invalid`, `accepted`, or `ignored` status.
- The repository example uses a deterministic provider; it does not prove that every Agent provider supports the same native structured-output mechanism.
- Do not treat a valid shape as authorization to perform an external side effect. Validate business rules before writing files, merging code, or sending messages.
- If you need structured output from a final `<FollowUp />`, remember that the final turn is the output boundary; earlier turns remain session inputs.

## API and source links

- [`evaluate`](https://agent-markup-language.com/docs/reference/runtime/)
- [`AmlModelSchema` and structured evaluation](https://agent-markup-language.com/docs/reference/runtime/#component-local-evaluation)
- [`<Agent />`](https://agent-markup-language.com/docs/reference/primitives/agent/)
- [Maintained source example](https://github.com/we-are-singular/aml/blob/main/examples/src/core/structured.tsx)
- [Zod](https://zod.dev/)
