Back to blog
xywa23

Build a Safe LangChain Agent in 5 Minutes

Add permission control to your LangChain agent with just 3 lines of code.

langchaintutorialsecurityai-agents

Your LangChain agent can call any tool you give it. That's powerful—and dangerous.

In this tutorial, you'll add permission control to a LangChain agent in under 5 minutes. No architecture changes. No complex setup. Just a decorator.

The Problem

Here's a typical LangChain agent:

from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI

@tool
def get_customer_data(customer_id: str) -> dict:
    """Fetch customer data from database."""
    return {"id": customer_id, "email": "alice@example.com", "balance": 5000}

@tool
def issue_refund(customer_id: str, amount: float) -> dict:
    """Process a refund for a customer."""
    # This hits your payment API!
    return {"status": "refunded", "amount": amount}

@tool
def delete_customer(customer_id: str) -> dict:
    """Delete a customer from the database."""
    # This is destructive!
    return {"status": "deleted"}

llm = ChatOpenAI(model="gpt-4")
tools = [get_customer_data, issue_refund, delete_customer]
agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)

# The agent can call ANY of these tools
executor.invoke({"input": "Delete customer 123"})  # 😱

The agent has access to everything. If it hallucinates or gets prompt-injected, it could:

  • Issue unauthorized refunds
  • Delete customer data
  • Access sensitive information

The Solution: AgentSudo

Install AgentSudo:

pip install agentsudo

Now add permission control with 3 lines of code:

from langchain.tools import tool
from agentsudo import Agent, sudo

# 1. Create an agent identity with specific permissions
support_bot = Agent(
    name="SupportBot",
    scopes=["read:customers", "write:refunds"]  # Can read and refund, but NOT delete
)

# 2. Protect your tools with @sudo
@tool
@sudo(scope="read:customers")
def get_customer_data(customer_id: str) -> dict:
    """Fetch customer data from database."""
    return {"id": customer_id, "email": "alice@example.com", "balance": 5000}

@tool
@sudo(scope="write:refunds")
def issue_refund(customer_id: str, amount: float) -> dict:
    """Process a refund for a customer."""
    return {"status": "refunded", "amount": amount}

@tool
@sudo(scope="delete:customers")
def delete_customer(customer_id: str) -> dict:
    """Delete a customer from the database."""
    return {"status": "deleted"}

# 3. Run your agent in a session
with support_bot.start_session():
    executor.invoke({"input": "Get customer 123"})      # ✅ Allowed
    executor.invoke({"input": "Refund $50 to customer 123"})  # ✅ Allowed
    executor.invoke({"input": "Delete customer 123"})   # ❌ PermissionDeniedError

That's it. The agent can only use tools it has permission for.

What Just Happened?

  1. Agent Identity: We created a SupportBot with specific scopes (read:customers, write:refunds)
  2. Protected Tools: Each tool requires a scope via @sudo(scope="...")
  3. Session Context: Inside start_session(), AgentSudo checks every tool call

When the agent tries to call delete_customer, AgentSudo sees that SupportBot doesn't have delete:customers scope and raises PermissionDeniedError.

Different Agents, Different Permissions

Create multiple agents with different access levels:

# Support can read and refund
support_bot = Agent(
    name="SupportBot",
    scopes=["read:customers", "write:refunds"]
)

# Analytics can only read
analytics_bot = Agent(
    name="AnalyticsBot",
    scopes=["read:*"]  # Wildcard: read anything
)

# Admin can do everything
admin_bot = Agent(
    name="AdminBot",
    scopes=["*"]  # Full access
)

Audit Mode: Log Without Blocking

Rolling out to production? Use audit mode to log violations without breaking anything:

@tool
@sudo(scope="delete:customers", on_deny="log")
def delete_customer(customer_id: str) -> dict:
    """Delete a customer from the database."""
    return {"status": "deleted"}

# Now unauthorized calls are LOGGED but still execute
# Check your logs to see what would have been blocked

Human-in-the-Loop: Require Approval

For high-risk actions, require human approval:

def slack_approval(agent, scope, context):
    """Ask a human via Slack."""
    response = ask_slack(f"Approve {agent.name} for {scope}?")
    return response == "yes"

@tool
@sudo(scope="delete:customers", on_deny=slack_approval)
def delete_customer(customer_id: str) -> dict:
    """Delete a customer from the database."""
    return {"status": "deleted"}

Full Example

Here's a complete working example:

from langchain.tools import tool
from langchain.agents import create_tool_calling_agent, AgentExecutor
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from agentsudo import Agent, sudo, PermissionDeniedError

# Define protected tools
@tool
@sudo(scope="read:orders")
def get_order(order_id: str) -> dict:
    """Get order details."""
    return {"id": order_id, "status": "shipped", "total": 99.99}

@tool
@sudo(scope="write:refunds")
def process_refund(order_id: str, amount: float) -> dict:
    """Process a refund."""
    return {"status": "refunded", "order_id": order_id, "amount": amount}

# Create agent identity
support_bot = Agent(
    name="SupportBot",
    scopes=["read:orders", "write:refunds"],
    session_ttl=3600  # Session expires in 1 hour
)

# Set up LangChain
llm = ChatOpenAI(model="gpt-4")
tools = [get_order, process_refund]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful customer support agent."),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools)

# Run with permission control
with support_bot.start_session():
    try:
        result = executor.invoke({
            "input": "Check order ORD-123 and refund $25 if needed"
        })
        print(result)
    except PermissionDeniedError as e:
        print(f"Blocked: {e}")

Next Steps


AgentSudo is the permission layer for AI agents. Stop letting your agents run wild.

pip install agentsudo

Ready to secure your AI agents?

Get started with AgentSudo in under 5 minutes.