← Files RedisARCHIVED FILE
skills/iris-development/references/ltm-search.md
4.12 KB · Oct 2, 2026 · 00:19 UTC
## Search Long-Term Memory Semantically with Filters
`search_long_term_memory(...)` (Python) / `searchLongTermMemory(...)` (TypeScript) runs a vector search over LTM records and applies structured filters in the same call. Combining both is the supported path — do not pull a wide vector result and filter on the client.
**Correct:** Pre-filter by the structured fields you already know, then rank by semantic similarity.
**Python:**
```python
from redis_agent_memory import AgentMemory, models
def recall(
agent_memory: AgentMemory,
*,
owner_id: str,
query: str,
namespace: str | None = None,
k: int = 5,
):
filt = {
"owner_id": {"eq": owner_id},
"memory_type": {"in": ["semantic", "episodic"]},
}
if namespace is not None:
filt["namespace"] = {"eq": namespace}
res = agent_memory.search_long_term_memory(
text=query, # embedded server-side
similarity_threshold=0.7, # normalized cosine, 0–1
filter_op=models.FilterConjunction.ALL, # AND across filter keys
filter_=filt, # NB: trailing underscore — `filter` is reserved in Python
limit=k, # 1–100, default 10
)
return res.memories
```
**TypeScript:**
```typescript
import { AgentMemory } from "@redis-iris/agent-memory";
async function recall(
agentMemory: AgentMemory,
args: { ownerId: string; query: string; namespace?: string; k?: number },
) {
const res = await agentMemory.searchLongTermMemory({
text: args.query,
similarityThreshold: 0.7,
filterOp: "all", // AND across filter keys
filter: {
ownerId: { eq: args.ownerId },
...(args.namespace ? { namespace: { eq: args.namespace } } : {}),
memoryType: { in: ["semantic", "episodic"] },
},
limit: args.k ?? 5,
});
return res.memories;
}
```
**Incorrect:** Querying with only `text` and filtering client-side.
```python
# Bad: pulls up to 100 unrelated records per user, then re-filters in Python.
# Pays the vector-search cost on the full store, and capped at 100 results
# you may miss the one you needed.
hits = agent_memory.search_long_term_memory(text=query, limit=100).memories
for m in hits:
if m.owner_id == owner_id and m.namespace == namespace:
...
```
**Filter operators (per field):**
| Field | Operators |
|---|---|
| `session_id`, `owner_id`, `namespace` | `eq`, `ne`, `in`, `all` |
| `topics`, `memory_type` | `eq`, `ne`, `in`, `all` (tag filter) |
| `created_at` | `gt`, `lt`, `gte`, `lte`, `eq` (tz-aware `datetime` / `Date`) |
`filter_op` / `filterOp` controls how the **top-level filter fields** combine: `"all"` (default, AND) or `"any"` (OR). Inside one field, `eq` / `ne` / `in` / `all` are mutually exclusive — set exactly one.
**Similarity threshold:** Normalized cosine similarity (0–1). Start at 0.7 and tune per workload — too high returns empty pages; too low returns noise.
**Pagination:** Pass `next_page_token` / `nextPageToken` back verbatim. Don't decode it; the server may change the encoding.
```python
def iter_results(agent_memory, *, query: str, owner_id: str):
token = None
while True:
page = agent_memory.search_long_term_memory(
text=query,
filter_={"owner_id": {"eq": owner_id}},
limit=50,
page_token=token,
)
yield from page.memories
token = page.next_page_token
if not token:
return
```
```typescript
async function* iterResults(
agentMemory: AgentMemory,
args: { query: string; ownerId: string },
) {
let pageToken: string | undefined;
while (true) {
const page = await agentMemory.searchLongTermMemory({
text: args.query,
filter: { ownerId: { eq: args.ownerId } },
limit: 50,
pageToken,
});
yield* page.memories;
if (!page.nextPageToken) return;
pageToken = page.nextPageToken;
}
}
```
**No-query browsing:** Omit `text` to apply only the structured filters (vector ranking is skipped, results are returned in record order).
SHA-256: 32eda0bacf631e08436bf58041b701560a8ea4b8c0a8ae655a69a758cab27893