rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
6e0462a
Β·
1 Parent(s): 3591985

fix(brain): don't re-ask recalled slots after returning-user confirm

Browse files

Live audit of the recall fix surfaced a real secondary bug: after the
user confirms 'yes' to 'are you the same <name>?', apply_pending_recall
correctly merged the stored profile (returning_user_recalled=True; a
recall probe proved income/city were present) β€” but the model still
RE-ASKED income band + pre-existing conditions, defeating 'pick up where
we left off'. The generic 'do not re-ask' snapshot was too weak on the
confirm turn.

Fix: _system_instruction now takes recall_applied (= _did_recall_this_turn)
and, on the apply turn, injects a high-priority 'RETURNING USER CONFIRMED
β€” PROFILE RESTORED' block listing the restored slots and forbidding any
re-ask/re-confirm of them ('Re-asking a RESTORED slot is a hard error'),
instructing a one-line welcome-back then resume at RULE 2.5 pricing /
recommendation.

Verified locally with a PRECISE interrogative-only detector (not the
naive substring checks that produced false alarms): turn-2 recaps the
full restored profile and asks only the optional RULE 2.5 pricing bundle
β€” zero re-ask of the 7. 8/8 recall regression tests pass; full pytest
suite exit 0.

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

backend/single_brain.py CHANGED
@@ -712,7 +712,7 @@ def _affirm_or_deny(text: str):
712
 
713
  def _system_instruction(
714
  profile, is_returning_user: bool = False, shortlist_block: str = "",
715
- pending_recall: "Optional[dict]" = None,
716
  ) -> dict:
717
  """Bake the profile snapshot into the system prompt so each turn the
718
  LLM knows what's already captured. Returned in Gemini's expected
@@ -777,7 +777,35 @@ def _system_instruction(
777
  "applies or discards the saved profile from their answer β€” you "
778
  "never merge anything yourself."
779
  )
780
- text = SYSTEM_PROMPT + extra + recall_block + (shortlist_block or "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
781
  return {"parts": [{"text": text}]}
782
 
783
 
@@ -1840,6 +1868,7 @@ async def handle_turn(
1840
  is_returning_user=is_returning_user,
1841
  shortlist_block=_shortlist_block,
1842
  pending_recall=_pending_recall,
 
1843
  )
1844
 
1845
  # Bug #108 + #110 β€” if the user explicitly declines the pricing /
 
712
 
713
  def _system_instruction(
714
  profile, is_returning_user: bool = False, shortlist_block: str = "",
715
+ pending_recall: "Optional[dict]" = None, recall_applied: bool = False,
716
  ) -> dict:
717
  """Bake the profile snapshot into the system prompt so each turn the
718
  LLM knows what's already captured. Returned in Gemini's expected
 
777
  "applies or discards the saved profile from their answer β€” you "
778
  "never merge anything yourself."
779
  )
780
+ restored_block = ""
781
+ if recall_applied:
782
+ _rs = json.dumps(snapshot, ensure_ascii=False, sort_keys=True)
783
+ restored_block = (
784
+ "\n\n═══════════════════════════════════\n"
785
+ "RETURNING USER CONFIRMED β€” PROFILE RESTORED "
786
+ "(HIGHEST PRIORITY THIS TURN)\n"
787
+ "═══════════════════════════════════\n"
788
+ "The user just confirmed they are the SAME returning person. "
789
+ "Their saved profile is RESTORED and FINAL for every slot "
790
+ "present here:\n" + _rs + "\n"
791
+ "Do NOT re-ask, re-confirm, re-verify or 'just double-check' "
792
+ "ANY slot present above β€” name, age, dependents, city/location, "
793
+ "income band, primary goal, health / pre-existing conditions, "
794
+ "sum insured, existing cover, budget. Re-asking a RESTORED slot "
795
+ "is a hard error: the entire point of recall is that the user "
796
+ "does NOT repeat themselves.\n"
797
+ "Your reply this turn: (1) ONE warm 'welcome back' line, then "
798
+ "(2) resume exactly where a returning user continues β€” if the "
799
+ "RULE 2.5 pricing inputs (sum insured / premium budget / co-pay "
800
+ "/ smoker / family medical history) are NOT yet captured, ask "
801
+ "ONLY those via the single RULE 2.5 prompt; otherwise go "
802
+ "straight to retrieve_policies + recommendations. Ask ONLY for "
803
+ "a slot that is genuinely ABSENT above β€” never one present."
804
+ )
805
+ text = (
806
+ SYSTEM_PROMPT + extra + recall_block + restored_block
807
+ + (shortlist_block or "")
808
+ )
809
  return {"parts": [{"text": text}]}
810
 
811
 
 
1868
  is_returning_user=is_returning_user,
1869
  shortlist_block=_shortlist_block,
1870
  pending_recall=_pending_recall,
1871
+ recall_applied=_did_recall_this_turn,
1872
  )
1873
 
1874
  # Bug #108 + #110 β€” if the user explicitly declines the pricing /
tests/test_returning_user_recall_singlebrain.py CHANGED
@@ -172,6 +172,13 @@ class TestHandleTurnIntegration(_StoredProfileFixture):
172
  self.assertEqual(sess.profile.age, 41,
173
  "stored profile was not merged on confirm")
174
  self.assertIsNone(getattr(sess, "pending_profile_recall", None))
 
 
 
 
 
 
 
175
 
176
  def test_no_keeps_session_blank(self):
177
  sess = SessionState(session_id=f"hti_{uuid.uuid4().hex[:8]}")
 
172
  self.assertEqual(sess.profile.age, 41,
173
  "stored profile was not merged on confirm")
174
  self.assertIsNone(getattr(sess, "pending_profile_recall", None))
175
+ # The apply turn MUST carry the PROFILE RESTORED directive so the
176
+ # model does not re-ask the just-recalled slots (the live bug:
177
+ # recall merged but turn-2 still asked income + pre-existing).
178
+ self.assertIn("RETURNING USER CONFIRMED β€” PROFILE RESTORED",
179
+ self.sys_prompts[-1])
180
+ self.assertIn("Re-asking a RESTORED slot is a hard error",
181
+ self.sys_prompts[-1])
182
 
183
  def test_no_keeps_session_blank(self):
184
  sess = SessionState(session_id=f"hti_{uuid.uuid4().hex[:8]}")