30 KiB
第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 |
| 节点幂等 | WorkflowNodeRun 必须按 workflow_type + workflow_batch_id + node_code 幂等创建或加唯一约束 |
| 输入优先级 | 用户消息指定文件名优先;其次 active 附件;再兼容最近成功文件汇总 |
| 模板固定 | 固定处理第1章监管信息 7 个模板 |
| 模板字段化 | 生成逻辑优先写 Word 内容控件 Tag 或稳定占位符,不以手工调整表格格式为前提 |
| 规则优先可演示 | 规则抽取可独立跑通;LLM 失败最多重试 3 次,失败后继续 |
| 文档并发生成 | 工作流整体串行,generate_docs 节点内部每个文档可独立线程并发处理 |
.doc 兜底 |
能力驱动:有 Word COM/UNO 时优先原生 .doc;无原生能力或原生失败时允许生成 .docx 兜底文件 |
| zip 只含成功文件 | zip 只打包成功或兜底成功的文件;失败文件不进入 zip |
| 高亮规则 | 缺失和 LLM-only 黄底;冲突黄底红字 |
| 追溯输出 | 用户下载 Excel;JSON 仅保存到后台 logs 目录 |
| 前端最小接入 | 不做多说明书选择 UI;不确定时通过对话反问 |
二、代码结构设计
2.1 目录结构
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 | 定位样例模板、复制到批次目录、审计字段 Tag/占位符 |
| 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
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
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 增加:
ZIP = "zip", "ZIP"
下载 MIME 按扩展名兜底:
| 条件 | MIME |
|---|---|
| zip | application/zip |
| .doc | application/msword |
| .docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
四、常量设计
4.1 工作流常量
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 触发关键词
REGULATORY_INFO_PACKAGE_TRIGGER_KEYWORDS = [
"根据说明书生成第1章监管信息",
"生成监管信息材料包",
"从说明书生成第1章材料",
"第1章监管信息",
"监管信息材料包",
]
4.3 文件状态
GENERATED_FILE_SUCCESS = "success"
GENERATED_FILE_FALLBACK_SUCCESS = "fallback_success"
GENERATED_FILE_FAILED = "failed"
GENERATED_FILE_SKIPPED = "skipped"
五、核心数据结构
5.1 TemplateSpec
@dataclass(frozen=True)
class TemplateSpec:
code: str
output_name: str
source_file: str
file_format: str
strategy: str
include_in_zip: bool
prefer_legacy_doc_native: bool = False
allow_docx_fallback: bool = True
fields: list[dict[str, Any]] = field(default_factory=list)
5.2 InstructionExtractResult
@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
@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
@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
@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 = ""
六、存储目录设计
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 的选择顺序:
- 用户消息显式指定文件名时,按 active 附件名模糊匹配。
- 当前对话 active 附件中文件名包含“说明书”的
.docx。 - 当前对话 active 附件中唯一
.docx。 - 最近成功
FileSummaryBatch.items中包含“说明书”的.docx。 - 多候选或无候选时返回
InputSelectionResult(status="waiting_user")。
7.2 多候选处理
本期不新增在线选择弹窗。多候选时:
| 场景 | 处理 |
|---|---|
| 用户消息可模糊匹配唯一附件 | 直接选择 |
| 多个候选且无法确定 | 对话反问用户确认哪个说明书 |
| 无说明书 | 提示上传产品说明书 |
反问示例:
我找到多个说明书候选,请回复要使用的文件名:A.docx、B.docx。
八、模板配置详细设计
配置路径:
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 模板需声明 prefer_legacy_doc_native,并配置允许 .docx 兜底 |
8.1 模板字段化约定
为避免生成时破坏 Word 表格、复选框、字号、缩进和合并单元格,本工作流优先使用字段化模板:
| 方式 | 使用场景 | 说明 |
|---|---|---|
| Word 内容控件 Tag | 正式模板优先 | 在 Word 中为产品名、申请人、复选框、日期、说明文字等填写区设置稳定 Tag,代码按 Tag 写入 |
| 稳定占位符 | 过渡方案 | 使用 {{ product_name }} 等不会影响版式的占位符,代码替换占位符所在 run |
| 行标签定位 | 兜底方案 | 仅用于未字段化的旧模板,必须保留原单元格、段落和 run 格式 |
模板配置中的字段目标优先级:
targets:
- type: content_control_tag
tag: product_name
- type: placeholder
marker: "{{ product_name }}"
- type: table_row_label
label: 产品名称
模板加载时必须执行字段审计:关键字段缺少 Tag/占位符时给出清晰错误或降级说明;不得静默使用会破坏格式的整格重建策略。
九、字段抽取详细设计
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 抽取:
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 统一接口
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_content_control | 按内容控件 Tag 填写文本、日期或复选框 |
| replace_placeholder | 按稳定占位符替换文本,保留占位符所在 run/段落格式 |
| fill_table_cell | 按行标签定位目标单元格,仅作为未字段化模板的兜底 |
| replace_table | 重建 CH1.5 产品列表表格 |
| apply_highlight | 使用 w:shd 设置黄色底色 |
| apply_conflict_style | 黄色底色 + 红字 |
10.3 LegacyDocDocumentAdapter
接口:
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: ...
执行顺序:
- 执行能力探测:Word COM、LibreOffice UNO 或其他可写
.doc能力。 - 有原生能力时优先尝试原生打开
.doc并保存.doc。 - 无原生能力或原生失败时,尝试生成同语义
.docx兜底文件,再交给DocxDocumentAdapter。 - 兜底成功时,输出
CH1.9 产品申报前沟通的说明.docx,状态为fallback_success。 - 原生和兜底均失败时,该文件状态为
failed,不进入 zip。
兜底成功 adapter_summary.doc:
{
"requested_format": "doc",
"actual_format": "docx",
"adapter": "ConversionFallbackAdapter",
"status": "fallback_success"
}
十一、材料生成详细设计
11.1 generate_docs 节点并发
工作流节点仍串行执行,但 generate_docs 内部并发生成单文件:
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
用户可下载:
exports/traceability.xlsx
创建导出记录:
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/,按需从后台查看:
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 文件名:
第1章 监管信息(预生成版).zip
规则:
| 场景 | 是否进入 zip |
|---|---|
文件状态 success |
是 |
文件状态 fallback_success |
是 |
文件状态 failed |
否 |
文件状态 skipped |
否 |
若 CH1.9 .doc 兜底 .docx 成功,zip 中放入:
CH1.9 产品申报前沟通的说明.docx
十三、工作流详细设计
13.1 批次创建
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 执行器
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 个模板 |
| template_audit | 审计模板字段 Tag/占位符,记录缺失和降级策略 |
| 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 增加分支:
- 调用
select_instruction_input(conversation, content)。 - 若多候选,回复反问,不启动工作流。
- 若无候选,回复请上传说明书。
- 若唯一候选,创建批次并启动工作流。
- SSE 发送
workflow_started。
14.3 views.py
接口:
GET /api/review-agent/regulatory-info-package/health/
POST /api/review-agent/regulatory-info-package/start/
GET /api/review-agent/regulatory-info-package/<batch_id>/status/
POST /api/review-agent/regulatory-info-package/<batch_id>/select-input/
status 返回:
| 字段 | 说明 |
|---|---|
| batch | 状态、产品名、缺失/LLM-only/冲突数量 |
| nodes | 节点状态 |
| generated_files | 7 个文件成功/失败/兜底状态 |
| exports | zip、单文件、Excel 下载 |
| risk_notes | 风险提示 |
| notifications | 通知 |
zip 不需要 is_primary 字段,前端或摘要按返回顺序把 zip 放首位。
十五、助手摘要设计
完成消息结构:
已生成第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:
<button
class="tool-chip"
type="button"
data-prompt-template="根据说明书生成第1章监管信息"
>第1章监管信息</button>
summaryPanel 增加:
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 增加:
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:
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 采用能力驱动策略:探测 Word COM/UNO 等原生能力,有能力时优先原生处理 |
| 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 | 模板优先字段化,正式填充路径使用内容控件 Tag 或稳定占位符,行标签定位仅作为兜底 |
| D15 | 本轮只产出详细设计,不写代码、不生成迁移 |