mirror of
https://github.com/magnus919/agent-skills.git
synced 2026-09-15 21:46:29 +03:00
Comprehensive LangGraph skill covering: - Core architecture: Graph API, Functional API, state management, agent loops - Three multi-agent patterns: supervisor (~94% accuracy), swarm (~40% fewer LLM calls), hierarchical teams (subgraphs with nested state) - Persistence: checkpointers vs stores, per-invocation/per-thread/stateless modes - Production: Agent Server deployment, LangSmith observability, 8 failure modes - Evals: routing accuracy, resolution coverage, LLM-as-judge methodology - Troubleshooting: symptom→cause→fix tables per pattern - 3 Python scripts: supervisor scaffold, swarm scaffold, eval generator - 3 runnable templates: supervisor, swarm, subgraph composition Ships 8 reference files, 3 scripts, and 3 templates.
5.9 KiB
5.9 KiB
Troubleshooting — Common Failures and Fixes
This reference catalogs the most common LangGraph failure modes, their symptoms, root causes, and fixes. Organized by pattern and symptom for quick lookup.
Supervisor Pattern Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
| Same agent called repeatedly in a loop | Routing loop — supervisor doesn't know the agent already handled it | Include resolution_notes in supervisor context |
| Slow response times under load | Supervisor bottleneck — even obvious intents get a routing LLM call | Add fast-path classifier for unambiguous intents |
| High token costs per request | Token waste on re-routing | Compare supervisor vs swarm token costs in LangSmith |
| Agent handles wrong domain | Routing accuracy regression | Check routing prompt, add negative feedback to eval dataset |
Routing Loop — Full Diagnosis
- Open LangSmith trace
- Look for repeating
supervisor → billing → supervisor → billingpattern - Check if
resolution_notesis being passed to the supervisor node - If missing, the supervisor has no memory of what's been done
- Fix: ensure supervisor prompt includes resolution notes:
def supervisor(state: MultiAgentState) -> dict:
notes = "\n".join(state.get("resolution_notes", []))
history_context = f"\n\nAlready resolved:\n{notes}" if notes else ""
# ... rest of supervisor
Swarm Pattern Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
| Infinite handoff loop | Swarm ping-pong — no recursion guard | Track handoff_count, hard limit at 3 |
| Customer asked the same question twice | Context loss on handoff — Agent B doesn't know what Agent A did | Propagate resolution context via Command.update |
| Next agent sees malformed history | Lost messages during handoff — ToolMessage pairing broken | Ensure Command.update includes paired messages |
| Agent routes to wrong specialist | Routing accuracy issue | Add more specific guidance in system prompt |
Swarm Ping-Pong — Fix
def route_after_agent(state: SwarmState) -> str:
if state.get("handoff_count", 0) >= 3:
return "human_escalation" # or END with a fallback
# ... rest of routing logic
Context Loss — Fix
When a handoff tool returns Command, include what was done in the update:
@tool
def transfer_to_billing(reason: str, context: dict = None) -> Command:
update = {"current_agent": "billing", "handoff_count": context["count"] + 1}
return Command(goto="billing", update=update, graph=Command.PARENT)
Subgraph Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
| Subgraph doesn't remember previous calls | Per-invocation default | Compile with checkpointer=True for per-thread memory |
| Tool calls fail with checkpoint conflict | Parallel calls to per-thread subgraph | Add ToolCallLimitMiddleware |
| Parallel subgraph calls overwrite each other | Same namespace conflict | Use create_sub_agent wrapper with unique names |
| Subgraph crashes without recovery | Stateless mode (checkpointer=False) |
Use per-invocation (default) for durability |
| Parent can't see subgraph state | Different checkpoint namespaces | Use Store for cross-boundary data |
Persistence Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
thread_id too long error |
Postgres column length limit | Keep under 255 chars, use UUID |
| State lost on restart | Using in-memory MemorySaver |
Switch to PostgresSaver or SqliteSaver |
| Checkpoints growing unbounded | No retention policy | Add cron job to prune old checkpoints |
interrupt() has no effect |
No checkpointer configured | Compile parent graph with checkpointer |
Stream and Event Failures
| Symptom | Likely Cause | Fix |
|---|---|---|
| Subgraph events not visible in stream | Using wrong event protocol | Use stream.subgraphs projection or filter by namespace |
| Stream hangs | Graph in infinite loop | Add recursion guard or max iteration limit |
| Event namespace is empty string | Parent-level event, not subgraph | Filter by namespace: [] = parent, [...] = subgraph |
Debugging Techniques
1. Check the Trace
LangSmith traces show every node execution, including:
- Input/output state per node
- Tool calls and their results
- Routing decisions (from structured output)
- Handoff chains (from Command objects)
2. Isolate the Subgraph
Test any subgraph independently before composing it:
# Test subgraph in isolation
result = subgraph.invoke({"topic": "test"})
print(result)
3. Reduce Parallelism
When debugging, set max_concurrency=1 to serialize execution:
result = graph.invoke(inputs, {"max_concurrency": 1})
4. Add Logging to Nodes
Wrap nodes with print statements or pass through LangSmith:
def debug_node(state: State) -> dict:
print(f"enter {node_name}: keys={list(state.keys())}")
result = actual_node(state)
print(f"exit {node_name}: keys={list(result.keys())}")
return result
5. Use get_state for Subgraph Inspection
state = graph.get_state(config, subgraphs=True)
for task in state.tasks:
if task.state:
print(f"Subgraph state: {task.state}")
Verification Checklist
When deploying a new multi-agent system, verify these in order:
- Single agent works independently (no routing dependencies)
- Supervisor/swarm routes correctly for single-domain requests
- Multi-domain requests are decomposed and handled completely
- Handoffs preserve context (no repeated questions)
- Recursion guard prevents infinite loops (supervisor and swarm)
- All errors are caught and produce graceful degradation
- Persistence works across thread resumptions
- Subgraphs are independently testable
- Eval dataset covers all routing paths
- LangSmith traces are readable and complete