LangChain + ExecLayer Governance Guide
Add execution authority and cryptographic audit trails to LangChain agent chains. Intercept tool calls, evaluate policies, and generate Authority Receipts for every agent action.
The LangChain Governance Problem
LangChain provides a powerful framework for building agentic applications. The AgentExecutor orchestrates an agent loop: observe state, select tools, execute tools, observe results, repeat. The framework makes it straightforward to give agents access to external tools like APIs, databases, and executable functions.
But LangChain has no built-in execution gating mechanism. Once you wire a tool into an agent, the agent can call it whenever its reasoning suggests doing so. There is no intermediate authorization layer. There is no record of why the action was taken. There is no mechanism to enforce organizational policies about which agents can perform which actions.
This is the core problem ExecLayer solves for LangChain-based agents. We sit between the agent's decision to act and the actual execution, providing:
- Policy evaluation: Does this agent have authority to perform this action?
- Intent canonicalization: What exactly is the agent trying to do, in standardized form?
- Threshold signing: Can multiple decision makers sign off on high-risk actions?
- Cryptographic receipts: Tamper-proof audit trails for compliance and forensics.
Conceptual Integration Architecture
The integration sits in the LangChain tool execution pipeline:
The agent doesn't see the gate. It makes tool calls as usual. The ExecLayer adapter intercepts those calls in LangChain's tool callback system, evaluates them against policy, and either allows them to proceed or returns an error.
How It Works: Four Key Integration Points
1. Intercepting Tool Calls
LangChain's AgentExecutor uses callbacks and tool runners. The ExecLayer adapter hooks into the tool execution callback:
class ExecLayerToolCallback(BaseCallbackHandler):
def on_tool_start(self, serialized, input_str, **kwargs):
tool_name = serialized.get("name")
tool_input = json.loads(input_str)
# Create request for ExecLayer
request = ExecutionRequest(
adapter="langchain",
tool_name=tool_name,
input=tool_input,
agent_id=self.agent_id,
context=self.extraction_context()
)
# Gate the execution
result = self.execLayer.evaluate(request)
if not result.approved:
raise ToolAuthorizationError(
f"Not authorized: {result.reason}"
)
The callback fires before LangChain actually executes the tool. ExecLayer evaluates the request synchronously and either allows the tool runner to proceed or raises an exception that the agent sees as a tool execution failure.
2. Canonicalizing Intent
LangChain tools have varying schemas. A database query tool accepts raw SQL strings. An API tool accepts structured JSON. A file tool accepts paths. To enforce consistent policies across tools, ExecLayer canonicalizes all tool calls into a Blueprint, our declarative representation of executable intent:
// Blueprint for a database write
{
"canonical_action": "data_mutation",
"operation": "update",
"resource_type": "database_table",
"resource_identifier": "customers",
"agent_capability": "write_customers",
"conditions": {
"row_where": "id=12345"
}
}
// Blueprint for an API call
{
"canonical_action": "external_api_call",
"operation": "post",
"resource_type": "external_service",
"resource_identifier": "stripe_api",
"agent_capability": "charge_payment",
"conditions": {
"amount_cents": 999
}
}
Each tool type (database, API, file system) has a canonicalization rule that converts from LangChain's tool schema to the Blueprint. Policies are then written in terms of Blueprint actions, making them tool-independent.
3. Policy Evaluation
Once the intent is canonicalized, ExecLayer evaluates it against the policy bundle for this agent. Policies are rules like:
- "research_agent can execute data_mutation operations on the demo_database table only if the row count affected is less than 100"
- "sales_agent can execute external_api_call to stripe_api only if the amount is less than 50000 cents"
- "any agent can execute data_read operations"
The policy engine is deterministic and designed for low-latency inline evaluation. It checks the canonicalized intent against the agent's capability set and any threshold requirements:
evaluation_result = {
"approved": true,
"tier": "standard",
"reason": "matches policy rule: sales_agent can charge_payment",
"policy_rule_id": "stripe_charge_rule_v2",
"nonce": "0x7a2c...",
"signatures_required": 0
}
4. Receipt Generation
Whether approved or denied, ExecLayer generates a signed Authority Receipt containing the complete decision record. If approved, the receipt is stored in the append-only audit ledger before tool execution proceeds:
{
"receipt_id": "auth_recv_1a2b3c...",
"timestamp": "2026-04-03T14:23:45Z",
"canonical_intent": { /* Blueprint */ },
"policy_evaluation": {
"approved": true,
"rule_id": "stripe_charge_rule_v2",
"tier": "standard"
},
"agent_binding": {
"agent_id": "sales_agent_01",
"adapter": "langchain"
},
"signatures": [],
"nonce": "0x7a2c9f...",
"merkle_index": 4521,
"hash": "0x92f14e..."
}
Which LangChain Components Does ExecLayer Hook Into?
The integration is not invasive. ExecLayer uses three standard LangChain extension points:
BaseCallbackHandler
LangChain callbacks fire at each step of the agent loop. ExecLayer registers callbacks for on_tool_start and on_tool_end, capturing tool invocations before execution and recording results after execution.
AgentExecutor Tool Runner
The AgentExecutor accepts a custom tool runner function. ExecLayer can wrap the default tool runner to inject policy evaluation between the agent's decision and the tool's execution:
def execLayer_tool_runner(tool, tool_input):
# ExecLayer policy check
request = ExecutionRequest(
tool_name=tool.name,
input=tool_input
)
auth_result = execLayer.evaluate(request)
if not auth_result.approved:
return f"Authorization denied: {auth_result.reason}"
# Run the actual tool
try:
result = tool.run(tool_input)
except Exception as e:
# Record the error
execLayer.record_failure(request, e)
raise
# Record success
execLayer.record_success(request, result)
return result
executor = AgentExecutor(
agent=agent,
tools=tools,
tool_runner=execLayer_tool_runner
)
Output Parser Hooks
LangChain agents use output parsers to extract tool calls from the LLM's response. ExecLayer can register with the parser to inspect the parsed tool calls before they are passed to the executor, providing an additional gate at parse time.
Practical Workflow Example
Consider a sales agent with tools for querying the CRM, checking inventory, and charging payment. Here is how the flow works:
- Agent observes that the customer needs 5 widgets and decides to charge $150.
- Agent calls the charge_payment tool with amount=15000 (in cents).
- on_tool_start callback fires. ExecLayer intercepts.
- ExecLayer canonicalizes: "external_api_call to stripe_api with amount=15000"
- ExecLayer evaluates against policy: "sales_agent can charge_payment if amount less than 50000"
- Check passes. Authority Receipt is generated and written to ledger.
- Tool runner executes the actual API call.
- Result is recorded in the receipt's outcome field.
- on_tool_end fires and ExecLayer logs the completion.
If the agent had tried to charge $600 (60000 cents), step 5 would fail the policy check. ExecLayer would return a denial receipt and the tool runner would return an error message to the agent, which would adapt and try a different approach or ask the user for permission.
Threshold Signatures for High-Risk Actions
Some actions are too high-risk to gate with policy alone. They require human or multi-agent approval. ExecLayer supports threshold signatures:
policy {
"rule_id": "large_refund",
"match": "data_mutation on refunds_table where amount > 100000",
"action": {
"approved": "requires_threshold",
"threshold": 2,
"signers": ["finance_lead", "cfo"]
}
}
When an agent action matches a threshold rule, ExecLayer generates a signing request and waits for approval. The approval can come from human approvers, other agents with signing capability, or a combination. The receipt records all signatures:
{
"receipt_id": "auth_recv_5a6b7c...",
"policy_evaluation": {
"approved": "pending_signatures",
"threshold": 2,
"signatures_collected": 1
},
"signatures": [
{
"signer": "finance_lead_01",
"timestamp": "2026-04-03T14:24:10Z",
"signature": "0xabc123..."
}
]
}
This Is Conceptual, Not Shipped
We want to be clear: the LangChain integration described here is our design for how it will work. It is not yet shipped. We are building it and plan to release it in Q2 2026. The architecture is final, the callback strategy is validated, but the implementation is in progress.
We are sharing this detailed conceptual design because it illustrates the broader ExecLayer thesis: any agentic framework can have execution authority layered on top without modifying the framework itself. By hooking into standard extension points (callbacks, tool runners, output parsers), we can add governance without rewriting agents.
Broader Implications
The LangChain integration demonstrates several principles that apply across all agentic systems:
- Governance must intercept at the execution boundary, not at reasoning time.
- Policies must work with canonicalized intent, not raw framework-specific schemas.
- Authorization must be synchronous and deterministic for inline gating.
- All decisions must generate cryptographic receipts for accountability.
- The integration should use standard extension points rather than forking the framework.
For more information, see our research on agentic systems governance and our technical documentation.
Frequently Asked Questions
How does ExecLayer govern a LangChain agent?
ExecLayer sits at the execution boundary between a LangChain agent's decision to act and the tool actually running. The agent's intended tool call is normalized into a Blueprint, validated deterministically against the agent's policy, and executed only if authorized. It hooks into standard LangChain extension points such as the tool callback and tool runner, so governance is added without modifying the framework itself.
Does ExecLayer require changes to my LangChain agents?
No. The integration uses LangChain's existing extension points — BaseCallbackHandler callbacks like on_tool_start and on_tool_end, the AgentExecutor tool runner, and output parser hooks. The agent makes tool calls as usual and never sees the gate directly. ExecLayer intercepts the call, evaluates policy, and either lets the tool runner proceed or returns an authorization error the agent handles like any tool failure.
Why intercept tool calls instead of filtering the LLM prompt?
Prompt-level filtering operates on probabilistic model output and can be bypassed by prompt injection or simply wrong reasoning. ExecLayer enforces at the execution boundary, where the intended action is reduced to a deterministic Blueprint and checked against policy before anything runs. Governance must intercept at execution, not at reasoning time, so an unauthorized action cannot execute regardless of how the agent was prompted.
How are LangChain tool calls evaluated against policy?
Each tool call is canonicalized into a Blueprint that captures the operation, target, parameters, and the agent's required capability, independent of the tool's native schema. Policies are written against these canonical actions, so the same rule applies whether the agent uses a database tool, an API tool, or a file tool. Evaluation is deterministic and runs inline, and the decision is recorded in a signed Authority Receipt before execution proceeds.
Is the LangChain integration available today?
Not yet. The LangChain integration described here is ExecLayer's design for how it will work, with a planned release in Q2 2026. The architecture and callback strategy are settled, but the adapter implementation is still in progress. It is shared to illustrate the broader thesis that any agent framework can have execution authority layered on through standard extension points.
Ready to add execution authority to your LangChain agents?
Request Early Access