rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
1a166f9
·
1 Parent(s): 78c652f

fix(retrieval): structural prose⇄card 1:1 gate (KI-PROSE-CARD-1to1)

Browse files

User requirement: "can't have a situation where it suggests something in
chat but won't generate a card; it should only suggest if it's worth
suggesting." Residual after KI-QUALITY-SEED: the LLM occasionally padded
prose with a sub-floor C-grade policy that the citation builder then gated
out (3 prose / 2 cards).

Fix: for intent=recommendation, apply the SAME fitness floor the citation
builder uses (single_brain._recommendation_fit, lazy import) to the
retrieved set BEFORE it reaches the LLM. The LLM therefore only ever sees
cardable policies → it cannot name one it won't card → prose ⇄ cards are
1:1 by construction. If nothing clears the floor the set is empty and the
LLM honestly says "no strong match" rather than padding (also the
requirement). qa / follow-up intents untouched (they cite supporting
source chunks regardless of recommendation grade).

Verified end-to-end: name captured turn 0 → fact-find → quality-seeded
retrieval → recommends ManipalCigna Sarvah Param (A/86), SBI Super Health
(A/82), ICICI Health AdvantEdge (A/80) with real UINs → every named
policy cited (CITATIONS 3-4, strict 1:1). Full pytest gate green (rc=0,
0 failures); scorecard-parity green. Not pushed (deploy-altogether).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

Files changed (1) hide show
  1. backend/brain_tools.py +18 -0
backend/brain_tools.py CHANGED
@@ -989,6 +989,24 @@ async def retrieve_policies(
989
  # session cache too so prose ⇄ cited cards are gated on the SAME set.
990
  filtered = filtered[: max((int(top_k) if top_k else 8), 12)]
991
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
992
  # X7 — cache slug→insurer lookups on session so a subsequent
993
  # mark_recommendation call (same turn) can stamp the right insurer on the
994
  # shown_policies event. Each retrieve_policies call MERGES into the cache
 
989
  # session cache too so prose ⇄ cited cards are gated on the SAME set.
990
  filtered = filtered[: max((int(top_k) if top_k else 8), 12)]
991
 
992
+ # KI-PROSE-CARD-1to1 (2026-05-17) — user requirement: "can't have a
993
+ # situation where it suggests something in chat but won't generate a
994
+ # card." For a RECOMMENDATION turn, apply the SAME fitness floor the
995
+ # citation builder uses (_recommendation_fit) HERE, so the LLM only
996
+ # ever SEES cardable policies → prose ⇄ cards are 1:1 by
997
+ # construction (it cannot name a policy it won't card because it
998
+ # never receives one). If nothing clears the floor we return none —
999
+ # the LLM then honestly says "no strong match" rather than padding
1000
+ # with a sub-floor policy (also the requirement). qa / follow-up
1001
+ # intents are untouched: they legitimately cite supporting source
1002
+ # chunks regardless of recommendation grade.
1003
+ if (intent or "").lower() == "recommendation" and filtered:
1004
+ try:
1005
+ from backend.single_brain import _recommendation_fit
1006
+ filtered = [c for c in filtered if _recommendation_fit(c)[0]]
1007
+ except Exception as e: # noqa: BLE001 — never break retrieval
1008
+ _log.warning("rec-fit 1:1 gate failed: %s", e)
1009
+
1010
  # X7 — cache slug→insurer lookups on session so a subsequent
1011
  # mark_recommendation call (same turn) can stamp the right insurer on the
1012
  # shown_policies event. Each retrieve_policies call MERGES into the cache