# 输入框附件问答 —— 终版(三路 + 最小流程线) ## 一、路由决策(f.txt 步骤 3-5 落地,不加冗余流水线) **上传时**做结构化类型路由(后缀 + Tika MIME,无 LLM): | 路径 | 条件 | 处理 | 进大模型的方式 | |---|---|---|---| | **拒绝** | 图片/音视频/压缩包/加密 | 明确提示不支持(无 OCR/ASR) | 不发消息 | | **A 内联** | 小表格(≤200 行)/ 小文档(全文 ≤30000 字) | 一次提取:表格→markdown 全文;文档→Tika 全文 | 全文拼进 USER 消息(Claude document block 同款) | | **B Python 直读** | 大表格(含 **100MB CSV**) | **一次流式扫描**:行数+表头+前 20 样本(不物化);xlsx 顺手转 sidecar csv(免 openpyxl 依赖)。**不落 DuckDB、不建表** | 消息注入「文件路径+行数+列+样本+指引」→ Agent 写 `execute_python`:`pd.read_csv(path)` **pandas/numpy 全量计算**(沙箱已验证可读任意服务器路径) | | **C RAG** | 大文档(**100MB txt/word**、全文 >30000 字) | 全文落 sidecar → 分块(1000/重叠100, 上限500块) → embedding → pgvector | doStream 按**用户问题** cosine 检索 top-8 片段注入 + 指引 | **计算意图 = 已有沙箱,零新开发**:`PythonAnalysisTool.execute_python`(ProcessBuilder 子进程隔离,pandas/numpy/duckdb/matplotlib,只读 DuckDB 快照,超时控制)+ `execute_sql`。**不加前置意图分类器**——意图路由由会话绑定 Agent 内化(IntentMiddleware + 工具选择),注入块写死指引: > 表数据文件:`{path}`(N 行,列:…,前 20 行样本见上)。**计算/统计必须用 execute_python 读该文件全量计算,不要只依据样本**;文档问题基于上方全文/检索片段回答。 执行过程经现有 `tool_call/tool_input/tool_result` SSE 在 ProcessTimeline 可视化(写了什么代码、跑了什么、返回什么)——f.txt 的"生成代码→沙箱执行→返回"完整呈现。 **业界对应**:A=Claude document block / ChatGPT 小文件直注入;B=Code Interpreter 直读文件;C=ChatGPT file_search RAG。 ## 二、数据隔离(三层,你的硬要求) 1. **磁盘**:`{storage.root}/chat-attachments/{userId}/{caseId}/{sessionId}/{雪花}_{safeName}`,sanitize + normalize 防穿越(沿用 `DmService#saveUploadedFile` 手法) 2. **附件表三重归属**:`chat_attachment` 记 `user_id/case_id/session_id`,`loadForSession` 校验不匹配即 400——跨用户/跨案件/跨会话读不到;Python 沙箱只拿到**归属校验通过后的路径** 3. **RAG 向量表**:`chat_att_vec_{caseId}_d{dims}` 按案件分表(沿 `rag_ws{id}_d{dims}` 惯例);chunk payload 强制带 `sessionId+attachmentId`,**检索 SQL WHERE 过滤当前会话**——同案件其他会话搜不到别人的附件片段 (不写 DuckDB,故无需 DuckDB 层隔离;沙箱可读服务器路径是既有属性,非本次新增面。) ## 三、后端设计 **新增 `module/agent/attachment/`**: 1. **`ChatAttachmentService`** - `upload(file, session)`:校验(表格 ≤200MB / 文档 ≤50MB / 白名单 / sanitize)→ 存盘三层路径 → 路由执行(A 提取 / B 流式扫描+(xlsx)sidecar csv / C Tika 全文→sidecar→同步 RAG 索引)→ 落表 → 返回 `{id, name, size, kind, rows/chars, truncated, indexedChunks?, status}` - `loadForSession(ids, session, question)`:三重归属 → 内联块 / 表指引块(路径+schema+样本)/ **大文档用 question 检索 top-8 片段**(复用 `RagVectorMapper.searchVector` 同款 cosine SQL + session 过滤) - `purgeOrphans()`:`@Scheduled` 24h 清理(文件 + sidecar + pgvector 行) 2. **`AttachmentSheetScanner`**:csv 流式一行行读(行数+表头+样本,常数内存);xls/xlsx Fesod `AiRowReader.forEachRow` 流式(同一回调顺写 sidecar csv) 3. **`AttachmentDocIndexer`**:分块 → embedding(**`EmbeddingModelFactory.resolveDefault()`**,与 `RagSchemaService` 同一模型维度,并发 8 路)→ `RagVectorMapper` 建表/写入。未配置 embedding 模型 → 上传报错「未配置嵌入模型,无法索引大文档」(与现有 RAG 同一前提) 4. **`AttachmentContentRouter`**:DOCUMENT | TABLE | UNSUPPORTED(纯函数单测) 5. **`ChatAttachment` entity + Mapper + `sql/chat_attachment.sql`**:`id/user_id/case_id/session_id/file_name/file_path/sidecar_path/mime/kind/size_bytes/char_count/row_count/truncated/index_blocks/index_status/status/link_message_id/create_at` 6. **`ChatAttachmentProperties`**:`zsjz.chat-attachment`(enabled/max-doc-mb=50/max-sheet-mb=200/inline-max-chars=30000/inline-max-rows=200/chunk-size=1000/chunk-overlap=100/max-chunks=500/top-k=8/白名单) 7. **`AgentChatController`** 增 `POST /chat/attachments`(multipart) **修改**:`ChatRequestDTO` + `attachmentIds`;`AgentChatServiceImpl#doStream` 的 `msgs` 组装(附件块+问题原文单条 USER Msg);`persistUserMessage` 落**原文** + `metadata.attachments` 徽标。 ## 四、前端设计 - **`ChatComposer.vue`**:附件按钮 + hidden input(aiPlugin 三件套)→ 选择即上传(`defHttp.uploadFile` 显式拼 `ctxPath+adminPath`,进度抄 governApi)→ chips(文件名+大小+「N 行/已索引 N 片/截断」+转圈/失败×移除);`emit('send')`:string → `{ text, attachmentIds }` - `ChatPanel` / `useAiChatPage` / `chatStream.ts`:载荷透传;乐观 userMessage 带附件;`onRegenerate` 复用原 attachmentIds - `chatApi.ts`+`types.ts`:`ChatStreamParams.attachmentIds`(协议前后端同步,AI_AGENT.md §6) - `ChatMessageItem.vue`:用户气泡徽标;`hydrateMessage` 从 `metadata.attachments` 还原历史 ## 五、验证计划 1. `mvn compile` + 新单测:Router 三分类、SheetScanner(csv 行数/样本/xlsx sidecar)、Extractor 截断、Indexer 分块与 payload session 字段、`loadForSession` 跨会话 400、doStream 注入块(mock agent 捕获 Msg) 2. 浏览器端到端:① 100MB CSV 问总金额 → 时间线出现 `execute_python`(`pd.read_csv`)→ 结果对行数核对;② 100MB txt/word 问细节 → RAG 片段注入 → 原文可核对;③ 小 txt/小表直接回答;④ 图片拒绝;⑤ 刷新徽标仍在;⑥ 旧 attachmentId 换会话 400 3. 既有测试全绿 ## 六、风险 - RAG 索引耗时:500 块×远程 embedding 并发 8 路 ≈ 十几秒(上传超时 120s);本地 bge-m3 慢 → 建议配远程 embedding(配置可调) - 文档 >50 万字:截断索引并在注入块标注「仅索引前 N 字」(诚实告知) - 附件生命周期:24h 孤儿清理(文件+sidecar+向量行);已关联随会话留存(级联删列后续) - pandas 读文件编码/分隔符报错 → tool_result 回给 Agent 自行纠错(`read_csv` 参数迭代),这是 Code Interpreter 模式的固有能力 **预计改动**:后端新增 7 文件 + DDL,修改 3;前端新增 1,修改 7;测试新增 3-4。相比上版砍掉了 DuckDB 导入整条流水线。