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

# Why the answer used what it used

> A receipt for every retrieval — what entered the prompt, what was cut, and why the memory you expected did not come back.

[← All use cases](/use-cases/overview)

<div className="uc-examples"><a href="https://github.com/anonalabs/Anona-Memory-SDK/blob/main/examples/context_receipts.py">context\_receipts.py</a></div>

## The scenario

A support agent answers a customer wrongly. You know the correct fact is in the
space, because you put it there. The search did not return it.

Without a record of the decision, there are only guesses: raise the limit, lower
the score floor, reword the query, try again. Each is cheap to try and none of
them tells you whether it was the right thing to try — **a memory the search
never matched looks exactly like a memory that does not exist.**

A context receipt is that decision written down. Every retrieve builds one,
whether or not you ask for it, so an answer stays explainable after the fact.

## Step 1 — Ask for the receipt id

`retrieve` returns a plain list and has nowhere to put a receipt id.
`retrieve_receipt` runs the same search and returns both.

```python theme={null}
res = client.retrieve_receipt(
    space_id=space,
    query="who is on call for billing?",
    limit=1,
    receipt_detail="full",
)
print(len(res), "returned, receipt", res.receipt_id)
```

`receipt_detail="full"` also asks the search to account for **its own** cuts.
Without it the receipt can only describe what happened after the search
returned — which is the half of the story you already knew.

## Step 2 — Read what was cut

```python theme={null}
receipt = client.get_receipt(res.receipt_id)
for entry in receipt["excluded"]:
    print(entry["reason"], entry["detail"])
```

Each entry carries a reason code, so "it was a near-duplicate of something you
already got" is distinguishable from "it scored below your floor" and from "it
fell past your limit". Those three have different fixes.

`excluded_truncated` is how a capped manifest admits it is capped. An incomplete
list must never read as a complete one.

## Step 3 — Ask about one specific memory

This is the call the whole feature exists for. You know an id. You did not get
it back. Ask why.

```python theme={null}
verdict = client.explain(res.receipt_id, memory_id)
print(verdict["outcome"])   # included | excluded | not_retrieved
print(verdict["arms"])      # which kinds of matching found it, and where they ranked
```

The outcome splits your problem in two:

* **`excluded`** — it was found and then dropped. `stage` says where. A bigger
  `limit` or a lower floor may well bring it back. `arms` tells you *how* it was
  found: a memory matched by keyword but not semantically shares words with your
  query but not meaning; the reverse is the more usual case.
* **`not_retrieved`** — nothing matched it at any stage. **No threshold change
  will help**, because nothing ever found it. The query wording, or the scope
  you searched, is what to change.

That distinction is the difference between an afternoon of threshold-tuning and
a one-line fix.

## Evals

Before you trust a tuning change, check it against a case you can score:

1. Write three memories you know the answer to, and one unrelated to anything.
2. Retrieve with `receipt_detail="full"` and a `limit` of 1.
3. Call `explain` on **every** id you wrote that did not come back — the
   [runnable script](https://github.com/anonalabs/Anona-Memory-SDK/blob/main/examples/context_receipts.py)
   does exactly this by diffing the ids the write reported against the ids the
   search returned.
4. Expect the unrelated memory to be `not_retrieved` and the relevant ones to be
   `excluded` at the limit. If a relevant memory comes back `not_retrieved`,
   your extraction or your scoping is the problem, not your ranking.

Re-run after any change to scoping, tags or query wording. A recall change that
moves nothing in the receipt has not done what you think it did.

## Guardrails

<Warning>
  **A receipt is not a copy of your data, and that is deliberate.** It carries
  ids, scores and reason codes only — never memory content. That is exactly what
  makes a receipt id safe to paste into a support ticket, a bug report or a
  vendor conversation.
</Warning>

* **Receipts are built for every retrieve**, so you can investigate an answer
  you did not flag at the time. You do not need to have predicted the problem.
* **`relevance_score` can exceed 1.0.** It is a product of several factors, not
  a probability. Compare scores within one result set; do not read an absolute
  threshold into them across queries.
* **A scoped query is strict.** If you passed a `user_id`, `agent_id` or
  `session_id`, untagged memories cannot match — so a memory written before you
  adopted scoping will read as `not_retrieved` forever until it is backfilled.
  Check the scope before you blame the ranking.

## The script

Complete and runnable:
[`context_receipts.py`](https://github.com/anonalabs/Anona-Memory-SDK/blob/main/examples/context_receipts.py).

See also the [context receipts API reference](/api-reference/context-receipts)
and [Tuning Recall Quality](/guides/tuning-recall).
