Spaces:
Sleeping
fix(retrieval): structural prose⇄card 1:1 gate (KI-PROSE-CARD-1to1)
Browse filesUser 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>
- backend/brain_tools.py +18 -0
|
@@ -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
|