- 11 Jun, 2026 2 commits
-
-
1. 画像标签筛选扩到全部枚举型标签(4→14 维):新增 年龄段9/性别2/家庭构成4/权益身份5/ 治疗史4/治疗敏感4/特殊关注4/时间偏好5/转介绍达人2/禁忌1;code
↔ 中文逐一对齐各 extractor 的 data 写入值。未收录并注明原因:获客渠道(取值随宿主非稳定枚举)、折扣锚点(数值型)。 后端零改动(字典驱动,plan.service 通吃)。 2. 左栏行 hover 出「认领」(仅 待认领+未分配)→ assign 给自己 → patchItem 原地改"进行中" (不重拉、不丢滚动/筛选)+ toast;usePatientPicker 加 patchItem。 3. 切患者白闪修复 — 保留 URL 路由(可刷新/分享/回退),loader 加 stale-while-loading: 模块级缓存上一份聚合,加载期旧详情垫底(60% 透明)+ "切换中…"小罩,新数据到无缝替换。 web tsc 0 + Next 生产构建过。仅本地,未部署。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
按反馈: 1. 上下大布局:详情 TopBar 经 createPortal 渲到 plans/layout 顶部全宽槽 (HeaderSlotPortal;无槽独立打开时原位渲染兜底)→ header 延伸到最左,左栏在其下; PlanDetailApp 根 h-screen→h-full,详情页包裹补高度链。 2. 左栏行加性别(formatGender,与列表页同源)+ 诊所名(token 字典翻译)。 3. 加诊所筛选(popover 多选 → 服务端 targetClinicIds,与列表页同款交互)。 4. 优先级展示对齐列表页:五格色条 + 10 分制数值 + PriorityHover 算分明细。 5. 行内去掉场景标签;状态改彩色 pill(待认领/进行中/已完成/已放弃,STATUS_META 同款)。 web tsc 0 + Next 生产构建过。仅本地,未部署。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-
- 10 Jun, 2026 14 commits
-
-
把"我的任务"hover 抽屉升级为详情页固定左栏(列表页将弃用,核心要素并入;KPI 面板按需求不带): - plans/layout.tsx:布局挂在动态段之上 — 选中 planId 时渲染「左栏 300px + 详情(原三列)」=4列; 切患者只重渲染右侧,左栏不重挂(筛选/已加载分页/滚动位置全保留);/plans 列表页不受影响。 - PatientPickerRail:召回池/我的/全部(权限)tab + 搜索(300ms 防抖)+ 排序 + 真实号码开关 +
⭐ 画像标签筛选(popover 四维分组多选:价值分群8/生命周期7/紧迫度3/潜在治疗8,选中 chips 可单删); 紧凑患者卡(名/性别年龄/真角标/优先级分色/场景/掩码号/状态),触底+按钮双加载;选中 teal 高亮。 - usePatientPicker:use-my-tasks 泛化版 — 全套筛选上服务端,筛选变化自动回第一页。 - 后端:ListPlansQuery.personaTags("key:value" 逗号串)→ plan.service 匹配患者当前版画像 (personas.supersededAt IS NULL)的 features:同维多选 OR、跨维 AND;数组型(潜在治疗) 用 JSON array_contains;非法 key/value 静默丢弃。 - @pac/types persona-tag-filters.ts:可筛维度字典(code↔ 中文,单一真理源,与 extractor 枚举对齐)。 - 详情页移除 TaskDrawer(被左栏取代)。 验证:API 对 DB 直查口径一致(重要价值 57==57;OR 173 / AND 10 / 数组 contains 40 / 非法=全量); web tsc 0 + Next 生产构建过。仅本地,未部署。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
服务器实测体感"启动慢"对症: 1. ASR 容器启动即预热(0.5s 静音跑一遍)— onnxruntime 首推建图/分配比稳态慢很多, 之前重启后用户第一句要付这笔冷启动。 2. 网关首句快路径:攒够 ~0.6s 音频立刻出第一个 partial(不等节奏),开口更快上屏。 3. partial 节奏 700→450ms、tick 250→150ms(inFlight 防重叠不变,服务器解码 ~0.3s 扛得住)。 底部提示按用户简化为固定"结果仅供参考"(去掉听写状态行;修正引号原样渲染)。两端 tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
交互(用户要求):点
🎤 开始 → 说话时文字实时滚动进输入框 → 再点 = 纯退出(已上屏文字保留)。 实现(复用 realtime-coach 成熟模式,ASR 容器零改动): - 前端:PCM16 16k 采音 + RMS 静音门控(无声不发帧)→ socket.io 推帧; dictation:partial(当前句滚动覆盖)/ dictation:final(句定稿累加)→ setInput 实时渲染; base 保留输入框已有文字,听写追加其后。 - 后端 DictationGateway(socket.io,JWT 握手鉴权同 coach):按"帧到达间隙"断句 —— 说话中每 700ms 把当前句 PCM 包 44 字节 WAV 头调 TranscribeService 出 partial; 停顿 ≥800ms / 超 30s 整句 final 并清缓冲。inFlight 防解码重叠;先清缓冲再 final 解码(下一句帧不混入)。SenseVoice 离线模型 RTF~0.1 → 句级重解码远快于实时。 实测(模拟浏览器推帧):开口 1.3s 首个 partial,~0.7s/次滚动更新,停顿 1.3s 出整句 final, 文本与一次性识别完全一致。两端 tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
三层: - apps/asr-sensevoice:sherpa-onnx + SenseVoice-small int8(纯 CPU,无 torch,镜像 <400MB); FastAPI /transcribe:任意浏览器音频(webm/mp4)→ ffmpeg 16k 单声道 → 离线识别(ITN 标点)。 模型不进镜像/git —— 卷挂载 ${PAC_MODELS_DIR:-../pac-models}/sensevoice(model.int8.onnx + tokens.txt, 来源 sherpa-onnx releases sense-voice-zh-en-ja-ko-yue-2024-07-17)。内网专用不发布端口(prod)。 - 后端:POST /assistant/transcribe(全局 JWT 鉴权,multipart ≤15MB)→ TranscribeService 转发 PAC_ASR_URL(provider 单一出口,以后切云 ASR 改这一处)。 - 前端:composer 加🎤 (点击录音/再点结束,红色脉冲态)→ MediaRecorder(webm/opus,Safari mp4) → 上传转写 → 文字落输入框可编辑再发;错误/录音中提示在底部状态行。 compose:dev + prod 都加 pac-asr 服务(prod 限 cpus:2/mem:2g,阿里云镜像源构建参数); pac-service 注入 PAC_ASR_URL=http://pac-asr:8000;deploy 脚本 SERVICES 加 pac-asr。 本地验证:容器直测 3.9s 音频 0.46s 出字"帮我查一下患者孙科的画像和召回计划。"; 经后端鉴权链路同样通过;无 token 10106 拒。两端 tsc 0。暂不部署服务器(模型待 scp)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
背景:增量游标 ISO 格式 bug 空转三天才被人工发现 — "success+fetched=0" 无人知晓。 - AlertService:识别企微 webhook(qyapi.weixin.qq.com)→ markdown 格式(级别 emoji/颜色, context 引用块,body 截断);企微 HTTP 永远 200 → 检查 body.errcode 才算送达。 - 增量空转探针(ColdImportService 收尾):带游标的增量跑出 0 写入时,反查 DW 「比游标新的行数」— DW 也 0 = 真没数据(静默);DW>0 = 拉不到但有 → 当天 critical 告警。 探针(ClickHouseSourceService.probeNewRowCounts)任何异常吞掉,绝不影响同步主流程。 - DwLagMonitorService:🟡/
🔴 滞后从"只打日志"接到 AlertService(企微同收)。 验证:本地用编译产物真发企微群,payload 被接受(无 errcode 报错)。tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
诊断(2026-06-10 服务器):增量 cron 每天 success 但 fetched=0 × 3 天;DW 实际有新数据 (fact_appointment_out max updated_date=06-09 22:50)。根因:DW 的 cursor 列是 Nullable(String)('YYYY-MM-DD HH:mm:ss'),WHERE 比较走字典序;游标存的是 run_start ISO ('2026-06-09T03:30:00.012Z'),第 11 字符 'T'(0x54) > ' '(0x20) → 所有真实行字典序都 小于游标 → 永远 0 行。实测同一时刻 ISO 谓词 0 行 / 普通格式 1178 行。 修复:读取侧规范化(toDwCursorLiteral)— 构造 IncrementalConfig 时把 ISO 游标按 manifest.timezone 转成 'YYYY-MM-DD HH:mm:ss'(sv-SE locale 恰好此格式); 旧游标无需迁移;已是普通格式/垃圾值原样返回;秒精度(同秒 .SSS 行值字典序更大仍被选中, 轻微重叠由 source_event_id 幂等吃掉)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
bash 按字节偏移惰性读脚本;本脚本运行中会 pull 更新自己 → 后半段按新文件偏移执行旧/错位代码 (实测:验证逻辑跑的是旧版)。包进 main() 并在文件末尾调用,bash 先整体解析完再执行,免疫自更新。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
docker compose `up -d --build` 有跨版本反复复发的已知缺陷(docker/compose#9308 #9259): 镜像构建成功但运行中容器不重建(仍跑旧镜像)。本项目 2026-06-10 部署实测中招: service 容器还在旧镜像 → migrate 报 "no pending" → phone_verified 列没建。 根治思路:生产部署不依赖 compose 的 diff 启发式判定。deploy/deploy-prod.sh: git pull --ff-only → 显式 build → up -d --force-recreate → 三道硬验证 (① 容器镜像 ID == 新构建镜像 ID;② migrate exit 0 + migrate status 无 pending; ③ service health 200 + web 200),任一不过即非零退出。 README 把日常更新指到脚本,并警告勿直接 up -d --build。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
- plan-aggregate 聚合 patient 透出 phoneVerified; - 详情页手机号旁角标:真(teal,外部对照表已核实)/ 假(灰,宿主同步造数号), tooltip 注明含义;类型/adapter/mock 同步。 验证:真号患者(刘倍磊)phoneVerified=true、假号患者(吴小燕)false,两端 tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
测试库 phone 全为造数假号;业务提供 1500 名患者的真实手机号(按病历号 file_num 对照)。 - schema: Patient 加 phoneVerified(默认 false)+ migration;true = 外部对照表核实替换的真号。 - cli/import-real-phones: 读 CSV(file_num,client_phone)→ 按 medical_record_number 匹配 → phone 改真号 + phoneVerified=true;支持 --dry-run;号码做基本卫生(去非数字)。 - 列表: ListPlansQuerySchema 加 phoneVerified(query 串 'true' preprocess 还原布尔); plan.service 把 keyword 与 phoneVerified 合并进 patient 子查询;PlanPatientBrief 透出 phoneVerified。 - web: 筛选条加"真实号码"开关(teal 高亮);行内手机号旁加"真"角标(tooltip 注明已核实)。 本地验证:1500 行对照表匹配 12 名(本地数据不全属预期)→ 更新 12; 列表 all=274 → phoneVerified=true 筛出 12,行内标记正确。两端 tsc 0。 注:重新全量摄入会被宿主假号覆盖 phone(phoneVerified 不回退)→ 重摄后需重跑导入脚本。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-
- 09 Jun, 2026 24 commits
-
-
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
上版每次流式增量都换 srcDoc → iframe 整段重载 → 反复重新下载 Tailwind/Chart.js CDN, 还没加载完下一帧又来 → 整个流式期间一直"重载中"=空白(用户实测空白)。 改为持久壳方案: - iframe 只加载一次(srcDoc=固定壳:CSP+Tailwind+Chart.js+#root+消息监听),CDN 只下一次; - 内容通过 postMessage 注入 #root.innerHTML(Tailwind 的 MutationObserver 自动给新内容上样式)→ 流式内容实时"长出",不重载、不白闪; - Artifact 加 streaming 标志:流式中只 set innerHTML(图表脚本不执行,避免半截脚本报错); 最终 tool_call(streaming=false)时重建 <script> 节点 → Chart.js 执行、图表渲染。 - 高度由 iframe 内 ResizeObserver 实时回传(initial 120,随内容增长),不再是空大白框。 - openFull 用自包含整页(buildStandaloneDoc)。 web tsc 0。纯前端改动。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
诊断:render_artifact 慢点全在"模型逐字吐 ~5.5KB HTML"(~32s),而 tool-call 要等整段 生成完才触发 → 用户对着"生成中"干等 30s+。MCP/工具往返仅 0.2s,非瓶颈;默认模型已是 deepseek-v4-flash(已是快的)。 优化(感知为主): - 后端:消费 AI SDK 的 tool-input-delta,按 toolCallId 累积入参 JSON,增量提取 html 字段 (extractHtmlField 容忍半截转义),250ms 节流推 artifact_html 事件。 - 前端:artifact_html 按 callId upsert 到 artifact 块(实时增长),最终 tool_call 覆盖完整 html+title; ArtifactView 对 srcDoc 重建做 600ms 节流(避免每增量都重载 iframe 白闪 / Tailwind 反复重扫)。 - 提示词加"HTML 力求紧凑"以略减 token。 实测:卡片 html 从 ~8s 起流式到达(35→5537 字符,65 次增量),界面从第 ~8s 起"长出"卡片, 而非 ~35s 整张蹦出。两端 tsc 0。 注:总时长仍受模型出字速度限制(~40s);若要"秒开"常见列表,需走机制 A(预制组件直渲工具结果)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
三个交互问题: 1. 工具 execute 抛错(如模型传错 patientId)时,AI SDK 发的是 tool-error 分片, 而 controller 只处理了 tool-result → 前端该步骤永远停在 running(右侧转圈不停)。 现转发 tool_error → 前端标记 status='error'(显示"出错"),loading 终止。 2. 事件带 toolCallId,前端 findToolIdx 优先按 callId 匹配(退化到"最后一个 running"), 并行/多工具调用不再串位。 3. 加"生成中…"指示(GeneratingHint):流式中且最后一块不是增长的文字时显示—— 覆盖"工具已返回、artifact 整段 HTML 还在生成"的空档,避免界面看起来卡住。 验证:强制假 patientId → tool_call 与 tool_error 同 callId,步骤正确收尾。两端 tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
机制 B(Claude Artifacts 式,即用即焚): - 后端加本地工具 render_artifact({title, html}),html 是 <body> 内部片段; SYSTEM_PROMPT 加"展示方式"skill:简短问答用文字;召回列表/画像卡/分析报表用 render_artifact, Tailwind + PAC teal 配色,图表用 Chart.js,禁外部网络、数据内联、手机号掩码。 - 前端 use-assistant-chat 加 artifact block(拦截 render_artifact 的 tool_call → html); assistant-chat 加 ArtifactView:沙箱 iframe 渲染。 - 安全:sandbox="allow-scripts"(无 allow-same-origin)→ 脚本在 null origin 碰不到父页 cookie/凭证;注入 CSP default-src 'none' + 只放行 Tailwind/Chart.js CDN + connect-src 'none' 堵死外传(患者数据不会被偷渡);postMessage 自适应高度;支持新窗口打开。 运行环境(iframe shell)由前端注入 Tailwind Play CDN + Chart.js,模型不写 <html>/<head>。 注:沙箱隔离,无法用父页的 Next.js/shadcn 组件;Tailwind+原生JS+Chart.js 已覆盖卡片+图表需求。 验证(deepseek):天气→纯文字不产卡;"卡片展示召回池TOP5"→ list_recall_queue→render_artifact, HTML 含 canvas 图表、teal 配色、无 fetch。两端 tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
外部 agent 是通用的(可同时挂多个 MCP),真正服务对象是牙科诊所客服。把 SYSTEM_PROMPT 从"你只是 PAC 患者召回助手"改为"通用智能助手 + PAC 只是附带的患者工具",并显式要求 "不要因为问题与患者业务无关就拒绝"。 保留的是数据正确性约束(用工具答患者问题时只依据真实数据、不编造、手机号掩码)—— 那不是 话题限制。 验证(qwen):冷笑话/算术等无关问题正常答;患者问题仍自主走 list_recall_queue。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
评审发现的 4 处小瑕疵: 1. 工具清单进程级缓存(McpClientService.toolsCache)— 工具静态、与租户无关,省每轮 tools/list 往返。 2. list_recall_queue 去掉 `as unknown as ListPlansQueryDto` 强转,改类型化字面量; 过程中发现隐藏 bug:schema 字段是 targetClinicId 而非 clinicId,旧 cast 把 clinicId 静默吞掉 → 工具的诊所过滤一直没生效。改用 targetClinicId,验证 271→96 条且全为该诊所。 3. McpClientService.url 注释澄清:同进程 loopback,PORT 与 main.ts 监听端口一致(默认 3001)。 4. get_facts 加注释说明经 getTimeline 自守(findFirst{host,tenant}+NotFound),与其它工具显式 assert 等价安全; 顶部注释据实修正两条隔离路径。 tsc 0;端到端验证:6 工具正常 + clinicId 过滤生效。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
之前每次 messages 变化都无条件 scrollInto, 流式时把上滚阅读的用户一直拽回底部。 改为 stick-to-bottom:onScroll 据是否接近底部(<80px)维护 stickRef,仅贴底时才 scrollTop=scrollHeight 跟随;用户上滚 → 停在阅读位;滚回底部 → 恢复跟随;发起新提问 → 重新贴底。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
之前无条件给所有 qwen 请求注入 json_object(给结构化输出框架用),把助手的 tool-calling(streamText+tools)也污染了 → DashScope 报 400(json mode 与工具调用互斥, 且硬性要求 messages 含 "json")。改为:仅当请求无 tools 且未自带 response_format 时才注入。 结构化框架(generateObject,json mode 无 tools)不受影响;助手 agent loop 带 tools 跳过。 验证:助手 model=qwen 问召回池 → 自主 list_recall_queue → 正常列出 TOP 患者,无报错。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
按设计交接包(claude.ai/design)的 PAC 助手.html / assistant-app.jsx 重做样式: - teal(#0D9488)主题;header:teal 机器人 logo + "PAC 助手" + "MCP 患者工具" 药丸 + 模型下拉 (自定义 dropdown,DeepSeek/Gemini/通义千问); - 用户气泡 slate-800 rounded-2xl rounded-tr-sm;助手 teal 头像; - 工具调用折叠卡(slate-50/70 + 扳手 + code 名 + 结果摘要 + ✓;展开看 入参/返回 CodeBlock)= Claude 式透明步骤; - composer rounded-xl + 自增高 textarea + teal 发送键 + 底部说明;空态 teal + 示例 chips。 - LLM 文本输出改用 react-markdown + remark-gfm 解析(表格/标题/列表/code),不复用设计的结构化卡。 SSE 流式 hook 不变;tsc 0。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
GPT/Claude 式聊天页:模型经 MCP 自主调用 PAC 患者工具,过程透明可见。 - use-assistant-chat:fetch+ReadableStream 消费 /assistant/chat 的 SSE(镜像 use-script-stream, Bearer header;EventSource 不支持自定义 header),把 text/tool_call/tool_result 事件组织成 消息内的有序 blocks(文本 + 工具步骤)。 - assistant-chat:消息气泡 + **工具步骤卡(可展开看 入参 + 返回数据)**= Claude 式"看得到怎么做到" + 模型切换(deepseek/gemini/qwen)+ 流式渲染 + 示例 prompt + 停止。 - /assistant 路由(AuthGate + AGENT_INVOKE 权限)。 前端 tsc 0 错误;后端 P1(MCP)+P2(agent loop)已端到端验证。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
独立 assistant 模块(不复用 AiCall 单发框架),实现 model-driven tool-calling agent loop: - McpClientService:极简 HTTP MCP 客户端(raw JSON-RPC),助手作为外部 agent 真连 PAC 自己的 /mcp(dogfood),转发用户 Bearer → 工具在该用户 tenant scope 执行。 - AssistantService:动态拉 MCP 工具 → 包成 AI SDK tool(execute=回调 MCP)→ streamText 多步循环(模型自主决定调哪些工具,stopWhen=stepCountIs(8))→ provider 可切换 (deepseek/gemini/qwen,复用 AiProviderService)。 - AssistantController:POST /assistant/chat(JWT)→ fullStream 部件转 SSE (text / tool_call / tool_result / error / done),供前端流式渲染 + 工具调用可视化。 本地端到端验证(:3101):问"孙柯画像+召回计划"→ 模型自主 find_patient→get_patient_overview, 流式产出基于真实数据的答案(价值分群/画像/召回 3 条),手机号掩码。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
POST /pac/v1/mcp(Streamable HTTP,无状态)暴露 6 个只读工具,薄封装现有 service: - find_patient(检索,掩码号)/ get_patient_overview(360 一把梭)/ get_persona(全量画像) / get_facts(纯事实时间轴)/ get_recall_plan(召回原因+优先级)/ list_recall_queue(工作台) - 鉴权:Bearer 平台 JWT(McpAuthService 复用 JwtService)→ 派生 TenantScope,工具继承租户隔离。 - 越权防护:assertPatientInScope 堵住 persona.getCurrent 等不带 scope 的读(defense-in-depth)。 - 集成:SDK 是 NodeNext-typed,经典 moduleResolution=Node 跟不动 → 手写最小 ambient 声明 (src/types/mcp-sdk.d.ts),运行时走 clean subpath(exports map 放行),类型与运行时解耦。 本地端到端验证(:3101):401 无 token / initialize 返回 pac-patient-tools / tools/list 6 工具 / find_patient(孙柯,138****7369)/ get_patient_overview(persona v4+召回+20事实)/ 跨租户 patientId → isError 不在租户范围内。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
开放给 agent 的 MCP 只读服务,第一步基座: - PatientService.search(scope, query, limit):按 姓名/手机/externalId 模糊检索,强制 tenant scope, 返回极简候选卡片(手机号掩码,真号走 revealPhone)。= find_patient 工具背后。 - 装 @modelcontextprotocol/sdk@1.29.0。 - 集成侦察(避免踩坑):经典 moduleResolution=Node 下静态 import 走 @modelcontextprotocol/sdk/dist/cjs/server/{mcp,streamableHttp}.js(CJS + 带 .d.ts); registerTool(name,{description,inputSchema:ZodRawShape},cb)+ StreamableHTTPServerTransport 无状态(sessionIdGenerator:undefined, enableJsonResponse:true)+ handleRequest(req,res,body)。 - Bearer 鉴权复用 JwtService(jwt.secret),payload 含 hostId/tenantId/permissions → 派生 scope。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
回答"宿主改了表结构/字段/枚举没通知,PAC 能不能察觉":在已有 mappingMiss(枚举漂移)基础上加 "字段/列漂移"检测,两路: - assembler 层(column_absent):映射字段对应 host 列在装配行里整列缺席 → 进 stats.suspectFields。 对直连原生表的资源有效;transform 产出表会补 key 掩盖,故补第二路 ↓ - raw 层(form A,column_removed/added,最可靠):ingestRawTables 在 transform 前快照推送列集, 对比"历史同源 rawPayload 列集"(基线抽样 50 条,排除本 run 防自污染,基线<5 跳过)→ 删列/加列。 两路都并入 SyncLog.metadata.suspectFields,/admin/mapping-miss 端点一并透出(报告含 misses+suspectFields)。 本地直连验证:删 doctor_name + 加 new_weird_col 推送 → suspectFields=[{doctor_name,column_removed},{new_weird_col,column_added}]。 不建表(复用 SyncLog.metadata)。只检测+可见化,不告警/不阻断(按需后续接 webhook)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
扫最近 N 次 sync run 的 SyncLog.metadata.mappingMisses,按出现量倒序聚合,给运维看 "摄入时哪些原值没映射上、落了 _default"(诊断名/获客渠道长尾)→ 扩 yaml → reparse 闭环。 权限 PLATFORM_MANAGE,host self scope(同其它 admin 端点)。 本地端到端验证(:3101): - POST /push/rows + HMAC → {code:0, transactionsWritten:4, personaEnqueued:1}(验签+落库+画像入队) - GET /admin/mapping-miss + JWT(admin) → 精确返回推送行里的未映射诊断 "恒牙列" Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
保障机制:enum_mapping 精确+keyword 都没命中、落到 _default 的原值,过去静默(code=null 悄悄烂),现在按 field+rawValue 聚合记账 → 落 SyncLog.metadata,供"看漏了啥 → 扩字典 → reparse"闭环。pull(cold-import)/push/reparse 三入口同享(都过 assembler)。 - assembler-engine:applyEnum 落 _default 时 recordMappingMiss(单值+数组); AssemblerResult.stats.mappingMisses[];导出 MappingMiss + mergeMappingMisses。 - cold-import:PerResourceStats.mappingMisses;processSubject 携带; ingestRawTables(push)写 SyncLog.metadata + warn;reparse 聚合 + top20 日志(批量审计面)。 - 不建表、不加列(复用既有 SyncLog.metadata Json)。 本地验证:push 一条含"乳牙列"(故意不映射)的 EMR 行 → metadata.mappingMisses 精确捕获 [{field:code, rawValue:乳牙列, fellBackTo:"", count:1}]。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
宿主把原生表行(导出给数仓那种)照抄推来,PAC 复用 transforms+assembler+processSubject 同核落库(拆分/映射/外键/幂等全 PAC 做),与 pull/reparse 完全一致,只是数据入口是 webhook。 - ColdImportService.ingestRawTables({hostName, source, rows}):tables={[source]:rows} → transformEngine.run → 对"终端原生表=source"的每个 assembler cfg 跑 processSubject; tenant 走 manifest brand resolver(同 pull,非 stub);返回 touched 患者(带 tenant)。 - push.schema:PushRowsRequest/Response。 - PushReceiverService.receiveRows:ingest → 对 touched 患者 enqueuePersonaRecompute (复用现有 BullMQ 触发 + watermark 幂等 + 凌晨 cron 兜底);plan 不在此触发(定时任务保)。 - PushController:POST /push/rows(HMAC 验签复用 /push/events 同逻辑)。 本地核验:原样推=同口径去重(dup=6/txn=0);bump updated_date 推=新 txn=6/touched=1 (tenant 正确解析);版本流无重复(active facts 9→9)。main.ts 已 rawBody:true。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
- §6.1 回执:统一 envelope {code,msg,data},HTTP 恒 200(崩溃才 5xx)→ 判成败看 body.code 非 HTTP; 按 code 族判重试(1xxxx/30802 不重试告警;10003/9xxxx/5xx/超时 退避重试)。修正之前误用 HTTP 401/400 的表。 - §6.2 防丢:本地 outbox + 重试到确认 + 异步不阻塞业务 + 不确定也重试(幂等去重)。 - §6.3 漏推补齐:数仓兜底(reconcile,首选)/ 周期幂等补推(纯 push)/ PAC 侧检测告警。 - checklist 补 outbox / 按 code 判重试 / 周期补推 / amountUnit+timezone 配置。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
不再要求宿主自己转整数分/ISO8601带时区:接入时配置 amountUnit(fen/yuan)+timezone(IANA), 宿主按自身系统原样发,PAC 的 normalize 归一成 分+UTC(同数仓口径)。带 offset 的时间直接用。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
- 两形态二选一,A 默认(字段最少、口径同数仓、可 reparse 自愈),C 备选(强宿主)。 - 公共 envelope 收敛为真·通用字段(subjectType/action/tenantId/patientId/occurredAt/updatedAt); subjectId 选填(有则更准、无则 PAC 用自然键合成),clinicId 条件,emrId 归 payload(资源专属)。 - 幂等:PAC 合成 source_event_id(宿主不发),partial UNIQUE + 内容指纹双层保证,A 即数仓同款已实证。 - 软删走 *_cancelled + updatedAt。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
我们讨论定稿的 Push 方案:一个端点 + HMAC + envelope/payload 分层;宿主只发原始字段 (原始诊断名等),K码映射/幂等键(subjectId+updatedAt)/ID合成/时区 全 PAC 内政; 诊断等一父多子用 payload.items[](身份 PAC 用 emrId+name+tooth 合成,宿主不发 source_event_id); 软删除走 *_cancelled 事件 + updatedAt;TLS + 批量 + 幂等重试。面向宿主开发、可直接实现。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed -
- 新增 §六bis Reparse SOP:原理(raw_payload 离线重跑、非破坏、不连 DW)/ 用法(dry-run/全量/ --patient/--no-recompute/docker)/ 实测基线(全量 541k ~28min,内存 ~630MB;单批 274 ~46s)/ 典型场景(诊断漏 code → 漏召,正畸 5554→1)。 - §五运维表 + §六 yaml 变更表:truncate 重导 → 改指向 reparse。 - §八已知边界:reparse 模式缺
✅ 已落地;新增"诊断 code 长尾待评估"(松动牙/种植体周围炎, 需业务决策,不是补 code 能解决)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>luoqi committed -
服务器 reparse 后残留 279 条正畸 null,几乎全是用"合"简写(反合 120+、开合系 ~70、对刃合) 而非"颌/牙合"的 crossbite/openbite 诊断。补这 3 个特定术语(不误伤:咬合/深覆合 等不含子串), 预计再 reparse 把残留压到 ~30(剩习惯/TMD 等非正畸,合理留 null)。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
luoqi committed
-