> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evermind.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Context Retrieval

> Multi-round, tool-calling retrieval that keeps searching until the evidence is sufficient

Context Retrieval (`POST /api/v2/memory/context`) hands retrieval to an LLM. The model gets your question and a set of retrieval tools, decides which ones to call, reads what comes back, works out what is still missing, and writes new queries to find it. When it judges the evidence sufficient, it stops, and EverOS returns the memories it found.

It is built for questions a single search answers poorly: multi-hop questions, questions about time and order, and questions whose answer is spread across several conversations.

> [!NOTE]
>
> Context Retrieval is **Cloud-only** and returns **memories, not an answer**. The model writes an answer to end its loop, and that answer is discarded. You pass the returned memories to your own LLM, exactly as you would with search results.

## How Context Retrieval Works

```mermaid theme={null}
flowchart TD
    Q["Your question"] --> P["LLM plans the next step:<br/>what do I have, what is missing?"]
    P --> T["Call retrieval tools<br/>(several in parallel)"]
    T --> E["Read the results"]
    E --> D{"Evidence<br/>sufficient?"}
    D -- "No: write new queries" --> P
    D -- "Yes" --> S["Submit, citing the memories<br/>the answer rests on"]
    S --> R["EverOS returns the memories<br/>found across all rounds"]
```

Each round, the model:

1. **Assesses** the evidence it has so far and what the question still needs
2. **Calls tools**, often several at once, with queries it writes itself
3. **Reads** what came back and decides whether to search again or stop

The tools it can call:

| Tool | What it does |
| - | - |
| Hybrid search | Keyword and semantic search, fused |
| Keyword search | Exact terms, names and identifiers |
| Semantic search | Paraphrases and related wording |
| Cluster search | Search within one of the owner's topic clusters |
| Conversation context | Read the turns around a memory |
| Read session | Read a whole conversation |
| List sessions | See which conversations exist |

Every tool runs inside the scope of your request. The owner, `app_id`, `project_id` and `filters` are applied by the server to every search, and nothing the model writes can widen them.

## Choosing a Retrieval Method

| | Hybrid search | Agentic Retrieval | Context Retrieval |
| - | - | - | - |
| Endpoint | `/memory/search`, `method="hybrid"` | `/memory/search`, `method="agentic"` | `/memory/context` |
| How it searches | One fused keyword + vector search | LLM splits the query into sub-queries up front, runs them, merges | LLM searches in rounds, deciding each next query from what it has found |
| Latency | Sub-second | Seconds | Seconds, capped at 45s |
| Best for | Most chat turns and direct lookups | Broad, multi-part questions | Multi-hop, temporal, and cross-conversation questions |
| Owners | User and agent | User and agent | User only |

Start with hybrid. Move a question to Context Retrieval when the answer depends on connecting facts from different places, for example:

```python theme={null}
# Multi-hop: the answer needs one fact to find the next
"Which restaurant did I book for the dinner with the client Alice introduced me to?"

# Temporal: order and change over time
"What did I decide about the Q3 budget after the meeting with finance?"

# Spread across conversations
"What have I said about switching jobs over the last few months?"
```

## Basic Usage

The request takes the core search fields: `user_id`, `query`, `top_k`, `include_profile` and `filters` work as they do on `/api/v2/memory/search`. If you are reusing a search request, remove `method` and `min_score`, which are rejected with 422 here. Give the request a timeout of at least 60 seconds.

```bash cURL theme={null}
curl -X POST https://api.evermind.ai/api/v2/memory/context \
  -H "Authorization: Bearer $EVEROS_API_KEY" \
  -H "Content-Type: application/json" \
  --max-time 60 \
  -d '{
    "user_id": "user_alice",
    "query": "What did I decide about the Q3 budget after the meeting with finance?",
    "top_k": 10,
    "include_profile": true
  }'
```

```python Python (HTTP) theme={null}
import requests

BASE_URL = "https://api.evermind.ai"
headers = {
    "Authorization": f"Bearer {EVEROS_API_KEY}",
    "Content-Type": "application/json",
}

def context_retrieval(user_id: str, query: str, top_k: int = 10) -> dict:
    """Retrieve memories with the multi-round tool-calling loop."""
    response = requests.post(
        f"{BASE_URL}/api/v2/memory/context",
        json={
            "user_id": user_id,
            "query": query,
            "top_k": top_k,
            "include_profile": True,
        },
        headers=headers,
        timeout=60,  # the loop is capped at 45s on the server
    )
    response.raise_for_status()
    return response.json()["data"]

data = context_retrieval(
    user_id="user_alice",
    query="What did I decide about the Q3 budget after the meeting with finance?",
)

for ep in data["episodes"]:
    marker = "*" if ep["cited"] else " "
    print(f"{marker} [{ep['score']:.3f}] {ep['summary'][:100]}")
```

## Reading the Response

The response has the same `SearchData` shape as search, so code that reads search results reads these too. Three things differ:

* **`episodes[].cited`** is `true` for the memories the model named as the ones its answer rests on. On `/search` it is always `false`.
* **`episodes[].score`** is a rank-fusion score, not a similarity. It reflects how consistently a memory came up across the loop's searches, roughly 0.01 to 0.05, plus 1.0 for a cited memory, so cited memories always come first. Do not compare it with search scores.
* **Only user owners get episodes.** With `agent_id`, `episodes`, `agent_cases` and `agent_skills` are all empty.

`profiles` (with `include_profile`) and `unprocessed_messages` (with a top-level `session_id` filter) are filled exactly as on search.

```json theme={null}
{
  "episodes": [
    {
      "id": "6a6d...",
      "user_id": "user_alice",
      "session_id": "session_042",
      "timestamp": "2026-09-18T14:02:11Z",
      "summary": "After the finance review, Alice cut the Q3 events budget by 20% ...",
      "episode": "full narrative",
      "atomic_facts": [{"id": "...", "content": "Alice cut the Q3 events budget by 20%."}],
      "cited": true,
      "score": 1.049
    },
    {
      "id": "7b1e...",
      "summary": "Alice met the finance team to review Q3 spending ...",
      "cited": false,
      "score": 0.032
    }
  ],
  "profiles": [],
  "agent_cases": [],
  "agent_skills": [],
  "unprocessed_messages": []
}
```

## Limits and Behavior

| | Behavior |
| - | - |
| Rounds | At most 5 LLM rounds per request |
| Time | At most 45 seconds per request |
| Reaching a limit | Not an error. The memories found so far are returned |
| Owners | `user_id` only. With `agent_id`, the response is empty |
| Cost | Several LLM calls per request, so noticeably slower and more expensive than search |

The full list of request fields, including the search fields this endpoint ignores, is in the [API reference](https://docs.evermind.ai/api-reference/memory/context-retrieval-\[cloud-only]).

## Best Practices

> **Ask the whole question**
>
> The model searches until it can answer `query`, so give it the question you actually want answered, not a few keywords:
>
> ```python theme={null}
> # Good: the model knows when it is done
> query = "What did I decide about the Q3 budget after the meeting with finance?"
>
> # Weak: nothing to judge sufficiency against
> query = "Q3 budget"
> ```

> **Use cited memories to keep prompts short**
>
> When you need a compact context, pass only the cited memories to your LLM and fall back to the top-ranked ones if nothing was cited:
>
> ```python theme={null}
> cited = [ep for ep in data["episodes"] if ep["cited"]]
> context = cited or data["episodes"][:5]
> ```

> **Set a timeout and keep a fallback**
>
> The server stops the loop at 45 seconds and returns what it has. Set your client timeout to at least 60 seconds, and fall back to hybrid search on a timeout or network error:
>
> ```python theme={null}
> try:
>     data = context_retrieval(user_id, query)
> except requests.RequestException:
>     data = requests.post(
>         f"{BASE_URL}/api/v2/memory/search",
>         json={"user_id": user_id, "query": query, "method": "hybrid", "top_k": 10},
>         headers=headers,
>         timeout=10,
>     ).json()["data"]
> ```

> **Scope with filters**
>
> `filters` works as on search and applies to every tool call in the loop. Narrowing to a time range or a set of sessions keeps the model from spending rounds on memories you already know are irrelevant.

> **Keep it off the hot path**
>
> Use hybrid search for real-time chat turns. Reserve Context Retrieval for questions where getting the full picture matters more than speed: research, preparation, and questions the user expects to take a moment.

## Next Steps

> **[Context Retrieval API](https://docs.evermind.ai/api-reference/memory/context-retrieval-\[cloud-only])**
>
> Full request and response reference

> **[Agentic Retrieval](https://docs.evermind.ai/cloud/agentic-retrieval.md)**
>
> Up-front query decomposition on the search endpoint


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.