Skip to content
Agentic Control Plane
Quick-start series · Part 3 of 7
Governance in Three Minutes →

Governed LangGraph in 3 Minutes

David Crowe · 6 min read
langgraph langchain a2a delegation

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=...)
  • Toolweb_search, send_email, whatever you passed to @governed("...")
  • Decision — allow / deny / redact / fail-open + reason
  • Session — groups all tool calls from one set_context scope
  • 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

Get the next post
Agentic governance, AgentGovBench updates, the occasional incident post-mortem. One email per post. No marketing fluff.
Share: Twitter LinkedIn
More in Governance in Three Minutes
  1. 1. Governance for Claude Code in 60 seconds
  2. 2. Governing the Anthropic Agent SDK
  3. 3. Governed LangGraph in 3 Minutes · you are here
  4. 4. Governed CrewAI in 3 Minutes
  5. 5. Governed Cursor in 3 Minutes
  6. 6. Governed Codex CLI in 3 Minutes
  7. 7. Governed OpenAI Agents SDK in 3 Minutes
Related posts

← back to blog