← Back to Agentish Framework Guide
Chapter 8 State
The most important concept in Agentish
What is State?
The global state is a single shared data store that every node
in your workflow can read from and write to. It’s defined as a Python
TypedDict with typed fields. There is no per-agent private state —
everything goes through the global state.
Think of it as a shared whiteboard: each agent reads what it needs from the board, does its work, and writes its results back. The next agent reads those results and continues.
How Agents Communicate Through State
- Read: An LLM Node declares which state variables it needs via Input State checkboxes. These values are injected into the prompt via template variables.
- Process: The LLM reasons about the input and produces structured output.
- Write: The structured output fields are written to the state variables declared in Global state to update.
# Node A: Analyzer
Input State: [] # Reads nothing (first node)
Output State: [analysis_result] # Writes analysis_result
# After Node A runs, state contains:
{ analysis_result: "Found XOR cipher in decrypt_flag function..." }
# Node B: Reporter
Input State: [analysis_result] # Reads analysis_result
Human Prompt: "Write a report: {analysis_result}"
Output State: [final_report] # Writes final_report
# Node B's LLM receives:
# "Write a report: Found XOR cipher in decrypt_flag function..."Data Types Reference
When defining state variables in the Entry Point, you choose a type for each one. The type determines how values are stored and merged.
| Type | Python Type | Merge Behavior | When to Use |
|---|---|---|---|
str |
str |
Last-write-wins. If two nodes write to the same str variable, the later write replaces the earlier one. |
Most common type. Use for analysis results, reports, reasons, flags, any text output. |
int |
int |
Last-write-wins. | Counters, scores, numeric identifiers. The built-in count variable uses this type. |
float |
float |
Last-write-wins. | Confidence scores, probabilities, thresholds. |
Annotated[List[BaseMessage], ...] |
List[BaseMessage] |
Append reducer. New messages are appended to the existing list. Messages are never overwritten or deleted — only added to. | The built-in messages variable uses this type. You generally don’t define new variables with this type. |
str variable, only one value survives. Design
your state schema so that each variable is written by exactly one node, or use
separate variables.
Built-in vs User-Defined Variables
Built-in (Automatic)
You don’t define these — they exist in every workflow:
| Variable | Type | Purpose |
|---|---|---|
count |
int |
Incremented by each node execution. Tracks how many nodes have run. |
messages |
Annotated[List[BaseMessage], ...] |
Global message log. Every node appends its conversation (system, human, AI, tool messages). This is the full trace of everything that happened. |
Auto-Generated Per Node
The compiler creates additional variables for internal tracking. You don’t define or interact with these directly, but they appear in the final state:
| Pattern | Purpose |
|---|---|
node_<id>_messages |
Per-node conversation history. Used for tool loop context. |
node_<id>_tool_iteration_count |
How many tool calls this node has made so far. |
node_<id>_pass_start |
For fresh-mode loop nodes: index where the current pass began in the message list. |
User-Defined
These are the variables you define in the Entry Point’s Initial State table. They are the primary mechanism for passing data between agents.
Reading State After Execution
After your agent finishes running in the sandbox, the final state is returned. You can read it to understand exactly what happened:
- User-defined variables show what each agent produced (analysis results, reports, decisions).
messagesis the complete conversation log — every system prompt, human message, AI response, and tool call/result, in order.counttells you how many node executions occurred (useful for detecting loops that ran too many times).
messages first. It shows the full chain of reasoning
and tool calls. You can trace exactly where the LLM went wrong — was it
a bad tool call? A misinterpretation of data? A routing mistake?
State Schema Design Tips
| Tip | Why |
|---|---|
| Use descriptive variable names. | analysis_result is better than data. LLMs understand what to produce when the variable name is clear. |
| One writer per variable. | Avoid having two nodes write to the same str variable. Last-write-wins means you lose data. |
Use str for most things. |
LLMs produce text. Unless you specifically need a number, str is the safest and most flexible type. |
| Include a routing_reason variable for loops. | Lets the Router communicate why it sent execution back. Critical for meaningful loop iterations. |
| Keep the schema small. | Every variable adds to the state. Only define variables you actually use. |
Chapter Summary
- State is the single shared data store for the entire workflow.
- Agents communicate by reading from and writing to state variables.
- Most variables use
strtype (last-write-wins).messagesuses append-only semantics. - The final state is your window into what happened — read
messagesto trace the full execution. - Design state schemas with descriptive names, one writer per variable, and only the variables you need.