Orchestrate a Sonnet
A poem pipeline with the Orchestrations module. Unlike Multi-Agent Sonnet with Nested Agent Calls, agents do not call one another: the graph invokes each agent directly, stores outputs in typed state, assembles the poem, and persists it through a builtin tool.
You will:
- Create a project, an AI provider, and a shared document for the poem.
- Create one fixed builtin tool that writes the finished poem to that document.
- Create five specialized agents: one for the title and four for the sonnet sections.
- Define an orchestration whose
agentnodes call those agents in sequence. - Run the orchestration with a theme and inspect both the persisted document and the run state.
Prerequisites
- SOAT running locally at
http://localhost:5047(Quick Start). - Key Concepts if new to SOAT.
- CLI or SDK set up.
- Configuration for production hardening.
- Ollama running locally with a chat model. For xAI, OpenAI, Anthropic, or Amazon Bedrock, see Connect Third-Party LLMs.
- CLI
- SDK
- curl
export SOAT_BASE_URL=http://localhost:5047
import { SoatClient } from '@soat/sdk';
export SOAT_URL=http://localhost:5047
Step 1 — Log in as admin
Admin is the built-in superuser role (Users).
- CLI
- SDK
- curl
ADMIN_TOKEN=$(soat login-user --username admin --password Admin1234! | jq -r '.token')
export SOAT_TOKEN=$ADMIN_TOKEN
const soat = new SoatClient({ baseUrl: 'http://localhost:5047' });
const { data: login } = await soat.users.loginUser({
body: { username: 'admin', password: 'Admin1234!' },
});
const adminSoat = new SoatClient({
baseUrl: 'http://localhost:5047',
token: login.token,
});
ADMIN_TOKEN=$(curl -s -X POST "$SOAT_URL/api/v1/users/login" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Admin1234!"}' | jq -r '.token')
Step 2 — Create a project
Every resource lives inside a project.
- CLI
- SDK
- curl
PROJECT_ID=$(soat create-project --name "Orchestrated Sonnet" | jq -r '.id')
echo "PROJECT_ID: $PROJECT_ID"
const { data: project } = await adminSoat.projects.createProject({
body: { name: 'Orchestrated Sonnet' },
});
const PROJECT_ID = project.id;
PROJECT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/projects" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Orchestrated Sonnet"}' | jq -r '.id')
echo "PROJECT_ID: $PROJECT_ID"
Step 3 — Create an AI provider
Create a local AI provider backed by Ollama.
- CLI
- SDK
- curl
AI_PROVIDER_ID=$(soat create-ai-provider \
--project-id "$PROJECT_ID" \
--name "Local Ollama" \
--provider "ollama" \
--default-model "qwen2.5:0.5b" | jq -r '.id')
echo "AI_PROVIDER_ID: $AI_PROVIDER_ID"
const { data: aiProvider } = await adminSoat.aiProviders.createAiProvider({
body: {
project_id: PROJECT_ID,
name: 'Local Ollama',
provider: 'ollama',
default_model: 'qwen2.5:0.5b',
},
});
const AI_PROVIDER_ID = aiProvider.id;
AI_PROVIDER_ID=$(curl -s -X POST "$SOAT_URL/api/v1/ai-providers" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"name\":\"Local Ollama\",\"provider\":\"ollama\",\"default_model\":\"qwen2.5:0.5b\"}" \
| jq -r '.id')
echo "AI_PROVIDER_ID: $AI_PROVIDER_ID"
Step 4 — Create the poem document and a fixed write tool
The poem is persisted in a document; a fixed builtin tool updates it from the orchestration.
- CLI
- SDK
- curl
POEM_DOC_ID=$(soat create-document \
--project-id "$PROJECT_ID" \
--content "(empty - orchestration will write the poem)" \
--path "/poems/orchestrated-sonnet.txt" | jq -r '.id')
echo "POEM_DOC_ID: $POEM_DOC_ID"
WRITE_POEM_TOOL_ID=$(soat create-tool \
--project-id "$PROJECT_ID" \
--name "persist-poem" \
--type "builtin" \
--description "Write the completed poem to the shared document" \
--actions '["update-document"]' \
--preset-parameters '{"document_id": "'"$POEM_DOC_ID"'"}' | jq -r '.id')
echo "WRITE_POEM_TOOL_ID: $WRITE_POEM_TOOL_ID"
const { data: poemDoc } = await adminSoat.documents.createDocument({
body: {
project_id: PROJECT_ID,
content: '(empty - orchestration will write the poem)',
path: '/poems/orchestrated-sonnet.txt',
},
});
const POEM_DOC_ID = poemDoc.id;
const { data: writePoemTool } = await adminSoat.tools.createTool({
body: {
project_id: PROJECT_ID,
name: 'persist-poem',
type: 'soat',
description: 'Write the completed poem to the shared document',
actions: ['update-document'],
preset_parameters: { documentId: POEM_DOC_ID },
},
});
const WRITE_POEM_TOOL_ID = writePoemTool.id;
POEM_DOC_ID=$(curl -s -X POST "$SOAT_URL/api/v1/documents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"content\":\"(empty - orchestration will write the poem)\",\"path\":\"/poems/orchestrated-sonnet.txt\"}" \
| jq -r '.id')
echo "POEM_DOC_ID: $POEM_DOC_ID"
WRITE_POEM_TOOL_ID=$(curl -s -X POST "$SOAT_URL/api/v1/tools" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"name\":\"persist-poem\",\"type\":\"soat\",\"description\":\"Write the completed poem to the shared document\",\"actions\":[\"update-document\"],\"preset_parameters\":{\"documentId\":\"$POEM_DOC_ID\"}}" \
| jq -r '.id')
echo "WRITE_POEM_TOOL_ID: $WRITE_POEM_TOOL_ID"
Step 5 — Create the title and stanza agents
Five agents: one returns the title as JSON, four return a stanza or couplet as JSON.
- CLI
- SDK
- curl
TITLE_AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Sonnet Title Agent" \
--instructions 'You receive context lines such as theme: "...". Return ONLY compact JSON matching {"title":"..."}. Create a short poetic sonnet title about the theme. No markdown, no code fences.' \
--max-steps 1 | jq -r '.id')
STANZA1_AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Sonnet Stanza 1 Agent" \
--instructions 'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the first quatrain of a sonnet using ABAB rhyme. Exactly 4 lines. Do not repeat previous text.' \
--max-steps 1 | jq -r '.id')
STANZA2_AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Sonnet Stanza 2 Agent" \
--instructions 'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the second quatrain of a sonnet using CDCD rhyme. Exactly 4 lines. Do not repeat previous text.' \
--max-steps 1 | jq -r '.id')
STANZA3_AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Sonnet Stanza 3 Agent" \
--instructions 'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the third quatrain of a sonnet using EFEF rhyme. Exactly 4 lines. Do not repeat previous text.' \
--max-steps 1 | jq -r '.id')
STANZA4_AGENT_ID=$(soat create-agent \
--project-id "$PROJECT_ID" \
--ai-provider-id "$AI_PROVIDER_ID" \
--name "Sonnet Couplet Agent" \
--instructions 'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the final couplet of a sonnet using GG rhyme. Exactly 2 lines. Do not repeat previous text.' \
--max-steps 1 | jq -r '.id')
echo "TITLE_AGENT_ID: $TITLE_AGENT_ID"
echo "STANZA1_AGENT_ID: $STANZA1_AGENT_ID"
echo "STANZA2_AGENT_ID: $STANZA2_AGENT_ID"
echo "STANZA3_AGENT_ID: $STANZA3_AGENT_ID"
echo "STANZA4_AGENT_ID: $STANZA4_AGENT_ID"
const { data: titleAgent } = await adminSoat.agents.createAgent({
body: {
project_id: PROJECT_ID,
ai_provider_id: AI_PROVIDER_ID,
name: 'Sonnet Title Agent',
instructions:
'You receive context lines such as theme: "...". Return ONLY compact JSON matching {"title":"..."}. Create a short poetic sonnet title about the theme. No markdown, no code fences.',
max_steps: 1,
},
});
const TITLE_AGENT_ID = titleAgent.id;
const stanzaAgentConfigs = [
{
name: 'Sonnet Stanza 1 Agent',
instructions:
'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the first quatrain of a sonnet using ABAB rhyme. Exactly 4 lines. Do not repeat previous text.',
},
{
name: 'Sonnet Stanza 2 Agent',
instructions:
'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the second quatrain of a sonnet using CDCD rhyme. Exactly 4 lines. Do not repeat previous text.',
},
{
name: 'Sonnet Stanza 3 Agent',
instructions:
'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the third quatrain of a sonnet using EFEF rhyme. Exactly 4 lines. Do not repeat previous text.',
},
{
name: 'Sonnet Couplet Agent',
instructions:
'You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {"stanza":"..."}. Write the final couplet of a sonnet using GG rhyme. Exactly 2 lines. Do not repeat previous text.',
},
];
const stanzaAgentIds: string[] = [];
for (const config of stanzaAgentConfigs) {
const { data: agent } = await adminSoat.agents.createAgent({
body: {
project_id: PROJECT_ID,
ai_provider_id: AI_PROVIDER_ID,
name: config.name,
instructions: config.instructions,
max_steps: 1,
},
});
stanzaAgentIds.push(agent.id);
}
const [STANZA1_AGENT_ID, STANZA2_AGENT_ID, STANZA3_AGENT_ID, STANZA4_AGENT_ID] =
stanzaAgentIds;
TITLE_AGENT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/agents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"ai_provider_id\":\"$AI_PROVIDER_ID\",\"name\":\"Sonnet Title Agent\",\"instructions\":\"You receive context lines such as theme: \\\"...\\\". Return ONLY compact JSON matching {\\\"title\\\":\\\"...\\\"}. Create a short poetic sonnet title about the theme. No markdown, no code fences.\",\"max_steps\":1}" \
| jq -r '.id')
STANZA1_AGENT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/agents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"ai_provider_id\":\"$AI_PROVIDER_ID\",\"name\":\"Sonnet Stanza 1 Agent\",\"instructions\":\"You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {\\\"stanza\\\":\\\"...\\\"}. Write the first quatrain of a sonnet using ABAB rhyme. Exactly 4 lines. Do not repeat previous text.\",\"max_steps\":1}" \
| jq -r '.id')
STANZA2_AGENT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/agents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"ai_provider_id\":\"$AI_PROVIDER_ID\",\"name\":\"Sonnet Stanza 2 Agent\",\"instructions\":\"You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {\\\"stanza\\\":\\\"...\\\"}. Write the second quatrain of a sonnet using CDCD rhyme. Exactly 4 lines. Do not repeat previous text.\",\"max_steps\":1}" \
| jq -r '.id')
STANZA3_AGENT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/agents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"ai_provider_id\":\"$AI_PROVIDER_ID\",\"name\":\"Sonnet Stanza 3 Agent\",\"instructions\":\"You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {\\\"stanza\\\":\\\"...\\\"}. Write the third quatrain of a sonnet using EFEF rhyme. Exactly 4 lines. Do not repeat previous text.\",\"max_steps\":1}" \
| jq -r '.id')
STANZA4_AGENT_ID=$(curl -s -X POST "$SOAT_URL/api/v1/agents" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"ai_provider_id\":\"$AI_PROVIDER_ID\",\"name\":\"Sonnet Couplet Agent\",\"instructions\":\"You receive context lines such as theme, title, and previous stanzas. Return ONLY compact JSON matching {\\\"stanza\\\":\\\"...\\\"}. Write the final couplet of a sonnet using GG rhyme. Exactly 2 lines. Do not repeat previous text.\",\"max_steps\":1}" \
| jq -r '.id')
echo "TITLE_AGENT_ID: $TITLE_AGENT_ID"
echo "STANZA1_AGENT_ID: $STANZA1_AGENT_ID"
echo "STANZA2_AGENT_ID: $STANZA2_AGENT_ID"
echo "STANZA3_AGENT_ID: $STANZA3_AGENT_ID"
echo "STANZA4_AGENT_ID: $STANZA4_AGENT_ID"
Step 6 — Create the orchestration graph
The orchestration stores each agent result in state, assembles the poem with a transform node, writes it with a tool node, and exposes it through a terminal return-poem node.
- CLI
- SDK
- curl
ORCH_NODES='[
{
"id": "generate-title",
"type": "agent",
"agent_id": "'"$TITLE_AGENT_ID"'",
"input_mapping": {"theme": {"var": "input.theme"}},
"output_schema": {"type": "object", "required": ["title"], "properties": {"title": {"type": "string"}}},
"state_mapping":{"state.title":{"var":"output.object.title"}}
},
{
"id": "generate-stanza-1",
"type": "agent",
"agent_id": "'"$STANZA1_AGENT_ID"'",
"input_mapping": {"theme": {"var": "input.theme"}, "title": {"var": "title"}},
"output_schema": {"type": "object", "required": ["stanza"], "properties": {"stanza": {"type": "string"}}},
"state_mapping":{"state.stanza1":{"var":"output.object.stanza"}}
},
{
"id": "generate-stanza-2",
"type": "agent",
"agent_id": "'"$STANZA2_AGENT_ID"'",
"input_mapping": {"theme": {"var": "input.theme"}, "title": {"var": "title"}, "stanza1": {"var": "stanza1"}},
"output_schema": {"type": "object", "required": ["stanza"], "properties": {"stanza": {"type": "string"}}},
"state_mapping":{"state.stanza2":{"var":"output.object.stanza"}}
},
{
"id": "generate-stanza-3",
"type": "agent",
"agent_id": "'"$STANZA3_AGENT_ID"'",
"input_mapping": {"theme": {"var": "input.theme"}, "title": {"var": "title"}, "stanza1": {"var": "stanza1"}, "stanza2": {"var": "stanza2"}},
"output_schema": {"type": "object", "required": ["stanza"], "properties": {"stanza": {"type": "string"}}},
"state_mapping":{"state.stanza3":{"var":"output.object.stanza"}}
},
{
"id": "generate-stanza-4",
"type": "agent",
"agent_id": "'"$STANZA4_AGENT_ID"'",
"input_mapping": {"theme": {"var": "input.theme"}, "title": {"var": "title"}, "stanza1": {"var": "stanza1"}, "stanza2": {"var": "stanza2"}, "stanza3": {"var": "stanza3"}},
"output_schema": {"type": "object", "required": ["stanza"], "properties": {"stanza": {"type": "string"}}},
"state_mapping":{"state.stanza4":{"var":"output.object.stanza"}}
},
{
"id": "assemble-poem",
"type": "transform",
"expression": {"cat": [{"var": "title"}, "\n\n", {"var": "stanza1"}, "\n\n", {"var": "stanza2"}, "\n\n", {"var": "stanza3"}, "\n\n", {"var": "stanza4"}]},
"state_mapping":{"state.poem":{"var":"output.result"}}
},
{
"id": "persist-poem",
"type": "tool",
"tool_id": "'"$WRITE_POEM_TOOL_ID"'",
"operation_id": "update-document",
"input_mapping": {"content": {"var": "poem"}}
},
{
"id": "return-poem",
"type": "transform",
"expression": {"var": "poem"}
}
]'
ORCH_EDGES='[
{"from": "generate-title", "to": "generate-stanza-1"},
{"from": "generate-stanza-1", "to": "generate-stanza-2"},
{"from": "generate-stanza-2", "to": "generate-stanza-3"},
{"from": "generate-stanza-3", "to": "generate-stanza-4"},
{"from": "generate-stanza-4", "to": "assemble-poem"},
{"from": "assemble-poem", "to": "persist-poem"},
{"from": "assemble-poem", "to": "return-poem"}
]'
ORCHESTRATION_ID=$(soat create-orchestration \
--project-id "$PROJECT_ID" \
--name "Direct Sonnet Orchestration" \
--description "Direct agent-node sonnet pipeline" \
--nodes "$ORCH_NODES" \
--edges "$ORCH_EDGES" | jq -r '.id')
echo "ORCHESTRATION_ID: $ORCHESTRATION_ID"
const { data: orchestration } =
await adminSoat.orchestrations.createOrchestration({
body: {
project_id: PROJECT_ID,
name: 'Direct Sonnet Orchestration',
description: 'Direct agent-node sonnet pipeline',
nodes: [
{
id: 'generate-title',
type: 'agent',
agent_id: TITLE_AGENT_ID,
input_mapping: { theme: { var: 'input.theme' } },
output_schema: {
type: 'object',
required: ['title'],
properties: { title: { type: 'string' } },
},
state_mapping: { 'state.title': { var: 'output.object.title' } },
},
{
id: 'generate-stanza-1',
type: 'agent',
agent_id: STANZA1_AGENT_ID,
input_mapping: { theme: { var: 'input.theme' }, title: { var: 'title' } },
output_schema: {
type: 'object',
required: ['stanza'],
properties: { stanza: { type: 'string' } },
},
state_mapping: { 'state.stanza1': { var: 'output.object.stanza' } },
},
{
id: 'generate-stanza-2',
type: 'agent',
agent_id: STANZA2_AGENT_ID,
input_mapping: {
theme: { var: 'input.theme' },
title: { var: 'title' },
stanza1: { var: 'stanza1' },
},
output_schema: {
type: 'object',
required: ['stanza'],
properties: { stanza: { type: 'string' } },
},
state_mapping: { 'state.stanza2': { var: 'output.object.stanza' } },
},
{
id: 'generate-stanza-3',
type: 'agent',
agent_id: STANZA3_AGENT_ID,
input_mapping: {
theme: { var: 'input.theme' },
title: { var: 'title' },
stanza1: { var: 'stanza1' },
stanza2: { var: 'stanza2' },
},
output_schema: {
type: 'object',
required: ['stanza'],
properties: { stanza: { type: 'string' } },
},
state_mapping: { 'state.stanza3': { var: 'output.object.stanza' } },
},
{
id: 'generate-stanza-4',
type: 'agent',
agent_id: STANZA4_AGENT_ID,
input_mapping: {
theme: { var: 'input.theme' },
title: { var: 'title' },
stanza1: { var: 'stanza1' },
stanza2: { var: 'stanza2' },
stanza3: { var: 'stanza3' },
},
output_schema: {
type: 'object',
required: ['stanza'],
properties: { stanza: { type: 'string' } },
},
state_mapping: { 'state.stanza4': { var: 'output.object.stanza' } },
},
{
id: 'assemble-poem',
type: 'transform',
expression: {
cat: [
{ var: 'title' },
'\n\n',
{ var: 'stanza1' },
'\n\n',
{ var: 'stanza2' },
'\n\n',
{ var: 'stanza3' },
'\n\n',
{ var: 'stanza4' },
],
},
state_mapping: { 'state.poem': { var: 'output.result' } },
},
{
id: 'persist-poem',
type: 'tool',
tool_id: WRITE_POEM_TOOL_ID,
operation_id: 'update-document',
input_mapping: { content: { var: 'poem' } },
},
{
id: 'return-poem',
type: 'transform',
expression: { var: 'poem' },
},
],
edges: [
{ from: 'generate-title', to: 'generate-stanza-1' },
{ from: 'generate-stanza-1', to: 'generate-stanza-2' },
{ from: 'generate-stanza-2', to: 'generate-stanza-3' },
{ from: 'generate-stanza-3', to: 'generate-stanza-4' },
{ from: 'generate-stanza-4', to: 'assemble-poem' },
{ from: 'assemble-poem', to: 'persist-poem' },
{ from: 'assemble-poem', to: 'return-poem' },
],
},
});
const ORCHESTRATION_ID = orchestration.id;
CREATE_ORCHESTRATION_RESP=$(curl -s -X POST "$SOAT_URL/api/v1/orchestrations" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"project_id\":\"$PROJECT_ID\",\"name\":\"Direct Sonnet Orchestration\",\"description\":\"Direct agent-node sonnet pipeline\",\"nodes\":[{\"id\":\"generate-title\",\"type\":\"agent\",\"agent_id\":\"$TITLE_AGENT_ID\",\"input_mapping\":{\"theme\":{\"var\": \"input.theme\"}},\"output_schema\":{\"type\":\"object\",\"required\":[\"title\"],\"properties\":{\"title\":{\"type\":\"string\"}}},\"state_mapping\":{\"state.title\":{\"var\":\"output.object.title\"}}},{\"id\":\"generate-stanza-1\",\"type\":\"agent\",\"agent_id\":\"$STANZA1_AGENT_ID\",\"input_mapping\":{\"theme\":{\"var\": \"input.theme\"},\"title\":{\"var\": \"title\"}},\"output_schema\":{\"type\":\"object\",\"required\":[\"stanza\"],\"properties\":{\"stanza\":{\"type\":\"string\"}}},\"state_mapping\":{\"state.stanza1\":{\"var\":\"output.object.stanza\"}}},{\"id\":\"generate-stanza-2\",\"type\":\"agent\",\"agent_id\":\"$STANZA2_AGENT_ID\",\"input_mapping\":{\"theme\":{\"var\": \"input.theme\"},\"title\":{\"var\": \"title\"},\"stanza1\":{\"var\": \"stanza1\"}},\"output_schema\":{\"type\":\"object\",\"required\":[\"stanza\"],\"properties\":{\"stanza\":{\"type\":\"string\"}}},\"state_mapping\":{\"state.stanza2\":{\"var\":\"output.object.stanza\"}}},{\"id\":\"generate-stanza-3\",\"type\":\"agent\",\"agent_id\":\"$STANZA3_AGENT_ID\",\"input_mapping\":{\"theme\":{\"var\": \"input.theme\"},\"title\":{\"var\": \"title\"},\"stanza1\":{\"var\": \"stanza1\"},\"stanza2\":{\"var\": \"stanza2\"}},\"output_schema\":{\"type\":\"object\",\"required\":[\"stanza\"],\"properties\":{\"stanza\":{\"type\":\"string\"}}},\"state_mapping\":{\"state.stanza3\":{\"var\":\"output.object.stanza\"}}},{\"id\":\"generate-stanza-4\",\"type\":\"agent\",\"agent_id\":\"$STANZA4_AGENT_ID\",\"input_mapping\":{\"theme\":{\"var\": \"input.theme\"},\"title\":{\"var\": \"title\"},\"stanza1\":{\"var\": \"stanza1\"},\"stanza2\":{\"var\": \"stanza2\"},\"stanza3\":{\"var\": \"stanza3\"}},\"output_schema\":{\"type\":\"object\",\"required\":[\"stanza\"],\"properties\":{\"stanza\":{\"type\":\"string\"}}},\"state_mapping\":{\"state.stanza4\":{\"var\":\"output.object.stanza\"}}},{\"id\":\"assemble-poem\",\"type\":\"transform\",\"expression\":{\"cat\":[{\"var\":\"title\"},\"\\n\\n\",{\"var\":\"stanza1\"},\"\\n\\n\",{\"var\":\"stanza2\"},\"\\n\\n\",{\"var\":\"stanza3\"},\"\\n\\n\",{\"var\":\"stanza4\"}]},\"state_mapping\":{\"state.poem\":{\"var\":\"output.result\"}}},{\"id\":\"persist-poem\",\"type\":\"tool\",\"tool_id\":\"$WRITE_POEM_TOOL_ID\",\"operation_id\":\"update-document\",\"input_mapping\":{\"content\":{\"var\": \"poem\"}}},{\"id\":\"return-poem\",\"type\":\"transform\",\"expression\":{\"var\":\"poem\"}}],\"edges\":[{\"from\":\"generate-title\",\"to\":\"generate-stanza-1\"},{\"from\":\"generate-stanza-1\",\"to\":\"generate-stanza-2\"},{\"from\":\"generate-stanza-2\",\"to\":\"generate-stanza-3\"},{\"from\":\"generate-stanza-3\",\"to\":\"generate-stanza-4\"},{\"from\":\"generate-stanza-4\",\"to\":\"assemble-poem\"},{\"from\":\"assemble-poem\",\"to\":\"persist-poem\"},{\"from\":\"assemble-poem\",\"to\":\"return-poem\"}]}" )
ORCHESTRATION_ID=$(printf '%s\n' "$CREATE_ORCHESTRATION_RESP" | jq -r '.id')
echo "ORCHESTRATION_ID: $ORCHESTRATION_ID"
Step 7 — Start a run
Run the orchestration with a theme. The output includes both terminal nodes: the document update and the poem text.
- CLI
- SDK
- curl
RUN=$(soat start-orchestration-run \
--orchestration-id "$ORCHESTRATION_ID" \
--input '{"theme":"artificial intelligence"}')
printf '%s\n' "$RUN" | jq '{status, trace_id, output}'
RUN_ID=$(printf '%s\n' "$RUN" | jq -r '.id')
echo "\nFinal poem returned by the orchestration:\n"
printf '%s\n' "$RUN" | jq -r '.output["return-poem"].result'
echo "\nRUN_ID: $RUN_ID"
Expected status output:
{
"status": "succeeded",
"trace_id": "trace_example",
"output": {
"persist-poem": {
"id": "doc_example"
},
"return-poem": {
"result": "... poem text ..."
}
}
}
const { data: run } = await adminSoat.orchestrations.startOrchestrationRun({
body: {
orchestration_id: ORCHESTRATION_ID,
input: { theme: 'artificial intelligence' },
},
});
console.log('Status:', run.status);
console.log('Trace ID:', run.trace_id);
console.log('Final poem:\n', run.output?.['return-poem']?.result);
const RUN_ID = run.id;
RUN=$(curl -s -X POST "$SOAT_URL/api/v1/orchestration-runs" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"orchestration_id\":\"$ORCHESTRATION_ID\",\"input\":{\"theme\":\"artificial intelligence\"}}")
printf '%s\n' "$RUN" | jq '{status, trace_id, output}'
RUN_ID=$(printf '%s\n' "$RUN" | jq -r '.id')
printf '%s\n' "$RUN" | jq -r '.output["return-poem"].result'
echo "RUN_ID: $RUN_ID"
Step 8 — Read the persisted poem document
The document holds the poem written by the persist-poem tool node.
- CLI
- SDK
- curl
soat get-document --document-id "$POEM_DOC_ID" | jq -r '.content'
const { data: poem } = await adminSoat.documents.getDocument({
path: { document_id: POEM_DOC_ID },
});
console.log(poem.content);
curl -s "$SOAT_URL/api/v1/documents/$POEM_DOC_ID" \
-H "Authorization: Bearer $ADMIN_TOKEN" | jq -r '.content'
Step 9 — Inspect the run state
get-orchestration-run exposes the accumulated orchestration state, every intermediate field included, unlike the nested-agent tutorial.
- CLI
- SDK
- curl
soat get-orchestration-run \
--orchestration-run-id "$RUN_ID" | jq '{status, state, output}'
Key fields:
state.titlestate.stanza1throughstate.stanza4state.poemoutput["return-poem"].result
const { data: runState } = await adminSoat.orchestrations.getOrchestrationRun({
path: { orchestration_run_id: RUN_ID },
});
console.log(JSON.stringify(runState.state, null, 2));
console.log(runState.output?.['return-poem']?.result);
curl -s "$SOAT_URL/api/v1/orchestration-runs/$RUN_ID" \
-H "Authorization: Bearer $ADMIN_TOKEN" | jq '{status, state, output}'
How It Works
agentnodes call the five agents;state_mappingwrites each result into state (state.title,state.stanza1, …).transformassembles the poem with JSON Logic;toolpersists it with a fixeddocumentId;return-poemexposes it in the run output.- Routing, sequencing and state live in the Orchestrations engine, not in an orchestrator agent as in Multi-Agent Sonnet with Nested Agent Calls.