1. 07 Jun, 2026 39 commits
    • feat(web): 画像标签并入身份卡 + hover 合并取值/算法 + 隐藏治疗链卡 · d45b0c86
      详情页左栏调整:
      - 去掉独立'画像标签'卡片,标签云并入左上第一个卡片(IdentityCard)底部(手机号下,带分隔线 + 详情→)。
      - 标签 hover:去掉原生 title(避免 title+hovercard 双浮窗);把 title 内容(该患者实际取值)
        并入 hovercard 顶部(teal 框)→ 一个浮窗同时看'是什么(取值)+ 怎么算的(规则)'。
        PersonaFeatureHover 加 value prop,meta 缺失时也能只显取值。
      - 隐藏'治疗链'卡片(链已弃用;chains 仍传 drawer 备用)+ 删未用 ChainSidebar import。
      - web tsc 通过(next dev 热更,无需重启)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(ai-script): 稳健/标准档也接入画像(精简版:安全 + 定语气) · 02e1e2ed
      原只有深度档喂画像。三档对齐:
      - buildPersonaGuide 加 mode:essential(禁忌/治疗敏感/特别关注 + rfm/生命周期)/ full(再叠切入点)。
      - 稳健 + 标准档 → essential:只灌'护栏'类(安全红线 + 语气),不灌切入点(折扣/转介/家庭),
        避免轻量填空档被诱导加推销、破坏填空纪律。安全类画像(种植禁忌/看牙恐惧)现三档都见得到。
      - 深度档保留 full(default,输出不变 → 无需 bump)。
      - bump promptVersion:standard v13 / stable v26(eval 可按版本对比)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(persona): 回填 evidence.factIds(证据链)— 7 个 fact 派生特征 · 1842fb02
      #4 证据链债:多个特征返回 evidence.factIds=[],详情页'为什么贴这标签'无法溯源到 fact(违反 db 原则4)。
      - 回填 7 个 fact 派生特征,收集驱动该标签的 fact id:
        · discount_anchor → 锚点 payment fact
        · special_attention → 命中的 no_show/迟到 预约 + 不可等候 emr fact(按触发标签收集)
        · time_preference → 窗口内计入的 appointment fact
        · lifecycle_stage → 就诊 + 消费 fact(去重)
        · treatment_sensitivity → 命中关键词的 emr fact(改 per-fact 匹配)
        · potential_treatment / urgency_level → gap 源 fact(PotentialGap 加 factId:selector SQL 加 sig.id)
      - 5 个非 fact 派生特征(age/gender/acquisition/family/referral/contraindication)保留空 + 注释说明
        (证据=patient 主档/副表立柱/关系边,本就非 fact)。
      - 本地 --force 重算 928:fact 派生特征 100% 有证据(discount 338/338、lifecycle 928/928、
        potential 771/771、special 88/88、time 601/601、urgency 771/771);age/gender 仍 0(符合预期)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(ai-script): 话术接入真实画像特征(修死 key)+ 收口 time/special/敏感/禁忌 · a19bbf3e
      #1 话术对画像全暗的 bug:fact-block 旧版只 filter key==='value'||'recall_risk'(早被 rfm 取代删除)
        → 深度档话术拿不到任何 persona 信号。
      - 新增 buildPersonaGuide:从 16 特征里按用途挑 + 排序渲染指引块:
        注意(禁忌/治疗敏感/特别关注)→ 定语气(rfm/生命周期)→ 切入点(治疗史/权益/折扣/转介/家庭/时间偏好)。
      - orchestrator 不再 slice(0,5) 任意截断,全量传给 buildPersonaGuide 按白名单挑。
      
      #3 特征接入(原 recommendedAt/Channel 目标已 moot:前端移除了推荐时间展示、免打扰已被池 SQL 硬排除)
        → 改由更合适的通道收口:time_preference/special_attention 已进①画像标签卡(展示)②话术指引(本次)
        ③ summary(orchestrator 早已全量传)。不再臆造 moot 的 plan 字段。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(web): 详情页画像卡片→患者信息·标签云 + 优先级/标签 hover 全部对齐现状 · 7f459b48
      - priority-hover:旧 6 因子表 → v3 三维(急迫×.4/价值×.3/意愿×.3 → ×新鲜度×置信度);
        意愿 sub(RFM/主诉/信任)入 hint;breakdown 类型同步(plan-detail-types + mock-data PlanReason)。
      - persona-feature-hover ALGORITHMS:原只有 5 个已删 key(value/recall_risk/...) → 重写为现 16 特征
        的'怎么算的'说明(rfm/年龄/性别/获客/家庭/转介/生命周期/治疗史/时间/折扣/特别关注/敏感/禁忌/急迫/潜在治疗/权益)。
      - 详情页'患者画像'卡片(top4 列表)→ '患者信息·标签'卡片:全部特征做成 chip 标签云,
        每个 chip hover 看算法说明(复用 PersonaFeatureHover);列表页优先级 hover 自动随之更新。
      - 修详情页两处死 key 读取:value→rfm(累计消费 hint)、recall_risk→lifecycle(久未到诊提示)。
      - web tsc 通过。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): potential-treatment-recall-flow 算分段对齐 v3 三维模型 · ffc64273
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): potential-treatment-recall §8 + canonical-fact-layer 对齐 v3 优先级 · 10c1319f
      - potential-treatment-recall §8.2/8.3:旧 6 因子 scorer 伪码/张志远例 → v3 三维(急迫×.4+价值×.3+意愿×.3)
        × 新鲜度 × 置信度;例子换 94 分真实 breakdown;UI 明细更新;加 uplift 局限。
      - canonical-fact-layer §7.2bis #11:timeWindowFactor → 新鲜度因子(W7 v3 改名,原理不变)。
      - §9 PR 实施路径保留(W3 历史里程碑记录)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): pac-algorithms-overview 对齐 v3 优先级模型 + 16 标签 · 5a268d52
      - §三 优先级:6 因子加权启发式 → v3.0 三维(急迫×.4+价值×.3+意愿×.3)× 新鲜度 × 置信度;
        意愿=RFM依从+主诉行为+信任基础;真实例子换 94 分;加 uplift 局限说明。
      - §二 画像:加现状指针(16 标签 v3.0;价值/风险并入 RFM、治疗链降级详情页、免打扰并入特别关注 → persona-design-v2)。
      - §六 端到端 + FAQ:6 因素/91分 → 三维 × 因子。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(recall): v3 优先级补置信度因子(影像AI降权)+ 第3加分项(近1年到诊履约) · dc813254
      回应两个缺口:
      1. 置信度(影像价值维度):新 v3 漏了信号置信度 → 影像AI诊断(code_source=image_ai,v3.0标70-90%)
         与医生诊断(std_code/name_map,100%)同权。补:sourceConfidence(医生1.0/影像0.85/建议0.8/未知0.9),
         cluster 取 max(最优来源胜出);confidenceFactor(≥0.9→1.0/≥0.7→0.9/<0.7→0.75)乘进综合分。
         本地:286条影像AI/建议 ×0.9,1169条医生 ×1.0。
      2. 信任加分项 3/3:原只做了同类治疗史+1转介+1,补近1年到诊且履约良好+1
         (lifecycle 末诊<365 且 非 special_attention 屡次爽约)。
      - 综合 = (急迫×0.4+价值×0.3+意愿×0.3) × 新鲜度 × 置信度。SELECT 加 code_source,merge 算 cluster_confidence。
      - 只改分数不改候选集(589 不变)。breakdown 加 confidenceFactor 可解释。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(recall): v3 优先级加诊断新鲜度衰减(老诊断止损)— 修重做时丢的 timeWindowFactor · cd4303ff
      v3 重做时丢了旧 scorer 的过晚衰减:新模型急迫性按【末诊】算、不看诊断年龄,
      导致很多年前的老诊断(末诊也久)反而 urgency=紧急、分还高。而入池无时间上界(W3:缺口不自愈
      仍入池),当初就靠 scorer 衰减止损 → 丢了就没人止损了。
      - 加 computeFreshness(daysSince, windowDays):黄金窗内 1.0;过窗线性衰减到 2×窗=0.4,地板 0.4。
        per-病种(K08窗180/K07窗365 → 慢病衰减慢)。软衰减不硬切(同 W3:止损交 scorer+UI排序+客服自选)。
      - 综合 = (急迫×0.4+价值×0.3+意愿×0.3) × 新鲜度。breakdown 加 freshness/base 可解释。
      - 本地:58% gap 有衰减(池里大量老诊断);老 missing_tooth base8.5→分42,新鲜的照常高分。
      - 注:PAC 无丢单数据(v3.0 靠已丢单止损),新鲜度衰减是 PAC 的止损替代。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(recall): 优先级算法重做为 v3.0 三维模型(急迫×0.4 + 价值×0.3 + 意愿×0.3) · ec55b5be
      替换旧 6 因子加权启发式(只用 value+risk 2 特征)为业务《画像字典 v3.0》优先级模型:
      - 急迫性 = urgency_level(紧急10/高7/中4/低1)
      - 价值性 = 治疗类型+牙数(种植 单颗8/多颗9/半口10 · 正畸7 · 根管6 · 修复5-6 · 牙周5 · 拔3 · 补2)
      - 意愿度 = RFM依从×0.375 + 主诉行为×0.375 + 信任基础×0.25
        · RFM依从 = rfm 八象限分群
        · 主诉行为 = 咨询过该类(consultation 意向命中)→8 / 仅诊断→2(触达活跃维度 PAC 无数据,去掉按比例归一)
        · 信任基础 = lifecycle + 同类治疗史+1 + 转介达人+1
      - scorer 用上 6 个画像特征(rfm/urgency/lifecycle/treatment_history/referral + consultation 意向),旧版只用 2 个。
      - ️ 只改 priorityScore(排序),不改召回候选集(gap 选择独立)→ verify-recall FP/FN 不受影响。
      - 本地 928:候选 589(≈旧588 不变),分数 23-94 avg66.6;价值序 种植78>牙周/根管/正畸73>拔/补67>发育62。
      - 局限(v3.0 同,记 follow-up):权重人拍(可调);意愿≈倾向性非增益(uplift,需试点对照组);
        confidence<0.6 降权 + 触达维度 待数据/二次筛选实现。breakdown 保留可解释。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 16 标签(删 do_not_contact_status)+ lifecycle 咨询 gate + 咨询主体/treatment_intent follow-up · cca5ef37
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • refactor(persona): 删 do_not_contact_status 特征(免打扰并入 special_attention)+ lifecycle 潜客加咨询 gate · 539dbcf2
      - 删 do_not_contact_status extractor(免打扰已在 special_attention/CDP D.2.3;合规闸召回读 profile 原始列,
        不依赖本特征)。enum 标弃用保留(前端 hover/label 无害)。已故/投诉信号后续补(投诉数据本就缺)。
      - lifecycle 潜客(B.1.4):零就诊 → 加 gate「有预约 OR 有咨询」(用上新摄的 consultation_record);
        零就诊且无触点 → 兜底新客(不落 lastDays 分支避免 Infinity 误判流失)。
      - 本地 928:现役 16 特征(dnc 0),lifecycle 成熟614/新客207/成长104/待激活2/流失1
        (样本无零就诊 → 无潜客,全量会出)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • fix(ingest): consult SQL 还原朴素(CSV 双源等价)— concat 移到 transforms.derive · fdbe3424
      - 违反了 manifest 纪律(SQL 只做朴素导出,形态改造归 transforms):consult_external_id 的 concat
        从 SQL 移到 transforms.derive(I 段),SQL 回归 SELECT 列 + WHERE 过滤。
      - organization_id IN(过滤无关诊所)+ cohort patient 过滤 保留(合法 WHERE,导出本就按诊所过滤)。
      - 审计其他表:均朴素(SELECT 列+WHERE);image_finding_rows 是文档记录的已知例外(影像AI pivot,CH 限制)。
      - 重摄验证:5993 facts/0 failed,subject_id 与 concat 版一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(ingest): 摄入咨询主体 consultation_record(fact_consult_out,5 试点意向源) · 956a42d0
      - 定义 PAC 咨询主体:一次咨询/初诊事件;intents=患者意向(potential_cure,主观意愿,非诊断)。
        区别 diagnosis(医生客观)/recommendation(医生建议)— 意向不进召回,喂 treatment_intent。
      - 链路:fact_consult_out 全 144 诊所,过滤到 5 试点(org IN EMR orgs + patient_register_id=patient_id,
        CH 允许 WHERE 引用别名,同 returnvisit);94.4% 命中。无 id → consult_external_id=(patient,appo,date) concat;
        无 updated_date → 不入 per_query,每轮全量按 org+patient 过滤(幂等,同 returnvisit)。
      - canonical-codes:CONSULT_INTENT_TO_CATEGORY(种植→implant…拔功能牙→surgical/早期矫正→ortho/美白→cosmetic)
        + parsePotentialCure(解析 Python list 串)。parser/schema/assembler/manifest 配齐。
      - 本地 928:5993 facts,0 未映射,失败0。意向类别:正畸1006/种植733/预防383/充填350/根管316…
      - ️ map 改后需 truncate consult+重摄才生效(reparse 缺口 task #46);部署是干净全摄,自动生效。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 urgency_level(17 标签)+ 急迫已治疗复查路径 follow-up · 2cdab12b
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 急迫等级特征(C.2.1,v1 仅潜在治疗路径) · efd767e5
      - urgency_level 单标签(取最大):有潜在待转(8 业务标签 gap)且 末诊>90天→紧急/30-90→高/<30→中。
        末诊口径同 lifecycle(encounter/actual treatment/挂号 max)。
      - ️ v1 跳过【已治疗复查路径】(召回未实现复查场景,follow-up)。美学/预防→低(8标签不含,暂不触发)。
      - 抽 classifyGapToLabel/ageYearsAt 为共享函数:potential_treatment 出标签 + urgency_level 判待转 共用(单一源)。
      - 本地 928:紧急384/高223/中164=771,与 potential_treatment 完全一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 potential_treatment(16 标签)+ gap 核心共享架构 + 召回种植年龄门 follow-up · e077c6ce
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 潜在治疗特征(C.1.1,复用召回 gap 核心) · 92f32b19
      - 新建 PotentialTreatmentSelector(clinical-gap):per-patient gap = 召回 gap 核心(共享 buildGapCore)
        去时间门/合规门(常态属性);按 active 诊断/建议码剪枝。
      - potential_treatment 画像特征:gap → 8 业务标签(种植←K08>18 / 补牙←K02 / 根管←K04 /
        牙周←K05,K06 / 正畸←K07>12≤40 / 早矫←K07 3-12 / 修复←K03默认 / 拔牙←K01+K03残根残冠)。多标签。
      - 码分组单一源 GAP_PRIMARY_GROUPS:召回 SUB_SCENARIOS 不再内联 dxCodes/recCodes,改引共享。
      -  召回字节等价再验(Phase2 码分组改后 EXCEPT 双向 0 diff,1450 全等)。
      - 本地 928:771 患者有潜在治疗(拔牙433/补牙341/修复289/种植184/牙周150/正畸105/根管97/早矫24),
        是召回候选超集(无时间门);正畸+早矫129<召回163 因 spec 正畸封顶40岁(符合预期)。
      - 置信度=诊断1.0/建议0.8(复用)。非已丢单 PAC 未摄入,省略(follow-up)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • refactor(recall): 抽 gap 核心到共享模块 clinical-gap(为潜在治疗画像单一源) · 76516429
      - 新建 modules/clinical-gap/potential-treatment-gap.sql.ts:toothArrSql + buildGapCore
        (sig 牙位/resolved/remaining 相减 + ⑤a 判定 + 废用牙/先天剔除 + §E flag),Layer 1 facts 消费层(中立)。
      - §E gap flag 单一真理源 GAP_FLAGS_BY_PRIMARY(召回 SUB_SCENARIOS 不再内联)。
      - 召回 scenario 改 import 共享片段,SQL 逻辑零改动只搬家。
      -  字节等价验证:新旧码召回集合 EXCEPT 双向 0 diff(本地 928,1450 条全等)。
      - 目的:Layer 2 潜在治疗画像将复用同一 gap 核心(避免 Layer 2→3 倒置 + 口径漂移)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(persona): 补 treatment_sensitivity + contraindication(15 标签)+ 审查发现/Layer C · bfaae12d
      - 总览表加 治疗敏感 / 禁忌标签;13→15 标签;多标签 + 稀疏 + 未完整实现(Layer C)说明。
      - 审查发现 +2:treatment_sensitivity 关键词精炼排假阳;contraindication 不上规则版(否定泛滥/
        量化无数据/需临床判断 → 仅种植年龄,余留 Layer C)。
      - follow-up 加 Layer C(LLM)层标签:禁忌完整版/沟通偏好/意向/家庭社交。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 禁忌标签特征(D.2.4 v1,仅种植年龄禁忌) · 4f5df5c8
      - contraindication 多标签结构(种植/正畸/麻醉/手术禁忌)。v1 仅实现【种植年龄禁忌:年龄≤18,
        骨骼发育未完全】,validUntil=满19岁自动解除(到期重算即不打)。
      - ️ 其余禁忌不上规则版(会医疗事故):既往史'过敏'97%是'否认过敏'(否定泛滥)、HbA1c全量仅10条
        (量化条件无数据)、需控制状态/急性期判断 → 留 Layer C(LLM 抽取)。data 结构已留多标签位。
      - 本地 928:种植禁忌 167,validUntil 正确,交叉验证 age_bracket≤18 一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 治疗敏感特征(D.2.2,病历关键词精炼) · ff44c92b
      - treatment_sensitivity 多标签:看牙恐惧/晕针/晕血/密闭恐惧。扫 emr_record.content
        (past_history 既往史/illness_desc/pre_illness/disposal 等)+ profile.notes/tags。
      -  关键词按全量数据排查精炼:看牙恐惧 恐惧/害怕看牙/牙科焦虑/看牙紧张(裸紧张=颏肌紧张);
        晕针;晕血/见血不适(裸见血=可见血凝块);幽闭/密闭/长时间张口不适(裸张口受限=物理受限)。
      - 宿主未给 health_profile;EMR 既往史已承载(晕针病史等)。无迁移/无重摄。
      - 本地 928:看牙恐惧 2(全量约 177 患者 0.13%,低覆盖高价值安全标记)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): 重写画像层 v2 为实现版(13 标签 + 架构 + 算法审查发现) · 5c7e097b
      旧版停在早期设计(RFM几何八象限/简化lifecycle/圈人群),跟最终实现对不上。
      重写为:画像层定位 + persona schema + 4 数据范式 + 时间语义 + Feature Registry(指向
      persona-feature-specs.ts 单一源)+ 13 标签总览表 + 算法审查发现/口径决策(RFM图口径/
      lifecycle顺序bug/discount金额非比率/TZ/no_show不在active等)+ follow-up + 部署清单。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 特别关注特征(D.2.3,屡次爽约/经常迟到/免打扰/不可等候) · 90ca7f57
      - special_attention 多标签:屡次爽约(近1年履约率<50%且决定≥3)/经常迟到(到店>预约+15min≥50%
        且≥3)/免打扰(doNotContact)/不可等候(notes/tags/病历关键词)。
      - ️ 挖出 2 个真问题并处理:
        ① no_show/cancelled 预约 patient_facts.status≠active/fulfilled,不在 persona ctx → 新增
           ctx.appointmentsAll(单独加载全状态预约,排 superseded)给爽约/迟到用。
        ② arrived_at(in_time)摄入 TZ bug:比 planned_for 一致早 8h → +8h 补偿(根治修摄入,follow-up)。
      - 本地 928:经常迟到86/屡次爽约2(免打扰/不可等候样本无数据,全量会出)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 折扣锚点特征(D.1.3)+ 修 spec naive 点 · 669f4fa6
      - DW 无 original_amount;discount_*_rate 实为折扣金额(应收3-dept0.45=实收2.55)→
        折扣率=1-Σ折扣/应收。摄入 payment_record.content.discount_cents + settlement_project(重摄)。
      - discount_anchor:取真实治疗最深【部分】折扣 + 日期/项目。
      - ️ 修 spec 两个 naive 点:① 免费洁牙/检查促销(100%off=0折)会霸占锚点→只看原价≥¥500+ratio>0;
        ② 保险方付款非诊所折扣→排除 discount_insurance_rate(只算科室/公司/卡)。
      - 本地 338 患者:avg 6.4折,样例合理(种植9.4折/修复/促销2.9-5.1折)。
      - ️ 留口径:近免费(>90%off)comp/全保 是否算锚点;¥500 阈值可调。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 时间偏好特征(D.1.2) · 5210f3ea
      - time_preference 多标签:工作日/周末/上午/下午/晚间偏好。近2年预约,北京 TZ。
      -  数据排查:occurred_at 钟点被搞乱(时区/解析,16-23 乱分布),planned_for +8 才是
        干净营业钟形(8-18 峰 9-15)→ 用 planned_for。无新摄入/无迁移。
      - 阈值照 spec:工作日≥60%/周末≥50%/各时段≥50%,记录≥2;无命中→不打标签。
      - 注册表 spec/enum/label。本地 601 患者:下午368/工作日305/上午289/周末289。
      - ️ follow-up:appointment occurred_at 钟点不可靠(latent bug);TZ 硬编北京,多宿主应读 host TZ。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 治疗史特征(C.3.1,读 canonical category) · 35170610
      - treatment_history 多标签:implant→种植史/orthodontic→正畸史/prosthodontic·cosmetic→修复史/
        periodontic→牙周治疗史。不标记 surgical/restorative/endodontic 等基础治疗。
      -  spec 用病历/结算关键词匹配(数仓原始文本);PAC canonical 层已归一成 category →
        直接读,无关键词/无新摄入/无迁移。canonical 层价值体现。
      - 注册表 spec/enum/label。本地 773 患者:牙周596/修复195/正畸165/种植120,多标签生效。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 生命周期特征(B.1.4)+ 修 spec 顺序 bug · 57838efe
      - lifecycle_stage 7 阶段(潜客/新客/成长/成熟/待激活/沉睡/流失)。PAC 自算:
        net_receipts_total/total_visit_times 源表(fact_client_out)没有 → 从 payment/就诊 fact 算
        (同 RFM 口径);潜客用 appointment fact。无新摄入/无迁移。
      - ️ 修正 spec 顺序 bug:原'沉睡>540'在'流失>730'前→流失永不触发;流失提前到沉睡前。
        本地验证:末诊1913天/23次患者正确判流失(原顺序会误判沉睡)。
      - 移除 rfm.data 里的简化 lifecycle(避免与本特征重复;生命周期归本特征单一来源)。
      - 注册表 spec/enum/label。本地:成熟614/新客207/成长104/待激活2/流失1。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 权益身份升级为 B.1.3 五类多标签(卡券关键词) · 6689b612
      - 摄入 fact_settlement_mode_out.card_type_name/card_name → payment_record.content
        (payment.parser + schema + payment.yaml;String() 强转防数字型卡券值炸 .trim)。
      - entitlement_status 重写:遍历结算卡券/保司/channel 关键词多标签(允许并列):
        高端保险直付/银行私行权益/储值会员/儿牙会员/医保客户;未命中→不打标签。
      - 注册表 spec(v2)。本地 928:医保225/银行私行175/儿牙35/储值27/高端保险17(274 患者有权益)。
      - 无 schema migration(卡券名落 content JSONB);需重摄 payment facts。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 转介绍达人特征(B.1.2 v1,DW 预聚合) · 37527216
      - 摄入 fact_client_out.recommend_num/recommend_amount → 副表立柱 referral_count/referral_amount_cents
        (元→分;canonical schema + patient.yaml + cold-import/dispatcher 两路 upsert + migration)。
      - referral_champion 特征:门槛 recommend_num≥3 且转化额>0;直系家庭≥3→家庭型,否则社交型;不满足→不打标签。
      - 本地 928:26 社交型(家庭型需≥3直系家庭,样本稀疏未出)。
      - ️ v1 用 DW 预聚合替代逐个被推荐人'均有效转化'(跨患者+需被推荐人已摄入,留 v2)。
      - ️ recommend_num 增量刷新滞后(fact_client_out 游标=last_visit_time,无 updated_date):
        推荐人下次到诊/全量重摄才更新;真修=游标改 greatest(last_visit_time,recommend_last_visit_time)(follow-up)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 家庭构成特征(A.3.1,PatientRelation 反推) · a99b88d7
      - 复用 PAC PatientRelation 边表(无迁移/无重摄):relations 注入 persona ctx,
        family_structure extractor 反推直系亲属结构。
      - 直系 spouse/child·grandchild/father·mother·grandparent;sibling/friend/other 噪音不计。
        有长辈→多代/有子女→多口/有配偶→两口/有非直系边→单身/无边→不打标签(数据缺不臆断)。
      - 注册表 spec/enum/label。本地 85/928:多口28/多代21/两口19/单身17。
        ️ 覆盖依赖关系边,样本稀疏+非全量偏低;多代采广义(有长辈即跨代)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 获客渠道特征(A.2.1)+ 副表立柱摄入 · 28783dde
      - DW fact_client_out.primary_category/sub_category(L2 初诊来源)→ 摄入 PAC 副表
        patient_profiles.acquisition_channel(PAC立柱标准枚举)/acquisition_sub(host原值)。
      - 链路:canonical-codes.PACAcquisitionChannels(单一收口)+ patient.yaml enum_mapping(走入→walk_in 等)
        + PatientCanonicalSchema + 两条 upsert 路径(cold-import 全量 / dispatcher 增量)+ migration(2列+索引)。
      - acquisition_channel persona 特征(snapshot,读副表立柱出标签)+ 注册表 spec/enum/label。
      - 本地重摄 928:口碑331/走入162/集团营销158/地区营销157/电商65/集团销售53/自媒体2,特征100%覆盖。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 性别特征(A.1.2,snapshot) · 48c60a48
      gender extractor:patient.gender 映射 M/男→男性、F/女→女性、其他→未知(照图,三值)。
      注册表 spec + enum/label;来源可切(现 patient.gender,宿主给 client_gender 后切)。
      本地 928:女 468 / 男 460,画像现役 5 特征(rfm/age_bracket/gender/dnc/entitlement)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 年龄段特征(A.1.1,snapshot) · 19793597
      - age_bracket extractor:从 patient.birthDate 算周岁 → 9 档(婴幼儿..老年),照图区间;
        3/55 重叠按'下界含、归下一档'+ ≥55→老年消歧。snapshot 时间语义(历史读版本流)。
      - 注册表 spec(标签卡)+ enum/label;数据来源可切(现 birthDate 自算,宿主给 client_age 后切)。
      - birthDate 缺失/年龄越界(<0/>120 脏数据)→ 不打标签。
      - 本地 928 验证:分布合理、边界消歧正确、覆盖率 100%。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): 翻转 scorer 读 rfm.data + 摘除旧 value/recall_risk/treatment_chain · 1b086e7b
      - treatment-initiation-recall.fetchPersonaContext:改读 rfm.data(valueTier/riskScore),
        rfm 缺失优雅回退旧字段。本地 1437/1437 plan 打分零变化 → 翻转行为等价。
      - FeatureRegistry/persona.module:摘除 value/recall_risk/treatment_chain_status 三个 extractor
        (treatment_chain 降级为详情页 episode 视图,后续单做);现役 rfm/dnc/entitlement。
      - rfm 决策树补全:develop 档 F=2 扩 F<3(近期单次新客→一般发展,不误落低活跃)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): RFM 采用业务 CDP 口径(图 B.1.1)+ M 分位 + 注册表 spec · 3a3abed7
      - rfm.feature 改为业务整理好的 RFM 定义:R/F 分段照图、M 按租户分位(p20/40/60/80)、
        8 段决策树(重要价值..低活跃)。R/F/M = last_visit_time/visit_times/net_receipts_total(lifetime)。
      - M 分位需群体计算:PersonaService 算+缓存租户分位阈值(30min TTL),注入 ctx.populationStats;
        缺失降级绝对¥档。valueTier(绝对¥)/riskScore 保留 → 仍 100% 兼容旧 value/recall_risk。
      - 新增 persona-feature-specs.ts:标签注册表(标签值/数据来源/数据字段/释义/算法/时间语义),
        代码存、来源可切(现 PAC 自算,宿主 CDP 报表给出后切宿主值)。score 列弃用语义。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • feat(persona): RFM 八象限特征(统计层,additive)+ recompute --force · 6fafb50e
      - 新增 rfm feature(融合 R最近/F频次/M金额三时间语义):data 带 segment 八象限 +
        lifecycle 生命周期 + valueTier(0-4)/riskScore(0-3)。统一旧 value+recall_risk。
      - additive 接入(暂不动 scorer/旧特征);本地 928 验证:分布临床合理,且
        valueTier/riskScore 100% 复现旧 value.score/recall_risk.score → 后续翻转零风险。
      - recompute-persona 加 --force:算法/特征变更后(数据没变)跳过水位幂等闸强制重算
        (部署到服务器也需要,否则全 noop)。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
    • docs(algorithm): 画像层设计 v2 — 时间语义/Feature Registry/RFM 八象限/圈人群 · 0edbe90d
      画像从'被动打分+话术标签'升级为有治理、有时间语义、可圈人群的特征体系:
      - 时间语义模型(snapshot/window/lifetime/trend,每特征声明;画像=压缩当前态,历史留fact层,版本流=point-in-time)
      - Feature Registry 单一收口(OneModel)+ CI 防漂移
      - persona_features 加 typed value JSONB(支持 SQL 圈人群)
      - RFM 八象限(统一现有 value+risk:M→value,R+F→lifecycle/risk)+ 召回语义映射
      - lifecycle_stage 生命周期分层;treatment_chain 移出画像→episode 视图
      - Campaign 圈人群→批量召回;质量监控;分 5 PR 实施
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed
  2. 06 Jun, 2026 1 commit
    • fix(plan-detail): 深度/标准话术刷新丢失 + 主治医生误显影像AI · 4c3f9c58
      - parseScriptMarkdownToSections 改通用 H2 解析:原只认稳健 4 固定标题(开场白/...),
        深度/标准的自由标题反 parse 全落空 → plan_scripts 存了内容但刷新显示'尚未生成'。
        现按任意 ## 标题切段(稳健固定标题映射已知 id,标准/深度自由标题用 s{n}+原标题),三档通用。
      - KeyFactsCard 主治医生:影像AI(image_ai 触发诊断)不当主治医生显示,退回真实最高频医生
        (排除影像AI/image_ai),真无人类医生 → '—'。话术侧 extractPrimaryDoctor 本就排除,一致。
      
      Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
      luoqi committed