A 300-Line LangGraph Alternative
A 300-Line LangGraph Alternative
Modern agent frameworks such as LangGraph allow developers to build complex workflows using graph-based execution models.
Example architecture:
User Input ↓Reasoning Node ↓Tool Node ↓Observation Node ↓Next Reasoning StepEach node represents a stage in the agent workflow.
However, the core mechanics behind these frameworks are surprisingly simple.
By combining the components we built earlier, we can construct a minimal agent runtime in roughly a few hundred lines of code.
The Core Idea
At its heart, an agent framework only needs a few elements:
| Component | Purpose |
|---|---|
| state | track execution context |
| node | unit of computation |
| graph | workflow structure |
| executor | runs the graph |
| tools | interact with external systems |
Conceptually:
State ↓Node Execution ↓State Update ↓Next NodeThis loop continues until the workflow finishes.
Defining the Agent State
The agent state stores all information required during execution.
Example fields include:
- user goal
- reasoning history
- observations
- current step
Example structure:
class AgentState:
def __init__(self, goal): self.goal = goal self.history = [] self.observations = [] self.step = 0struct AgentState { goal: String, history: Vec<String>, observations: Vec<String>, step: u32,}This structure persists information across reasoning steps.
Defining Nodes
A node represents a stage in the workflow.
Examples include:
| Node Type | Purpose |
|---|---|
| reasoning node | generate next action |
| tool node | execute external tool |
| observation node | update state |
Conceptually:
Node(Input State) → Output StateExample Reasoning Node
The reasoning node calls the language model.
def reasoning_node(state, llm):
prompt = f"Goal: {state.goal}\nHistory: {state.history}"
thought = llm.generate(prompt)
state.history.append(thought)
return thoughtfn reasoning_node(state: &mut AgentState, llm: &LLM) -> String {
let prompt = format!("Goal: {}\nHistory: {:?}", state.goal, state.history);
let thought = llm.generate(&prompt);
state.history.push(thought.clone());
thought}This node produces the next reasoning step.
Tool Execution Node
The tool node dispatches tool calls generated by the model.
def tool_node(action, tools):
tool = tools[action["name"]]
result = tool(**action["args"])
return resultfn tool_node(action: Action, tools: &Tools) -> String {
tools.execute(action.name, action.args)}The result becomes a new observation.
Updating the State
Observations are added to the agent state.
Example:
Observation → added to state.observationsstate.observations.append(result)state.observations.push(result);This information influences the next reasoning step.
Graph-Based Execution
Agent workflows can be represented as a graph of nodes.
Example graph:
Reason → Tool → Observe → ReasonGraph representation:
Nodes ├─ Reason ├─ Tool └─ ObserveTransitions define how execution moves between nodes.
Minimal Graph Executor
The executor controls the workflow.
Conceptually:
Initialize state↓Execute node↓Update state↓Select next node↓RepeatExample Executor Implementation
def run_agent(goal, tools, llm):
state = AgentState(goal)
while True:
thought = reasoning_node(state, llm)
action = parse_action(thought)
if action["name"] == "finish": return action["args"]["answer"]
result = tool_node(action, tools)
state.observations.append(result)
state.step += 1fn run_agent(goal: &str, tools: &Tools, llm: &LLM) -> String {
let mut state = AgentState { goal: goal.to_string(), history: vec![], observations: vec![], step: 0, };
loop {
let thought = reasoning_node(&mut state, llm);
let action = parse_action(&thought);
if action.name == "finish" { return action.answer; }
let result = tool_node(action, tools);
state.observations.push(result);
state.step += 1; }}This loop forms a fully functional minimal agent runtime.
Adding Time-Travel Debugging
The runtime can also record snapshots.
Example:
Step 1 → reasoningStep 2 → tool callStep 3 → observationThese snapshots enable replay and debugging.
Runtime Architecture Overview
After combining all components, the final runtime looks like this:
Minimal Agent Framework ├─ Agent State ├─ Reasoning Node ├─ Tool Node ├─ Graph Executor ├─ Tool Registry └─ Execution HistoryThis architecture closely mirrors modern agent frameworks.
Comparing with LangGraph
LangGraph provides additional features such as:
- persistent execution
- graph branching
- checkpointing
- advanced debugging tools
However, the core execution logic remains similar.
| Feature | Minimal Runtime | LangGraph |
|---|---|---|
| agent loop | ✔ | ✔ |
| tool calling | ✔ | ✔ |
| graph execution | basic | advanced |
| persistence | ✖ | ✔ |
| distributed execution | ✖ | ✔ |
Understanding the minimal version makes it easier to understand advanced frameworks.
What We Built in Module 11
In this module we constructed the core components of an agent runtime.
| Article | Concept |
|---|---|
| 11.1 | why build an agent runtime |
| 11.2 | agent state machine |
| 11.3 | tool calling system |
| 11.4 | time-travel debugging |
| 11.5 | minimal agent framework |
These components form the foundation of modern agent systems.
Looking Ahead
In the final module of this series we will build complete agent applications.
These capstone projects will combine everything covered so far, including:
- planning systems
- tool usage
- multi-agent collaboration
- computer-use capabilities
→ Continue to 12.1 — The Computer-Use Researcher