This article is published in English.
Practical notes: How to Build Better AI Agents with LangGraph
Operable walkthrough of Practical notes: How to Build Better AI Agents with LangGraph: contracts, checks, and drop-in code slots for teams shipping this pattern.
The following notes reconstruct a practical path around “How to Build Better AI Agents with LangGraph”. Emphasis stays on contracts, checks, and drop-in code placeholders rather than motivational framing. When working through the Overview stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
1. Research Outline: Mastering Agentic Workflows
The 1 Research Outline Mastering stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
2. The Solution: A Self-Correcting Search Agent
The 2 The Solution A stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
Imports
The Imports stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
import operator
from typing import Annotated, TypedDict, Union
from langgraph.graph import StateGraph, START, END
The Shared Brain
The The Shared Brain stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit without reading the whole graph. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
# 1. State: The agent's shared memory
class AgentState(TypedDict):
# 'operator.add' lets us append messages instead of overwriting
messages: Annotated[list[str], operator.add]
attempts: int
found_info: bool
The Worker Nodes
The The Worker Nodes stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
# 2. Nodes: Individual 'steps' in the process
def search_node(state: AgentState):
print(f\n"--- Attempt {state['attempts'] + 1}: Searching ---")
# Simulating a logic check
success = state['attempts'] >= 1
msg = "Success: Found LangGraph info!" if success else "No results found."
return {"messages": [msg], "attempts": state['attempts'] + 1, "found_info": success}
def refine_query_node(state: AgentState):
print("\n--- Refining query for better results ---")
return {"messages": ["System: Query refined."]}
The Routing Logic
The The Routing Logic stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
def should_continue(state: AgentState):
if state["found_info"] or state["attempts"] >= 3:
return "end"
return "refine"
Building the Graph
The Building the Graph stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
# 4. Build the Graph
workflow = StateGraph(AgentState)
workflow.add_node("search", search_node)
workflow.add_node("refine", refine_query_node)
workflow.add_edge(START, "search")
workflow.add_conditional_edges("search", should_continue, {"refine": "refine", "end": END})
workflow.add_edge("refine", "search")
Run the Agent
The Run the Agent stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
# 5. Execute
app = workflow.compile()
for output in app.stream({"messages": [], "attempts": 0, "found_info": False}):
print(output)
--- Attempt 1: Searching ---
{'search': {'messages': ['No results found.'], 'attempts': 1, 'found_info': False}}
--- Refining query for better results ---
{'refine': {'messages': ['System: Query refined.']}}
--- Attempt 2: Searching ---
{'search': {'messages': ['Success: Found LangGraph info!'], 'attempts': 2, 'found_info': True}}
The Run the Agent stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
3. Five Tips to Level Up Your LangGraph Game
For the 3 Five Tips to stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Tip 1: Nail Your State Schema
For the Tip 1 Nail Your stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness.
Tip 2: Master Conditional Edges
For the Tip 2 Master Conditional stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Put human approval on edges that spend money or change production data. Compile-time wiring does not equal business completeness. For the Tip 2 Master Conditional stage, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Tip 3: Don’t Forget Persistence (Checkpointing)
When working through the Tip 3 Don t stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
Tip 4: Embrace the Human-in-the-Loop
When working through the Tip 4 Embrace the stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node.
Tip 5: Keep Your Nodes Small
When working through the Tip 5 Keep Your stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Record timings and token or query cost next to functional results. Cost visibility early prevents surprise bills when the path moves from demo to shared environments. Checkpoint after expensive steps. Resume should not re-bill the same LLM call when an operator retries a later node. When working through the Tip 5 Keep Your stage, write down the contract first: required inputs, success signal, and what happens on partial failure. That checklist keeps later code changes honest. Document the happy path and the recovery path together. Retries, human gates, and dead-letter handling are part of the product, not later polish.
Real-Life Application
The Real-Life Application stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility rather than a tangled pipeline. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.
References
The References stage works best when treated as a measurable surface. Capture one golden transcript, one failure case, and the rollback note before expanding scope. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion.