# NMPA 注册资料法规核查与整改闭环工作流详细设计 ## 文档信息 | 项目 | 内容 | | --- | --- | | 需求分析文档 | docs/1.需求分析/2.NMPA注册资料法规核查与整改闭环.md | | 功能设计文档 | docs/2.功能设计/2.NMPA注册资料法规核查与整改闭环.md | | 数据库设计文档 | docs/4.数据库设计/2.NMPA注册资料法规核查与整改闭环.md | | 依赖详细设计 | docs/3.详细设计/1.自动汇总.md | | 功能名称 | NMPA 注册资料法规核查与整改闭环 | | 所属模块 | 审核智能体 review_agent | | 设计日期 | 2026-06-06 | | 设计版本 | V1.0 | --- ## 一、详细设计目标 本详细设计用于指导“NMPA 注册资料法规核查与整改闭环”功能开发落地,覆盖代码结构、通用工作流改造、法规核查执行器、规则/RAG/LLM 调用边界、服务拆分、接口契约、前端交互、飞书 CLI 通知、过程产物留底、异常重试和测试建议。 核心约束: | 约束 | 说明 | | --- | --- | | 复用自动汇总 | 不重复实现上传、解压、扫描和页数统计,法规核查基于 `FileSummaryBatch` 执行 | | 独立工作流 | 法规核查有独立 `RegulatoryReviewBatch` 和卡片,事件机制与文件汇总共用 | | 通用事件模型 | `WorkflowNodeRun`、`WorkflowEvent`、`ExportedSummaryFile` 增加 workflow_type 和 workflow_batch_id | | 异步执行 | 启动接口立即返回 batch_id,后台执行并通过 SSE 更新卡片 | | 暂停恢复 | 遇到 waiting_user 时后台任务结束,用户确认后重新唤起执行器继续 | | 规则优先 | 结构化规则负责合规判断,RAG 只补充依据,LLM 只用于低置信度字段抽取和建议润色 | | 过程留底 | 文本抽取、RAG 结果、LLM 输出、通知和复核记录均生成过程产物 | --- ## 二、代码结构设计 ### 2.1 目录结构 在 `review_agent` 应用内新增 `regulatory_review/` 模块。法规核查与文件汇总并列,通过共享工作流事件和导出服务协同。`review_agent/workflow/` 是对模块 1 中 `file_summary/events.py`、节点状态和导出记录能力的通用化抽取,不是为法规核查重建一套并行事件体系。 ```text review_agent/ models.py urls.py views.py file_summary/ ... workflow/ __init__.py constants.py events.py node_runs.py exports.py regulatory_review/ __init__.py constants.py schemas.py urls.py views.py workflow.py storage.py services/ __init__.py rule_loader.py rag_citation.py info_extract.py text_extract.py completeness_check.py structure_check.py consistency_check.py risk_assess.py export.py feishu_notifier.py rectification_review.py condition_parser.py rules/ nmpa_ivd_registration_v1.yaml prompts/ condition_parse.md field_extract.md suggestion_polish.md ``` ### 2.2 文件职责 | 文件 | 职责 | | --- | --- | | workflow/constants.py | 通用 workflow_type、节点状态、事件类型 | | workflow/events.py | 通用 SSE 事件持久化和格式化 | | workflow/node_runs.py | 通用节点状态创建、更新和恢复 | | workflow/exports.py | 通用导出记录和下载权限校验 | | regulatory_review/constants.py | 法规核查节点、风险等级、问题状态常量 | | regulatory_review/schemas.py | RegulatoryContext、NodeResult、Finding 等 dataclass | | regulatory_review/workflow.py | RegulatoryWorkflowExecutor,负责编排节点和暂停恢复 | | regulatory_review/storage.py | 法规核查过程产物路径、hash、文件保存 | | services/rule_loader.py | 加载规则版本、校验 hash、裁剪适用规则 | | services/rag_citation.py | 基于 findings 批量检索法规依据 | | services/info_extract.py | 从文件清单和文本片段抽取适用条件候选值 | | services/condition_parser.py | 将用户自然语言确认解析为结构化字段 | | services/text_extract.py | 统一抽取关键文件文本并缓存为 JSON 产物 | | services/completeness_check.py | 完整性核查,生成 findings | | services/structure_check.py | 章节结构核查,生成 findings | | services/consistency_check.py | 跨文件一致性核查,生成 findings | | services/risk_assess.py | 去重、风险分级、RAG 依据引用、写入 RegulatoryIssue | | services/export.py | 生成最终报告和过程产物,支持重试 | | services/feishu_notifier.py | 通过飞书 CLI 发送通知,支持 3 次重试 | | services/rectification_review.py | 补充资料后的问题复核和状态更新 | --- ## 三、通用工作流改造 ### 3.1 WorkflowNodeRun 改造 现有节点状态表需要兼容多类工作流。 | 字段 | 处理 | | --- | --- | | batch_id | 保留,兼容文件汇总旧逻辑 | | workflow_type | 新增,file_summary、regulatory_review | | workflow_batch_id | 新增,保存对应工作流批次 ID | | node_group | 新增,可选,用于法规核查卡片主节点聚合 | 唯一约束调整为: ```text unique(workflow_type, workflow_batch_id, node_code) ``` 文件汇总旧逻辑写入时同步设置: ```text workflow_type = file_summary workflow_batch_id = file_summary_batch.id batch_id = file_summary_batch.id ``` ### 3.2 WorkflowEvent 改造 事件表同样新增: | 字段 | 说明 | | --- | --- | | workflow_type | file_summary、regulatory_review | | workflow_batch_id | 对应工作流批次 ID | | conversation_id | 冗余记录对话 ID,便于 SSE 查询 | SSE 查询时按 `conversation_id` 获取多个工作流事件,前端根据 `workflow_type + workflow_batch_id` 更新对应卡片。 ### 3.3 ExportedSummaryFile 改造 最终下载文件表通用化: | 字段 | 说明 | | --- | --- | | workflow_type | file_summary、regulatory_review | | workflow_batch_id | 对应工作流批次 ID | | export_category | summary_report、risk_report、excel_list、json_package | 法规核查最终 Markdown、Excel、JSON 结果包进入 `ExportedSummaryFile`;过程产物进入 `RegulatoryArtifact`。 --- ## 四、核心数据结构 ### 4.1 RegulatoryContext 节点间传递统一上下文,避免每个服务重复组装状态。 ```python @dataclass class RegulatoryContext: regulatory_batch: RegulatoryReviewBatch file_summary_batch: FileSummaryBatch | None rule_version: RegulatoryRuleVersion | None rules: dict[str, Any] scoped_rules: list[dict[str, Any]] conditions: dict[str, Any] file_items: list[FileSummaryItem] text_artifacts: dict[str, Any] findings: list["Finding"] issues: list[RegulatoryIssue] artifacts: list[RegulatoryArtifact] reference_only: bool = False ``` ### 4.2 NodeResult 每个节点统一返回 `NodeResult`。 ```python @dataclass class NodeResult: status: str message: str = "" payload: dict[str, Any] = field(default_factory=dict) findings: list["Finding"] = field(default_factory=list) artifacts: list[RegulatoryArtifact] = field(default_factory=list) next_node: str | None = None ``` ### 4.3 Finding 核查服务只返回 findings,不直接写 `RegulatoryIssue`。Issue 由 `RiskAssessService` 统一去重、分级和落库。 ```python @dataclass class Finding: finding_key: str issue_type: str initial_risk_level: str title: str description: str rule_id: str | None = None file_item_id: int | None = None file_path: str | None = None page_no: int | None = None field_name: str | None = None evidence: dict[str, Any] = field(default_factory=dict) suggestion_template: str | None = None source_node: str | None = None ``` --- ## 五、工作流执行设计 ### 5.1 启动流程 ```text POST /regulatory-review/start/ -> 创建 RegulatoryReviewBatch(status=pending) -> 查找当前对话最近一次 success FileSummaryBatch -> 如有则绑定并异步启动法规核查 -> 如无则创建 FileSummaryBatch 并启动自动汇总 -> 自动汇总 success 后回填 file_summary_batch_id -> 继续法规核查 prepare 节点 ``` 如果用户明确说“重新核查最新上传资料”,系统强制创建新的 `FileSummaryBatch`,再创建新的 `RegulatoryReviewBatch`。 ### 5.2 暂停与恢复 当适用条件缺失或解析冲突时: ```text RegulatoryWorkflowExecutor -> 写入 condition_confirm 节点 status=waiting_user -> RegulatoryReviewBatch.status=waiting_user -> 发送 workflow SSE -> 后台任务结束 ``` 用户确认后: ```text POST /regulatory-review/{batch_id}/confirm-condition/ -> LLM 解析自然语言为结构化 JSON -> 字段校验器校验必填字段 -> 如仍缺失,继续追问并保持 waiting_user -> 如完整,写入 batch 核心字段和 condition_json -> 重新唤起 RegulatoryWorkflowExecutor,从 rule_scope 节点继续 ``` ### 5.3 节点调度 ```text prepare -> info_extract -> condition_confirm 或 rule_scope -> rule_scope -> completeness_check -> text_extract -> 并行执行 structure_check 和 consistency_check -> risk_assess -> report_export -> notify -> completed ``` 章节核查和一致性核查通过后台线程池并行: ```python with ThreadPoolExecutor(max_workers=2) as pool: structure_future = pool.submit(structure_service.run, context) consistency_future = pool.submit(consistency_service.run, context) ``` ### 5.4 关键节点 关键节点失败时终止批次: | 节点 | 失败处理 | | --- | --- | | prepare | 无法绑定文件汇总批次,批次 failed | | rule_scope | 规则 hash 不一致,批次 failed;规则加载失败可降级 reference_only | | report_export | 最终报告重试失败,批次 failed | 非关键节点失败时生成 `Finding` 或 `RegulatoryIssue`,工作流尽量继续: | 节点 | 失败处理 | | --- | --- | | text_extract | 对相关文件生成待确认 finding | | structure_check | 生成章节核查失败 finding | | consistency_check | 生成一致性待确认 finding | | notify | 写通知失败记录,批次可 partial_success | --- ## 六、规则、RAG 与 LLM 设计 ### 6.1 RuleLoader 流程: ```text 读取当前 active RegulatoryRuleVersion -> 读取 rule_file_path -> 计算文件 hash -> 与 rule_file_hash 比对 -> hash 一致则解析规则 -> 按适用条件裁剪 scoped_rules ``` 处理策略: | 场景 | 处理 | | --- | --- | | 规则文件 hash 不一致 | 停止执行并标记 failed | | 规则文件不存在或解析失败 | 降级 RAG 辅助核查,batch.status=reference_only | | RAG 索引版本缺失 | 记录提示项,但规则核查可继续 | ### 6.2 RagCitationService RAG 在 `RiskAssessService` 阶段批量调用,而不是每个核查节点实时调用。 输入: | 字段 | 说明 | | --- | --- | | findings | 所有核查 findings | | rule_version | 当前法规规则版本 | | scoped_rules | 本次适用规则 | 输出: | 字段 | 说明 | | --- | --- | | citations_by_finding | finding_key 到法规依据列表的映射 | | rag_result_json | RAG 检索结果过程产物 | ### 6.3 LLM 调用边界 | 场景 | 是否调用 LLM | 说明 | | --- | --- | --- | | 自然语言适用条件解析 | 是 | 解析为结构化 JSON,再由字段校验器校验 | | 低置信度字段抽取 | 是 | 规则/正则失败或置信度低时调用 | | 整改建议润色 | 是 | 规则模板生成标准动作,LLM 润色表达 | | 风险等级判断 | 否 | 风险等级由规则和 RiskAssess 决定 | | 法规结论判断 | 否 | 合规判断不交给 LLM | LLM 抽取结果需写入过程产物,可使用 `llm_extract_json` 或并入 `text_extract_json`。 --- ## 七、服务详细设计 ### 7.1 RegulatoryWorkflowExecutor | 方法 | 说明 | | --- | --- | | start(batch_id) | 创建后台任务并返回 | | run(batch_id, start_node=None) | 运行法规核查节点 | | build_context(batch_id) | 组装 RegulatoryContext | | run_node(node_code, context) | 执行单个节点并处理 NodeResult | | run_parallel_checks(context) | 并行执行章节和一致性核查 | | pause_for_user(batch, node_code, message) | 写 waiting_user 状态并结束任务 | | complete(batch) | 标记批次完成 | | fail(batch, error) | 标记批次失败 | ### 7.2 ConditionParserService | 方法 | 说明 | | --- | --- | | parse(raw_user_input, previous_conditions) | 使用 LLM 解析自然语言 | | validate(parsed_json) | 校验产品类别、注册类型、临床路径、产品名称、型号规格、预期用途 | | merge(batch, parsed_json) | 写入批次字段和 condition_json | ### 7.3 RiskAssessService | 方法 | 说明 | | --- | --- | | deduplicate(findings) | 按 finding_key、rule_id、file_item_id 去重 | | attach_citations(findings) | 批量调用 RAG 获取法规依据 | | resolve_risk(finding) | 统一风险等级,处理升级/降级 | | generate_suggestion(finding) | 规则模板 + LLM 润色 | | create_issues(batch, findings) | 统一写入 RegulatoryIssue | | build_risk_summary(batch) | 写入 risk_summary_json | ### 7.4 RegulatoryExportService | 方法 | 说明 | | --- | --- | | export_final_markdown(batch) | 生成最终 Markdown 核查报告 | | export_final_excel(batch) | 生成 Excel 缺失清单 | | export_json_package(batch) | 生成结构化 JSON 结果包 | | create_artifact(batch, artifact_type, path) | 写 RegulatoryArtifact 并计算 hash | | create_export_record(batch, path, category) | 写 ExportedSummaryFile | | retry_export(fn, max_retry=3) | 导出失败重试 | 重试策略: | 产物 | 重试后仍失败 | | --- | --- | | 最终 Markdown/Excel/JSON | 批次 failed | | 非关键过程产物 | 批次 partial_success | ### 7.5 FeishuNotifier 调用方式必须使用参数数组,不拼接 shell 字符串。 ```python subprocess.run( [cli_path, "send", "--user", feishu_user_id, "--message", message], check=True, capture_output=True, text=True, ) ``` 处理策略: | 场景 | 处理 | | --- | --- | | 用户无 feishu_user_id | 写通知失败记录,不阻断 | | CLI 执行失败 | 最多重试 3 次 | | 仍失败 | send_status=failed,批次可 partial_success | | 成功 | 写 external_message_id 和 sent_at | 通知内容包含系统内风险报告链接,不附原始文件。 --- ## 八、接口详细设计 ### 8.1 发起法规核查 | 项目 | 内容 | | --- | --- | | URL | POST /api/review-agent/regulatory-review/start/ | | 请求 | conversation_id、file_summary_batch_id 可选、force_resummary 可选 | | 响应 | regulatory_batch_id、workflow_type、status | 响应示例: ```json { "regulatory_batch_id": 2001, "workflow_type": "regulatory_review", "status": "pending" } ``` ### 8.2 确认适用条件 | 项目 | 内容 | | --- | --- | | URL | POST /api/review-agent/regulatory-review/{batch_id}/confirm-condition/ | | 请求 | raw_user_input、可选结构化字段 | | 响应 | status、missing_fields、next_question | 如果解析完整: ```json { "status": "accepted", "next_node": "rule_scope" } ``` 如果仍缺失: ```json { "status": "need_more_info", "missing_fields": ["clinical_evaluation_path"], "next_question": "请确认临床评价路径:临床试验、免临床,还是同品种比对?" } ``` ### 8.3 查询状态 | 项目 | 内容 | | --- | --- | | URL | GET /api/review-agent/regulatory-review/{batch_id}/ | | 响应 | 批次、节点、风险摘要、导出文件、过程产物 | ### 8.4 发起整改复核 | 项目 | 内容 | | --- | --- | | URL | POST /api/review-agent/regulatory-review/{batch_id}/rectify-review/ | | 请求 | issue_ids、file_summary_batch_id 或 uploaded_file_ids | | 响应 | review_status、updated_issues、review_artifact_id | 补充文件必须复用自动汇总上传与汇总能力。上传后先生成新的 `FileSummaryBatch`,再由 `RectificationReviewService` 对原批次问题执行复核。复核不创建新的 `RegulatoryReviewBatch`。 --- ## 九、前端与对话交互 ### 9.1 工作流卡片 | 设计点 | 说明 | | --- | --- | | 卡片切换 | 多工作流卡片使用轮播切换 | | 卡片识别 | 使用 workflow_type + workflow_batch_id | | 状态来源 | SSE workflow 事件 | | 法规卡片 | 展示主节点和可展开子节点 | | waiting_user | 卡片显示等待确认,对话框给出选择和追问 | ### 9.2 自然语言确认 对话框中用户可以用自然语言确认,例如: ```text 按体外诊断试剂首次注册处理,临床评价路径走同品种比对,产品名称是 XXX,型号规格是 YYY,预期用途是 ZZZ。 ``` 后端解析并校验后继续工作流。原始输入写入 `condition_json.raw_user_input`。 ### 9.3 整改复核触发 Demo 阶段通过对话指令触发: ```text 我已补充注册检验报告,请复核阻断项。 ``` 系统识别后调用复核接口,要求用户上传补充文件或选择已上传文件。 --- ## 十、过程产物与报告 ### 10.1 文件命名 过程产物和最终报告采用固定模板: ```text {batch_no}_{artifact_type}.{ext} ``` 示例: ```text RRB202606060001_rule_matrix.xlsx RRB202606060001_risk_list.json RRB202606060001_final_report.md ``` ### 10.2 文件保存 路径: ```text media/regulatory_review/{user_id}/{conversation_id}/{batch_id}/ ``` 所有 `RegulatoryArtifact` 必须计算 SHA-256 hash。 ### 10.3 报告内容 最终 Markdown 报告包含: | 模块 | 说明 | | --- | --- | | 核查概览 | 批次、规则版本、RAG 版本、上传人 | | 适用条件 | 系统抽取和用户确认结果 | | 风险清单 | 五级风险、状态、责任人、建议 | | 法规核查矩阵 | 应有文件、实际文件、缺失情况 | | 章节核查结果 | 缺失章节、异常章节 | | 一致性核查结果 | 字段冲突和来源文件 | | 飞书通知记录 | 发送对象、状态、失败原因 | | 整改复核记录 | 复核方式、复核结果、关闭确认 | --- ## 十一、异常与重试 | 场景 | 处理 | | --- | --- | | 无成功 FileSummaryBatch | 自动启动文件汇总,成功后继续 | | 文件汇总失败 | 法规核查批次 failed | | 规则 hash 不一致 | 法规核查批次 failed | | 规则加载失败 | 降级 reference_only,仅输出参考性结果 | | 用户确认信息缺失 | waiting_user,追问缺失字段 | | 文本抽取失败 | 生成待确认 finding,继续后续节点 | | 章节或一致性节点失败 | 生成对应 issue,继续风险汇总 | | RAG 检索无结果 | 规则问题仍输出,依据标记原文待补充 | | LLM 调用失败 | 回退规则/正则结果,低置信度项待确认 | | 飞书失败 | 重试 3 次,仍失败写通知失败记录 | | 最终报告导出失败 | 重试 3 次,仍失败 batch failed | | 非关键产物导出失败 | 重试 3 次,仍失败 batch partial_success | --- ## 十二、测试建议 ### 12.1 单元测试 | 模块 | 测试点 | | --- | --- | | RuleLoader | hash 校验、规则解析、规则裁剪、加载失败降级 | | ConditionParserService | 自然语言解析、缺失字段追问、原始输入留痕 | | TextExtractService | 首页文本、章节文本、抽取失败产物 | | CompletenessCheckService | 文件名/目录名/首页内容三层匹配 | | StructureCheckService | 必需章节缺失识别 | | ConsistencyCheckService | 字段冲突、低置信度 LLM 辅助 | | RiskAssessService | findings 去重、风险升级/降级、Issue 落库 | | RegulatoryExportService | 文件命名、hash、导出重试 | | FeishuNotifier | 参数数组调用、3 次重试、失败记录 | ### 12.2 集成测试 | 场景 | 验证 | | --- | --- | | 已有汇总批次发起核查 | 默认复用最近 success 批次 | | 无汇总批次发起核查 | 自动串联文件汇总后继续 | | waiting_user 暂停恢复 | 用户确认后从 rule_scope 继续 | | 章节和一致性并行 | 两个节点均完成后进入 risk_assess | | 规则加载失败 | batch.status=reference_only | | 飞书失败 | 不阻断报告,通知记录 failed | | 补充文件复核 | 新 FileSummaryBatch 生成,原 Issue 状态更新 | ### 12.3 验收测试 | 序号 | 验收项 | 标准 | | --- | --- | --- | | 1 | 多工作流卡片 | 文件汇总和法规核查卡片可切换且状态独立 | | 2 | 条件确认 | 用户自然语言确认后能结构化入库 | | 3 | 完整性核查 | 能识别缺失注册检验报告等问题 | | 4 | 章节核查 | 能识别关键章节缺失 | | 5 | 一致性核查 | 能识别产品名称、型号规格、预期用途冲突 | | 6 | 风险报告 | 输出 Markdown、Excel、JSON 结果包 | | 7 | 飞书通知 | 阻断项、高风险、中风险能 @ 上传人 | | 8 | 过程留底 | RAG、文本抽取、通知、复核均有 artifact | | 9 | 整改复核 | 补充文件后原 Issue 可进入复核通过或复核不通过 | --- ## 十三、实施顺序建议 结合当前优先级,建议先打通 RAG 和 LLM 能力,再落完整工作流: 1. 构建本地法规材料 RAG 索引,并实现 `RagCitationService`。 2. 实现适用条件解析和低置信度字段抽取的 LLM 调用封装。 3. 完成数据库模型和通用 workflow/export 表改造。 4. 实现 `RuleLoader` 与规则 hash 校验。 5. 实现 `RegulatoryWorkflowExecutor`、`RegulatoryContext`、`NodeResult`。 6. 实现完整性、文本抽取、章节核查、一致性核查和风险归并。 7. 实现报告导出、过程产物 hash 和导出重试。 8. 接入飞书 CLI 通知和 3 次重试。 9. 改造前端多工作流卡片和适用条件确认交互。 10. 实现整改复核和 Issue 状态流转。