Skip to main content

LangChain Integration

Auditail works as a wrapper around LangChain tools. Add guardrails to any existing tool in one line.


Python (LangChain)​

import os
from langchain.agents import tool
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate

from auditail import Auditail, rules

# Initialize Auditail once
aegis = Auditail(api_key=os.environ["AUDITAIL_API_KEY"])
aegis.add_rules([
rules.no_external_email(),
rules.no_data_deletion(),
])

# Wrap LangChain tools with aegis.guard()
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""Send an email to a recipient."""
result = aegis.guard(
action="send_email",
payload={"to": to, "subject": subject, "body": body},
agent_id="langchain-email-agent",
)
if not result.approved:
return f"[Auditail] Action blocked: {result.reason}"

# Your actual email sending logic here
# email_client.send(to=to, subject=subject, body=body)
return f"Email sent to {to}"


@tool
def execute_sql(query: str) -> str:
"""Execute a SQL query against the database."""
result = aegis.guard(
action="execute_query",
payload={"query": query},
agent_id="langchain-db-agent",
)
if not result.approved:
return f"[Auditail] Action blocked: {result.reason}"

# Your actual DB execution logic here
# return db.execute(query)
return f"Query executed: {query[:50]}..."


# Build the agent as normal
llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [send_email, execute_sql]

prompt = ChatPromptTemplate.from_messages([
("system", "You are a helpful assistant. Use the tools provided."),
("human", "{input}"),
("placeholder", "{agent_scratchpad}"),
])

agent = create_openai_tools_agent(llm, tools, prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

# Run — Auditail will intercept any dangerous actions
agent_executor.invoke({"input": "Send the Q3 report to [email protected]"})

TypeScript (LangChain.js)​

import { ChatOpenAI } from '@langchain/openai';
import { DynamicStructuredTool } from '@langchain/core/tools';
import { AgentExecutor, createOpenAIToolsAgent } from 'langchain/agents';
import { z } from 'zod';
import { Auditail } from 'auditail-sdk';

const aegis = new Auditail({ apiKey: process.env.AUDITAIL_API_KEY! });
aegis.addRule(Auditail.rules.noExternalEmail());
aegis.addRule(Auditail.rules.noDataDeletion());

const sendEmailTool = new DynamicStructuredTool({
name: 'send_email',
description: 'Send an email to a recipient.',
schema: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string(),
}),
func: async ({ to, subject, body }) => {
const result = await aegis.guard({
action: 'send_email',
payload: { to, subject, body },
agentId: 'langchain-agent',
});

if (!result.approved) {
return `[Auditail] Action blocked: ${result.reason}`;
}

// await emailClient.send({ to, subject, body });
return `Email sent to ${to}`;
},
});

CrewAI Integration​

from crewai import Agent, Task, Crew
from auditail import Auditail, rules

aegis = Auditail(api_key=os.environ["AUDITAIL_API_KEY"])
aegis.add_rule(rules.no_external_email())

def guarded_send_email(to: str, body: str) -> str:
result = aegis.guard(
action="send_email",
payload={"to": to, "body": body},
agent_id="crewai-outreach-agent",
)
if not result.approved:
return f"Blocked: {result.reason}"
return f"Sent to {to}"

outreach_agent = Agent(
role="Outreach Specialist",
goal="Send personalized emails to leads",
backstory="You are an expert at writing personalized outreach.",
tools=[guarded_send_email],
)

Why Wrap at the Tool Level?​

Wrapping at the tool level (rather than the LLM call level) means:

  1. The LLM decides what to do — Auditail doesn't interfere with reasoning.
  2. Only the action is blocked — the agent can recover gracefully.
  3. The audit trail is precise — you know exactly what action was attempted, with what parameters.