# Chat 图片与文档附件(F074) `POST /api/chat` 增加可选的 `pictures`、`files` 字符串数组。省略与 `[]` 等价,`null`、字符串代替数组、非字符串元素或多余字段返回HTTP 422。`thread_id`、非空 `question` 的原校验及SSE协议保持;图片和文件不会拼接改写原问题。 F109:两个数组现在也接受相对路径。接口先以 `API_ATTACHMENT_BASE_URL` 补齐路径,再按原有规则校验和下载;本地 `.env` 配置为 `http://121.43.55.7:10081/dms/`。例如 `/upload/a.png` 解析为 `http://121.43.55.7:10081/dms/upload/a.png`。已是HTTP(S)完整地址的元素保持原样;未配置前缀时相对路径仍返回HTTP 422。服务端重启后读取新配置。 ```json { "thread_id": "chat-example", "question": "请结合图片中的设备和附件预算,说明有哪些支持方向及需要核实的条件。", "pictures": ["https://files.example.com/download?id=picture"], "files": ["https://files.example.com/download?id=budget"] } ``` 链接必须无需登录即可由后端GET下载。允许无扩展名、带签名查询参数的HTTP(S)链接;按实际文件内容识别格式,不依赖URL后缀或响应文件名。浏览器依赖Cookie才能下载的地址不符合此契约。服务不转发用户Cookie或Authorization;签名URL不传给模型、不写终端日志或Redis附件记录。图片下载后以Base64发送到视觉模型。 本轮未增加域名白名单配置。下载器默认拒绝解析到非公网地址的主机,并在每次重定向重新检查与固定连接IP;因此内网文件服务尚不在当前可访问范围,后续如需接入需单独定义可信下载范围。最多跟随3次重定向,不接受带用户名密码、片段或控制字符的URL。相对路径补齐不会绕过这些下载限制。 ## 格式与提取范围 | 字段 | 格式 | 处理 | | --- | --- | --- | | pictures | JPEG/JPG、PNG、静态GIF、静态WebP | `deepseek-flash`读取图片文字、表格和可观察信息,生成带不确定性的视觉笔记 | | files | XLSX、XLSM、XLS | pandas读取全部工作表;新格式使用openpyxl,XLS使用xlrd | | files | DOCX | python-docx按正文顺序读取段落及表格 | | files | 文字PDF | pdfplumber逐页提取文字;保留页码 | Excel保留工作表和行号,使用存储值及已有公式缓存,不计算公式、不运行宏、不读取图表中的图片文字;数字格式显示的前导零、货币等样式不等于实际单元格值。DOCX范围为正文段落及表格,不含图片OCR、文本框和页眉页脚;PDF不做OCR。F111起,PDF任一页无法提取文字即视为该附件读取不完整,本轮终止;空白页也会触发这一规则,请提供每页均有可提取文字的PDF。加密、损坏、无文字或不支持的文件同样终止本轮,不假装读取成功。多帧GIF/WebP当前提示改传静态图。 图片API依据:[官方Vision文档](https://api-docs.deepseek.com/guides/vision/)。图片识别使用同一模型服务地址/凭据、独立的`deepseek-flash`模型名;后续文字问答沿用现有`DEEPSEEK_MODEL`。服务端必须支持该视觉模型。视觉笔记不是图片逐字原文或已核实的企业事实。 F110:视觉提示要求在没有可辨文字时仍描述可观察的主体和画面;最终回答提示按用户实际问题区分图片观察与知识库规则。图片本身不能证明广告排版、申报条件等未呈现的事实;用户明确询问规则时才依据知识资料另答,并标明不是从图片读出。是否能辨识具体内容仍取决于原图与视觉模型,本轮未取得用户原图复现。 ## 信息流与预算 1. 在原有有界请求工作线程内下载并解析,图片模型调用共用现有LLM并发信号量。文档解析在子进程运行,30秒超时终止,不依赖主进程导入解析器执行未知文档。 2. 需求识别/查询规划只使用每个附件的有限原文摘录,总摘录不超过约2400 UTF-8字节(另有ID等结构开销)。优先选择与问题词项相关的片段并标明`incomplete`,用于理解“这个/这些资料”等;不能把摘录当作完整文件。附件来源可辅助寻找政策,但不独立证明资格。 3. 已解析的完整文档文字、视觉笔记与实际知识证据一起进入最终回答,附件来源类型为`user_attachment`,与政策来源分开。即使没有知识库命中也能依据附件回答;同题QA不再绕过本轮附件。附件不产生政策卡片。 4. 文本调用沿用保守的UTF-8字节上界估算,预留16384输出token及至少904安全余量;意图识别预留2048输出token。最终综合超预算时无损拆分证据、分批整理再综合,最多4层/32次调用;不静默截断最终证据。中间整理失败、不收敛或超限时返回明确整理失败及原文来源。图片逐张调用,额外预留每张1024图片token与4096输出token。 可接收范围不等于所有文件都能在单次请求时间内完成;长文摘要会增加模型调用及延迟,摘要存在遗漏风险,不等于逐页独立语义审计。 | 处理边界 | 当前值 | | --- | --- | | pictures + files数量 | 合计最多8个 | | 请求JSON | 保持64 KiB上限,单URL最多8192字符 | | 单附件/本轮累计下载 | 10 MiB / 32 MiB | | 单次下载 | 总预算30秒,阻塞socket读取最长10秒,故可能有一次读取的超出;DNS使用系统解析超时 | | 单文档解析子进程 | 最长30秒 | | 单图片视觉请求 | 最长45秒服务读取超时;阻塞期间保持会话占用 | | 图片尺寸 | 单边最多8192,最多3200万像素 | | Office ZIP展开 | 最多64 MiB、5000个内部条目 | | Excel | 最多32工作表,每表10000行、256列 | | PDF | 最多200页 | | 单文档提取文字 | 最多200000 UTF-8字节;最多10000个非空片段 | | 本轮全部文字/视觉笔记 | 最多400000 UTF-8字节 | F111(2026-09-23):附件串行处理,遇到首个下载、格式、可读性或限额失败立即停止,不再读取后续链接,也不进入需求分类、企业查询、知识检索和回答。已成功读取的前序附件仅保留在本轮`attachments`状态事件,不用于生成部分答案;下次提问需重新提交全部所需附件。原有请求超时/断连约束保持,超时后的工作者实际退出才释放会话锁。 若链接本身的语法或协议在请求接纳前无效,接口仍返回HTTP 422;响应新增`message`与`attachment.kind/index/title/error`,指出具体“图片N”或“附件N”,不会启动SSE或下载。数组结构整体无效仍返回普通`invalid_request`。 ## SSE和来源 新增`progress.stage=attachments`的开始/结束事件,以及`attachments`事件;外层仍为`thread_id/request_id/data`。 ```json {"items":[ {"id":"attachment_随机ID","kind":"file","index":1,"title":"附件1","status":"ready","format":"docx","warnings":["docx_body_only"]}, {"id":"attachment_另一个ID","kind":"file","index":2,"title":"附件2","status":"failed","error":"document_no_text","message":"文档没有可提取的文字,可能为空或为扫描件。"} ]} ``` 全部成功时,`result.data.attachments`重复完整处理状态,`index`是各字段数组内从1起的序号,成功下载后`fetched_at`记录上海时间。空输入时该字段为`[]`,无需新事件。处理失败时先发`attachments`(含处理到的状态),再发`progress.stage=attachments/status=failed`,最后以`error.code=attachment_processing_failed`结束SSE;错误`message`指明“图片N”或“附件N”及原因,`attachment`提供`kind/index/title/error`,不回传下载URL。此时没有`result`、`summary`或政策卡片,前端应显示`error.data.message`,请用户更换附件后重新提问。知识检索失败仍与没有命中区分。 成功附件通过原有`source`事件和`recommendation.sources`提供:`type=user_attachment`,`source.provider=user_attachment`,`source.kind/format`描述来源;`full_record.sections[]`保留`location/text`,`warnings`记录提取边界。`summary.source_ids/citations`关联实际采用的来源,文档引用`path=/sections/N/text`及页/行/段落位置。图片引用指向视觉笔记,前端不能将其标为人工核验原文。下载URL不回传;前端通过`kind/index`关联自己的原始链接。 公司确认中断和`/retry`恢复保留原轮次的附件,不重复下载;确认/恢复请求请发送空数组或省略字段。此时提交新的非空附件会通过SSE返回`attachments_during_resume`,保留原中断供再次确认,避免静默丢弃新文件。后续新问题不会自动继承旧附件全文;如需再次分析请重新提交链接。历史答复仍按既有有界对话上下文保留,不视为独立证据。 附件提取文字及视觉笔记会随本轮图状态保存在同一thread的Redis检查点,沿用现有保留周期。原始下载二进制仅用于内存/解析子进程,不写资料目录或开发文档。DMS问答记录仍由前端保存,本轮不修改DMS结构、不把附件原文件写入DMS。 ## 验证与生效 `python -m unittest discover -s src/step4_api/tests -q`覆盖真实合成DOCX/Excel/PDF解析、图片四格式请求、失败隔离、下载限制、预算分批、联合QA来源、可选字段与中断恢复;`python -m unittest discover -s src/step3_question_answer/tests -q`验证原问答行为。 `python -m src.step4_api.attachment_check`是主动调用真实模型的纯合成检查,生成图片及文档,不使用真实用户附件。仅保存检查布尔值、预算与安全错误码到`output/checks/attachments.json`,不记录正文/密钥。 依赖已写入pyproject.toml及uv.lock。安装锁定依赖并重启后端后生效;外部前端按新字段发送即可,处理状态可选展示。生产附件下载、前端来源呈现、大型真实文档及模型语义质量仍需联调验收。