MLflow LangChain Flavor
langchain
flavor 正在积极开发中,并标记为实验性。公共 API 可能会更改,并且随着 flavor 的发展可能会添加新功能。
欢迎阅读 LangChain 与 MLflow 集成的开发人员指南。本指南将作为理解和利用 LangChain 与 MLflow 组合功能开发高级语言模型应用程序的综合资源。
LangChain 是一个通用框架,专为构建由语言模型驱动的应用程序而设计。它擅长创建上下文感知应用程序,利用语言模型进行推理和生成响应,从而实现复杂的 NLP 应用程序的开发。
LangGraph 是 Langchain 创建者提供的一个补充的基于代理的框架,支持创建有状态代理和多代理 GenAI 应用程序。LangGraph 利用 LangChain 接口与 GenAI 代理组件。
为什么将 MLflow 与 LangChain 结合使用?
除了使用 MLflow 管理和部署机器学习模型的好处之外,LangChain 与 MLflow 的集成还提供了许多与在更广泛的 MLflow 生态系统中使用 LangChain 相关的好处。
实验跟踪
LangChain 在试验各种代理、工具和检索器方面的灵活性与 MLflow Tracking 结合使用时变得更加强大。这种组合允许快速实验和迭代。您可以轻松比较运行,从而更容易地改进模型并加速从开发到生产部署的旅程。
依赖管理
利用 MLflow 自动管理和记录代码和环境依赖关系的能力,自信地部署您的 LangChain 应用程序。您还可以明确声明外部资源依赖关系,例如 LLM 服务端点或 LangChain 应用程序查询的向量搜索索引。MLflow 将这些依赖关系作为模型元数据进行跟踪,以便下游服务系统可以确保您的已部署 LangChain 应用程序到这些依赖资源的身份验证正常工作。
这些功能确保了开发和生产环境之间的一致性,减少了部署风险并减少了手动干预。
MLflow Evaluate
MLflow Evaluate 在 MLflow 中提供了评估语言模型的原生功能。通过此功能,您可以轻松地对 LangChain 应用程序的推理结果使用自动化评估算法。此功能有助于有效评估 LangChain 应用程序的推理结果,确保稳健的性能分析。
可观测性
MLflow Tracing 是 MLflow 的一项新功能,它允许您跟踪数据如何通过 LangChain 链/代理等。此功能提供了数据流的可视化表示,使得更容易理解 LangChain 应用程序的行为并识别潜在的瓶颈或问题。凭借其强大的自动跟踪功能,您只需运行一次 mlflow.langchain.autolog()
命令即可在不更改任何代码的情况下检测您的 LangChain 应用程序。
自动日志记录
自动日志记录是一个强大的“一站式”解决方案,只需一行代码 mlflow.langchain.autolog()
即可实现上述所有优点。通过启用自动日志记录,您可以轻松地自动记录 LangChain 应用程序的所有组件,包括链、代理和检索器。此功能简化了跟踪和管理 LangChain 应用程序的过程,让您可以专注于开发和改进模型。有关如何使用此功能的更多信息,请参阅 MLflow LangChain 自动日志记录文档。
MLflow LangChain 集成中支持的元素
- 代理
- 检索器
- 可运行对象
- LangGraph 编译图(仅通过代码中的模型支持)
- LLMChain(已弃用,仅支持
langchain<0.3.0
) - RetrievalQA(已弃用,仅支持
langchain<0.3.0
)
记录包含 ChatOpenAI 和 AzureChatOpenAI 的链/代理需要 MLflow>=2.12.0
和 LangChain>=0.0.307
。
链、代理和检索器概述
- 链
- 代理
- 检索器
在代码中硬编码的动作或步骤序列。LangChain 中的链结合了各种组件,如提示、模型和输出解析器,以创建处理步骤流。
下图显示了一个直接通过 API 调用与 SaaS LLM 交互的示例,其中没有上下文对话历史(上部)。下部显示了向 LangChain 链提交相同的查询,该链包含对话历史状态,以便将整个对话历史包含在每个后续输入中。以这种方式保留对话上下文是创建“聊天机器人”的关键。
使用语言模型选择动作序列的动态构造。与链不同,代理根据输入、可用工具和中间结果决定动作顺序。
RetrievalQA 链中负责获取相关文档或数据的组件。检索器在 LLM 需要引用特定外部信息以获得准确响应的应用程序中至关重要。
MLflow LangChain Flavor 入门 - 教程和指南
入门教程
在本入门教程中,您将学习 LangChain 最基本的组件,以及如何利用与 MLflow 的集成来存储、检索和使用链。
高级教程
在这些教程中,您可以了解 LangChain 与 MLflow 的更复杂用法。强烈建议在探索这些更高级的用例之前阅读入门教程。
从代码中记录模型
自 MLflow 2.12.2 版本以来,MLflow 引入了直接从代码定义中记录 LangChain 模型的能力。
该功能为管理 LangChain 模型提供了多项好处
-
避免序列化复杂性:文件句柄、套接字、外部连接、动态引用、lambda 函数和系统资源是不可 pickle 化的。某些 LangChain 组件不支持原生序列化,例如
RunnableLambda
。 -
无需 Pickling:在与用于序列化对象所使用的 Python 版本不同的 Python 版本中加载 pickle 或 cloudpickle 文件不能保证兼容性。
-
可读性:序列化的对象通常难以被人阅读。代码中的模型允许您通过代码审查模型定义。
有关此功能的更多信息,请参阅代码中的模型功能文档。
要使用此功能,您将使用 mlflow.models.set_model()
API 来定义您想要作为 MLflow 模型记录的链。在您的代码中设置此项后,在记录模型时,您将指定定义链的文件的路径。
以下示例演示了如何使用此方法记录一个简单的链
-
在单独的 Python 文件中定义链
提示如果您使用的是 Jupyter Notebook,可以使用
%%writefile
magic 命令直接将代码单元格写入文件,而无需离开 Notebook 手动创建它。# %%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) -
然后从主笔记本中,通过提供定义链的文件的路径来记录模型
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") -
在
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 上看到模型作为代码记录
从代码记录模型时,请确保您的代码不包含任何敏感信息,例如 API 密钥、密码或其他机密数据。代码将以纯文本形式存储在 MLflow 模型工件中,任何有权访问该工件的人都将能够查看代码。
详细文档
要了解有关 MLflow LangChain flavor 的更多详细信息,请阅读下面的详细指南。
常见问题
我无法加载我的链!
-
允许危险的反序列化:LangChain 中的 Pickle opt-in 逻辑将阻止通过 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 组件与 MLflow 时遇到问题,请参阅代码中的模型功能文档,以确定从脚本文件记录模型是否提供了更简单、更强大的日志记录解决方案!
-
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 安装的外部依赖项的模型时,您将需要这些额外的依赖项。模型 flavor 包含两个选项来声明这些依赖项:
extra_pip_requirements
和pip_requirements
。虽然指定pip_requirements
完全有效,但我们建议使用extra_pip_requirements
,因为它不依赖于定义使用 langchain 模型进行推理所需的所有核心依赖包(其他核心依赖项将自动推断)。
如何将流式 API 与 LangChain 一起使用?
-
使用 LangChain 模型进行流式传输:确保 LangChain 模型支持流式响应并使用 MLflow 版本 >= 2.12.2。
截至 MLflow 2.12.2 版本,支持流式响应并使用 MLflow 2.12.2(或更高版本)保存的 LangChain 模型可以使用
predict_stream
API 加载并用于流式推理。请确保您正确地使用返回类型,因为这些模型返回的是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 predict 中控制我的输入是否转换为 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"]