Deploy Poimen Memory K8s cluster with ArgoCD tracking (M2.2, M3.5-M3.7)
ci / markdown (push) Waiting to run
ci / markdown (push) Waiting to run
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
# M3.6.5 — Query: filter-then-recall, R opt-in, relevance floor
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Phase | M3.6 — Reference corpora |
|
||||
| Size | M — 1–3 days |
|
||||
| Status | ⬜ Not started |
|
||||
| Flags | — |
|
||||
| Spec | inlined below |
|
||||
| Blocks | M3.6.6 |
|
||||
| Depends | M3.6.2, M3.3, M3.2, M2.7 |
|
||||
|
||||
## Goal
|
||||
|
||||
Make R reachable on request, unreachable by default, and stop the retriever
|
||||
answering questions it has no evidence for.
|
||||
|
||||
## Facts (inlined — no spec read needed)
|
||||
|
||||
```
|
||||
mem query "why did requests over 10KB fail?" # L1,L2 — unchanged
|
||||
mem query --levels R "kubectl describe pod" # reference only
|
||||
mem query --levels L1,L2,R "..." # both, R marked in output
|
||||
mem query "…" --min-score 0.4 # override the floor
|
||||
```
|
||||
|
||||
**Filter before recall, not after.** M3.3 recalls `10×k` from HNSW and reranks
|
||||
down to `k`. A corpus is typically an order of magnitude larger than the project's
|
||||
own memory, so R rows compete for those 50 candidate slots even when the caller
|
||||
excluded them — and post-filtering then returns three results instead of five,
|
||||
quietly. The level predicate belongs in the SQL that drives the HNSW scan. The
|
||||
existing `(project, level)` index already supports it.
|
||||
|
||||
**Abstention.** With a corpus loaded, every question has *something* moderately
|
||||
close, so unconditional top-k starts returning plausible prose for questions the
|
||||
memory cannot answer — worse than an empty result, because it reads as an answer.
|
||||
If the best post-rerank score is below the floor, return no hits and say why:
|
||||
|
||||
```
|
||||
no hits above relevance floor (best 0.21 < 0.35 threshold)
|
||||
try --min-score to lower it, or --levels R to search reference docs
|
||||
```
|
||||
|
||||
The floor applies to the **reranked** score, not cosine distance. M3.2's own
|
||||
fixture separates a relevant from an irrelevant passage by four orders of
|
||||
magnitude; cosine distance does not, which is why the floor cannot live at the
|
||||
recall stage.
|
||||
|
||||
**R is visually distinct in output.** A reference hit prints its source URI and
|
||||
heading path where a project hit prints provenance. A caller must never have to
|
||||
infer from wording whether an answer came from this cluster's history or from
|
||||
upstream documentation.
|
||||
|
||||
**R has no provenance walk.** M3.3 walks `memory_edge` one hop for L1 and two for
|
||||
L2. R has no edges by construction (M3.6.2), so the walk is skipped rather than
|
||||
returning empty — and `mem verify` gains the assertion that makes that safe.
|
||||
|
||||
## Steps
|
||||
|
||||
1. Push the level filter into the recall query; assert candidate width is `10×k`
|
||||
*after* filtering.
|
||||
2. `--levels` accepts `R`; default remains `L1,L2`.
|
||||
3. Apply the relevance floor to reranked scores; `--min-score` overrides,
|
||||
`--min-score 0` disables.
|
||||
4. Abstention message names the best score, the threshold, and the two escapes.
|
||||
5. Render R hits with source URI and heading path; suppress the provenance walk.
|
||||
6. Exit code: abstention is exit 0 with no hits, not an error — it is a valid
|
||||
answer. Unresolvable project stays non-zero (M3.3 assertion 7).
|
||||
7. `mem verify --edges` asserts no `memory_edge` row names an R sha as parent.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- Default query over a database containing a large corpus returns exactly the
|
||||
same hits as before the corpus was added.
|
||||
- `--levels R` returns reference sections with URI and heading path.
|
||||
- A question with no good match returns nothing and explains itself.
|
||||
- Lowering `--min-score` surfaces the suppressed hits.
|
||||
- `mem verify` rejects a hand-inserted `L1 -> R` edge.
|
||||
|
||||
## Verify
|
||||
|
||||
**Harness:** seeded database with the poimen log *plus* a reference corpus large
|
||||
enough to dominate raw recall — at least 10× the project node count. Live
|
||||
reranker for scoring assertions, deterministic embedder elsewhere.
|
||||
|
||||
**Integration test** — `tests/it_query_levels.rs`:
|
||||
1. `a1_default_unchanged_by_corpus` — snapshot default query results before and
|
||||
after ingesting the corpus; assert byte-identical output. This is the
|
||||
headline assertion of the task.
|
||||
2. `a2_filter_before_recall` — instrument the repository; assert the SQL driving
|
||||
HNSW carries the level predicate and returns `10×k` rows post-filter, not
|
||||
`10×k` pre-filter then fewer.
|
||||
3. `a3_levels_r_returns_reference` — `--levels R` returns R nodes with source URI
|
||||
and heading path populated.
|
||||
4. `a4_floor_abstains` — a question with no relevant content returns zero hits,
|
||||
exit 0, message naming best score and threshold.
|
||||
5. `a5_floor_override_recovers` — same question with `--min-score 0` returns the
|
||||
suppressed hits, proving abstention is a floor and not a bug upstream.
|
||||
6. `a6_floor_applies_post_rerank` — construct a case where cosine is high and
|
||||
rerank is low; assert it is suppressed. The reverse ordering passes every
|
||||
other assertion here.
|
||||
7. `a7_r_hits_visually_distinct` — assert R hits carry no provenance block and do
|
||||
carry a URI, in both human and `--format json` output.
|
||||
8. `a8_no_edge_to_r` — hand-insert an `L1 -> R` edge; assert `mem verify --edges`
|
||||
exits non-zero and names the offending pair.
|
||||
9. `a9_mixed_levels_ordering` — `--levels L1,L2,R` returns both kinds ranked
|
||||
together with the level labelled on every row.
|
||||
|
||||
**Command:** `cargo test -p mem-cli query_levels`
|
||||
|
||||
**False pass:**
|
||||
- Running assertion 1 against a small corpus. If the corpus is smaller than the
|
||||
recall width, post-filtering and pre-filtering give the same answer and
|
||||
assertion 2 is the only thing separating them — the fixture size is part of the
|
||||
test.
|
||||
- Testing abstention with a query that matches nothing at all. Zero recall
|
||||
returns zero hits regardless of the floor; the fixture needs a *weak but
|
||||
non-empty* match, or assertion 4 passes with the floor unimplemented.
|
||||
- Asserting `--levels R` works without asserting the default excludes R. Both
|
||||
directions are the contract.
|
||||
|
||||
## Traps
|
||||
|
||||
- Applying the floor to the first-stage cosine score. Cosine on `nomic` puts
|
||||
unrelated text closer than intuition suggests; a floor there either suppresses
|
||||
good hits or does nothing, depending on the corpus.
|
||||
- Treating abstention as an error exit. Callers wrap `mem query` in scripts; a
|
||||
non-zero exit for "no confident answer" turns a normal outcome into a pipeline
|
||||
failure and the floor gets disabled within a week.
|
||||
- Letting the reranker see 50 R candidates and 3 project candidates in a mixed
|
||||
query. The reranker is not calibrated across levels, and the corpus wins on
|
||||
fluency; recall per level, then merge.
|
||||
|
||||
---
|
||||
|
||||
Background: [DESIGN.md](../DESIGN.md) — reference corpora, pgvector, retrieval
|
||||
Reference in New Issue
Block a user