> This page is for version v4 (default).
> For other versions, use one of these documentation indexes:
> - v4 (default): https://docs-beta.getzep.com/v4/llms.txt
> - v3: https://docs-beta.getzep.com/v3/llms.txt
> - v2: https://docs-beta.getzep.com/v2/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs-beta.getzep.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-beta.getzep.com/_mcp/server.

# Observations

> Observations are durable, evidence-backed patterns Zep derives from a Context Graph — recurring loops, preferences, commitments, and state transitions across many facts.

> **Info**
>
> Available to [Flex Plus and Enterprise](https://www.getzep.com/pricing) customers.

## Overview

An **observation** is a durable, evidence-backed piece of context that Zep automatically derives from a graph. Each observation captures a meaningful change, decision, commitment, constraint, preference, state transition, recurring pattern, or stable relationship involving one or more entities.

Observations sit alongside [facts](/facts) and [entity summaries](/entities) as a derived construct on the graph, but they answer a different question:

* **Facts** are granular, time-stamped claims stored on a single edge between two entities.
* **Entity summaries** are entity-centered narratives that describe the history of a single node.
* **Observations** are cross-entity context that captures *why* something matters — the persistent decisions, behaviors, and relationships that span multiple facts and entities.

This makes observations especially useful for surfacing context that would otherwise be diluted across many facts: a user's evolving preferences, a recurring failure mode, a long-running commitment, or the way two entities consistently interact over time.

## How Zep creates observations

Observations are detected automatically by analyzing structural patterns in the graph — clusters of facts and entities that together describe the same underlying behavior or relationship. Zep then synthesizes a name and summary for each observation it detects.

Observations are deduplicated and merged: when new evidence fits an existing observation, the existing observation is regenerated with the new evidence merged in. When a newer observation supersedes an older one, the older observation is retired so the graph reflects the current state of what is known.

Observations are read-only — they cannot be created, edited, or deleted directly. They follow the evidence in the graph.

## Steering observations

By default, Zep decides how each observation is worded and assigns it a generic type. With [observation steering](/steering-observations), you can guide that generation — shaping the wording and relevance of new observations and assigning custom types you can filter on during search. Steering changes how observations are generated, not the evidence behind them.

## Retrieving observations

Use the SDK to list the observations of a graph, fetch a single observation by UUID, or search a graph for observations.

Each call takes the UUID of a graph. For a user graph, use the `graph_uuid` that `user.create` returns. Store this UUID in your application database next to your own user ID.

### List and fetch

**`Python`**

```python Python
from zep_cloud.client import Zep

client = Zep(api_key=API_KEY)

# The graph_uuid of the user, from your application database.
graph_uuid = zep_graph_uuid

# List the observations Zep has materialized for a user's graph.
# The pager fetches the next page when you iterate.
observations = client.graph.observation.list(graph_uuid, limit=20)

for obs in observations:
    print(f"{obs.name}: {obs.summary}")

# Fetch a single item by UUID.
item = client.graph.observation.get(graph_uuid, observation_uuid)
```

**`TypeScript`**

```typescript TypeScript
import { ZepClient } from "@getzep/zep-cloud";

const client = new ZepClient({ apiKey: API_KEY });

// The graph UUID of the user, from your application database.
const graphUuid = zepGraphUuid;

// List the observations Zep has materialized for a user's graph.
// The pager fetches the next page when you iterate.
const observations = await client.graph.observation.list(graphUuid, { limit: 20, body: {} });

for await (const obs of observations) {
  console.log(`${obs.name}: ${obs.summary}`);
}

// Fetch a single item by UUID.
const item = await client.graph.observation.get(graphUuid, observationUuid);
```

**`Go`**

```go Go
import (
    "context"
    "fmt"

    zep "github.com/getzep/zep-go/v4"
    zepclient "github.com/getzep/zep-go/v4/client"
    "github.com/getzep/zep-go/v4/graph"
    "github.com/getzep/zep-go/v4/option"
)

client := zepclient.NewClient(option.WithAPIKey(API_KEY))

// The graph UUID of the user, from your application database.
graphUUID := zepGraphUUID

// List the observations Zep has materialized for a user's graph.
// The iterator fetches the next page when it reaches the end of a page.
observations, err := client.Graph.Observation.List(
    context.TODO(),
    graphUUID,
    &graph.ObservationListRequest{Limit: zep.Int(20)},
)
if err != nil {
    // handle error
}

iter := observations.Iterator()
for iter.Next(context.TODO()) {
    obs := iter.Current()
    fmt.Printf("%s: %s\n", *obs.Name, *obs.Summary)
}
if err := iter.Err(); err != nil {
    // handle error
}

// Fetch a single item by UUID.
item, err := client.Graph.Observation.Get(context.TODO(), graphUUID, observationUUID)
```

List methods use cursor pagination. The SDK pager fetches the next page when you iterate. Set `limit` to control the page size. For a graph that you create with `graph.create`, pass the `uuid` that `graph.create` returns.

### Search

To search a graph for observations, use `graph.search_observations`. The call returns one page of results:

**`Python`**

```python Python
results = client.graph.search_observations(
    graph_uuid,
    query="account suspension and recovery",
    limit=5,
)

for obs in results.items or []:
    print(obs.name, obs.score)
```

**`TypeScript`**

```typescript TypeScript
const results = await client.graph.searchObservations(graphUuid, {
  limit: 5,
  body: { query: "account suspension and recovery" },
});

for (const obs of results.data) {
  console.log(obs.name, obs.score);
}
```

**`Go`**

```go Go
results, err := client.Graph.SearchObservations(context.TODO(), graphUUID, &zep.GraphSearchObservationsRequest{
    Limit: zep.Int(5),
    Body:  &zep.SearchRequest{Query: "account suspension and recovery"},
})
if err != nil {
    // handle error
}

for _, obs := range results.Results {
    fmt.Println(*obs.Name, *obs.Score)
}
```

## Observations and the Context Block

The default [Context Block](/retrieving-context) can include observations when Smart Context Assembly selects them. Use a [context template](/context-templates) to always include observations or set a limit.

Retrieve observations directly or use [advanced Context Block construction](/advanced-context-block-construction) when you need full control.

## Related

* [Steering observations](/steering-observations) — guide how Zep words and categorizes new observations.
* [Facts](/facts) — granular, edge-level claims that often serve as evidence for observations.
* [Entities](/entities) — entity-level summaries on the Context Graph.
* [Episodes](/episodes) — the supporting evidence that observations are grounded in.
* [Searching the graph](/searching-the-graph) — how to retrieve observations with graph search.
* [Context types](/context-types) — overview of the other context types.