Governed LangGraph in 3 Minutes
LangGraph’s most popular pattern is a supervisor with workers: the supervisor node routes to a researcher, a coder, or a reviewer based on state, then loops until done. It’s the reference architecture for every serious multi-agent system built on LangChain.
It’s also a delegation chain. Nobody calls it that, but that’s what it is — one agent decides, hands work to another, and expects a result back. The supervisor is accountable for what the workers do.
Which is a problem, because LangGraph gives you zero tools for enforcing that accountability. The workers share credentials with the supervisor. Their tool access is whatever you happened to put in the tools list. And when something goes wrong, the trace shows which node ran but not what permissions that node actually had at the time.
Agentic Control Plane fixes this at the tool layer — so you don’t have to rewrite your graph.
The 3-minute setup
pip install acp-langchain. Stack @governed under @tool. Bind the user’s JWT. Done.
import os
from acp_langchain import configure, governed, set_context
from langchain.agents import create_agent
from langchain.tools import tool
configure(base_url="https://api.agenticcontrolplane.com")
@tool
@governed("web_search")
def web_search(query: str) -> str:
"""Search the web."""
return my_search(query)
@tool
@governed("send_email")
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email."""
return sendmail(to, subject, body)
def run(prompt: str, user_jwt: str):
set_context(
user_token=user_jwt,
agent_name="my-langgraph-agent",
agent_tier="background",
)
agent = create_agent(
model="openai:gpt-4o-mini",
tools=[web_search, send_email],
)
result = agent.invoke({"messages": [{"role": "user", "content": prompt}]})
return result["messages"][-1].content
Every tool call your graph makes — supervisor’s, researcher’s, every worker’s — flows through ACP’s /govern/tool-use endpoint before the tool runs, and through /govern/tool-output after. Every call is identity-attributed, audited, and policy-checked.
create_agent is the 2026 idiom (it replaces the legacy langgraph.prebuilt.create_react_agent). For custom StateGraph builds, the decorator pattern is identical — what matters is the tool decoration, not the graph shape.
What you get for free
Open the Activity dashboard. One row per tool call, each with:
- Actor — the user from
set_context(user_token=...) - Tool —
web_search,send_email, whatever you passed to@governed("...") - Decision — allow / deny / redact / fail-open + reason
- Session — groups all tool calls from one
set_contextscope - Findings — PII or secret patterns detected
LangGraph tool calls sit alongside Claude Code, Cursor, CrewAI, and every other framework wired into ACP — one audit log across every agent surface in your stack.
What changes when policy enforces
Per-tool denials become tool errors. When ACP denies a call (rate limit, scope violation, PII detection), @governed returns the string "tool_error: <reason>". LangChain treats this as the tool’s output; the model sees it and adapts. Your graph keeps running.
Output redaction is automatic. If your policy redacts PII or secrets in tool outputs, the redacted version replaces the original before the model sees it. The model never sees [redacted:credit_card]’s original value, even when the model itself called the tool that returned it.
Identity flows through every hop. set_context uses Python’s contextvars — every governed call inside the request scope reads the bound user identity automatically. You don’t thread the JWT through nodes manually.
Custom StateGraph
The decorator pattern works regardless of how you build the graph. For a custom supervisor-worker graph:
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolNode
from acp_langchain import governed
@tool
@governed("query_db")
def query_db(sql: str) -> str:
"""Run a SQL query."""
return db.execute(sql)
# ToolNode dispatches tool calls — @governed runs on every dispatch.
tools_node = ToolNode([query_db])
graph = StateGraph(MyState)
graph.add_node("tools", tools_node)
# rest of your graph as you'd normally build it
The tool decoration is what matters. ACP doesn’t care about your graph shape — it cares about your tool boundaries.
Per-tier policy
set_context(agent_tier="...") controls which policy tier evaluates the call:
interactive— a real user is at the keyboard. Permissive default.subagent— invoked by another agent, no human in the immediate loop.background— autonomous, no human anywhere. Most restrictive.api— programmatic call from your backend.
A destructive tool (Bash.kubectl, db.delete, wire_transfer) typically gets deny in background, ask in subagent, allow in interactive. Match the tier to actual deployment reality, not what’s permissive.
Alternative: LLM-layer governance
If you want LLM-call audit, per-agent cost attribution, or budget enforcement at the model layer in addition to (or instead of) tool-layer governance, point your model at ACP’s OpenAI-compatible proxy:
from langchain_openai import ChatOpenAI
governed_llm = ChatOpenAI(
base_url="https://api.agenticcontrolplane.com/v1",
api_key=os.environ["ACP_API_KEY"],
model="claude-sonnet-4-6",
)
Both patterns are valid and composable. Tool-layer (the @governed decorator) is the primary recommendation because it gives you per-tool-call interception, output redaction, and per-user identity attribution. LLM-layer (OAI proxy) adds budget enforcement and per-agent cost rollups. Use one, the other, or both.
What this unlocks
LangGraph is the most widely deployed Python agent framework. Three lines of integration — configure, @governed, set_context — and your graph becomes auditable, identity-attributed, and policy-bound without rewriting a single node.
Your graph stays a graph. Your governance becomes a control plane. They stay out of each other’s way.
Install ACP free · LangGraph integration guide · LangGraph governance scorecard · Delegation chains deep dive
- 1. Governance for Claude Code in 60 seconds
- 2. Governing the Anthropic Agent SDK
- 3. Governed LangGraph in 3 Minutes · you are here
- 4. Governed CrewAI in 3 Minutes
- 5. Governed Cursor in 3 Minutes
- 6. Governed Codex CLI in 3 Minutes
- 7. Governed OpenAI Agents SDK in 3 Minutes