Specification
Fields
Fields define what data flows into each step's prompt. You never write a prompt template — you declare fields, and the assembled prompt is built from them.
Fields
Fields define what data flows into each step's prompt. You never write a prompt template — you declare fields, and the assembled prompt is built from them.
What Fields Do
Every step declares a list of fields. When the step is about to run, each field resolves to a concrete value — a user input, another step's output, a knob value, or a runtime number. These resolved values are then assembled into the prompt the LLM receives.
The assembled prompt has two parts. First, the resolved fields render as labeled lines:
Query: What is the meaning of life?
Analysis: The data shows a clear upward trend in existential inquiry...
Draft 1: First attempt at answering
Draft 2: Second attempt at answering
Node Number: 3
Branch Count: 5Each field produces one line — FieldName: value. For fields that resolve to multiple values (multi_ingest), the lines are auto-numbered: FieldName 1: value, FieldName 2: value, and so on.
After all field lines, the step's systemPrompt is appended under [System Instruction]. Fields are rendered in declaration order, separated by blank lines.
Field Types
Five field types are available. The first three carry data from different sources; the last two inject runtime values.
text reads a value from the stilt's runtime inputs. The from property is a dot-path string — most commonly input.context. What input.context contains depends on how the stilt is called. In Studio, it's the thread's entire conversation history — all previous messages formatted as "User: ...\nAssistant: ...". Via the API, it's whatever is passed in. During recursion, input.context is replaced with the output of the step that triggered the child execution.
- name: Query
type: text
from: input.contextRenders as Query: Should we use retrieval augmentation?
ingest pulls a single output from another step. The from object specifies which step (stepId), which loop iteration (loopRef), and optionally which node (nodeRef).
- name: Analysis
type: ingest
from:
stepId: analyze
loopRef: currentRenders as Analysis: The data shows a clear upward trend...
multi_ingest pulls multiple outputs and merges them into one auto-numbered block. The from property is an array of source objects — each with stepId, loopRef, and optional nodeRef. All resolved values are concatenated and numbered sequentially, regardless of which source they came from. This is the only field type that can use loopRef: "accumulate" or nodeRef: "accumulate".
- name: Previous Drafts
type: multi_ingest
from:
- stepId: step0
loopRef: 0
- stepId: final
loopRef: accumulateRenders as multiple numbered lines: Previous Drafts 1: ..., Previous Drafts 2: ..., etc. The numbering is continuous — it doesn't reset per source.
nodeInfo injects the current node number (1-based). No from property is needed — it automatically resolves to which parallel branch is executing.
- name: Node Number
type: nodeInfoRenders as Node Number: 3
knobInfo injects a knob's resolved value. The from property is the knob's key name (not a {{}} reference — just the bare name).
- name: Branch Count
type: knobInfo
from: coverageIf the coverage knob is set to 5, this renders as Branch Count: 5.
ingest and multi_ingest References
The ingest and multi_ingest field types share the same reference system for pointing at step outputs. Every source object has three properties:
stepId — the id of the step whose output to read. Must reference a step that exists and has already executed.
loopRef — which loop iteration to read from. Four options:
current— the same loop that's currently executing.previous— the loop immediately before the current one. At loop 0 this resolves to nothing (there is no previous loop), so it's only meaningful from loop 1 onward.- A number (e.g.
0) — a specific loop index. Always reads from that exact iteration, regardless of which loop is currently running. accumulate— collects outputs from all previous loops. At loop 2, this reads loops 0 and 1. At loop 3, it reads loops 0, 1, and 2. Only valid onmulti_ingestbecause it produces multiple values.
nodeRef — optional. Which node within the referenced step to read from. Three options:
current— the same node number that's currently executing.previous— one less than the current node number. In a sequential step with 5 nodes, node 3 reads node 2's output. Node 1 has no previous, so pair withskipFirstNode: true.accumulate— collects all node outputs from the referenced step. Only valid onmulti_ingest.
When nodeRef is omitted, the reference reads the step's output without targeting a specific node — this works for single-node steps.
skipFirstNode — a boolean property available on ingest fields. When true, the field returns an empty string for node 1 instead of trying to read a previous output that doesn't exist. Standard pattern for sequential self-referencing steps:
- id: refine
name: Refine
type: sequential
nodes: 3
fields:
- name: Previous
type: ingest
from:
stepId: refine
loopRef: current
nodeRef: previous
skipFirstNode: true
systemPrompt: "Improve the previous output."Node 1 gets an empty Previous: line and generates from scratch. Nodes 2 and 3 each read the previous node's output and refine it.
How accumulate Grows Across Loops
The accumulate loop reference collects outputs from all previous loops. Each time the stilt completes a loop, the accumulated list of outputs grows by one entry.
A single-source accumulate, collecting the final step from every previous loop:
- name: Draft
type: multi_ingest
from:
- stepId: final
loopRef: accumulateDuring loop 1 (the second pass), the stilt has completed loop 0, so the field renders:
Draft 1: <final step output from loop 0>During loop 2 (the third pass):
Draft 1: <final step output from loop 0>
Draft 2: <final step output from loop 1>During loop 3:
Draft 1: <final step output from loop 0>
Draft 2: <final step output from loop 1>
Draft 3: <final step output from loop 2>Each loop, the accumulated list grows by one. The field name stays the same and the numbering is appended.
A multi-source accumulate mixes fixed references with accumulated ones:
- name: Drafts
type: multi_ingest
from:
- stepId: step0
loopRef: 0
- stepId: step11
loopRef: accumulateThe first source always reads loop 0 (the initial draft). The second accumulates across all previous loops. Numbering is continuous across both sources.
During loop 1:
Drafts 1: <step0 loop 0 output>
Drafts 2: <step11 loop 0 output>During loop 2:
Drafts 1: <step0 loop 0 output>
Drafts 2: <step11 loop 0 output>
Drafts 3: <step11 loop 1 output>The fixed source always produces the same entries. The accumulated source adds one more entry each loop.
Prompt Assembly Across Loops
When a stilt has loops, the assembled prompt evolves with each pass. Fields using loopRef: accumulate grow, fixed references stay the same, and the LLM receives progressively more context each round.
Consider this step inside a looping stilt:
fields:
- name: Context
type: text
from: input.context
- name: Drafts
type: multi_ingest
from:
- stepId: step0
loopRef: 0
- stepId: final
loopRef: accumulate
- name: Node Number
type: nodeInfo
systemPrompt: "Produce the final refined draft."During loop 0, the accumulate is empty (there are no previous loops yet). The step receives only the initial draft:
Context: Write an essay on vector databases
Drafts 1: <step0 loop 0 output — the initial draft>
Node Number: 1
[System Instruction]
Produce the final refined draft.During loop 1, the accumulate collected loop 0's final output:
Context: Write an essay on vector databases
Drafts 1: <step0 loop 0 output — the initial draft>
Drafts 2: <final step loop 0 output — first refinement>
Node Number: 1
[System Instruction]
Produce the final refined draft.During loop 2, one more entry appears:
Context: Write an essay on vector databases
Drafts 1: <step0 loop 0 output — the initial draft>
Drafts 2: <final step loop 0 output — first refinement>
Drafts 3: <final step loop 1 output — second refinement>
Node Number: 1
[System Instruction]
Produce the final refined draft.Each loop, the text field stays the same (input.context doesn't change). The multi_ingest field grows by one entry. The nodeInfo field shows the current node. The system prompt stays fixed.
Full Example
A step using all five field types together:
fields:
- name: Query
type: text
from: input.context
- name: Analysis
type: ingest
from:
stepId: analyze
loopRef: current
- name: Previous Drafts
type: multi_ingest
from:
- stepId: step0
loopRef: 0
- stepId: final
loopRef: accumulate
- name: Node Number
type: nodeInfo
- name: Branch Count
type: knobInfo
from: coverageThis produces the following prompt (assuming the stilt is on loop 2, node 3 of 5, with coverage set to 5):
Query: What is the best approach to quantum error correction?
Analysis: Surface codes and color codes are the two dominant families...
Previous Drafts 1: <step0 loop 0 output — the initial draft>
Previous Drafts 2: <final step loop 0 output>
Previous Drafts 3: <final step loop 1 output>
Node Number: 3
Branch Count: 5
[System Instruction]
Evaluate and improve the previous drafts.Each field type contributes its line. The multi_ingest field produces three numbered entries because loop 0 is fixed and the accumulate covers loops 0 and 1. The system prompt closes it out.
Common Validation Failures
Using loopRef: "accumulate" or nodeRef: "accumulate" on an ingest field — these produce arrays and only work on multi_ingest. Giving a text field an object from instead of a string path. Giving nodeInfo a from property — it doesn't take one. Referencing a stepId in ingest or multi_ingest that doesn't exist in the config. Referencing a knob name in knobInfo.from that isn't defined in the knobs section.