Commit e44f5d9a by luoqi

docs: 对齐 onboarding/canonical-fact-layer/potential-recall 到代码

审计发现 3 文档跟 sync 重构 + scenario 扩展(K00-K09)漂移,修正:

onboarding-runbook.md(运维命令,最高优先):
- Phase 4 + 能力表 + 完成标准 + 落点图:pnpm cold-import → pnpm sync
  (--no-recompute + 手动 persona/plan;PAC_COHORT_CONCURRENCY 并行提示)
- 4 子场景 → 10 子场景(K00-K09)
- 加统一 sync 入口说明(cold-import = legacy 全量 alias)

canonical-fact-layer.md:
- §六/§九 "3 子规则(K08/K02/K05)" → 10 子规则 K00-K09 + tooth-overlap union-find
- 实现状态表 DW 直连增量 🟡框架预留 →  统一 pnpm sync(cohort/并发/bulk/run_start cursor)

potential-treatment-recall.md(W2 末设计稿):
- 顶部加 banner:已被 potential-treatment-recall-flow.md(实现版)取代
- 列关键差异(4→10 子场景 / 去时间上界 / tooth-overlap union-find / cold-import→sync)
- 标"勿照本文实施",保留作设计思路存档

design-v2.md 审计无漂移(讲的是另一个 scenario post_treatment_recall,不动)
parent 418d47d9
...@@ -692,7 +692,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased ...@@ -692,7 +692,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased
| PAC 字典集中度 | canonical-codes.ts 单一源 | ✅ K00-K14 + 业务码 + 推荐码 + 11 治疗 category(含 review)| | PAC 字典集中度 | canonical-codes.ts 单一源 | ✅ K00-K14 + 业务码 + 推荐码 + 11 治疗 category(含 review)|
| fact_type 设计 | 拆出 diagnosis/treatment/recommendation 独立 | ✅ encounter 退化只元数据,3 个独立 fact_type 落地 | | fact_type 设计 | 拆出 diagnosis/treatment/recommendation 独立 | ✅ encounter 退化只元数据,3 个独立 fact_type 落地 |
| recommendation_record | 新增 | ✅ Layer A.5 route_by_pattern 抽"建议X" → 55 行入库 | | recommendation_record | 新增 | ✅ Layer A.5 route_by_pattern 抽"建议X" → 55 行入库 |
| 算法读 fact | scenario JOIN 独立 fact_type | ✅ 3 子规则跑通(K08/K02/K05) | | 算法读 fact | scenario JOIN 独立 fact_type | ✅ 10 子规则 K00–K09 全覆盖 + tooth-overlap union-find 合并 |
| 多品牌 / 多租户隔离 | yaml `brand_to_tenant` 映射 | ✅ 瑞尔 → tenant=ruier;瑞泰另一份 manifest | | 多品牌 / 多租户隔离 | yaml `brand_to_tenant` 映射 | ✅ 瑞尔 → tenant=ruier;瑞泰另一份 manifest |
| CI 漂移防护 | schema + yaml + canonical-codes 联立测试 | ✅ 53 测试通过(transforms 49 + canonical-fact 19 - 重叠) | | CI 漂移防护 | schema + yaml + canonical-codes 联立测试 | ✅ 53 测试通过(transforms 49 + canonical-fact 19 - 重叠) |
| **Layer A.5 transforms**(W3 新加)| — | ✅ 6 operator 白名单 + 49 单元测试 | | **Layer A.5 transforms**(W3 新加)| — | ✅ 6 operator 白名单 + 49 单元测试 |
...@@ -857,7 +857,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased ...@@ -857,7 +857,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased
| 2 | fact-content-schemas(17 fact_type × zod 强校验)| 17 schema | | 2 | fact-content-schemas(17 fact_type × zod 强校验)| 17 schema |
| 3 | FactWriter 唯一入口 + canonical_payload 双留底 | 1 入口 | | 3 | FactWriter 唯一入口 + canonical_payload 双留底 | 1 入口 |
| 4 | fact 拆分:encounter / diagnosis / treatment / recommendation 独立 | 4 新 fact_type | | 4 | fact 拆分:encounter / diagnosis / treatment / recommendation 独立 | 4 新 fact_type |
| 5 | scenario 3 子规则跑通独立 fact_type(K08 / K02 / K05)| 3 子规则 | | 5 | scenario 10 子规则 K00–K09 跑通独立 fact_type | 10 子规则 |
| 6 | brand → tenant_id 多品牌隔离(瑞尔/瑞泰)| 2 brand 实测 | | 6 | brand → tenant_id 多品牌隔离(瑞尔/瑞泰)| 2 brand 实测 |
| 7 | **Layer A.5 transforms 6 operator + 49 单元测试** | 6 operator | | 7 | **Layer A.5 transforms 6 operator + 49 单元测试** | 6 operator |
| 8 | **enum_mapping `_default` 兜底键** | AssemblerEngine 改 | | 8 | **enum_mapping `_default` 兜底键** | AssemblerEngine 改 |
...@@ -871,7 +871,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased ...@@ -871,7 +871,7 @@ WHERE p.active AND NOT pp.do_not_contact AND NOT pp.deceased
| 入口 | 状态 | 验证方式 | | 入口 | 状态 | 验证方式 |
|---|---|---| |---|---|---|
| Cold-import(SQL/CSV)| ✅ jvs-dw 跑通 0 failed | 真 DW + 1000 cohort | | Cold-import(SQL/CSV)| ✅ jvs-dw 跑通 0 failed | 真 DW + 1000 cohort |
| DW 直连增量 | 🟡 框架预留 | cold-import + cursor | | DW 直连增量 | ✅ 统一 pnpm sync(存量增量同一套 importDirectory)| cohort 分批 + 并发 + bulk write + run_start cursor |
| Mock Pull | ✅ 跑通 + 自带 emitsPerResource | adapter | | Mock Pull | ✅ 跑通 + 自带 emitsPerResource | adapter |
| Real HTTP Pull | 🔵 W5+ | 接 5i5ya 时实装 | | Real HTTP Pull | 🔵 W5+ | 接 5i5ya 时实装 |
| Push 形态 A | ✅ event 自带 emits | dispatchTables resolver | | Push 形态 A | ✅ event 自带 emits | dispatchTables resolver |
......
# 潜在治疗召回 — 算法设计 + 数据摄入 # 潜在治疗召回 — 算法设计 + 数据摄入
> ⚠️ **本文是 W2 末设计稿(历史)**。已实现版本 + 真理源见
> [`potential-treatment-recall-flow.md`](./potential-treatment-recall-flow.md)。
> 关键差异(以 flow 文档为准):
> - 子场景 **4 → 10**(K00–K09 全覆盖),非本文的 A/B/C/D 4 个
> - 入池**只设时间下界 cooldown,不设上界**(超黄金窗仍入池,交 scorer 衰减)
> - 合并逻辑:本文的"per-patient 单 hit" → 实际是 **tooth-overlap union-find**
> (sub_key = `<sub>@<tooth|whole>`,同患者多牙位 = 多 reason)
> - 数据摄入:本文的 cold-import → 统一 `pnpm sync`(cohort 分批 + 并发 + bulk write)
>
> 本文保留作设计思路存档,**具体参数 / SQL / 命令请勿照本文实施**。
> **版本**:v1.1 · W2 末 > **版本**:v1.1 · W2 末
> **作者**:luoqi > **作者**:luoqi
> **状态**:🎨 设计稿,待 review 后实施 > **状态**:🎨 设计稿(历史)— 已被 potential-treatment-recall-flow.md 取代
> >
> **本文定位**:**特定召回算法**("潜在治疗新链发现")的设计。通用 fact 规范化基础设施见姊妹文档 [`canonical-fact-layer.md`](./canonical-fact-layer.md)——本文不重复讲 fact 怎么变 canonical,聚焦"基于 canonical fact,潜在治疗召回怎么算"。 > **本文定位**:**特定召回算法**("潜在治疗新链发现")的设计。通用 fact 规范化基础设施见姊妹文档 [`canonical-fact-layer.md`](./canonical-fact-layer.md)——本文不重复讲 fact 怎么变 canonical,聚焦"基于 canonical fact,潜在治疗召回怎么算"。
> >
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
| **Platform 管理(create/rotate/deactivate)** | ✅ | `pnpm pac:platform <cmd>` CLI(走 SSH,不走 HTTP)| | **Platform 管理(create/rotate/deactivate)** | ✅ | `pnpm pac:platform <cmd>` CLI(走 SSH,不走 HTTP)|
| **Admin 只读 + 受限更新** | ✅ | `GET / PATCH /pac/v1/admin/platforms/*`(actionUrls/pullConfig/active)| | **Admin 只读 + 受限更新** | ✅ | `GET / PATCH /pac/v1/admin/platforms/*`(actionUrls/pullConfig/active)|
| **AssemblerEngine — yaml 驱动 host→canonical 投影** | ✅ | `data/<host>/assemblers/*.yaml` + `AssemblerEngine` | | **AssemblerEngine — yaml 驱动 host→canonical 投影** | ✅ | `data/<host>/assemblers/*.yaml` + `AssemblerEngine` |
| **Cold-import 装载脱敏数据** | ✅ | `pnpm cold-import -- --dir=...` | | **Sync 装载脱敏数据(首跑全量/日级增量统一)** | ✅ | `pnpm sync -- --dir=...`(cold-import = legacy 全量 alias)|
| **Patient timeline 可查** | ✅ | `GET /pac/v1/patients/:id/timeline` + `pnpm timeline -- --pid=...` | | **Patient timeline 可查** | ✅ | `GET /pac/v1/patients/:id/timeline` + `pnpm timeline -- --pid=...` |
| **Drift 单条识别** | ✅ | `ContractDriftError` + `[drift]` 日志前缀 | | **Drift 单条识别** | ✅ | `ContractDriftError` + `[drift]` 日志前缀 |
| **数据建模能力** | ✅ | 18 action / 13 subjectType / 19 fact_type / 18 personaFeatureKey / 12 outcome(含产品收集 — 见 host-data-request-checklist)| | **数据建模能力** | ✅ | 18 action / 13 subjectType / 19 fact_type / 18 personaFeatureKey / 12 outcome(含产品收集 — 见 host-data-request-checklist)|
...@@ -175,21 +175,27 @@ assemblers: ...@@ -175,21 +175,27 @@ assemblers:
```bash ```bash
cd apps/pac-service cd apps/pac-service
# 先 dry-run 看翻译效果 # 先 dry-run 看翻译效果(不写库)
pnpm cold-import -- --dir=./data/friday --dry-run pnpm sync -- --dir=./data/friday --dry-run
# 真写库 # 真写库(首跑 cursor 空 = 全量;cohort 分批 + 可并行,内存恒定)
pnpm cold-import -- --dir=./data/friday pnpm sync -- --dir=./data/friday --no-recompute
# 榨资源:PAC_COHORT_CONCURRENCY=3 pnpm sync -- --dir=./data/friday --no-recompute
# 首跑完手动补 persona/plan:
pnpm recompute-persona -- --host=friday && pnpm recompute-plans -- --host=friday
# 验证 timeline # 验证 timeline
pnpm timeline -- --platform=friday --tenant=t_friday_grp1 --pid=<host pid> pnpm timeline -- --platform=friday --tenant=t_friday_grp1 --pid=<host pid>
# 跑 selector 自检(看召回算法能不能在该 host 数据上识别 4 子场景) # 跑 selector 自检(看召回算法能不能在该 host 数据上识别 10 子场景 K00-K09)
pnpm verify-scenarios # 注意:这个验证脚本基于 demo 数据;真实 host 数据需重写期望集 pnpm verify-scenarios # 注意:这个验证脚本基于 demo 数据;真实 host 数据需重写期望集
``` ```
**冷启数据装载是 PAC 三大数据通道之一**(跟 pull/push 共用 AssemblerEngine + 下游 pipeline)。 > **统一 sync 入口**:`pnpm sync` 首跑(cursor 空)= 全量,之后 cron 增量,同一套 `importDirectory`。
host 持续导出 + PAC 持续 cold-import 即可作为产线长期数据通道,不一定要切到 pull/push。 > `pnpm cold-import` 保留为 legacy「忽略 cursor 强制全量」alias。
**数据装载是 PAC 三大数据通道之一**(跟 pull/push 共用 AssemblerEngine + 下游 pipeline)。
host 持续导出 + PAC 日级 `pnpm sync` 增量(cron 自动)即可作为产线长期数据通道。
### Phase 5:开 pull 周期 / push 实时(W5+ 才能做) ### Phase 5:开 pull 周期 / push 实时(W5+ 才能做)
...@@ -232,11 +238,11 @@ pnpm pac:platform deactivate friday ...@@ -232,11 +238,11 @@ pnpm pac:platform deactivate friday
- [ ] `POST /admin/platforms` 拿到新 host 的 id / appId / appSecret - [ ] `POST /admin/platforms` 拿到新 host 的 id / appId / appSecret
- [ ] Host 用 appSecret 换出 JWT(code=0,有 accessToken) - [ ] Host 用 appSecret 换出 JWT(code=0,有 accessToken)
- [ ] `pnpm cold-import -- --dir=<host 导出目录>` 全成功(failed=0,facts > 0) - [ ] `pnpm sync -- --dir=<host 导出目录> --no-recompute` 全成功(failed=0,facts > 0)
- [ ] `pnpm timeline -- --pid=<host 某 patient>` 能看到完整 actual + planned 列表 - [ ] `pnpm timeline -- --pid=<host 某 patient>` 能看到完整 actual + planned 列表
- [ ] 4 子场景 selector 跑出来命中数 > 0(说明数据形态真的能进召回) - [ ] 10 子场景(K00-K09)selector 跑出来命中数 > 0(说明数据形态真的能进召回)
如果 cold-import`[drift] xxx row rejected: <字段路径>` —— 那是 host 实际字段 / 枚举值跟 PAC 的 assembler.yaml 错位,调整对应 yaml 重跑即可(不需要 host 改任何东西)。 如果 sync`[drift] xxx row rejected: <字段路径>` —— 那是 host 实际字段 / 枚举值跟 PAC 的 assembler.yaml 错位,调整对应 yaml 重跑即可(不需要 host 改任何东西)。
--- ---
...@@ -246,7 +252,7 @@ pnpm pac:platform deactivate friday ...@@ -246,7 +252,7 @@ pnpm pac:platform deactivate friday
host 脱敏导出 → CSV/JSON 文件 + manifest.yaml host 脱敏导出 → CSV/JSON 文件 + manifest.yaml
↓ scp / S3 / 邮件 ↓ scp / S3 / 邮件
data/<host>-<timestamp>/ data/<host>-<timestamp>/
↓ pnpm cold-import -- --dir=... ↓ pnpm sync -- --dir=...
[1] platforms (Admin 早已录入) [1] platforms (Admin 早已录入)
[2] patients (upsert,主档) [2] patients (upsert,主档)
[3] patient_transactions (append-only,raw_payload 留 host 原文) [3] patient_transactions (append-only,raw_payload 留 host 原文)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment