diff --git a/TASK-consultation-birth-accuracy-20260901.md b/TASK-consultation-birth-accuracy-20260901.md new file mode 100644 index 00000000..2bc32016 --- /dev/null +++ b/TASK-consultation-birth-accuracy-20260901.md @@ -0,0 +1,97 @@ +# 任务书 · 咨询侧出生时间精度真值传递:填了准确时间不应被当作"未校正按范围交互"(2026-09-01) + +基线:`origin/staging` @ 当前 tip(含 PR #47 合并 `b98777aa`)。 + +## 0. 用户报告与定性 + +用户在初始化(onboarding)填了**准确出生时间**(如医院记录),咨询时模型仍说"没做过生时校正,就按一个时间范围来交互"。经静态溯源,这不是模型幻觉,是**三处叠加的真值传递断裂**——模型每一轮都被服务端明确告知"未校正": + +### 根因 1 · 前端从不把时间来源/精度传给引擎(P0) + +Profile 里存有 `birth_time_source`(`hospital_record` / `family_exact` / `approximate` / `period_only` / `unknown`)和 `birth_time_status`(`accepted` / `confirmed` 表示已完成校正采用)。但 `consultation-route-service.ts` 的 `serverChartFromProfile`(约 :309)构造的 `toolInput` 只有 `year/month/day/hour/minute/city/lat/lon/tz`——**`declared_accuracy` 和 `time_source` 从未发出**。全前端 grep `declared_accuracy|time_source` 零个发送点。 + +### 根因 2 · 引擎默认值把所有人降到 '5min',且 '5min' 档位缺失(P0/P1) + +`scripts/jyotish_api_server.py` 的 `run_rectification_gate`(约 :2219)与 `_compute_rectification_gate`(约 :8610):收不到字段就默认 `declared_accuracy='minute'`、`time_source='family_clear'` → `get_effective_accuracy` 得 `'5min'`。而: + +- `_PRECISE_BIRTH_TIME_ACCURACY = ('minute', 'rectified')`(:1806)——`'5min'` 不算精确 → **每次咨询**都执行 `add_domain_layer('birth_time uncertain boundary', 'not_auto_rectified', ...)`(约 :1857),边界文案"Birth-time uncertainty requires candidate windows and event evidence"进入模型上下文; +- `birth_time_rectifier.ACCURACY_MATRIX` **没有 `'5min'` 键**,`get_enabled_vargas('5min')` 落到 `'unknown'` 档 → 只开 `D1` + `D9(warn)`,headline 变成"可读主盘,但高敏分盘需要降级"(已实测复现:`get_effective_accuracy('minute','family_clear')=='5min'`,enabled 只有 `{'D1': 'enabled', 'D9': 'enabled_with_warning'}`;对照 `('minute',''hospital')=='minute'` 时 D1/D9/D10/D5/D4 全开)。 + +### 根因 3 · mastra workflow 把 not_auto_rectified 硬编码进每次咨询(P0) + +`mastra/consultation-workflow.ts:695`:`rectification: { boundary: "not_auto_rectified", ... }` ——**无条件硬编码**,不看模式、不看引擎 gate 的实际输出。`mastra/index.ts:55` 的系统指令又要求 "Treat rectification.boundary=not_auto_rectified as final"。两者叠加,模型每轮都被指示"该出生时间未校正"。 + +### 影响面 + +- 自报医院精确时间的用户:被当作未校正、按范围交互——本任务书的直接投诉。 +- **已付费完成校正并采用时间的 `verified_chart` 用户:同样被告知未校正**——`'rectified'` 这个精度值前端从未发出过,付费成果在咨询里零体现。这是对校正商业价值的直接损害。 +- `UNVERIFIED_BIRTH_TIME_NOTICE`("使用未校正填报时间;分钟敏感结论的置信度已降低")对 hospital 级来源同样一刀切。 + +## 1. 硬红线 + +1. **真实性不放宽。** `'rectified'` 精度只允许在 `birth_time_status ∈ {accepted, confirmed}` 且 `active_birth_time` 存在时声明;不得让未校正用户获得 rectified 待遇。医院记录按 `('minute','hospital')` 处理是 skill 方法论本来的定义(`birth_time_rectifier.ACCURACY_RULES`),不属于放宽。 +2. **输出守卫机制保留。** `createBirthTimeModeOutputGuard` / `guardPreciseTimingOutput` 的机制不动;改变的只是喂给它和模型的**精度事实**。`declared_birth_window` / `general_no_birth_time` 两档行为完全不变。 +3. **不动生时校正产品本身**(rectification-agentic、引擎 v5 打分、Round A/2/tails 全部成果)。 +4. 分级降级仍要诚实:`family_exact → '5min'`、`approximate → '15min'` 的降级保留,只是**表达**从"未校正按范围"改为"按你填报的 HH:MM 排盘,分钟敏感结论标注精度"。 +5. `cd frontend && ./node_modules/.bin/tsc --noEmit` 通过(不要用 `npx tsc`);前端 `consultation-*`/`consult-*`/`chat-*` 及 `rectification-*` 测试 fail=0;Python 用 `.venv`,`tests/test_rectification_*.py` 与受影响模块全绿。 +6. 不改 `.gitea/workflows/**`;无凭据不得声称已在真实环境验证。 + +## 2. 开工前置 + +```bash +git fetch origin --prune +git worktree add -b codex/consultation-birth-accuracy-20260901 \ + ../.worktrees/consultation-birth-accuracy-20260901 origin/staging +``` + +读 `frontend/AGENTS.md`、`docs/BUG_HISTORY.md`。**行号只是线索,按符号名定位。** + +## 任务 0(门控)· 先写不变量,先让它红 + +前端(新文件 `frontend/tests/consultation-birth-accuracy.test.ts` 或并入 `consultation-birth-time-mode.test.ts`)+ Python 各写: + +1. **verified_chart 用户**:workflow 请求携带 `declared_accuracy='rectified'`;模型消费上下文的 `rectification.boundary` **不是** `not_auto_rectified`;不含"按时间范围交互"类指令。 +2. **hospital_record 未校正用户**:请求携带 `('minute','hospital')`;引擎 gate `effective_accuracy='minute'`、无 `birth_time_uncertainty_boundary` 域边界、D9/D10 enabled。 +3. **family_exact / approximate**:分别得 `'5min'` / `'15min'`,保留降级但边界文案为"精度标注"式,不是"需要候选窗/按范围交互"式。 +4. **Python 档位**:`get_enabled_vargas('5min')` 与 `get_enabled_vargas('15min')` 不再落到 `'unknown'`(见任务 C)。 +5. **守卫回归**:`declared_birth_window` / `general_no_birth_time` 行为逐字节不变(现有 `consultation-birth-time-mode.test.ts` 9 条必须保持全绿)。 + +跑一遍确认 1-4 是红的。 + +## 任务 A(P0)· 前端把出生时间真值映射进引擎请求 + +`consultation-route-service.ts` 的 `serverChartFromProfile` / workflow 请求构造处,按 profile 真值附加两个字段: + +| profile 状态 | declared_accuracy | time_source | +|---|---|---| +| `accepted` / `confirmed`(verified_chart) | `rectified` | (引擎忽略或传 `rectified`) | +| `hospital_record` | `minute` | `hospital` | +| `family_exact` | `minute` | `family_clear` | +| `approximate` | `15min` | (任意) | + +映射函数单独导出、可单测;`declared_birth_window` / `general_no_birth_time` 不走 chart workflow,不受影响。字段随 `runConsultationWorkflow` 的 body 传至 Python(`run_rectification_gate` 已按 `body.get('declared_accuracy')` 读取,服务端无需改协议)。 + +## 任务 B(P0)· mastra 停止硬编码 not_auto_rectified + +`mastra/consultation-workflow.ts`(约 :695):`rectification.boundary` 改为**透传引擎 gate 的真实结论**——`effective_accuracy ∈ {'minute','rectified'}` 且无 lagna 边界敏感时给非限制值(如 `precision_ok` / 直接省略 boundary),否则才是 `not_auto_rectified`。`mastra/index.ts:55` 的指令改为条件化表述("当 boundary=not_auto_rectified 时……"语义已是条件式,确认无别处无条件复述)。同步检查 `consultation-tools.ts:500` 附近对该字段的消费逻辑。 + +## 任务 C(P1)· 引擎补 '5min'/'15min' 档位与分级文案 + +`scripts/birth_time_rectifier.py`: + +- `ACCURACY_MATRIX` 补 `'5min'` 键(建议:D1/D9/D10 enabled,D5/D4 warn,D7 warn,D30/D60 disabled——比 `'minute'` 保守一档、比 `'15min'` 宽一档,D10 必须可用否则事业咨询全瘸)。`'15min'` 键已存在,确认其被正确命中而非落 `'unknown'`。 +- `_compute_rectification_gate` 的 headline/next_action 分三级:precise(无警示)/ 轻降级("按填报时间排盘,分钟敏感结论已标注精度")/ 重降级(现文案)。"建议先做事件反验"只出现在 lagna 边界敏感或 `approximate` 以下。 + +## 任务 D(P1)· 面向用户的文案分级 + +- `UNVERIFIED_BIRTH_TIME_NOTICE` 按来源分级:`hospital_record` →"使用你填报的出生时间(医院记录)排盘";`family_exact` → 现文案弱化版;`approximate` → 现文案。消费点同步。 +- 检索模型指令与提示词中所有"按时间范围来交互 / 未校正"类表述(`generalNoMinuteInstruction` / `declaredWindowInstruction` 之外的),确认只在真实低精度档触发。 + +## 验收标准 + +1. 任务 0 五组不变量全绿;改动前 1-4 为红的证据贴 PR。 +2. `tsc --noEmit` exit 0;`npx tsx --test tests/consult*.test.ts tests/consultation-*.test.ts tests/chat-*.test.ts tests/rectification-*.test.ts` 全部 fail=0,逐套贴数字。 +3. Python:`.venv` 下受影响模块 + `tests/test_rectification_*.py` 全绿;`_expand_pytest_targets` 钉子保持。 +4. **前后对照贴 PR**:hospital_record 用户与 verified_chart 用户的 workflow 消费上下文(rectification 块 + answer_policy)改动前后各一份;确认 `not_auto_rectified` 只剩低精度档出现。 +5. 说明 family_exact 保留 '5min' 降级的理由(方法论:家人记忆非医院记录)已写进 PR,供产品复核是否接受。 +6. 未在真实环境验证的部分如实注明;建议与 PR #47 的九步点测单合并执行一次真实环境复核(补一条:hospital 精确时间用户咨询不再被告知"未校正按范围")。