# STRING Tree Reference Resolution Design 日期:2026-07-20 状态:范围已确认,实施计划审查通过 ## 目标 为 PPI 增加基于固定 STRING v12.0 物种注册表和 species tree 的通用物种解析:目标物种受 STRING 支持时直接查询;否则按 STRING tree 拓扑距离返回候选参考物种;有效缓存中仍无法得到候选时,推荐拟南芥作为明确标注的模型兜底,并在用户确认后才执行 Ortholog→PPI。本轮只闭合 STRING→Ortholog→PPI,不修订 KaKs 功能。 ## 已确认决策 - 运行期只使用项目内固定快照,不联网刷新。 - 同时缓存 `species.v12.0.txt` 和约 97 MB 的 `species.tree.v12.0.txt`。 - PPI 的候选发现和排序只依赖 STRING 自身数据,不引入 NCBI taxonomy。 - 项目阶段身份继续使用 `Species_Code`;STRING 外部身份使用 TaxID。 - 返回稳定排序的 Top 3 STRING tree 候选。 - STRING tree 没有分类等级和分子枝长,因此只报告 LCA 节点与拓扑边数,不声称“同目”“最相似”或“进化距离最近”。 - 找不到候选时推荐 `Arabidopsis thaliana`(TaxID 3702)作为 `model_fallback`,必须由用户确认一次,不能静默执行。 - PPI 与 KaKs 保持独立 owner;KaKs 缺少 CDS 不得阻断可直接执行的 PPI。 - PPI-only fallback 只准备参考 PEP;只有调用方明确请求 KaKs 时才准备 CDS。 ## 非目标 - 不修改 PlanningAgentV2 的 PPI 静态依赖。 - 不恢复 PPI→Ortholog 静态绑定。 - 不修改 TFBS 的 NCBI taxonomy 路径。 - 不让 KaKs 强制使用 STRING 物种。 - 不修改 KaKs 的算法、参数、参考物种选择、输入契约、Manifest 或结果展示。 - 不为 KaKs 新增 resolver,也不把 PPI 选中的 STRING 参考物种传给 KaKs。 - 不建设通用 taxonomy 服务或数据库。 - 不使用 LLM、模糊匹配或联网搜索猜测物种身份。 - 不删除、重命名或覆盖既有分析结果。 ## 当前问题 当前 `gfa_core/ppi_string_policy.py` 仅维护少量 STRING code/TaxID 和 Ft→Spinacia 静态推荐。`recommended_string_reference_species()` 只是字典查询;`resolve_ppi_species_resolution()` 将空结果直接暴露为“无可用近源推荐”。因此前端虽然是数据驱动的,后端并没有通用候选发现能力。 另一个必须同时修复的边界是执行身份:当前 PPI 执行仍依赖少量三字母 code。新 resolver 即使找到 registry-only 物种,如果 TaxID 不能传到 STRING client,仍会出现“预检支持、执行不支持”的假通用化。 ## 固定数据布局 ```text data/string/v12.0/ species.v12.0.txt species.tree.v12.0.txt source_manifest.json ``` `source_manifest.json` 必须记录: - STRING 版本; - 官方下载 URL; - 下载时间; - 文件大小; - 每个文件的 SHA256; - parser schema version; - Viridiplantae 根节点和 Arabidopsis TaxID 校验结果。 缓存发布前必须验证: - 每个 parent TaxID 都能在 tree 中解析,根节点除外; - 官方根节点 `1 -> 1` 自环被识别为终止条件,且除此之外不存在任何环; - `is_STRING_species` 只接受 `t/f` 并严格解析为布尔值,禁止使用 `bool("f")`; - `is_STRING_species` 节点与 species registry 一致; - Viridiplantae 根节点 TaxID 33090 存在; - Arabidopsis TaxID 3702 存在且标记为 STRING species; - 文件 SHA256 与 Manifest 一致。 运行期缓存缺失、损坏或校验失败属于系统资源错误,返回 `string_registry_unavailable` 或 `string_registry_integrity_failed`,不得伪装成“没有近源物种”,也不得因此自动回退 At。 ## Resolver 输入与输出 ### 输入 - `project_species_code`:项目身份,例如 `Ft`、`Os`; - `target_scientific_name`:存在时优先用于匹配; - `ppi_target_tree_taxon_id`:明确属于 STRING tree 的 TaxID,不复用含义不同的 NCBI 通用 taxid 字段; - 固定 STRING snapshot 路径; - `top_n=3`。 常用简称 alias 只用于将项目输入映射到 STRING tree 节点,不作为候选知识库。服务器维护最小的 PPI 默认 alias,例如 `Os -> 39947`,避免在没有更明确信息时把通用 NCBI 物种 TaxID 4530 错当成 STRING 实际使用的 japonica TaxID 39947。解析顺序固定为:明确的 `ppi_target_tree_taxon_id` → 科学名精确唯一匹配 → PPI 项目 alias 默认值。用户显式指定的 STRING tree TaxID 不得被 alias 覆盖。无法唯一匹配返回 `target_string_tree_unresolved`,不做模糊猜测。 ### 输出 ```json { "selection_mode": "exact_string|tree_nearest|model_fallback|unresolved", "project_species_code": "Ft", "target_tree_taxon_id": "62330", "selected_string_taxon_id": "3562", "selected_string_name": "Spinacia oleracea", "lca_taxon_id": "...", "lca_name": "...", "target_edges_to_lca": 4, "candidate_edges_to_lca": 3, "tree_distance": 7, "string_version": "12.0", "snapshot_id": "...", "requires_user_confirmation": true } ``` ## 解析算法 1. 加载并验证固定 snapshot。 2. 用显式 TaxID、科学名或现有常用 alias 将目标解析为 STRING tree 节点。 3. 若目标节点本身 `is_STRING_species=true`: - 返回 `exact_string`; - PPI 直接使用该 TaxID; - 不插入 Ortholog。 4. 若目标节点存在但不受 STRING 支持: - 候选池限定为 Viridiplantae 子树内 `is_STRING_species=true` 的节点; - 计算目标与每个候选的 LCA 和双向拓扑边数; - 排序键固定为 `(tree_distance, candidate_taxon_id, candidate_name)`; - 返回 Top 3,模式为 `tree_nearest`; - 用户确认候选后才准备参考 PEP、执行 Ortholog,再执行 PPI。 5. 若目标名称/TaxID 有歧义: - 返回 `unresolved`,要求用户补充 STRING tree TaxID 或精确科学名; - 不推荐 At,避免把可修正的身份问题伪装成生物学兜底。 6. 若目标完全无法解析且用户选择继续,或有效 tree 中没有植物 STRING 候选: - 返回 `model_fallback`; - 推荐 At/3702; - 显示“远缘模型兜底,不是近源物种”; - 用户确认一次后才进入 Ortholog→PPI。 7. 若缓存无效: - 返回资源错误; - 不提供 At 兜底,避免掩盖部署故障。 ## 身份与依赖边界 项目级身份不变: - `targetSpecies`、`referenceSpecies`、`scope_id`、`memberSetId` 和文件名继续使用 `Species_Code`。 新增 PPI 技术身份: - `ppiSpecies`:唯一 STRING selector,可为旧 alias 或数字 TaxID; - `ppiReferenceSpeciesCode`:可选旧项目 code,registry-only 候选允许为空; - `ppiReferenceStorageKey`; - `ppiReferenceTaxonId`; - `ppiReferenceSelectionMode`; - `ppiReferenceUserConfirmed`; - `ppiReferencePolicyId`。 `snapshot_id = sha256(parser_schema_version + registry_sha256 + tree_sha256)`。`ppiReferencePolicyId` 由项目目标 Species_Code、目标 tree TaxID、选择的 STRING TaxID、selection mode、STRING version 和 `snapshot_id` 计算。它记录 PPI 如何选择和确认参考物种,不作为 Ortholog mapping 的复用身份,也不进入全局 gene-family `scope_id`。 Ortholog 另行计算 `orthologMappingIdentity`,至少绑定 target/member set、参考 Species_Code/TaxID、query/reference PEP SHA256、BLAST 参数和工具版本。只有该身份一致时,才可复用 final mapping 或 `ortholog/*_pairs.txt` 中间结果;PPI policy 改变但序列和 Ortholog 参数未变时,不应无意义重跑 BLAST。 STRING registry 不保证提供项目三字母 `Species_Code`。科学身份始终以 `ppiSpecies` 中的数字 STRING TaxID 为准;job-local 参考 PEP、ID map 和 Ortholog subject label 使用 `ppiReferenceStorageKey`。已存在唯一旧 code 时可兼容读取旧目录;registry-only 候选的 `ppiReferenceSpeciesCode` 为空,存储键固定按 `string_taxon_{taxid}` 生成,不得写回全局物种表或冒充项目 Species_Code。 现有 NCBI Datasets 下载器只可作为“按已选 TaxID 获取参考 PEP”的文件传输层,不参与候选发现、树排序或物种身份推断;本轮不建设 NCBI taxonomy 映射。 Ortholog 仍只是 producer:只有 `tree_nearest` 或已确认的 `model_fallback` 缺少目标→参考映射时才动态插入。PPI 是选择策略 owner。KaKs 不读取 PPI 的 STRING 候选;若用户另行要求 KaKs,独立检查其 reference CDS/PEP。 PPI fallback 只写 PPI 专用参数,并给动态 Ortholog 标记 `dependencyOwner="ppi"`。KaKs 调度路径必须忽略 `ppiReference*`、`ppiReferenceStorageKey` 与 `ppiReferencePolicyId`。fallback PPI 与 KaKs 同批且需要不同参考物种时,不在本轮自动生成两套 Ortholog;本轮联合验收只覆盖 direct Os PPI 不被 KaKs 阻断。 现有参考包下载函数不得为 PPI-only 请求隐式追加 `kaks`。`requested_stages=["ppi"]` 只要求参考 PEP;`requested_stages` 明确包含 `kaks` 时,既有 CDS/PEP 行为保持不变。此处只解除 owner 泄漏,不改变 KaKs 逻辑。 ## 前端行为 - `exact_string`:不弹参考选择框,不显示 Ortholog。 - `tree_nearest`:显示 Top 3、TaxID、LCA 名称、拓扑距离、STRING 版本,用户选择后继续。 - `model_fallback`:单独警告“拟南芥是远缘模型兜底”,默认不勾选;用户确认后记录确认状态。 - `unresolved`:允许补充 TaxID/科学名、显式选择参考物种或跳过 PPI。 - 缓存错误:显示部署资源错误,不显示生物学 fallback。 同一 `ppiReferencePolicyId` 的确认在当前批次内只询问一次;参考物种或 snapshot 改变后必须重新确认。 STRING 候选的参考 PEP 可下载性初始标记为 `unknown`。NCBI Datasets 下载失败时保留当前候选和错误,允许用户选择第二候选或上传 PEP;不得自动换候选或静默回退 At。 单次 PPI 执行继续只解析一个目标物种。多目标请求不得把第一个目标的推荐偷用于其余目标,保持现有“需要显式参考/Ortholog mapping”的行为,不在本轮扩展为多目标自动推荐。 ## Manifest 与诊断 预检、等待确认和等待参考 PEP 使用独立的 decision record,不写成最终 failed PPI Manifest。只有真正提交 PPI 执行后发生成功或失败时,execution Manifest 才记录: - project Species_Code; - 原始目标输入和解析方式; - 目标 tree TaxID; - 选择模式; - 选中 STRING TaxID; - LCA 和拓扑距离证据; - snapshot 版本和 SHA256; - `ppiReferencePolicyId`; - 用户是否确认模型兜底; - Ortholog mapping 路径及其参考 TaxID。 诊断代码至少区分: - `string_registry_unavailable`; - `string_registry_integrity_failed`; - `target_string_tree_unresolved`; - `no_string_tree_candidate`; - `ppi_model_fallback_confirmation_required`; - `ppi_requires_ortholog_mapping`; - `stale_ortholog_reference_conflict`。 ## 分任务与门禁 ### T0 基线冻结 - 记录真实 9000 服务目录、测试结果和 Os/Ft/非 Ft 预检响应。 - 通过条件:现有聚焦测试绿色,错误现状可重复。 - 不通过:停止,不叠加新功能。 ### T1 STRING snapshot 落地 - 下载两个官方文件,生成 Manifest,完成完整性和 tree 一致性校验。 - 通过条件:断网可加载;篡改任一文件会被拒绝;At 和 Viridiplantae 根节点校验通过。 - 不通过:不得进入 resolver 开发。 ### T2 只读 registry/tree loader - 建立内存索引并按文件 SHA256 缓存,避免每个请求重复解析 97 MB 文件。 - 通过条件:第一次加载可测量;后续请求复用同一只读索引;结果不受字典遍历顺序影响。 - 不通过:不得接 API。 ### T3 通用 resolver - 实现 exact、Top 3 tree-nearest、unresolved、At model fallback。 - 通过条件:Os direct;Ft 不靠静态表得到合理候选;第二个非 Ft 目标返回候选;无解析目标进入 At 确认分支。 - 不通过:不得修改生产预检。 ### T4 TaxID 执行链 - 复用现有 `ppiSpecies` 作为唯一执行 transport,允许其承载数字 TaxID;backend、CLI、STRING client 和 Manifest 原样使用;旧 code 保持兼容,不新增平行 `ppiStringTaxonId` 字段。 - 通过条件:一个不在旧 12 项 map、但 registry 支持的物种能真实调用 STRING;请求、CLI 日志、API TaxID、Manifest 完全一致。 - 不通过:不得宣称通用支持。 ### T5 PPI preflight、PPI-only 参考包和动态 Ortholog - 用 resolver 替换 Ft 静态推荐;PPI-only 参考包只要求 PEP;保持 PPI/KaKs owner 隔离。 - 通过条件:direct PPI 无 Ortholog;tree nearest/已确认 At 才插 Ortholog;PPI-only 下载不索要 CDS;明确请求 KaKs 时原有 CDS/PEP 检查不变;KaKs 缺 CDS 不阻断 direct PPI。 - 不通过:不得进入 UI 完成验收。 ### T6 前端确认与错误显示 - 复用现有动态弹窗,增加 Top 3 evidence 和 At model fallback 确认。 - 通过条件:前端选择的 TaxID 原样进入下载、Ortholog 和 PPI;缓存错误不显示 At;At 不能静默执行。 - 不通过:不得部署。 ### T7 PPI/Ortholog Contract、Manifest、status 与 diagnosis 闭环 - 只同步 PPI/Ortholog 合同、Manifest provenance、旧映射冲突和状态复用;不修改 KaKs contract/Manifest/status。 - 通过条件:`/api/execute`、CLI 日志、Manifest、`/api/stage-status` 使用一致的 Species_Code/STRING TaxID/policy ID。 - 不通过:不得复用旧结果。 ### T8 真实 9000 验收 - 重启真实服务和 worker,使用真实固定 snapshot 执行 direct 与 fallback 链路。 - 通过条件:Os direct;Ft tree nearest;第二个非 Ft;registry-only 物种;At 确认 fallback;缓存损坏;PPI/KaKs 隔离全部通过。 - 至少一个真实小家族完成“推荐→确认→参考 PEP→Ortholog→PPI→final artifacts→Manifest→status→前端展示”。 - 不通过:不得报告线上完成。 ## 反模式约束 - 不继续扩充 `STRING_REFERENCE_RECOMMENDATIONS` 静态表。 - 不用 `species_code` 是否在旧 map 判断 STRING 支持。 - 不把 tree topology 描述成分子进化距离。 - 不因缓存错误静默使用 At。 - 不让前端重新计算或重新排序候选。 - 不只验证预检;必须验证 registry-only TaxID 能进入真实 STRING 请求。 - 不在错误工作副本测试后宣称真实 9000 已修复。 ## 最终验收矩阵 1. Os:`exact_string`,无需 Ortholog。 2. Ft:由 tree 算法产生候选,无 Ft 特例读取。 3. 第二个非 Ft、非直接 STRING 植物:返回 Top 3。 4. 旧 12 项 map 外、registry 内物种:真实 PPI 成功。 5. 未知目标:进入用户确认 At 的 `model_fallback`。 6. cache missing/corrupt:资源错误,不回退 At。 7. 旧 mapping 与新 TaxID 冲突:拒绝复用。 8. Os PPI + 缺 CDS 的 KaKs:只作为隔离回归,证明 PPI 可继续;不改变 KaKs 的暂停原因或行为。