A3S Docs
A3S CodeExamples

Structured Output

Generate schema-validated JSON objects with the generate_object tool.

Structured Output

The built-in generate_object tool asks the configured LLM for a JSON object, validates the response against a JSON Schema you supply, and returns the validated object only on a zero-exit result. Use it whenever you need machine-readable results: extraction, classification, config generation, or feeding another program.

You can call it directly through session.tool('generate_object', ...). The tool result carries the validated object as JSON on result.output — parse it and read the object field. The same tool also supports agent-driven invocation, where the model decides to call it during a send.

Direct tool call

The simplest path: call generate_object directly, check the tool exit code, and parse the validated object out of the result.

import { Agent } from '@a3s-lab/code';

const agent = await Agent.create('agent.acl');
const session = agent.session('.');

const result = await session.tool('generate_object', {
  schema: {
    type: 'object',
    required: ['name', 'age', 'skills'],
    properties: {
      name: { type: 'string' },
      age: { type: 'integer', minimum: 0 },
      skills: {
        type: 'array',
        items: { type: 'string' },
        minItems: 1,
      },
    },
  },
  prompt: 'Extract: "Alice is 28, skilled in Rust, TypeScript, and Python."',
  schema_name: 'developer',
  mode: 'tool',
});

if (result.exitCode !== 0) {
  throw new Error(result.output);
}

const { object } = JSON.parse(result.output);
console.log(object);
// { name: "Alice", age: 28, skills: ["Rust", "TypeScript", "Python"] }

session.close();
import json
from a3s_code import Agent

agent = Agent.create('agent.acl')
session = agent.session('.')

result = session.tool("generate_object", {
    "schema": {
        "type": "object",
        "required": ["name", "age", "skills"],
        "properties": {
            "name": {"type": "string"},
            "age": {"type": "integer", "minimum": 0},
            "skills": {
                "type": "array",
                "items": {"type": "string"},
                "minItems": 1,
            },
        },
    },
    "prompt": 'Extract: "Alice is 28, skilled in Rust, TypeScript, and Python."',
    "schema_name": "developer",
    "mode": "tool",
})

if result.exit_code != 0:
    raise RuntimeError(result.output)

obj = json.loads(result.output)["object"]
print(obj)
# {"name": "Alice", "age": 28, "skills": ["Rust", "TypeScript", "Python"]}

session.close()

The validated value lives on the object key of the parsed output. When result.exitCode (Node) / result.exit_code (Python) is zero, fields declared in required have passed runtime validation. If the model cannot satisfy the schema after repair attempts, the tool reports a non-zero exit code.

Enum classification

Constrain a field to a fixed set with enum. This turns a free-form model classification into a schema-gated result.

const result = await session.tool('generate_object', {
  schema: {
    type: 'object',
    required: ['sentiment', 'confidence'],
    properties: {
      sentiment: { type: 'string', enum: ['positive', 'negative', 'neutral'] },
      confidence: { type: 'number', minimum: 0, maximum: 1 },
    },
  },
  prompt: 'Classify sentiment: "This is the worst product I have ever used."',
  schema_name: 'sentiment',
});

const { object } = JSON.parse(result.output);
console.log(object.sentiment, object.confidence); // "negative" 0.97
result = session.tool("generate_object", {
    "schema": {
        "type": "object",
        "required": ["sentiment", "confidence"],
        "properties": {
            "sentiment": {"type": "string", "enum": ["positive", "negative", "neutral"]},
            "confidence": {"type": "number", "minimum": 0, "maximum": 1},
        },
    },
    "prompt": 'Classify sentiment: "This is the worst product I have ever used."',
    "schema_name": "sentiment",
})

obj = json.loads(result.output)["object"]
print(obj["sentiment"], obj["confidence"])  # "negative" 0.97

Nested schemas and arrays

Schemas can nest objects and arrays to any depth, and the runtime validates the whole structure. This models real config files, manifests, or API payloads in one call.

const result = await session.tool('generate_object', {
  schema: {
    type: 'object',
    required: ['items'],
    properties: {
      items: {
        type: 'array',
        minItems: 3,
        maxItems: 5,
        items: {
          type: 'object',
          required: ['name', 'category'],
          properties: {
            name: { type: 'string' },
            category: { type: 'string', enum: ['fruit', 'vegetable', 'grain'] },
          },
        },
      },
    },
  },
  prompt: 'List 3 food items with their categories.',
  schema_name: 'food_list',
});

const { items } = JSON.parse(result.output).object;
console.log(items.length, items.map((i) => i.name));
result = session.tool("generate_object", {
    "schema": {
        "type": "object",
        "required": ["items"],
        "properties": {
            "items": {
                "type": "array",
                "minItems": 3,
                "maxItems": 5,
                "items": {
                    "type": "object",
                    "required": ["name", "category"],
                    "properties": {
                        "name": {"type": "string"},
                        "category": {"type": "string", "enum": ["fruit", "vegetable", "grain"]},
                    },
                },
            },
        },
    },
    "prompt": "List 3 food items with their categories.",
    "schema_name": "food_list",
})

items = json.loads(result.output)["object"]["items"]
print(len(items), [i["name"] for i in items])

Agent-driven invocation

You can also let the agent decide when to use structured output. Ask it to call generate_object during a send; it gathers context first, then emits the object.

const result = await session.send(
  'Use the generate_object tool to extract the following into an object ' +
    'with fields "title" (string), "year" (integer), "genre" (string): ' +
    'The movie "Inception" was released in 2010 and is a sci-fi thriller.'
);

console.log(`tool calls: ${result.toolCallsCount}, tokens: ${result.totalTokens}`);
result = session.send(
    'Use the generate_object tool to produce a JSON object with schema '
    '{"type":"object","required":["language","paradigm"],"properties":'
    '{"language":{"type":"string"},"paradigm":{"type":"string"}}} '
    'for: "Rust is a systems programming language with a focus on safety."'
)

print(f"tool calls: {result.tool_calls_count}, tokens: {result.total_tokens}")

Schema validation coverage

The built-in validator supports:

  • type (including nullable arrays like ["string", "null"])
  • required, properties, additionalProperties
  • enum, const
  • anyOf, oneOf
  • minLength, maxLength, pattern
  • minimum, maximum, exclusiveMinimum, exclusiveMaximum
  • minItems, maxItems, items
  • Nested object and array validation

Notes

  • The validated value is on the object key of the parsed result.output. Pass mode: 'tool' for the current cross-provider default, or mode: 'prompt' for a prompt-only fallback. auto, strict, and json currently resolve to tool in this runtime path.
  • List every field you depend on in required — the runtime enforces it, so missing or mistyped fields fail validation instead of silently returning partial data.
  • generate_object is a built-in tool registered independently of built-in skills.
  • Direct session.tool(...) calls are host control-plane calls. Use permissionPolicy when you let the model choose tools inside send / run / stream; use host authorization before direct SDK calls.

A runnable version ships at crates/code/sdk/node/examples/basic/test_generate_object.ts and crates/code/sdk/python/examples/test_generate_object.py.

On this page