feat(rectification): add adaptive question selector
This commit is contained in:
@@ -5,9 +5,18 @@ from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
SCRIPTS_DIR = Path(__file__).resolve().parent
|
||||
if str(SCRIPTS_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(SCRIPTS_DIR))
|
||||
|
||||
from active_rectification_selector import select_next_questions
|
||||
|
||||
OPTIONS = [
|
||||
{"key": "A", "label": "明确有,且时间大致吻合", "score": 2},
|
||||
{"key": "B", "label": "有类似,但时间略偏或不够重大", "score": 1},
|
||||
@@ -15,6 +24,18 @@ OPTIONS = [
|
||||
{"key": "D", "label": "不确定 / 不记得", "score": 0},
|
||||
]
|
||||
|
||||
QUESTION_RELIABILITY = {
|
||||
"education": 1.0,
|
||||
"residence": 0.95,
|
||||
"relationship": 0.95,
|
||||
"career": 0.9,
|
||||
"career_learning": 0.88,
|
||||
"health_pressure": 0.72,
|
||||
"public_work": 0.82,
|
||||
"fine_timing": 0.45,
|
||||
}
|
||||
|
||||
|
||||
QUESTION_TEMPLATES = [
|
||||
("education_environment_shift", 1, "education", ["D24", "D4", "Dasha"], "age_16_to_18", "16-18岁附近,是否有明显学业、学校、专业方向或学习环境变化?", "middle_candidate_cluster", "against_D24_sensitive_cluster"),
|
||||
("residence_relocation_shift", 1, "residence", ["D4", "12H", "Rahu/Ketu", "Transit"], "age_20_to_24", "20-24岁附近,是否有搬家、离乡、长期异地、住宿或居住结构变化?", "D4_relocation_cluster", "against_D4_relocation_cluster"),
|
||||
@@ -69,6 +90,26 @@ def _candidate_scan(
|
||||
samples.append(sample)
|
||||
has_true_recast = all("varga_lagna" in sample for sample in samples)
|
||||
has_kp_recast = all("kp_cusps" in sample for sample in samples)
|
||||
minute_scan = None
|
||||
if lat is not None and lon is not None and tz is not None:
|
||||
try:
|
||||
from candidate_time_sensitivity_scan import scan_candidate_times
|
||||
except ModuleNotFoundError: # pragma: no cover - package import
|
||||
from scripts.candidate_time_sensitivity_scan import scan_candidate_times
|
||||
minute_scan = scan_candidate_times(
|
||||
{
|
||||
"year": center.year,
|
||||
"month": center.month,
|
||||
"day": center.day,
|
||||
"hour": center.hour,
|
||||
"minute": center.minute,
|
||||
"lat": lat,
|
||||
"lon": lon,
|
||||
"tz": tz,
|
||||
},
|
||||
uncertainty_minutes=uncertainty_minutes,
|
||||
step_minutes=step_minutes,
|
||||
)
|
||||
computed_layers = ["time_range", "candidate_cluster", "question_sensitivity_map"]
|
||||
blocked_layers = ["true_varga_recast", "true_kp_cusp_recast", "true_arudha_recast"]
|
||||
if has_true_recast:
|
||||
@@ -84,6 +125,7 @@ def _candidate_scan(
|
||||
"candidate_count": candidate_count,
|
||||
"cluster_labels": ["early_candidate_cluster", "middle_candidate_cluster", "late_candidate_cluster"],
|
||||
"samples": samples,
|
||||
"minute_scan": minute_scan,
|
||||
"sensitivity_summary": {
|
||||
"method": "minute_feature_scan_v2",
|
||||
"high_value_layers": ["D2", "D4", "D9", "D10", "D24", "D30", "D60", "UL", "A7", "A10", "KP_cusp"],
|
||||
@@ -201,6 +243,18 @@ def build_questionnaire(
|
||||
"window": window,
|
||||
"prompt": prompt,
|
||||
"options": OPTIONS,
|
||||
"positive_cluster": yes_bias,
|
||||
"negative_cluster": no_bias,
|
||||
"factual_reliability": QUESTION_RELIABILITY.get(domain, 0.75),
|
||||
"why_this_question": [
|
||||
f"用于区分 {yes_bias} 与 {no_bias},并核对 {', '.join(sensitivity)} 在候选窗口内的差异。"
|
||||
],
|
||||
"answer_impact": {
|
||||
"A": f"提高 {yes_bias} 的优先级。",
|
||||
"B": f"弱支持 {yes_bias}。",
|
||||
"C": f"提高 {no_bias} 的相对权重。",
|
||||
"D": "保持中性并转问其他可核验主题。",
|
||||
},
|
||||
"scoring_map": {
|
||||
"A": {"effect": "support", "cluster": yes_bias, "points": 2},
|
||||
"B": {"effect": "weak_support", "cluster": yes_bias, "points": 1},
|
||||
@@ -208,18 +262,23 @@ def build_questionnaire(
|
||||
"D": {"effect": "neutral", "cluster": "neutral", "points": 0},
|
||||
},
|
||||
})
|
||||
candidate_scan = _candidate_scan(
|
||||
_parse_time(birth_time),
|
||||
uncertainty_minutes,
|
||||
step_minutes,
|
||||
lat=lat,
|
||||
lon=lon,
|
||||
tz=tz,
|
||||
ayanamsa=ayanamsa,
|
||||
)
|
||||
selection = select_next_questions(
|
||||
{"questions": questions, "candidate_scan": candidate_scan}, {}, limit=1
|
||||
)
|
||||
return {
|
||||
"scope": "active_birth_time_rectification_questionnaire",
|
||||
"schema_version": 1,
|
||||
"candidate_scan": _candidate_scan(
|
||||
_parse_time(birth_time),
|
||||
uncertainty_minutes,
|
||||
step_minutes,
|
||||
lat=lat,
|
||||
lon=lon,
|
||||
tz=tz,
|
||||
ayanamsa=ayanamsa,
|
||||
),
|
||||
"candidate_scan": candidate_scan,
|
||||
"selection": selection,
|
||||
"workflow": [
|
||||
"candidate_time_scan",
|
||||
"varga_arudha_kp_sensitivity_diff",
|
||||
@@ -357,6 +416,16 @@ def score_answers(
|
||||
"downgrade_reasons": downgrade_reasons,
|
||||
}
|
||||
)
|
||||
next_round_selection = select_next_questions(
|
||||
{
|
||||
"questions": questions,
|
||||
"candidate_scan": questionnaire.get("candidate_scan")
|
||||
if isinstance(questionnaire.get("candidate_scan"), dict)
|
||||
else None,
|
||||
},
|
||||
{item["id"]: item["answer"] for item in applied},
|
||||
limit=1,
|
||||
)
|
||||
return {
|
||||
"scope": "active_birth_time_rectification_scoring",
|
||||
"schema_version": 1,
|
||||
@@ -412,6 +481,7 @@ def score_answers(
|
||||
],
|
||||
"next_round": next_round,
|
||||
"next_round_questions": [question for question in unanswered if question.get("round") == next_round],
|
||||
"next_round_selection": next_round_selection,
|
||||
"applied_scoring": applied,
|
||||
"unknown_question_ids": unknown_ids,
|
||||
"invalid_answers": invalid_answers,
|
||||
|
||||
Reference in New Issue
Block a user