# 图片上传与独立解析设计

## 背景

聊天中用户经常直接给出监控截图、日志截图或诊断页面截图。图片里的信息可能是文字，也可能是指标曲线。主 agent 需要理解这些内容，但原始图片不能进入主 agent context，否则会放大 prompt、污染历史上下文，也会让图片处理和诊断推理耦合在一起。

本设计把图片处理收敛到 backend-owned attachment pipeline：后端接收图片、保存原图、调度独立解析任务，解析任务产出结构化分析和给主 agent 使用的短摘要。主 agent 只通过 attachment id 读取摘要，不读取图片 bytes 或 base64。

## Current Design

当前实现包含三层：

- 前端 ChatPanel 提供独立图片上传按钮，用户可以先上传图片，再输入文字；解析完成后，文字和图片附件一起发送。
- 后端 image attachment API 保存图片到 workspace 文件，并在 DB 中维护 attachment 状态。
- 后端 image parser 调用 vision model 解析图片，产出 `analysis.json` 和 `summary_for_agent.txt`。

当前不支持把剪贴板图片直接 paste 到聊天框。paste 支持可以作为独立入口扩展，但不改变后端 attachment 契约。

## Target Contract

图片上传链路的 canonical source of truth 是 `workspace_image_attachments` 表和 workspace 文件：

- `workspace_image_attachments` 记录 attachment id、workspace、上传者、原图路径、解析产物路径、状态、类型和错误。
- 原图只存放在 workspace 文件目录，例如 `user_uploads/images/{attachment_id}/original.png`。
- 结构化解析只存放在 `analysis.json`。
- 主 agent 可读摘要只存放在 `summary_for_agent.txt`。
- user message 只持有 attachment metadata，不保存原始图片内容。

主 agent 感知图片的唯一方式是：

1. `/api/chat/stream` 收到 `attachment_ids`。
2. 后端校验这些 attachment 属于当前 workspace 且状态为 `ready`。
3. 后端读取每个 attachment 的 `summary_for_agent.txt`。
4. 后端把短摘要作为附加文本块拼入本次 prompt。

原始图片 bytes、base64、完整 OCR 结果和完整结构化 JSON 都不进入主 agent prompt。

## Request Flow

上传流程：

1. 用户点击 ChatPanel 的上传按钮并选择图片。
2. 前端调用 `POST /api/workspace/{workspace_id}/image-attachments`。
3. 后端校验用户有 workspace editor 权限。
4. 后端校验文件类型和大小，只接受 PNG、JPEG、WEBP、GIF。
5. 后端写入原图文件和 DB attachment 记录。
6. 后端调度解析任务，接口立即返回 attachment 当前状态。
7. 前端轮询 attachment 查询接口，直到状态变为 `ready` 或 `failed`。

发送流程：

1. 用户输入文字，可同时带一个或多个 ready attachment。
2. 前端在 attachment 未 ready 或 failed 时阻止发送。
3. 前端调用 `/api/chat/stream`，通过 `attachment_ids` 传递图片引用。
4. 后端再次校验 attachment 状态，防止绕过前端。
5. 后端把 attachment message view 写入 user message，并把摘要拼入 prompt。
6. ACP 主 agent 只看到用户文字和图片摘要。

## State Machine

attachment 状态只允许表达后端事实：

- `uploaded`: 原图已经落盘，DB 记录已创建，解析任务尚未开始或刚被调度。
- `processing`: 解析任务正在运行。
- `ready`: `analysis.json` 和 `summary_for_agent.txt` 都已写入，可用于 chat。
- `failed`: 解析失败，`error` 字段记录失败原因。

读路径不能把 `uploaded` 或 `failed` 隐式修复成可用状态；chat 入口也不能对非 ready attachment 做 fallback。

## Parser Output

`analysis.json` 是完整结构化结果，供调试、后续 UI 或后台流程使用。它包含：

- `kind`: `text`、`metric_chart`、`mixed` 或 `unknown`
- `confidence`: `high`、`medium` 或 `low`
- `ocr_summary`
- `chart_summary`
- `axes`
- `legends`
- `trends`
- `notable_points`
- `limitations`
- `metadata`

`summary_for_agent.txt` 是给主 agent 的 compact 摘要。它保留诊断所需的关键信息，例如 OCR 摘要、曲线趋势、异常点和不确定性，但避免把完整结构化 JSON 或大 payload 注入 prompt。

## Frontend UX

当前用户体验：

- 输入框旁有上传按钮。
- 上传后显示 attachment chip，包括文件名、解析状态和打开预览入口。
- 解析中或失败的图片不能发送。
- 图片 ready 后，可以和输入框文字混合发送。
- 发送后，用户消息里保留附件展示，方便回看当时引用了哪张图。

后续如果支持 paste，应复用同一个上传接口和 pending attachment 状态，不引入第二套图片语义。

## Invariants

- 原图不能进入 `messages_v2.content`、`messages_v2.parts` 或 ACP prompt。
- `summary_for_agent.txt` 是主 agent 消费图片信息的唯一文本输入。
- `analysis.json` 是解析详情，不作为默认 prompt 输入。
- chat 入口只接受当前 workspace 下的 ready attachment。
- 前端禁用发送只是 UX，后端必须重复校验 attachment 状态。
- 读接口只读状态和文件，不做解析、修复或迁移。
- 解析失败必须显式暴露为 `failed`，不能静默降级为空摘要。

## Extension Points

- paste 图片：前端从 clipboard 读取 image file 后调用同一个上传接口。
- 多图片：前端和 chat API 已以 attachment id list 为契约，可继续沿用。
- 更多解析器：后端可按 provider 扩展 vision model，但输出仍必须归一到同一份 `analysis.json` schema。
- 更丰富预览：UI 可读取 `analysis.json` 展示 OCR/趋势详情，但主 agent 默认仍只读取摘要。
