← Back to Agentish Framework Guide

Chapter 8 State

The most important concept in Agentish

State is everything. Every piece of data in your workflow lives in the global state. Agents communicate through state. You can understand exactly what happened in a workflow run by looking at the final state. If you master state, you master Agentish.
Deep dive: For the theory behind shared-state architectures, reducers, and information flow patterns, see Agentic Workflow — Chapter 5: Information Flow.

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

  1. Read: An LLM Node declares which state variables it needs via Input State checkboxes. These values are injected into the prompt via template variables.
  2. Process: The LLM reasons about the input and produces structured output.
  3. Write: The structured output fields are written to the state variables declared in Global state to update.
Example: Data Flow Between Two Nodes
# 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.

TypePython TypeMerge BehaviorWhen 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.
Last-write-wins matters. If you have a fan-out topology where two nodes write to the same 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:

VariableTypePurpose
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:

PatternPurpose
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:

Debugging tip: If your workflow produced the wrong result, look at 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

TipWhy
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

Key Takeaways:
← Chapter 7: Loops Chapter 9: Tools →