AI Agents — A Graph, Not a Model
LangGraph — Build an Agent That Stops
Goal
You build a small agent with LangGraph that answers questions about inventory. It has eight steps: a graph, accumulating state, a conditional branch, a loop that stops, a tool node, a failure path, a checkpointer and a path record.
No model is called
The node that makes the judgment is written as rules. This is because what this lab teaches is the graph, not the model call — when to stop, what to carry in the state, and where to go when a tool fails. That is where the real difficulty in agents is, and attaching a model is later a matter of swapping one node.
Format
Put the following in /root/work/agent/graph.py.
build_graph(checkpoint: bool = False)— returns a compiled graph.CATALOG— a dictionary of stock per item (from step 5).
The state has at least question, steps (a list), tries (an integer) and answer.
Set up a graph and run it
After mkdir -p /root/work/agent, put build_graph() in graph.py. Create a StateGraph, add one node, and return the result of compile(). Put a steps list in the state and have the node leave its own name in it.
build_graph().invoke() runs and the node name is left in steps
Make the state accumulate
Increase the nodes to two and have both leave their names in steps. If you do not use Annotated[list, operator.add], only the last one remains — the path taken disappears.
It passes through two or more different nodes and all of them remain in steps
Split the branch by the question
Build the branch with add_conditional_edges. A question containing the word for "stock" (inventory) goes to the lookup node, and anything else goes to another node.
A stock question passes through lookup and a greeting does not
Loop, but always stop
Build a loop that retries when it cannot find the item. Put tries in the state and set a limit (about 3 attempts). Without a limit, it hits the recursion limit and dies with an exception.
The loop runs at least twice and stops by itself within 5 times
Actually use the tool's result
Put at least 3 entries in the CATALOG dictionary (item: quantity), and have lookup find the item in the question and look it up. The looked-up value must actually go into answer.
The value from CATALOG is reflected in answer
A tool failure is a path, not an exception
If you ask about something that is not in the list, go to a fallback node and make "could not find it" the answer. If you pass the empty result along as it is, it will make things up, and that is the most common route to hallucination.
A question about a missing item passes through fallback and answer carries the meaning "I don't know"
Continue the conversation
Make it accept build_graph(checkpoint=True) and attach a MemorySaver. Check whether the state is separated by thread_id — the same id continues and a different id is kept separately.
The state remains through get_state and is managed separately per thread_id
Record the path taken
Run two questions that take different branches and save {질문: [노드, ...]} (a mapping from each question to its list of nodes) to /root/work/agent/08-trace.json. If the record differs from the actual execution, you cannot diagnose anything from that record.
The paths in 08-trace.json match the actual execution and the two paths differ from each other