# 数字分身图片与病例理解详细设计 ## 1. 目标与边界 本功能让数字分身在私聊和公开分享聊天中接收图片,并围绕图片内容继续使用现有的“标准答题对 -> 分身独立知识库 -> Qwen 兼容模型”链路回答。 第一期支持 JPEG、PNG、WebP,覆盖以下场景: 1. 普通照片、截图、图表和界面图片的内容理解。 2. 病例、处方、检查单、检验报告等图片文档的文字和表格提取。 3. X 光、CT、MRI 等医学影像的客观可见内容描述。 第一期不把通用视觉模型的输出当作医学诊断,不自动把图片或病例写入知识库,不保存原图供长期访问,也不支持 DICOM 原始影像。 ## 2. 核心原则 - **资料优先级不变**:标准答题对最高,分身独立知识库其次,图片识别结果属于待核对的会话资料,最后才由模型组织表达。 - **病例最小留存**:应用不把原图写入业务存储,上传内容在内存中归一化并调用视觉服务;数据库只保存结构化结果和必要元数据。 - **严格隔离**:每条图片记录必须绑定 `avatar_id`,私聊校验分身所有者,公开聊天校验分享令牌对应的分身。 - **不确定性显式化**:OCR 看不清、表格列错位、医学影像无法确认时必须指出待核对项,不允许补齐缺失内容。 - **可计量**:视觉理解和病例 OCR 分别计入分身所有者的积分消耗,失败时释放预留积分。 - **可降级**:OCR 失败但通用视觉结果有效时仍可回答;视觉主调用失败则不进入聊天发送。 ## 3. 总体流程 ```text 用户选择图片 -> 前端本地预览 -> 私聊/公开图片上传接口 -> 文件大小、MIME、真实格式、像素数校验 -> 自动旋转、缩放、去 EXIF、统一 JPEG -> 通用视觉模型分类并输出结构化 JSON -> 若为病例/检查单,再调用 OCR 模型精确转录 -> 保存结构化结果,不持久化原图 -> 返回 attachmentId -> 用户发送文字 + attachmentIds -> 标准答题对匹配 -> 用文字 + 图片提取结果检索独立知识库 -> 把标准答案、知识片段、图片资料注入系统上下文 -> Qwen SSE 流式回答 ``` ## 4. 模型编排 ### 4.1 通用视觉模型 默认 `qwen3.6-flash`,可在后台数字分身专用模型配置中修改。输入为归一化后的 Base64 Data URL,要求返回 JSON: ```json { "category": "general_image|document|medical_document|medical_image", "summary": "客观、完整的图片描述", "visible_text": "图片中可确认的文字", "key_facts": ["事实1", "事实2"], "uncertainties": ["无法确认的内容"], "medical": { "document_type": "", "patient_info": {}, "chief_complaint": "", "findings": [], "measurements": [], "doctor_advice": "" } } ``` 模型提示词禁止诊断、补全被遮挡文字、猜测患者身份和输出模型信息。 ### 4.2 病例 OCR 当 `category=medical_document` 时追加调用 `qwen-vl-ocr`,按原布局转录文字和表格。OCR 文本优先替换通用视觉输出中的 `visible_text`,但保留通用视觉模型提供的分类、摘要和不确定项。 ### 4.3 医学影像 当 `category=medical_image` 时只保存客观描述,不输出疾病结论、分期、用药或治疗方案。聊天提示词必须要求结合正规影像报告和医生意见,并显示“图片识别结果仅供辅助,不能替代医生诊断”。 ## 5. 数据模型 新增 `chat_attachments`: | 字段 | 说明 | |---|---| | `id` | 不可猜测的附件 ID | | `avatar_id` | 所属数字分身,强制隔离 | | `filename` | 原文件名,去除路径 | | `mime_type` / `file_size` | 上传元数据 | | `status` | `processing / ready / failed` | | `category` | 图片分类 | | `summary` | 通用视觉摘要 | | `extracted_text` | 可确认文字/OCR 结果 | | `structured_data` | 结构化 JSON | | `warning` | 不确定项和医学提示 | | `vision_model` / `ocr_model` | 实际调用模型 | | `created_at` / `used_at` | 创建和最近使用时间 | 不保存公开原图 URL。应用层不落盘原图;框架上传缓冲在请求结束时关闭,处理结果在 24 小时后自动清理。 ## 6. API 设计 ### 6.1 上传并解析 - `POST /api/avatar/{avatar_id}/chat/images` - `POST /api/public/avatar/{share_token}/chat/images` - `multipart/form-data: file` 成功返回: ```json { "id": "attachment-id", "filename": "病例.jpg", "status": "ready", "category": "medical_document", "summary": "门诊检查单", "warning": "部分手写内容需要人工核对" } ``` ### 6.2 聊天 原聊天接口增加: ```json { "message": "请帮我看看异常指标", "attachmentIds": ["attachment-id"], "history": [ {"role": "user", "content": "上一条问题", "attachmentIds": ["attachment-id"]} ] } ``` 当前消息最多 3 张图,历史最多引用最近 3 个不同附件。后端只读取与当前 `avatar_id` 相同且状态为 `ready` 的记录。 ## 7. 安全与隐私 - 单图最大 8MB,解码后最大 1600 万像素,最长边归一化到 4096 像素以内。 - 使用 Pillow 验证真实图片格式并防止解压炸弹;重新编码时清除 EXIF、GPS 和其他元数据。 - 图片不会写入 FastAPI `StaticFiles` 或知识库目录,模型请求和日志不得输出 Base64 内容。 - 日志只记录附件 ID、分身 ID、状态、耗时和模型,不记录图片 Base64、OCR 全文、病例内容或 API Key。 - 公开分享上传仍消耗分身所有者积分;余额不足时拒绝视觉调用。 - 生产环境需要补充用户授权、数据处理协议、存储地域和模型供应商留存策略确认。 ## 8. 前端交互 - 输入框左侧增加图片按钮,支持相册选择和移动端拍照。 - 选择后显示本地缩略图和“正在识别图片”,识别完成前禁止发送。 - 用户可删除待发送图片;发送后图片保留在当前会话气泡中,但刷新页面后不恢复原图。 - 病例和医学影像在输入区及回答下方显示辅助提示,不使用恐吓式红色告警。 - 上传或识别失败时保留文字输入,明确提示重新选择图片,不产生空白消息。 ## 9. 配置 数字分身专用模型配置新增: - `vision_model_version`,默认 `qwen3.6-flash` - `ocr_model_version`,默认 `qwen-vl-ocr` 环境变量兜底: ```dotenv VISION_MODEL=qwen3.6-flash VISION_OCR_MODEL=qwen-vl-ocr VISION_MAX_OUTPUT_TOKENS=2048 VISION_TIMEOUT_SECONDS=90 VISION_TOKEN_RESERVE=12000 CHAT_IMAGE_MAX_BYTES=8388608 CHAT_IMAGE_MAX_PIXELS=16000000 CHAT_ATTACHMENT_RETENTION_HOURS=24 CHAT_ATTACHMENT_CLEANUP_MINUTES=60 ``` 视觉调用复用数字分身专用配置的 `api_base_url` 和 `api_key`,不额外复制密钥。 ## 10. 验收标准 1. 普通照片、截图和图表能够返回与图片一致的描述并支持追问。 2. 病例图片可以提取标题、患者字段、检查结果、异常指标和医生意见,模糊内容明确标记待核对。 3. 上传后服务器业务目录不残留原图,响应和日志不包含 Base64 或完整病例正文。 4. A 分身无法引用 B 分身附件;公开分享令牌无法访问其他分身附件。 5. 有图片时标准答题对仍作为最高优先级事实,知识库命中次之。 6. 视觉与 OCR 积分分别结算,失败调用释放预留积分。 7. SSE 打字效果、Markdown、用户头像、公开分享和纯文本聊天均无回归。 8. CT、MRI、X 光回答不作确定诊断,并显示人工复核提示。