Skip to content

Return structured output

Deterministic

Turn an <Agent /> response into a typed finding instead of passing unvalidated prose between workflow stages. This recipe follows the maintained structured.tsx example.

  • Node.js >=26;
  • @aml-jsx/sdk and zod installed;
  • no model credentials or network access for the deterministic example.
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)

Save the complete source as recipe.tsx in a project configured as shown in Getting started, then run exactly that file:

Terminal
npx vite-node recipe.tsx

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

Terminal
npm run example -- structured
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 />.

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

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.

  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.
  • 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.