From 2c193a5ca0c3ead893efed39e709a07f69794963 Mon Sep 17 00:00:00 2001 From: Sriram Rampelli Date: Thu, 9 Jul 2026 09:28:36 -0400 Subject: [PATCH 1/2] docs(langgraph): add Pramagent tool policy guide --- src/docs.json | 1 + src/oss/langgraph/pramagent-tool-policy.mdx | 133 ++++++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/oss/langgraph/pramagent-tool-policy.mdx diff --git a/src/docs.json b/src/docs.json index 9141ca051d..32cd135902 100644 --- a/src/docs.json +++ b/src/docs.json @@ -429,6 +429,7 @@ "group": "Production", "pages": [ "oss/python/langgraph/application-structure", + "oss/python/langgraph/pramagent-tool-policy", "oss/python/langgraph/test", "oss/python/langgraph/backward-compatibility", "oss/python/langgraph/studio", diff --git a/src/oss/langgraph/pramagent-tool-policy.mdx b/src/oss/langgraph/pramagent-tool-policy.mdx new file mode 100644 index 0000000000..7adf78fac5 --- /dev/null +++ b/src/oss/langgraph/pramagent-tool-policy.mdx @@ -0,0 +1,133 @@ +--- +title: Pramagent tool policy +sidebarTitle: Pramagent tool policy +--- + +This guide shows how to add an external policy gate in front of LangGraph tool execution with [Pramagent](https://github.com/sriram7737/pramagent). Use this pattern when a model can propose sensitive tool calls, but execution should still be checked by deterministic policy before the tool runs. + +LangGraph already gives you orchestration, persistence, interrupts, and human-in-the-loop control. Pramagent complements those features by evaluating the proposed tool name and arguments before any side effect happens. + +## When to use this pattern + +Use an external tool policy layer when tools can: + +- mutate data +- spend money +- send messages outside your system +- delete or overwrite state +- access tenant-scoped or regulated resources + +The important boundary is: the model may propose a tool call, but the policy layer decides whether that call is allowed to execute. + +## Install + +```bash +pip install langgraph langchain-core pramagent +``` + +## Define a ToolGuard policy + +Pramagent's `ToolGuardLayer` validates the tool arguments with JSON Schema, checks tenant and action allow-lists, classifies the side effect, and returns a deterministic verdict. + +```python +from pramagent import Pramagent, Verdict +from pramagent.layers import ToolGuardLayer, ToolPolicy +from pramagent.layers.tool_guard import SideEffect + +tool_guard = ToolGuardLayer( + policies=[ + ToolPolicy( + name="send_payment", + side_effect=SideEffect.PAYMENT, + action=Verdict.ESCALATE, + allowed_tenants={"finance_team"}, + schema={ + "type": "object", + "required": ["amount_usd", "destination"], + "properties": { + "amount_usd": { + "type": "number", + "minimum": 0.01, + "maximum": 5000, + }, + "destination": { + "type": "string", + "pattern": r"acct-\d{6,}", + }, + }, + "additionalProperties": False, + }, + ) + ] +) + +armor = Pramagent(tool_guard=tool_guard) +``` + +In this policy, valid payments are escalated for human approval. Invalid payments, such as an amount above the schema maximum or a destination that does not match the account pattern, are blocked before execution. + +## Guard a LangGraph tool + +Wrap the side-effecting operation so it calls `validate_tool` before performing the real action. + +```python +from langchain_core.tools import tool +from langgraph.prebuilt import ToolNode +from pramagent import Verdict + + +@tool +def send_payment(amount_usd: float, destination: str) -> str: + """Send a payment to an account.""" + decision = armor.validate_tool( + "send_payment", + {"amount_usd": amount_usd, "destination": destination}, + tenant_id="finance_team", + session_id="invoice-agent-demo", + action_label="payment", + ) + + if decision.verdict == Verdict.BLOCK: + raise PermissionError(f"Payment blocked: {decision.reason}") + + if decision.verdict == Verdict.ESCALATE: + raise PermissionError( + f"Payment requires human approval before execution: {decision.reason}" + ) + + # Execute the real side effect only after the policy gate allows it. + return f"sent ${amount_usd:.2f} to {destination}" + + +tools = [send_payment] +tool_node = ToolNode(tools) +``` + +Add the guarded tool node to your graph the same way you would add any other `ToolNode`: + +```python +from langgraph.graph import END, START, MessagesState, StateGraph + +builder = StateGraph(MessagesState) +builder.add_node("tools", tool_node) +builder.add_edge(START, "tools") +builder.add_edge("tools", END) + +graph = builder.compile() +``` + +## Combine with interrupts + +If `validate_tool` returns `Verdict.ESCALATE`, route the request into a [LangGraph interrupt](/oss/python/langgraph/interrupts) instead of executing the tool immediately. Store the proposed tool call in graph state, pause for approval, and then re-run the side effect after the approval response is recorded. + +For production systems, also follow the [idempotency guidance](/oss/python/langgraph/functional-api#idempotency): any external action that can run after a resume should use an idempotency key or a read-before-write check. + +## What this adds + +This pattern gives the graph a separate control boundary: + +- LangGraph controls orchestration, state, persistence, and resumability. +- Pramagent controls whether a proposed tool call is allowed, blocked, or escalated before execution. +- The tool body remains the only place where the side effect actually happens. + +That separation is useful when tool approvals need to be reviewed as policy, tested independently, and audited after the run. From 8416b3a5c614dec6e9382f00f66ebdf5a71003e0 Mon Sep 17 00:00:00 2001 From: Sriram Rampelli Date: Thu, 9 Jul 2026 09:35:17 -0400 Subject: [PATCH 2/2] docs(langgraph): use langchain tool import --- src/oss/langgraph/pramagent-tool-policy.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/oss/langgraph/pramagent-tool-policy.mdx b/src/oss/langgraph/pramagent-tool-policy.mdx index 7adf78fac5..5d214fdb63 100644 --- a/src/oss/langgraph/pramagent-tool-policy.mdx +++ b/src/oss/langgraph/pramagent-tool-policy.mdx @@ -22,7 +22,7 @@ The important boundary is: the model may propose a tool call, but the policy lay ## Install ```bash -pip install langgraph langchain-core pramagent +pip install langgraph langchain pramagent ``` ## Define a ToolGuard policy @@ -71,7 +71,7 @@ In this policy, valid payments are escalated for human approval. Invalid payment Wrap the side-effecting operation so it calls `validate_tool` before performing the real action. ```python -from langchain_core.tools import tool +from langchain.tools import tool from langgraph.prebuilt import ToolNode from pramagent import Verdict