Design your output schemas | Diaphora Docs | Diaphora

Design your output schemas

A session's schema is a contract: it's the exact JSON shape the model must return, and it's what every downstream session and your application reads. A good schema makes the model more accurate (it knows precisely what to produce) and makes the blueprint's output easy to consume. This guide covers the full syntax and the design habits that keep schemas clean.

Prerequisites

The basics

A schema block lists fields and their types:

schema {
    summary:   string
    keyPoints: string[]
    published: bool
}

Syntax rules:

Comments are instructions, not decoration

This is the highest-leverage habit in the whole language. Inline # comments on schema fields are read by the model and used as field descriptions:

schema {
    summary:   string    # one concise paragraph, no bullet points
    sentiment: string    # exactly one of: positive, neutral, negative
    score:     int       # confidence 0-100
}

Those comments meaningfully steer the output. Always add one to every field unless the name is completely self-explanatory — they're the cheapest quality win available.

Keep the root flat

The session name is already the outer wrapper for its output. A session called overview publishes under context.overview, so wrapping the fields in another overview object just creates redundant nesting:

# AVOID — produces context.overview.overview.summary
schema {
    overview: {
        summary:   string
        keyPoints: string[]
    }
}

# PREFER — produces context.overview.summary
schema {
    summary:   string
    keyPoints: string[]
}

Nest only when the data genuinely has sub-structure:

# FINE — metadata is a real sub-object
schema {
    summary:  string
    metadata: {
        author:    string
        createdAt: string   # ISO 8601 timestamp
    }
}

Scalar and array shorthand

When a session's whole output is a single value or a flat list, skip the block and declare the type directly:

schema string[]     # the output is a list of strings
schema int          # the output is a single integer

This pairs naturally with iterate: an iterating session runs once per element and collects the results into an array, so its schema must be an array, either schema type[] or a block with []:

session("elaborate",
        after="gather",
        iterate="context.gather.keyPoints") {
    - Expand the key point "{{ .it }}" into a paragraph.
    schema string[]     # array required when iterate is set
}

Reuse shapes with components

When the same structure appears in more than one session, define it once in a root-level components block and reference it with $Name:

components {
    schema("SourceRef") {
        id:    string   # unique identifier of the source
        title: string   # human-readable title
        url?:  string   # optional URL; omit if unavailable
    }
}

session("gather") {
    - Extract the sources used.
    schema {
        summary: string
        sources: $SourceRef[]   # reuse the component as an array
    }
}

Component schemas follow the exact same syntax as session schema: fields, optionals, comments, nesting. Reference one anywhere a type is expected: sources: $SourceRef[] or primary: $SourceRef.

Sessions without a schema

A schema is optional. Omit it and the session still runs — its output is just the model's raw text response instead of a structured object:

parameter("query", type=string, title="Search Query")

session("search_web") {
    use search

+ Search the internet for information about: {{ .params.query }}

- Provide a comprehensive answer based on the search results.
}

The session's result lands in context.search_web (and in the blueprint output) as a single string — the model's free-form answer verbatim:

{
  "search_web": "Spain won the 2026 FIFA World Cup, held across Canada, Mexico, and the United States, defeating Argentina 1–0 in the final. Ferran Torres scored the winning goal in the 106th minute of extra time at New York/New Jersey Stadium on July 19, 2026."
}

Skip the schema when the session's job is to produce prose for a person to read, or when a downstream session will consume the text as free-form input. Add a schema as soon as you need to reference specific fields (context.search_web.answer), enforce a shape, or hand structured data to another tool — a downstream session can't reach into a raw string.

Common mistakes

Mistake Fix
Wrapping fields in a top-level object named after the session Define fields flat at the root — the session name is the wrapper
Quoting field names Use unquoted identifiers: summary: string
A non-array schema on an iterate session Use schema type[] or schema { ... }[]
Skipping field comments Add a # description to every non-obvious field, the model reads them
Referencing $Name with no components block Define the type in a root-level components { } block

Next steps

Shape a session's typed output — field syntax, comments the model reads as instructions, flat-vs-nested structure, scalar shorthand, and reusable $Component refs.