Proxy vs SDK: Two Architectures for AI Agent Enforcement
In-process SDK enforcement against out-of-process proxy enforcement: where the guard runs, what it costs to deploy, and which bypass each one leaves open.
- Measured: 40 of 47 payloads, our own corpus
- Not measured: latency
- Observed, not governed: hosted MCP tool calls
Every organization deploying AI agents in production eventually reaches the same decision point: How do we enforce policy on what agents do?
There are two fundamentally different architectural approaches:
- SDK/In-Process: Embed enforcement rules inside the agent's code
- Proxy/Out-of-Process: Intercept agent requests from outside the process
Each approach has distinct tradeoffs. This guide covers both, explains when to choose each, and explains why we built Intercis as a proxy.
What Is SDK-Based Enforcement?
Definition: A developer imports a library into their agent code. That library validates actions before they execute.
The rules are written in the framework's own validator format and the guard object is passed to the agent executor. We do not print a sample here: the exact call differs by library and by version, and a snippet that no longer runs is worse than none.
Examples of SDK-based solutions:
- Guardrails AI
- NeMo Guardrails (NVIDIA)
- LangChain callbacks and tools validation
- Custom decorators and middleware
Typical feature set:
- Output validation (check LLM responses for credentials, banned topics, etc.)
- Tool use validation (check which tools are called and with what parameters)
- Custom validators (regex, JSON schema, semantic checks)
- Per-agent customization
What Is Proxy-Based Enforcement?
Definition: A proxy sits between the agent and the LLM API. The tool calls the model asks for cross that wire, so the proxy reads them and judges them there. A tool a hosted MCP server runs for the model has already run by the time the response reaches the proxy, so it is observed, not governed: one observe row, never judged or blocked.
Agents point to the proxy instead of the API:
from anthropic import Anthropic
# The only change is the endpoint:
client = Anthropic(api_key="sk-ant-...", base_url="https://api.intercis.io")
Examples of proxy-based solutions:
- Intercis
- Custom API gateways (Kong, Nginx with custom rules)
- Network-level policy enforcement
Typical feature set:
- Tool call interception
- Policy applied to the call before the agent runtime can act on it
- Append-only audit logging outside the agent process
Side-by-Side Comparison
| Dimension | SDK, in process | Proxy, out of process |
|---|---|---|
| Code changes | An import in every agent | The client's base URL |
| Where enforcement runs | Inside the agent process, so a compromise of it disables the guard | Outside it. The agent can still repoint its base URL, so pair it with egress control |
| Framework support | Per framework | Any client that speaks the Anthropic or OpenAI wire format |
| What it takes to deploy | An import statement | Infrastructure: a reverse proxy or an ingress |
| Several agents at once | Each agent carries its own rules | One rule set per tenant, applied to every agent on it |
| Latency overhead | A function call | Unmeasured. We publish no figure, and a stream is buffered in full before it is judged |
| Audit trail | The agent's own logs, which the agent can edit | An external store, append-only and hash-chained |
| Reasoning steps | Visible in process | Not seen. Only the tool calls on the wire |
| Source | Open source options exist | Proprietary |
| Switching cost | The imports come out of every service | Repoint the endpoint. No application code to unwind |
When to Use SDK-Based Enforcement
SDK is the right choice if:
- Single-framework environment: All your agents use LangChain, and you're willing to standardize on one framework.
- Development/testing only: You're building agent prototypes, not deploying to production, so code changes are acceptable.
- You control all agent code: You own the codebase and can require guardrails as a coding standard.
- Latency-sensitive systems: You cannot tolerate the network latency of a proxy, and a streamed response is buffered in full before it is judged.
- Fine-grained reasoning validation: You need to validate intermediate steps, not just final actions.
When to Use Proxy-Based Enforcement
Proxy is the right choice if:
- Production with real infrastructure access: Agents have filesystem, database, or cloud API access with real consequences.
- Multi-framework environment: Teams use different agent frameworks (some LangChain, some Claude SDK directly, some custom).
- Strict policy requirements: the agent process must not be able to switch enforcement off, even if it is compromised. Routing to the proxy is still a configuration setting, so pair it with egress control.
- Multi-agent coordination: You have many agents that need consistent policy across teams.
- Audit compliance: you need an audit trail the agents cannot modify.
- You will not touch the agent code: the only change you can make is the endpoint it points at.
How Each Approach Handles One Scenario
Scenario: "Delete Old Deployment Artifacts"
An agent is tasked with deleting files from /var/log/deployments/ that are older than 90 days. However, the prompt is vague:
"Clean up old deployment artifacts to save disk space."
The agent interprets this broadly and tries to delete:
/var/log/deployments/old/:✓ Legitimate/var/lib/:✗ Should be blocked/opt/application/:✗ Should be blocked
With an SDK
The validator runs in the agent process, raises on the path it does not recognise, and the agent gets a tool error back. The record of it is in the agent's own logs.
If an attacker gains code execution in that process, they can set
guard = None, widen the directory list to ["/"], or
wrap the call in a try-except. The guard is code running inside the process it
is judging.
With Intercis
There is no policy file to write. The layer is a deny list of compiled regexes
we maintain in apps/proxy/deny_list.py, and a tenant adjusts what
applies to its own traffic through scoped exclusions that each carry a reason
and an expiry. The flow on a match:
1. The model asks for: delete_file, path "/var/lib/"
2. That tool_use block crosses the wire to api.intercis.io
3. The proxy flattens the tool input and runs the deny list over it
4. On a match the verdict is deny
5. The response is rebuilt: the tool_use block is replaced by a
text block naming the policy, stop_reason end_turn
6. The agent runtime receives no tool call, so there is
nothing for it to execute
7. One row is written to the append-only audit store
An attacker with code execution in the agent process cannot switch that off, because it is not running in their process. They can repoint the client at the provider API, and that works: being in path is a configuration setting, which is why this wants network egress control beside it.
Why Intercis Is a Proxy
Three reasons:
1. A guard inside the process is optional
If a policy is just code running inside the agent process, it's a suggestion. In production environments where agents have filesystem or database access, a suggestion is not enough.
The founding incident behind Intercis happened because no layer between intent and action could stop an underspecified agent. An in-process guard would have been better than nothing, but not good enough. A proxy outside the process boundary is the one place the guard is not code the agent controls.
2. Multi-Framework Reality
Agent code gets written against whatever framework the team picked:
- Some teams use Claude SDK directly
- Some use LangChain
- Some use AutoGen
- Some use custom agents
Requiring each team to use the same guardrails library, or requiring developers to implement guardrails at all, creates friction. The proxy works with all of them without code changes.
3. The log has to sit outside the thing it records
SOC 2 and ISO 27001 work asks you to show the log was not edited. If the log is written by the same process that would want to hide something, you cannot show that. A proxy writes to an append-only store outside the agent, which is what makes the rows usable as evidence.
The Real Tradeoff: Latency vs. Guarantee
The honest tradeoff is this:
In-process (SDK): Faster, easier to deploy, but optional: developers can bypass it.
Out-of-process (Proxy): Slightly slower (network latency) and it needs an infrastructure change, but the enforcement runs outside the agent process. Routing to it is a configuration setting, so pair it with network egress control when the agent process itself is the threat.
We do not publish a latency figure. Nothing in the repo measures governed-request latency, and a streamed response is buffered in full before it is judged, which adds the length of the stream. Where latency is critical, measure it in your own environment before you commit.
Where a millisecond matters more than a compromised process does, in-process enforcement with hard code review is the trade you want. Out-of-process removes the in-process bypass instead. Routing is still a configuration setting, and closing that needs egress control.
Four questions that decide it
- Do the agents reach production infrastructure?
- Do you control every line of agent code, in every team?
- Do you need the audit trail to be somewhere the agent cannot edit?
- Is more than one agent framework in use?
A yes to the first, third or fourth points at a proxy. A yes only to the second points at an SDK.
Both at once
The two layers stack: in-process guards catch what they catch before the request leaves, and the proxy judges what reaches the wire. If one is switched off, the other still runs. The cost is two sets of rules to keep straight.
The Bottom Line
The choice is not which technology is better. It is where you need the guard to run. Inside the process is faster and it is optional. Outside the process survives the process being compromised, and costs you an infrastructure change and a stream you cannot see until it has finished.
What Intercis does not govern
Intercis judges the tool calls that cross the Anthropic and OpenAI API wire. When a hosted MCP server runs a tool for the model, that call has already run by the time the response reaches the proxy, so it is observed, not governed: it writes one observe row and is never judged or blocked. Being in path is also a configuration setting: a process that can change its own base URL can route around the proxy unless network egress control forces the traffic through it.