Custom Agents

A custom agent is an agent you build and run yourself — your own code, in your own process — as opposed to a third-party managed or hosted agent product. Netzilo embeds directly inside that process as an SDK: every LLM prompt, model response, and tool call your agent makes can be evaluated — allowed, blocked, or redacted — against policy pulled live from your Netzilo Server. No daemon, no sidecar, nothing to stand up.

The policy is the set of Endpoint filters, under Edge > Filters, that match the SDK's peer. Enroll the agent with a setup key whose auto-group is your agents' group, and bind guardrails to that group in an endpoint filter. Agents enrolled with a setup key are listed under Agents in the AI Agent Activity report.

Watch · 1:28

Govern the agents you build, from the inside

Two lines of the Netzilo SDK put an agent under the guardrails, with its processes and files in its session.

All how-to videos →YouTube ↗

Install

pip install netzilo

Core API

One call starts the embedded client. It's non-blocking — your agent keeps running while policy syncs in the background.

import netzilo

netzilo.start(
    server="https://srv.netzilo.com",  # management server
    pat="nzl_...",                     # or setup_key="..."
    agent_name="my-agent",
)

server is https://srv.netzilo.com on Netzilo Cloud (the default) and https://<your domain> on a self-hosted Netzilo Server. The SDK does not support interactive SSO sign-in: use a setup key or a personal access token.

Everything on the Framework Adapters, Raw LLM Clients, and MCP & Middleware Integrations pages builds on this one call.

Platform support

Every toolkit on the following pages — framework hooks, raw LLM clients, MCP, and the third-party Guardrails AI library — works on macOS, Linux, and Windows.

Minimal example with advanced governance

A complete, runnable agent with advanced governance turned on — no framework, no manual tool wrapping. Once start() returns, every LLM call, subprocess, and outbound request your agent code makes is inspected in-process.

import time
import netzilo

netzilo.start(
    server="https://srv.netzilo.com",
    setup_key="...",                     # or pat="nzl_..."
    agent_name="my-agent",
    enable_advanced_governance=True,     # Linux only — see note above
)

while not netzilo.is_running():           # wait until policy has synced
    time.sleep(0.5)

# Your agent's own code runs unmodified from here — call any LLM SDK,
# spawn subprocesses, make outbound requests. All of it is inspected.
...

netzilo.stop()   # graceful stop: snapshots the behavior graph, flushes events

TypeScript also has a one-line fire-and-forget form for this exact case — govern() reads the same config from NETZILO_* environment variables and starts with advanced governance on by default. On macOS and Windows set NETZILO_ADVANCED=0, because advanced governance is Linux only:

import { govern } from "netzilo";

await govern(); // NETZILO_SERVER / NETZILO_SETUP_KEY / NETZILO_AGENT_NAME env vars; non-blocking

Advanced governance adds in-process TLS interception, with a CA at ~/.netzilo-sdk/netzilo-ca.pem, and a probe that records file and process activity of the agent and its subprocesses. Statically linked binaries and programs that ignore the proxy environment variables are not covered, Node.js fetch needs undici, and inside a container the CA can only be installed as root.

Configuration

Option (Python)Environment variableMeaning
serverNETZILO_SERVERNetzilo Server address; default https://srv.netzilo.com
setup_keyNETZILO_SETUP_KEYSetup key to enroll with
patNETZILO_PATPersonal access token, instead of a setup key
agent_nameNETZILO_AGENT_NAMEName shown for the agent
enable_advanced_governanceNETZILO_ADVANCEDIn-process interception and probe; Linux only
log_level, log_fileNETZILO_LOG_LEVELLogging
config_pathWhere the SDK keeps its state; default ~/.netzilo-sdk/config.json

In TypeScript the options are camel case (agentName, setupKey, enableAdvancedGovernance). When a supervisor process starts the SDK for another process, call set_agent_pid(pid) so that the real workload is recorded as the agent.

In containers, also set NETZILO_PROXY_PORT, NETZILO_CONFIG_DIR, NETZILO_EXTRA_NO_PROXY and NETZILO_READY_TIMEOUT (seconds, default 45) as needed, and start the agent only after the SDK reports it is running.

Direct checks

Without a framework adapter, ask the SDK directly:

import netzilo

if netzilo.is_allowed("web_search", {"query": q}):
    ...
netzilo.check_prompt(prompt_text)
netzilo.check_response(answer_text)

Upgrading from SDK 4.x

Calling models through the AI Gateway

An SDK agent can also call its models through the AI Gateway: point its OpenAI client at <gateway>/v1 or its Anthropic client at <gateway>/anthropic, with the token of a service user or of the agent's owner as a bearer token. The SDK governs what the agent does; the gateway governs which models it may use, its budget and its prompts.

Watch · 1:20

Govern the agents you build, through the gateway

Point an Anthropic SDK agent at the gateway with its own service identity, and its guardrails follow.

All how-to videos →YouTube ↗

One session per task

The gateway groups an agent's model calls into sessions, which you find under Activity > AI Sessions and in Agentic Activity. Coding agents such as Claude Code send their own session ID. A model API call from your own agent carries none, so the gateway groups calls by conversation: calls that share a system prompt and first message belong together, and a call that starts the conversation again starts a new session.

For exact sessions, give each task its own ID in the x-bf-session-id header. This also keeps apart two runs of the same task at the same time.

import uuid
import anthropic

client = anthropic.Anthropic(
    base_url="https://<gateway>/anthropic",
    auth_token=token,
    default_headers={"x-bf-session-id": str(uuid.uuid4())},
)

A client that serves many tasks sends the ID per request instead, for example client.messages.create(..., extra_headers={"x-bf-session-id": task_id}). The OpenAI client takes the same default_headers and extra_headers.