Anatomy of a session | Diaphora Docs | Diaphora

Anatomy of a session

A session is the core unit of an FML blueprint. Each one runs its own LLM interaction with its own isolated context, and produces a single typed JSON object stored under context.<session_name>. A blueprint is just a set of sessions plus the declarations that wire them together.

This guide walks through every part of a session, in the order they appear in the body, and explains the one rule that trips people up most: the difference between a prePrompt and a prompt.

Prerequisites

The shape of a session

session("overview") {
    use search                       # 1. tools this session may use
    call("searchDocuments") -> docs  # 2. deterministic PreCall(s)
    context "..."                    # 3. context transferred in from other sessions
    set threshold = 10               # 4. session-scoped variables
    + Search for background on the topic.   # 5. prePrompt(s) — may call tools
    - Summarize what you found.              # 6. exactly one prompt — produces output
    schema {                          # 7. the typed output shape
        summary: string
    }
}

Only two parts are strictly required: the name and a prompt. Everything else is optional and depends on what the session needs to do.

1. The name

The first positional argument is the session's name:

session("overview") { ... }

That name is doing double duty. It's the key the session's output appears under in both the context namespace (context.overview) and the blueprint's final output object. It's also what other sessions reference in after, expect, and iterate. Use target if you want to publish under a different key see Control the flow of a blueprint.

2. Each session has its own context

This is the mental model that makes the rest click: a session does not automatically see other sessions' work. Every session starts with a fresh LLM context. It only knows about:

That isolation keeps each session focused on one job with a small, relevant context, which is far more reliable than one giant prompt trying to do everything. To hand one session's result to another, you route it explicitly (covered in Data flow: params, vars & context).

3. use — declaring tools

Before a prePrompt can call a tool, the session has to declare it:

use mcp salesforce
use collection postgres
use search              # special — no name follows

This mirrors the root-level require, but scoped to the session. search is the one exception: it's declared only as use search, never with require.

4. prePrompt (+) vs prompt (-) — the key distinction

Every session ends with instructions to the model, and there are two kinds. Getting them straight is the single most important thing about a session.

A prePrompt starts with +. It enriches the context and runs with tools available. This is where the model searches, fetches, and gathers. You can have as many as you need.

+ Search for recent information about {{ .params.topic }}.
  Focus on the most relevant sources.

The prompt starts with -. It's the single final instruction that turns the gathered context into the structured output. A prompt is tool-free — it does not call tools, it only produces the JSON the schema describes. There is exactly one prompt per session.

- Using the information above, extract the summary and key points
  required by the schema.
prePrompt + prompt -
Purpose Gather / enrich context Produce the final output
Tools Allowed Never
How many Zero or more Exactly one
Produces Context for the prompt The session's typed JSON

The most common beginner mistake is asking the - prompt to "search for X and summarize it." Split it: a + prePrompt searches, the - prompt summarizes. (See Call MCP servers and databases for tool-driven prePrompts.)

Both + and - support Go text templates ({{ .params.x }}, {{ .context.y | json }}) and span multiple lines via indentation.

5. The schema

The schema declares the JSON shape the prompt must produce:

schema {
    summary:   string    # a concise paragraph
    keyPoints: string[]  # the most important points
}

The session name is already the outer wrapper, so keep fields flat at the root rather than re-nesting them under an object named after the session. Schemas have their own depth — optional fields, arrays, nested objects, reusable $Component refs, and scalar shorthand like schema string[] — all covered in Design your output schemas.

6. The supporting cast

Three more optional directives round out a session:

Putting it together

A complete, realistic session using most of the parts:

require mcp knowledge_base

session("gather") {
    use search
    use mcp knowledge_base

# Deterministic fetch before the model runs
    call("searchDocuments") -> rawDocs {
        query = "{{ .params.topic }}"
        limit = 5
    }

+ Search the web and the knowledge base for {{ .params.topic }}.
      Prefer recent, primary sources.

- Using the results above, extract the summary and key points
      required by the schema.

schema {
        summary:   string    # concise paragraph summarising the topic
        keyPoints: string[]  # the most important points
        sources:   string[]  # URLs or titles used
    }
}

Read top to bottom: declare tools, fetch deterministically, gather with a prePrompt, then produce the typed output with the one prompt.

Next steps

Every part of a session, top to bottom — name, tools, PreCalls, context, and the key prePrompt (+) vs prompt (-) distinction that turns gathered context into typed output.