---
title: "LangGraph Quickstart"
description: "Run a LangGraph application as a named agent, so every model request it makes is signed with its own key and recorded under it."
source: "Guardian Platform"
---
A LangGraph application runs through Viper as a **named agent**. It has its own key, it describes itself when it starts, and it names itself on every model request. The Guardian Platform then records each request under that agent, together with a description of the graph that sent it. The [Guardian Agent SDK](https://guardian-agent-sdk.docs.eqtylab.io/) does the describing and the naming.

This page takes you from an empty machine to a signed request you can see in Guardian.

## Before you start

- **Python 3.10 to 3.14**, with the Guardian Agent SDK's `langchain` extra and `langchain-openai`. The SDK comes from EQTY Lab's Cloudsmith repository, with the entitlement token you install Viper with ([Package managers](https://guardian-agent-sdk.docs.eqtylab.io/install/package-managers/)).
- **Viper 0.3.0 or later**, installed and pointed at your Agent Gateway. See [Install Viper](./install-viper.mdx).
- **An API key with the Register Agents scope**, or the **Register Agents** permission to get a registration token from Guardian, to enroll the agent. See [Register Agents](./register-agents.mdx#1-generate-an-enrollment-key).
- **Your own OpenAI API key.** The gateway does not hold provider keys; your key passes through it to OpenAI unchanged.

```bash
python3 -m venv .venv && . .venv/bin/activate
export VIPER_CLOUDSMITH_TOKEN=<your entitlement token>
PIP_INDEX_URL="https://dl.cloudsmith.io/$VIPER_CLOUDSMITH_TOKEN/eqtylab/prod/python/simple/" \
  pip install "eqty-guardian-agent[langchain]"
pip install "langchain-openai>=1.7,<1.8"
```

**Coming from the Register Agent dialog in Guardian?** The dialog shows the commands for this page's steps 1 and 2, with your agent's name, token and gateway address filled in. You still need **Before you start** (the install) and the `agent.py` in step 3, and you use your own agent's name wherever this page says `support-bot`.

## 1. Enroll the agent

Give the agent a name and its own key, then register it with the Guardian Platform. The name is 1 to 64 characters of lowercase letters, digits, `.`, `_` or `-`, starting with a letter or digit.

```bash
viper agent add support-bot
GUARDIAN_API_KEY=ak_… viper agent register support-bot
```

Viper prints the agent's key, then the agent ID the Guardian Platform minted:

```console
added agent `support-bot`
did:key:z6Mk…
registered agent `support-bot` (did:key:z6Mk…) as urn:uuid:3f2a…
```

If you have no API key, register with a token from Guardian instead: `VIPER_PROVISIONING_TOKEN='ggr1.…' viper agent register support-bot`. Choosing **LangGraph** in the **Register Agent** dialog shows this page's commands with the token and your gateway address filled in. See [Named agents](./register-agents.mdx#named-agents).

Check the enrollment with `viper doctor`. It confirms the gateway answers and accepts `support-bot`'s key, and prints a fix for anything that fails. Its warning that the proxy has no key is expected until you start the proxy.

## 2. Start the proxy

Start a proxy for OpenAI traffic. It loads every enrolled agent when it starts, so start it after enrolling the agent, and restart it after enrolling another. To restart it, stop it with `kill %1` (or `fg`, then Ctrl-C), then run `viper proxy` again. The proxy also needs a key of its own, which signs any request that names no agent; create it once if you have not already.

```bash
viper did add --assign proxy
viper proxy --app proxy --port 8080 \
  --gateway-endpoint https://gateway.example.com/v1/openai_api &
```

Replace `https://gateway.example.com` with your gateway address. The proxy prints its address, `http://localhost:8080`. If something else already listens on port 8080, the proxy can't start, and with `&` its error is easy to miss: pick another `--port` and set `VIPER_PROXY_URL` to the address the proxy prints. To confirm it loaded the agent, ask its local agent API:

```bash
curl -s http://127.0.0.1:8080/__viper__/v1/agents/support-bot   # your agent's name
```

```json
{ "name": "support-bot", "did": "did:key:z6Mk…", "definitions": [], "latestDefinitionCid": null }
```

A `404` with `agent_not_enrolled` means the name doesn't match the one you enrolled, or the proxy started before the agent was enrolled; restart it.

## 3. Run the agent

`describe_graph` builds your graph with its model routed through the proxy as the agent. It registers the agent's definition: the compiled graph's nodes and edges, a fingerprint of each node's code, the model and its settings, the instructions you pass, and the package versions. Changing the graph, a prompt or a package version gives a new definition.

Save this as `agent.py`:

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

```python
import os

from langchain_core.messages import SystemMessage
from langchain_openai import ChatOpenAI
from langgraph.graph import END, START, MessagesState, StateGraph

import guardian_agent as ga
from guardian_agent.langgraph import describe_graph

AGENT = os.environ.get("VIPER_AGENT", "support-bot")
INSTRUCTIONS = "You answer support questions briefly."


# describe_graph calls this with the model routed through the proxy, as AGENT
def build(llm: ChatOpenAI) -> StateGraph[MessagesState]:
    def answer(state: MessagesState) -> MessagesState:
        return {
            "messages": [llm.invoke([SystemMessage(INSTRUCTIONS), *state["messages"]])]
        }

    graph = StateGraph(MessagesState)
    graph.add_node("answer", answer)
    graph.add_edge(START, "answer")
    graph.add_edge("answer", END)
    return graph


app = describe_graph(
    build,
    name=AGENT,
    models={"llm": ChatOpenAI(model="gpt-4o-mini")},
    instructions=INSTRUCTIONS,
    guardian=ga.Guardian(os.environ.get("VIPER_PROXY_URL", "http://127.0.0.1:8080")),
)

result = app.invoke(
    {"messages": [{"role": "user", "content": "Say hello in five words."}]}
)
print(result["messages"][-1].content)
```

{/* end generated */}

Run it as the agent you enrolled:

```bash
VIPER_AGENT=support-bot OPENAI_API_KEY=sk-… python agent.py
```

The agent reaches the proxy at `http://127.0.0.1:8080`. Set `VIPER_PROXY_URL` if yours is elsewhere.

## 4. Confirm it in Guardian

1. Open the [Agent Catalog](./agent-inventory.mdx) and paste the agent ID from step 1 (`urn:uuid:…`) into the search box. The agent is listed with the **LangGraph** type once its first request lands, and as **Custom** before that. If you used the Register Agent dialog, its last step updates when the request lands, and links to the agent.
2. Open the agent. The request appears in its **Request Logs**, signed with the agent's key.
3. To have project policy apply to the agent and see its traffic in a project, [associate it with the project](./associated-agents.mdx).

## What is and is not recorded

Recorded:

- Every model request the graph makes, signed with the agent's own key, after the SDK checks that its model settings and tools match the definition. The prompts your nodes send aren't checked.
- The agent definition each request was sent with: the graph's nodes and edges, a fingerprint of each node's code, the models and their settings, the instructions, the tools, and the package versions.
- The provenance Viper records for any request: host, git and environment details of the process.

Not recorded:

- **Per-step LangGraph state.** Node names, run IDs, thread IDs and checkpoint IDs are not attached to individual requests, and conversations aren't part of the definition.
- **Tool traffic.** Calls your tools make to other services, such as databases and web APIs, do not go through the proxy and are neither signed nor audited.
- **A model the graph builds as it runs.** Only the models `describe_graph` routes are signed as the agent. One built as the graph runs is signed as the proxy if it's pointed at the proxy, and isn't recorded at all if it calls the provider directly. See [Calls the SDK can't see](https://guardian-agent-sdk.docs.eqtylab.io/guides/run-an-agent/#calls-the-sdk-cant-see).

## Troubleshooting

| Symptom                                                                      | What to do                                                                                                                                                                               |
| ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `viper proxy` stops with `no DID configured for proxy`                       | Create the proxy's key: `viper did add --assign proxy`.                                                                                                                                  |
| `Connection error.` from the OpenAI client                                   | Nothing answers at `http://127.0.0.1:8080`. Start the proxy, or set `VIPER_PROXY_URL` to the address it printed.                                                                         |
| `404` with `agent_not_enrolled`: ``agent `…` is not enrolled on this proxy`` | Set `VIPER_AGENT` to the exact name you enrolled: the **Agent Name** in the Register Agent dialog. If you haven't enrolled it yet, do step 1, then restart the proxy.                    |
| `404` with `proxy_incompatible`: `nothing at … serves the Viper agent API`   | Something other than `viper proxy` answers at that address, often another service on port 8080. Start the proxy on another `--port`, and set `VIPER_PROXY_URL` to the address it prints. |
| `502` with `proxy_incompatible`                                              | The proxy's Viper is older than the SDK needs. Upgrade to Viper 0.3.0 or later ([Install Viper](./install-viper.mdx)).                                                                   |
| `OpenAIError: Missing credentials`                                           | Set `OPENAI_API_KEY`.                                                                                                                                                                    |
| Model requests fail with `401` from OpenAI                                   | `OPENAI_API_KEY` is wrong. The gateway does not add a key.                                                                                                                               |
| Model requests fail with `502` and another code                              | Viper could not sign the request or reach the gateway; the error's `code` and `hint` say which. See [Run Agents](./run-agents.mdx#troubleshooting).                                      |

Each error reaches you through the OpenAI client, inside LangGraph's traceback, with the proxy's `code`, `message` and `hint` in its body. In your own code, `ga.proxy_error(error)` turns it into the SDK's exception. The SDK's [Errors](https://guardian-agent-sdk.docs.eqtylab.io/reference/errors/) reference lists every error, with what to do.

## Go further

The [Guardian Agent SDK](https://guardian-agent-sdk.docs.eqtylab.io/) docs cover everything this page leaves out:

- [LangGraph graphs](https://guardian-agent-sdk.docs.eqtylab.io/frameworks/langgraph-graphs/): tools, routers, subgraphs, and what `describe_graph` records and refuses.
- [LangChain agents](https://guardian-agent-sdk.docs.eqtylab.io/frameworks/langchain-agents/): agents built with `create_agent`, whose every call is checked against the definition.
- [Multiple agents](https://guardian-agent-sdk.docs.eqtylab.io/frameworks/multiple-agents/): several agents in one process, each signing as itself.
- [Test your agents](https://guardian-agent-sdk.docs.eqtylab.io/guides/testing/): run them in tests without Viper, keys or a network.

## Related

- [Deep Agents Quickstart](./deepagents-quickstart.mdx) — the same setup for an agent with sub-agents
- [OpenShell Quickstart](./openshell-quickstart.mdx) — run an agent in a sandbox where Viper is its only way out
- [Run Agents](./run-agents.mdx) — sidecar mode in general, and troubleshooting for failed requests
- [Associated Agents](./associated-agents.mdx) — link the agent to a project so project policy applies
- [Agent Configuration](./agent-configuration.mdx) — what Viper captures about the agent at the start of each session
- [Agent Coverage](./agent-coverage.mdx) — how this integration's coverage compares with the others