rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
75b229d
·
1 Parent(s): 2412797

fix(orchestrator): KI-012 critical — transition to free-form after fact-find

Browse files

P002 transcript revealed: after fact-find completed at turn 11, the bot
returned the SAME readback summary ("Got it — here's what I've understood:
…") to all 19 subsequent free-form policy questions. User asked about
claim settlement, room rent caps, comparisons — bot just echoed the
profile readback every time.

Root cause: on fact-find completion, the code only cleared
`session.awaiting_question_id` but did NOT set
`session.free_form_session = True`. Next turn classifier still routed
through the fact-find branch, `next_question()` returned None, code
hit the "complete" path again, emitted readback summary again. Loop.

Fix is one line: set `session.free_form_session = True` + flush. Now
subsequent turns skip the fact-find branch entirely and go through
retrieval + brain as intended.

Effect on the audit data: every persona's post-fact-find ~20 turns
will now go through real RAG + brain instead of silently looping the
readback. The 100-persona audit data will finally surface real
policy-Q&A behaviour at scale.

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

backend/orchestrator.py CHANGED
@@ -242,9 +242,15 @@ async def handle_turn(
242
  else:
243
  brain_tag = "needs_finder::fact_find_continue" if in_fact_find_continuation else "needs_finder::fact_find_start"
244
  else:
245
- # Fact-find complete — produce a profile readback + invite next step
 
 
 
 
246
  from backend.needs_finder import readback_summary
247
  session.set_awaiting(None)
 
 
248
  summary = readback_summary(session.profile)
249
  reply = (
250
  f"Got it — here's what I've understood: {summary}. Want me to suggest 2-3 policies that fit your profile, "
 
242
  else:
243
  brain_tag = "needs_finder::fact_find_continue" if in_fact_find_continuation else "needs_finder::fact_find_start"
244
  else:
245
+ # Fact-find complete — produce a profile readback + invite next step.
246
+ # CRITICAL (KI-012): flip free_form_session=True so subsequent turns
247
+ # don't re-enter the fact-find branch and repeat the readback. Before
248
+ # this, every post-fact-find turn returned the same readback summary
249
+ # because next_question() kept returning None.
250
  from backend.needs_finder import readback_summary
251
  session.set_awaiting(None)
252
+ session.free_form_session = True
253
+ session._flush()
254
  summary = readback_summary(session.profile)
255
  reply = (
256
  f"Got it — here's what I've understood: {summary}. Want me to suggest 2-3 policies that fit your profile, "
docs/40-evaluation/known-issues.md CHANGED
@@ -209,3 +209,48 @@ empirical signal for whether a fix is actually working in production.
209
  The standing ratio target: **for every 1 user-facing bug a reviewer
210
  catches, we should close 5 internal issues from this log before the next
211
  review.**
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  The standing ratio target: **for every 1 user-facing bug a reviewer
210
  catches, we should close 5 internal issues from this log before the next
211
  review.**
212
+
213
+ ### KI-011 — Fact-find re-ask infinite loop under load — **FIXED in `171f2a4`**
214
+
215
+ **Severity:** P0
216
+ **Source:** `backend/orchestrator.py` fact-find branch + `backend/fact_find_normalizer.py` LLM-only path
217
+ **Discovered:** First persona of the 100-persona audit (P002, verbose style)
218
+
219
+ When NIM rate-limited the Llama-3.3-70B normalizer under audit
220
+ concurrency, the LLM call raised, the orchestrator marked the answer
221
+ ambiguous, kept `awaiting_question_id` set, and re-asked the same
222
+ question on the next turn. User moved on with answers to OTHER
223
+ questions. Normalizer rejected them. Bot re-asked again. Infinite loop.
224
+
225
+ **Fix:** Keyword fast-path in `fact_find_normalizer.py` (hand-curated
226
+ substring matchers for 9 metros, 15 tier1 cities, dependents
227
+ combinations, income/budget bands, primary goals, common health
228
+ conditions) bypasses the LLM for ~80% of answers. Re-ask cap in
229
+ `orchestrator.py` gives up after 2 failed normalizations on the same
230
+ question and marks it asked. Production audit on persona P002
231
+ post-fix: 30/30 turns completed in 85s with 0 refusals (vs. infinite
232
+ loop before).
233
+
234
+ ### KI-012 — Bot stuck in fact_find_complete readback loop — **FIXED in next commit**
235
+
236
+ **Severity:** P0
237
+ **Source:** `backend/orchestrator.py` fact-find-complete branch
238
+ **Discovered:** Reviewing audit transcript of P002 post-KI-011-fix
239
+
240
+ After fact-find completes, the orchestrator only calls
241
+ `session.set_awaiting(None)` — it does NOT flip
242
+ `session.free_form_session = True`. On every subsequent turn, the
243
+ classifier still routes through the fact-find branch (because
244
+ `free_form_session` is false), `next_question()` returns None (all
245
+ fields captured), and the code path emits the readback summary AGAIN
246
+ instead of going to retrieval + brain.
247
+
248
+ **Effect:** P002 used 19 of 30 turns repeating the same readback
249
+ "Got it — here's what I've understood: …" instead of answering the
250
+ user's real policy questions. Every persona that completes fact-find
251
+ hits this.
252
+
253
+ **Fix:** When fact-find completes, set
254
+ `session.free_form_session = True` and flush to disk. Subsequent turns
255
+ skip the fact-find branch entirely and go through retrieval + brain
256
+ as intended.