1. 项目缘起从“黑盒”分类到“白盒”规则的探索在化学信息学、药物发现乃至材料科学领域化学反应分类一直是个核心且棘手的问题。我们常常面对海量的反应数据需要将它们精准地归入不同的类别比如亲核取代、环加成、氧化还原等等。传统的做法无论是基于专家手工编写的规则如SMARTS模式匹配还是依赖深度学习的端到端模型都各有其明显的短板。手工规则严谨、可解释但编写和维护是场噩梦面对化学空间的无限可能性规则库永远在追赶且极易遗漏边缘案例。深度学习模型尤其是基于图神经网络或Transformer的模型在准确率上可能表现惊艳但它们是不折不扣的“黑盒”。模型告诉你这个反应是“Diels-Alder反应”但你永远不知道它做出这个判断究竟是基于双烯和亲双烯体的轨道对称性匹配还是仅仅因为反应物里有个六元环的模糊特征。这种不可解释性在需要高可靠性、可审计性的场景如自动化合成路线设计、法规遵从性审查中是致命的。这就引出了我们这次探索的核心Agentic generation of verifiable rules for deterministic, self-expanding reaction classification。这个标题信息量巨大它描绘的是一种全新的范式。简单来说我们想构建一个“智能体Agent”它能够自动地从反应数据中“学习”并“生成”分类规则。这些规则不是神经网络的权重而是人类可读、可验证的逻辑语句比如“如果反应中心原子由sp3杂化变为sp2杂化且伴随π键形成则可能为消除反应”。整个系统是确定性的Deterministic意味着相同的输入永远产生相同的分类结果排除了随机性。更重要的是它是自扩展的Self-expanding新发现的反应或分类错误能够反过来驱动智能体生成新的规则或修正旧规则让系统像滚雪球一样越用越强。最近“Agentic RAG”概念的兴起给了我们很大启发。传统的检索增强生成RAG是被动的而Agentic RAG让智能体主动规划、调用工具去完成任务。我们的思路异曲同工让一个智能体主动去“思考”如何制定分类规则调用化学信息学工具如RDKit去验证规则的合理性和覆盖率最终形成一个不断进化的、透明的规则体系。这不仅仅是提高分类准确率更是为了赢得化学家的信任让机器决策过程变得可审计、可辩论、可改进。2. 系统架构核心智能体、规则与验证循环要实现标题中的宏伟蓝图我们需要一个精心设计的架构。这个架构的核心是一个闭环系统由三个关键角色构成规则生成智能体Rule-Generating Agent、可验证规则库Verifiable Rule Base和确定性分类器Deterministic Classifier。它们共同构成了一个自我驱动、持续优化的飞轮。2.1 规则生成智能体从“炼丹师”到“规则工程师”这个智能体是整个系统的“大脑”。它的输入是一组已标记的反应实例例如1000个标记为“SN2”的反应以及当前规则库的状态。它的核心任务不是直接预测标签而是提出假设——生成一条可能区分该类反应与其他类反应的规则。这个智能体本身可以是一个大语言模型LLM例如经过化学文献微调的模型或者是一个符号学习与神经网络结合的混合系统。它的工作流程是推理式的模式观察分析输入的反应集合寻找统计上显著的结构或电子特征模式。例如它可能发现几乎所有SN2反应都涉及一个亲核原子进攻一个连接着离去基团的碳原子。假设生成将观察到的模式转化为形式化的、可执行的规则假设。这通常表达为SMARTSSMILES Arbitrary Target Specification模式或基于反应指纹Reaction Fingerprint的逻辑条件。例如生成一条SMARTS规则来描述亲核原子、中心碳和离去基团的空间与键合关系。初步评估智能体会利用内置的化学知识或调用工具对生成的规则进行合理性检查避免生成化学上不可能的规则比如要求一个碳原子同时形成五根共价键。注意这里智能体的输出必须是可解释的符号化规则而不是一个概率值或嵌入向量。这是与端到端深度学习模型最本质的区别。2.2 可验证规则库透明化的知识容器生成的规则假设不会直接投入使用而是进入一个待验证的规则库。每条规则都包含几个关键属性规则表达式人类可读的SMARTS字符串或逻辑表达式。覆盖集当前数据集中能被这条规则匹配的所有反应实例。准确率/置信度在覆盖集上规则预测的类别与真实标签的一致程度。父规则/衍生关系记录这条规则是由哪条更泛化的规则细化而来便于追溯和知识管理。“可验证Verifiable”体现在两个方面一是规则本身是符号化的化学家可以阅读并判断其化学意义是否正确二是其性能指标覆盖度、准确率可以在任何数据集上被精确计算和复现。这个规则库是系统知识的唯一来源也是系统“白盒”特性的基石。2.3 确定性分类器与验证循环让系统自我进化分类器的工作很简单给定一个新反应遍历规则库中所有激活的规则按照预设的优先级如精确匹配优先应用第一条匹配的规则输出其类别。这个过程是确定性的没有随机采样或 dropout。系统的“自扩展Self-expanding”能力来自于一个关键的验证与反馈循环分类与错误检测当系统对新的或已有的反应进行分类时会产出结果。如果遇到分类错误与专家标签不符或无法分类没有规则匹配的情况这些“问题案例”会被特别标记。触发智能体一批“问题案例”或一个新发现的反应类别会作为新的输入触发规则生成智能体。规则精炼与扩展智能体分析这些案例可能执行两种操作规则精炼Refinement如果某条规则导致了错误如过泛化导致误判智能体会尝试生成一条更严格、更精确的规则来替换或补充它。例如原规则“任何醇与羧酸的反应”可能误判某些案例精炼为“伯醇或仲醇与脂肪族羧酸在酸催化下的反应”。规则扩展Expansion如果遇到全新类型的反应规则库无法匹配智能体会尝试为这个新类别生成一条初始规则。规则验证与入库新生成或精炼的规则会在一个独立的验证集或通过交叉验证进行性能评估。只有当其准确率和覆盖率超过某个阈值并且与现有规则库无严重冲突时才会被正式纳入激活规则库。这个循环使得系统能够从错误中学习从新数据中增长知识从而实现“自我扩展”。它不像神经网络那样通过梯度更新来隐式学习而是通过显式的、可追溯的规则修改来进化。3. 关键技术实现从理论到代码的跨越理解了架构我们来看看具体如何实现。这里涉及几个关键的技术选择和实践细节。3.1 规则表示为什么选择SMARTS与反应指纹结合规则的表达必须兼顾表达能力和可计算性。我们主要采用两种方式SMARTS反应模式这是最直观、化学家最易读的方式。它直接描述反应中心原子、键在反应前后的变化。例如一个简单的SN2反应SMARTS规则可能写成[#6:1]-[#8X2:2][#6:1]-[#7:3]但这太粗糙了。更实用的规则需要包含邻接环境、立体化学等。智能体生成SMARTS是一个难点需要将自然语言描述或统计特征转化为精确的SMARTS字符串。一种策略是让智能体输出SMARTS的“组件”如官能团模式然后通过模板组合。基于反应指纹的逻辑规则反应指纹是将整个反应编码为一个固定长度的二进制或实数向量每个位表示某种化学特征的存在与否或程度。例如RDKit可以生成基于差异的指纹反应物指纹减去产物指纹。一条规则可以表示为“如果指纹的第15、28、73位同时为1且第42位为0则分类为A”。这种表示对智能体尤其是基于树的模型更友好易于进行逻辑操作与、或、非但可读性稍差。在实际系统中我们通常混合使用用高精度、高可读性的SMARTS规则作为“骨干规则”处理常见、明确的反应类型用基于指纹的逻辑规则作为“补充规则”或“过滤器”处理复杂、多条件的边缘情况或者用于快速预筛选。智能体需要学习在何种情况下生成何种类型的规则。3.2 智能体的实现路径LLM驱动 vs 符号学习驱动如何构建这个规则生成智能体目前有两条主流技术路径路径一LLM大语言模型驱动这是目前最火热的方向得益于ChatGPT等模型在代码生成和逻辑推理上展现的潜力。我们可以设计一个提示Prompt工程流程你是一个经验丰富的计算化学家。请分析以下一组反应以SMILES表示它们都属于“Diels-Alder环加成反应”。 [反应1 SMILES] [反应2 SMILES] ... 请总结出这些反应共同的关键结构特征。然后将这些特征转化为一条或多条具体的、可执行的SMARTS反应规则用于从混合反应池中识别此类反应。请确保规则尽可能精确避免匹配到其他类型的环加成反应。LLM可能会输出一段包含SMARTS规则的自然语言描述。我们需要后处理来提取和标准化这些规则。这种方法的优点是灵活能够利用LLM海量的化学知识。缺点是输出不稳定可能生成语法错误的SMARTS且每次调用成本高。实操心得需要对LLM输出进行严格的语法验证和化学合理性检查例如用RDKit测试规则是否能被正确解析和编译。路径二符号归纳学习Inductive Logic Programming, ILP驱动这是一种更传统但非常稳健的AI方法。它直接从正例某类反应和反例其他类反应中学习一阶逻辑规则。系统会像侦探一样不断提出假设规则用反例去反驳逐步精炼直到找到一个能覆盖所有正例且排除所有反例的规则。ILP的优点在于其数学上的严谨性和规则的可解释性极致。缺点是对数据质量要求高计算复杂度可能随问题规模指数增长且处理复杂化学结构时表达能力可能受限。我们的混合策略在实际项目中我们采用了混合策略。用轻量级的LLM或经过微调的较小模型进行初始规则假设的生成和自然语言到符号的转换然后使用符号学习算法对生成的规则集进行精炼、去冗余和冲突消解。同时我们构建了一个化学规则校验器利用RDKit等工具对任何生成的SMARTS规则进行语法和基本化学原理的校验例如检查原子价是否合理。3.3 确定性分类的逻辑冲突消解与规则优先级当多条规则可能匹配同一个反应时如何保证分类的确定性我们设计了一套优先级策略特异性优先更具体、更严格的规则优先于更泛化的规则。我们可以用规则的长度SMARTS的复杂程度或覆盖的反应实例数量作为特异性度量。覆盖实例越少的规则通常越具体。置信度优先在特异性相近时选择在历史验证集中准确率更高的规则。最近使用优先对于新加入的规则如果其与旧规则冲突但性能更优可以暂时赋予更高优先级但需要监控。所有这些逻辑都必须被明确编码形成一个决策树或决策列表。分类过程就是遍历这个有序的规则列表返回第一个匹配规则的类别。这个过程完全透明每一步都可追溯。4. 构建实战一个基于开源工具的简化原型理论说了这么多我们来点实际的。下面我将展示如何利用Python和开源库搭建一个极简的原型系统。这个原型不会包含完整的自扩展循环但会演示核心的“规则生成-验证-分类”流程。4.1 环境准备与数据加载首先我们需要一个化学反应数据集。这里使用一个经典的公开数据集比如USPTO的某些子集或者Named Reactions数据集。我们使用rdkit和pandas进行处理。# 假设环境已安装conda conda create -n agentic_rxn_classify python3.9 conda activate agentic_rxn_classify pip install rdkit-pypi pandas scikit-learnimport pandas as pd from rdkit import Chem from rdkit.Chem import AllChem, Draw, rdChemReactions from rdkit.Chem.Draw import IPythonConsole # 加载一个示例数据集这里用模拟数据演示概念 # 假设我们有一个CSV包含三列reaction_smiles, reaction_class, split # 例如CCO.CC(O)OCCOC(O)C.Cl, Esterification, train data pd.read_csv(reaction_data.csv) train_data data[data[split] train].copy() # 将SMILES字符串转换为RDKit的反应对象 def smi_to_reaction(smi): # 注意实际SMILES反应表达式可能需要预处理常用格式是“反应物试剂产物”或“反应物产物” # 这里假设是“反应物产物”格式 rxn AllChem.ReactionFromSmarts(smi, useSmilesTrue) return rxn train_data[rxn_obj] train_data[reaction_smiles].apply(smi_to_reaction) print(fLoaded {len(train_data)} training reactions.)4.2 规则生成智能体的简易实现我们实现一个基于“频繁子结构挖掘”的简易智能体。它不依赖LLM而是通过分析同一类反应中反应物和产物的共同子结构变化来生成规则。from rdkit.Chem import rdFMCS from collections import defaultdict class SimpleRuleAgent: def __init__(self, min_atoms5): self.min_atoms min_atoms # 规则匹配的最少原子数避免太泛化 def generate_rule_for_class(self, reaction_objects, class_name): 为一组同类的反应对象生成一条代表性的SMARTS规则。 这是一个简化版寻找反应物到产物最大共同子结构MCS的变化模式。 实际应用需要更复杂的反应中心识别。 if len(reaction_objects) 2: return None # 尝试提取反应中心简化处理取第一个反应为例分析原子映射 # 注意真实数据需要原子映射信息才能准确识别反应中心 try: # 这里我们假设反应对象有原子映射。我们寻找在反应物和产物中都出现的映射号。 example_rxn reaction_objects.iloc[0] reactants example_rxn.GetReactants() products example_rxn.GetProducts() # 这是一个非常简化的演示我们直接生成一个基于整个反应SMILES的规则。 # 实际上需要智能分析并生成只针对反应中心的SMARTS。 # 为了演示我们退回一种更简单的方法生成一个覆盖所有反应物的通用子结构。 reactant_mols [mol for mol in reactants] # 寻找反应物分子的最大共同子结构MCS mcs_result rdFMCS.FindMCS(reactant_mols, threshold0.8, timeout5) if mcs_result.numAtoms self.min_atoms: # 以这个MCS作为规则的核心部分 core_smarts mcs_result.smartsString # 构建一个非常粗略的反应规则核心部分在反应前后不变实际上需要更精细设计 # 这只是为了演示规则的形式 rule_smarts f({core_smarts}).({core_smarts})({core_smarts}).({core_smarts}) return { class: class_name, rule_smarts: rule_smarts, coverage: len(reaction_objects), # 覆盖的样本数 source: MCS_agent } else: return None except Exception as e: print(fError generating rule for {class_name}: {e}) return None # 使用智能体为每个反应类别生成初始规则 agent SimpleRuleAgent(min_atoms5) rule_candidates [] for class_name, group in train_data.groupby(reaction_class): if len(group) 10: # 只对有一定样本量的类别生成规则 rule agent.generate_rule_for_class(group[rxn_obj], class_name) if rule: rule_candidates.append(rule) print(fGenerated {len(rule_candidates)} initial rule candidates.)4.3 规则验证与规则库构建生成的规则候选需要验证。我们在一个保留的验证集或通过交叉验证上计算每条规则的准确率和覆盖率。from sklearn.model_selection import train_test_split # 划分训练集用于规则生成测试集用于验证 train_ruleset, val_set train_test_split(train_data, test_size0.2, stratifytrain_data[reaction_class]) # 重新用train_ruleset生成规则...代码略同上 class RuleValidator: def __init__(self): self.rules [] # 存储最终激活的规则每条规则是一个dict def add_rule(self, rule_smarts, rule_class, min_coverage5, min_accuracy0.85): 添加并验证一条规则 try: rxn_smarts AllChem.ReactionFromSmarts(rule_smarts) except: print(fInvalid SMARTS rule: {rule_smarts}) return False matches [] for idx, row in val_set.iterrows(): # 尝试用规则匹配验证集中的反应 if rxn_smarts.IsMoleculeReactant(row[rxn_obj]): matches.append(idx) if len(matches) min_coverage: print(fRule coverage too low ({len(matches)} {min_coverage}). Discarded.) return False # 计算准确率匹配的反应中有多少个真实标签是rule_class correct 0 for idx in matches: if val_set.loc[idx, reaction_class] rule_class: correct 1 accuracy correct / len(matches) if matches else 0 if accuracy min_accuracy: self.rules.append({ smarts: rule_smarts, class: rule_class, coverage: len(matches), accuracy: accuracy, specificity: len(rule_smarts) # 简易特异性度量越长越具体 }) # 按特异性长度和准确率排序特异性高的在前 self.rules.sort(keylambda x: (-len(x[smarts]), -x[accuracy])) print(fRule added: {rule_class} (Acc: {accuracy:.2f}, Cov: {len(matches)})) return True else: print(fRule accuracy too low ({accuracy:.2f} {min_accuracy}). Discarded.) return False validator RuleValidator() for rc in rule_candidates: validator.add_rule(rc[rule_smarts], rc[class]) print(fFinal rule library has {len(validator.rules)} verified rules.)4.4 确定性分类器的实现分类器就是有序地应用规则库中的规则。class DeterministicReactionClassifier: def __init__(self, rule_list): # rule_list 已经是按优先级排序好的 self.rule_list rule_list def predict(self, reaction_smiles): rxn_obj smi_to_reaction(reaction_smiles) for rule in self.rule_list: if rule[smarts].IsMoleculeReactant(rxn_obj): return rule[class] return Unknown # 没有规则匹配 # 初始化分类器 classifier DeterministicReactionClassifier(validator.rules) # 在测试集上测试 test_data data[data[split] test].copy() correct 0 total len(test_data) for idx, row in test_data.iterrows(): pred classifier.predict(row[reaction_smiles]) if pred row[reaction_class]: correct 1 accuracy correct / total print(fDeterministic Classifier Accuracy on test set: {accuracy:.4f}) # 查看分类细节 print(\nSample predictions:) for i in range(min(5, len(test_data))): row test_data.iloc[i] pred classifier.predict(row[reaction_smiles]) print(fTrue: {row[reaction_class]:20} Pred: {pred:20} Match: {pred row[reaction_class]})这个原型展示了从数据到规则生成再到验证和分类的完整流程。虽然我们的“智能体”非常简陋基于MCS但架构是清晰的。要使其真正“Agentic”和“Self-expanding”我们需要用更强大的LLM或ILP引擎替换SimpleRuleAgent并实现一个后台服务持续收集分类错误和未知反应定期触发规则生成与优化循环。5. 挑战、应对策略与未来展望构建这样一个系统绝非易事在实际操作中我们遇到了不少坑也总结出一些关键策略。5.1 规则冲突与知识一致性维护随着规则库的扩展规则之间的冲突几乎不可避免。两条规则可能匹配同一个反应却给出不同的分类。我们的优先级策略能解决一部分但更根本的是需要维护一个规则间的逻辑关系图。策略我们引入了一个“规则冲突检测”模块。每当新规则加入时不仅测试其单独性能还测试其与现有规则集的交互。如果新规则R_new与旧规则R_old在验证集上有重叠匹配且分类不同系统会标记冲突。解决冲突可以靠人工仲裁也可以设计更复杂的机制比如在重叠区域比较两者的准确率或生成一条更具体的“仲裁规则”来覆盖冲突区域。实操心得维护一个“规则-反例”的映射表极其有用。每条规则记录下它错误分类的案例这些案例是驱动规则精炼的直接燃料。5.2 处理模糊与多中心反应许多反应并不“干净”可能同时发生多个键的断裂与形成或者处于两类反应的边界。确定性分类在这里会遇到挑战。策略我们允许系统输出多个可能标签及其对应的规则依据并附加一个置信度分数例如匹配规则的特异性和历史准确率的乘积。对于最终应用可以设置一个置信度阈值低于阈值的交由人工复核。同时可以定义一些“元规则”或“冲突消解规则”专门处理已知的模糊情况。例如“如果同时匹配规则A亲电芳香取代和规则B亲核芳香取代则检查亲核试剂的强度……”。5.3 评估指标超越准确率对于自扩展系统传统的准确率、精确率、召回率不够用了。我们需要关注规则库的健康度规则数量 vs. 平均规则特异性。我们希望规则库在增长的同时规则的平均质量特异性、准确率不下降。覆盖增长曲线随着系统运行未知反应‘Unknown’的比例是否在持续下降人工干预频率系统运行一段时间后需要人工介入解决冲突或标注新案例的频率变化。理想情况下这个频率应该逐渐降低。5.4 与现有工作流的集成化学家不关心你的算法多精妙他们关心是否好用。如何将这个系统集成到现有的化学信息学平台如KNIME Pipeline, Jupyter Notebook或电子实验记录本ELN中策略我们将系统封装成RESTful API微服务。提供两个核心端点/classify输入反应SMILES返回分类结果和依据规则和/feedback输入反应SMILES和正确标签/错误报告用于触发自扩展循环。前端只需调用这些API即可。同时提供规则库的可视化查询界面让化学家能浏览、搜索、甚至手动微调规则增强信任感。未来展望标题中的“Agentic”一词指明了方向。未来的系统可能不止一个智能体而是一个多智能体协作系统一个“探索者”智能体负责发现新的反应模式一个“批评者”智能体负责评估规则的严谨性和潜在漏洞一个“整合者”智能体负责管理规则库的知识图谱确保一致性。结合“Simulink Agentic Toolkit”这类仿真验证思想我们甚至可以在加入规则前先在量子化学计算或分子动力学模拟中“虚拟验证”一条规则预测的反应是否在能量和机理上合理将可验证性推到新的高度。这条路走下来最大的体会是将AI的生成能力与符号逻辑的确定性结合起来是解决化学领域高可靠性、可解释性需求的一条充满希望的路径。它不像追求SOTA的深度学习那样充满戏剧性更像是在搭建一个由代码和逻辑构成的“化学知识晶体”每一个面规则都清晰、稳定并且能够自主地生长。这个过程里工程师的角色从“调参师”变成了“系统架构师”和“知识工程师”与领域专家化学家的对话也变得前所未有的重要和顺畅。