---
title: "Quick start"
description: "Enroll an agent with Viper, run the proxy, and send your first signed request from Python."
source: "Guardian Agent SDK"
---
This page takes one agent from nothing to its first signed request, and then to its configuration in Guardian.

You need:

- Python 3.10 or later, with the SDK and your model provider's client installed: `eqty-guardian-agent`, and `openai` 1.55.3 or later for this page ([Package managers](./install/package-managers.mdx)).
- Viper 0.3.0 or later, pointed at your Guardian Platform deployment's Agent Gateway ([Install Viper](https://guardian.docs.eqtylab.io/user-guide/install-viper/)).
- A Guardian Platform API key (`ak_...`) or a registration token, to register the agent ([Register Agents](https://guardian.docs.eqtylab.io/user-guide/register-agents/)).
- A provider key for your model, such as `OPENAI_API_KEY`. It reaches the provider unchanged.

## 1. Enroll the agent

Viper holds a key for each agent and signs its requests with it. Give the proxy a key of its own once, then add and register the agent:

```sh
# The proxy's own key, once per host
viper did add --assign proxy

# A key for the agent, then its registration with the Guardian Platform
viper agent add researcher
GUARDIAN_API_KEY=ak_... viper agent register researcher
```

The name is how your code will refer to the agent: 1 to 64 characters of `a-z`, `0-9`, `.`, `_` or `-`, starting with a letter or digit. `viper agent register` registers the agent's key with the Guardian Platform and saves the agent ID it gets. If you were given a registration token instead, run `VIPER_PROVISIONING_TOKEN=<registration token> viper agent register researcher`. Pass either one in the environment, as here, rather than as a flag, so it stays out of process listings. [Agents and identities](./concepts/agents-and-identities.mdx) explains what each step creates.

## 2. Start the proxy

```sh
viper proxy --port 8080 --require-agent-header \
  --gateway-endpoint "$GUARDIAN_URL/v1/openai_api"
```

`GUARDIAN_URL` is your gateway's address. A proxy fronts one provider's gateway endpoint: `/v1/openai_api` for OpenAI clients, as here, or `/v1/anthropic_api` for Anthropic's. An agent that uses both providers runs through two proxies, one for each endpoint, with a `Guardian` for each and the same agent name and definition. `--port` keeps the address fixed; without it, the proxy picks a free port and prints it. `--require-agent-header` makes the proxy refuse any request that doesn't name an enrolled agent, so nothing goes out signed as the proxy instead.

The proxy loads its agents when it starts, so start it after enrolling them, and restart it after enrolling another.

## 3. Check the setup

Point the SDK at the proxy and check that everything fits before your agent's first real request:

```sh
export VIPER_PROXY_URL=http://127.0.0.1:8080
```

{/* generated file sdks/python/snippets/check_setup.py python */}

```python
import guardian_agent as ga

guardian = ga.Guardian()  # the proxy's address, from VIPER_PROXY_URL

capabilities = guardian.capabilities()
print(f"Viper {capabilities.viper}, enrolled: {', '.join(capabilities.agents)}")

researcher = guardian.agent(
    "researcher",
    model=ga.Model("openai", "gpt-4.1", temperature=0),
    instructions="You research questions and cite your sources.",
)
# Fails here, with a typed error, if the proxy or the agent's enrollment is wrong
definition_cid = researcher.register()
print(f"{researcher.name} is registered as {definition_cid}")
```

{/* end generated */}

`capabilities()` checks the proxy speaks this SDK's API and lists its enrolled agents. `guardian.agent()` has already checked the definition, so a mistake in it fails there, at the line that made it. `register()` sends the definition and returns its content ID. If anything is wrong (the proxy isn't running, the agent isn't enrolled, the proxy refuses the definition) it raises a typed error saying what to do: for an agent that isn't enrolled, `ga.AgentNotEnrolled`, whose hint names the `viper agent add` command to run and says to restart the proxy. Calling it at start-up is optional: an agent registers on its first request anyway.

## 4. Send a signed request

Describe the agent and route the model client you already use through the proxy:

{/* generated file sdks/python/snippets/quick_look.py python */}

```python
from openai import OpenAI

import guardian_agent as ga

INSTRUCTIONS = "You research questions and cite your sources."

guardian = ga.Guardian()  # the proxy's address, from VIPER_PROXY_URL
researcher = guardian.agent(
    "researcher",  # as enrolled with `viper agent add researcher`
    model=ga.Model("openai", "gpt-4.1", temperature=0),
    instructions=INSTRUCTIONS,
)

client = researcher.bind(OpenAI())  # every request now signed as the researcher
reply = client.chat.completions.create(
    model="gpt-4.1",
    temperature=0,
    messages=[
        {"role": "system", "content": INSTRUCTIONS},
        {"role": "user", "content": "What changed in the last release?"},
    ],
)
print(reply.choices[0].message.content)
```

{/* end generated */}

Save it as `quick_look.py` and run it with `python quick_look.py`, or `uv run python quick_look.py` in a uv project.

`bind()` returns a copy of your client that sends its requests to the proxy, naming the agent on each one. The proxy signs every request with the researcher's key and the definition it registered, and forwards it to the gateway. Your client is left as it was.

The definition records the agent's instructions, but sending them is still your code's job: the request above sends them as its system message. With a model client you call yourself, as here, keep what your requests send consistent with the definition, since the proxy signs every request as matching it. LangChain's `create_agent` and Deep Agents, through the SDK's [framework integrations](./frameworks/index.mdx), build each request from what they describe, and check each call's model, settings, tools and response format against the definition.

## 5. See it in Guardian

Open **Agents** in Guardian, select **researcher**, then **Configuration**. The panel shows the definition the agent registered: its model, prompt and, as you add them, its tools, skills, memory and sub-agents. Change the definition (a prompt, a setting, a tool's code) and run it again, and the version selector lists the new definition beside the old one. See [View an agent's configuration](https://guardian.docs.eqtylab.io/user-guide/agent-configuration/#custom-agents) in the Guardian Platform docs.

## Next

- [Agents and identities](./concepts/agents-and-identities.mdx): what enrolling creates, and how one proxy signs for several agents.
- [Definitions](./concepts/definitions.mdx): what an agent's definition records, and how it becomes signed state.
- [What is captured](./concepts/what-is-captured.mdx): what reaches the Guardian Platform, and what stays with you.
- [Describe an agent](./guides/describe-an-agent.mdx) and [Run an agent](./guides/run-an-agent.mdx): tools, skills and instructions, every client the SDK routes, and refusals.
- [Test your agents](./guides/testing.mdx) without Viper.
- [Frameworks](./frameworks/index.mdx): LangChain's `create_agent`, Deep Agents, LangGraph graphs and MCP tools, described and routed for you.
- The [API reference](./api/index.mdx), for every class and function.