---
title: "LangGraph"
description: "guardian_agent.langgraph: describe_graph, which describes a hand-built LangGraph graph as an agent that signs its own requests."
source: "Guardian Agent SDK"
---
{/* generated api guardian_agent.langgraph */}

Hand-built LangGraph graphs, as agents that sign their own requests.

Give `describe_graph` the function that builds your graph and the chat
models it uses. It calls that function once, with each model routed through
the proxy as the agent, describes the graph it built, and returns it:

```python
from guardian_agent.langgraph import describe_graph

def build(llm):
    graph = StateGraph(MessagesState)
    ...  # your nodes call llm
    return graph.compile()

app = describe_graph(build, name="inventory", models={"llm": ChatOpenAI(...)})
```

Needs `langgraph`, which the `langchain` extra brings.

**Functions:**

- [**describe_graph**](#langgraphdescribe_graph) – Your LangGraph graph, built with routed models, described, as the agent `name`.

## `langgraph.describe_graph`

```python
describe_graph(build: Callable[..., Any], *, name: str, models: Mapping[str, BaseChatModel | str], tools: Sequence[BaseTool | Callable[..., Any] | dict[str, Any]] = (), instructions: Instruction | Sequence[Instruction] | None = None, description: str | None = None, version: str | None = None, sources: Iterable[Source] = (), exclude: Sequence[str] = (), guardian: Guardian | None = None) -> CompiledStateGraph[Any, Any, Any, Any]
```

Your LangGraph graph, built with routed models, described, as the agent `name`.

`build` is called once, with each of `models` routed through the proxy
as the agent and passed by name, so every call its nodes make is signed
as the agent. The definition records the models, the graph's nodes and
edges (a subgraph's too, named `outer:inner`), each node's and router's
code with the values it captured, and the tools of each `ToolNode`.
Each request is checked as it's built: one sending settings, or offering
a tool, the definition doesn't describe is refused before it's sent.

Example:

```python
def build(llm: ChatOpenAI):
    model = llm.bind_tools([inventory])

    def answer(state: MessagesState):
        return {"messages": [model.invoke(state["messages"])]}

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

app = describe_graph(
    build, name="inventory", models={"llm": ChatOpenAI(model="gpt-4.1")}
)
```

**Parameters:**

- **build** (`Callable[..., Any]`) – Builds your graph from the routed models, given by name, and
  returns it compiled, or as a `StateGraph` to compile as it is.
- **name** (`str`) – The agent's name, as enrolled with `viper agent add`.
- **models** (`Mapping[str, BaseChatModel | str]`) – Each of `build`'s parameters that takes a chat model, and
  the model: a chat model, or a `"provider:model"` name built
  with `init_chat_model` (as `create_agent` takes one); in the
  definition in this order.
- **tools** (`Sequence[BaseTool | Callable[..., Any] | dict[str, Any]]`) – Tools the graph's models are offered besides those in its
  `ToolNode`s.
- **instructions** (`Instruction | Sequence[Instruction] | None`) – Text for a system prompt, or a list of prompts, as
  `guardian_agent.Definition` takes them.
- **description** (`str | None`) – What the agent is for.
- **version** (`str | None`) – Your own label for this version of the agent.
- **sources** (`Iterable[Source]`) – Source files to fingerprint, such as a module whose
  constants your nodes read.
- **exclude** (`Sequence[str]`) – JSON Pointers to fields to leave out of the definition, such
  as `/extensions/langgraph/nodes/answer` for a node holding a
  value that can't be recorded.
- **guardian** (`Guardian | None`) – The proxy to use; `Guardian()` (`VIPER_PROXY_URL`) by
  default.

**Returns:**

- `CompiledStateGraph[Any, Any, Any, Any]` – The compiled graph, whose models are routed as the agent.
  `agent_of(graph)` is the SDK's agent.

**Raises:**

- `TypeError` – `models` is empty, names a parameter `build` doesn't
  take, or holds something `bind()` can't route; `build`
  returned something other than a LangGraph graph; or a node or
  router holds a chat model that isn't one of the routed models.
- `ValueError` – A router's destinations can't be seen (give it a
  `Literal` return type or a `path_map`); a node holds a value
  that can't be recorded; or a model sends a setting no definition
  can record.
- `DefinitionInvalid` – The definition breaks a rule.
- `DefinitionContainsSecret` – It holds what looks like a credential.

{/* end generated */}