Preformu / tests /test_compatibility_regression.py
Kevinshh's picture
feat: 意图保真(intent-fidelity) + 描述性梳理技能 + 相容性引擎升级; 修复转置宽表解析/CQA对账/澄清交互/功能切换串显; .gitignore 排除专利与机密Demo数据
0e6887b
Raw
History Blame Contribute Delete
6.44 kB
"""相容性确定性回归基准(compatibility-engine-upgrade 任务 8.1)。
金标准用例覆盖经典相互作用 + 纠偏 + 别名,断言风险**档位**与命中**反应集合**。
确定性、离线运行(直接喂官能团 id,不依赖网络/LLM/RDKit),守护规则/SMARTS 改动
不致非预期回归(需求 1.4 / 2.2 / 2.3)。
"""
from __future__ import annotations
import pytest
from skills.compatibility import rules
def _assess(group_ids, excipients):
return rules.evaluate([{"id": g} for g in group_ids], excipients)
def _excipient_reactions(assessment, idx=0):
er = assessment.excipient_results[idx]
return er.risk, {r.reaction for r in er.reactions}
# ---------------------------------------------------------------------------
# 经典相互作用金标准
# ---------------------------------------------------------------------------
@pytest.mark.parametrize(
"group_ids,excipient,expected_overall,expected_reactions",
[
# 美拉德:伯胺 + 还原糖(乳糖)→ 高风险
(["primary_amine"], "乳糖", rules.RISK_HIGH, {"maillard"}),
# 碱催化水解:酯 + 偏碱(硬脂酸镁)→ 高风险
(["ester"], "硬脂酸镁", rules.RISK_HIGH, {"hydrolysis"}),
# 内酯 + 碱 → 高风险水解
(["lactone"], "硬脂酸镁", rules.RISK_HIGH, {"hydrolysis"}),
# 氧化:酚 + 过氧化物(聚维酮)→ 中风险
(["phenol"], "聚维酮", rules.RISK_MEDIUM, {"oxidation"}),
# 氧化:巯基 + 过氧化物 → 高风险
(["thiol"], "聚维酮", rules.RISK_HIGH, {"oxidation"}),
# 酸碱:羧基 + 偏碱 → 中风险
(["carboxylic_acid"], "硬脂酸镁", rules.RISK_MEDIUM, {"acid_base"}),
# 光降解:酚 + 二氧化钛(光催化剂 + 金属)→ 中风险(氧化 + 光降解)
(["phenol"], "二氧化钛", rules.RISK_MEDIUM, {"oxidation", "photodegradation"}),
# Michael:Michael 受体 + 亚硫酸盐 → 中风险
(["michael_acceptor"], "亚硫酸氢钠", rules.RISK_MEDIUM, {"michael"}),
],
)
def test_classic_interactions(group_ids, excipient, expected_overall, expected_reactions):
a = _assess(group_ids, [excipient])
assert a.overall_risk == expected_overall, (group_ids, excipient)
risk, reactions = _excipient_reactions(a)
assert expected_reactions.issubset(reactions), (reactions, expected_reactions)
# ---------------------------------------------------------------------------
# 纠偏:淀粉 / MCC 假阳性(需求 2.2)
# ---------------------------------------------------------------------------
def test_starch_amine_not_high_maillard():
"""淀粉 + 伯胺 → 中风险(经还原糖杂质途径),而非默认高风险。"""
a = _assess(["primary_amine"], ["淀粉"])
risk, reactions = _excipient_reactions(a)
assert "maillard" in reactions
assert risk == rules.RISK_MEDIUM
assert a.overall_risk == rules.RISK_MEDIUM
def test_mcc_amine_no_maillard():
"""微晶纤维素 + 伯胺 → 不触发美拉德(仅可能吸附低风险)。"""
a = _assess(["primary_amine"], ["微晶纤维素"])
risk, reactions = _excipient_reactions(a)
assert "maillard" not in reactions
def test_lactose_still_high():
"""乳糖(真还原糖)+ 伯胺仍为高风险(纠偏不误伤真阳性)。"""
a = _assess(["primary_amine"], ["乳糖"])
assert a.overall_risk == rules.RISK_HIGH
def test_aromatic_amine_maillard_differentiated_from_aliphatic():
"""芳香胺 + 还原糖 → 低风险美拉德,区别于脂肪伯/仲胺的高风险(需求 2.1.3)。"""
aromatic = _assess(["aromatic_amine"], ["乳糖"])
arom_reactions = {r.reaction: r.risk for r in aromatic.excipient_results[0].reactions}
assert arom_reactions.get("maillard") == rules.RISK_LOW
aliphatic = _assess(["primary_amine"], ["乳糖"])
ali_reactions = {r.reaction: r.risk for r in aliphatic.excipient_results[0].reactions}
assert ali_reactions.get("maillard") == rules.RISK_HIGH
# 差异化:脂肪胺风险严格高于芳香胺。
assert rules.RISK_ORDER[ali_reactions["maillard"]] > rules.RISK_ORDER[arom_reactions["maillard"]]
def test_aromatic_amine_oxidation_prone():
"""芳香胺 + 过氧化物辅料 → 中风险氧化(苯胺类氧化敏感)。"""
a = _assess(["aromatic_amine"], ["聚维酮"])
reactions = {r.reaction: r.risk for r in a.excipient_results[0].reactions}
assert reactions.get("oxidation") == rules.RISK_MEDIUM
# ---------------------------------------------------------------------------
# 最长别名(需求 2.3)
# ---------------------------------------------------------------------------
@pytest.mark.parametrize(
"name,wrong_parent_tag_absent",
[
("预胶化淀粉", "reducing_sugar_impulse"), # 不应带淀粉的还原糖杂质途径标签
("交联聚维酮", None),
],
)
def test_longest_alias_not_parent(name, wrong_parent_tag_absent):
a = _assess(["primary_amine"], [name])
er = a.excipient_results[0]
assert er.known is True
if name == "预胶化淀粉":
# 预胶化淀粉档案 tags=[high_moisture],不含 reducing_sugar_impurity → 不命中美拉德。
assert "reducing_sugar_impurity" not in er.tags
assert "maillard" not in {r.reaction for r in er.reactions}
if name == "交联聚维酮":
# 交联聚维酮 tags=[peroxide](无 high_moisture),区别于母体聚维酮。
assert "high_moisture" not in er.tags
# ---------------------------------------------------------------------------
# 不确定 + 多辅料聚合
# ---------------------------------------------------------------------------
def test_no_groups_uncertain():
a = _assess([], ["乳糖", "硬脂酸镁"])
assert a.overall_risk == rules.RISK_UNCERTAIN
assert a.overall_confidence == rules.CONF_LOW
def test_multi_excipient_worst_aggregation():
"""多辅料取最坏:乳糖(高) + 甘露醇(无) → 整体高。"""
a = _assess(["primary_amine"], ["乳糖", "甘露醇"])
assert a.overall_risk == rules.RISK_HIGH
def test_determinism_repeated_runs():
first = _assess(["primary_amine", "ester"], ["乳糖", "硬脂酸镁"]).summary()
again = _assess(["primary_amine", "ester"], ["乳糖", "硬脂酸镁"]).summary()
assert first == again