> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs-beta.getzep.com/v4/steering-observations/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs-beta.getzep.com/_mcp/server. # Steering observations > **Experimental API** > > Observation steering is an experimental feature. The API may change in future releases. > **Info** > > Available to [Flex Plus and Enterprise](https://www.getzep.com/pricing) customers — the same plans that generate [observations](/observations). ## Why use observation steering Zep automatically derives [observations](/observations) — durable, evidence-backed patterns — from a graph. By default, Zep decides how each observation is worded and labels every observation with the type `generic`. Observation steering lets you guide that generation without changing the underlying evidence. Steering influences generation along two axes. You can set either one on its own, or both together: * **Instruction**: free-text guidance that shapes the wording and relevance of the observations Zep writes. The instruction cannot introduce facts, choose which evidence is used, or change how many observations are produced — evidence always comes from the graph. * **Observation types**: up to ten named categories Zep may assign to an observation. Zep writes the selected type to the `observation_type` property on the observation node, which you can [filter on](/searching-the-graph) during graph search. When none of your types fit, Zep falls back to `generic`. Steering applies only to observations generated *after* you configure it. It does not enqueue a job or regenerate existing observations; those update on their own as new evidence arrives. ### Use cases * **Domain vocabulary**: Label observations with categories that match your product — `commitment`, `risk`, `preference`, `churn_signal` — then filter graph search by type. * **Relevance tuning**: Steer wording and emphasis toward what your agent needs, without post-processing. * **Per-user or per-graph control**: Apply distinct guidance to a specific user graph or another graph while keeping a project-wide default. ## Basic usage Use `set_observation_steering` to replace the configuration at a scope, and `get_observation_steering` to read it back. Both return the resulting configuration. The `project` methods address the project-wide default. The `graph` methods address one graph. **`Python`** ```python Python from zep_cloud import Zep, ObservationType client = Zep( api_key=API_KEY, ) # Set the project-wide default. config = client.project.set_observation_steering( instruction="Prioritize durable commitments and stated preferences. Keep observations concise.", types=[ ObservationType(name="commitment", description="A promise or planned action the user has committed to."), ObservationType(name="preference", description="A durable preference the user has expressed."), ], ) # Read the current project configuration. config = client.project.get_observation_steering() print(config.instruction) for t in config.types or []: print(f"{t.name}: {t.description}") ``` **`TypeScript`** ```typescript TypeScript import { ZepClient } from "@getzep/zep-cloud"; const client = new ZepClient({ apiKey: API_KEY, }); // Set the project-wide default. let config = await client.project.setObservationSteering({ instruction: "Prioritize durable commitments and stated preferences. Keep observations concise.", types: [ { name: "commitment", description: "A promise or planned action the user has committed to." }, { name: "preference", description: "A durable preference the user has expressed." }, ], }); // Read the current project configuration. config = await client.project.getObservationSteering(); console.log(config.instruction); for (const t of config.types ?? []) { console.log(`${t.name}: ${t.description}`); } ``` **`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/option" ) client := zepclient.NewClient( option.WithAPIKey(API_KEY), ) // Set the project-wide default. config, err := client.Project.SetObservationSteering(context.TODO(), &zep.ObservationSteering{ Instruction: zep.String("Prioritize durable commitments and stated preferences. Keep observations concise."), Types: []*zep.ObservationType{ {Name: zep.String("commitment"), Description: zep.String("A promise or planned action the user has committed to.")}, {Name: zep.String("preference"), Description: zep.String("A durable preference the user has expressed.")}, }, }) // Read the current project configuration. config, err = client.Project.GetObservationSteering(context.TODO()) if config.Instruction != nil { fmt.Println(*config.Instruction) } for _, t := range config.Types { fmt.Printf("%s: %s\n", *t.Name, *t.Description) } ``` ## Scoping to a user or graph Steering resolves at one of two scopes: * **Project**: the default configuration, applied to every graph in the project. Use the `project` methods. * **Graph**: configuration for a single graph, addressed with the UUID of the graph. Use the `graph` methods. For a user graph, use the `graph_uuid` that `user.create` returns. For another graph, use the `uuid` that `graph.create` returns. A graph configuration is a **complete override**, not a merge — when a graph has its own configuration, Zep uses it in full and ignores the project default. Reading a graph scope returns its effective configuration. When the graph has no configuration of its own, the response contains the project default and `inherited` is `true`. **`Python`** ```python Python # Steer observations for one user only. # zep_graph_uuid is the graph_uuid of the user, from your application database. client.graph.set_observation_steering( zep_graph_uuid, instruction="Focus on support escalations and unresolved issues.", types=[ ObservationType(name="escalation", description="An issue the user escalated to support."), ], ) # Read the effective configuration for the user's graph. config = client.graph.get_observation_steering(zep_graph_uuid) print(config.inherited) # False: the graph has its own configuration ``` **`TypeScript`** ```typescript TypeScript // Steer observations for one user only. // zepGraphUuid is the graph UUID of the user, from your application database. await client.graph.setObservationSteering(zepGraphUuid, { instruction: "Focus on support escalations and unresolved issues.", types: [ { name: "escalation", description: "An issue the user escalated to support." }, ], }); // Read the effective configuration for the user's graph. const config = await client.graph.getObservationSteering(zepGraphUuid); console.log(config.inherited); // false: the graph has its own configuration ``` **`Go`** ```go Go // Steer observations for one user only. // zepGraphUUID is the graph UUID of the user, from your application database. _, err := client.Graph.SetObservationSteering(context.TODO(), zepGraphUUID, &zep.ObservationSteering{ Instruction: zep.String("Focus on support escalations and unresolved issues."), Types: []*zep.ObservationType{ {Name: zep.String("escalation"), Description: zep.String("An issue the user escalated to support.")}, }, }) // Read the effective configuration for the user's graph. config, err := client.Graph.GetObservationSteering(context.TODO(), zepGraphUUID) fmt.Println(*config.Inherited) // false: the graph has its own configuration ``` ## Observation types Each entry in `types` defines a category Zep may assign to an observation. When Zep generates an observation, it selects the best-fitting type and writes the name to the `observation_type` property on the observation node. If none of your configured types fit — or you configure no types — Zep uses `generic`. | Field | Rules | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `name` | Must match `^[a-z][a-z0-9_]*$` (start with a lowercase letter, then lowercase letters, digits, or underscores); up to 50 characters; unique within the list. `generic` is reserved and cannot be configured. | | `description` | Required; 1–500 characters. Describes when the type applies, which guides Zep's selection. | You can configure up to 10 types. Because the selected type is stored as the `observation_type` node property, you can retrieve observations of a given type with [graph search](/searching-the-graph) property filters: **`Python`** ```python Python from zep_cloud.types import SearchFilters, PropertyFilter results = client.graph.search_observations( zep_graph_uuid, query="commitments to the customer", filters=SearchFilters( property_filters=[ PropertyFilter( operator="eq", property_name="observation_type", value="commitment", ) ] ), ) for obs in results.items or []: print(obs.name, obs.score) ``` **`TypeScript`** ```typescript TypeScript const results = await client.graph.searchObservations(zepGraphUuid, { body: { query: "commitments to the customer", filters: { propertyFilters: [ { operator: "eq", propertyName: "observation_type", value: "commitment", }, ], }, }, }); for (const obs of results.data) { console.log(obs.name, obs.score); } ``` **`Go`** ```go Go results, err := client.Graph.SearchObservations(context.TODO(), zepGraphUUID, &zep.GraphSearchObservationsRequest{ Body: &zep.SearchRequest{ Query: "commitments to the customer", Filters: &zep.SearchFilters{ PropertyFilters: []*zep.PropertyFilter{ { Operator: zep.PropertyFilterOperatorEq.Ptr(), PropertyName: zep.String("observation_type"), Value: &zep.PropertyFilterValue{String: "commitment"}, }, }, }, }, }) for _, obs := range results.Results { fmt.Println(*obs.Name, *obs.Score) } ``` ## Clearing steering Sending an empty configuration — no instruction and an empty `types` list — clears steering at the addressed scope. Clearing the project scope removes the default. Clearing a graph scope removes the override, so that graph falls back to the project default. **`Python`** ```python Python # Remove the override for a user's graph (falls back to the project default). client.graph.set_observation_steering( zep_graph_uuid, types=[], ) ``` **`TypeScript`** ```typescript TypeScript // Remove the override for a user's graph (falls back to the project default). await client.graph.setObservationSteering(zepGraphUuid, { types: [] }); ``` **`Go`** ```go Go // Remove the override for a user's graph (falls back to the project default). _, err := client.Graph.SetObservationSteering(context.TODO(), zepGraphUUID, &zep.ObservationSteering{ Types: []*zep.ObservationType{}, }) ``` ## Configurable parameters | Parameter | Type | Description | Default | Required | | ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------- | | `graph_uuid` | string | The UUID of the graph. Only the `graph` methods take this parameter. For a user graph, use the `graph_uuid` of the user. | - | Yes, for the `graph` methods | | `instruction` | string | Guidance for the wording and relevance of generated observations. Up to 2000 characters. An empty or omitted instruction clears it. | - | No | | `types` | array | Up to 10 custom observation types, each with a `name` and `description`. | `[]` | No | The `project` methods address the project scope and take no UUID. ### Observation type fields | Field | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------------ | | `name` | string | Category name. Must match `^[a-z][a-z0-9_]*$`, up to 50 characters, unique within the list. `generic` is reserved. | | `description` | string | When the type applies. Required, 1–500 characters. | ## Response Both `get_observation_steering` and `set_observation_steering` return the resulting configuration at the addressed scope: | Field | Type | Description | | ------------- | ------- | ---------------------------------------------------------------------------------------------------- | | `instruction` | string | The configured instruction. The field is absent when no instruction is set. | | `types` | array | The configured observation types, each with a `name` and `description`. | | `inherited` | boolean | `true` when the graph has no configuration of its own and the response contains the project default. | Reading a graph scope returns its effective configuration, falling back to the project default when that graph has no configuration of its own. ## Related * [Observations](/observations) — the durable, evidence-backed context that steering shapes. * [Searching the Graph](/searching-the-graph) — filter observations by the `observation_type` property. * [Shape the Graph](/customizing-context) — the other ways to tailor how Zep builds the Context Graph. > Guide how Zep generates observations with a custom instruction and observation types