fix: Simplified signal→ID mapping (direct lookup)
Root Cause: - Previous index-based mapping assumed signals come in same order as questions - But LLM response order can differ from question configuration order - Led to signal values being assigned to wrong question IDs Old Logic (BUGGY): 1. Build question_type → [list of IDs] 2. Track index per type 3. Get Nth ID from list → Assumes LLM answers in question definition order ❌ New Logic (CORRECT): 1. Build question_type → question_id (direct mapping) 2. For each signal: lookup type → get ID → Order-independent ✅ Backend workflow_executor.py: - Removed index tracking (type_counts) - Direct lookup: question_type_to_id[signal.question_type] - Added ERROR log if duplicate question types found - Added INFO log for each mapped signal (debugging) Important: - Each question MUST have a UNIQUE type - If two questions share same type: ERROR logged - System designed for unique types (LLM can't answer duplicates) Example Debug Output: ``` Mapped signal: protein_ausreichend → signal_q21 = 'nein' Mapped signal: kohlenhydrate_strategie → signal_q1775... = 'von Proteinen' ``` Issue: Signal values assigned to wrong question IDs Version: 0.9p (workflow module) Part 3: End Node Template Engine - Signal Mapping Fix Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
3b4902dc11
commit
29a3dbceb5
|
|
@ -603,8 +603,8 @@ def execute_end_node(
|
||||||
"status": node_state.status.value if node_state.status else "unknown",
|
"status": node_state.status.value if node_state.status else "unknown",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Build question_type → question_id mapping from workflow graph
|
# Build direct question_type → question_id mapping
|
||||||
question_id_map = {}
|
question_type_to_id = {}
|
||||||
if graph:
|
if graph:
|
||||||
workflow_node = next((n for n in graph.nodes if n.id == node_id), None)
|
workflow_node = next((n for n in graph.nodes if n.id == node_id), None)
|
||||||
if workflow_node and workflow_node.question_augmentations:
|
if workflow_node and workflow_node.question_augmentations:
|
||||||
|
|
@ -613,35 +613,31 @@ def execute_end_node(
|
||||||
q_type = q_dict.get('type')
|
q_type = q_dict.get('type')
|
||||||
q_id = q_dict.get('id')
|
q_id = q_dict.get('id')
|
||||||
if q_type and q_id:
|
if q_type and q_id:
|
||||||
# Multiple questions with same type → use ID
|
# WICHTIG: Wenn mehrere Fragen den gleichen type haben, ist das ein Fehler!
|
||||||
if q_type not in question_id_map:
|
if q_type in question_type_to_id:
|
||||||
question_id_map[q_type] = []
|
logger.error(
|
||||||
question_id_map[q_type].append(q_id)
|
f"DUPLICATE question type '{q_type}'! "
|
||||||
|
f"First ID: {question_type_to_id[q_type]}, Second ID: {q_id}. "
|
||||||
|
f"Each question MUST have a UNIQUE type!"
|
||||||
|
)
|
||||||
|
question_type_to_id[q_type] = q_id
|
||||||
|
|
||||||
# Add normalized signals as {{node_id.signal_ID}} (not type!)
|
# Add normalized signals as {{node_id.signal_ID}}
|
||||||
if node_state.normalized_signals:
|
if node_state.normalized_signals:
|
||||||
# Track which types we've seen (for duplicate detection)
|
|
||||||
type_counts = {}
|
|
||||||
|
|
||||||
for signal in node_state.normalized_signals:
|
for signal in node_state.normalized_signals:
|
||||||
# Convert NormalizedSignal object to dict if needed
|
# Convert NormalizedSignal object to dict if needed
|
||||||
signal_dict = signal.model_dump() if hasattr(signal, 'model_dump') else signal
|
signal_dict = signal.model_dump() if hasattr(signal, 'model_dump') else signal
|
||||||
q_type = signal_dict['question_type']
|
q_type = signal_dict['question_type']
|
||||||
|
|
||||||
# Determine which ID to use for this signal
|
# Direct lookup: question_type → question_id
|
||||||
if q_type in question_id_map:
|
if q_type in question_type_to_id:
|
||||||
# Get the Nth ID for this type (handles duplicates)
|
q_id = question_type_to_id[q_type]
|
||||||
type_idx = type_counts.get(q_type, 0)
|
signal_key = f"signal_{q_id}"
|
||||||
type_counts[q_type] = type_idx + 1
|
signal_value = signal_dict['normalized_value'] or signal_dict['raw_value']
|
||||||
|
node_context[signal_key] = signal_value
|
||||||
if type_idx < len(question_id_map[q_type]):
|
logger.info(f"Mapped signal: {q_type} → {signal_key} = '{signal_value}'")
|
||||||
q_id = question_id_map[q_type][type_idx]
|
|
||||||
signal_key = f"signal_{q_id}"
|
|
||||||
node_context[signal_key] = signal_dict['normalized_value'] or signal_dict['raw_value']
|
|
||||||
else:
|
|
||||||
logger.warning(f"No question_id found for signal type={q_type} index={type_idx}")
|
|
||||||
else:
|
else:
|
||||||
logger.warning(f"No question_id mapping for type={q_type}")
|
logger.warning(f"No question_id found for signal type='{q_type}' (available types: {list(question_type_to_id.keys())})")
|
||||||
|
|
||||||
# Add question texts as {{node_id.question_ID}}
|
# Add question texts as {{node_id.question_ID}}
|
||||||
if graph:
|
if graph:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user