Langgraph state machine designer
Skill Notysoty/openagentskills/skills/langgraph-state-machine-designer
Converts a workflow description into a LangGraph node/edge graph with typed state, conditional routing, and human-in-the-loop checkpoints.From its SKILL.md
npx -y skills add Notysoty/openagentskills --skill langgraph-state-machine-designerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 9 stars9 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
6.5 KB, ~1.4k tokens by cl100k_base, as published. Nobody here has run it
LangGraph State Machine Designer
What this skill does
This skill takes a plain-language description of an agentic workflow and designs the corresponding LangGraph state machine: typed state schema, node functions, conditional edges, and checkpoint configuration. It handles the hard parts — state typing, routing logic, error recovery, and human-in-the-loop interrupts.
How to use
Claude Code / Cline
Copy this file to .agents/skills/langgraph-state-machine-designer/SKILL.md in your project root.
Then ask:
- "Use the LangGraph State Machine Designer to build a research-then-write workflow."
- "Design a LangGraph agent that routes between a SQL tool and a web search tool."
Provide:
- What the agent should do (in plain English)
- What tools or actions it has available
- Whether humans need to approve any steps
- Your LangGraph version (v0.2+ assumed)
Cursor / Codex
Describe the workflow and paste these instructions. Ask for the full graph code.
The Prompt / Instructions for the Agent
When asked to design a LangGraph state machine, produce the following:
Step 1 — Define the TypedDict state
Every LangGraph graph has a single shared state object. Define it as a TypedDict with Annotated fields for lists (so they append rather than overwrite):
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage
import operator
class AgentState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
# Add task-specific fields:
query: str
search_results: list[str]
draft: str
approved: bool
error: str | None
Rules for state design:
- Use
Annotated[list, operator.add]for any field that accumulates over time (messages, results) - Use plain types for fields that get overwritten each step (current_step, status)
- Add an
errorfield to every state — nodes should write errors here instead of raising
Step 2 — Design the nodes
Each node is a function that takes state and returns a partial state update:
def call_model(state: AgentState) -> dict:
"""Main LLM reasoning node."""
response = llm.invoke(state["messages"])
return {"messages": [response]}
def run_tool(state: AgentState) -> dict:
"""Execute the tool the model requested."""
last_message = state["messages"][-1]
tool_result = execute_tool(last_message.tool_calls[0])
return {"messages": [ToolMessage(content=tool_result)]}
def handle_error(state: AgentState) -> dict:
"""Graceful error recovery node."""
return {"messages": [AIMessage(content="I encountered an error. Let me try a different approach.")], "error": None}
Node design rules:
- Nodes should do ONE thing (single responsibility)
- Always return a dict — never mutate state directly
- Catch exceptions inside nodes and write to
state["error"] - Keep nodes stateless — all context comes from state
Step 3 — Design the edges and routing
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
builder = StateGraph(AgentState)
# Add nodes
builder.add_node("agent", call_model)
builder.add_node("tools", ToolNode(tools))
builder.add_node("error_handler", handle_error)
# Set entry point
builder.set_entry_point("agent")
# Conditional routing
def should_continue(state: AgentState) -> str:
last = state["messages"][-1]
if state.get("error"):
return "error_handler"
if hasattr(last, "tool_calls") and last.tool_calls:
return "tools"
return END
builder.add_conditional_edges("agent", should_continue, {
"tools": "tools",
"error_handler": "error_handler",
END: END
})
builder.add_edge("tools", "agent")
builder.add_edge("error_handler", "agent")
Step 4 — Add human-in-the-loop (if needed)
For workflows requiring human approval before destructive actions:
from langgraph.checkpoint.memory import MemorySaver
# Compile with checkpointer
memory = MemorySaver()
graph = builder.compile(
checkpointer=memory,
interrupt_before=["execute_action"] # pause before this node
)
# Run until interrupt
config = {"configurable": {"thread_id": "thread_001"}}
result = graph.invoke({"messages": [HumanMessage(content=user_query)]}, config)
# Resume after human approval
graph.invoke(None, config) # continues from checkpoint
Step 5 — Standard graph patterns
Pattern: ReAct agent (reason → act → observe loop)
entry → agent → [tools | END]
↑_______|
Pattern: Multi-step pipeline (sequential)
entry → step_1 → step_2 → step_3 → END
Pattern: Supervisor with sub-agents
entry → supervisor → [researcher | writer | reviewer | END]
↑___________________________|
Pattern: Retry with fallback
entry → primary_agent → [success: END | failure: fallback_agent → END]
Step 6 — Output the complete graph
Always produce:
- Full
AgentStateTypedDict - All node functions with docstrings
- Complete graph builder with edges
- Compiled graph with appropriate checkpointer
- Example invocation with config
Example
Input:
"Design a LangGraph agent that takes a user question, searches the web, then writes a summarized answer. No human approval needed."
Output:
from typing import TypedDict, Annotated, Sequence
import operator
from langchain_core.messages import BaseMessage, HumanMessage
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
class ResearchState(TypedDict):
messages: Annotated[Sequence[BaseMessage], operator.add]
query: str
def research_agent(state: ResearchState) -> dict:
response = llm_with_tools.invoke(state["messages"])
return {"messages": [response]}
def should_continue(state: ResearchState) -> str:
last = state["messages"][-1]
if hasattr(last, "tool_calls") and last.tool_calls:
return "search"
return END
builder = StateGraph(ResearchState)
builder.add_node("agent", research_agent)
builder.add_node("search", ToolNode([web_search_tool]))
builder.set_entry_point("agent")
builder.add_conditional_edges("agent", should_continue, {"search": "search", END: END})
builder.add_edge("search", "agent")
graph = builder.compile()
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.