Files
PlantAI-dev/docs/superpowers/specs/2026-07-20-string-tree-reference-resolution-design.md
2026-08-03 18:42:47 +08:00

276 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 的暂停原因或行为。