跳到主要内容

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 跟踪结合使用时会变得更加强大。这种组合允许快速实验和迭代。您可以轻松比较运行,从而更轻松地优化模型并加速从开发到生产部署的过程。

依赖项管理

放心地部署您的 LangChain 应用程序,利用 MLflow 的能力来自动管理和记录代码和环境依赖项。您还可以显式声明外部资源依赖项,例如 LLM 服务端点或 LangChain 应用程序查询的向量搜索索引。这些依赖项由 MLflow 作为模型元数据进行跟踪,以便下游服务系统可以确保从已部署的 LangChain 应用程序到这些依赖资源的身份验证能够正常工作。

这些功能可确保开发环境和生产环境之间的一致性,从而减少部署风险,并减少手动干预。

MLflow 评估

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

可观察性

MLflow 跟踪是 MLflow 的一项新功能,可让您跟踪数据如何在 LangChain 链/代理/等中流动。此功能提供了数据流的可视化表示,使您可以更轻松地了解 LangChain 应用程序的行为并识别潜在的瓶颈或问题。凭借其强大的自动跟踪功能,您可以检测 LangChain 应用程序,而无需任何代码更改,只需运行一次 mlflow.langchain.autolog() 命令即可。

自动日志记录

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

MLflow LangChain 集成中支持的元素

警告

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

信息

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

链、代理和检索器概述

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

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

The importance of stateful storage of conversation history for chat applications

MLflow LangChain 风格入门 - 教程和指南

入门教程

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

高级教程

在这些教程中,您可以了解 LangChain 与 MLflow 的更复杂用法。强烈建议在探索这些更高级的用例之前通读入门教程。

来自代码的日志记录模型

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

该功能提供了多种管理 LangChain 模型的好处

  1. 避免序列化复杂性:文件句柄、套接字、外部连接、动态引用、lambda 函数和系统资源无法进行 pickle 操作。某些 LangChain 组件不支持本机序列化,例如 RunnableLambda

  2. 无 Pickle 操作:在与用于序列化对象的 Python 版本不同的 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, name="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 上看到该模型已记录为代码

    Logging a LangChain model from a code script file

警告

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

详细文档

要了解有关 MLflow LangChain 风格的详细信息,请阅读下面的详细指南。

常见问题

我无法加载我的链!

  • 允许危险的反序列化: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 声明中指定此选项的示例,请参阅 LangChain 检索器的教程,了解在 loader_fn 声明中指定此选项的示例。

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

提示

如果您在使用 MLflow 记录或保存 LangChain 组件时遇到问题,请参阅来自代码的模型功能文档,以确定从脚本文件记录模型是否提供了更简单、更强大的日志记录解决方案!

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

    某些对象,尤其是那些具有复杂内部状态或依赖于外部系统资源的对象,本质上是无法 pickle 操作的。出现此限制的原因是序列化本质上需要将对象转换为字节流,这对于与系统状态紧密集成的对象或具有外部 I/O 操作的对象来说可能很复杂。尝试将 PyDantic 升级到 2.x 版本以解决此问题。

  • 验证本机序列化支持:如果使用 MLflow 保存或记录不起作用,请确保 langchain 对象(链、代理或检索器)可使用 langchain API 进行本机序列化。

    由于其复杂的结构,并非所有 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 版本,可以使用 predict_stream API 加载和用于可流式推理使用 MLflow 2.12.2(或更高版本)保存的 LangChain 模型,这些模型支持流式响应。确保您正确使用返回类型,因为这些模型返回的是 Generator 对象。要了解更多信息,请参阅 predict_stream 指南

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

LangGraph 与 MLflow 的集成旨在利用 MLflow 中的来自代码的模型功能来扩展和简化代理序列化的支持。

要记录 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
name="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)

如何在 PyFunc 预测中控制是否将输入转换为 List[langchain.schema.BaseMessage]?

默认情况下,MLflow 会将聊天请求格式输入 {"messages": [{"role": "user", "content": "some_question"}]} 转换为 List[langchain.schema.BaseMessage],如 [HumanMessage(content="some_question")],用于某些模型类型。要强制转换,请将环境变量 MLFLOW_CONVERT_MESSAGES_DICT_FOR_LANGCHAIN 设置为 True。要禁用此行为,请将环境变量 MLFLOW_CONVERT_MESSAGES_DICT_FOR_LANGCHAIN 设置为 False,如下所示

import json
import mlflow
import os
from operator import itemgetter
from langchain.schema.runnable import RunnablePassthrough

model = RunnablePassthrough.assign(
problem=lambda x: x["messages"][-1]["content"]
) | itemgetter("problem")

input_example = {
"messages": [
{
"role": "user",
"content": "Hello",
}
]
}
# this model accepts the input_example
assert model.invoke(input_example) == "Hello"

# set this environment variable to avoid input conversion
os.environ["MLFLOW_CONVERT_MESSAGES_DICT_FOR_LANGCHAIN"] = "false"
with mlflow.start_run():
model_info = mlflow.langchain.log_model(
model, name="model", input_example=input_example
)

pyfunc_model = mlflow.pyfunc.load_model(model_info.model_uri)
assert pyfunc_model.predict(input_example) == ["Hello"]