# 反应模块 — 设计方案 **项目**:有机化学反应工作台 · 反应模块 **版本**:1.0(评审通过) **日期**:2026-09-18 **文档类型**:模块设计方案 **上游文档**:[设计方案](./设计方案.md)、[协议规范](./协议规范.md)、[用户手册](./用户手册.md)、[原型设计文档](../设计文档.md) > 本版已纳入评审结论,评审记录见 [§13 已确认决策](#13-已确认决策)。 --- ## 1. 定位与范围 ### 1.1 定位 分子构建器解决「怎么把一个分子画出来」。反应模块解决「怎么把多个分子组成一个反应,并推导产物」。二者共享同一份分子图数据(`MoleculeGraph`),反应模块是分子构建器的下游消费者。 ``` 分子构建器 ── ├──▶ 分子库(localStorage)──▶ 反应模块 ──▶ 反应式 / 产物 / 导出 试剂与条件库 ─┘ ``` ### 1.2 本期范围 | 纳入 | 说明 | |------|------| | **反应式绘制** | 反应物 / 试剂 / 产物 + 箭头 + 条件文字 + 计量配平 + 规范化导出 | | **反应预测** | 本地规则库推导候选产物(首批 67 条规则,见 §8);外部服务兜底**本期一并落地** | | **试剂与条件库** | 内置可检索的常用试剂、催化剂、溶剂与条件预设 | | **导出** | SVG / PNG / **PDF** / **化学方程式** / RXN / 反应 SMILES,见 §10.4 | | 不纳入 | 原因 | |--------|------| | **逆合成分析** | 依赖预测规则的反向搜索与路线评分,复杂度显著高于正向预测,留作后续阶段 | | 机理箭头(推电子) | 属独立表达体系,与结构式编辑模型不同 | | 反应收率 / 条件推荐 | 需实验数据集支撑,超出原型范围 | | 后端服务 | 延续纯前端原型定位,持久化仍用 localStorage | ### 1.3 设计原则 1. **复用优先**:反应物/产物就是 `MoleculeGraph`,反应的化学校验、命名、渲染、导出全部复用分子构建器既有模块。 2. **本地可离线**:规则库、配平、绘制、导出全部本地完成;联网仅为可选增强。 3. **不假装精确**:自研规则的产物标注为「候选」,给出命中位点与依据,不声称概率置信度。 4. **可解释**:每一次预测都要能回答「命中了哪条规则、作用于哪些原子、用了哪些试剂」。 5. **区分「能算」与「只能说」**:并非所有化学知识都能产出确定的结构。规则按产物形态分三类,引擎与 UI 分别对待(见 §8.1)——**不把性质描述伪装成结构预测**。 --- ## 2. 技术前提与约束(已实测核实) ### 2.1 RDKit MinimalLib 不含反应能力 对当前依赖 `@rdkit/rdkit@2025.3.4-1.0.0` 的实测结论: | 检查对象 | 结果 | |----------|------| | `dist/index.d.ts` 中检索 `rxn / reaction / smirks / RunReactants` | 无匹配 | | `public/rdkit/RDKit_minimal.js` 中检索 `get_rxn / RunReactants / ReactionFromSmarts` | 无匹配 | **结论**:无法使用反应 SMARTS 做变换,也无法用 RDKit 直接跑反应。**反应变换必须自研**,且只在自有 `MoleculeGraph` 上执行。 ### 2.2 RDKit 可用能力(用于辅助) 同一份实测确认可用的接口: | 接口 | 用途 | |------|------| | `get_mol(input)` / `get_qmol(input)` | 构建分子 / 构建 SMARTS 查询对象 | | `mol.get_substruct_match(q)` | 单次子结构匹配,返回匹配到的原子与键 | | `mol.get_substruct_matches(q)` | 全部匹配位置 | | `SubstructLibrary.get_matches(q)` | 批量子结构检索 | | `mol.get_smiles()` / `get_inchi()` / `is_valid()` | 产物规范化与合法性校验 | | `mol.get_descriptors()` | 分子描述符扩展位 | **结论**:**官能团识别**与**产物规范化**可依托 RDKit,这恰好覆盖规则引擎最需要外部支撑的两端。 ### 2.3 无稳定免费的公开反应预测接口 | 方案 | 状态 | |------|------| | PubChem PUG REST | 域仅 `compound / substance / assay`,**无 reaction 域**,不能做反应查询 | | IBM RXN for Chemistry | 需申请 API Key,学术免费额度有限 | | ASKCOS(MIT) | 开源可自托管,但需部署 Python 服务与模型权重 | | ORD(Open Reaction Database) | 是数据集与 schema,不是在线预测服务 | **结论**:外部兜底只能作为「用户自备凭据」的可插拔适配器,**默认关闭**,且必须提供隐私提示(会把结构外发给第三方)。 ### 2.4 标准格式的可用性 | 格式 | 说明 | |------|------| | **RXN V2000** | `$RXN` 头 + 若干 molblock 分块;现有 `graphToMolblock()` 可直接复用作分块内容 | | **反应 SMILES** | `反应物>试剂>产物`,多组分以 `.` 连接;不支持计量系数 | | **RInChI** | 反应标准标识符,需外部工具生成,本期不实现,仅在去重键设计中预留 | --- ## 3. 总体架构 ### 3.1 分层 ``` ┌────────────────────────────────────────────────────────────┐ │ components/ ReactionView · SchemeCanvas · ComponentCard │ │ ArrowEditor · ConditionsForm · ReagentPanel │ │ PredictionPanel │ ├────────────────────────────────────────────────────────────┤ │ store/ reactionStore(反应式 + 历史) │ │ reagentStore(试剂与条件库) │ ├────────────────────────────────────────────────────────────┤ │ engine/ balance.ts 计量配平(线性代数) │ │ smarts.ts RDKit SMARTS 桥接与原子索引映射 │ │ reactionRules.ts 规则定义与匹配 │ │ reactionOps.ts 变换算子执行与产物校验 │ │ reactionPredict.ts 预测编排与候选排序 │ │ reactionSerialize.ts RXN / 反应 SMILES │ ├────────────────────────────────────────────────────────────┤ │ data/ reagents.ts 内置试剂与条件预设 │ │ rules/*.ts 内置反应规则集 │ ├────────────────────────────────────────────────────────────┤ │ 复用 engine/{moleculeOps,valence,ring,descriptors, │ │ naming} · layout/* · render/* · lib/* │ └────────────────────────────────────────────────────────────┘ ``` ### 3.2 关键复用点 | 既有模块 | 反应模块中的用法 | |----------|------------------| | `types/molecule.ts` | `MoleculeGraph` 直接作为反应组分的结构载体 | | `engine/moleculeOps.ts` | 变换算子复用其 `settle()`(芳香性规范化 + 隐式氢重算)与超价校验 | | `engine/descriptors.ts` | 扩展出 `elementCounts()`,供配平与守恒校验使用 | | `engine/naming.ts` | 产物直接进入既有命名管线,得到中英文名与标识符 | | `layout/arrange.ts` | 组分内部排版;新增组分行排版 | | `render/*` | 组分渲染复用同一套 SVG 渲染器 | | `lib/localStore.ts` | 反应式持久化 | ### 3.3 数据流 ``` 用户组入组分(从分子库 / 当前分子 / 试剂库) ↓ reactionStore 维护 ReactionScheme(不可变更新 + 历史栈) ↓ 配平:elementCounts → 线性方程组 → 最小正整数系数 ↓ 预测(可选):smarts 匹配 → 规则命中 → 变换算子 → 候选产物 ↓ 产物规范化:normalizeAromatics + recalcImplicitH + RDKit 校验 ↓ 候选排序 → 用户采纳 → 写入 products ``` --- ## 4. 数据模型 ### 4.1 ReactionScheme ```ts type ArrowKind = 'forward' | 'equilibrium' | 'resonance' interface SchemeComponent { id: string molecule: MoleculeGraph /** 计量系数,正整数,默认 1 */ coefficient: number } interface AgentEntry { id: string /** 自由文本名,如 "Pd/C"、"reflux" */ text: string /** 可选结构(来自试剂库条目或分子库) */ molecule?: MoleculeGraph /** 来源试剂库条目 id */ reagentId?: string } interface ReactionConditions { /** 箭头上下方文字(自由文本,支持上下标) */ above?: string below?: string temperature?: string solvent?: string time?: string atmosphere?: string note?: string } interface ReactionScheme { id: string reactants: SchemeComponent[] agents: AgentEntry[] products: SchemeComponent[] arrow: { kind: ArrowKind } conditions: ReactionConditions title?: string createdAt: number /** 去重键,见 §10.3 */ key?: string } ``` ### 4.2 与分子图的关系 - 反应物与产物**都是完整的 `MoleculeGraph`**,含坐标、隐式氢、芳香性标记,与分子库条目同构,可直接存取。 - 试剂的 `molecule` 可缺省(很多试剂如 Pd/C、分子筛没有确定结构)。 - **计量系数不属于分子图**,只存在于 `SchemeComponent`,导出 RXN/SMILES 时按各自规则处理(RXN 复制 molblock,SMILES 忽略)。 ### 4.3 不变量 1. 反应式内的组分 `id` 唯一。 2. 反应物与产物侧的分子图**必须元素守恒**才能通过配平(除非用户显式关闭校验)。 3. 试剂的 `molecule` 不参与配平与守恒校验。 4. 任何预测产出的产物都必须通过超价校验与 RDKit `is_valid()`,否则不进入候选列表。 --- ## 5. 反应式绘制 ### 5.1 视图结构 反应模块采用**顶层视图切换**:顶栏在现有「分子构建器」旁增加「反应」入口。 ``` ┌──────────────────────────────────────────────────────────────┐ │ 有机化学反应工作台 [分子构建器] [反应] │ ├──────────────┬──────────────────────────────┬────────────────┤ │ 组建分面板 │ 反应式画布 │ 属性面板 │ │ · 从分子库 │ ┌────┐ ┌────┐ │ 箭头与条件 │ │ · 从当前分子 │ │组分│+ │组分│ ──▶ ┌────┐ │ 配平状态 │ │ · 从试剂库 │ └────┘ ────┘ │产物│ │ 预测候选 │ │ · 试剂/条件 │ 反应物侧 产物侧 │ 组分属性 │ │ 预测 │ │ │ │ · 规则预测 │ │ │ │ · 外部服务 │ │ │ └──────────────┴──────────────────────────────┴────────────────┘ ``` **为什么不复用单分子画布的交互**:单分子画布是单一坐标系 + 悬停编辑;反应式是横向多组分 + 组间操作。两者交互冲突,强行合并会让双方都变复杂。 ### 5.2 组分卡片(R1 阶段) 每个组分渲染为一个「卡片」: - 卡片内是该分子的**自动整理后的 2D 结构式**(复用 `arrangeLayout` + `render/*`),带独立坐标系与缩放适配。 - 卡片左上角显示计量系数,可点击编辑(正整数)。 - 卡片下方显示名称与分子式(复用命名管线)。 - **双击卡片**进入分子构建器编辑该组分,返回后替换(R1 阶段的取舍)。 - 卡片可拖拽重排、可在反应物侧 ↔ 产物侧之间拖动迁移、可从试剂库拖入。 > **R2 演进方向**:卡片内联编辑(在卡片坐标系内直接触发既有的悬停工具条),免除往返。R1 先用「双击进入」换取实现成本。 ### 5.3 箭头与条件 - 箭头支持三种:正向 `→`、平衡 `⇌`、共振 `↔`。 - 箭头上下方各有一个文本槽(`above` / `below`),自由输入,用于写试剂与条件(如上方 `PCC`、下方 `CH₂Cl₂, 25 °C`)。 - 右侧属性面板提供结构化条件字段(温度 / 溶剂 / 时间 / 气氛),填写后**自动同步**到箭头文字槽(可手动覆盖,覆盖后不再自动同步)。 - 文本支持下标(`H₂SO₄`)与升温符号(`Δ`)的快捷插入按钮。 ### 5.4 配平状态提示 - 实时显示每个元素的守恒情况:`C 4=4 ✓ · H 8=8 ✓ · O 2=2 ✓`。 - 不守恒时高亮差异元素,并给出提示「左侧比右侧多 2 H」。 - 一键配平:见 §6。 - 允许关闭校验(用于画半反应或未完成的草稿),关闭时画布上显示"未配平"角标。 ### 5.5 渲染规范 | 项目 | 规范 | |------|------| | 组分间距 | 水平 ≥ 1.5 倍键长 | | 加号 | 组分之间居中,位于组分垂直中线上 | | 箭头长度 | 按左右两侧内容自适应,最小 2 倍键长 | | 系数位置 | 组分左上角,字号略小于原子标签 | | 反应物 → 产物 | 水平流向,长反应式自动换行(按箭头分段) | | 导出 | 复用 `captureSvg` 逻辑,输出含箭头与条件的完整反应式 | --- ## 6. 计量配平 ### 6.1 问题表述 设反应物系数 $x_i$、产物系数 $y_j$,对每个元素 $e$ 要求原子守恒: $$\sum_i a_{ie}\,x_i - \sum_j b_{je}\,y_j = 0$$ 即一个齐次线性方程组 $A\mathbf{z} = 0$,求**最小正整数解**。 ### 6.2 实现步骤 1. **元素计数**:新增 `engine/descriptors.ts` 的 `elementCounts(graph): Record`,从图直接统计(含隐式氢与显式 H)。现有 `molecularFormula()` 返回的是含电荷标记的字符串,不适合解析,因此抽出该函数并让 `molecularFormula` 复用它,保证两处口径一致。 2. **构造矩阵**:行 = 元素种类,列 = 各组分(反应物取正、产物取负)。 3. **求解零空间**:整数行变换的高斯消元(记录变换矩阵),避免浮点与分数误差。 4. **取最小解**:对基向量做整数线性组合,搜索使系数全为正且绝对值之和最小者。 5. **归一**:整体除以最大公约数,得到最小正整数系数。 ### 6.3 分支处理 | 情况 | 处理 | |------|------| | 唯一解 | 直接应用,Toast「已配平」 | | 多解(零空间维数 > 1) | 提示「存在多组配平方案」,给出一个解并让用户手动调整 | | 无正解 | 提示「原子不守恒」,列出差异元素与差值,高亮相关组分 | | 元素种类或组分数过大(> 30) | 跳过配平,仅做守恒提示 | --- ## 7. 反应预测引擎 ### 7.1 规则结构 规则是**声明式数据**,不是代码,便于扩充与校对。 ```ts /** 规则产物形态,决定引擎处理路径与 UI 呈现(见 §8.1) */ type RuleKind = 'transform' | 'qualitative' | 'polymer' interface ReactionRule { id: string // 如 "R-ALC-05" name: string // "伯醇催化氧化为醛(Cu/Ag, Δ)" category: RuleCategory /** 产物形态;决定是否参与配平、是否写入产物侧 */ kind: RuleKind /** 反应中心模式:SMARTS + 命名占位符 */ pattern: { smarts: string /** SMARTS 原子序号 → 占位名,如 { 0: 'Cα', 1: 'OH' } */ labels: Record /** 需要的反应中心副本数(分子间反应 > 1),如炔三聚为 3 */ copies?: number } /** 试剂约束:命中可提升排序;kind='transform' 时决定条件分流 */ agents?: { /** 必需试剂(试剂库条目 id) */ required?: string[] /** 任选一组即可 */ anyOf?: string[][] /** 排除试剂(出现则不适用) */ exclude?: string[] } /** 条件约束:温度等结构化条件,用于分流(如醇 140/170 ℃) */ conditions?: { temperatureRange?: [number, number] } /** kind='transform' 时必填:变换动作序列 */ transforms?: TransformOp[] /** kind='qualitative' 时必填:结论与现象文字 */ conclusion?: { /** 结论,如 "FeCl₃ 显紫色(鉴别酚)" */ text: string /** 现象描述,如 "溶液显紫色" */ phenomenon?: string /** 是否可生成方程式(燃烧类为 true) */ equationFrom?: 'combustion' } /** kind='polymer' 时必填:重复单元与聚合物信息 */ polymer?: { /** 重复单元(用于渲染与「插入画布」) */ repeatUnit?: MoleculeGraph name: string // "聚乙烯" degreeNote?: string // "n 为聚合度" } /** 是否为逆反应(供未来逆合成复用),如蛋白质水解与成肽互逆 */ reversible?: boolean /** 同一位点的竞争排序权重 */ priority: number /** 适用条件与出处,展示给用户 */ note?: string reference?: string } type RuleCategory = | 'addition' | 'elimination' | 'substitution' | 'oxidation' | 'reduction' | 'esterification' | 'hydrolysis' | 'condensation' | 'polymerization' | 'property' | 'other' ``` **`kind` 的引擎含义**(对应 §8.1 的 A/B/C 三类): | `kind` | 对应 | 必需字段 | 引擎行为 | |--------|:----:|----------|----------| | `transform` | A | `pattern` + `transforms` | 执行变换 → `settle()` → 超价校验 → 拆分产物 → 守恒校验 → 配平 | | `qualitative` | B | `pattern` + `conclusion` | 不执行变换;产出文字结论卡片;**跳过配平**;`equationFrom: 'combustion'` 时由分子式生成方程式 | | `polymer` | C | `pattern` + `polymer` | 不执行变换;展示重复单元;**跳过配平**(聚合度 n 不可解) | > `transforms` 与 `conclusion` / `polymer` 的互斥性由 TypeScript 联合类型在实现时进一步收紧,避免出现「既声称有结构产物、又不给变换」的非法规则。 ### 7.2 变换算子 算子与现有 `engine/moleculeOps.ts` 同构,作用于自有 `MoleculeGraph`: ```ts type AtomRef = { label: string } // 引用 pattern 中的占位原子 type BondRef = { a: AtomRef; b: AtomRef } type TransformOp = | { op: 'setBondOrder'; bond: BondRef; order: BondOrder } | { op: 'removeBond'; bond: BondRef } | { op: 'addBond'; a: AtomRef; b: AtomRef; order: BondOrder } | { op: 'setElement'; atom: AtomRef; element: string } | { op: 'setCharge'; atom: AtomRef; charge: number } | { op: 'addAtom'; to: AtomRef; element: string; order: BondOrder } | { op: 'removeAtom'; atom: AtomRef } ``` 执行后统一走 `settle()`(芳香性规范化 + 隐式氢重算)与超价校验——**与手工编辑完全同一条收尾路径**,这是保证产物合法性的关键。 ### 7.3 官能团识别与原子索引映射 新增 `engine/smarts.ts`,职责: 1. 由 `graphToMolblock(graph)` 构建 RDKit 分子对象(复用 `layout/rdkitLayout.ts` 的 `tryCreateMol` 思路)。 2. **原子索引约定**:molblock 按 `graph.atoms` 数组顺序写出,RDKit 读入后索引 `i` 对应 `graph.atoms[i].atom_id`。该约定需在 `scripts/verify.ts` 中加断言用例固定下来。 3. 用 `get_qmol(smarts)` 构建查询,`get_substruct_matches(q)` 取得全部匹配位置。 4. 返回 `{ atomIds: number[], bondIds: number[], labels: Record }[]`——即把 SMARTS 匹配结果翻译回**自有原子编号**。 5. 失败降级:molblock 无法被 RDKit 解析(如超价结构)时返回空匹配集,并在 UI 提示「结构无法被引擎解析,预测不可用」,不抛异常。 ### 7.4 匹配与冲突消解 1. **多组分统一匹配**:把反应物侧的多个分子合并为一张多连通分量图(现有 `MoleculeGraph` 天然支持),同一条规则即可匹配跨分子的反应中心(如酯化:酸 + 醇)。 2. **位点枚举**:同一规则可能命中多个位点,全部枚举。 3. **竞争排序**:按 `规则 priority × 试剂匹配度 × 位点选择性` 排序,输出 Top-N(默认 3)候选。 4. **试剂匹配度**:`agents.required` 全部命中得满分,`anyOf` 命中得部分分,`exclude` 命中则直接淘汰该候选;用户未填写试剂时按中性分处理,并在结果中标注「未指定试剂,结果仅供参考」。 5. **位点选择性**:优先选择取代基更多 / 更少(按规则声明)的位点;未声明则按位点顺序。 ### 7.5 产物后处理 后处理路径**按 `rule.kind` 分流**(对应 §8.1 的 A/B/C 三类): **`transform`(A 类,完整路径)** 1. 变换后调用 `settle()` 与超价校验,不通过则丢弃该候选并记录原因。 2. 按连通分量**拆分为多个产物分子**,各自成为独立的 `SchemeComponent`(系数由配平确定)。 3. 用 RDKit 生成 canonical SMILES / InChI / InChIKey,`is_valid()` 不通过则丢弃。 4. 元素守恒校验:产物总元素必须等于反应物总元素,否则标记该规则「未配平」并降权(提示用户规则可能只描述了主反应中心)。 5. 复用命名管线给出产物中英文名。 **`qualitative`(B 类,短路路径)** 1. **不执行变换**,直接构造文字结论候选,携带 `conclusion.text` 与 `phenomenon`。 2. **跳过配平与守恒校验**——无结构产物可校验。 3. 若 `conclusion.equationFrom === 'combustion'`,则由反应物分子式调用配平引擎生成燃烧方程式文字(产物固定为 CO₂ + H₂O),附在结论中。 4. 候选**不可写入产物侧**,UI 上不提供「采纳」按钮,仅提供「复制结论」。 **`polymer`(C 类,短路路径)** 1. **不执行变换**,展示 `polymer.repeatUnit` 的结构式与 `polymer.name`。 2. **跳过配平**:聚合度 n 是变量,元素守恒方程无解,强行校验会误报「不守恒」。 3. 提供「把重复单元插入画布」按钮,由用户手工补全聚合度表达。 4. 同样不可写入产物侧。 > 三类的分流点集中在 `reactionPredict.ts` 一处,避免 B/C 类逻辑散落到 UI 层造成「看起来像预测失败」的误读。 ### 7.6 候选呈现 候选按三类**分区展示**,避免混淆: | 分区 | 标题 | 卡片形态 | 可用操作 | |------|------|----------|----------| | A | 结构候选 | 结构式 + 规则信息 | 采纳到产物侧 / 插入画布 | | B | 典型性质与现象 | 文字结论 + 现象 | 复制结论 | | C | 聚合提示 | 重复单元结构式 + 聚合物名 | 插入重复单元 | A 类卡片展示: | 展示项 | 说明 | |--------|------| | 结构式 | 复用渲染器 | | 规则名与类别 | 如「氧化 · 伯醇催化氧化为醛(Cu/Ag, Δ)」 | | 命中位点 | 在反应物卡片上高亮命中原子与键 | | 排序说明 | 为什么排在前面(试剂匹配 / 位点选择性 / 规则优先级) | | 提示 | 规则适用条件与出处、是否元素守恒、是否为多解之一、是否可逆 | B 类卡片展示:结论文字、现象、适用官能团、是否附燃烧方程式。 C 类卡片展示:重复单元结构式、聚合物名、聚合度说明、**明确标注「聚合反应不参与配平」**。 > **措辞约定**:界面一律使用「候选」「建议」而非「预测结果」,不使用百分比置信度。排序是启发式打分,不是概率。B/C 类分区标题使用「典型性质」「聚合提示」,避免被读作预测失败。 ### 7.7 外部兜底适配器(本期落地) ```ts interface PredictionBackend { id: string name: string /** 是否已具备可用条件(凭据、网络) */ available(): Promise predict(input: PredictionInput): Promise } interface PredictionInput { reactants: MoleculeGraph[] agents: string[] conditions: ReactionConditions } ``` | 后端 | 默认 | 说明 | |------|------|------| | `LocalRuleBackend` | 启用 | 本地规则库,唯一必需路径 | | `RxnApiBackend` | 关闭 | 需用户自备 API Key,存 localStorage;首次启用时明确提示**结构将被发送至第三方** | - 编排顺序:本地规则先跑;若**未命中任何规则**且外部后端已启用,再调用外部后端。 - 外部结果与本地结果在 UI 中分组展示,来源标注清晰,不混合排序。 - 后端接口失败一律静默降级为「仅本地结果」,与现有 PubChem 命名的容错策略一致。 - 适配器接口与 `RxnApiBackend` 的具体实现(IBM RXN 等)**本期交付**,见里程碑 R5。默认关闭以保证离线可用与隐私,但代码路径完整可测。 --- ## 8. 反应规则库清单 本节是 R3 的实现依据,覆盖评审确认的首批反应类型(中学有机化学主干 + 常见合成场景)。 ### 8.1 规则的三种产物形态 评审给出的反应类型表中,并非每一项都能产出确定的结构。规则按产物形态分为三类,**引擎与 UI 分别对待**: | 类 | 名称 | 产物 | 参与配平 | UI 呈现 | |:--:|------|------|:--------:|---------| | **A** | 结构变换 | 确定的 `MoleculeGraph`,写入产物侧 | ✓ | 结构式候选卡片,可采纳/插入 | | **B** | 定性结论 | 无结构(现象、性质、无确定产物) | ✗ | 文字结论卡片,可复制;不进入产物侧 | | **C** | 聚合提示 | 聚合物(重复单元 + 聚合度 n) | ✗ | 重复单元结构式 + 聚合物名 + n 提示 | **为什么必须分三类**: - **B 类的存在是化学事实,不是偷懒**。「FeCl₃ 显紫色」「蛋白质遇浓硝酸变黄」是鉴别现象,没有可画的结构;「羧酸与 NaHCO₃ 反应」的产物取决于用哪种碱。若强行编造结构,等于伪造化学结论。B 类在预测面板中作为「该官能团的典型性质」展示,帮助用户判断而非误导。 - **C 类不能参与配平**。加聚/缩聚的聚合度 n 是变量,元素守恒方程无解(例如 `nC₂H₄ → (C₂H₄)ₙ` 两侧 C/H 计数无法用固定系数平衡)。因此 C 类规则**跳过配平校验**,产物侧不写入,仅在面板展示重复单元,并提供「把重复单元插入画布」的按钮供用户手工表达。 - **A 类是唯一的可预测路径**,走完整的变换 → 校验 → 拆分 → 配平 → 命名流程。 统计: | 类 | 条数 | 占比 | |:--:|:----:|:----:| | A 结构变换 | 47 | 70% | | B 定性结论 | 14 | 21% | | C 聚合提示 | 6 | 9% | | **合计** | **67** | 100% | > 表中 SMARTS 为**设计草案**,R3 实现时需逐条用 `get_qmol` 验证并补充 `verify.ts` 用例。验证不通过的规则必须降级或修正,不得带着未验证的 SMARTS 上线。 ### 8.2 碳碳双键(烯烃 C=C) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-ENE-01 | 催化加氢 | H₂ / Ni, Δ | 烷烃 | A | `[CX3]=[CX3]` | | R-ENE-02 | 加卤素 | Cl₂ / Br₂ | 邻二卤代烷 | A | `[CX3]=[CX3]` | | R-ENE-03 | 加卤化氢 | HX | 卤代烷(马氏) | A | `[CX3]=[CX3]` | | R-ENE-04 | 加水 | H₂O / H⁺ 催化 | 醇(马氏) | A | `[CX3]=[CX3]` | | R-ENE-05 | 加聚 | 催化剂 | 聚烯烃 | **C** | `[CX3]=[CX3]` | | R-ENE-06 | 氧化 | 稀冷 KMnO₄ → 邻二醇
酸性加热 → 断键 | 邻二醇 / 羰基化合物 | A | `[CX3]=[CX3]` | | R-ENE-07 | 燃烧 | O₂,点燃 | CO₂ + H₂O | **B** | — | | R-ENE-08 | 1,2-加成 | X₂(共轭二烯) | 邻二卤代烯 | A | `[CX3]=[CX3][CX3]=[CX3]` | | R-ENE-09 | 1,4-加成 | X₂(共轭二烯) | 对位二卤代烯 | A | `[CX3]=[CX3][CX3]=[CX3]` | **规则备注**: - R-ENE-03 / R-ENE-04 标注马氏规则(H 加到含氢较多的碳上);抗马氏加成(HBr/过氧化物)作为 `note` 提及,本期不建独立规则。 - R-ENE-06 的断键产物随取代基不同而不同,实现时按双键两端碳的取代情况枚举产物(双键碳无 H → 酮,有 H → 醛/CO₂),因此该规则在引擎内部展开为子分支。 - R-ENE-07 归 B 类,但可调用配平引擎由分子式**直接生成燃烧方程式文字**(产物固定为 CO₂ + H₂O),这是 B 类中唯一可自动补全系数的情形。 ### 8.3 碳碳三键(炔烃 C≡C) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-YNE-01 | 部分加氢 | H₂ / Lindlar 或 Ni(不足量) | 烯烃 | A | `[CX2]#[CX2]` | | R-YNE-02 | 完全加氢 | H₂ 过量 / Ni, Δ | 烷烃 | A | `[CX2]#[CX2]` | | R-YNE-03 | 加卤素 | X₂ | 卤代烯(可继续加成) | A | `[CX2]#[CX2]` | | R-YNE-04 | 加卤化氢 | HX | 卤代烯(马氏) | A | `[CX2]#[CX2]` | | R-YNE-05 | 加水 | H₂O / HgSO₄, H₂SO₄ | 乙炔 → 乙醛
其他端炔 → 甲基酮 | A | `[CX2]#[CX2]` | | R-YNE-06 | 氧化 | 酸性 KMnO₄ | 羧酸 / CO₂ | A | `[CX2]#[CX2]` | | R-YNE-07 | 燃烧 | O₂,点燃 | CO₂ + H₂O | **B** | — | | R-YNE-08 | 三聚 | 高温催化(如活性炭/苯中) | 苯 | A | `[CX2]#[CX2]`(需 3 分子) | **规则备注**: - R-YNE-05 的产物取决于端炔/内炔,是经典的教学重点,实现时按取代情况分支。 - R-YNE-08 是**分子间**规则:需 3 个乙炔分子参与,依赖 §7.4 的多组分统一匹配,且产物苯由 6 个碳构成,是验证跨分子变换的最佳用例。 ### 8.4 苯环(芳烃) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-ARE-01 | 卤代 | Br₂ / FeBr₃ | 卤苯 | A | `c1ccccc1` | | R-ARE-02 | 硝化 | 浓 HNO₃ + 浓 H₂SO₄,50~60 ℃ | 硝基苯 | A | `c1ccccc1` | | R-ARE-03 | 磺化 | 浓 H₂SO₄,加热 | 苯磺酸 | A | `c1ccccc1` | | R-ARE-04 | 加氢 | 3 H₂ / Ni, Δ | 环己烷 | A | `c1ccccc1` | | R-ARE-05 | 侧链氧化 | 酸性 KMnO₄ | 苯甲酸(需苄位 H) | A | `c1ccccc1[CX4H]` | **规则备注**: - R-ARE-01 ~ 03 属亲电取代,**定位效应**(邻/对位、间位)本期只按已有取代基给出主要产物,作为 `note` 说明;不建立完整的定位规则矩阵。 - R-ARE-04 需要 3 分子 H₂,是**配平引擎的关键用例**(系数 3 由守恒方程自动解出,不由规则硬编码)。 - R-ARE-05 要求苄位有氢(`[CX4H]`),无氢(如叔丁基苯)时不适用——SMARTS 已表达该约束。 ### 8.5 卤代烃(R-X) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-HAL-01 | 水解(取代) | NaOH **水**溶液,Δ | 醇 | A | `[CX4][F,Cl,Br,I]` | | R-HAL-02 | 消去 | NaOH **醇**溶液,Δ | 烯烃 | A | `[CX4][CX4][F,Cl,Br,I]` | **规则备注**: - R-HAL-01 与 R-HAL-02 是**同一底物的条件分流**:试剂是「水溶液」还是「醇溶液」决定产物。这要求 `agents.required` 能区分 `NaOH(aq)` 与 `NaOH(EtOH)`——试剂库需为两者建立独立条目,是 §9 数据设计的关键约束。 - R-HAL-02 要求 β-H 存在,SMARTS 中 `[CX4][CX4][F,Cl,Br,I]` 已表达相邻碳,实现时需再校验 β 位确有 H。 ### 8.6 醇(R-OH) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-ALC-01 | 取代(与 HX) | HX,Δ | 卤代烃 | A | `[CX4][OX2H]` | | R-ALC-02 | 分子间脱水成醚 | 浓 H₂SO₄,140 ℃ | 醚 | A | `[CX4][OX2H]`(需 2 分子) | | R-ALC-03 | 酯化 | 羧酸 / 浓 H₂SO₄,Δ | 酯 | A | `[CX4][OX2H]` | | R-ALC-04 | 分子内脱水(消去) | 浓 H₂SO₄,170 ℃ | 烯烃 | A | `[CX4][CX4][OX2H]` | | R-ALC-05 | 催化氧化 | Cu / Ag,Δ | 伯醇 → 醛
仲醇 → 酮 | A | 伯:`[CX4H2][OX2H]`
仲:`[CX4H1]([#6])[OX2H]` | | R-ALC-06 | 氧化 | 酸性 KMnO₄ | 羧酸 | A | 同 R-ALC-05 | | R-ALC-07 | 燃烧 | O₂,点燃 | CO₂ + H₂O | **B** | — | **规则备注**: - R-ALC-02 与 R-ALC-04 是**同一底物的温度分流**(140 ℃ 成醚 / 170 ℃ 成烯),与 R-HAL-01/02 同理,是「条件决定产物」的第二组用例。 - R-ALC-05 的伯/仲醇分支必须严格区分:叔醇无 α-H,不被催化氧化,SMARTS 天然不匹配——这是 SMARTS 表达化学约束优于手写判断的典型例子。 - R-ALC-03 与羧酸侧的 R-ACD-03 是**同一条反应的两种入口**(从醇看 / 从酸看)。实现时合并为一条规则 `R-EST-FORM`,在两侧各挂一个入口别名,避免重复定义与排序冲突。 ### 8.7 酚(Ar-OH) | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-PHE-01 | 弱酸性(与强碱) | NaOH | 苯酚钠 + H₂O | A | `c[OX2H]` | | R-PHE-02 | 与碳酸钠 | Na₂CO₃ | 苯酚钠 + NaHCO₃ | A | `c[OX2H]` | | R-PHE-03 | 取代 | 浓溴水 | 2,4,6-三溴苯酚 ↓ | A | `c[OX2H]` | | R-PHE-04 | 显色 | FeCl₃ 溶液 | 紫色(鉴别) | **B** | `c[OX2H]` | | R-PHE-05 | 氧化 | 空气 / 酸性 KMnO₄ | 苯醌类(粉红色) | **B** | `c[OX2H]` | | R-PHE-06 | 缩聚 | 甲醛 | 酚醛树脂 | **C** | `c[OX2H]` | **规则备注**: - R-PHE-01/02 归 A 类但产物是**盐**:需要 `setCharge` 算子生成苯酚负离子(`c[O-]`)。这是规则库中首次需要电荷处理的场景,`engine/valence.ts` 的电荷修正逻辑需覆盖。 - R-PHE-03 需在邻、对位同时引入 3 个溴,一条规则内产生 3 处取代,考验算子的多点位应用能力。 - R-PHE-05 归 B 类:氧化产物随条件变化且常为混合物,不做结构承诺。 ### 8.8 醛与酮 | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-ALD-01 | 加成(还原) | H₂ / Ni, Δ | 伯醇 | A | `[CX3H1]=O` | | R-ALD-02 | 银镜反应 | 银氨溶液,水浴加热 | 羧酸铵 + Ag ↓ | A | `[CX3H1]=O` | | R-ALD-03 | 与氢氧化铜 | 新制 Cu(OH)₂,Δ | 羧酸钠 + Cu₂O ↓ | A | `[CX3H1]=O` | | R-ALD-04 | 催化氧化 | O₂ / 催化剂 | 羧酸 | A | `[CX3H1]=O` | | R-ALD-05 | 缩聚 | 甲醛 + 尿素 | 脲醛树脂 | **C** | `[CX3H1]=O` | | R-KET-01 | 加成(还原) | H₂ / Ni, Δ | 仲醇 | A | `[CX3]=O` 且非醛 | | R-KET-02 | 燃烧 | O₂,点燃 | CO₂ + H₂O | **B** | — | **规则备注**: - `[CX3H1]=O` 与 `[CX3]=O` 的区分是**醛/酮分流的根本依据**:醛基碳带 1 个 H,酮羰基碳不带 H。SMARTS 精确表达,无需额外判断。 - R-ALD-02/03 的产物含金属(Ag、Cu₂O),**不属于有机分子图**。设计取舍:只输出有机产物(羧酸/羧酸根)并写入产物侧,金属产物作为 `note` 中的现象文字(「析出银镜」「砖红色沉淀」),不建金属原子节点——避免为无机盐污染分子图模型。 - R-ALD-05 归 C 类(脲醛树脂为交联聚合物)。 ### 8.9 羧酸与酯 | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-ACD-01 | 酸性(与碱) | NaOH / Na₂CO₃ | 羧酸盐 | **B** | `[CX3](=O)[OX2H]` | | R-ACD-02 | 与碳酸氢钠 | NaHCO₃ | 羧酸盐 + CO₂ ↑ | **B** | `[CX3](=O)[OX2H]` | | R-ACD-03 | 酯化 | 醇 / 浓 H₂SO₄,Δ | 酯 + H₂O | A | `[CX3](=O)[OX2H]` | | R-ACD-04 | 缩聚(聚酯) | 二元酸 + 二元醇 | 聚酯 | **C** | `[CX3](=O)[OX2H]` | | R-ACD-05 | 缩聚(聚酰胺) | 二元酸 + 二胺 | 聚酰胺 | **C** | `[CX3](=O)[OX2H]` | | R-EST-01 | 酸性水解 | 稀 H₂SO₄,Δ | 羧酸 + 醇(可逆) | A | `[CX3](=O)[OX2][#6]` | | R-EST-02 | 碱性水解(皂化) | NaOH,Δ | 羧酸钠 + 醇(完全) | A | `[CX3](=O)[OX2][#6]` | **规则备注**: - R-ACD-01/02 归 B 类:产物是盐,且「与 NaHCO₃ 放 CO₂」是**鉴别羧酸的特征现象**(区别于酚),其价值在结论而非结构。若用户确实需要盐的结构,可用 R-PHE-01 的电荷算子手动构造。 - R-EST-01/02 是**条件决定可逆性**的经典:酸性可逆、碱性完全。UI 需在候选卡片上标注「可逆」,提示用户该反应不会进行到底。 - R-ACD-04/05 要求**双官能团**(二元酸 + 二元醇 / 二胺),SMARTS 需表达两个官能团同时存在,是规则库中首批需要「多反应中心」的模式。 ### 8.10 油脂 | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-FAT-01 | 氢化(硬化) | H₂ / Ni, Δ | 饱和甘油三酯 | A | `[CX3]=[CX3]`(脂肪酸链) | | R-FAT-02 | 酸性水解 | 稀 H₂SO₄,Δ | 甘油 + 高级脂肪酸 | A | `[CX3](=O)[OX2][CX4]`×3 | | R-FAT-03 | 碱性水解(皂化) | NaOH,Δ | 甘油 + 高级脂肪酸钠 | A | 同 R-FAT-02 | **规则备注**: - 油脂是**甘油三酯**(甘油 + 3 分子高级脂肪酸),结构较大。R-FAT-02/03 需一次断开 3 个酯键并产出 4 个分子(1 甘油 + 3 脂肪酸/盐),是**产物拆分能力**的最强用例。 - 油脂本身没有唯一结构(取决于脂肪酸组成)。设计取舍:不建「油脂」分子模板,用户需自行构建或从分子库载入具体甘油三酯;规则只负责变换。 ### 8.11 糖类 | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-SAC-01 | 氧化(葡萄糖) | 银氨溶液 / 新制 Cu(OH)₂ | 葡萄糖酸(盐) | A | `[CX3H1]=O`(醛基) | | R-SAC-02 | 燃烧(葡萄糖) | O₂,点燃 | CO₂ + H₂O | **B** | — | | R-SAC-03 | 水解(蔗糖) | 稀 H₂SO₄ / 酶,Δ | 葡萄糖 + 果糖 | A | `[CX4]1[OX2][CX4][CX4][CX4][CX4]1`(糖苷键) | | R-SAC-04 | 水解(麦芽糖) | 稀 H₂SO₄ / 酶,Δ | 2 × 葡萄糖 | A | 同 R-SAC-03 | | R-SAC-05 | 水解(淀粉 / 纤维素) | 酸或酶,Δ | 葡萄糖 | A | `[CX4][OX2][CX4]`(苷键,多聚) | | R-SAC-06 | 显色(淀粉) | I₂ 溶液 | 蓝色(鉴别) | **B** | — | **规则备注**: - R-SAC-01 复用醛基规则 `[CX3H1]=O`,**与 R-ALD-02/03 是同一反应中心**:葡萄糖含游离醛基(开链式),故有还原性。这体现了规则库的可组合性——糖类的还原性不需要新规则,只需新底物。 - R-SAC-03/04 的**区分**:蔗糖无游离醛基(无还原性)、麦芽糖有。SMARTS 匹配糖苷键后需按分子组成判断产物,实现时按已知单糖组分表处理。 - R-SAC-05 的淀粉/纤维素是**多聚体**,SMARTS 匹配苷键后产物固定为葡萄糖。因聚合度不定,**归 A 类但按 B 类方式呈现产物文字**(不写入结构),避免与 C 类混淆——这是 A/B 边界的特例,实现时以 `note` 说明。 ### 8.12 氨基酸与蛋白质 | ID | 反应 | 条件 / 试剂 | 产物 | 类 | 反应中心(SMARTS 草案) | |----|------|-------------|------|:--:|------------------------| | R-AMI-01 | 两性 | 与酸、与碱均成盐 | 铵盐 / 羧酸盐 | **B** | `[NX3][CX4][CX3](=O)[OX2H]` | | R-AMI-02 | 成肽 | 脱水缩合,Δ | 二肽 + H₂O | A | 同上(需 2 分子) | | R-AMI-03 | 缩聚 | 多分子脱水 | 聚酰胺(蛋白质) | **C** | 同上 | | R-PRO-01 | 水解 | 酸 / 碱 / 酶,Δ | 氨基酸 | A | `[CX3](=O)[NX3]`(肽键) | | R-PRO-02 | 变性 | 加热、重金属盐、乙醇、紫外线 | 结构破坏(不可逆) | **B** | — | | R-PRO-03 | 盐析 | 饱和 (NH₄)₂SO₄ | 析出(可逆) | **B** | — | | R-PRO-04 | 显色 | 浓 HNO₃ | 变黄(黄蛋白反应) | **B** | — | **规则备注**: - R-AMI-01 归 B 类:两性离子的具体形态(等电点)取决于 pH,不做结构承诺,只给结论「既可与酸反应也可与碱反应」。 - R-AMI-02 与 R-AMI-03 是**同一反应中心的不同聚合度**:成肽(2 分子 → 二肽,A 类可配平)与缩聚(n 分子 → 聚酰胺,C 类不可配平)。这是 A/C 分界的典型例证。 - R-PRO-01 与 R-AMI-02 **互为逆反应**,共用同一 SMARTS `[CX3](=O)[NX3]`(肽键),仅方向与条件不同。规则库需支持逆反应标记,供未来逆合成复用。 - R-PRO-02/03/04 全为 B 类:变性、盐析、显色都是**蛋白质整体性质**,无法在单体分子图上表达。这类结论的呈现需要明确说明「作用于蛋白质分子整体」,避免用户误以为适用于单个氨基酸。 ### 8.13 实现批次与优先级 规则量大,分三批交付以保证每批都可验证: | 批次 | 内容 | 条数 | 说明 | |:----:|------|:----:|------| | **R3a** | 单分子、单反应中心、无条件分流 | 约 25 | 烯/炔加成、醇氧化、醛氧化还原、酯化水解、卤代烃水解。先打通引擎全链路 | | **R3b** | 条件分流、电荷、多点位、跨分子 | 约 16 | 卤代烃水解/消去、醇成醚/消去、苯环取代、炔三聚、酚的三溴取代。验证条件匹配与多产物拆分 | | **R3c** | 定性结论与聚合提示 | 20 | 全部 B 类与 C 类。验证三类规则的 UI 分流呈现 | **验收口径**:每批规则必须附带 `verify.ts` 用例,断言「给定反应物 + 试剂 → 期望产物 SMILES」,全部通过方可进入下一批。 --- ## 9. 试剂与条件库 ### 9.1 条目结构 ```ts interface ReagentEntry { id: string name: string // 中文名 nameEn: string abbr?: string // 缩写,如 "PCC"、"mCPBA" smiles?: string // 有确定结构者(用于插入结构或参与规则匹配) category: ReagentCategory tags: string[] hazard?: string // 危险性简述 } type ReagentCategory = | 'acid' | 'base' | 'oxidant' | 'reductant' | 'catalyst' | 'ligand' | 'solvent' | 'dehydrant' | 'protective' | 'other' interface ConditionPreset { id: string name: string // "Fischer 酯化" temperature?: string solvent?: string catalyst?: string time?: string note?: string /** 关联规则 id */ ruleId?: string } ``` ### 9.2 数据组织 - 位置:`src/data/reagents.ts`、`src/data/conditions.ts`,纯静态常量,**不联网**。 - 初始规模:试剂约 80 条、条件预设约 20 条,覆盖教学与常见合成场景(酸碱、氧化还原、偶联催化、保护基、常用溶剂)。 - 每条数据人工校对名称与结构,结构一律以 SMILES 存储并标注来源。 - 缩写是规则匹配的关键字段,需保证与规则中 `agents` 声明一致,用单测校验两者引用闭合。 **规则清单倒逼出的三条数据要求**(源自 §8 的规则备注): 1. **条件分流必须可区分**。`NaOH 水溶液` 与 `NaOH 醇溶液` 决定卤代烃是水解还是消去;`浓 H₂SO₄ 140 ℃` 与 `170 ℃` 决定醇成醚还是成烯。因此试剂库需为同一物质的**不同条件形态建立独立条目**(`NaOH(aq)` / `NaOH(EtOH)`),条件预设则需携带温度字段供匹配。 2. **需要金属/无机产物条目,但不入分子图**。银镜反应与 Cu(OH)₂ 氧化的产物含 Ag、Cu₂O。试剂库可为这些无机物建立**纯文字条目**(无 `smiles` 或 `smiles` 仅作展示),规则只输出有机产物,无机产物写入 `note`。 3. **需要「燃烧」「显色」等无结构条目**。B 类规则依赖的试剂(O₂、I₂ 溶液、FeCl₃ 溶液、浓 HNO₃、饱和 (NH₄)₂SO₄)只需名称与类别,不要求结构。 ### 9.3 检索与插入 - 检索:名称 / 英文名 / 缩写 / 标签的本地模糊匹配,支持按类别筛选。 - 插入:点击条目 → 插入为反应式的 `AgentEntry`;若条目有 `smiles`,可选择「带结构插入」或「仅文字插入」。 - 条件预设:一键填充箭头上方文字与结构化条件字段。 --- ## 10. 协议 ### 10.1 RXN V2000 导出 复用现有 `graphToMolblock()`,按 RXN 规范拼接: ``` $RXN MoleculeBuilder <用户> <注释> <反应物数(3)><产物数(3)> $MOL <反应物 1 的 molblock>M END $MOL ... ``` 约定: | 项目 | 规范 | |------|------| | 试剂 | 不计入反应物数,V2000 无试剂位;导出时按用户选择「并入反应物侧」或「丢弃」 | | 计量系数 | V2000 不表达,多拷贝组分按系数**重复写出**多个 molblock | | 编码 | UTF-8,行尾 `\n`,与分子 MOL 导出一致 | | 兼容性 | 目标可被 RDKit、Marvin、ChemDraw 读入 | ### 10.2 反应 SMILES ``` 反应物.反应物>试剂.试剂>产物.产物 ``` - 各组分取 canonical SMILES(RDKit 优先,兜底自研 `toFallbackSmiles`)。 - 多组分以 `.` 连接;计量系数不表达(SMILES 语义限制),导出时提示该差异。 - 试剂含无结构条目时以文字名写入中间段。 ### 10.3 持久化与去重 | 项目 | 约定 | |------|------| | 存储键 | `molecule-reactions-v1` | | 存储内容 | `ReactionScheme[]` 的 JSON | | 去重键 | `key = 反应物 InChIKey 升序 join("+") + ">" + 产物 InChIKey 升序 join("+")` | | 降级 | 任一组分缺 InChIKey 时退回比较 canonical SMILES | | 预留 | 未来接入 RInChI 后替换去重键,需递增存储版本号 | > 去重键**不含条件与试剂**,因此同一对反应物/产物在不同条件下视为同一反应式,符合「反应式 = 结构变换」的语义。 ### 10.4 导出格式 | 格式 | 说明 | 依赖 | |------|------|------| | **SVG** | 含箭头、条件文字与系数的完整反应式矢量图 | 复用 `captureSvg` 思路 | | **PNG** | 3 倍分辨率,白底 | 复用现有 Canvas 绘制路径 | | **PDF** | 矢量、文字可选中 | **浏览器原生打印**,见下 | | **化学方程式** | 文本形式的方程式,三种变体,见下 | 本地生成,零依赖 | | **RXN** | 见 §10.1 | 复用 `graphToMolblock` | | **反应 SMILES** | 见 §10.2 | 复用 SMILES 管线 | #### PDF 实现取舍 采用**浏览器原生打印**(`window.print()` + 专用打印样式表),而非引入 PDF 生成库: | 方案 | 评价 | |------|------| | **浏览器打印 → 另存为 PDF**(选用) | 零新增依赖;矢量保真;**中文文字可选中、可搜索**;跨平台一致。代价是需用户在弹出的打印对话框中选择「另存为 PDF」 | | jsPDF + svg2pdf.js | 可直接落盘,但需新增两个依赖;**中文字体需内嵌**(体积增加数百 KB 至数 MB),否则中文变方框 | 打印样式表的要点:隐藏顶栏/左右面板/状态栏/浮层工具条与热区,只保留反应式内容;页面尺寸按内容自适应;`@page { margin: 12mm }`;强制白底。 #### 化学方程式导出 从反应式生成文本方程式,提供三种变体供不同用途: | 变体 | 样例 | 用途 | |------|------|------| | **Unicode 下标** | `CH₃CH₂OH + CH₃COOH --[浓H₂SO₄, Δ]--> CH₃COOCH₂CH₃ + H₂O` | 直接粘贴到 Word、课件、试卷 | | **LaTeX(mhchem)** | `\ce{CH3CH2OH + CH3COOH ->[H2SO4][\Delta] CH3COOCH2CH3 + H2O}` | 论文与 LaTeX 文档排版 | | **纯 ASCII** | `CH3CH2OH + CH3COOH -> CH3COOCH2CH3 + H2O` | 程序处理、纯文本环境 | 实现要点: 1. **下标渲染**:新增 `lib/formulaText.ts`,把 `molecularFormula()` 的输出转为 Unicode 下标(数字 → `₀-₉`)。该工具同时可复用于 UI 中分子式的显示。 2. **系数**:取自配平结果(§6),未配平时输出 `?` 并在导出提示中说明。 3. **条件**:箭头上方文字 → LaTeX 的 `->[...]`;下方文字 → `->[...][...]` 的第二参数。无下方文字时省略。 4. **箭头符号**:正向 `→` / `\rightarrow` / `->`;平衡 `⇌` / `\rightleftharpoons` / `<->`;共振 `↔`。 5. **B 类燃烧反应特例**:产物固定为 CO₂ + H₂O,可由分子式直接调用配平引擎**自动补全系数**并输出方程式,无需结构变换。 6. **局限提示**:方程式以**分子式**表达,会丢失结构信息(同分异构体无法区分,如乙醇与二甲醚同为 C₂H₆O)。导出面板需明确标注该局限,并建议以 SVG/PDF 作为正式交付格式。 --- ## 11. 里程碑 ### R1 · 反应式绘制(MVP) - 数据模型、`reactionStore`、顶层视图切换、组分卡片布局。 - 从分子库 / 当前分子组入组分,计量系数编辑,箭头与条件文字。 - `elementCounts` + 配平算法 + 守恒状态提示。 - RXN 与反应 SMILES 导出。 - **验收**:能画出「乙醇 + 乙酸 → 乙酸乙酯」并一键配平;导出的 RXN 可被 RDKit 读入且原子数正确;刷新后反应式仍在(若已保存)。 ### R2 · 试剂与条件库 - 试剂与条件预设数据集、检索面板、插入交互、条件一键填充。 - 按 §9.2 的三条数据要求,为条件分流建立独立条目(`NaOH(aq)` / `NaOH(EtOH)`)、为无机产物与显色试剂建立纯文字条目。 - 数据完整性单测(缩写与规则引用闭合、规则 `agents` 引用全部存在)。 - **验收**:检索「PCC」能命中条目并插入到箭头条件;条件预设一键填充后结构化字段同步;`NaOH(aq)` 与 `NaOH(EtOH)` 可作为不同条件被规则区分。 ### R3 · 反应预测(本地规则库) 按 §8.13 分三批交付,共 **67 条规则**: | 批次 | 内容 | 条数 | |:----:|------|:----:| | R3a | 单分子、单反应中心、无条件分流 | 约 25 | | R3b | 条件分流、电荷、多点位、跨分子 | 约 16 | | R3c | 定性结论(B 类)与聚合提示(C 类) | 20 | - 引擎:`smarts.ts` 桥接与原子索引映射(含 `verify.ts` 断言)、变换算子、产物拆分、守恒校验、候选排序与位点高亮。 - UI:A/B/C 三类结果的**分流呈现**(结构候选卡片 / 文字结论卡片 / 重复单元卡片)。 - **验收**: - R3a:给定「乙醇 + PCC」给出乙醛候选并标明规则;产物通过 `is_valid()` 且命名正确。 - R3b:「乙酸 + 乙醇 + 浓硫酸」给出乙酸乙酯候选;「溴乙烷 + NaOH 醇溶液」给出乙烯而非乙醇(验证条件分流);「3 乙炔 → 苯」配平正确。 - R3c:「苯酚 + FeCl₃」给出紫色结论且**不写入产物侧**;「乙烯 → 聚乙烯」展示重复单元且**不参与配平**(不报配平错误)。 - 每批规则的 `verify.ts` 断言全部通过。 ### R4 · 导出增强 - PDF(浏览器打印路径 + 打印样式表)、PNG 完善、化学方程式三变体(Unicode / LaTeX / ASCII)。 - `lib/formulaText.ts` 下标渲染工具,并复用到 UI 分子式显示。 - **验收**:打印预览只含反应式内容、无 UI 残留;导出的 PDF 中文可选中可搜索;三种方程式变体内容一致且系数正确;B 类燃烧反应能自动补全系数。 ### R5 · 外部兜底与打磨 - `PredictionBackend` 接口与 `RxnApiBackend` 实现(默认关闭 + 启用时的隐私提示 + 凭据存 localStorage)。 - 边界处理、无障碍、文档更新(用户手册新增反应章节)。 - **验收**:未配置凭据时外部路径完全不可见且无报错;配置后可调用;断网或接口失败时静默降级为仅本地结果。 --- ## 12. 风险与对策 | 风险 | 影响 | 对策 | |------|------|------| | 自研规则的化学正确性 | 产物错误 | 规则显式标注适用条件与出处;产物一律标注「候选」;SMARTS 逐条验证后才上线;只做教学级常见反应,不覆盖复杂区域选择性 | | RDKit 原子索引映射失效 | 预测全不可用 | 固定 molblock 顺序约定并加单测;解析失败时降级提示而非报错 | | 配平多解 / 无解 | 用户困惑 | 明确文案与差异元素高亮;允许手动指定与关闭校验 | | 规则数量大(67 条) | 工期与质量失控 | 分 R3a/R3b/R3c 三批,每批带 `verify.ts` 断言,通过方可继续 | | B/C 类被误读为「预测失败」 | 用户困惑 | 三类在 UI 中分区展示并各有说明文案;B/C 类不进入产物侧、不触发配平错误 | | 化学方程式丢失结构信息 | 同分异构体混淆 | 导出面板明确标注局限,建议以 SVG/PDF 为正式交付格式 | | 外部服务不可用或收费 | 功能承诺落空 | 默认关闭;本地规则为唯一必需路径;UI 明确标注来源 | | 反应式渲染复杂度(多坐标系) | 工期膨胀 | R1 用卡片式简化,内联编辑推迟到后续 | | 规则集维护成本 | 长期落后 | 规则为声明式数据,独立于代码;新增规则不改引擎 | | PDF 依赖用户操作打印对话框 | 体验断点 | 打印预览页内嵌操作指引;同时保留 SVG/PNG 作为一键落盘的替代 | --- ## 13. 已确认决策 评审已确认以下五项,本版方案据此定稿: | # | 议题 | 决策 | 落点 | |:-:|------|------|------| | 1 | 反应式的位置 | **采用顶层视图切换**(分子构建器 / 反应) | §5.1 | | 2 | R1 组分编辑交互 | **接受**「双击卡片进入分子构建器」的简化交互 | §5.2 | | 3 | 首批规则覆盖范围 | 按评审表全量纳入,整理为 **67 条规则 / 14 个类别**,按产物形态分 A/B/C 三类,分三批交付 | §8、§11 R3 | | 4 | 外部兜底接口 | **本期落地**(接口 + 实现,默认关闭) | §7.7、§11 R5 | | 5 | 导出格式 | 在 SVG / PNG 之外,**新增 PDF 与化学方程式**(Unicode / LaTeX / ASCII 三变体) | §10.4、§11 R4 | **评审中确立的一条设计原则**(本版新增,见 §1.3 第 5 条):规则必须按产物形态分为 A 结构变换 / B 定性结论 / C 聚合提示三类。评审给出的反应类型表中,显色、变性、盐析、燃烧等现象**没有可画的产物结构**,聚合反应**无法配平**;若强行编造结构或以结构预测的名义呈现,等于伪造化学结论。因此三类规则在引擎与 UI 中分别对待。 --- ## 14. 实现记录:试跑中发现并修复的缺陷 以下问题在实现阶段通过端到端试跑(`scripts/verify.ts` + 浏览器实测)暴露,均已修复并固化为回归断言。记录在此,因为它们揭示了 §2 技术前提之外的真实约束。 ### 14.1 molblock 键行必须用「原子块序号」而非内部 atom_id(严重) | 项 | 内容 | |----|------| | 现象 | 由乙苯删出的苯(`atom_id` 为 3..8)导出 molblock 后,RDKit **完全无法解析**;经删除操作得到的分子一律受影响 | | 原因 | V2000 的键行与 `M CHG` 行按「原子在原子块中的 1 基序号」引用原子,而实现直接写了内部 `atom_id`。分子图编号删除后不回收,二者不再相等 | | 影响面 | 不止反应模块——`.mol` 导出与「RDKit 描述符」路径同样受损(乙醇能通过纯属巧合:其 id 恰为 1,2) | | 修复 | 导出前建立 `atom_id → 序号` 映射;`M CHG` 行同步修正 | | 断言 | `verify.ts` 第 6、13 项:键行端点必须落在 `1..原子数`,且 id 不连续分子必须能被 RDKit 解析回 `c1ccccc1` | ### 14.2 V2000 芳香键(键级 4)无法被当前 RDKit 解析 | 项 | 内容 | |----|------| | 现象 | 含芳香键的分子 molblock 一律解析失败,静默回退到自研 SMILES | | 原因 | 实测 `@rdkit/rdkit` MinimalLib 不接受键级 4 的写法 | | 修复 | 导出时统一把芳香环转为**凯库勒单双交替**(`kekuleOrders`)。这同时更符合多数工具偏好 | ### 14.3 自研 SMILES 用 `in_ring` 判断芳香性(化学错误) | 项 | 内容 | |----|------| | 现象 | **环己烷被写成 `c1ccccc1`**(苯),分子式正确但结构错误 | | 原因 | `symbolOf()` 用渲染层的 `in_ring` 提示判断是否小写 `c`,而饱和环同样为 `true` | | 修复 | 改为判断该碳是否连有**芳香键**(`aromatic`),与化学语义一致 | ### 14.4 SMARTS 解析器未处理分支括号(导致占位符映射错位) | 项 | 内容 | |----|------| | 现象 | 酯化规则产出三个碎片、骨架错误;苯酚类规则的变换作用到错误原子 | | 原因 | `[CX3](=O)[OX2H1]` 中两个 O 都连在同一个 C 上,解析器按线性链处理得到 C—O—O,占位符下标全部偏移 | | 修复 | 按 SMILES/SMARTS 语义正确处理 `( )` 分支;并补上元素与键级校验 | ### 14.5 `get_substruct_matches` 的原子顺序不等于 SMARTS 顺序 | 项 | 内容 | |----|------| | 现象 | 苯酚 `c1ccccc1[OX2H1]` 匹配中「下标 5」并非与 O 成键的环碳,占位符映射到错误原子 | | 原因 | RDKit 返回的 `atoms` 数组顺序**不保证**等于查询中的原子顺序(方案 §7.3 原先假设了这一点) | | 修复 | 改为按模式子图做**同构搜索**(同时校验元素与键级),由化学环境唯一确定映射,不再依赖返回顺序 | | 断言 | `verify.ts` 第 7 项:苯酚模式的下标 5 必须是与 O 成键的环碳;两个 O 必须同连一个 C | ### 14.6 `addBond` 对已存在的键跳过(消去得到烷烃) | 项 | 内容 | |----|------| | 现象 | 溴乙烷消去生成**乙烷**而非乙烯 | | 原因 | 执行器对 `addBond` 做了幂等跳过;而消去需要把原有 C—C 单键**升级**为双键,于是被跳过 | | 修复 | 键已存在时改为升级键级而非跳过 | ### 14.7 合并多分子时 `nextAtomId` 按个数计算(变换静默失败) | 项 | 内容 | |----|------| | 现象 | 多条规则(含加卤素、硝化、磺化、三聚)全部静默不命中 | | 原因 | `mergeGraphs` 用「原子个数 + 1」计算 `nextAtomId`,而编号删除后不回收、个数往往小于最大编号 → `addAtom` 生成重复 id,变换结果非法被丢弃 | | 修复 | 改为按**最大编号 + 1** 计算 | ### 14.8 方程式配平系数错误 | 项 | 内容 | |----|------| | 现象 | 乙烯燃烧被写成 `C3H6 + 9O₂ → 3CO₂ + 6H₂O`(把「氧原子数」当成了「氧气分子数」,且产物系数不自洽) | | 修复 | 按通式 `CxHyOz + (x + y/4 − z/2) O₂ → x CO₂ + (y/2) H₂O` 计算,先以公分母 4 展开为整数再约分。断言:乙烯 → `C2H4 + 3O₂ → 2CO₂ + 2H₂O` | ### 14.9 守恒提示的措辞与方向 | 项 | 内容 | |----|------| | 问题 | 初版提示「有机组分未完全体现守恒」既未说明差异元素,方向也可能反(把「反应物侧少」说成「多」) | | 修复 | 改为逐元素列出差异与方向(`H 少2`),并说明成因——试剂参与成键、其原子计入产物,按提示把试剂加入反应物侧即可配平 | ### 14.10 一条被证伪的担忧(记为正向结论) 曾在界面观察到「聚合规则命中苯环」,一度怀疑烯烃 SMARTS 会误匹配芳香环。加入断言后实测**不成立**: - `[CX3]=[CX3]` 对苯的匹配数为 **0**;`c1ccccc1` 为 **1** - 「苯 + Br₂」的候选列表中**不含**任何 `R-ENE-*` 规则 原因是 molblock 导出为凯库勒式后,RDKit 会正确**感知芳香性**,芳香碳不会被 `[CX3]` 匹配。该性质已固化为断言(`verify.ts` 第 14 项),防止后续改动破坏。 --- ## 15. 实现状态 | 里程碑 | 状态 | 说明 | |--------|:----:|------| | R1 反应式绘制 | ✅ | 顶层视图切换、组分卡片、箭头与条件、配平、RXN/SMILES 导出 | | R2 试剂与条件库 | ✅ | 78 条试剂、23 条条件预设、检索与一键填充 | | R3 反应预测 | ✅ | 66 条规则(A 46 / B 14 / C 6),分 R3a/R3b/R3c 三批 | | R4 导出增强 | ✅ | PDF(打印)、PNG、化学方程式三变体、下标渲染 | | R5 外部兜底 | ✅ | `PredictionBackend` 接口 + 通用 HTTP JSON 适配器,默认关闭 | 验证口径:`npx tsx scripts/verify.ts` 覆盖配平、下标、方程式、规则集完整性、SMARTS 索引约定、8 条端到端预测、条件分流、A/B/C 分流、跨分子反应、molblock 往返与上述全部回归项。 > **与原计划的偏差**:§8 规划 67 条规则,实现为 66 条——因 R-ALC-03(醇的酯化)与 R-ACD-03(羧酸的酯化)是同一条反应的两种入口,已合并为单条 `R-EST-FORM`,避免重复定义与排序冲突。 --- ## 参考资料 - [PUG-SOAP and PUG-REST: web services for programmatic access to chemical information in PubChem](https://pmc.ncbi.nlm.nih.gov/articles/PMC4489244/) — PUG REST 域与操作列表,确认无 reaction 域 - [PubChem PUG REST Specification](https://pubchem.ncbi.nlm.nih.gov/pug_rest/PUG_REST.html) — 接口规范 - [Synthesis AI Part 5: From Algorithm to Lab](https://seongokryu.github.io/posts/synthesis-ai-part5-algorithm-to-lab/) — 反应记录格式(RInChI / XDL / reaction SMARTS)与 ORD schema 现状 - [What is the Best AI for Organic Chemistry? (2026)](https://aicrazenews.com/best-ai-for-organic-chemistry/) — IBM RXN、ASKCOS 等反应预测服务的可用性与门槛