Explainability
For every query your application runs, MemorySync captures a structured, immutable record describing how the query was answered — which intent was detected, which knowledge path was used, which memories were selected, and whether the query was refused. These records are exposed through an admin-only API for audit, debugging, and recall-quality investigations.
The Explainability Contract
Every POST /memory/query call produces exactly one explanation record. The record is written asynchronously after the response is returned to the caller, so explainability never adds latency to the query itself. The captured fields are strictly structured — IDs, enum values, scores, and timestamps — with no free-text reasoning, prompts, or chain-of-thought.
- Explainability is read-only. There is no API to create, modify, or delete records.
- Capture happens after the response is returned, in a background task. Failures are swallowed and never affect the query result.
- Records are immutable. No update or upsert path exists.
- No free-text fields. No prose, no LLM output, no prompts are ever stored or exposed.
The Explanation Record
Every record contains the following structured fields, and only these fields:
| Field | Description |
|---|---|
query_id | UUID assigned to the query at request time. Use this to correlate explanation records with your own request logs. |
query_timestamp | When the query was received. |
processing_time_ms | Total processing time for the query, in milliseconds. |
intent_classification | The detected intent: factual, analytical, hybrid, or conversational. |
knowledge_path | Which knowledge sources were used to answer the query: canonical, structured, hybrid, or refused. |
confidence_score | The result confidence score (0.0–1.0) as computed by the retrieval pipeline. Read-only — never recomputed by explainability. |
canonical_memory_ids | IDs of canonical memories that were used to answer the query. |
structured_source_ids | IDs of structured knowledge rows that were used. |
refused | Whether the query was refused. |
refusal_reason | If refused, one of low_confidence, missing_data, conflict_detected, policy_restricted, or ambiguous_intent. Null otherwise. |
created_at | When the explanation record itself was written. |
Intent Classifications
Each query is classified into exactly one intent before retrieval runs. The intent steers routing inside the query pipeline:
| Intent | What it means |
|---|---|
factual | A direct lookup over canonical memories — e.g. “What is the user’s preferred timezone?”. |
analytical | An aggregation or computation over structured knowledge — e.g. “How many tickets were resolved last week?”. |
hybrid | A question that requires both canonical memories and structured knowledge. |
conversational | A free-form conversational query that may not have a single retrievable answer. |
Knowledge Paths
The knowledge path describes which retrieval sources actually contributed to the answer. It is derived deterministically from the sources the pipeline consulted — there is no inference involved.
| Knowledge Path | Meaning |
|---|---|
canonical | The answer came from canonical memories only. |
structured | The answer came from structured knowledge only. |
hybrid | Both sources contributed. |
refused | No sources contributed because the query was refused before retrieval completed. |
Refusal Reasons
When a query is refused, the record captures the reason as one of a small set of enumerated values. Refusal decisions are made by the query router before retrieval runs; explainability only records what happened.
| Reason | When it is used |
|---|---|
low_confidence | The best-ranked result fell below the configured confidence threshold. |
missing_data | No relevant memories or rows exist to answer the query. |
conflict_detected | Multiple candidate answers contradicted one another and no single answer could be selected. |
policy_restricted | The query violates a configured refusal policy for the project or organization. |
ambiguous_intent | The query intent could not be resolved unambiguously. |
API Endpoints
All explainability endpoints are admin-only and tenant-scoped. They live under /api/v1/explainability and require a JWT with admin privileges.
| Endpoint | Purpose |
|---|---|
GET /api/v1/explainability/explanations | List explanation records with paging and filters (intent, knowledge path, date range, user, refused-only). |
GET /api/v1/explainability/explanations/{query_id} | Fetch a single explanation record by query_id. |
GET /api/v1/explainability/stats | Aggregated statistics over the last N days: total queries, breakdown by intent and knowledge path, refusal rate, average confidence, average processing time. |
GET /api/v1/explainability/metrics | Internal capture metrics: write success/failure counts and capture latency. Used to verify that explainability never blocks query execution. |
All list and detail responses use the same structured field set described above. There is no field for free-text reasoning, prompt traces, or LLM output — by design.
Non-Blocking Capture
Explainability capture is intentionally decoupled from the query path:
- Post-response. The query response is built and returned to the caller first. Only afterwards does the platform queue the explanation write.
- Background task. The write executes in a background task and is independent of the caller’s connection.
- Failure isolation. If the capture fails for any reason, the failure is logged and silently swallowed. The query result is never affected.
- Verifiable. The metrics endpoint surfaces write success/failure counts and latency so operators can confirm capture health.
What Explainability Never Stores
Several categories of information are explicitly excluded from explanation records as a matter of design:
- Raw query strings. Queries may contain PII; only the
query_idappears in records. - Free-text reasoning or “why this answer” prose.
- Prompts, prompt fragments, or any model-call payloads.
- LLM output, chain-of-thought, or scratchpad content.
- Decrypted memory content. Records only reference memories by ID.