feat(avatar): add private vision chat support
This commit is contained in:
@@ -51,6 +51,16 @@ EMBEDDING_API_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
|
||||
EMBEDDING_API_KEY=<production-embedding-api-key>
|
||||
EMBEDDING_MODEL=text-embedding-v3
|
||||
EMBEDDING_BATCH_SIZE=10
|
||||
|
||||
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
|
||||
```
|
||||
|
||||
如生产 AI 配置中心不可用,还应提供当前项目支持的 `OPENAI_API_KEY`、`OPENAI_BASE_URL`、`CHAT_MODEL` 等兜底配置。`/data` 必须挂载持久卷,数据库与知识库文件不可存放在容器临时层。
|
||||
@@ -109,7 +119,9 @@ location /api/ {
|
||||
}
|
||||
```
|
||||
|
||||
`proxy_buffering off` 用于数字分身 SSE 流式吐字,`client_max_body_size` 用于知识库文件上传。网关和应用日志必须关闭完整 URL 查询参数记录,任何异常日志都不得输出 token、Authorization 或平台密钥。建议同时设置严格的 `Referrer-Policy: no-referrer`。
|
||||
`proxy_buffering off` 用于数字分身 SSE 流式吐字,`client_max_body_size` 同时用于知识库文件和聊天图片上传。应用只保存图片识别结果,不保存原图;识别结果 24 小时失效,后台默认每小时清理一次。公开分享图片识别会消耗分身所有者积分,生产网关应针对 `/api/public/avatar/*/chat/images` 设置每 IP 和每分享令牌的上传频率限制,防止恶意消耗。
|
||||
|
||||
网关和应用日志必须关闭完整 URL 查询参数记录,任何异常日志都不得输出 token、Authorization、图片 Base64、病例正文或平台密钥。建议同时设置严格的 `Referrer-Policy: no-referrer`。
|
||||
|
||||
## 5. 发布验收
|
||||
|
||||
@@ -123,6 +135,9 @@ location /api/ {
|
||||
8. 重建容器后数据库、头像、知识库文档仍存在,`/api/health` 返回成功。
|
||||
9. `https://digital.99hui.com/api/health` 可访问,证书域名和有效期正确,HTTP 自动跳转 HTTPS。
|
||||
10. 微信和支付宝各创建一笔最小套餐订单,未付款时积分不变;支付成功后回调到账一次,重复回调积分不重复增加。
|
||||
11. 私聊和公开分享各上传 JPG、PNG、WebP 图片并完成追问;上传非图片、超过 8MB 或跨分身附件时必须拒绝。
|
||||
12. 病例图片可以提取可见文字并标记待核对内容,医学影像不作确定诊断;视觉与 OCR 调用分别扣减积分。
|
||||
13. 检查服务器上传目录不残留聊天原图,数据库过期图片识别记录在清理周期后删除,日志不出现 Base64 或病例正文。
|
||||
|
||||
## 6. 回滚
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
# 数字分身图片与病例理解详细设计
|
||||
|
||||
## 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 光回答不作确定诊断,并显示人工复核提示。
|
||||
Reference in New Issue
Block a user