Variables and memory
A graph runs once per message and remembers nothing by itself. State lives in two separate mechanisms: variables, which you control, and memory, which the conversation with the model fills in.
Variables
A variable is declared by the var node: a name, a type (string,
integer, float, boolean, json, csv, dataframe), a scope and a default value.
That node only reads; writing is done by var_set — the value
arrives on its Input port, the reference to the declaring node on its Var port.
- Execute + Data
- Data
On write the value is coerced to the declared type: the string "1" written into an
integer variable becomes a number, so the next comparison still works. A value that
cannot be coerced is stored as it is.
Three scopes
| Scope | How long it lives | When to pick it |
|---|---|---|
session |
One workflow run | Loop counters, values passed between branches of one turn. |
thread |
Across messages, stored in the database | Conversation state: the customer’s language, a ticket number, the stage of a scenario. |
call |
Never stored | Every read answers with the default — a constant, not memory. |
The key for thread is the chat thread: a web-chat session, a Telegram chat. A run with no
chat behind it (a manual run, a webhook without a session) uses the run itself as the
thread, so thread there behaves like session.
Reading them in templates
{{ variables.<name> }} sees session variables plus the engine’s loop_index inside
while_loop. A thread variable is not part of that namespace —
read it through the declaring node’s output: {{ nodes.<var node id>.output }}.
Conversation memory
Memory holds the message history and connects to ai_agent with a
link_memory edge. The three nodes differ in what happens as the history grows:
- buffer_memory — the whole history;
- window_memory — only the last N messages (
window_size, 10 by default); - summary_memory — older messages are compressed into a
summary by a model: by default compression starts past 20 messages and the latest 6 stay
verbatim. The model comes from the parent agent or from a dedicated
link_llmedge.
All three take the same thread / session / call scope, defaulting to thread — the
history survives between messages of one chat. The key is the node plus the thread, so two
memory nodes in one graph keep two separate histories.
llm_response has no memory port: use its own chat-history option or the chat_history node.
Agent memory
agent_memory is not a history but a long-lived key-value
store the agent maintains itself. The node is attached with a link_extension edge and the
model gets tools to store, delete and clear entries (expose_tools, with the set chosen in
enabled_tools). An initial state can be set in the configuration.
The difference from variables is who writes:
| Variables | Agent memory | |
|---|---|---|
| Who writes | Graph nodes — you decide when | The model, through a tool call |
| Who reads | Templates and nodes | The model, in its own context |
| Shape | One typed value per name | A key-value dictionary |
If the graph needs the value (a condition, a filter, a request field), use a variable. If the model has to remember it between turns, use agent memory.
Inspecting and clearing
Open a run, click a node and switch to the “State” tab. Memory nodes show the message
history, agent memory shows the dictionary; the scope is printed beside it and there is a
“Clear state” action. The tab appears only for nodes that serve state — a thread
variable cannot be inspected this way, so surface it through a node in the graph.
Node inspector
agent_memory_1
Scope: thread
| Key | Value |
|---|---|
| client_name | Maria |
| order_id | A-1043 |
| stage | waiting_payment |