Files
zyt/.workbuddy/memory/2026-08-20.md
2026-08-22 08:51:35 +08:00

91 lines
6.3 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 2026-08-20 — 医生工作站 AI 问诊对话框布局优化
## 改动
- 文件: `app/src/doctor_workstation/ui/dialogs/ai_consult.py`
- 模块: `AiConsultDialog` 右侧「智能分析」卡片 + 问诊对话卡片密度优化
- 新增 helpers: `_InsightItem`, `_split_insight_segments`, `_split_numbered_list`, `_split_plain_paragraph`, `_split_label_or_sentence`, `_escape_html`
- `_set_insights` 改成将稠密段落拆成 lead / list / plain 三类, 每类单独 QLabel 显示
- CSS 新增 `AiConsultInsightMarker` / `AiConsultInsightText` / `AiConsultInsightPlaceholder`
- 卡片 padding `10,8,10,8``14,12,14,14`; 卡间距 `8``10`
- 聊天气泡垂直 margin `4``6`, chat_layout spacing `10``12`
## 拆分策略
- 文本以 `1./2、/3)/4` 起头 → 视为完整编号列表
- 文本中嵌入 `…:1.…2.…` → 拆出序言 + 编号列表
- 短中文标签 `XXYY` 开头 → 视为 lead, 标签部分加粗
- 其余按 `。//;/` 分句 → 单独成行
## 测试
- 现有 `tests/test_ai_consult_ui.py` 通过 `widget.objectName() == "AiConsultRecordTitle"` 校验标题仍在, 未受影响
- 暂未运行 UI 测试 (开发环境未装 PySide6); 解析逻辑单独脚本验证通过
## 注意
- PySide6 QSS 对 `QLabel``line-height` 支持有限; 主要靠 item 间距 `8px``padding:1px 0` 提供节奏
- 如果后续接入 `AiConsultInsightText` 还嫌密集, 可再调 `body_host.setSpacing`
---
# 接诊台 AI 智能分析详情对话框 (`ReceptionAiAnalysisDialog`) 优化
用户截图(`clipboard-2026-08-20T08-42-40`...)反馈: 「问诊详情 → AI 智能分析」卡片里的诊断建议 / 风险评估 / 治疗建议三段文字太稠密, 不利于阅读。
## 关键差异
这次的目标文件和上次不同 — 用户截图里出现 "诊断建议 / 风险评估 / 治疗建议" 标题, 而 `ai_consult.py` 用的是 "血糖控制评估 / 并发症风险评估 / 用药合理性评估"。实际是 `app/src/doctor_workstation/ui/pages/reception.py` 中的 `_ReceptionAiAnalysisDialog` 类。
## 改动
- 文件: `app/src/doctor_workstation/ui/pages/reception.py`
- 新增 helpers (与 ai_consult.py 类似但导出名加 `_ai_` 前缀):
- `_ai_split_into_segments` / `_ai_split_numbered_list` / `_ai_split_plain_paragraph`
- `_ai_segments_to_html` / `_ai_narrative_structured_html`
- `_ai_split_label_or_sentence` / `_ai_escape_html`
- 正则: `_AI_INSIGHT_LIST_RE`, `_AI_INSIGHT_LIST_LOOKAHEAD_RE`, `_AI_INSIGHT_LABEL_RE`
- `add_text_section` 闭包内多挂一个 RichText `_structured_labels[name]` 标签 (`.setObjectName(name + "Structured")`, `property="dialogAiStructured"`)
- 原 PlainText `value_label` 仍存在 (`hide()` + `setMaximumHeight(0)`) 用于兼容既有 findChild 测试
- `_render_model` 同步刷新两个结构化标签; 空 HTML 时回退显示原 label 文本
- 风险评估 FlowLayout spacing `7×7``9×9`
- `body_layout.setSpacing` `12``14`
- 卡片 contentsMargins `14,13,16,15``16,14,18,16`
- QSS: `dialogAiBody``line-height: 1.85`, 新增 `dialogAiStructured` 选择器 (line-height 175%); 风险 pill `min-height` `28``30`, padding `4 10``5 12`, font 12 → 12.5, border-radius `7``8`
## 兼容约束
- `tests/test_reception_parity_ui.py` 严格校验:
- `findChild(QLabel, "ReceptionAiAnalysisDialogDiagnosisText")` 必须能找到
- `.text()` 必须等于 `_ai_narrative_text` 归一化结果
- `.textFormat() == Qt.TextFormat.PlainText`
- 因此 PlainText 标签必须原样保留, 仅做 `.hide()` + `setMaximumHeight(0)` (避免 QSS `>` 子选择器不被 Qt 支持)
## 验证
- `py_compile` 通过, 无 `SyntaxWarning`
- 单独脚本跑过 4 组样本文本 (稠密段落 / 嵌入式编号列表 / 短句 / 纯编号列表), 拆分结果符合预期
- 未在本机跑 UI 测试 (缺 PySide6)
---
# 患者列表 AI 分析: 发送完整患者资料给 AI
用户需求: 患者列表 AI 分析时, 每日血糖报告、舌苔、AI 报告、视频录制回放的文字对话都要发给 AI。
## 改动
- `app/src/doctor_workstation/ui/dialogs/ai_consult.py`
- `_load_workspace` 新增 `list_call_records` section (视频问诊记录, 含 `transcript_text`)
- 模块级纯函数 (无 Qt 依赖, 便于测试):
- `AI_CONTEXT_MAX_CHARS=320`, `AI_PROMPT_LIMIT=500`, `AI_CONTEXT_SEPARATOR="\n\n— 医生提问 —\n"`
- `_truncate_for_context` / `_patient_context_blood_sugar` / `_patient_context_tongue` / `_patient_context_reports` / `_patient_context_prescriptions` / `_patient_context_videos` / `build_patient_ai_context` / `_compose_ai_prompt`
- `build_patient_ai_context` 返回 `(text, labels)`, 按 每日血糖→舌苔/脉象→视频问诊文字→历史AI报告→处方记录 顺序装入 320 字预算的信封
- `_patient_context_videos``diagnosis_id` 过滤归属 (`_exact_positive_id`), 只取最近一条有文字的
- `_compose_ai_prompt` 本地强制 500 字上限: 上下文 + "— 医生提问 —" + 问题, 超长时截断问题加 `…` (服务端 `tcm.diagnosis/aiAssistant` prompt 上限 500)
- `_ChatBubble.attach_extra()` 支持气泡下方挂载控件
- `_render_patient_context`: 聊天区系统气泡 "AI 已加载本诊单上下文(N 项…)", `_attach_context_detail` 挂 "查看本次发送给 AI 的资料全文" 展开按钮 (QSS: `AiConsultContextToggle`/`AiConsultContextReveal`)
- `_ask``_compose_ai_prompt(text, self._patient_ai_context)` 组装后传给 `_AiStreamWorker`; 医生气泡仍显示原始问题
- `open_for` / `_clear_chat` 重置 `_patient_ai_context*` 三个状态, 防止跨患者残留
## 测试
- `app/tests/test_ai_consult_ui.py` 新增 5 个测试 (总 15 个全通过):
纯函数: 全 section 信封+视频按诊单过滤 / 仅基本病历返回空 / 长问题截断≤500
UI: 上下文气泡+展开按钮+视频记录计数 / `_ask` 注入上下文 (FakeWorker 捕获 prompt, monkeypatch `QThreadPool`)
- `app/scripts/check_ai_context.py`: AST 提取纯函数的冒烟脚本 (无需 Qt)
- `tests/test_ai_consult_workspace_ui.py` 18 个测试无回归
## 环境要点 (重要)
- **PySide6 + pytest 在 `app/.venv` 里可用**: 用 `D:/web/zyt/app/.venv/Scripts/python.exe -m pytest` 跑 UI 测试 (QT_QPA_PLATFORM=offscreen)。之前"本机缺 PySide6"的结论只对系统 Python 成立。
- 测试 FakeWorker 需实现 `signals.event/error/finished.connect()``cancel()` (closeEvent 会调 cancel)