> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs-beta.getzep.com/v4/observations/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. > Durable, evidence-backed context that captures meaningful patterns across a graph