LangChain 在生命科学与医疗保健领域的应用——LangChain 在生物学中的应用

172 阅读43分钟

生成式人工智能与生物学的交汇,代表了生命科学中最具前景的前沿领域之一。预计未来8到10年,该市场规模将增长四倍,至少达到15亿美元。如此显著的增长,得益于正在重塑生物学研究和药物发现流程的革命性技术。市场增长的部分动力来自于药物研发和医疗健康应用,生成式AI显著降低成本并加速研发进程。这些领域的一些具体应用案例将在第8章和第10章中介绍。制药和生物技术行业占据市场半壁江山,是生成式AI应用的领先采用者。

生物学中的大型语言模型(LLMs)

最初为自然语言处理设计的文本大型语言模型(LLMs),现已应用于从基因组学到单细胞分析的生物学领域。该技术支持合成基因和基因组的设计,促进具有特定所需特性的生物体的创建。这在生物技术中具有广泛应用,如开发生物燃料和改良农作物。此外,生成式AI还用于优化基因组编辑工具,如CRISPR-Cas9,提高其在遗传物质修改中的精度和效率。

生成式AI在基因组学中的一个重要应用是创建合成基因组,同时保留真实基因组数据的统计特性。这在数据有限时尤其有价值,有助于研究人员发现新的基因组模式。在基因组学领域,LLMs帮助解码DNA序列,以理解遗传变异、预测功能元素和分析调控序列。DNA语言模型如DNAGPT和DNABERT,基于基因组数据训练,用于预测全基因组变异效应、识别顺式调控区域及确定DNA-蛋白质相互作用。DNAGPT更擅长预测或生成序列型DNA信息,而DNABERT更适合理解整条DNA序列的上下文,用于分类、识别和关系预测。

微调后的LLMs在基因组学、蛋白质组学等多个生物学领域取得了重要应用。虽然这些模型最初为人类语言设计,但其学习复杂序列模式的能力,使其在DNA、RNA及蛋白质序列分析中表现出色。其适应性推动了基因注释、蛋白质结构预测和生物分子相互作用的突破。

在转录组学中,LLMs处理RNA序列,用于研究基因表达和RNA结构。RNA-FM和RNA-MSM预测RNA的二级和三级结构,帮助研究RNA折叠和功能。专用模型SpliceBERT预测RNA剪接事件,这对从同一基因产生不同蛋白异构体至关重要。LLMs还可用于RNA修饰研究,这些修饰影响RNA的稳定性和功能。比如BERT-m7G和Bert2Ome用于识别RNA甲基化位点,CodonBERT则预测mRNA降解及蛋白质翻译效率。这些应用对基于RNA的疗法和疫苗设计尤为重要。

单细胞分析也是LLMs受益匪浅的生物学领域,这些模型帮助细胞类型分类、多组学数据整合和基因调控网络推断。scGPT和scBERT分析单细胞RNA测序(scRNA-seq)数据,识别不同细胞群体并预测细胞状态,辅助理解组织发育、疾病进展和免疫反应。DeepMAPS和scMoFormer促进多模态分析,提升对复杂生物系统的洞察。LLMs还能预测基因功能、绘制基因互作图谱及发现潜在治疗靶点,利用注意力机制突出基因组中的关键调控元素,支持功能基因组学和精准医学。

Transformer模型最初用于自然语言处理,能够捕捉序列元素间复杂关系,突破传统序列计算限制。其注意力机制使模型能理解蛋白质序列中远程相互作用,类似于语言模型理解文本中远距离语义关联。

不同的Transformer架构在蛋白质建模中各有侧重。基于编码器的模型如BERT侧重蛋白质序列表示理解;基于解码器的模型如GPT则能生成新颖的蛋白质序列;完整的编码器-解码器模型支持不同分子表示间复杂转换。每种方法都为计算蛋白质设计带来独特见解。

专门的蛋白质语言模型正面向特定研究领域涌现。抗体专用模型解决治疗性蛋白设计中的独特挑战,酶专用模型聚焦催化分子工程。这些领域定制的模型有助于更精准的计算设计策略。

早期蛋白质语言模型展现出卓越科学能力。Meta开发的ESM-2模型以150亿参数,仅基于序列信息就能准确预测蛋白质结构和突变效应,生成蛋白质结构速度和效率均优于如AlphaFold初版等早期方法。这些模型还能生成自然界不存在的蛋白质序列,极大扩展理论蛋白设计空间。

蛋白质语言模型在多应用领域展现广阔前景。研究者利用它们预测蛋白性质、生成新蛋白序列、设计治疗分子。例如,ProteinBERT、ProtTrans和Salesforce的ProGen能生成功能明确的蛋白质,预测二级结构、折叠模式及进化关系。IgLM专注抗体设计,解决治疗性蛋白工程关键难题。LLMs还能预测蛋白质的翻译后修饰如磷酸化、糖基化和甲基化位点,这些修饰对蛋白功能和调控至关重要。最后,LLMs帮助研究蛋白-蛋白相互作用,这对细胞过程和药物靶向具有基础意义。

注意事项
生成式人工智能在生命科学领域的潜力极大,以至于诸如Salesforce、Meta、微软、谷歌、NVIDIA等与生命科学毫无直接关联的科技巨头,也投入大量资源进行相关研究。

计算蛋白质设计为多个领域带来了前所未有的机遇。其潜在应用包括为复杂疾病开发新型治疗药物、设计工业用酶类、以及为环境修复设计蛋白质。理论上,蛋白质序列空间几乎是无限的,估计可能的蛋白质组合多达10^1000种。自然进化仅探索了这其中极为微小的一部分,为计算设计留下了巨大潜力。

蛋白质语言模型的计算复杂度极高,训练这类模型需要大量计算资源。这些模型高度依赖先进的GPU基础设施(如NVIDIA A100/H100)。TPU在云环境中则提供了高效的大规模计算替代方案。研究人员正在探索模型压缩技术,以让这些强大工具更易于访问。如何在保持性能的同时减小模型规模,是当前的关键研究目标。

尽管取得了诸多成功,生物领域的LLMs依然面临挑战,如高质量训练数据的需求、模型可解释性问题,以及之前提到的高昂计算成本。蛋白质序列的分词(tokenization)比自然语言更为复杂,缺乏明显的分隔符和标点符号,数据注释工作仍在持续改进中。开发可解释的LLM技术仍处于探索阶段,因为这些模型作为复杂的高维系统,其决策过程不透明。LLMs的“灰盒”特性使得全面理解其如何得出特定输出变得困难。当前研究重点包括提升模型的可解释性——即生成易于人类理解的理由,以及增强模型的可理解性——即揭示其内部机制如何影响输出响应。

生物学LangGraph应用

第6章介绍了多个化学功能整合成一个智能体。正如第5章所述,LangGraph允许将多个独立的AI智能体组成团队。我们将创建一个蛋白质超级应用,以更好地理解如何构建复杂工具、开发智能体以及创建子团队。

创建生物学工具

LangGraph工具是智能体用来与数据和外部系统交互或执行计算的专用功能。这些工具范围广泛,从简单的实用函数(如基本字符串操作或文件加载/保存)到复杂的API和数据库集成,再到运行机器学习或语言模型。每个工具设计成模块化和可复用,使开发者可以在LangGraph智能体框架内组合多个工具,构建复杂的工作流,并确保根据任务恰当调用。由于LangGraph支持动态且灵活的执行路径,工具可以根据工作流逻辑以不同顺序或组合被调用。

2024年,诺贝尔化学奖授予David Baker、Demis Hassabis和John Jumper,以表彰他们在蛋白质结构预测与设计领域的开创性贡献。他们的工作解决了基础科学难题,为医学、生物技术和材料科学创新铺平了道路。基于此,我们创建第一个ColabFold智能体子团队——这是最接近AlphaFold的替代方案,可轻松在Google Colab上运行。

示例7-1和7-2定义了我们用于两个智能体子团队的四个工具:run_colabfold_predictionvisualize_structureshow_pae_plotshow_plddt_plot。另外,prepare_data_for_colabfold函数用于将用户数据转换为指定格式。

如前所述,设计工具的最佳实践之一是模块化。工具可能比普通函数更复杂,因为它需执行指定的目标操作。从理论上讲,我们可以通过提供必要描述,将任何函数转换为工具。正如第5章中讲,LangChain中链(chain)和智能体(agent)的主要区别是链是顺序执行,而智能体则决定是否调用以及调用哪个工具。为做出决策,智能体利用大型语言模型(LLM)调用带有预测参数的工具。稍后我们将学习如何创建实用智能体,现在先把prepare_data_for_colabfold保留为函数。

你会注意到示例7-1详细描述了kwargs参数。这是通过解析用户输入调用带参数工具,向LLM提供上下文,因为缺乏上下文时,期望参数名可能与生成参数名不符。完整代码可在LangChain4LifeSciencesHealthcare代码库中查看。

示例7-1. LangGraph ColabFold预测工具

from colabfold.batch import get_queries, set_model_type, run as colabfold_run
from colabfold.download import download_alphafold_params
from pathlib import Path

def prepare_data_for_colabfold(query_sequence: str, jobname: str = 'test'):
    """准备ColabFold。在预测蛋白质结构前调用,输入蛋白序列。"""
    ...
    <数据准备代码>
    ...
    with open(f"{jobname}.csv", "w") as text_file:
        text_file.write(f"id,sequence\n{jobname},{query_sequence}")
    return f"{jobname}.csv"

@tool
def run_colabfold_prediction(query_sequence: str, model_type: str = "auto",
                            jobname: str = "demo", **kwargs) -> str:
    """使用ColabFold预测蛋白质结构。kwargs参数:
       result_dir: Union[str, Path], num_models: int,
       is_complex: bool, num_recycles: Optional[int] = None,
       ...
       calc_extra_ptm: bool = False, use_probs_extra: bool = True"""

    # 准备参数
    queries_path = prepare_data_for_colabfold(query_sequence, jobname)
    queries, is_complex = get_queries(queries_path)
    model_type = set_model_type(is_complex, "auto")

    # 下载参数并运行
    download_alphafold_params(model_type, Path("."))
    results = colabfold_run(
        queries=queries,
        is_complex=is_complex,
        **kwargs
    )
    return f"预测完成。结果保存在{jobname}目录"

示例7-2涵盖了用于ColabFold的可视化工具。我们将3D分子可视化过程与生成预测局部距离差异测试(pLDDT)和预测对齐误差(PAE)图的过程分开。pLDDT图显示模型对预测3D结构中每个氨基酸位置的置信度,衡量局部距离的保持情况。PAE图展示蛋白质结构中所有残基对之间预测距离误差,帮助识别哪些区域模型间相对准确性更高。

示例7-2. LangGraph ColabFold可视化工具

@tool
def visualize_structure(pdb_file: str, color: str = "lDDT") -> str:
    """使用py3Dmol可视化ColabFold蛋白质结构"""
    view = py3Dmol.view()
    view.addModel(open(pdb_file, 'r').read(), 'pdb')

    if color == "lDDT":
        view.setStyle({'cartoon': {'colorscheme': {'prop': 'b', 'gradient': 'roygb'}}})
    elif color == "rainbow":
        view.setStyle({'cartoon': {'color': 'spectrum'}})
    view.zoomTo()
    return view.show()

@tool
def show_plddt_plot(metrics: Dict) -> str:
    """创建并显示预测的pLDDT(局部距离差异测试)图"""
    plt.figure()
    plt.plot(metrics["plddt"])
    plt.title("预测pLDDT")
    plt.xlabel("残基")
    plt.ylabel("pLDDT")
    return plt.show()

@tool
def show_pae_plot(metrics: Dict) -> Optional[str]:
    """创建并显示预测对齐误差(PAE)图(如可用)"""
    if metrics.get("pae") is None:
        return None
    plt.figure()
    plt.imshow(metrics["pae"])
    plt.colorbar(label="预测对齐误差")
    plt.title("预测对齐误差")
    plt.xlabel("残基")
    plt.ylabel("残基")
    return plt.show()

接下来,我们回顾应用如何协调多个AI智能体完成蛋白质结构预测和可视化。核心采用状态机架构管理各专用智能体间的工作流程,每个智能体承担特定职责。create_team_supervisor函数是基于LLM的路由器,充当项目经理,依据当前对话状态决定下一个应执行的智能体。它配置了团队成员列表(本例中为Calculate和Visualize),并能判断何时完成工作(通过FINISH选项)。示例7-3展示了该监督者如何利用函数调用能力,确保决策结构化且可预测;它必须从预定义选项列表中通过JSON schema进行选择。

create_agent函数用于创建专用智能体。每个智能体包含特定工具和一个系统提示(system prompt),该提示定义了智能体的角色和能力。该函数使用ChatPromptTemplate处理对话历史和智能体的草稿板(scratchpad),帮助智能体进行推理。生成的智能体被封装在AgentExecutor中,负责执行智能体选择的操作并管理其状态。示例7-3定义了两个智能体:colabfold_calc_agentcolabfold_viz_agent。计算智能体拥有运行ColabFold预测的工具(见示例7-1),而可视化智能体则配备了结构可视化和结果绘图工具(见示例7-2)。每个智能体都有独特的系统提示,明确其专业化工作流程角色。

agent_node函数将智能体集成到状态机图中。它接收当前状态、智能体和名称作为输入,执行智能体逻辑,并以标准化消息格式返回结果。该抽象使得不同类型的智能体可以无缝插入工作流程。

示例7-3. LangGraph设置参数

def create_team_supervisor(llm: ChatOpenAI, members, system_prompt) -> str:
    """基于LLM的路由器"""
    options = ["FINISH"] + members
    function_def = {
        "name": "route",
        "description": "选择下一个角色",
        "parameters": {
            "title": "routeSchema",
            "type": "object",
            "properties": {
                "next": {
                    "title": "Next",
                    "anyOf": [{"enum": options}],
                },
            },
            "required": ["next"],
        },
    }
    prompt = ChatPromptTemplate.from_messages([
        ("system", system_prompt),
        MessagesPlaceholder(variable_name="messages"),
        (
            "system", f"""基于以上对话,谁应该下一步执行?还是结束(FINISH)?
请选择:{options}"""
        )
    ]).partial(options=str(options), team_members=", ".join(members))

    return (prompt
            | llm.bind_functions(functions=[function_def], function_call="route")
            | JsonOutputFunctionsParser()
           )

def create_agent(llm: ChatOpenAI, tools: List[BaseTool], system_prompt: str):
    """创建包含特定工具和提示的智能体"""
    prompt = ChatPromptTemplate.from_messages([
        ("system", system_prompt),
        MessagesPlaceholder(variable_name="messages"),
        MessagesPlaceholder(variable_name="agent_scratchpad")
    ])
    agent = create_openai_functions_agent(llm, tools, prompt)
    return AgentExecutor(agent=agent, tools=tools)

# 节点函数
def agent_node(state: Dict, agent: AgentExecutor, name: str) -> Dict:
    """智能体通用节点函数"""
    result = agent.invoke(state)
    return {"messages": [HumanMessage(content=result["output"], name=name)]}

示例7-4展示了如何使用带有自定义ProteinState类型的StateGraph类构建状态机图。该图包含三个主要节点:Calculate、Visualize和supervisor。Calculate和Visualize节点分别对应其智能体版本,而supervisor节点负责管理它们之间的流程。图结构通过显式边(edges)构建,决定流程如何在节点间流转。supervisor节点的条件边使用lambda函数,根据supervisor决策(存储在状态的next字段)路由工作流:若选择Calculate,流程进入计算智能体;选择Visualize,则进入可视化智能体;选择FINISH,流程结束。ColabFold子团队结构概览见图7-1。

image.png

整个系统以supervisor(监督者)作为入口点初始化,这意味着每个工作流程都从监督者开始,由其决定哪个智能体应当先执行。随后,图被编译成一个可执行的链(executable chain),能够处理输入并管理智能体之间的工作流转。最后,enter_chain函数提供了一个简洁的接口,用于启动新的工作流程。它接收一条消息,将其包装成预期的状态格式,并传递给已编译的链进行处理。

实际应用中,该系统可以处理类似“预测蛋白质序列MAEGEITT…LLYCSNG的结构并进行可视化”的请求。监督者会先将流程路由到计算智能体执行预测,然后切换到可视化智能体生成可查看的结构,最后在工作完成时决定结束(FINISH)。

示例7-4. LangGraph ColabFold团队

# 创建智能体
colabfold_calc_agent = create_agent(
    llm,
    [run_colabfold_prediction],
    "你是一个负责运行ColabFold预测的计算智能体"
)
colabfold_viz_agent = create_agent(
    llm,
    [visualize_structure, plot_results],
    "你是一个负责创建结构可视化的可视化智能体"
)
colabfold_supervisor = create_team_supervisor(
    llm,
    ["Calculate", "Visualize"],
    """你负责监督包含计算和可视化智能体的ColabFold团队,
       完成时回复FINISH。"""
)

# 创建具体节点函数
colabfold_calc_node = functools.partial(
    agent_node, agent=colabfold_calc_agent, name="ColabFold-Calc"
)
colabfold_viz_node = functools.partial(
    agent_node, agent=colabfold_viz_agent, name="ColabFold-Viz"
)

class ProteinState(Dict[str, Any]):
    messages: Annotated[List[BaseMessage], operator.add]
    next: str
    sequence: str = ""
    predictions: Dict[str, Any] = {}
    properties: Dict[str, Any] = {}
    visualization: Any = None

# 创建ColabFold子图
colabfold_graph = StateGraph(ProteinState)
colabfold_graph.add_node("Calculate", colabfold_calc_node)
colabfold_graph.add_node("Visualize", colabfold_viz_node)
colabfold_graph.add_node("supervisor", colabfold_supervisor)

# 添加边
colabfold_graph.add_edge("Calculate", "supervisor")
colabfold_graph.add_edge("Visualize", "supervisor")
colabfold_graph.add_conditional_edges(
    "supervisor",
    lambda x: x["next"],
    {
        "Calculate": "Calculate",
        "Visualize": "Visualize",
        "FINISH": END
    }
)
colabfold_graph.set_entry_point("supervisor")
colabfold_team = colabfold_graph.compile()

colabfold_team.invoke({
    """预测蛋白质序列MAEGEITT...LLYCSNG的结构并进行可视化"""
})

执行“预测蛋白质序列MAEGEITT…LLYCSNG的结构并进行可视化”的结果如图7-2所示。

image.png

采用这里展示的方法,我们将新增一个ThermoMPNN团队,专门用于预测蛋白质序列引入突变后蛋白稳定性的变化。选择该团队主要是因为存在一个Google Colab演示——创建研究子团队从未如此简单。

示例7-5中创建的团队包含以下组件:

  • 基于facebook/esmfold_v1的ESMFold智能体
  • 基于facebook/esm2_t6_8M_UR50D的突变分析工具
  • Cold Spot Scanner可视化工具
  • 基于nferruz/ProtGPT2的蛋白质生成智能体
  • 配备Prodigal工具的DNA生成和分析智能体,能够预测蛋白质编码基因
  • 基于togethercomputer/evo-1-131k-base模型的DNA计算工具
  • 供实用智能体使用的若干辅助工具

该团队结构的可视化展示见图7-3。

image.png

正如前面提到的,生命科学应用中大型语言模型(LLMs)的两个主要用途,包括上述示例中,是用于协调流程(包括智能体间的通信、工具调用等)和直接生成。在该应用中,直接生成涵盖蛋白质和DNA的生成。这里定义了run_dna_generationgenerate_protein工具:

@tool
def run_dna_generation(input_text: str = None):
    "使用evo模型生成DNA序列"
    if not input_text:
        input_text = (
            "|"  # 开始提示符
            + "d__Bacteria;"
            + "p__Pseudomonadota;"
            + "c__Gammaproteobacteria;"
            + "o__Enterobacterales;"
            + "f__Enterobacteriaceae;"
            + "g__Escherichia;"
            + "s__Escherichia"
            + "|"  # 结束提示符
        )
    model_name = 'togethercomputer/evo-1-131k-base'
    config = AutoConfig.from_pretrained(
        model_name, trust_remote_code=True, revision="1.1_fix"
    )
    model = AutoModelForCausalLM.from_pretrained(
        model_name, config=config, trust_remote_code=True, revision="1.1_fix"
    )
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    inputs = tokenizer(input_text, return_tensors="pt").to(device)

    outputs = model.generate(inputs.input_ids, max_length=100, temperature=0.7)
    output = tokenizer.decode(outputs[0], skip_special_tokens=True)
    with open(output_probabilities, 'w', encoding='utf-8') as f:
        json.dump(output, f, ensure_ascii=False, indent=4)

    gen_dna_seq = output['choices'][0]['text']
    gen_dna_seq_record = SeqRecord(
        Seq(gen_dna_seq),
        id="evo-dna",
        description="由Evo生成的DNA序列"
    )
    with open(sequence_fasta, "w") as output_handle:
        SeqIO.write(gen_dna_seq_record, output_handle, "fasta")
    return output

@tool
def generate_protein(input_text: str, num_return_sequences: int = 1) -> str:
    "使用ProtGPT2生成具有期望特性的蛋白质序列。"
    tokenizer = AutoTokenizer.from_pretrained("nferruz/ProtGPT2")
    model = AutoModelForCausalLM.from_pretrained("nferruz/ProtGPT2")
    inputs = tokenizer(input_text, return_tensors="pt").to(device)

    outputs = model.generate(
        inputs.input_ids, max_length=100, temperature=0.7,
        num_return_sequences=num_return_sequences, top_k=500,
        repetition_penalty=1.2, eos_token_id=0
    )
    return tokenizer.decode(outputs[0], skip_special_tokens=True)

说明
ProtGPT2基于GPT-2 Transformer架构,包含36层,模型维度为1280,总参数量约7.38亿。Evo是一个生成式AI模型,将LLM技术应用于DNA序列,训练数据规模庞大,包括8万份微生物基因组和270万份原核及噬菌体基因组,总计约3000亿核苷酸,另加若干质粒序列。

如你所见,团队组织结构是按照智能体和工具的领域划分,而非单纯功能。这使得快速整合最新发布的研究和新兴科学流程成为可能。若工作流程常见类似管道(如加载→计算→可视化),则创建功能性团队最为高效。

每个创建的智能体均通过functools.partial方法转为节点,便于结构化地添加到图中。例如,esmfold_node代表ESMFold智能体,dna_node负责DNA相关任务等。这些节点构成计算工作流基础。除了智能体节点,继之前子团队外,还新增了非智能体的监督者节点用于管理其他节点。团队的StateGraph连接所有节点,定义任务如何在系统中流动,起始于充当入口点的Manager节点。每个节点代表蛋白质分析整体流程中的一个逻辑步骤。我们还通过LCEL链将主图与创建的团队节点连接,如示例7-5中的add_node("Run ColabFold", get_last_message | colabfold_team | join_graph)

示例7-5. 创建LangGraph蛋白质智能体和节点

def get_last_message(state) -> str:
    return state["messages"][-1].content

def join_graph(response: dict):
    return {"messages": [response["messages"][-1]]}

esmfold_agent = create_agent(
    llm, [run_esmfold_prediction], "运行esmfold蛋白质结构预测"
)
dna_agent = create_agent(
    llm, [plot_dna_token_probabilities, predict_protein_coding_genes,
          run_dna_generation],
    """生成DNA序列,预测蛋白质编码基因并可视化DNA token概率"""
)
protein_generation_agent = create_agent(
    llm, [generate_protein, validate_sequences],
    "生成并验证蛋白质序列。"
)
...
# 其他智能体创建
...

esmfold_node = functools.partial(agent_node, agent=esmfold_agent, name="ESMFold")
dna_node = functools.partial(agent_node, agent=dna_agent, name="DNA计算")
protein_generation_node = functools.partial(
    agent_node, agent=protein_generation_agent, name="蛋白质生成"
)
...
# 其他节点创建
...

# 使用LangGraph设置参数创建团队监督者
supervisor = create_team_supervisor(
    llm,
    ["Generate Protein", "Calculate Properties", "Visualize", "Utility",
     "ColabFold", "ThermoMPNN", "DNA Calculations"],
    """你正在监督包含多种计算方法和可视化智能体的蛋白质团队
       要创建或生成新蛋白质-运行Generate Protein
       要生成DNA序列、预测蛋白质编码基因并可视化DNA token概率-运行DNA Calculations
       要使用ESMFold计算蛋白质结构-使用ESMFold
       要使用ColabFold或AlphaFold计算蛋白质结构-使用ColabFold
       要使用ThermoMPNN计算蛋白质稳定性-使用ThermoMPNN
       要计算蛋白质属性和突变影响-运行Calculate Properties
       要可视化蛋白质或网络-运行Visualize
       要加载/保存/下载pdb或按名称查找序列-使用Utility
       完成时回复FINISH。"""
)

# 创建主图
master_graph = StateGraph(ProteinState)
master_graph.add_node("Manager", supervisor)
master_graph.add_node("Generate Protein", protein_generation_agent)
...
# 添加其他节点
...
master_graph.add_node(
    "Run ColabFold", get_last_message | colabfold_team | join_graph)
master_graph.add_node(
    "Run ThermoMPNN", get_last_message | thermompnn_team | join_graph)

# 边定义任务在节点间的流动
# 当相关节点生成蛋白质时,系统将响应返回Manager节点以便进一步处理
# 类似地,可视化任务和结构预测也返回Manager以协调
# 该设计确保每个任务遵循明确路径
# 条件边允许图内动态决策
# 示例7-6展示了如果用户请求计算属性且提到可视化,任务会在Calculate Properties后转到Visualize节点,否则返回Manager节点
# 这种灵活性确保不同工作流可被灵活处理,无需死板的预定义步骤
# 根据请求,Manager节点通过条件边确定下一步动作
# 系统入口点设定在Manager,确保所有任务从中心位置开始,完成时发出FINISH信号退出

这套设计充分体现了基于LLM的多智能体协作和动态流程管理的理念,实现了灵活且模块化的生命科学计算工作流。

模块化设计使系统易于扩展和可伸缩,因为可以在不干扰整体流程的情况下,添加更多的代理或节点。例如,未来的更新可以包括一个节点或团队,用于模拟生化反应。

示例7-6. LangGraph 蛋白质团队

master_graph.add_edge("Generate Protein", "Manager")
master_graph.add_edge("Visualize", "Manager")
master_graph.add_edge("Run Utility", "Manager")
master_graph.add_edge("Run ColabFold", "Manager")
master_graph.add_edge("Run ThermoMPNN", "Manager")
master_graph.add_edge("DNA Calculations", "Manager")

master_graph.add_conditional_edges("Calculate Properties",
    lambda x: "Visualize" if "visualize" in x["messages"][-1].content.lower()
    else "Manager", {
        "Visualize": "Visualize", "Manager": "Manager",
    }
)
master_graph.add_conditional_edges("Manager", lambda x: x["next"], {
    "Generate Protein": "Generate Protein",
    "Run ColabFold": "Run ColabFold",
    "Calculate Properties": "Calculate Properties",
    "Visualize": "Visualize",
    "Run ThermoMPNN": "Run ThermoMPNN",
    "DNA Calculations": "DNA Calculations",
    "Run Utility": "Run Utility",
    "FINISH": END,
})
master_graph.set_entry_point("Manager")

通过实现这样的图系统,我们可以一次性提出多个相互关联的问题,比如评估蛋白质结构的热力学稳定性、识别热点和冷点、以及可视化热图和三维结构:

query = """analyze pdb:1PXV ThermoMPNN stability chain A, show both its heatmap
    and 3D visualization. Additionally, visualize the protein with chains A and C
    with Cold Spot Scanner"""

# 或者

query = """run ColabFold prediction for sequence
    MKTVRQERLKSIVRILERSKEPVSGAQLAEELSVSRQVIVQDIAYLRSLGYNIVATPRGYVLAGG and
    visualize the results"""

# 或者

query = """generate a DNA sequence, calculate the protein coding genes and
    visualize DNA token probabilities"""

# 或者

query = """generate 3 proteins and calculate their mutation impacts"""

# 然后

input_message = [HumanMessage(content=query)]
for s in workflow.stream({"messages": input_message, "recursion_limit": 5}):
    print(list(s.values())[0])
    print("----")

查询 “analyze pdb:1PXV ThermoMPNN stability chain A, show both its heatmap and 3D visualization. Additionally, visualize the protein with chains A and C with Cold Spot Scanner” 的结果展示如图7-4所示。

image.png

开发的应用程序可以帮助我们理解突变或环境因素如何影响蛋白质的折叠和功能。现在,我们能够在同一个应用中生成DNA序列和蛋白质,预测三维结构,计算相互作用,并创建可视化展示,从分子相互作用到结构动态都能提供深入洞察。

大型语言模型和推理模型的微调

第2章和第6章讨论了针对生命科学调优的语言模型通常比通用模型表现更好。通用模型虽然对各种领域都有所了解,但生物学包含很多特定术语、过程和规则,这些模型可能无法完全理解。调优后的模型通过专门在生物学文本和数据上训练,学会正确的术语和概念。因此,在处理蛋白质相互作用或遗传通路等复杂话题时,调优模型犯错更少,答案更有价值。调优模型还能更好地理解科学家的工作方式和思维过程。一个以生物学论文为训练数据的模型,会更擅长分析实验数据、提出研究方法建议以及辅助文献综述。

大型推理模型简介

大型推理模型(LRMs)是专门为推理设计的特殊大型语言模型,是AI中的新范式,强调推理时的计算能力。这使得LRMs能执行比传统LLMs更复杂的推理任务。传统LLMs主要专注于预测序列中的下一个词元,依赖快速的模式识别,而LRMs则预测序列中的下一个元素,不论这个元素是词元、逻辑步骤还是符号。关键差别在于模型的训练和微调方式。通过专门训练模型理解逻辑结构、逐步解决问题,以及显式表达推理过程,我们能引导一个下一个词元预测器生成代表推理过程的词元序列。

第2章指出,LLMs的训练依赖于海量的预训练数据集。许多先进的LRMs(如DeepSeek R1)是在现有LLMs(如DeepSeek V3)基础上微调而成,可能还加入了中间的强化学习步骤。LRMs采用链式思考(CoT)、思维树(ToT)、蒙特卡洛树搜索等技术,引导模型逐步思考问题,探索多条推理路径,从而做出更合乎逻辑的决策。基于强大的LLM基础,利用这些推理技术,LRMs在科学推理、数学问题解决及复杂决策任务上表现优于标准LLMs。图7-5展示了标准LLM推理、利用CoT等技术的LRM推理,以及更复杂的智能体方法如何处理信息并得出结论。

LRM与LLM的主要区别包括:

  • 推理机制
    LLMs基于统计概率生成文本,而LRMs通过链式思考提示、自我验证等推理策略迭代优化输出。
  • 计算与参数扩展
    传统LLMs通过增加参数量提升能力(例如GPT-4拥有万亿级参数),并利用专家混合(MoE)等条件计算技术。LRMs则在推理时优化决策过程,采用自适应计算,根据具体输入动态分配计算资源,从而提升性能和效率。
  • 知识的静态与动态
    LLMs知识固定于预训练时,LRMs能够动态更新推理路径,并利用符号引擎、领域数据库等外部工具辅助推理。
  • 成本与效率
    目前LRMs推理步骤多、计算开销大,每个查询通常需要处理数百万词元。早期版本如OpenAI的O1或DeepSeek R1即如此。但随着专用模型语言和更优推理技术的发展,效率有望快速提升。

image.png

注意
大型推理模型(LRMs)和由常规模型(LLMs)驱动的多智能体系统在解决任务的方式上有许多相似之处。但两者的方法有所不同:LRMs通常专注于增强单一强大LLM的能力,以完成复杂的推理任务。它们的设计重点是改进模型架构、训练数据和算法,使模型能够处理复杂的逻辑问题、规划和决策。而由LLMs驱动的多智能体系统则将问题解决过程分布到多个智能体中,每个智能体拥有自己的LLM。

LRMs常通过逐步推理、错误检测和自我纠正等技术来提升复杂任务的表现。一些LRMs还设计有输出验证机制,可能减少幻觉(hallucination),提高可靠性。许多LRM设计特别注重增强可解释性,使模型生成比部分标准LLM更透明的推理链条,从而使其推理过程更易审计和解释。此外,某些LRM架构能够更高效地整合最新数据或外部知识,无需对底层LLM进行全面重训练。

由于LRMs采用多步推理,其计算成本较高,响应时间可能较基本模型更慢。复杂的处理也会增加词元使用量,依据上下文窗口大小和推理过程长度,有时可能更容易超过上下文限制,如图7-6所示。此外,LRMs的开发和微调通常需要专门的框架和定制的强化学习流程以达到预期性能。需要强调的是,这些只是一般趋势,具体模型可能表现不同。例如,据报道DeepSeek系列模型在词元生成方面较为高效,因为每生成一个词元所用参数较少,响应速度可能快于某些其他LRMs。

image.png

由于其推理能力,大型推理模型(LRMs)在生物学、其他生命科学及医疗健康领域拥有广泛的潜在应用。LRMs可以综合分析海量生物医学数据集,从基因组数据、医学文献到临床试验等多种来源汲取洞见。DeepSeek R1在生物统计学和实验设计方面表现出色,假设检验的能力超过了标准LLMs。

LRMs能够精准建模分子相互作用、蛋白质折叠和化合物筛选,加快潜在药物候选分子的发现。结合知识图谱的图形化LRMs(第8章讨论了基于图的RAG)有助于预测药物-靶点相互作用。LRMs还可以通过模拟多种治疗策略,结合患者特定的遗传标记和病史,优化治疗方案。

我在第2章已经讨论过LLMs的主要微调概念。微调LLM和LRM遵循相同的基本原则:通过在较小的、针对特定任务的数据集上训练,调整预训练模型以适应特定任务或领域。根本区别在于它们的核心目标和训练方法。LLMs通常用指令跟随数据集进行微调,以提升对各类指令的理解和响应能力,进而增强在翻译、摘要等特定任务上的表现。此过程侧重于训练模型根据给定指令生成合适输出,而非仅限于特定领域适应。LRMs则专注于提升复杂推理和问题解决能力,因此所用训练数据注重包含详尽的推理过程和逐步解答,而非单纯的领域文本示例。

示例7-7展示了使用unsloth包以提升管理大规模模型效率的做法。我们将使用FastLanguageModel.from_pretrained微调DeepSeek模型——unsloth/DeepSeek-R1-Distill-Qwen-1.5B-unsloth-bnb-4bit。根据质量与计算成本的权衡,模型规模可在15亿到140亿参数间选择。2048词元的序列长度允许模型处理长篇推理任务,同时保持连贯性。

注意
选择1.5B模型的实际原因是:与更大模型相比,微调成本更低,便于试验。

微调过程需要结构化的提示(prompt),引导模型生成逻辑性强的回答。示例7-7中定义了一个自定义模板,指示模型逐步思考每个问题。模板包含指令、问题和回答的占位符,确保训练样本一致性。加入结束标记(EOS token)确保生成的回答适时终止。

示例7-7. 设置DeepSeek微调流水线

from datasets import Dataset, load_dataset
from unsloth import FastLanguageModel

max_seq_length = 2048
dtype = None
load_in_4bit = True
model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "unsloth/DeepSeek-R1-Distill-Qwen-1.5B-unsloth-bnb-4bit",
    max_seq_length = max_seq_length,
    dtype = dtype,
    load_in_4bit = load_in_4bit,
)

system_prompt = "You are a world expert in biology."

def formatting_prompts_func(example):
    inputs = example["question"]
    cots = [x["reasoning"] for x in example["metadata"]]
    outputs = example["answer"]
    texts = []
    ...
    <根据模型提示风格处理数据>
    ...
    return {"text": texts}

数据集通过加载moremilk/ToT-Biology中基于复杂推理的生物学问题集合准备,我们将其预处理成符合提示模板格式的样本。示例数据如下:

dataset = load_dataset("moremilk/ToT-Biology")

print(dataset['train'][0])
# 输出示例
{
    'answer': '细胞学说,生物学中的基础原理,指出:\n\n* 所有生物体都由一个或多个细胞组成。\n* 细胞是生物体结构和组织的基本单位。\n* 细胞来源于已有细胞...',
    'id': 'qa_20250123_074422_850678',
    'metadata': {
        'difficulty': 3,
        'reasoning': '为有效回答该问题,我首先回顾了细胞学说的核心内容。了解这些原则帮助我围绕它们组织答案。我从细胞学说的三大要点开始...',
        'topic': '细胞学说、生物学史、显微镜、科学发现'
    },
    'question': '描述细胞学说的起源,重点介绍关键贡献者及其导致该学说形成的观察。',
}

Hugging Face的SFTTrainer默认使用交叉熵损失函数进行LLM微调。该损失函数衡量预测词元概率与目标序列真实词元之间的差异,适用于模型预测序列下一个词元的任务。

注意
批量大小(batch size)、学习率和训练轮数等参数会影响模型从数据中学习的效果。合理的批量大小保证模型权重更新稳定且内存消耗适中;学习率决定模型适应新信息的速度,需要在快速学习和稳定收敛间权衡。

此外,我们可以利用量化(quantization)、低秩适配(LoRA)、参数高效微调(PEFT)等技术,将LLM压缩到更小内存空间,避免全面微调带来的巨大计算成本,同时高效适配特定任务。

量化(Quantization)是一种通过使用低精度数据类型(如4位或8位整数,int4、int8)来表示模型权重和激活,从而降低推理时计算和内存开销的技术,取代通常使用的32位浮点数(float32)。我们的模型采用了4位量化,这由模型名称末尾的“4bit”后缀表示。

参数高效微调(PEFT)是一个更广泛的技术类别,包括LoRA、前缀调优(prefix tuning)、提示调优(prompt tuning)等方法,这些方法只修改模型参数的一小部分。LoRA通过向模型现有权重矩阵中添加小型可训练的低秩矩阵,形成一个紧凑且参数高效的“适配器”,在保持原模型权重冻结的同时进行训练。与完全微调相比,这种方法大幅降低了内存占用和计算需求,通常只需训练不到1%的参数,同时性能相当。在我们的示例中,将训练0.37%的全部参数,详细代码可见于LangChain4LifeSciencesHealthcare仓库。

LoRA的核心思想是,权重矩阵在适配过程中更新部分可以用远低于原始权重矩阵秩的矩阵近似表示,既能捕捉大部分任务特定信息,又能极大减少参数数量。通过对特定层(通常是Transformer模型中的注意力层)应用该方法,LoRA实现了大型模型在特定领域或任务上的高效专门化,无需为每个应用存储完整独立模型。

示例7-8中指定低秩分解矩阵的秩为16(r=16),决定了适配的压缩程度。目标模块包括Transformer结构中的注意力机制组件(q_proj、k_proj、v_proj、o_proj)和前馈网络组件(gate_proj、up_proj、down_proj),它们是模型行为适配最关键的层。参数lora_alpha=16控制LoRA更新的缩放强度,决定了适配对原模型预测的影响力。其他配置包括lora_dropout=0(LoRA层无丢弃)、bias="none"(不适配偏置项),并启用带有unsloth选项的梯度检查点以优化长上下文窗口训练时的内存使用。该配置显式禁用秩稳定LoRA(use_rslora=False)和LoRA微调感知量化(LoFTQ,loftq_config=None),两者是基本LoRA方法的高级变体,针对特定场景提供额外优化。

示例7-8还涵盖了监督微调(SFT)过程,使用配置好LoRA的模型、相应的分词器和训练数据集初始化SFTTrainer。max_seq_length控制输入的最大词元长度,dataset_num_proc=2启用两个工作线程的并行数据处理,平衡效率和资源利用。TrainingArguments定义详细的训练循环参数:每设备批量大小8,梯度累积4步,有效虚拟批量大小32;训练共200步,包含10步预热期;学习率5e-5,采用线性学习率调度器和权重衰减0.01做正则化。

示例7-8. DeepSeek微调流水线训练示例

from trl import SFTTrainer
from transformers import TrainingArguments
from unsloth import is_bfloat16_supported

model = FastLanguageModel.get_peft_model(
    model, r=16,
    target_modules=[
        "q_proj","k_proj","v_proj","o_proj","gate_proj","up_proj","down_proj"
    ],
    lora_alpha=16, lora_dropout=0,
    bias="none", use_gradient_checkpointing="unsloth",
    random_state=2025, use_rslora=False, loftq_config=None,
)

trainer = SFTTrainer(
    model=model, tokenizer=tokenizer,
    train_dataset=dataset, dataset_text_field="text",
    max_seq_length=max_seq_length, dataset_num_proc=2,
    args=TrainingArguments(
        per_device_train_batch_size=8, gradient_accumulation_steps=4,
        warmup_steps=10, max_steps=200,
        learning_rate=5e-5, logging_steps=10,
        fp16=not is_bfloat16_supported(), bf16=is_bfloat16_supported(),
        optim="adamw_8bit", weight_decay=0.01,
        lr_scheduler_type="linear", seed=2025,
        output_dir="outputs", report_to="none"
    ),
)

示例7-9展示了微调过程,使用unsloth框架运行训练循环。模型经过多次迭代,不断调整权重以最小化误差。每次迭代更新模型内部结构,提升其生成逻辑回答的能力。训练过程中通过损失值监控进展,损失值下降(如图7-7所示)表明模型对数据集理解逐步提升。收敛标准决定训练停止时机。

image.png

继续训练可能导致过拟合,尤其是在验证损失不再显著下降时。此时,为避免不必要的计算,训练会被终止。微调后的模型会被保存以供后续使用,保留训练过程中取得的改进。确保训练在最佳时点停止,有助于防止过度训练并保持模型的泛化能力。微调完成后,模型会被重新加载用于推理测试。

示例7-9. 配置DeepSeek微调流水线

trainer_stats = trainer.train()
model.save_pretrained("bio-tuned-deepseek-r1")
tokenizer.save_pretrained("bio-tuned-deepseek-r1")

bio_model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = "bio-tuned-deepseek-r1",
    max_seq_length = max_seq_length,
    dtype = dtype, load_in_4bit = load_in_4bit,
)

def generate_text(question, _model, max_length=2048):
    FastLanguageModel.for_inference(_model)  # Unsloth推理速度提升2倍!
    ...
    <对问题进行分词处理>
    ...
    outputs = model.generate(
        input_ids=inputs,
        max_new_tokens=max_length,
        temperature=0.6,  # DeepSeek推荐参数
    )
    response = tokenizer.batch_decode(outputs)
    return response

接下来我们将对比微调模型与基础模型的表现,以了解性能提升。通过基准测试比较两者在推理准确性和连贯性上的差异,侧面对比回答内容,便能直观感受到微调效果,验证训练过程的有效性。理解这些提升能够证明微调所付出的努力是值得的。

提示
还可以通过进一步的提示工程技术进行优化,尝试不同的表述风格和结构调整,观察对模型回答的影响。

以下为基础模型和微调模型在几个问题上的表现对比(更多示例可见LangChain4LifeSciencesHealthcare仓库):

  • 如果一群老鼠的毛色基因型有AA、Aa和aa三种,且A完全显性于a,群体中会出现三种不同的毛色表现型吗?

  • 在一个池塘生态系统中,若有100个生产者(藻类),且每个营养级只传递10%的能量,最多能支持多少三级消费者(三级捕食者)?

  • 在携带编码氨苄青霉素抗性和GFP表达基因质粒的10^6个细菌中,若:

    • 质粒丢失率为每次细胞分裂0.1%,
    • 携带质粒的细胞生长速度比不携带者慢20%,
    • 无抗生素选择压力,
      最少需要多少代才能使99.9%的细胞丢失质粒?
  • 不同类型干细胞在细胞衰老中扮演什么具体角色?有哪些干细胞疗法正被探索用于治疗骨关节炎或黄斑变性?

  • 详细描述肝细胞中尿素循环的步骤,说明特定酶如何将有毒氨转化为尿素以供排泄。

  • 描述至少三种轮作不同作物(如豆类、谷物)如何促进土壤健康并减少合成肥料使用。

这些问题均需推理能力。以第一个问题为例,模型需识别完全显性意味着A存在时会掩盖a,还要核查每种基因型对应表现型:

  • AA → 显性表现型
  • Aa → 同AA,显性表现型(A掩盖a)
  • aa → 隐性表现型

因此,表现型只有两种,不是三种。示例7-10展示了两个模型对该问题的回答。

示例7-10. 基础模型与微调模型针对老鼠毛色表现型问题的回答

基础模型回答

鼠群中存在基因型AA、Aa和aa。由于A完全显性于a,毛色表现型由显性等位基因A决定。因此,毛色表现型为:
- AA:表现型1(显性)
- Aa:表现型1(显性)
- aa:表现型2(隐性)

最终答案:群体中存在3种不同的毛色表现型。

微调模型回答

等等,这里因为A完全显性,任何含A的基因型表现型都相同。所以AA和Aa表现型相同,都是显性A;aa表现型是隐性a。

因此,群体中只有两种表现型。

所以,用户问是否有三种表现型,实际上答案是否定的,只有两种。

两个模型给出了不同答案,但均体现了逻辑推理。我们的微调模型正确识别了显性等位基因A掩盖隐性等位基因a的遗传规律,准确推断出只有两种表现型,避免了逻辑错误,显示出对遗传学基础知识的良好理解。

为了更好地理解两个模型的推理能力,不同查询的结果汇总在表7-1中。除了答案质量参数,我们还包括推理稳定性——即推理过程中是否陷入循环,以及答案完整性——是否给出了完整答案,或者是否因词元限制等问题导致答案未能生成。

问题类别模型正确性推理稳定性答案完整性
遗传学:老鼠毛色表现型基础模型错误——与完全显性矛盾稳定——推理简短且线性给出明确结论
微调模型正确——判定为两种表现型不稳定——冗长重复循环最终给出答案
生态学:池塘三级消费者基础模型错误——混淆能量与生物数量大量循环与过度思考无明确结论
微调模型不完整——结论前被截断更加聚焦无答案(被截断)
微生物学:细菌质粒丢失基础模型误解质粒动力学陷入重复循环无答案
微调模型不完整——结论前被截断轻微循环(文本不完整)可见文本中无答案
干细胞生物学:干细胞与衰老基础模型低质量——包含错误细节句式大量重复技术性回答(但内容浅显)
微调模型部分正确但含不准确内容无循环检测不完整(句中截断)
生物化学:尿素循环基础模型提及不存在的酶是——陷入重复循环部分尝试但错误
微调模型编造生化反应无循环结构化但内容错误
农业生物学:轮作益处基础模型未能提供正确原则在水分需求上严重循环无具体答案
微调模型展示趋向正确的意图有条理,无循环检测开始时提供有用背景

基础模型与微调模型的对比显示性能存在显著差异。基础模型在所有问题中普遍准确率低,常常给出错误信息、捏造不存在的过程,且未能得出有效结论。虽然偶尔会给出答案,但通常浅显、包含捏造内容或与已知科学原理相悖。基础模型最大弱点是易陷入推理循环,反复表达相同想法或问题,无法推进解决。

提示
可以通过更长时间和更大规模数据集对更大模型进行微调,这些都会提升生成回答的质量。

微调模型推理稳定性有所提升,循环现象减少,问题解决方式更有条理,且具备更好的自我纠正能力。但调优模型仍在内容准确性方面存在困难,回答中混杂部分正确与明显错误的信息,例如捏造的生化反应和错误的细胞关系(完整回答见LangChain4LifeSciencesHealthcare仓库)。评估调优模型的一大限制是其多条回答在推理过程中被截断,导致难以全面评估其性能,尤其是答案完整度方面。

综合考虑模型规模,我们仍可得出结论:无论基础模型还是调优模型,均未在所有三项评估标准上表现出持续优异。调优模型表现出更好的推理结构、减少循环行为及内容准确性的良好迹象。两者都在理解分子机制和定量推理方面面临最大挑战。两者最成功的回答均针对概念简单、类别明确的问题。

注意
像本案例这样使用非零温度参数比较模型并不完全准确。如果温度设为零,我们将观察仅最高概率词元的概率变化情况。

总结

本章探讨了LangChain在生物学领域的应用,详细阐述了生成式人工智能如何正在革新生物学研究。展示了最初为自然语言处理设计的大型语言模型(LLMs)如何被广泛应用于基因组学、转录组学、蛋白质组学和单细胞分析等多个生物学领域,专门化模型为生物学理解做出了重要贡献。

本章详细演示了如何构建一个生物学LangGraph超级应用,该应用协调多个AI智能体,包括用于蛋白质结构预测的ColabFold团队、用于蛋白质稳定性分析的ThermoMPNN团队,以及执行DNA序列生成、蛋白质生成和蛋白质属性计算等任务的多个专门智能体。该应用展示了如何创建动态工作流,根据用户查询灵活调整,帮助研究人员在同一界面无缝完成蛋白质结构分析、热力学稳定性评估和结果可视化。

章节最后探讨了针对生物学应用的高级推理模型(LRMs)的微调方法。对比了LRMs与传统LLMs,强调LRMs在推理时加大计算力度以完成更复杂的推理任务。展示了如何利用低秩适配(LoRA)和参数高效微调(PEFT)等技术对DeepSeek R1模型进行微调。基础模型与微调模型的比较分析显示了推理稳定性和问题解决方法的提升。

下一章将深入介绍变分自编码器(VAE)及其如何基于设定属性生成分子。同时还将探讨知识图谱,设计基于知识图谱的RAG应用,用于药物发现和药物再利用。