diff --git a/docs/1.需求分析/5.第1章监管信息材料包生成.md b/docs/1.需求分析/5.第1章监管信息材料包生成.md new file mode 100644 index 0000000..091caff --- /dev/null +++ b/docs/1.需求分析/5.第1章监管信息材料包生成.md @@ -0,0 +1,448 @@ +# 第1章监管信息材料包生成需求分析 + +## 文档信息 + +| 项目 | 内容 | +| --- | --- | +| 原始输入 | docs/0.原始材料/目标产品说明书.docx | +| 样例模板 | docs/0.原始材料/第1章 监管信息 | +| 法规材料 | docs/0.原始材料/关于公布体外诊断试剂注册申报资料要求和批准证明文件格式的公告 | +| 功能主题 | 从产品说明书生成第1章监管信息材料包 | +| 工作流名称 | 第1章监管信息材料包生成 | +| 工作流编码 | regulatory_info_package | +| 批次号规则 | RIP-YYYYMMDDHHMMSS-abcdef | +| 分析日期 | 2026-06-10 | +| 分析版本 | V1.0 | + +--- + +## 一、需求背景 + +体外诊断试剂注册申报资料中,第1章监管信息包含监管信息目录、申请表、产品列表、申报前沟通说明、符合标准清单、真实性声明和符合性声明等材料。注册人员通常需要根据产品说明书、企业信息和法规要求手工整理这些文件,容易出现产品名称、包装规格、组成成分、预期用途等字段重复录入、漏填、格式不一致和待补信息不醒目的问题。 + +本需求新增独立工作流:用户上传或选择一个产品说明书后,系统以既有 `第1章 监管信息` 样例文件作为模板,抽取说明书中的产品关键信息,生成一套类似样例目录的第1章监管信息材料包。生成结果以 zip 压缩包作为主下载入口,同时保留单文件辅助下载。 + +该工作流可以复用现有自动填表工作流中已拆分出的字段抽取、LLM 调用、Word 写入、导出下载、批次事件和通知能力,但不并入 `application_form_fill`,而是作为独立工作流建设。 + +--- + +## 二、需求范围 + +### 2.1 本期范围 + +| 序号 | 范围项 | 说明 | +| --- | --- | --- | +| 1 | 独立工作流 | 新增 `regulatory_info_package`,不复用 `application_form_fill` 的 workflow_type | +| 2 | 单说明书输入 | 本期只支持一个产品说明书作为主输入 | +| 3 | 模板复用 | 以 `docs/0.原始材料/第1章 监管信息` 下的样例文件作为生成模板 | +| 4 | 固定输出文件 | 固定生成 7 个第1章监管信息文件 | +| 5 | 代码抽取与 LLM 抽取并行 | 规则/代码抽取与 LLM 结构化抽取并行处理,合并后写入模板 | +| 6 | 尽量多填 | 对说明书中可识别的产品名称、包装规格、预期用途、组成成分、储存条件、适用仪器、样本类型、检测靶标等字段尽量填入 | +| 7 | 缺失项标记 | 系统新填入的缺失项使用 `/`,并设置黄色底色提醒负责人补充 | +| 8 | LLM-only 标记 | 代码抽取未取到但 LLM 抽取到的字段,也需要在输出文件中高亮提示人工复核 | +| 9 | doc 能力增强 | `.doc` 文档需要具备与 `.docx` 等价的原始处理能力,不能只依赖预转换作为唯一方案 | +| 10 | zip 主输出 | 生成 `第1章 监管信息(预生成版).zip` 作为主下载入口,单文件作为辅助下载 | +| 11 | 对话唤起提示 | 在对话框底部增加本工作流的唤起提示词 | +| 12 | LLM 意图判断 | 触发判断不能只依赖固定关键词,需要引入 LLM 判断用户是否要生成第1章监管信息材料包 | + +### 2.2 非本期范围 + +| 序号 | 范围项 | 说明 | +| --- | --- | --- | +| 1 | 多资料综合生成 | 本期不从产品技术要求、检验报告、企业证照等多文件综合生成 | +| 2 | 人工在线编辑 | 本期只生成文件并标记待确认项,不提供网页内字段编辑 | +| 3 | 自动保证法规最终准确 | 标准清单、分类编码、管理类别等无法从说明书确认的信息仍需负责人确认 | +| 4 | 自动提交监管系统 | 本期只生成申报材料包,不对接外部申报平台 | +| 5 | 版式人工校订替代 | 系统尽量保持模板版式,但最终提交前仍需人工核对 | + +--- + +## 三、输入与触发 + +### 3.1 输入文件规则 + +| 场景 | 处理规则 | +| --- | --- | +| 用户上传一个 `.docx` 说明书 | 直接作为本次输入 | +| 用户上传多个文件 | 优先选择文件名包含“说明书”的 `.docx` | +| 多个说明书候选 | 工作流进入待确认状态,提示用户选择 | +| 未找到说明书 | 提示用户上传产品说明书 | +| 非 `.docx` 说明书 | 本期可提示格式不支持,后续扩展 `.doc`、PDF 或 OCR | + +### 3.2 对话触发规则 + +固定提示词需要支持: + +| 触发表达 | 触发结果 | +| --- | --- | +| 根据说明书生成第1章监管信息 | 启动第1章监管信息材料包生成 | +| 生成监管信息材料包 | 启动第1章监管信息材料包生成 | +| 从说明书生成第1章材料 | 启动第1章监管信息材料包生成 | + +除固定表达外,系统需要引入 LLM 意图判断。当用户自然语言表达包含“根据说明书”“第1章”“监管信息”“材料包”“申请表/产品列表/声明”等意图组合时,LLM 可判断为 `regulatory_info_package`。规则命中优先,规则未命中时再进入 LLM 路由,避免只靠固定模板。 + +### 3.3 对话框底部唤起提示 + +对话框底部快捷提示词新增: + +```text +根据说明书生成第1章监管信息 +``` + +后续可追加: + +```text +生成监管信息材料包 +从说明书生成第1章材料 +``` + +--- + +## 四、输出文件范围 + +本期固定生成与样例目录一致的 7 个文件: + +| 序号 | 输出文件 | 模板来源 | 生成规则 | +| --- | --- | --- | --- | +| 1 | CH1.2 监管信息目录.docx | 样例 `CH1.2 监管信息目录.docx` | 替换产品名称,目录结构和页码沿用样例 | +| 2 | CH1.4 申请表.docx | 样例 `CH1.4 申请表.docx` | 尽量填入说明书字段,未知项填 `/` 并黄底 | +| 3 | CH1.5 产品列表.docx | 样例 `CH1.5 产品列表.docx` | 按样例表头重建产品列表,货号留空并黄底 | +| 4 | CH1.9 产品申报前沟通的说明.doc | 样例 `CH1.9 产品申报前沟通的说明.doc` | `.doc` 应支持与 `.docx` 等价替换能力 | +| 5 | CH1.11.1 符合标准的清单.docx | 样例 `CH1.11.1 符合标准的清单.docx` | 从说明书和 RAG/法规知识库提取或推荐标准,非明确项需高亮待确认 | +| 6 | CH1.11.5 真实性声明.docx | 样例 `CH1.11.5 真实性声明.docx` | 保留样例正文结构,替换产品名称,公司名位置黄底 `/` | +| 7 | CH1.11.6 符合性声明.docx | 样例 `CH1.11.6 符合性声明.docx` | 保留样例正文结构,替换产品名称,公司名位置黄底 `/` | + +### 4.1 下载形态 + +| 输出类型 | 要求 | +| --- | --- | +| zip 主入口 | 生成 `第1章 监管信息(预生成版).zip`,只包含成功或兜底成功的文件 | +| 单文件下载 | 每个生成文件均可作为辅助下载项展示 | +| 追溯清单 | 建议生成 JSON/Excel,记录字段来源、抽取方式、高亮原因和待确认项 | + +--- + +## 五、字段抽取与填写规则 + +### 5.1 抽取字段范围 + +系统应从说明书中尽量抽取以下字段: + +| 字段 | 示例来源 | +| --- | --- | +| 产品名称 | `【产品名称】` | +| 包装规格 | `【包装规格】` | +| 预期用途 | `【预期用途】` | +| 检测原理/方法原理 | `【检测原理】` | +| 主要组成成分 | `【主要组成成分】` 及其下方表格 | +| 储存条件及有效期 | `【储存条件及有效期】` | +| 样本类型 | `【样本要求】` 中的适用样本类型 | +| 检测靶标 | 预期用途或检测原理中的基因、病原体、抗原、抗体等 | +| 适用仪器 | `【适用仪器】` | +| 检验方法 | `【检验方法】` | +| 生产日期和使用期限描述 | 储存条件章节 | + +字段抽取采用规则/代码抽取与 LLM 结构化抽取并行模式: + +```text +读取说明书 +-> 规则/代码抽取 +-> LLM 结构化抽取 +-> 字段合并 +-> 标记字段来源和置信度 +-> 写入模板 +``` + +### 5.2 合并与高亮规则 + +| 场景 | 处理规则 | +| --- | --- | +| 代码抽取和 LLM 都命中且结果一致 | 正常写入,不强制高亮 | +| 代码抽取和 LLM 都命中但结果不一致 | 优先按规则配置选择,写入值高亮并进入追溯清单 | +| 代码抽取未命中,LLM 命中 | 写入 LLM 值,并高亮提示人工复核 | +| 代码抽取命中,LLM 未命中 | 正常写入,追溯记录代码抽取来源 | +| 两者均未命中 | 写入 `/` 并设置黄色底色 | +| 企业信息缺失 | 写入 `/` 并设置黄色底色 | + +高亮含义: + +| 高亮类型 | 视觉要求 | 含义 | +| --- | --- | --- | +| 缺失项高亮 | 黄色底色 | 说明书无法提供,负责人需填写 | +| LLM-only 高亮 | 黄色底色,可在追溯清单标记 `llm_only` | 代码未抽到,仅 LLM 推断,需要复核 | +| 冲突高亮 | 黄色底色,可配合红色字体 | 规则结果与 LLM 结果不一致 | + +仅标记系统新填入的缺失项或需复核项。样例模板中原本存在的 `/` 不统一高亮,避免整份文件过度标记。 + +--- + +## 六、各文件生成规则 + +### 6.1 CH1.2 监管信息目录 + +| 项目 | 规则 | +| --- | --- | +| 产品名称 | 替换为说明书抽取的产品名称 | +| 目录条目 | 沿用样例目录结构 | +| 适用情况 | 沿用样例 | +| 资料名称 | 沿用样例 | +| 页码 | 沿用样例页码 | + +### 6.2 CH1.4 申请表 + +| 字段类型 | 规则 | +| --- | --- | +| 产品名称 | 从说明书抽取 | +| 包装规格 | 从说明书抽取 | +| 主要组成成分 | 优先使用说明书组成成分摘要或附件提示 | +| 预期用途 | 从说明书抽取 | +| 产品储存条件及有效期 | 从说明书抽取 | +| 方法原理 | 从说明书检测原理抽取 | +| 产品类别 | 缺失,填 `/` 并黄底 | +| 分类编码 | 缺失,填 `/` 并黄底 | +| 临床评价路径 | 缺失,填 `/` 并黄底 | +| 申请人信息 | 缺失,填 `/` 并黄底 | +| 联系人、法定代表人、邮箱、组织机构代码 | 缺失,填 `/` 并黄底 | +| 生产地址 | 缺失,填 `/` 并黄底 | + +管理类别、分类编码、临床评价路径、UDI、国家标准品/强制标准等不得根据经验自动下结论,全部按待确认处理。 + +### 6.3 CH1.5 产品列表 + +产品列表需要转成样例表头: + +| 包装规格 | 货号 | 组成 | 组分 | 主要组成成分 | 规格/数量 | +| --- | --- | --- | --- | --- | --- | + +生成规则: + +| 字段 | 规则 | +| --- | --- | +| 包装规格 | 从说明书组成成分表的规格列或包装规格章节抽取 | +| 货号 | 说明书未提供,填 `/` 并黄底 | +| 组成 | 根据组分名称推断为反应液、质控品、处理液、增强剂等;无法判断则填 `/` 并黄底 | +| 组分 | 使用说明书表格中的组分名称 | +| 主要组成成分 | 使用说明书表格中的主要组成成分 | +| 规格/数量 | 使用说明书表格中的对应规格数量 | + +目标产品说明书中存在规格A大包装、规格A分管包装、规格B大管包装等多个组成表,系统应尽量展开为多行产品列表。 + +### 6.4 CH1.9 产品申报前沟通的说明 + +`CH1.9` 当前为 `.doc` 格式。本工作流要求 `.doc` 文档具备与 `.docx` 等价的原始功能,即模板复制、文本定位、字段替换、高亮标记、导出和打包均应支持 `.doc`。 + +实现上不应只把转换作为唯一方案。可选技术路径包括: + +| 路径 | 说明 | +| --- | --- | +| 原生 `.doc` 处理 | 优先探索可直接读取和写入 `.doc` 的库、COM 或二进制文档处理能力 | +| Office/COM 自动化 | Windows 环境下通过 Word COM 直接打开 `.doc` 并原格式写入保存 | +| LibreOffice UNO/API | 通过 LibreOffice API 直接处理旧版 Word,而不只作为离线预转换 | +| 转换兜底 | 当原生处理不可用时,可作为兜底手段,但不能作为需求定义中的唯一能力 | + +如运行环境不具备 `.doc` 写入能力,工作流应明确失败原因或降级提示,不应静默输出未改写文件。 + +### 6.5 CH1.11.1 符合标准的清单 + +生成规则: + +| 来源 | 处理方式 | +| --- | --- | +| 说明书明确出现的标准号 | 可直接写入,并记录来源片段 | +| RAG/法规知识库命中的候选标准 | 可作为候选写入或追溯提示,但需高亮待确认 | +| 样例中的标准清单 | 不可无条件沿用 | +| 无法确认的标准 | 填 `/` 并黄底 | + +法规材料目录中存在 `医疗器械注册申报资料和批准证明文件格式要求(体外诊断试剂).doc`、`体外诊断试剂注册申报资料要求及说明.doc`、`体外诊断试剂安全和性能基本原则清单.doc` 等材料。其中安全和性能基本原则清单属于第3章非临床资料,不直接等同于 `CH1.11.1 符合标准的清单`。系统应优先查询已上传 RAG/法规知识库来确认标准清单要求;未命中时不得强行套用样例标准。 + +### 6.6 CH1.11.5 真实性声明 + +| 项目 | 规则 | +| --- | --- | +| 正文结构 | 保留样例结构 | +| 产品名称 | 替换为说明书抽取的产品名称 | +| 公司名/申请人 | 填 `/` 并黄底 | +| 日期 | 使用当天日期 | +| 材料列表 | 沿用样例材料列表 | + +### 6.7 CH1.11.6 符合性声明 + +| 项目 | 规则 | +| --- | --- | +| 正文结构 | 保留样例结构 | +| 产品名称 | 替换为说明书抽取的产品名称 | +| 公司名/申请人 | 填 `/` 并黄底 | +| 日期 | 使用当天日期 | + +--- + +## 七、工作流设计 + +### 7.1 主流程 + +```text +用户上传或选择产品说明书 +-> 用户触发“根据说明书生成第1章监管信息” +-> 系统通过规则和 LLM 判断工作流意图 +-> 创建 regulatory_info_package 批次 +-> 校验输入说明书 +-> 复制第1章监管信息样例模板到批次目录 +-> 抽取说明书文本、段落和表格 +-> 规则/代码抽取字段 +-> LLM 结构化抽取字段 +-> 合并字段并识别缺失、LLM-only 和冲突项 +-> 生成 7 个目标文件 +-> 对缺失项、LLM-only 项和冲突项进行高亮 +-> 生成追溯清单 +-> 打包第1章监管信息 zip +-> 写入导出记录 +-> 对话框展示 zip 主下载入口、单文件下载和待确认摘要 +``` + +### 7.2 节点建议 + +| 节点编码 | 节点名称 | 成功条件 | +| --- | --- | --- | +| prepare | 准备资料 | 找到唯一说明书输入 | +| template_copy | 复制模板 | 7 个样例模板复制到批次目录 | +| text_extract | 抽取说明书 | 提取说明书段落和表格 | +| field_extract | 抽取字段 | 规则和 LLM 抽取结果均留底 | +| field_merge | 合并字段 | 输出最终字段、缺失项、LLM-only 项和冲突项 | +| generate_docs | 生成材料 | 7 个文件生成完成 | +| highlight_review_items | 标记待确认 | 缺失项、LLM-only、冲突项完成高亮 | +| trace_export | 追溯清单 | 生成 JSON/Excel 追溯清单 | +| zip_export | 打包下载 | 生成 `第1章 监管信息(预生成版).zip` | +| completed | 完成 | 更新批次状态并返回下载摘要 | + +### 7.3 状态建议 + +| 状态 | 含义 | +| --- | --- | +| pending | 已创建,等待执行 | +| running | 执行中 | +| waiting_user | 多个说明书或缺少说明书,等待用户确认 | +| success | zip 和必要单文件生成成功 | +| partial_success | zip 已生成,但部分 `.doc`、追溯清单或高亮处理失败 | +| failed | 关键文件均未生成 | + +--- + +## 八、数据与产物 + +### 8.1 批次数据 + +建议新增独立批次模型或等价数据结构,记录: + +| 字段 | 说明 | +| --- | --- | +| batch_no | RIP 批次号 | +| workflow_type | regulatory_info_package | +| conversation | 所属对话 | +| user | 发起用户 | +| trigger_message | 触发消息 | +| source_instruction_file | 输入说明书 | +| product_name | 抽取到的产品名称 | +| status | 批次状态 | +| work_dir | 批次工作目录 | +| missing_fields | 缺失字段清单 | +| llm_only_fields | 仅 LLM 命中的字段 | +| conflict_fields | 冲突字段 | +| risk_notes | `.doc` 处理、标准清单待确认等风险提示 | + +### 8.2 追溯清单 + +追溯清单至少记录: + +| 字段 | 说明 | +| --- | --- | +| target_file | 目标文件 | +| target_field | 目标字段 | +| final_value | 写入值 | +| extraction_source | rule、llm、missing、rag_candidate | +| evidence | 来源片段 | +| highlight_reason | missing、llm_only、conflict、rag_candidate | +| needs_review | 是否需要负责人确认 | + +--- + +## 九、界面与交互 + +### 9.1 对话回复 + +工作流完成后,对话框展示: + +| 信息 | 说明 | +| --- | --- | +| 批次号 | RIP 批次号 | +| 产品名称 | 抽取到的产品名称 | +| 主下载 | `第1章 监管信息(预生成版).zip` | +| 单文件下载 | 7 个文件列表 | +| 待确认摘要 | 缺失字段数、LLM-only 字段数、冲突字段数 | +| `.doc` 状态 | CH1.9 是否成功完成 `.doc` 写入 | +| 标准清单提示 | 标准来源和待确认说明 | + +### 9.2 工作流卡片 + +前端需新增 `regulatory_info_package` 工作流卡片,展示节点状态和导出结果。对话框底部新增快捷唤起提示词: + +```text +根据说明书生成第1章监管信息 +``` + +--- + +## 十、异常与降级 + +| 异常场景 | 处理方式 | +| --- | --- | +| 未上传说明书 | 提示用户上传产品说明书 | +| 多个说明书候选 | 进入 waiting_user,提示选择 | +| 产品名称未抽到 | 目标文件产品名位置填 `/` 并黄底 | +| 企业信息缺失 | 相关位置填 `/` 并黄底 | +| LLM 调用失败 | 使用规则抽取结果继续生成,并记录风险提示 | +| 规则抽取失败 | 使用 LLM 结果继续生成,LLM-only 字段高亮 | +| RAG/法规知识库不可用 | 标准清单不自动套用样例,写入 `/` 并黄底 | +| `.doc` 原生处理失败 | 批次标记 partial_success 或 failed,明确提示 CH1.9 处理失败原因 | +| zip 打包失败 | 保留单文件下载,并提示压缩包生成失败 | + +--- + +## 十一、验收标准 + +| 序号 | 验收项 | 标准 | +| --- | --- | --- | +| 1 | 触发识别 | 用户输入“根据说明书生成第1章监管信息”可启动 `regulatory_info_package` | +| 2 | LLM 路由 | 非固定话术但语义明确时,可由 LLM 判断进入本工作流 | +| 3 | 输入选择 | 单说明书可直接执行,多说明书进入待确认 | +| 4 | 输出文件 | 生成 7 个与样例同名或同语义的第1章文件 | +| 5 | zip 下载 | 生成 `第1章 监管信息(预生成版).zip` 作为主下载入口 | +| 6 | 单文件下载 | 7 个生成文件均可单独下载 | +| 7 | 产品名称替换 | 目录、申请表、声明类文件中的产品名称替换为说明书产品名称 | +| 8 | 产品列表 | CH1.5 使用样例表头展开说明书组成成分,货号填 `/` 并黄底 | +| 9 | 缺失项高亮 | 系统新填入的 `/` 均有黄色底色 | +| 10 | LLM-only 高亮 | 代码未抽到但 LLM 抽到的字段在文件中高亮 | +| 11 | 标准清单 | 不无条件沿用样例标准;无法确认时填 `/` 并黄底 | +| 12 | 日期 | 声明类文件日期使用当天日期 | +| 13 | `.doc` 支持 | CH1.9 `.doc` 具备与 `.docx` 等价的处理能力,失败时明确提示 | +| 14 | 追溯清单 | 输出字段来源、抽取方式和高亮原因 | +| 15 | 权限隔离 | 用户只能访问自己对话下的批次和导出文件 | + +--- + +## 十二、已确认结论 + +| 编号 | 结论 | +| --- | --- | +| D1 | 输出范围固定为样例第1章监管信息目录下的 7 个文件 | +| D2 | 样例文件作为模板使用,不只是效果参考 | +| D3 | 企业信息、申请人信息缺失时不沿用样例公司,填 `/` 并黄底 | +| D4 | 管理类别、分类编码、临床评价路径等无法从说明书确认的信息填 `/` 并黄底 | +| D5 | 产品列表货号留空,填 `/` 并黄底 | +| D6 | 标准清单不得无条件沿用样例,优先从说明书和 RAG/法规知识库确认 | +| D7 | 声明日期使用当天日期 | +| D8 | 新建独立工作流,可复用原自动填表工作流拆出的 skill/service | +| D9 | 需求分析文档新增为 `docs/1.需求分析/5.第1章监管信息材料包生成.md` | +| D10 | zip 作为主入口,单文件作为辅助下载 | +| D11 | 对话框底部增加工作流唤起提示词 | +| D12 | `.doc` 要实现与 `.docx` 等价能力,不能只依赖转换作为需求唯一方案 | +| D13 | 触发判断需要引入 LLM,不只依赖固定关键词 | diff --git a/docs/2.功能设计/5.第1章监管信息材料包生成.md b/docs/2.功能设计/5.第1章监管信息材料包生成.md new file mode 100644 index 0000000..348812b --- /dev/null +++ b/docs/2.功能设计/5.第1章监管信息材料包生成.md @@ -0,0 +1,860 @@ +# 第1章监管信息材料包生成功能设计 + +## 文档信息 + +| 项目 | 内容 | +| --- | --- | +| 需求分析文档 | docs/1.需求分析/5.第1章监管信息材料包生成.md | +| 参考功能设计 | docs/2.功能设计/3.产品关键信息提取与申报文件自动填表.md | +| 功能名称 | 第1章监管信息材料包生成 | +| 工作流编码 | regulatory_info_package | +| 所属模块 | 审核智能体 review_agent | +| 设计日期 | 2026-06-10 | +| 设计版本 | V1.0 | + +--- + +## 一、设计目标 + +新增独立工作流 `regulatory_info_package`,用于根据产品说明书生成第1章监管信息材料包。用户在对话中上传或选择一个产品说明书,发送“根据说明书生成第1章监管信息”等指令后,系统复制 `docs/0.原始材料/第1章 监管信息` 下的 7 个样例模板,抽取说明书中的产品关键信息,生成一套新的第1章监管信息文件,并打包为 `第1章 监管信息(预生成版).zip` 作为主下载入口。 + +本功能与 `application_form_fill` 平级,不复用其 workflow_type 和批次表;但复用其已形成的服务思想和部分可拆能力,包括字段抽取、LLM 调用、Word 写入、追溯清单、导出下载、通知、工作流事件和前端卡片。 + +本期重点实现: + +| 目标 | 说明 | +| --- | --- | +| 独立工作流 | 新增 `regulatory_info_package` 批次、节点和卡片 | +| 单说明书输入 | 直接从当前对话 active 附件中选择唯一说明书;兼容最近成功文件汇总批次 | +| 模板驱动 | 通过 YAML 配置维护 7 个模板、字段映射和生成策略 | +| 规则 + LLM 并行抽取 | 代码抽取与 LLM 抽取并行,合并后写入模板 | +| 待确认高亮 | 系统新填入的 `/`、LLM-only 字段、冲突字段均高亮 | +| `.doc` 等价处理 | 设计 `LegacyWordDocumentService`,提供与 `.docx` 一致的文档操作接口 | +| zip 主输出 | 扩展 `ExportedSummaryFile.ExportType.ZIP`,统一下载权限 | +| LLM 意图路由 | 扩展路由 action,支持固定话术和 LLM 语义判断 | + +--- + +## 二、规范依据与裁决 + +| 规范来源 | 命中内容 | 设计处理 | +| --- | --- | --- | +| GYRX 后端开发规范 | 服务层职责清晰、接口响应统一、记录必要日志 | Django 项目沿用现有 JsonResponse/SSE 模式;服务拆入独立模块,记录批次与节点日志 | +| GYRX 前端开发规范 | 前端样式复用、交互一致、下载图标语义 | 当前项目为 Django 模板 + 原生 JS,按现有工具 chip、工作流卡片和下载链接风格扩展 | +| 既有自动填表设计 | 独立工作流、YAML 配置、字段抽取、追溯清单、导出记录 | 复用模式,不复用批次表和 workflow_type | +| 需求分析确认 | `.doc` 不只依赖转换、zip 主入口、LLM-only 高亮 | 在服务抽象和验收标准中作为强约束 | + +冲突裁决:GYRX 规范中部分 Java/Spring 约束不适用于当前 Django 项目,按当前项目既有 Django 架构落地;通用原则如服务拆分、日志、权限和前端交互一致性继续采用。 + +--- + +## 三、与既有功能关系 + +### 3.1 复用边界 + +| 能力 | 处理方式 | 现有代码/模块 | +| --- | --- | --- | +| 对话与消息 | 复用 | `Conversation`、`Message`、`stream_message` | +| 附件上传 | 复用 | `FileAttachment`、`file_summary.storage` | +| 文件汇总结果 | 兼容复用 | `FileSummaryBatch`、`FileSummaryItem` | +| 文本抽取 | 复用并扩展 | `regulatory_review/services/text_extract.py`、`rag_index.py` | +| LLM 调用 | 复用 | `review_agent/llm.py` | +| 知识库搜索 | 复用系统现有能力 | `knowledge_base.py`、法规 RAG 相关服务 | +| 导出下载 | 扩展复用 | `ExportedSummaryFile`、`file_summary.views.export_download` | +| 工作流事件 | 复用 | `WorkflowNodeRun`、`WorkflowEvent` | +| 通知 | 复用统一通知链路 | `review_agent.notifications` | +| 前端卡片 | 扩展复用 | `templates/home.html`、`static/js/app.js` | + +### 3.2 新增边界 + +| 能力 | 说明 | +| --- | --- | +| 独立批次 | 新增 `RegulatoryInfoPackageBatch`,批次号 `RIP-...` | +| 独立产物 | 新增 `RegulatoryInfoPackageArtifact` 记录模板副本、抽取结果、生成文件、zip 和追溯清单 | +| 独立通知记录 | 新增 `RegulatoryInfoPackageNotificationRecord`,结构与自动填表通知保持一致 | +| 模板配置 | 新增 `regulatory_info_package_templates_v1.yaml` | +| 说明书选择 | 新增输入选择服务,优先从 active 附件选择,兼容文件汇总批次 | +| 材料包生成 | 新增 7 个文件的生成策略和 zip 打包服务 | +| `.doc` 适配 | 新增旧版 Word 文档适配层 | + +--- + +## 四、总体架构 + +### 4.1 目录结构 + +新增模块: + +```text +review_agent/ + regulatory_info_package/ + __init__.py + constants.py + schemas.py + storage.py + events.py + workflow.py + views.py + services/ + __init__.py + input_select.py + template_config.py + template_repository.py + instruction_extract.py + field_extract.py + field_merge.py + standard_candidates.py + document_writer.py + docx_document.py + legacy_doc_document.py + package_generate.py + traceability_export.py + zip_export.py + summary.py + notifier.py + templates/ + regulatory_info_package_templates_v1.yaml + prompts/ + field_extract.md + router_intent.md + standard_candidate.md +``` + +### 4.2 逻辑架构 + +```mermaid +flowchart TD + A["AI 对话页"] --> B["意图路由"] + B --> C{"action = regulatory_info_package"} + C --> D["RegulatoryInfoPackageBatch"] + D --> E["RegulatoryInfoPackageWorkflowExecutor"] + E --> F["输入说明书选择"] + E --> G["模板配置 YAML"] + F --> H["说明书文本与表格抽取"] + H --> I1["规则/代码抽取"] + H --> I2["LLM 结构化抽取"] + I1 --> J["字段合并与高亮决策"] + I2 --> J + J --> K["标准候选服务"] + J --> L["材料包生成服务"] + K --> L + L --> M1["DOCX 文档适配器"] + L --> M2["Legacy DOC 文档适配器"] + M1 --> N["7 个目标文件"] + M2 --> N + N --> O["追溯清单"] + N --> P["ZIP 打包"] + O --> Q["ExportedSummaryFile"] + P --> Q + E --> R["WorkflowEvent/SSE"] + E --> S["通知服务"] +``` + +### 4.3 技术选型 + +| 设计项 | 本期方案 | 说明 | +| --- | --- | --- | +| Web 框架 | Django | 沿用当前项目 | +| 工作流执行 | 轻量 Executor + 后台线程 | 与文件汇总、法规核查、自动填表一致 | +| 工作流状态 | `WorkflowNodeRun`、`WorkflowEvent` | 使用 `workflow_type=regulatory_info_package` | +| 模板配置 | YAML | 便于维护 7 个模板和字段映射 | +| `.docx` 操作 | `python-docx` | 表格、段落、run、底色和字体可控 | +| `.doc` 操作 | 适配器抽象 | Python 标准库不支持 `.doc` 二进制 Word 写入;设计为 COM/UNO/第三方库适配器 | +| zip 打包 | Python `zipfile` 标准库 | 标准库可满足打包需求 | +| Excel 追溯 | `openpyxl` | 复用现有依赖 | +| LLM | `review_agent.llm.generate_completion` | 统一模型调用 | +| 知识库 | 系统现有知识库/RAG | 不新增单独 RAG 模块 | + +关于 `.doc`:Python 自带库不能实现类似 Apache POI HWPF 的 Word 97-2003 二进制文档完整读写。项目依赖中有 `olefile`,可读取 OLE 复合文档结构,但不足以可靠修改 Word 文本、表格和样式。因此设计上必须使用文档适配器屏蔽实现差异,底层可选 Word COM、LibreOffice UNO、专用第三方库或受控转换兜底。 + +--- + +## 五、触发与路由设计 + +### 5.1 action 扩展 + +`skill_router.py` 扩展: + +| 项 | 设计 | +| --- | --- | +| 新 action | `regulatory_info_package` | +| 新属性 | `starts_regulatory_info_package` | +| ROUTE_ACTIONS | 增加 `regulatory_info_package` | +| LLM prompt | 描述该 action 用于“根据说明书生成第1章监管信息、监管信息材料包、申请表/产品列表/声明材料包” | + +### 5.2 固定规则 + +规则预判关键词: + +```python +REGULATORY_INFO_PACKAGE_TRIGGER_KEYWORDS = [ + "根据说明书生成第1章监管信息", + "生成监管信息材料包", + "从说明书生成第1章材料", + "第1章监管信息", + "监管信息材料包", +] +``` + +规则命中时直接进入本工作流。规则未命中时,继续走 LLM 路由判断,避免自然表达漏触发。 + +### 5.3 对话启动 + +`review_agent/services.py::stream_message` 增加分支: + +```text +if route.starts_regulatory_info_package: + -> 选择说明书输入 + -> 创建 RegulatoryInfoPackageBatch + -> start_regulatory_info_package_workflow + -> SSE workflow_started + -> 回复“已启动第1章监管信息材料包生成工作流,批次号:RIP-...” +``` + +如果没有 active 附件,也没有可复用的最近文件汇总批次,则回复“请先上传产品说明书”。 +如果存在多个候选说明书且用户消息无法唯一命中文件名,则不展示选择弹窗,由对话反问用户确认具体文件名后再启动工作流。 + +--- + +## 六、输入选择设计 + +### 6.1 选择优先级 + +| 优先级 | 来源 | 规则 | +| --- | --- | --- | +| 1 | 用户消息指定文件名 | 按 active 附件名或可复用文件名模糊匹配,唯一命中则使用 | +| 2 | 当前对话 active 附件 | 文件名包含“说明书”且扩展名为 `.docx` | +| 3 | 当前对话 active 附件 | 唯一 `.docx` 文件 | +| 4 | 最近成功 `FileSummaryBatch.items` | 文件名包含“说明书”且扩展名为 `.docx` | +| 5 | 无法唯一选择 | 对话反问用户确认使用哪个说明书;必要时批次进入 `waiting_user` | + +本期直接输入只支持 `.docx` 产品说明书。`.doc`、PDF、扫描件说明书作为后续扩展;但输出模板中的 `.doc` 必须支持。 + +### 6.2 输入绑定 + +批次记录: + +| 字段 | 来源 | +| --- | --- | +| source_attachment | 直接选择的 FileAttachment | +| source_summary_batch | 可选,来自最近成功文件汇总 | +| source_summary_item | 可选,来自汇总条目 | +| source_file_name | 原始说明书文件名 | +| source_storage_path | 说明书存储路径 | + +--- + +## 七、模板配置设计 + +配置路径: + +```text +review_agent/regulatory_info_package/templates/regulatory_info_package_templates_v1.yaml +``` + +配置结构: + +```yaml +version: regulatory_info_package_templates_v1 +source_dir: docs/0.原始材料/第1章 监管信息 +output_zip_name: 第1章 监管信息(预生成版).zip +templates: + - code: ch1_2_directory + output_name: CH1.2 监管信息目录.docx + source_file: CH1.2 监管信息目录.docx + file_format: docx + strategy: directory + include_in_zip: true + fields: + - key: product_name + targets: + - type: paragraph_contains_replace + match: 呼吸道合胞病毒、肺炎支原体核酸检测试剂盒(荧光PCR法) + - code: ch1_4_application_form + output_name: CH1.4 申请表.docx + source_file: CH1.4 申请表.docx + file_format: docx + strategy: application_form + include_in_zip: true + - code: ch1_9_pre_submission + output_name: CH1.9 产品申报前沟通的说明.doc + source_file: CH1.9 产品申报前沟通的说明.doc + file_format: doc + strategy: pre_submission + require_legacy_doc_native: true + include_in_zip: true +``` + +### 7.1 配置项说明 + +| 配置项 | 说明 | +| --- | --- | +| version | 配置版本,写入批次 | +| source_dir | 样例模板目录 | +| output_zip_name | zip 主输出文件名 | +| templates | 7 个目标模板 | +| code | 模板编码 | +| output_name | 生成文件名 | +| source_file | 样例文件 | +| file_format | docx/doc | +| strategy | 生成策略 | +| include_in_zip | 是否进入 zip | +| fields | 字段映射与替换目标 | +| require_legacy_doc_native | `.doc` 是否要求原生处理能力 | + +--- + +## 八、字段抽取设计 + +### 8.1 说明书解析 + +`instruction_extract.py` 输出: + +| 数据 | 说明 | +| --- | --- | +| paragraphs | 按顺序提取段落 | +| sections | 按 `【章节名】` 切分 | +| tables | 提取表格二维数据 | +| component_tables | 识别主要组成成分表 | +| front_text | 前 4000 字,供 LLM 使用 | + +### 8.2 规则抽取 + +规则抽取覆盖: + +| 字段 | 规则 | +| --- | --- | +| product_name | `【产品名称】` 下一段 | +| package_specification | `【包装规格】` 到下一章节 | +| intended_use | `【预期用途】` 到下一章节 | +| detection_principle | `【检测原理】` 到下一章节 | +| main_components | `【主要组成成分】` 表格摘要 | +| storage_condition_and_validity | `【储存条件及有效期】` 到下一章节 | +| sample_type | `样本要求` 中“适用样本类型” | +| detection_targets | 从预期用途/检测原理中抽取基因、病原体、靶标 | +| applicable_instruments | `【适用仪器】` 到下一章节 | +| test_method | `【检验方法】` 摘要 | +| standards | 正则抽取 `GB/T`、`YY/T`、`YY`、`GB` 等标准号 | + +### 8.3 LLM 抽取 + +LLM prompt 要求只输出 JSON: + +```json +{ + "fields": [ + { + "key": "product_name", + "label": "产品名称", + "value": "...", + "evidence": "...", + "confidence": 0.9 + } + ], + "product_list_rows": [ + { + "package_specification": "...", + "composition": "...", + "component_name": "...", + "main_component": "...", + "quantity": "..." + } + ], + "standards": [] +} +``` + +LLM 不允许填企业信息、分类编码、管理类别、临床评价路径等说明书无法证明的内容。 + +### 8.4 字段合并 + +`field_merge.py` 输出 `MergedField`: + +| 字段 | 说明 | +| --- | --- | +| key | 字段编码 | +| label | 中文名 | +| value | 最终写入值 | +| source | rule、llm、missing、conflict | +| evidence | 来源片段 | +| confidence | 置信度 | +| highlight_reason | none、missing、llm_only、conflict、rag_candidate | +| needs_review | 是否需人工复核 | + +合并规则: + +| 场景 | 处理 | +| --- | --- | +| rule 与 LLM 一致 | 采用值,不高亮 | +| rule 与 LLM 不一致 | 采用规则优先或配置优先,标记 conflict | +| rule 缺失、LLM 命中 | 采用 LLM 值,标记 llm_only | +| 全部缺失 | 写 `/`,标记 missing | + +--- + +## 九、文档生成设计 + +### 9.1 文档适配器接口 + +`document_writer.py` 定义统一接口: + +```python +class DocumentAdapter: + def replace_text(self, old: str, new: str, *, highlight: bool = False) -> int: ... + def fill_table_cell(self, row_label: str, value: str, *, highlight: bool = False) -> bool: ... + def replace_table(self, marker: str, rows: list[dict], *, highlight_columns: list[str] = None) -> bool: ... + def highlight_value(self, value: str, reason: str) -> int: ... + def save(self, path: Path) -> Path: ... +``` + +`.docx` 使用 `DocxDocumentAdapter`。`.doc` 使用 `LegacyDocDocumentAdapter`。 + +### 9.2 `.docx` 处理 + +能力: + +| 能力 | 实现 | +| --- | --- | +| 段落替换 | 遍历 paragraph runs | +| 表格行填充 | 按首列 label 定位 | +| 单元格高亮 | `w:shd` 黄色底色 | +| 字体颜色 | 冲突项可红色字体 | +| 产品列表重建 | 清空目标表格数据行后追加 | +| 声明日期替换 | 按日期正则或段落末尾替换 | + +### 9.3 `.doc` 处理 + +设计 `LegacyDocDocumentAdapter`,对外提供与 `.docx` 一致能力。底层按可用性选择适配器: + +| 适配器 | 定位 | +| --- | --- | +| `WordComDocAdapter` | Windows + Microsoft Word 环境下优先,直接打开 `.doc`、查找替换、设置高亮并保存 `.doc` | +| `LibreOfficeUnoDocAdapter` | LibreOffice UNO/API 环境下使用,直接操作文档模型 | +| `OleDocReadOnlyAdapter` | 仅可读取时用于诊断,不满足写入验收 | +| `ConversionFallbackAdapter` | 兜底路径,可转换为 `.docx` 后处理,但不能作为唯一实现 | + +功能设计约束: + +| 约束 | 说明 | +| --- | --- | +| 不静默降级 | `.doc` 原生写入失败时必须记录适配器失败原因,随后尝试 `.docx` 兜底;兜底仍失败时该文件失败并触发 partial_success | +| 不只靠转换 | 转换可作为兜底,但设计主路径必须是文档适配器 | +| 能力探测 | 启动时或节点执行时检测适配器可用性 | +| 追溯记录 | 写入 `.doc` 的适配器类型和失败信息写入 artifact metadata | + +### 9.4 7 个文件生成策略 + +| 模板 | 策略服务 | 关键动作 | +| --- | --- | --- | +| CH1.2 监管信息目录 | `generate_directory_doc` | 替换产品名称;页码沿用样例 | +| CH1.4 申请表 | `generate_application_form_doc` | 填表格行;缺失字段 `/` 黄底 | +| CH1.5 产品列表 | `generate_product_list_doc` | 使用样例表头重建产品列表;货号 `/` 黄底 | +| CH1.9 申报前沟通说明 | `generate_pre_submission_doc` | `.doc` 原生替换产品名和公司名;原生失败则输出 `.docx` 兜底文件;两者均失败才不进入 zip | +| CH1.11.1 符合标准清单 | `generate_standard_list_doc` | 说明书标准号直接写;候选/缺失高亮 | +| CH1.11.5 真实性声明 | `generate_authenticity_statement_doc` | 保留正文,替换产品名,公司名 `/` 黄底,日期当天 | +| CH1.11.6 符合性声明 | `generate_compliance_statement_doc` | 保留正文,替换产品名,公司名 `/` 黄底,日期当天 | + +`generate_docs` 节点内部允许多线程并发处理 7 个目标文件。每个文档使用独立模板副本,子线程只返回生成结果,数据库 artifact/export 记录由主线程统一写入,避免并发写库和共享文件冲突。 + +--- + +## 十、标准清单设计 + +系统中已有知识库/RAG 能力,不新增单独 RAG 模块。本功能只新增 `standard_candidates.py` 作为业务服务,调用既有知识库搜索能力。 + +处理规则: + +| 来源 | 处理 | +| --- | --- | +| 说明书明确标准号 | 写入标准清单,记录 `source=instruction` | +| 知识库候选标准 | 可写入候选区或追溯清单,标记 `rag_candidate` 并高亮 | +| 无命中 | 写 `/` 并黄底 | +| 样例标准 | 不无条件沿用 | + +查询建议: + +```text +体外诊断试剂 核酸扩增 检测试剂 标准 清单 +新型冠状病毒 2019-nCoV 核酸检测试剂盒 荧光PCR 标准 +``` + +--- + +## 十一、zip 与导出设计 + +### 11.1 ExportType 扩展 + +`ExportedSummaryFile.ExportType` 增加: + +```python +ZIP = "zip", "ZIP" +``` + +下载 content type 增加: + +```python +"zip": "application/zip" +``` + +### 11.2 导出记录 + +| 文件 | export_category | export_type | +| --- | --- | --- | +| 第1章 监管信息(预生成版).zip | regulatory_info_package | zip | +| 7 个生成文件 | generated_document | word 或 legacy_word | +| 追溯清单 Excel | traceability | excel | + +追溯 JSON 和抽取过程 JSON 只保存到后台 `logs/` 目录和 artifact 记录,不作为用户下载入口。用户侧只提供追溯 Excel 下载。 + +如果不新增 `legacy_word` export_type,则 `.doc` 也可暂用 `word`,通过文件扩展名和 content type 判断下载 MIME。功能设计建议新增 content type 映射时按扩展名兜底,避免 `.doc` 被当作 `.docx`。 + +### 11.3 权限 + +`file_summary.views._export_for_user` 增加: + +```text +if exported.workflow_type == "regulatory_info_package": + 查询 RegulatoryInfoPackageBatch + 校验 conversation__user == request.user 且 is_deleted=False +``` + +--- + +## 十二、数据模型设计 + +### 12.1 RegulatoryInfoPackageBatch + +```python +class RegulatoryInfoPackageBatch(models.Model): + class Status(models.TextChoices): + PENDING = "pending", "待执行" + RUNNING = "running", "执行中" + WAITING_USER = "waiting_user", "等待用户" + SUCCESS = "success", "成功" + PARTIAL_SUCCESS = "partial_success", "部分成功" + FAILED = "failed", "失败" + CANCELLED = "cancelled", "已取消" +``` + +字段建议: + +| 字段 | 类型 | 说明 | +| --- | --- | --- | +| conversation | FK Conversation | 所属对话 | +| user | FK User | 发起用户 | +| trigger_message | FK Message | 触发消息 | +| source_attachment | FK FileAttachment | 直接选中的说明书附件 | +| source_summary_batch | FK FileSummaryBatch | 可选文件汇总批次 | +| source_summary_item_id | PositiveBigIntegerField | 可选汇总条目 ID | +| batch_no | CharField unique | RIP 批次号 | +| status | CharField | 状态 | +| source_file_name | CharField | 说明书原文件名 | +| source_storage_path | CharField | 说明书路径 | +| product_name | CharField | 抽取产品名 | +| output_zip_name | CharField | zip 文件名 | +| generated_files | JSONField | 7 个文件状态 | +| missing_fields | JSONField | 缺失项 | +| llm_only_fields | JSONField | LLM-only 项 | +| conflict_fields | JSONField | 冲突项 | +| risk_notes | JSONField | 风险提示 | +| template_config_version | CharField | 配置版本 | +| template_config_hash | CharField | 配置 hash | +| adapter_summary | JSONField | `.doc`/`.docx` 适配器信息 | +| work_dir | CharField | 工作目录 | +| error_message | TextField | 错误信息 | +| started_at/finished_at | DateTimeField | 执行时间 | +| is_deleted | BooleanField | 软删除 | + +索引: + +| 索引 | 字段 | +| --- | --- | +| idx_ra_rip_batch_conv_status | conversation, status | +| idx_ra_rip_batch_user_created | user, created_at | +| idx_ra_rip_batch_attachment | source_attachment | +| idx_ra_rip_batch_summary | source_summary_batch | + +### 12.2 RegulatoryInfoPackageArtifact + +产物类型: + +| 类型 | 说明 | +| --- | --- | +| template_copy | 模板副本 | +| instruction_extract | 说明书抽取结果 | +| field_extract_result | 字段抽取结果 | +| merged_fields | 合并字段 | +| generated_document | 生成文件 | +| traceability | 追溯清单 | +| zip_package | zip 包 | +| notification_record | 通知记录 | + +字段与 `ApplicationFormFillArtifact` 保持一致:`batch`、`artifact_type`、`file_format`、`name`、`file_name`、`storage_path`、`file_size`、`content_hash`、`metadata`、`created_by_node`、`is_deleted`。 + +`file_format` 增加 `DOC`、`ZIP`。 + +### 12.3 RegulatoryInfoPackageNotificationRecord + +结构对齐 `ApplicationFormFillNotificationRecord`: + +| 字段 | 说明 | +| --- | --- | +| batch | 所属 RIP 批次 | +| recipient | 通知对象 | +| channel | feishu_cli、feishu_api、mock | +| export_ids | 导出 ID | +| message_summary | 通知摘要 | +| send_status | pending、success、failed | +| retry_count | 重试次数 | +| external_message_id | 外部消息 ID | +| error_message | 错误 | +| sent_at | 发送时间 | + +--- + +## 十三、工作流设计 + +### 13.1 节点定义 + +| 节点编码 | 节点名称 | 触发服务 | 成功条件 | 失败处理 | +| --- | --- | --- | --- | --- | +| prepare | 准备资料 | `RegulatoryInfoPackageWorkflowExecutor` | 找到唯一说明书 | 缺失或多候选进入 waiting_user | +| template_copy | 复制模板 | `TemplateRepository` | 7 个模板进入批次目录 | 缺关键模板则 failed | +| text_extract | 抽取说明书 | `InstructionExtractService` | 提取文本、章节和表格 | 失败则 failed | +| field_extract | 抽取字段 | `FieldExtractionService` | 规则/LLM 结果留底 | LLM 失败可继续 | +| field_merge | 合并字段 | `FieldMergeService` | 输出 merged_fields | 无产品名仍继续,产品名 `/` | +| generate_docs | 生成材料 | `PackageGenerateService` | 生成 7 个文件 | 单文件失败可 partial_success | +| highlight_review_items | 标记待确认 | 文档适配器 | 缺失/LLM-only/冲突完成高亮 | 失败则对应文件失败 | +| trace_export | 追溯清单 | `TraceabilityExportService` | 生成 Excel/JSON | 不阻断 zip | +| zip_export | 打包下载 | `ZipExportService` | 生成 zip 并创建导出记录 | zip 失败则保留单文件 | +| notify | 通知 | `Notifier` | 写通知记录 | 不阻断下载 | +| completed | 完成 | Executor | 状态落定、摘要写入对话 | - | + +### 13.2 状态落定 + +| 结果 | 批次状态 | +| --- | --- | +| 7 个文件、zip、追溯清单均成功 | success | +| zip 成功但部分单文件/追溯/通知失败 | partial_success | +| 单文件成功但 zip 失败 | partial_success | +| 关键输入或模板缺失 | failed 或 waiting_user | +| 所有目标文件生成失败 | failed | + +--- + +## 十四、接口设计 + +### 14.1 URL + +```text +GET /api/review-agent/regulatory-info-package/health/ +POST /api/review-agent/regulatory-info-package/start/ +GET /api/review-agent/regulatory-info-package//status/ +POST /api/review-agent/regulatory-info-package//select-input/ +``` + +### 14.2 start + +请求: + +```json +{ + "conversation_id": 1, + "attachment_id": 10, + "file_summary_batch_id": 20, + "source_summary_item_id": 30 +} +``` + +响应: + +```json +{ + "batch_id": 1, + "workflow_type": "regulatory_info_package", + "batch_no": "RIP-20260610153000-abcdef", + "status": "pending" +} +``` + +### 14.3 status + +响应包含: + +| 字段 | 说明 | +| --- | --- | +| batch | 批次基础信息、产品名、缺失数、LLM-only 数、冲突数 | +| nodes | 工作流节点 | +| generated_files | 7 个文件状态 | +| exports | zip、单文件、追溯清单下载 | +| missing_fields | 缺失项摘要 | +| llm_only_fields | LLM-only 摘要 | +| conflict_fields | 冲突摘要 | +| risk_notes | 风险提示 | +| notifications | 通知记录 | + +--- + +## 十五、前端设计 + +### 15.1 对话框底部快捷提示 + +`templates/home.html` 增加 tool chip: + +```text +根据说明书生成第1章监管信息 +``` + +点击后填入 prompt,不自动发送,保持现有交互一致。 + +### 15.2 工作流卡片 + +`build_workflow_cards()` 增加 RIP 批次,前端复用现有卡片样式,展示: + +| 信息 | 说明 | +| --- | --- | +| 批次号 | RIP-... | +| 状态 | pending/running/success/partial_success/failed | +| 风险摘要 | 缺失字段 N、LLM复核 N、提示 N | +| 节点 | RIP 节点 | + +### 15.3 状态轮询 + +`summaryPanel` 增加: + +```html +data-regulatory-info-package-status-url-template="/api/review-agent/regulatory-info-package/__batch_id__/status/" +``` + +`static/js/app.js` 在工作流类型判断中增加 `regulatory_info_package`。 + +### 15.4 结果展示 + +状态 payload 中 `exports` 按类别展示: + +| 类别 | 展示 | +| --- | --- | +| zip | 主下载按钮 | +| generated_document | 单文件下载列表 | +| traceability | 追溯清单下载 | + +--- + +## 十六、通知设计 + +复用统一通知服务,新增 `build_regulatory_info_package_context(batch)`: + +| 摘要项 | 说明 | +| --- | --- | +| 工作流 | 第1章监管信息材料包生成 | +| 批次号 | RIP-... | +| 产品名称 | 抽取产品名 | +| 导出文件 | zip + 单文件数量 | +| 待确认 | 缺失项、LLM-only、冲突项数量 | +| 下载提示 | 进入系统下载 zip | + +通知失败不影响下载。 + +--- + +## 十七、异常与降级 + +| 异常 | 处理 | +| --- | --- | +| 未找到说明书 | 返回提示,不创建或创建 waiting_user 批次 | +| 多说明书候选 | waiting_user,等待选择 | +| YAML 配置错误 | failed,提示配置错误 | +| 样例模板缺失 | failed,列出缺失模板 | +| LLM 失败 | 使用规则抽取继续,写 risk_notes | +| 规则抽取为空 | 使用 LLM-only 继续并高亮 | +| 知识库不可用 | 标准清单填 `/` 并高亮,写 risk_notes | +| `.doc` 适配器不可用 | CH1.9 失败,批次 partial_success 或 failed,明确原因 | +| zip 打包失败 | 保留单文件下载,状态 partial_success | +| 下载文件不存在 | 返回 404,记录日志 | + +--- + +## 十八、安全与权限 + +| 控制点 | 设计 | +| --- | --- | +| 批次访问 | `conversation__user == request.user` | +| 附件访问 | 附件必须属于当前对话和当前用户 | +| 汇总批次访问 | 批次必须属于当前对话和当前用户 | +| 导出下载 | `workflow_type=regulatory_info_package` 时反查 RIP 批次 | +| 工作目录 | `media/regulatory_info_package/{user_id}/{conversation_id}/{batch_no}` | +| 路径安全 | 所有复制/输出路径必须校验位于批次工作目录内 | +| 原始模板保护 | 只读复制,不允许覆盖 `docs/0.原始材料` | + +--- + +## 十九、测试设计 + +| 测试文件 | 覆盖 | +| --- | --- | +| `tests/test_regulatory_info_package_models.py` | 批次、产物、通知、zip 导出类型 | +| `tests/test_regulatory_info_package_trigger.py` | 固定规则与 LLM 路由 | +| `tests/test_regulatory_info_package_input_select.py` | 说明书选择、多候选 waiting_user | +| `tests/test_regulatory_info_package_template_config.py` | YAML 加载、模板存在性校验 | +| `tests/test_regulatory_info_package_field_extract.py` | 说明书字段、表格、标准号抽取 | +| `tests/test_regulatory_info_package_field_merge.py` | missing、llm_only、conflict 高亮决策 | +| `tests/test_regulatory_info_package_docx_writer.py` | docx 替换、表格填充、黄底 | +| `tests/test_regulatory_info_package_legacy_doc.py` | `.doc` 适配器能力探测和失败提示 | +| `tests/test_regulatory_info_package_zip.py` | zip 只包含 success/fallback_success 文件 | +| `tests/test_regulatory_info_package_workflow.py` | 工作流节点和状态落定 | +| `tests/test_regulatory_info_package_views.py` | start/status/权限 | +| `tests/test_regulatory_info_package_frontend.py` | 卡片、快捷提示、状态 URL | + +回归测试: + +```bash +python manage.py check +pytest tests/test_application_form_fill_*.py tests/test_file_summary_views.py tests/test_regulatory_*tests.py +``` + +实际执行时按项目现有测试命名拆分运行。 + +--- + +## 二十、实施顺序建议 + +| 阶段 | 内容 | +| --- | --- | +| RIP-1 | 模型、迁移、ExportType.ZIP、下载权限 | +| RIP-2 | 模块骨架、YAML 配置、输入说明书选择 | +| RIP-3 | 路由 action、对话启动、工作流节点 | +| RIP-4 | 说明书文本/表格抽取、规则 + LLM 字段抽取 | +| RIP-5 | docx 文档生成、黄底高亮、产品列表重建 | +| RIP-6 | `.doc` 适配器、CH1.9 处理能力 | +| RIP-7 | 追溯清单、zip 导出、助手摘要 | +| RIP-8 | 前端卡片、快捷提示、状态轮询 | +| RIP-9 | 通知、权限、全量回归 | + +--- + +## 二十一、待确认与风险 + +| 风险 | 说明 | 建议 | +| --- | --- | --- | +| `.doc` 原生写入难度 | Python 标准库不支持 Word `.doc` 完整写入 | 优先调研 Word COM 或 LibreOffice UNO;设计适配器隔离风险 | +| 样例模板文本碎片 | Word run 拆分可能导致简单字符串替换失败 | 文档写入服务需支持跨 run 替换 | +| 产品列表结构复杂 | 说明书表格可能存在合并单元格和多规格 | 先覆盖目标说明书结构,再扩展通用表格归一化 | +| 标准清单准确性 | 说明书未必包含标准号,知识库候选不能直接作为结论 | 候选全部高亮并进入追溯清单 | +| LLM-only 风险 | LLM 推断可能过度补全 | 写入但高亮,追溯清单标记需复核 | + +--- + +## 二十二、设计结论 + +| 编号 | 结论 | +| --- | --- | +| D1 | 功能设计文档新增为 `docs/2.功能设计/5.第1章监管信息材料包生成.md` | +| D2 | 新增独立模块 `review_agent/regulatory_info_package/` | +| D3 | 新建独立批次、产物、通知三张表 | +| D4 | 输入选择以 active 附件为主,兼容最近成功文件汇总批次 | +| D5 | `ExportedSummaryFile.ExportType` 扩展 `zip` | +| D6 | 采用 YAML 配置驱动 7 个模板 | +| D7 | `.doc` 通过 `LegacyWordDocumentService` 适配器实现与 `.docx` 等价接口 | +| D8 | 标准候选复用系统已有知识库/RAG,不新增独立 RAG | +| D9 | 前端只扩展现有对话页、工作流卡片、快捷提示和状态轮询 | +| D10 | 本轮先产出功能设计;数据库设计先在本文档中给出,后续可拆成正式数据库设计文档 | diff --git a/docs/3.数据库设计/5.第1章监管信息材料包生成.md b/docs/3.数据库设计/5.第1章监管信息材料包生成.md new file mode 100644 index 0000000..4e0aba9 --- /dev/null +++ b/docs/3.数据库设计/5.第1章监管信息材料包生成.md @@ -0,0 +1,579 @@ +# 第1章监管信息材料包生成数据库设计 + +## 文档信息 + +| 项目 | 内容 | +| --- | --- | +| 需求分析文档 | docs/1.需求分析/5.第1章监管信息材料包生成.md | +| 功能设计文档 | docs/2.功能设计/5.第1章监管信息材料包生成.md | +| 数据库类型 | SQLite / Django ORM | +| 表名前缀 | ra_ | +| 工作流编码 | regulatory_info_package | +| 设计日期 | 2026-06-10 | +| 设计版本 | V1.0 | + +--- + +## 一、设计原则 + +| 原则 | 说明 | +| --- | --- | +| 独立工作流批次 | 第1章监管信息材料包生成使用独立批次表,不复用自动填表批次 | +| 附件优先 | 输入说明书优先绑定 `FileAttachment`,兼容最近成功 `FileSummaryBatch` 与 `FileSummaryItem` | +| 过程产物文件化 | 大 JSON、追溯清单、模板副本、生成文件和 zip 均保存为文件,数据库只保存路径、hash、摘要 | +| 导出记录复用 | zip、单文件、追溯清单继续写入 `ExportedSummaryFile`,统一下载权限 | +| 工作流通用表复用 | 节点状态和 SSE 事件复用 `WorkflowNodeRun`、`WorkflowEvent` | +| 通知独立留痕 | 新增专项通知记录表,结构与自动填表通知记录保持一致 | +| SQLite 兼容 | 使用 Django ORM 常规字段和 JSONField,避免数据库特定语法 | +| 原始模板保护 | 数据库只记录批次工作目录产物,不记录对原始模板的写操作 | + +--- + +## 二、ER 图 + +```mermaid +erDiagram + AUTH_USER ||--o{ CONVERSATION : owns + CONVERSATION ||--o{ MESSAGE : contains + CONVERSATION ||--o{ RA_FILE_ATTACHMENT : has + CONVERSATION ||--o{ RA_REGULATORY_INFO_PACKAGE_BATCH : has + AUTH_USER ||--o{ RA_REGULATORY_INFO_PACKAGE_BATCH : runs + MESSAGE ||--o{ RA_REGULATORY_INFO_PACKAGE_BATCH : triggers + RA_FILE_ATTACHMENT ||--o{ RA_REGULATORY_INFO_PACKAGE_BATCH : provides_instruction + RA_FILE_SUMMARY_BATCH ||--o{ RA_REGULATORY_INFO_PACKAGE_BATCH : optionally_feeds + RA_REGULATORY_INFO_PACKAGE_BATCH ||--o{ RA_REGULATORY_INFO_PACKAGE_ARTIFACT : keeps + RA_REGULATORY_INFO_PACKAGE_BATCH ||--o{ RA_REGULATORY_INFO_PACKAGE_NOTIFICATION_RECORD : sends + RA_REGULATORY_INFO_PACKAGE_BATCH ||--o{ RA_EXPORTED_SUMMARY_FILE : exports + RA_REGULATORY_INFO_PACKAGE_BATCH ||--o{ RA_WORKFLOW_NODE_RUN : tracks + RA_REGULATORY_INFO_PACKAGE_BATCH ||--o{ RA_WORKFLOW_EVENT : emits +``` + +说明:`ra_workflow_node_run`、`ra_workflow_event`、`ra_exported_summary_file` 通过 `workflow_type` 与 `workflow_batch_id` 支持多工作流。本功能统一使用 `workflow_type=regulatory_info_package`。 + +--- + +## 三、表结构设计 + +### 3.1 ra_regulatory_info_package_batch + +一次第1章监管信息材料包生成工作流批次。记录触发来源、输入说明书、产品名称、生成状态、待确认摘要、zip 名称、配置版本和工作目录。 + +| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| id | BigAutoField | integer | 是 | 主键 | +| conversation_id | ForeignKey | bigint | 是 | 所属对话 | +| user_id | ForeignKey | bigint | 是 | 发起用户 | +| trigger_message_id | ForeignKey | bigint | 否 | 触发本工作流的用户消息 | +| source_attachment_id | ForeignKey | bigint | 否 | 直接选中的说明书附件 | +| source_summary_batch_id | ForeignKey | bigint | 否 | 可选,最近成功文件汇总批次 | +| source_summary_item_id | PositiveBigIntegerField | integer | 否 | 可选,文件汇总条目 ID | +| batch_no | CharField(64) | varchar(64) | 是 | 批次编号,格式 `RIP-YYYYMMDDHHMMSS-abcdef`,唯一 | +| status | CharField(30) | varchar(30) | 是 | pending、running、waiting_user、success、partial_success、failed、cancelled | +| source_file_name | CharField(255) | varchar(255) | 否 | 说明书原文件名 | +| source_storage_path | CharField(500) | varchar(500) | 否 | 说明书存储路径 | +| product_name | CharField(200) | varchar(200) | 否 | 抽取到的产品名称 | +| output_zip_name | CharField(255) | varchar(255) | 否 | 主输出 zip 文件名,默认 `第1章 监管信息(预生成版).zip` | +| generated_files | JSONField | text/json | 是 | 7 个文件生成状态摘要 | +| missing_fields | JSONField | text/json | 是 | 缺失并填 `/` 的字段 | +| llm_only_fields | JSONField | text/json | 是 | 仅 LLM 命中的字段 | +| conflict_fields | JSONField | text/json | 是 | 规则和 LLM 冲突字段 | +| risk_notes | JSONField | text/json | 是 | `.doc` 适配器、知识库不可用、zip 失败等提示 | +| template_config_version | CharField(80) | varchar(80) | 否 | 模板配置版本 | +| template_config_hash | CharField(128) | varchar(128) | 否 | 模板配置 hash | +| adapter_summary | JSONField | text/json | 是 | docx/doc 适配器使用情况 | +| work_dir | CharField(500) | varchar(500) | 否 | 批次工作目录 | +| error_message | TextField | text | 否 | 批次异常说明 | +| created_at | DateTimeField | datetime | 是 | 创建时间 | +| started_at | DateTimeField | datetime | 否 | 开始时间 | +| finished_at | DateTimeField | datetime | 否 | 完成时间 | +| archived_at | DateTimeField | datetime | 否 | 归档时间 | +| is_deleted | BooleanField | bool | 是 | 软删除标记 | + +唯一约束: + +| 约束名 | 字段 | +| --- | --- | +| uq_ra_rip_batch_no | batch_no | + +索引: + +| 索引名 | 字段 | 说明 | +| --- | --- | --- | +| idx_ra_rip_batch_conv_status | conversation_id, status | 查询对话下材料包批次状态 | +| idx_ra_rip_batch_user_created | user_id, created_at | 查询用户发起历史 | +| idx_ra_rip_batch_attachment | source_attachment_id | 查询某说明书附件生成历史 | +| idx_ra_rip_batch_summary | source_summary_batch_id | 查询文件汇总关联的材料包批次 | +| idx_ra_rip_batch_created | created_at | 后台按时间排查 | + +--- + +### 3.2 ra_regulatory_info_package_artifact + +第1章监管信息材料包生成过程产物表。仅保存文件元数据,不保存大文本正文。 + +| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| id | BigAutoField | integer | 是 | 主键 | +| batch_id | ForeignKey | bigint | 是 | 所属材料包批次 | +| artifact_type | CharField(60) | varchar(60) | 是 | template_copy、instruction_extract、field_extract_result、merged_fields、generated_document、traceability、zip_package、notification_record | +| file_format | CharField(20) | varchar(20) | 是 | json、excel、docx、doc、zip、markdown | +| name | CharField(160) | varchar(160) | 是 | 产物名称 | +| file_name | CharField(255) | varchar(255) | 是 | 文件名 | +| storage_path | CharField(500) | varchar(500) | 是 | 文件存储路径 | +| file_size | BigIntegerField | bigint | 是 | 文件大小 | +| content_hash | CharField(128) | varchar(128) | 否 | 文件 SHA-256 hash | +| metadata | JSONField | text/json | 是 | 模板编码、生成状态、高亮数量、适配器、错误摘要等 | +| created_by_node | CharField(60) | varchar(60) | 否 | 生成该产物的工作流节点 | +| created_at | DateTimeField | datetime | 是 | 创建时间 | +| is_deleted | BooleanField | bool | 是 | 软删除标记 | + +索引: + +| 索引名 | 字段 | 说明 | +| --- | --- | --- | +| idx_ra_rip_artifact_batch_type | batch_id, artifact_type | 查询批次过程产物 | +| idx_ra_rip_artifact_format | file_format | 按文件格式查询 | +| idx_ra_rip_artifact_created | created_at | 按时间追溯 | + +--- + +### 3.3 ra_regulatory_info_package_notification_record + +第1章监管信息材料包生成通知记录表。通知失败不阻断下载,但需要留痕和支持后续重试。 + +| 字段名 | Django 类型 | SQLite 类型 | 必填 | 说明 | +| --- | --- | --- | --- | --- | +| id | BigAutoField | integer | 是 | 主键 | +| batch_id | ForeignKey | bigint | 是 | 所属材料包批次 | +| recipient_id | ForeignKey(User) | bigint | 是 | 通知对象,默认发起人 | +| channel | CharField(30) | varchar(30) | 是 | feishu_cli、feishu_api、mock | +| export_ids | JSONField | text/json | 是 | 本次通知关联导出文件 ID | +| message_summary | TextField | text | 是 | 通知摘要 | +| send_status | CharField(20) | varchar(20) | 是 | pending、success、failed | +| retry_count | PositiveIntegerField | integer | 是 | 已重试次数 | +| external_message_id | CharField(120) | varchar(120) | 否 | 飞书外部消息 ID | +| error_message | TextField | text | 否 | 失败原因 | +| sent_at | DateTimeField | datetime | 否 | 发送成功时间 | +| created_at | DateTimeField | datetime | 是 | 创建时间 | +| updated_at | DateTimeField | datetime | 是 | 更新时间 | +| is_deleted | BooleanField | bool | 是 | 软删除标记 | + +索引: + +| 索引名 | 字段 | 说明 | +| --- | --- | --- | +| idx_ra_rip_notify_batch | batch_id, created_at | 查询批次通知 | +| idx_ra_rip_notify_recipient | recipient_id, send_status | 查询用户通知状态 | +| idx_ra_rip_notify_status | send_status, retry_count | 查询待重试通知 | + +--- + +## 四、既有表扩展 + +### 4.1 ra_exported_summary_file + +继续复用导出文件表,新增 zip 导出类型,并支持 `regulatory_info_package` 权限反查。 + +| 字段/枚举 | 处理 | +| --- | --- | +| export_type | 增加 `zip` | +| workflow_type | 使用 `regulatory_info_package` | +| workflow_batch_id | 记录 `RegulatoryInfoPackageBatch.id` | +| export_category | 使用 `regulatory_info_package`、`generated_document`、`traceability` | + +导出类型枚举: + +| value | 中文展示 | 说明 | +| --- | --- | --- | +| markdown | Markdown | 既有报告 | +| excel | Excel | 追溯清单 | +| json | JSON | 抽取结果、合并字段 | +| word | Word | 生成的 Word 文件,包含 `.docx` 和可下载 `.doc` | +| pdf | PDF | 既有预留 | +| zip | ZIP | 第1章监管信息材料包主下载 | + +下载 MIME 规则: + +| 条件 | content_type | +| --- | --- | +| export_type=zip | application/zip | +| export_type=word 且文件名后缀 `.doc` | application/msword | +| export_type=word 且文件名后缀 `.docx` | application/vnd.openxmlformats-officedocument.wordprocessingml.document | + +### 4.2 ra_workflow_node_run + +本功能使用通用工作流节点表: + +| 字段 | 值 | +| --- | --- | +| workflow_type | regulatory_info_package | +| workflow_batch_id | RegulatoryInfoPackageBatch.id | +| node_group | regulatory_info_package | +| batch_id | 可为空;如为兼容旧查询,不建议绑定文件汇总批次 | + +建议新增节点: + +```text +prepare, template_copy, text_extract, field_extract, field_merge, +generate_docs, highlight_review_items, trace_export, zip_export, notify, completed +``` + +### 4.3 ra_workflow_event + +本功能事件写入: + +| 字段 | 值 | +| --- | --- | +| workflow_type | regulatory_info_package | +| workflow_batch_id | RegulatoryInfoPackageBatch.id | +| conversation_id | 当前对话 ID | +| payload | 节点状态、文件生成状态、导出 ID、待确认摘要等 | + +--- + +## 五、枚举设计 + +### 5.1 RegulatoryInfoPackageBatch.status + +| value | 中文展示 | 说明 | +| --- | --- | --- | +| pending | 待执行 | 批次已创建,等待执行 | +| running | 执行中 | 工作流正在执行 | +| waiting_user | 等待用户 | 未找到唯一说明书,需要用户选择 | +| success | 成功 | 7 个文件、zip 和必要追溯产物生成成功 | +| partial_success | 部分成功 | zip 或主要文件已生成,但部分单文件、`.doc` 原生处理、`.docx` 兜底、追溯或通知存在失败 | +| failed | 失败 | 关键输入、模板或全部目标文件生成失败 | +| cancelled | 已取消 | 用户或系统取消执行 | + +### 5.2 RegulatoryInfoPackageArtifact.artifact_type + +| value | 说明 | +| --- | --- | +| template_copy | 模板副本 | +| instruction_extract | 说明书文本、章节、表格抽取结果 | +| field_extract_result | 规则与 LLM 抽取原始结果 | +| merged_fields | 合并字段、高亮决策、标准候选 | +| generated_document | 生成后的单个目标文件 | +| traceability | 追溯清单 | +| zip_package | 主下载 zip 包 | +| notification_record | 通知记录产物 | + +### 5.3 RegulatoryInfoPackageArtifact.file_format + +| value | 说明 | +| --- | --- | +| json | JSON 产物 | +| excel | Excel 追溯清单 | +| docx | Word OpenXML 文件 | +| doc | Word 97-2003 文件 | +| zip | 压缩包 | +| markdown | Markdown 摘要或报告 | + +### 5.4 通知枚举 + +| 字段 | value | +| --- | --- | +| channel | feishu_cli、feishu_api、mock | +| send_status | pending、success、failed | + +--- + +## 六、JSON 字段结构 + +### 6.1 generated_files + +```json +[ + { + "template_code": "ch1_4_application_form", + "file_name": "CH1.4 申请表.docx", + "status": "success", + "artifact_id": 12, + "export_id": 34, + "highlight_count": 8, + "missing_count": 5, + "llm_only_count": 2, + "error_message": "" + } +] +``` + +### 6.2 missing_fields + +```json +[ + { + "target_file": "CH1.4 申请表.docx", + "field_key": "applicant_name", + "field_label": "申请人名称", + "final_value": "/", + "highlight_reason": "missing", + "needs_review": true + } +] +``` + +### 6.3 llm_only_fields + +```json +[ + { + "target_file": "CH1.4 申请表.docx", + "field_key": "detection_targets", + "field_label": "检测靶标", + "final_value": "ORF1ab、N基因", + "evidence": "预期用途和检测原理章节", + "highlight_reason": "llm_only", + "needs_review": true + } +] +``` + +### 6.4 conflict_fields + +```json +[ + { + "field_key": "package_specification", + "field_label": "包装规格", + "rule_value": "规格A:24人份/盒、48人份/盒、96人份/盒", + "llm_value": "规格A、规格B均为24/48/96人份", + "selected_value": "规格A:24人份/盒、48人份/盒、96人份/盒", + "handling": "规则优先,写入值高亮并进入追溯清单" + } +] +``` + +### 6.5 risk_notes + +```json +[ + { + "type": "legacy_doc_adapter_unavailable", + "message": "CH1.9 为 .doc 文件,当前环境未检测到可写入适配器。", + "template_code": "ch1_9_pre_submission" + }, + { + "type": "knowledge_base_unavailable", + "message": "标准清单知识库查询不可用,未自动写入候选标准。" + } +] +``` + +### 6.6 adapter_summary + +```json +{ + "docx": { + "adapter": "DocxDocumentAdapter", + "status": "available" + }, + "doc": { + "adapter": "WordComDocAdapter", + "status": "available", + "fallback_used": false + } +} +``` + +### 6.7 artifact.metadata + +```json +{ + "template_code": "ch1_5_product_list", + "strategy": "product_list", + "source_template": "CH1.5 产品列表.docx", + "generated_status": "success", + "highlight_count": 12, + "missing_count": 6, + "llm_only_count": 1, + "adapter": "DocxDocumentAdapter", + "created_by_node": "generate_docs" +} +``` + +--- + +## 七、存储路径设计 + +批次目录: + +```text +media/regulatory_info_package/{user_id}/{conversation_id}/{batch_no}/ +``` + +目录结构: + +```text +media/regulatory_info_package/12/1001/RIP-20260610153000-abcdef/ + templates/ + ch1_2_directory.source.docx + ch1_9_pre_submission.source.doc + extracted/ + instruction_extract.json + field_extract_result.json + merged_fields.json + generated/ + CH1.2 监管信息目录.docx + CH1.4 申请表.docx + CH1.5 产品列表.docx + CH1.9 产品申报前沟通的说明.doc + CH1.11.1 符合标准的清单.docx + CH1.11.5 真实性声明.docx + CH1.11.6 符合性声明.docx + exports/ + traceability.xlsx + 第1章 监管信息(预生成版).zip + logs/ + instruction_extract.json + field_extract_result.json + merged_fields.json + traceability.json + doc_adapter_result.json +``` + +路径安全要求: + +| 要求 | 说明 | +| --- | --- | +| 输出目录校验 | 所有输出路径必须位于当前批次 `work_dir` 下 | +| 原始模板只读 | 不允许覆盖 `docs/0.原始材料` | +| 导出路径 | `ExportedSummaryFile.storage_path` 保存实际文件路径,下载时校验权限 | + +--- + +## 八、权限关系 + +### 8.1 批次权限 + +```text +RegulatoryInfoPackageBatch.conversation.user_id == request.user.id +``` + +### 8.2 输入附件权限 + +```text +FileAttachment.conversation_id == batch.conversation_id +FileAttachment.user_id == batch.user_id +FileAttachment.upload_status != deleted +``` + +### 8.3 导出下载权限 + +`ExportedSummaryFile` 下载时按 `workflow_type` 分支: + +```text +workflow_type == "regulatory_info_package" +-> workflow_batch_id 反查 RegulatoryInfoPackageBatch +-> conversation__user == request.user +-> is_deleted == false +``` + +--- + +## 九、迁移设计 + +建议新增一个迁移文件,包含: + +| 变更 | 说明 | +| --- | --- | +| 新增 `RegulatoryInfoPackageBatch` | 批次表 | +| 新增 `RegulatoryInfoPackageArtifact` | 产物表 | +| 新增 `RegulatoryInfoPackageNotificationRecord` | 通知记录表 | +| 扩展 `ExportedSummaryFile.ExportType` | 增加 `zip` 枚举 | + +Django 模型建议仍集中放在 `review_agent/models.py`,业务逻辑放入 `review_agent/regulatory_info_package/`。 + +--- + +## 十、DDL 参考 + +以下 DDL 为 SQLite / Django ORM 参考,实际以 migration 生成为准。 + +```sql +CREATE TABLE ra_regulatory_info_package_batch ( + id integer NOT NULL PRIMARY KEY AUTOINCREMENT, + conversation_id bigint NOT NULL REFERENCES review_agent_conversation(id), + user_id bigint NOT NULL REFERENCES auth_user(id), + trigger_message_id bigint NULL REFERENCES review_agent_message(id), + source_attachment_id bigint NULL REFERENCES ra_file_attachment(id), + source_summary_batch_id bigint NULL REFERENCES ra_file_summary_batch(id), + source_summary_item_id integer NULL, + batch_no varchar(64) NOT NULL UNIQUE, + status varchar(30) NOT NULL, + source_file_name varchar(255) NOT NULL DEFAULT '', + source_storage_path varchar(500) NOT NULL DEFAULT '', + product_name varchar(200) NOT NULL DEFAULT '', + output_zip_name varchar(255) NOT NULL DEFAULT '', + generated_files text NOT NULL DEFAULT '[]', + missing_fields text NOT NULL DEFAULT '[]', + llm_only_fields text NOT NULL DEFAULT '[]', + conflict_fields text NOT NULL DEFAULT '[]', + risk_notes text NOT NULL DEFAULT '[]', + template_config_version varchar(80) NOT NULL DEFAULT '', + template_config_hash varchar(128) NOT NULL DEFAULT '', + adapter_summary text NOT NULL DEFAULT '{}', + work_dir varchar(500) NOT NULL DEFAULT '', + error_message text NOT NULL DEFAULT '', + created_at datetime NOT NULL, + started_at datetime NULL, + finished_at datetime NULL, + archived_at datetime NULL, + is_deleted bool NOT NULL DEFAULT 0 +); + +CREATE INDEX idx_ra_rip_batch_conv_status + ON ra_regulatory_info_package_batch(conversation_id, status); +CREATE INDEX idx_ra_rip_batch_user_created + ON ra_regulatory_info_package_batch(user_id, created_at); +CREATE INDEX idx_ra_rip_batch_attachment + ON ra_regulatory_info_package_batch(source_attachment_id); +CREATE INDEX idx_ra_rip_batch_summary + ON ra_regulatory_info_package_batch(source_summary_batch_id); +CREATE INDEX idx_ra_rip_batch_created + ON ra_regulatory_info_package_batch(created_at); +``` + +--- + +## 十一、实现注意事项 + +| 注意事项 | 说明 | +| --- | --- | +| JSONField 默认值 | 使用 `default=list` 或 `default=dict`,禁止使用可变对象字面量 | +| 外键删除策略 | conversation/user 使用 CASCADE;输入附件和文件汇总批次建议 PROTECT 或 SET_NULL,避免历史批次断链 | +| `source_summary_item_id` | 当前没有强制外键到 `FileSummaryItem`,可先保存 ID,后续需要强约束时再改 FK | +| `.doc` 失败记录 | `.doc` 原生适配器不可用或执行失败时必须写入 `risk_notes` 和 artifact metadata;若 `.docx` 兜底成功则 generated_files 状态为 `fallback_success` | +| zip 主入口 | zip 导出记录的 `export_category` 固定为 `regulatory_info_package` | +| 单文件下载 | 7 个生成文件也写入 `ExportedSummaryFile`,作为辅助下载 | +| 软删除 | 批次和产物使用 `is_deleted`,下载权限需过滤软删除批次 | + +--- + +## 十二、验收标准 + +| 序号 | 验收项 | 标准 | +| --- | --- | --- | +| 1 | 模型创建 | 三张 RIP 专项表可通过 migration 创建 | +| 2 | 批次编号 | `batch_no` 唯一,符合 `RIP-...` 格式 | +| 3 | 附件关联 | 批次可绑定直接说明书附件 | +| 4 | 汇总兼容 | 批次可选绑定 `FileSummaryBatch` 与 `source_summary_item_id` | +| 5 | 产物留痕 | 模板副本、抽取结果、生成文件、zip、追溯清单均可写 artifact | +| 6 | zip 导出 | `ExportedSummaryFile` 支持 `export_type=zip` | +| 7 | 下载权限 | 非批次所属用户不能下载 RIP 导出 | +| 8 | 节点事件 | `WorkflowNodeRun` 和 `WorkflowEvent` 可通过 `workflow_type=regulatory_info_package` 查询 | +| 9 | 通知记录 | 通知成功、失败和重试次数可落库 | +| 10 | JSON 摘要 | 缺失项、LLM-only、冲突项、风险提示结构符合本文约定 | + +--- + +## 十三、规范依据与裁决 + +| 规范来源 | 命中规则 | 本设计裁决 | +| --- | --- | --- | +| GYRX 数据库设计流程 | 项目规范优先,未命中时回退基线规范 | 当前项目为 Django/SQLite,沿用既有数据库设计文档风格 | +| 既有自动填表数据库设计 | 独立批次、产物、通知三表;大 JSON 文件化;通用导出表复用 | 本功能按同样模式新增 RIP 三表 | +| 自动汇总数据库设计 | 对话隔离、多版本附件、工作流事件留痕 | 输入附件和批次权限沿用该关系 | +| 飞书通知数据库设计 | 通知摘要入库、失败不阻断主流程 | RIP 通知表结构与自动填表通知对齐 | + +冲突裁决:技能规范中的低代码/Java 表达不适用于当前 Django 项目,数据库设计以当前项目 ORM、SQLite 兼容和既有 `ra_` 表风格为准。 diff --git a/docs/4.详细设计/5.第1章监管信息材料包生成.md b/docs/4.详细设计/5.第1章监管信息材料包生成.md new file mode 100644 index 0000000..2d4c3a8 --- /dev/null +++ b/docs/4.详细设计/5.第1章监管信息材料包生成.md @@ -0,0 +1,931 @@ +# 第1章监管信息材料包生成详细设计 + +## 文档信息 + +| 项目 | 内容 | +| --- | --- | +| 需求分析文档 | docs/1.需求分析/5.第1章监管信息材料包生成.md | +| 功能设计文档 | docs/2.功能设计/5.第1章监管信息材料包生成.md | +| 数据库设计文档 | docs/3.数据库设计/5.第1章监管信息材料包生成.md | +| 参考详细设计 | docs/4.详细设计/3.产品关键信息提取与申报文件自动填表.md | +| 功能名称 | 第1章监管信息材料包生成 | +| 工作流编码 | regulatory_info_package | +| 所属模块 | 审核智能体 review_agent | +| 设计日期 | 2026-06-10 | +| 设计版本 | V1.0 | + +--- + +## 一、详细设计目标 + +本详细设计用于指导 `regulatory_info_package` 独立工作流开发落地。系统根据用户上传或指定的产品说明书,抽取产品关键信息,基于 `docs/0.原始材料/第1章 监管信息` 下的样例模板生成第1章监管信息材料包,并以 `第1章 监管信息(预生成版).zip` 作为对话摘要首位下载入口。 + +核心约束: + +| 约束 | 说明 | +| --- | --- | +| 独立工作流 | 使用 `workflow_type=regulatory_info_package`,拥有独立批次、产物、通知和卡片 | +| 独立模块 | 新增 `review_agent/regulatory_info_package/`,与 `application_form_fill` 平级 | +| 模型集中 | Django 模型仍集中放在 `review_agent/models.py` | +| 输入优先级 | 用户消息指定文件名优先;其次 active 附件;再兼容最近成功文件汇总 | +| 模板固定 | 固定处理第1章监管信息 7 个模板 | +| 规则优先可演示 | 规则抽取可独立跑通;LLM 失败最多重试 3 次,失败后继续 | +| 文档并发生成 | 工作流整体串行,`generate_docs` 节点内部每个文档可独立线程并发处理 | +| `.doc` 兜底 | 优先原生 `.doc` 写入;失败后允许生成 `.docx` 兜底文件 | +| zip 只含成功文件 | zip 只打包成功或兜底成功的文件;失败文件不进入 zip | +| 高亮规则 | 缺失和 LLM-only 黄底;冲突黄底红字 | +| 追溯输出 | 用户下载 Excel;JSON 仅保存到后台 logs 目录 | +| 前端最小接入 | 不做多说明书选择 UI;不确定时通过对话反问 | + +--- + +## 二、代码结构设计 + +### 2.1 目录结构 + +```text +review_agent/ + models.py + services.py + skill_router.py + regulatory_info_package/ + __init__.py + constants.py + schemas.py + storage.py + events.py + workflow.py + views.py + services/ + __init__.py + input_select.py + template_config.py + template_repository.py + instruction_extract.py + field_extract.py + field_merge.py + standard_candidates.py + document_writer.py + docx_document.py + legacy_doc_document.py + package_generate.py + traceability_export.py + zip_export.py + summary.py + notifier.py + templates/ + regulatory_info_package_templates_v1.yaml + prompts/ + field_extract.md +``` + +### 2.2 文件职责 + +| 文件 | 职责 | +| --- | --- | +| constants.py | 工作流编码、节点定义、触发关键词、模板编码、状态常量 | +| schemas.py | dataclass 数据结构,如 `TemplateSpec`、`InstructionExtractResult`、`MergedField`、`GeneratedFileResult` | +| storage.py | 批次目录、子目录、hash、产物创建、路径安全校验 | +| events.py | 记录与序列化 `WorkflowEvent` | +| workflow.py | `RegulatoryInfoPackageWorkflowExecutor`、批次创建、工作流启动 | +| views.py | health、start、status、select-input 接口 | +| input_select.py | 根据用户消息、active 附件、文件汇总选择说明书 | +| template_config.py | YAML 加载、校验、hash | +| template_repository.py | 定位样例模板、复制到批次目录 | +| instruction_extract.py | 说明书段落、章节、表格和组成成分表解析 | +| field_extract.py | 规则抽取与 LLM 抽取并行执行,LLM 最多 3 次重试 | +| field_merge.py | 合并字段,输出缺失、LLM-only、冲突和高亮决策 | +| standard_candidates.py | 从说明书抽标准号,调用现有知识库搜索候选 | +| document_writer.py | 文档适配器接口与通用高亮策略 | +| docx_document.py | `DocxDocumentAdapter`,处理 `.docx` | +| legacy_doc_document.py | `LegacyDocDocumentAdapter`,处理 `.doc` 原生写入与 `.docx` 兜底 | +| package_generate.py | 7 个文档生成策略,多线程生成文件 | +| traceability_export.py | 生成 `exports/traceability.xlsx` 和 `logs/traceability.json` | +| zip_export.py | 生成主下载 zip,只包含成功文件 | +| summary.py | 构造助手回显,zip 链接排首位 | +| notifier.py | 写专项通知记录,并调用统一通知服务 | + +--- + +## 三、数据模型详细设计 + +模型放在 `review_agent/models.py`。 + +### 3.1 RegulatoryInfoPackageBatch + +```python +class RegulatoryInfoPackageBatch(models.Model): + class Status(models.TextChoices): + PENDING = "pending", "待执行" + RUNNING = "running", "执行中" + WAITING_USER = "waiting_user", "等待用户" + SUCCESS = "success", "成功" + PARTIAL_SUCCESS = "partial_success", "部分成功" + FAILED = "failed", "失败" + CANCELLED = "cancelled", "已取消" +``` + +关键字段: + +| 字段 | 说明 | +| --- | --- | +| conversation | 所属对话 | +| user | 发起用户 | +| trigger_message | 触发消息 | +| source_attachment | 直接选中的说明书附件,可空 | +| source_summary_batch | 兼容文件汇总批次,可空 | +| source_summary_item_id | 文件汇总条目 ID,可空 | +| batch_no | `RIP-YYYYMMDDHHMMSS-abcdef` | +| source_file_name | 说明书原文件名 | +| source_storage_path | 说明书存储路径 | +| product_name | 抽取产品名称 | +| output_zip_name | `第1章 监管信息(预生成版).zip` | +| generated_files | 7 个文件状态 | +| missing_fields | 缺失字段 | +| llm_only_fields | LLM-only 字段 | +| conflict_fields | 冲突字段 | +| risk_notes | 风险和降级提示 | +| adapter_summary | doc/docx 适配器实际执行摘要 | +| template_config_version/hash | 模板配置版本和 hash | +| work_dir | 批次工作目录 | +| is_deleted | 软删除 | + +### 3.2 RegulatoryInfoPackageArtifact + +```python +class RegulatoryInfoPackageArtifact(models.Model): + class ArtifactType(models.TextChoices): + TEMPLATE_COPY = "template_copy", "模板副本" + INSTRUCTION_EXTRACT = "instruction_extract", "说明书抽取结果" + FIELD_EXTRACT_RESULT = "field_extract_result", "字段抽取结果" + MERGED_FIELDS = "merged_fields", "合并字段" + GENERATED_DOCUMENT = "generated_document", "生成文件" + TRACEABILITY = "traceability", "追溯清单" + ZIP_PACKAGE = "zip_package", "ZIP包" + NOTIFICATION_RECORD = "notification_record", "通知记录" +``` + +`file_format` 包含:`json`、`excel`、`docx`、`doc`、`zip`、`markdown`。 + +### 3.3 RegulatoryInfoPackageNotificationRecord + +字段对齐自动填表通知记录:`batch`、`recipient`、`channel`、`export_ids`、`message_summary`、`send_status`、`retry_count`、`external_message_id`、`error_message`、`sent_at`、`is_deleted`。 + +### 3.4 ExportedSummaryFile 扩展 + +`ExportedSummaryFile.ExportType` 增加: + +```python +ZIP = "zip", "ZIP" +``` + +下载 MIME 按扩展名兜底: + +| 条件 | MIME | +| --- | --- | +| zip | application/zip | +| .doc | application/msword | +| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document | + +--- + +## 四、常量设计 + +### 4.1 工作流常量 + +```python +WORKFLOW_TYPE = "regulatory_info_package" +DEFAULT_ZIP_NAME = "第1章 监管信息(预生成版).zip" + +REGULATORY_INFO_PACKAGE_NODE_DEFINITIONS = [ + ("prepare", "准备资料", "regulatory_info_package"), + ("template_copy", "复制模板", "regulatory_info_package"), + ("text_extract", "抽取说明书", "regulatory_info_package"), + ("field_extract", "抽取字段", "regulatory_info_package"), + ("field_merge", "合并字段", "regulatory_info_package"), + ("generate_docs", "生成材料", "regulatory_info_package"), + ("highlight_review_items", "标记待确认", "regulatory_info_package"), + ("trace_export", "追溯清单", "regulatory_info_package"), + ("zip_export", "打包下载", "regulatory_info_package"), + ("notify", "通知", "regulatory_info_package"), + ("completed", "完成", "completed"), +] +``` + +### 4.2 触发关键词 + +```python +REGULATORY_INFO_PACKAGE_TRIGGER_KEYWORDS = [ + "根据说明书生成第1章监管信息", + "生成监管信息材料包", + "从说明书生成第1章材料", + "第1章监管信息", + "监管信息材料包", +] +``` + +### 4.3 文件状态 + +```python +GENERATED_FILE_SUCCESS = "success" +GENERATED_FILE_FALLBACK_SUCCESS = "fallback_success" +GENERATED_FILE_FAILED = "failed" +GENERATED_FILE_SKIPPED = "skipped" +``` + +--- + +## 五、核心数据结构 + +### 5.1 TemplateSpec + +```python +@dataclass(frozen=True) +class TemplateSpec: + code: str + output_name: str + source_file: str + file_format: str + strategy: str + include_in_zip: bool + require_legacy_doc_native: bool = False + fields: list[dict[str, Any]] = field(default_factory=list) +``` + +### 5.2 InstructionExtractResult + +```python +@dataclass +class InstructionExtractResult: + source_file_name: str + paragraphs: list[str] + sections: dict[str, str] + tables: list[list[list[str]]] + component_tables: list["ComponentTable"] + front_text: str +``` + +### 5.3 ProductListRow + +```python +@dataclass +class ProductListRow: + package_specification: str + item_no: str + composition: str + component_name: str + main_component: str + quantity: str + source_table_title: str + needs_review_fields: list[str] = field(default_factory=list) +``` + +其中 `item_no` 对应货号,本期固定 `/` 并黄底。 + +### 5.4 MergedField + +```python +@dataclass +class MergedField: + key: str + label: str + value: str + source: str + evidence: str + confidence: float + highlight_reason: str = "none" + needs_review: bool = False + rule_value: str = "" + llm_value: str = "" +``` + +### 5.5 GeneratedFileResult + +```python +@dataclass +class GeneratedFileResult: + template_code: str + file_name: str + requested_format: str + actual_format: str + status: str + path: str = "" + artifact_id: int | None = None + export_id: int | None = None + highlight_count: int = 0 + missing_count: int = 0 + llm_only_count: int = 0 + error_message: str = "" +``` + +--- + +## 六、存储目录设计 + +```text +media/regulatory_info_package/{user_id}/{conversation_id}/{batch_no}/ + templates/ + logs/ + instruction_extract.json + field_extract_result.json + merged_fields.json + doc_adapter_result.json + traceability.json + generated/ + CH1.2 监管信息目录.docx + CH1.4 申请表.docx + CH1.5 产品列表.docx + CH1.9 产品申报前沟通的说明.docx + CH1.11.1 符合标准的清单.docx + CH1.11.5 真实性声明.docx + CH1.11.6 符合性声明.docx + exports/ + traceability.xlsx + 第1章 监管信息(预生成版).zip +``` + +说明: + +| 目录 | 说明 | +| --- | --- | +| templates | 模板副本 | +| logs | 后台 JSON 产物,不作为用户主下载 | +| generated | 生成成功或兜底成功的单文件 | +| exports | 用户可下载的追溯 Excel 和 zip | + +--- + +## 七、输入选择详细设计 + +### 7.1 选择优先级 + +`input_select.py` 的选择顺序: + +1. 用户消息显式指定文件名时,按 active 附件名模糊匹配。 +2. 当前对话 active 附件中文件名包含“说明书”的 `.docx`。 +3. 当前对话 active 附件中唯一 `.docx`。 +4. 最近成功 `FileSummaryBatch.items` 中包含“说明书”的 `.docx`。 +5. 多候选或无候选时返回 `InputSelectionResult(status="waiting_user")`。 + +### 7.2 多候选处理 + +本期不新增在线选择弹窗。多候选时: + +| 场景 | 处理 | +| --- | --- | +| 用户消息可模糊匹配唯一附件 | 直接选择 | +| 多个候选且无法确定 | 对话反问用户确认哪个说明书 | +| 无说明书 | 提示上传产品说明书 | + +反问示例: + +```text +我找到多个说明书候选,请回复要使用的文件名:A.docx、B.docx。 +``` + +--- + +## 八、模板配置详细设计 + +配置路径: + +```text +review_agent/regulatory_info_package/templates/regulatory_info_package_templates_v1.yaml +``` + +必须包含 7 个模板: + +| code | source_file | strategy | +| --- | --- | --- | +| ch1_2_directory | CH1.2 监管信息目录.docx | directory | +| ch1_4_application_form | CH1.4 申请表.docx | application_form | +| ch1_5_product_list | CH1.5 产品列表.docx | product_list | +| ch1_9_pre_submission | CH1.9 产品申报前沟通的说明.doc | pre_submission | +| ch1_11_1_standard_list | CH1.11.1 符合标准的清单.docx | standard_list | +| ch1_11_5_authenticity | CH1.11.5 真实性声明.docx | authenticity_statement | +| ch1_11_6_compliance | CH1.11.6 符合性声明.docx | compliance_statement | + +校验规则: + +| 校验 | 说明 | +| --- | --- | +| version 必填 | 写入批次 | +| source_dir 存在 | 指向样例目录 | +| code 唯一 | 防止覆盖产物 | +| source_file 存在 | 缺失则配置错误 | +| strategy 合法 | 必须命中生成策略 | +| doc 模板标记 | `.doc` 模板需声明 `require_legacy_doc_native` | + +--- + +## 九、字段抽取详细设计 + +### 9.1 规则抽取 + +规则抽取必须独立可用,覆盖: + +| 字段 | 规则 | +| --- | --- | +| product_name | `【产品名称】` 下一段 | +| package_specification | `【包装规格】` 至下一章节 | +| intended_use | `【预期用途】` 至下一章节 | +| detection_principle | `【检测原理】` 至下一章节 | +| main_components | `【主要组成成分】` 下方表格摘要 | +| storage_condition_and_validity | `【储存条件及有效期】` 至下一章节 | +| sample_type | 样本要求章节中的“适用样本类型” | +| detection_targets | 预期用途/检测原理中的基因、病原体、靶标 | +| applicable_instruments | `【适用仪器】` 至下一章节 | +| test_method | `【检验方法】` 摘要 | +| standards | 正则抽取标准号 | + +### 9.2 LLM 抽取与重试 + +`field_extract.py` 并行执行规则抽取和 LLM 抽取: + +```text +ThreadPoolExecutor(max_workers=2) + -> rule_extract() + -> llm_extract_with_retry(max_attempts=3) +``` + +LLM 重试策略: + +| 次数 | 间隔 | +| --- | --- | +| 第 1 次 | 立即 | +| 第 2 次 | 等待 1 秒 | +| 第 3 次 | 等待 2 秒 | + +三次失败后: + +| 产物 | 处理 | +| --- | --- | +| risk_notes | 增加 `llm_extract_failed` | +| logs/field_extract_result.json | 记录每次错误摘要 | +| 工作流 | 继续使用规则结果 | + +LLM 不允许填企业信息、分类编码、管理类别、临床评价路径等说明书无法证明的内容。 + +### 9.3 字段合并 + +| 场景 | 写入值 | 高亮 | needs_review | +| --- | --- | --- | --- | +| rule 与 LLM 一致 | rule/LLM 值 | 否 | 否 | +| rule 与 LLM 冲突 | 规则优先或配置优先 | 黄底红字 | 是 | +| rule 缺失、LLM 命中 | LLM 值 | 黄底 | 是 | +| 全部缺失 | `/` | 黄底 | 是 | + +--- + +## 十、文档适配器详细设计 + +### 10.1 统一接口 + +```python +class DocumentAdapter(Protocol): + def replace_text(self, old: str, new: str, *, highlight: bool = False, conflict: bool = False) -> int: ... + def fill_table_cell(self, row_label: str, value: str, *, highlight: bool = False, conflict: bool = False) -> bool: ... + def replace_table(self, marker: str, rows: list[ProductListRow], *, highlight_columns: list[str] | None = None) -> bool: ... + def save(self, path: Path) -> Path: ... +``` + +高亮规则: + +| 类型 | 视觉 | +| --- | --- | +| missing | 黄色底色 | +| llm_only | 黄色底色 | +| conflict | 黄色底色 + 红色字体 | + +### 10.2 DocxDocumentAdapter + +实现能力: + +| 方法 | 说明 | +| --- | --- | +| replace_text | 支持段落与表格中的文本替换,需处理 run 拆分 | +| fill_table_cell | 按行标签定位目标单元格 | +| replace_table | 重建 CH1.5 产品列表表格 | +| apply_highlight | 使用 `w:shd` 设置黄色底色 | +| apply_conflict_style | 黄色底色 + 红字 | + +### 10.3 LegacyDocDocumentAdapter + +接口: + +```python +class AdapterCapability: + adapter_name: str + supports_native_doc_write: bool + supports_docx_fallback: bool + status: str + error_message: str = "" + +class LegacyDocDocumentAdapter: + @staticmethod + def detect_available_adapter() -> AdapterCapability: ... +``` + +执行顺序: + +1. 优先尝试 `WordComDocAdapter` 原生打开 `.doc` 并保存 `.doc`。 +2. 原生失败时,尝试将 `.doc` 另存为 `.docx`,再交给 `DocxDocumentAdapter`。 +3. 兜底成功时,输出 `CH1.9 产品申报前沟通的说明.docx`。 +4. 原生和兜底均失败时,该文件状态为 `failed`,不进入 zip。 + +兜底成功 `adapter_summary.doc`: + +```json +{ + "requested_format": "doc", + "actual_format": "docx", + "adapter": "ConversionFallbackAdapter", + "status": "fallback_success" +} +``` + +--- + +## 十一、材料生成详细设计 + +### 11.1 generate_docs 节点并发 + +工作流节点仍串行执行,但 `generate_docs` 内部并发生成单文件: + +```python +with ThreadPoolExecutor(max_workers=min(7, len(specs))) as executor: + futures = [executor.submit(generate_one_document, spec, context) for spec in specs] +``` + +并发注意事项: + +| 注意事项 | 说明 | +| --- | --- | +| 每个文档使用独立模板副本 | 避免并发写同一文件 | +| 共享字段只读 | `merged_fields`、`product_list_rows` 不在子线程修改 | +| 数据库写入集中处理 | 子线程返回 `GeneratedFileResult`,主线程统一写 artifact/export | +| 异常隔离 | 单文件失败不影响其他文件 | + +### 11.2 7 个生成策略 + +| 模板 | 输出规则 | +| --- | --- | +| CH1.2 | 替换产品名;页码沿用样例 | +| CH1.4 | 填产品名、包装规格、预期用途、组成、储存有效期、方法原理;企业/分类等缺失项 `/` 黄底 | +| CH1.5 | 按样例表头重建,货号 `/` 黄底 | +| CH1.9 | 优先 `.doc` 原生写入;失败则 `.docx` 兜底;兜底失败则不输出 | +| CH1.11.1 | 说明书标准号直接写;知识库候选只作为待确认高亮/追溯 | +| CH1.11.5 | 保留正文,替换产品名,公司名 `/` 黄底,日期当天 | +| CH1.11.6 | 保留正文,替换产品名,公司名 `/` 黄底,日期当天 | + +### 11.3 产品名缺失 + +规则和 LLM 都抽不到产品名称时: + +| 项 | 处理 | +| --- | --- | +| 文件内容 | 产品名位置写 `/` 并黄底 | +| 批次状态 | 至少 `partial_success` | +| zip | 仍生成,包含成功文件 | +| 摘要 | 明确提示产品名称待确认 | + +--- + +## 十二、追溯与 zip 设计 + +### 12.1 追溯 Excel + +用户可下载: + +```text +exports/traceability.xlsx +``` + +创建导出记录: + +```text +export_category = traceability +export_type = excel +``` + +字段: + +| 字段 | 说明 | +| --- | --- | +| target_file | 目标文件 | +| target_field | 目标字段 | +| final_value | 写入值 | +| extraction_source | rule、llm、missing、knowledge_candidate | +| evidence | 来源片段 | +| highlight_reason | missing、llm_only、conflict、rag_candidate | +| needs_review | 是否需复核 | + +### 12.2 后台 JSON + +JSON 产物仅写入 `logs/`,按需从后台查看: + +```text +logs/instruction_extract.json +logs/field_extract_result.json +logs/merged_fields.json +logs/traceability.json +logs/doc_adapter_result.json +``` + +这些 JSON 产物写入 `RegulatoryInfoPackageArtifact`,但不作为用户主下载。 + +### 12.3 zip 打包 + +zip 文件名: + +```text +第1章 监管信息(预生成版).zip +``` + +规则: + +| 场景 | 是否进入 zip | +| --- | --- | +| 文件状态 `success` | 是 | +| 文件状态 `fallback_success` | 是 | +| 文件状态 `failed` | 否 | +| 文件状态 `skipped` | 否 | + +若 `CH1.9 .doc` 兜底 `.docx` 成功,zip 中放入: + +```text +CH1.9 产品申报前沟通的说明.docx +``` + +--- + +## 十三、工作流详细设计 + +### 13.1 批次创建 + +```python +def create_regulatory_info_package_batch( + *, + conversation: Conversation, + user, + trigger_message: Message | None = None, + source_attachment: FileAttachment | None = None, + source_summary_batch: FileSummaryBatch | None = None, + source_summary_item_id: int | None = None, +) -> RegulatoryInfoPackageBatch: +``` + +创建后初始化 `REGULATORY_INFO_PACKAGE_NODE_DEFINITIONS`。 + +### 13.2 执行器 + +```python +class RegulatoryInfoPackageWorkflowExecutor: + def run(self) -> None: ... + def _nodes(self): ... + def _run_node(self, node: WorkflowNodeRun) -> None: ... + def _execute_node(self, node: WorkflowNodeRun) -> None: ... +``` + +节点执行: + +| 节点 | 关键动作 | +| --- | --- | +| prepare | 确认说明书,或 waiting_user | +| template_copy | 复制 7 个模板 | +| text_extract | 抽取说明书章节和表格 | +| field_extract | 规则 + LLM 并行抽取 | +| field_merge | 合并字段、高亮决策 | +| generate_docs | 多线程生成单文件 | +| highlight_review_items | 若生成策略已完成高亮,该节点记录确认结果即可 | +| trace_export | 写 Excel 和 logs JSON | +| zip_export | 打包成功/兜底成功文件 | +| notify | 写专项通知并调用统一通知 | +| completed | 写助手摘要 | + +### 13.3 状态落定 + +| 条件 | 状态 | +| --- | --- | +| zip 成功且 7 个文件均 success/fallback_success | success | +| zip 成功但有 failed/skipped | partial_success | +| zip 失败但至少一个单文件成功 | partial_success | +| 全部文件失败或关键输入缺失 | failed | +| 多说明书候选等待确认 | waiting_user | + +--- + +## 十四、路由与接口详细设计 + +### 14.1 skill_router.py + +增加: + +| 项 | 内容 | +| --- | --- | +| ROUTE_ACTIONS | 加入 `regulatory_info_package` | +| SkillRoute 属性 | `starts_regulatory_info_package` | +| deterministic route | 命中触发关键词直接返回 | +| LLM prompt | action 列表加入 `regulatory_info_package` | + +### 14.2 services.py + +`stream_message` 增加分支: + +1. 调用 `select_instruction_input(conversation, content)`。 +2. 若多候选,回复反问,不启动工作流。 +3. 若无候选,回复请上传说明书。 +4. 若唯一候选,创建批次并启动工作流。 +5. SSE 发送 `workflow_started`。 + +### 14.3 views.py + +接口: + +```text +GET /api/review-agent/regulatory-info-package/health/ +POST /api/review-agent/regulatory-info-package/start/ +GET /api/review-agent/regulatory-info-package//status/ +POST /api/review-agent/regulatory-info-package//select-input/ +``` + +`status` 返回: + +| 字段 | 说明 | +| --- | --- | +| batch | 状态、产品名、缺失/LLM-only/冲突数量 | +| nodes | 节点状态 | +| generated_files | 7 个文件成功/失败/兜底状态 | +| exports | zip、单文件、Excel 下载 | +| risk_notes | 风险提示 | +| notifications | 通知 | + +zip 不需要 `is_primary` 字段,前端或摘要按返回顺序把 zip 放首位。 + +--- + +## 十五、助手摘要设计 + +完成消息结构: + +```markdown +已生成第1章监管信息材料包。 + +批次号:RIP-... +产品名称:... +状态:success / partial_success + +主下载:[第1章 监管信息(预生成版).zip](...) + +| 文件 | 状态 | 下载/原因 | +| --- | --- | --- | +| CH1.2 监管信息目录.docx | 成功 | 下载 | +| CH1.9 产品申报前沟通的说明.docx | 兜底成功 | 下载 | +| CH1.11.1 符合标准的清单.docx | 失败 | 失败原因 | + +待确认:缺失项 X 个,LLM复核项 Y 个,冲突项 Z 个。 +``` + +要求: + +| 要求 | 说明 | +| --- | --- | +| zip 首位 | zip 链接必须在单文件列表之前 | +| 失败可见 | 失败文件展示状态和原因,无下载链接 | +| 兜底提示 | `.doc -> .docx` 时显示“兜底成功” | +| 待确认摘要 | 展示 missing、llm_only、conflict 数量 | + +--- + +## 十六、前端详细设计 + +### 16.1 模板 + +`templates/home.html` 增加工具 chip: + +```html + +``` + +`summaryPanel` 增加: + +```html +data-regulatory-info-package-status-url-template="/api/review-agent/regulatory-info-package/__batch_id__/status/" +``` + +### 16.2 app.js + +增加: + +| 位置 | 处理 | +| --- | --- | +| workflow type 判断 | 支持 `regulatory_info_package` | +| 状态 URL 选择 | 使用 `data-regulatory-info-package-status-url-template` | +| 终态判断 | success、partial_success、failed、waiting_user | +| 导出展示 | 直接按 exports 返回顺序展示,zip 在后端排首位 | + +### 16.3 不做选择 UI + +多说明书候选时,本期不做弹窗。通过对话反问用户确认文件名。 + +--- + +## 十七、导出下载权限 + +`file_summary.views._export_for_user` 增加: + +```python +if exported.workflow_type == "regulatory_info_package": + allowed = RegulatoryInfoPackageBatch.objects.filter( + pk=exported.workflow_batch_id, + conversation__user=user, + is_deleted=False, + ).exists() + return exported if allowed else None +``` + +下载 content type 增加 zip 和 `.doc` 后缀判断。 + +--- + +## 十八、通知详细设计 + +`notifier.py`: + +```python +def notify_completion(batch: RegulatoryInfoPackageBatch, exports: list[ExportedSummaryFile]) -> RegulatoryInfoPackageNotificationRecord: +``` + +处理: + +| 步骤 | 说明 | +| --- | --- | +| 创建专项通知记录 | 写 `RegulatoryInfoPackageNotificationRecord` | +| 调用统一通知 | `dispatch_workflow_notification(build_regulatory_info_package_context(batch))` | +| 捕获异常 | 通知失败写记录和 risk_notes,不影响批次下载 | + +--- + +## 十九、测试详细设计 + +| 测试文件 | 覆盖 | +| --- | --- | +| test_regulatory_info_package_models.py | 三张表、zip export type、基础关联 | +| test_regulatory_info_package_trigger.py | 固定关键词与 LLM action | +| test_regulatory_info_package_input_select.py | 文件名模糊匹配、active 附件、多候选反问 | +| test_regulatory_info_package_template_config.py | YAML 加载、模板缺失、code 唯一 | +| test_regulatory_info_package_instruction_extract.py | 说明书章节和组成表抽取 | +| test_regulatory_info_package_field_extract.py | 规则抽取、LLM 三次重试、失败降级 | +| test_regulatory_info_package_field_merge.py | missing、llm_only、conflict | +| test_regulatory_info_package_docx_writer.py | 替换、表格填充、黄底、红字 | +| test_regulatory_info_package_legacy_doc.py | adapter 探测、docx 兜底、失败状态 | +| test_regulatory_info_package_package_generate.py | 7 文件生成结果、多线程异常隔离 | +| test_regulatory_info_package_traceability.py | Excel 追溯和 logs JSON | +| test_regulatory_info_package_zip.py | zip 只包含 success/fallback_success | +| test_regulatory_info_package_workflow.py | 节点流转、partial_success、waiting_user | +| test_regulatory_info_package_views.py | start/status/download 权限 | +| test_regulatory_info_package_frontend.py | chip、卡片、状态 URL | + +--- + +## 二十、异常处理矩阵 + +| 异常 | 批次状态 | 处理 | +| --- | --- | --- | +| 无说明书 | waiting_user 或不创建批次 | 提示上传说明书 | +| 多候选无法匹配 | waiting_user 或不创建批次 | 反问确认文件名 | +| 模板缺失 | failed | 列出缺失模板 | +| 规则抽取失败 | partial_success/continue | 使用 LLM 结果 | +| LLM 三次失败 | continue | 使用规则结果,写 risk_notes | +| 产品名缺失 | partial_success | 写 `/` 黄底,继续生成 zip | +| 单个 docx 文件生成失败 | partial_success | 不进入 zip,摘要展示失败 | +| CH1.9 doc 原生失败但 docx 兜底成功 | success/partial_success | 状态 fallback_success,进入 zip | +| CH1.9 doc 和 docx 兜底均失败 | partial_success | 不进入 zip,摘要展示失败 | +| traceability.xlsx 失败 | partial_success | 不阻断 zip | +| zip 失败 | partial_success | 保留单文件下载 | +| 通知失败 | 不影响主状态 | 写通知失败和 risk_notes | + +--- + +## 二十一、设计结论 + +| 编号 | 结论 | +| --- | --- | +| D1 | 详细设计文档路径为 `docs/4.详细设计/5.第1章监管信息材料包生成.md` | +| D2 | 模型集中在 `review_agent/models.py`,业务模块为 `review_agent/regulatory_info_package/` | +| D3 | `.doc` 采用 A+C:优先 Word COM 原生处理,同时设计适配器层和能力探测 | +| D4 | `.doc` 原生失败时允许 `.docx` 兜底;兜底文件名为 `CH1.9 产品申报前沟通的说明.docx` | +| D5 | zip 只包含成功或兜底成功文件,失败文件不进入 zip | +| D6 | LLM 最多重试 3 次,失败后使用规则结果继续 | +| D7 | 缺失和 LLM-only 黄底,冲突黄底红字 | +| D8 | 产品列表使用 `ProductListRow`,货号固定 `/` 黄底 | +| D9 | 标准清单只复用现有知识库能力,不新增独立 RAG 流程 | +| D10 | 前端最小接入,不做说明书选择弹窗 | +| D11 | 追溯 Excel 可下载,JSON 只放后台 logs | +| D12 | 本期不新增字段级数据库表 | +| D13 | 工作流串行,文档生成节点内部可多线程 | +| D14 | 本轮只产出详细设计,不写代码、不生成迁移 |