Plan the full task as a dependency graph, then execute deterministically.
Atom of Thought = "SQL for Reasoning"
Just as SQL breaks complex data operations into atomic, composable statements, AoT breaks reasoning into minimal, executable steps.
An atom is the smallest unit of reasoning that:
❌ Not atomic (compound statement):
"Search for rooms in Graz and filter by capacity"
✅ Atomic (separate steps):
1. Search for rooms in Graz
2. Filter rooms by minimum capacity of 30
┌─────────────────────────────────┐
│ LLM (Planning Layer) │
│ - Proposes atomic plan │
│ - Does NOT execute │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Validator (Safety Layer) │
│ - Checks plan structure │
│ - Validates dependencies │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Executor (Execution Layer) │
│ - Runs atoms deterministically│
│ - Manages state │
└─────────────────────────────────┘
LLM thinks → LLM acts → LLM thinks → LLM acts
Problem: Execution logic lives inside the model (black box)
LLM plans → System validates → System executes
Benefit: Execution logic lives in code (white box)
Think of AoT as the difference between:
| Cooking | Programming |
|---|---|
| Recipe (AoT plan) | Algorithm |
| "Boil water" | boilWater() |
| "Add pasta" | addPasta() |
| "Cook 8 minutes" | cook(8) |
vs.
| Improvising | Natural Language |
|---|---|
| "Make dinner" | "Figure it out" |
| (figure it out) | (hallucinate) |
{
"id": 2,
"kind": "tool", // tool | decision | final
"name": "multiply", // operation name
"input": { // explicit inputs
"a": "<result_of_1>", // reference to previous result
"b": 3
},
"dependsOn": [1] // must wait for atom 1
}
Why this structure?
id: Establishes orderkind: Categorizes operation typename: References executable functioninput: Makes data flow explicitdependsOn: Declares dependenciesAtoms form a directed acyclic graph (DAG):
┌─────┐
│ 1 │ add(15, 7)
└──┬──┘
│
┌──▼──┐
│ 2 │ multiply(result_1, 3)
└──┬──┘
│
┌──▼──┐
│ 3 │ subtract(result_2, 10)
└──┬──┘
│
┌──▼──┐
│ 4 │ final
└─────┘
Properties:
const state = {};
// After atom 1
state[1] = 22; // result of add(15, 7)
// After atom 2
state[2] = 66; // result of multiply(22, 3)
// After atom 3
state[3] = 56; // result of subtract(66, 10)
State is:
Thought: I need to add 15 and 7 first
Action: add(15, 7)
Observation: 22
Thought: Now multiply by 3
Action: multiply(22, 3)
Observation: 66
Thought: Finally subtract 10
Action: subtract(66, 10)
Observation: 56
Answer: 56
{
"atoms": [
{"id": 1, "kind": "tool", "name": "add", "input": {"a": 15, "b": 7}},
{"id": 2, "kind": "tool", "name": "multiply", "input": {"a": "<result_of_1>", "b": 3}, "dependsOn": [1]},
{"id": 3, "kind": "tool", "name": "subtract", "input": {"a": "<result_of_2>", "b": 10}, "dependsOn": [2]},
{"id": 4, "kind": "final", "name": "report", "dependsOn": [3]}
]
}
| Aspect | ReAct | AoT |
|---|---|---|
| Format | Natural language | Structured data |
| Validation | Impossible | Before execution |
| Testing | Mock entire LLM | Test executor independently |
| Debugging | Read through text | Inspect atom N |
| Replay | Re-run entire conversation | Re-run from any atom |
| Audit trail | Conversational history | Data structure |
ReAct is like a chef improvising:
AoT is like following a recipe:
When something goes wrong:
ReAct:
"The model said something weird in iteration 7"
→ Re-read entire conversation
→ Guess where it went wrong
→ Hope it doesn't happen again
AoT:
"Atom 3 failed with 'Division by zero'"
→ Look at atom 3's inputs
→ Check where those inputs came from (atom 1, 2)
→ Fix tool or add validation
→ Re-run from atom 3
✅ LLM side:
✅ System side:
ReAct asks: "What would an intelligent agent say next?"
AoT asks: "What is the minimal, executable plan?"
For production systems, you want the second question.