MLflow LangChain 风格

注意

langchain 风格正在积极开发中,并被标记为实验性。公共API可能会发生变化,随着风格的演进,可能会添加新功能。

欢迎阅读 LangChain 与 MLflow 集成的开发者指南。本指南旨在全面帮助理解并利用 LangChain 和 MLflow 在开发高级语言模型应用中的综合能力。

LangChain 是一个多功能框架,专为构建由语言模型驱动的应用程序而设计。它在创建利用语言模型进行推理和生成响应的上下文感知应用程序方面表现出色,从而能够开发出复杂的NLP应用程序。

LangGraph 是由 Langchain 的创建者提供的基于代理的补充框架,支持创建有状态的代理和多代理 GenAI 应用程序。LangGraph 利用 LangChain 来与 GenAI 代理组件进行接口。

为什么要将 MLflow 与 LangChain 一起使用?

除了使用 MLflow 管理与部署机器学习模型的优势外,LangChain 与 MLflow 的集成还提供了在更广泛的 MLflow 生态系统中使用 LangChain 的诸多好处。

实验跟踪

LangChain 在实验各种代理、工具和检索器时的灵活性,在与 MLflow Tracking 结合时变得更加强大。这种组合允许快速实验和迭代。你可以轻松比较运行结果,从而更容易优化模型并加速从开发到生产部署的进程。

依赖管理

借助 MLflow 自动 管理和记录所有外部依赖项 的能力,自信地部署您的 LangChain 应用程序。这确保了开发和生产环境之间的一致性,减少了手动干预带来的部署风险。

MLflow 评估

MLflow Evaluate 在 MLflow 中提供了评估语言模型的原生功能。通过此功能,您可以轻松地在 LangChain 应用程序的推理结果上利用自动化评估算法。此功能有助于高效评估 LangChain 应用程序的推理结果,确保强大的性能分析。

可观测性

MLflow Tracing 是 MLflow 的一个新功能,它允许你追踪数据如何流经你的 LangChain 链/代理等。此功能提供了数据流的视觉表示,使得更容易理解你的 LangChain 应用程序的行为并识别潜在的瓶颈或问题。凭借其强大的 自动追踪 能力,你可以在不更改代码的情况下,只需运行 mlflow.langchain.autolog() 命令一次,就能对你的 LangChain 应用程序进行检测。

自动记录

Autologging 是一个强大的一站式解决方案,只需一行代码 mlflow.langchain.autolog() 即可实现上述所有优势。通过启用 autologging,您可以自动记录 LangChain 应用程序的所有组件,包括链、代理和检索器,几乎不需要额外努力。此功能简化了跟踪和管理 LangChain 应用程序的过程,使您能够专注于开发和改进您的模型。有关如何使用此功能的更多信息,请参阅 MLflow LangChain Autologging 文档

MLflow LangChain 集成中支持的元素

警告

在记录依赖于 合作伙伴包 (如 langchain-openai)的链或代理时,存在一个已知的反序列化问题。如果您使用基于传统序列化的日志记录来记录此类模型,某些组件可能会从相应的 langchain-community 包中加载,而不是从合作伙伴包库中加载,这可能会在执行代码时导致意外行为或导入错误。为了避免此问题,我们强烈建议使用 代码生成的模型 方法来记录此类模型。此方法允许您绕过模型序列化并稳健地保存模型定义。

注意

包含 ChatOpenAIAzureChatOpenAI 的日志链/代理需要 MLflow>=2.12.0LangChain>=0.0.307

链、代理和检索器的概述

在代码中硬编码的动作或步骤序列。LangChain中的链结合了各种组件,如提示、模型和输出解析器,以创建处理步骤的流程。

下图展示了直接通过API调用与SaaS LLM接口的示例,顶部部分没有对话历史的上下文。底部部分展示了相同的查询提交给一个LangChain链,该链包含对话历史状态,使得整个对话历史都包含在每个后续输入中。以这种方式保留对话上下文是创建“聊天机器人”的关键。

对话历史的有状态存储对于聊天应用程序的重要性

开始使用 MLflow LangChain 风格 - 教程和指南

入门教程

在本入门教程中,您将学习 LangChain 的最基本组件,以及如何利用与 MLflow 的集成来存储、检索和使用链。

高级教程

在这些教程中,你可以学习到如何将 LangChain 与 MLflow 结合使用更复杂的用法。强烈建议在探索这些更高级用例之前,先阅读入门教程。

从代码中记录模型

自 MLflow 2.12.2 起,MLflow 引入了直接从代码定义记录 LangChain 模型的能力。

该功能为管理 LangChain 模型提供了几个好处:

  1. 避免序列化复杂性:文件句柄、套接字、外部连接、动态引用、lambda 函数和系统资源是不可序列化的。一些 LangChain 组件不支持原生序列化,例如 RunnableLambda

  2. 不支持序列化:在不同于序列化对象时使用的Python版本中加载pickle或cloudpickle文件不能保证兼容性。

  3. 可读性: 序列化的对象通常很难被人阅读。代码生成模型允许你通过代码审查你的模型定义。

有关此功能的更多信息,请参阅 从代码生成模型功能文档

为了使用此功能,您将使用 mlflow.models.set_model() API 来定义您希望记录为 MLflow 模型的链。在您的代码中设置此链后,当记录您的模型时,您将指定定义您的链的文件的 路径

以下示例展示了如何使用此方法记录一个简单的链:

  1. 在单独的Python文件中定义链。**

    小技巧

    如果你在使用 Jupyter Notebook,你可以使用 %%writefile 魔法命令将代码单元格直接写入文件,而无需离开笔记本手动创建它。

    %%writefile chain.py
    
    import os
    from operator import itemgetter
    
    from langchain_core.output_parsers import StrOutputParser
    from langchain_core.prompts import PromptTemplate
    from langchain_core.runnables import RunnableLambda
    from langchain_openai import OpenAI
    
    import mlflow
    
    mlflow.set_experiment("Homework Helper")
    
    mlflow.langchain.autolog()
    
    prompt = PromptTemplate(
        template="You are a helpful tutor that evaluates my homework assignments and provides suggestions on areas for me to study further."
        " Here is the question: {question} and my answer which I got wrong: {answer}",
        input_variables=["question", "answer"],
    )
    
    
    def get_question(input):
        default = "What is your name?"
        if isinstance(input_data[0], dict):
            return input_data[0].get("content").get("question", default)
        return default
    
    
    def get_answer(input):
        default = "My name is Bobo"
        if isinstance(input_data[0], dict):
            return input_data[0].get("content").get("answer", default)
        return default
    
    
    model = OpenAI(temperature=0.95)
    
    chain = (
        {
            "question": itemgetter("messages") | RunnableLambda(get_question),
            "answer": itemgetter("messages") | RunnableLambda(get_answer),
        }
        | prompt
        | model
        | StrOutputParser()
    )
    
    mlflow.models.set_model(chain)
    
  2. 然后从主笔记本中,通过提供定义链的文件路径来记录模型:

    from pprint import pprint
    
    import mlflow
    
    chain_path = "chain.py"
    
    with mlflow.start_run():
        info = mlflow.langchain.log_model(lc_model=chain_path, artifact_path="chain")
    
  3. chain.py 中定义的模型现在已记录到 MLflow。您可以重新加载模型并运行推理:

    # Load the model and run inference
    homework_chain = mlflow.langchain.load_model(model_uri=info.model_uri)
    
    exam_question = {
        "messages": [
            {
                "role": "user",
                "content": {
                    "question": "What is the primary function of control rods in a nuclear reactor?",
                    "answer": "To stir the primary coolant so that the neutrons are mixed well.",
                },
            },
        ]
    }
    
    response = homework_chain.invoke(exam_question)
    
    pprint(response)
    

    你可以在 MLflow UI 上看到模型被记录为代码:

    从代码脚本文件中记录一个 LangChain 模型

警告

当从代码中记录模型时,请确保您的代码不包含任何敏感信息,例如API密钥、密码或其他机密数据。代码将以纯文本形式存储在MLflow模型工件中,任何有权访问该工件的人都可以查看代码。

详细文档

要了解更多关于 MLflow LangChain 风格的细节,请阅读下面的详细指南。

View the Comprehensive Guide

常见问题解答

我无法加载我的链!

  • 允许危险反序列化:LangChain 中的 Pickle 选择加入逻辑将阻止通过 MLflow 加载组件。您可能会看到类似这样的错误:

    ValueError: This code relies on the pickle module. You will need to set allow_dangerous_deserialization=True if you want to opt-in to
    allow deserialization of data using pickle. Data can be compromised by a malicious actor if not handled properly to include a malicious
    payload that when deserialized with pickle can execute arbitrary code on your machine.
    

    LangChain 中的一项更改,强制用户选择加入 pickle 反序列化,可能会在使用 MLflow 记录的链、向量存储、检索器和代理加载时产生一些问题。由于该选项未在每个组件上公开以在加载器函数上设置此参数,因此您需要在记录模型时确保直接在定义的加载器函数中设置此选项。未设置此值的 LangChain 组件将保存时不会有问题,但在加载时如果未设置,将引发 ValueError

    要解决这个问题,只需重新登录您的模型,在定义的加载器函数中指定选项 allow_dangerous_deserialization=True。有关在 loader_fn 声明中记录 FAISS 向量存储实例时指定此选项的示例,请参阅 LangChain 检索器教程

我无法使用 MLflow 保存我的链、代理或检索器。

小技巧

如果你在记录或保存 LangChain 组件时遇到问题,请参阅 从代码创建模型 功能文档,以确定从脚本文件记录模型是否提供了一个更简单和更健壮的记录解决方案!

  • 使用 Cloudpickle 进行序列化的挑战: 使用 cloudpickle 进行序列化可能会遇到限制,这取决于对象的复杂性。

    一些对象,特别是那些具有复杂内部状态或依赖外部系统资源的对象,本质上是不能被pickle化的。这种限制的出现是因为序列化本质上需要将一个对象转换为字节流,这对于紧密耦合系统状态的对象或具有外部I/O操作的对象来说可能很复杂。尝试将PyDantic升级到2.x版本来解决这个问题。

  • 验证本地序列化支持:确保 langchain 对象(链、代理或检索器)在使用 langchain API 进行保存或日志记录时能够本地序列化,如果使用 MLflow 无法正常工作。

    由于其复杂的结构,并非所有 langchain 组件都能轻松序列化。如果原生序列化不受支持,且 MLflow 不支持保存模型,您可以在 LangChain 仓库 中提交问题,或在 LangChain 讨论板 中寻求指导。

  • 保持与MLflow新功能的同步:MLflow可能不会立即支持最新的LangChain功能。

    如果 MLflow 不支持某个新功能,请考虑 在 MLflow GitHub 问题页面上提交功能请求。由于处于活跃开发中的库(如 LangChain 的发布速度)变化迅速,可能会导致破坏性更改、API 重构以及对现有功能的基本功能支持出现问题。如果您希望在 LangChain 中看到对链、代理、检索器或任何未来结构的支持,请告知我们!

当我保存我的模型时,我遇到了一个 AttributeError

  • 处理 LangChain 和 MLflow 中的依赖安装:LangChain 和 MLflow 不会自动安装所有依赖项。

    对于特定代理、检索器或工具可能需要的其他包,在保存或记录模型时可能需要明确定义。如果您的模型依赖于这些外部组件库(特别是对于工具),而这些库不包含在标准的 LangChain 包中,这些依赖项将不会始终自动记录为模型的一部分(有关如何包含它们的指导,请参见下文)。

  • 声明额外依赖:在保存和记录时使用 extra_pip_requirements 参数。

    当保存或记录包含核心langchain安装之外的外部依赖项的模型时,您将需要这些额外的依赖项。模型风格包含两种声明这些依赖项的选项:extra_pip_requirementspip_requirements。虽然指定 pip_requirements 是完全有效的,但我们建议使用 extra_pip_requirements,因为它不需要定义使用langchain模型进行推理所需的所有核心依赖包(其他核心依赖项将自动推断)。

如何将流式API与LangChain一起使用?

  • 使用LangChain模型进行流式传输:确保LangChain模型支持流式响应,并使用MLflow版本>= 2.12.2。

    自 MLflow 2.12.2 版本起,支持流式响应的 LangChain 模型,如果使用 MLflow 2.12.2(或更高版本)保存,可以通过 predict_stream API 加载并用于流式推理。请确保正确消费返回类型,因为这些模型的返回是一个 Generator 对象。了解更多信息,请参阅 predict_stream 指南

如何将使用 LangGraph 构建的代理记录到 MLflow 中?

LangGraph 与 MLflow 的集成旨在利用 MLflow 中的 Models From Code 功能 来扩展和简化代理序列化的支持。

要记录一个 LangGraph 代理,您可以在脚本中定义您的代理代码,如下所示,保存到文件 langgraph.py 中:

from typing import Literal

from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent

import mlflow


@tool
def get_weather(city: Literal["seattle", "sf"]):
    """Use this to get weather information."""
    if city == "seattle":
        return "It's probably raining. Again."
    elif city == "sf":
        return "It's always sunny in sf"


llm = ChatOpenAI()
tools = [get_weather]
graph = create_react_agent(llm, tools)

# specify the Agent as the model interface to be loaded when executing the script
mlflow.models.set_model(graph)

当你准备好将这个代理脚本定义记录到 MLflow 时,你可以在定义模型时直接引用这个保存的脚本:

import mlflow

input_example = {
    "messages": [{"role": "user", "content": "what is the weather in seattle today?"}]
}

with mlflow.start_run():
    model_info = mlflow.langchain.log_model(
        lc_model="./langgraph.py",  # specify the path to the LangGraph agent script definition
        artifact_path="langgraph",
        input_example=input_example,
    )

当从MLflow加载代理时,脚本将被执行,定义的代理将可供调用使用。

代理可以按如下方式加载并用于推理:

agent = mlflow.langchain.load_model(model_info.model_uri)
query = {
    "messages": [
        {
            "role": "user",
            "content": "Should I bring an umbrella today when I go to work in San Francisco?",
        }
    ]
}
agent.invoke(query)