Spaces:
Sleeping
fix(retrieval): KI-243 — CRITICAL: relax citation_grounding (chunk_offset optional)
Browse filesZ2 5-persona test showed 15/15 retrieve_policies calls returned 0 chunks.
0/5 personas reached recommendation. Total blocker.
Z3 diagnosis (via /tmp/diag_retrieval.py): raw rag.retrieve returned 8
healthy chunks for the Rajesh profile (52, Pune, family of 2, 40L,
diabetes+BP). enforce_citation_grounding then dropped 8/8 because:
- The filter required chunk_offset or chunk_idx as a non-negative int
- brain_tools.retrieve_policies's dict-builder copies these keys from
RetrievedChunk: chunk_id / policy_id / policy_name / insurer_slug /
doc_type / source_url / chunk_text / score / min_entry_age / max_entry_age
- Neither chunk_idx nor chunk_offset is in that list — they're stored
on the dataclass but not copied to the dict the filter sees.
Brains cite policies by identity (policy_id, policy_name, UIN), not by
chunk offset. The grounding filter only needs to verify the chunk has
a real policy attached. Relaxed to:
- require: policy_id (non-empty str) + policy_name (non-empty str)
- drop: chunk_offset / chunk_idx requirements entirely
Diag rerun after fix: 8/8 chunks pass enforce_citation_grounding.
apply_profile_filter then correctly passes all 8 (Rajesh age=52 inside
every policy's entry_age band). No over-constraint regression.
Unblocks Path B end-to-end recommendations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- backend/retrieval_filters.py +14 -12
|
@@ -326,28 +326,30 @@ def empty_retrieval_guard(
|
|
| 326 |
# ---------------------------------------------------------------------------
|
| 327 |
|
| 328 |
def enforce_citation_grounding(chunks: Iterable[Any]) -> list[Any]:
|
| 329 |
-
"""Drop chunks missing
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 334 |
"""
|
| 335 |
kept: list[Any] = []
|
| 336 |
for ch in chunks:
|
| 337 |
m = _meta(ch)
|
| 338 |
pid = m.get("policy_id")
|
| 339 |
pname = m.get("policy_name")
|
| 340 |
-
# accept either field name; chunk_idx is the existing schema in rag/retrieve.py
|
| 341 |
-
offset = m.get("chunk_offset")
|
| 342 |
-
if offset is None:
|
| 343 |
-
offset = m.get("chunk_idx")
|
| 344 |
-
|
| 345 |
if not pid or not isinstance(pid, str):
|
| 346 |
continue
|
| 347 |
if not pname or not isinstance(pname, str):
|
| 348 |
continue
|
| 349 |
-
if not isinstance(offset, int) or offset < 0:
|
| 350 |
-
continue
|
| 351 |
kept.append(ch)
|
| 352 |
return kept
|
| 353 |
|
|
|
|
| 326 |
# ---------------------------------------------------------------------------
|
| 327 |
|
| 328 |
def enforce_citation_grounding(chunks: Iterable[Any]) -> list[Any]:
|
| 329 |
+
"""Drop chunks missing citation-critical fields.
|
| 330 |
+
|
| 331 |
+
A citable chunk MUST expose:
|
| 332 |
+
- policy_id (non-empty str)
|
| 333 |
+
- policy_name (non-empty str)
|
| 334 |
+
|
| 335 |
+
The chunk offset field (`chunk_offset` or legacy `chunk_idx`) is
|
| 336 |
+
INFORMATIONAL only — it is not required for citation grounding because
|
| 337 |
+
upstream call sites (e.g. brain_tools.retrieve_policies) build pruned
|
| 338 |
+
dicts that intentionally omit it, and the brain cites by policy
|
| 339 |
+
identity, not by chunk offset. Z2 live test (2026-05-15) showed 15/15
|
| 340 |
+
retrieve_policies calls returning 0 chunks because we required an
|
| 341 |
+
offset that the upstream builder never included → every chunk dropped
|
| 342 |
+
here even though raw retrieval was healthy.
|
| 343 |
"""
|
| 344 |
kept: list[Any] = []
|
| 345 |
for ch in chunks:
|
| 346 |
m = _meta(ch)
|
| 347 |
pid = m.get("policy_id")
|
| 348 |
pname = m.get("policy_name")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
if not pid or not isinstance(pid, str):
|
| 350 |
continue
|
| 351 |
if not pname or not isinstance(pname, str):
|
| 352 |
continue
|
|
|
|
|
|
|
| 353 |
kept.append(ch)
|
| 354 |
return kept
|
| 355 |
|