# 自动汇总文件夹文件目录与页数流程功能设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 需求分析文档 | docs/1.需求分析/1.自动汇总.md | | 功能名称 | 自动汇总文件夹文件目录与页数 | | 所属模块 | 审核智能体 review_agent | | 设计日期 | 2026-06-05 | | 设计版本 | V1.0 | --- ## 一、设计目标 本功能面向试剂盒 NMPA 注册申报资料审核场景,支持用户在 AI 对话页上传压缩包或多个文件,由后台异步执行文件汇总工作流,自动完成解压、文件清单扫描、页数统计、产品名识别、Markdown 报告生成和 Excel 导出。 前台 AI 对话页需要展示一个工作流卡片,实时呈现“上传中、解压中、扫描中、解析中、识别中、输出中、完成/失败”等节点状态;工作流完成后,AI 对话框展示 Markdown 简表,并提供 Markdown 报告和 Excel 明细下载链接。上传文件、批次、节点状态、文件明细和导出结果均需要与当前对话绑定,一个对话对应一套文件,不能跨对话串用。 --- ## 二、总体架构 ### 2.1 架构原则 | 原则 | 说明 | | --- | --- | | 对话绑定 | 上传文件、处理批次、结果文件均绑定当前 Conversation | | 按需加载 | 将文件处理流程拆分为多个可单独执行的 Skill,按工作流节点调用 | | 后台异步 | 用户提交后后台执行工作流,前台通过 SSE 接收状态事件 | | 失败隔离 | 解压失败导致批次失败;单文件解析失败最多重试 3 次后记录异常并继续 | | 可迁移 MCP | Demo 阶段使用项目内 Skill 注册与调用,后续可迁移为 MCP Tool | | 可追溯 | 每个节点状态、文件统计结果、导出文件均持久化入库 | ### 2.2 逻辑架构 ```mermaid flowchart TD A["AI 对话页"] --> B["上传接收接口"] B --> C["工作流任务表"] C --> D["后台工作流执行器"] D --> E["Skill 注册表"] E --> F1["上传接收 Skill"] E --> F2["压缩包解压 Skill"] E --> F3["文件清单扫描 Skill"] E --> F4["文档页数统计 Skill"] E --> F5["产品信息识别 Skill"] E --> F6["汇总报告生成 Skill"] E --> F7["Excel 导出 Skill"] D --> G["数据库存档"] D --> H["导出文件存储"] D --> I["SSE 状态事件"] I --> A ``` ### 2.3 技术选型 | 设计项 | Demo 方案 | 后续演进 | | --- | --- | --- | | 工作流编排 | 项目内 LangGraph 风格节点图执行器 | 接入 LangGraph | | Skill 形态 | Python 类或函数注册表,按节点名称动态调用 | 封装为 MCP Tool | | 后台任务 | Django 后台线程 + 数据库状态 | Celery/RQ + Redis | | 实时更新 | 沿用现有 SSE 流式能力,新增 workflow 事件 | 独立任务事件通道 | | 文件存储 | 本地 media 目录 | 对象存储或加密文件服务 | | Markdown 渲染 | 前端引入安全 Markdown 渲染 | 统一富文本渲染组件 | --- ## 三、工作流设计 ### 3.1 节点图 ```mermaid flowchart LR N1["上传中"] --> N2{"是否压缩包"} N2 -->|"是"| N3["解压中"] N2 -->|"否"| N4["扫描中"] N3 --> N4 N4 --> N5["解析页数中"] N5 --> N6["识别产品名中"] N6 --> N7["输出中"] N7 --> N8["完成"] N3 -->|"解压失败"| F["失败"] N7 -->|"导出失败"| F ``` ### 3.2 节点定义 | 节点编码 | 节点名称 | 触发 Skill | 成功条件 | 失败处理 | | --- | --- | --- | --- | --- | | upload | 上传中 | 上传接收 Skill | 原始文件保存成功,批次创建成功 | 批次失败 | | extract | 解压中 | 压缩包解压 Skill | zip/rar/7z 等压缩包解压成功 | 批次失败 | | inventory | 扫描中 | 文件清单扫描 Skill | 生成文件清单 | 批次失败或空文件提示 | | page_count | 解析页数中 | 文档页数统计 Skill | 支持类型完成页数统计或异常记录 | 单文件失败不阻断 | | product_detect | 识别产品名中 | 产品信息识别 Skill | 识别到产品名或返回空 | 不阻断 | | report | 输出中 | 汇总报告生成 Skill | Markdown 报告与对话简表生成成功 | 批次失败 | | export | 输出中 | Excel 导出 Skill | Excel 明细生成成功 | 批次失败或记录导出异常 | | completed | 完成 | 工作流执行器 | 所有必需产物完成 | 写入完成状态 | ### 3.3 状态机 | 状态 | 含义 | | --- | --- | | pending | 已创建,等待执行 | | running | 执行中 | | retrying | 单文件解析失败,正在重试 | | success | 节点执行成功 | | failed | 节点或批次失败 | | skipped | 当前节点不需要执行,例如非压缩包跳过解压 | --- ## 四、Skill 设计 ### 4.1 Skill 注册与调用 Demo 阶段在项目内定义 Skill 注册表,工作流执行器根据节点编码按需加载并执行对应 Skill。 ```text WorkflowExecutor -> 根据当前节点读取 Skill 名称 -> 从 SkillRegistry 获取 Skill -> 执行 skill.run(context) -> 写入节点状态与结果 -> 发送 SSE 状态事件 -> 进入下一节点 ``` 后续 MCP 化时,每个 Skill 可映射为独立 MCP Tool,输入输出保持稳定 JSON 契约。 ### 4.2 上传接收 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 上传接收 Skill | | 职责 | 接收对话页上传的压缩包或多个文件,保存原始文件,创建上传批次 | | 输入 | conversation_id、user_id、uploaded_files | | 输出 | batch_id、upload_file_ids、upload_type、original_storage_paths | | 数据写入 | FileSummaryBatch、FileAttachment、FileSummaryBatchAttachment | | 关键规则 | 文件必须绑定当前 Conversation;同一对话只使用本对话上传的文件 | ### 4.3 压缩包解压 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 压缩包解压 Skill | | 职责 | 识别并解压 zip、rar、7z 等常见压缩包,保留目录结构 | | 输入 | batch_id、source_file_path | | 输出 | extract_root、extracted_file_count | | 数据写入 | WorkflowNodeRun、批次工作目录 | | 关键规则 | 防止路径穿越;解压目录必须限定在批次工作目录内;解压失败批次失败 | ### 4.4 文件清单扫描 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 文件清单扫描 Skill | | 职责 | 遍历解压目录或散装文件目录,生成文件清单 | | 输入 | batch_id、scan_root | | 输出 | inventory_items | | 数据写入 | FileSummaryItem | | 关键规则 | 保留目录层级;散装文件归入同一批次根目录;隐藏文件和空文件可标记跳过 | ### 4.5 文档页数统计 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 文档页数统计 Skill | | 职责 | 对支持类型统计页数或数量 | | 输入 | batch_id、FileSummaryItem 列表 | | 输出 | page_count、statistics_status、error_message | | 数据写入 | FileSummaryItem | | 关键规则 | 支持 pdf、doc、docx、xls、xlsx、ppt、pptx;单文件失败最多重试 3 次,仍失败则记录异常并继续 | 页数统计口径: | 文件类型 | 统计口径 | | --- | --- | | pdf | PDF 页面数量 | | doc/docx | 优先转 PDF 后统计页面数量 | | xls/xlsx | Demo 阶段按工作表数量统计 | | ppt/pptx | 按幻灯片数量统计 | ### 4.6 产品信息识别 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 产品信息识别 Skill | | 职责 | 尝试识别产品名称,并用于更新对话标题 | | 输入 | batch_id、文件名、目录名、可读取文本片段 | | 输出 | product_name、confidence、evidence | | 数据写入 | FileSummaryBatch.product_name、Conversation.title | | 关键规则 | 优先从文件名和目录名识别;其次读取文档首页或关键文本;识别失败不阻断流程 | 会话标题规则: | 场景 | 标题处理 | | --- | --- | | 识别到产品名 | 更新为“产品名-文件汇总” | | 未识别产品名 | 保持原对话标题 | | 用户已手动命名 | 可保留用户标题,产品名写入批次信息 | ### 4.7 汇总报告生成 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | 汇总报告生成 Skill | | 职责 | 生成完整 Markdown 报告和对话框展示简表 | | 输入 | batch_id、统计摘要、文件明细、异常清单 | | 输出 | markdown_report_path、assistant_markdown_summary | | 数据写入 | ExportedSummaryFile、Message | | 关键规则 | Markdown 简表需要适合前端对话框渲染;完整报告包含全部文件明细 | ### 4.8 Excel 导出 Skill | 项目 | 说明 | | --- | --- | | 中文名称 | Excel 导出 Skill | | 职责 | 生成 Excel 汇总文件 | | 输入 | batch_id、统计摘要、文件明细 | | 输出 | excel_path、download_url | | 数据写入 | ExportedSummaryFile | | 关键规则 | 至少包含“汇总信息”“文件明细”两个 Sheet | --- ## 五、数据模型设计 ### 5.1 FileSummaryBatch 文件汇总批次,表示一次对话内的文件汇总任务。 | 字段 | 类型 | 说明 | | --- | --- | --- | | id | BigAutoField | 主键 | | conversation | ForeignKey(Conversation) | 绑定对话 | | user | ForeignKey(User) | 上传用户 | | batch_no | CharField | 批次编号 | | product_name | CharField | 识别出的产品名,可为空 | | upload_type | CharField | archive、multi_file | | status | CharField | pending、running、success、failed | | total_files | Integer | 文件总数 | | supported_files | Integer | 支持统计的文件数 | | success_files | Integer | 统计成功数 | | failed_files | Integer | 统计失败数 | | unsupported_files | Integer | 不支持文件数 | | total_pages | Integer | 总页数 | | work_dir | CharField | 批次工作目录 | | error_message | TextField | 批次异常说明 | | created_at | DateTimeField | 创建时间 | | started_at | DateTimeField | 开始时间 | | finished_at | DateTimeField | 完成时间 | ### 5.2 FileAttachment 上传原始文件记录。用户上传即存储为 `FileAttachment`,批次启动时再通过 `FileSummaryBatchAttachment` 固化本次使用的附件版本。 | 字段 | 类型 | 说明 | | --- | --- | --- | | id | BigAutoField | 主键 | | batch | ForeignKey(FileSummaryBatch) | 所属批次 | | original_name | CharField | 原始文件名 | | storage_path | CharField | 保存路径 | | file_size | BigInteger | 文件大小 | | content_type | CharField | MIME 类型 | | created_at | DateTimeField | 上传时间 | ### 5.3 FileSummaryItem 文件明细记录。 | 字段 | 类型 | 说明 | | --- | --- | --- | | id | BigAutoField | 主键 | | batch | ForeignKey(FileSummaryBatch) | 所属批次 | | file_index | Integer | 文件序号 | | directory_level | CharField | 目录层级 | | file_name | CharField | 文件名 | | file_type | CharField | 文件类型 | | relative_path | CharField | 相对路径 | | storage_path | CharField | 实际处理路径 | | page_count | Integer | 页数,可为空 | | statistics_status | CharField | success、failed、unsupported、skipped | | retry_count | Integer | 页数统计重试次数 | | error_message | TextField | 异常说明 | | created_at | DateTimeField | 创建时间 | | updated_at | DateTimeField | 更新时间 | ### 5.4 WorkflowNodeRun 工作流节点运行记录。 | 字段 | 类型 | 说明 | | --- | --- | --- | | id | BigAutoField | 主键 | | batch | ForeignKey(FileSummaryBatch) | 所属批次 | | node_code | CharField | 节点编码 | | node_name | CharField | 节点名称 | | status | CharField | pending、running、retrying、success、failed、skipped | | progress | Integer | 进度百分比 | | message | TextField | 节点提示 | | started_at | DateTimeField | 开始时间 | | finished_at | DateTimeField | 完成时间 | ### 5.5 ExportedSummaryFile 导出文件记录。 | 字段 | 类型 | 说明 | | --- | --- | --- | | id | BigAutoField | 主键 | | batch | ForeignKey(FileSummaryBatch) | 所属批次 | | export_type | CharField | markdown、excel | | file_name | CharField | 文件名 | | storage_path | CharField | 保存路径 | | download_url | CharField | 下载链接 | | status | CharField | success、failed | | error_message | TextField | 导出异常说明 | | created_at | DateTimeField | 生成时间 | --- ## 六、后端接口设计 ### 6.1 上传并启动工作流 | 项目 | 内容 | | --- | --- | | URL | POST /api/review-agent/conversations/{conversation_id}/file-summary/start/ | | 认证 | 登录用户 | | 请求类型 | multipart/form-data | | 请求参数 | files[]、prompt | | 响应 | batch_id、status、workflow_nodes | 处理逻辑: ```text 校验 conversation 属于当前用户 -> 保存上传文件 -> 创建 FileSummaryBatch -> 创建 WorkflowNodeRun 初始节点 -> 启动后台线程执行工作流 -> 返回 batch_id 和初始节点状态 ``` ### 6.2 工作流 SSE 事件流 | 项目 | 内容 | | --- | --- | | URL | GET /api/review-agent/file-summary/{batch_id}/events/ | | 认证 | 登录用户 | | 响应类型 | text/event-stream | 事件类型: | 事件 | 说明 | | --- | --- | | workflow_started | 工作流开始 | | node_started | 节点开始 | | node_progress | 节点进度更新 | | node_retrying | 单文件解析重试 | | node_completed | 节点完成 | | node_failed | 节点失败 | | workflow_completed | 工作流完成 | | workflow_failed | 工作流失败 | 示例: ```json { "batch_id": 12, "node_code": "page_count", "node_name": "解析页数中", "status": "retrying", "progress": 42, "message": "检测报告.pdf 解析失败,正在第 2 次重试" } ``` ### 6.3 查询批次状态 | 项目 | 内容 | | --- | --- | | URL | GET /api/review-agent/file-summary/{batch_id}/ | | 认证 | 登录用户 | | 响应 | 批次摘要、节点状态、文件简表、导出文件链接 | 用途: | 场景 | 说明 | | --- | --- | | 页面刷新恢复 | 前端重新加载后恢复工作流卡片状态 | | 历史记录查看 | 从会话历史进入后展示已完成汇总结果 | ### 6.4 下载导出文件 | 项目 | 内容 | | --- | --- | | URL | GET /api/review-agent/file-summary/exports/{export_id}/download/ | | 认证 | 登录用户 | | 响应 | 文件流 | 权限规则: ```text export_id -> batch -> conversation -> user 必须等于当前登录用户,才允许下载。 ``` --- ## 七、前端设计 ### 7.1 AI 对话页改造 现有 `templates/home.html` 和 `static/js/app.js` 需要增强: | 改造点 | 说明 | | --- | --- | | 附件选择 | 在输入框旁增加文件上传按钮,支持压缩包和多个文件 | | 工作流卡片 | 用户提交后在对话流中插入工作流状态卡片 | | SSE 监听 | 监听后台节点事件,实时更新卡片节点状态 | | Markdown 渲染 | AI 回复支持 Markdown 表格和下载链接渲染 | | 状态恢复 | 页面刷新后查询批次状态,恢复工作流卡片 | ### 7.2 工作流卡片 卡片包含节点列表: | 节点 | 前台展示文案 | | --- | --- | | upload | 上传中 | | extract | 解压中 | | inventory | 扫描中 | | page_count | 解析页数中 | | product_detect | 识别产品名中 | | report/export | 输出中 | | completed | 已完成 | 节点状态样式: | 状态 | 展示 | | --- | --- | | pending | 灰色等待 | | running | 高亮进行中 | | retrying | 黄色重试中 | | success | 绿色完成 | | failed | 红色失败 | | skipped | 灰色跳过 | ### 7.3 对话框结果展示 工作流完成后,AI 对话框新增助手消息,内容为 Markdown: ```markdown 已完成文件目录与页数汇总。 | 指标 | 数量 | | --- | --- | | 文件总数 | 24 | | 统计成功 | 21 | | 统计失败 | 2 | | 不支持 | 1 | | 总页数 | 386 | | 序号 | 目录层级 | 文件名 | 类型 | 页数 | 状态 | 异常说明 | | --- | --- | --- | --- | --- | --- | --- | | 1 | 注册资料/说明书 | 说明书.docx | docx | 12 | 成功 | | | 2 | 注册资料/检测报告 | 检测报告.pdf | pdf | 38 | 成功 | | [下载 Markdown 报告](download-url) [下载 Excel 明细](download-url) ``` --- ## 八、后台服务设计 ### 8.1 WorkflowExecutor 负责批次级工作流编排。 | 方法 | 说明 | | --- | --- | | start(batch_id) | 启动后台任务 | | run(batch_id) | 串行执行节点图 | | run_node(node_code, context) | 执行单个节点 | | emit_event(batch_id, event_type, payload) | 写入并推送事件 | | complete(batch_id) | 完成批次 | | fail(batch_id, error) | 标记批次失败 | ### 8.2 SkillRegistry 负责 Skill 注册与按需加载。 | 方法 | 说明 | | --- | --- | | register(name, skill_cls) | 注册 Skill | | get(name) | 获取 Skill | | run(name, context) | 执行 Skill | ### 8.3 PageCountService 负责具体文件页数统计。 | 方法 | 说明 | | --- | --- | | count_pdf(path) | 统计 PDF 页面数 | | count_word(path) | doc/docx 转 PDF 后统计页面数 | | count_excel(path) | 统计工作表数量 | | count_ppt(path) | 统计幻灯片数量 | | count_with_retry(item, max_retry=3) | 单文件重试统计 | ### 8.4 ExportService 负责 Markdown 和 Excel 导出。 | 方法 | 说明 | | --- | --- | | build_markdown_report(batch) | 生成完整 Markdown 报告 | | build_chat_summary(batch) | 生成对话简表 | | build_excel(batch) | 生成 Excel 明细 | | create_download_record(batch, path, type) | 创建下载记录 | --- ## 九、异常与重试设计 ### 9.1 批次级失败 | 场景 | 处理 | | --- | --- | | 上传保存失败 | 批次不创建或标记失败 | | 压缩包无法解压 | 批次失败,工作流终止 | | 文件清单为空 | 批次失败,提示未检测到可处理文件 | | 报告导出失败 | 批次失败或标记导出异常 | ### 9.2 文件级失败 | 场景 | 处理 | | --- | --- | | 单文件页数解析失败 | 最多重试 3 次 | | 重试仍失败 | statistics_status=failed,记录异常说明,继续处理其他文件 | | 不支持类型 | statistics_status=unsupported,不重试 | | 加密或损坏文件 | statistics_status=failed,记录“文件加密或损坏” | --- ## 十、安全设计 | 设计点 | 说明 | | --- | --- | | 对话隔离 | 所有批次查询和下载必须校验 conversation.user | | 防串文件 | 工作流只能读取当前 batch 通过 FileSummaryBatchAttachment 绑定的 FileAttachment | | 解压安全 | 禁止压缩包内路径跳出批次工作目录 | | 文件执行安全 | 不执行上传文件中的脚本、宏或外部链接 | | 下载权限 | 下载接口必须验证当前用户拥有批次所属对话 | | 存储隔离 | 按 user_id/conversation_id/batch_id 建立存储目录 | --- ## 十一、验收设计 | 序号 | 验收项 | 验收标准 | | --- | --- | --- | | 1 | 对话绑定 | A 对话上传的文件不会出现在 B 对话的汇总结果中 | | 2 | 压缩包处理 | 支持 zip、rar、7z 常见压缩包解压并保留目录结构 | | 3 | 多文件处理 | 支持一次上传多个散装文件并生成同一批次结果 | | 4 | 工作流卡片 | 前台能实时展示上传中、解压中、扫描中、解析中、输出中、完成状态 | | 5 | 解析重试 | 单文件解析失败最多重试 3 次,失败后记录异常并继续 | | 6 | Markdown 展示 | 对话框能正确渲染 Markdown 表格和下载链接 | | 7 | 导出下载 | Markdown 报告和 Excel 明细可通过对话框链接下载 | | 8 | 数据存档 | 数据库保留批次、上传文件、节点状态、文件明细、导出文件记录 | | 9 | 标题更新 | 识别到产品名后,可将会话标题更新为“产品名-文件汇总” | --- ## 十二、待确认事项 | 序号 | 问题 | 当前建议 | 状态 | | --- | --- | --- | --- | | 1 | 是否接入真实 LangGraph 依赖 | Demo 先按 LangGraph 节点图思想自实现轻量编排器 | 待确认 | | 2 | rar/7z 解压依赖 | 可选 py7zr、rarfile、系统 7z 命令 | 待技术验证 | | 3 | doc/docx 转 PDF 依赖 | 建议使用 LibreOffice headless | 待技术验证 | | 4 | 用户手动命名对话时是否允许覆盖 | 建议不覆盖,仅写入产品名字段 | 待确认 | | 5 | 后台任务是否需要取消能力 | Demo 可不做,正式版建议支持取消 | 待确认 | --- ## 十三、实施建议 1. 先补充数据模型和迁移,建立批次、文件明细、节点状态和导出文件表。 2. 增加上传并启动工作流接口,确保文件和当前对话强绑定。 3. 实现轻量 WorkflowExecutor 和 SkillRegistry,先完成 zip、pdf、xlsx、pptx 的主链路。 4. 改造前端对话框,增加附件上传、工作流卡片和 Markdown 渲染。 5. 补齐 doc/docx、rar、7z 等依赖能力,再完善异常重试和下载权限测试。