Bootstrap

构建LangChain应用程序的示例代码:61、如何使用 LangChain 和 LangSmith 优化链

本示例介绍如何使用 LangChain 和 LangSmith 优化链。

设置

我们将为 LangSmith 设置环境变量,并加载相关数据

import os

os.environ["LANGCHAIN_PROJECT"] = "movie-qa"
# 设置 LANGCHAIN_PROJECT 环境变量为 "movie-qa"
import pandas as pd
# 导入 pandas 库
df = pd.read_csv("data/imdb_top_1000.csv")
# 从 CSV 文件中读取 IMDB 前 1000 部电影的数据
df["Released_Year"] = df["Released_Year"].astype(int, errors="ignore")
# 将 "Released_Year" 列转换为整数类型,忽略错误

创建初始检索链

我们将使用自查询检索器

from langchain.schema import Document
from langchain_community.vectorstores import Chroma
from langchain_openai import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()
# 导入必要的库并创建 OpenAIEmbeddings 对象
records = df.to_dict("records")
documents = [Document(page_content=d["Overview"], metadata=d) for d in records]
# 将数据框转换为字典列表,并创建 Document 对象列表
vectorstore = Chroma.from_documents(documents, embeddings)
# 使用文档和嵌入创建 Chroma 向量存储
from langchain.chains.query_constructor.base import AttributeInfo
from langchain.retrievers.self_query.base import SelfQueryRetriever
from langchain_openai import ChatOpenAI

metadata_field_info = [
    AttributeInfo(
        name="Released_Year",
        description="The year the movie was released",
        type="int",
    ),
    AttributeInfo(
        name="Series_Title",
        description="The title of the movie",
        type="str",
    ),
    AttributeInfo(
        name="Genre",
        description="The genre of the movie",
        type="string",
    ),
    AttributeInfo(
        name="IMDB_Rating", description="A 1-10 rating for the movie", type="float"
    ),
]
document_content_description = "Brief summary of a movie"
llm = ChatOpenAI(temperature=0)
retriever = SelfQueryRetriever.from_llm(
    llm, vectorstore, document_content_description, metadata_field_info, verbose=True
)
# 创建元数据字段信息列表,设置文档内容描述,创建 ChatOpenAI 对象,
# 并使用这些信息创建 SelfQueryRetriever 对象
from langchain_core.runnables import RunnablePassthrough
# 导入 RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
# 导入 StrOutputParser 和 ChatPromptTemplate
prompt = ChatPromptTemplate.from_template(
    """Answer the user's question based on the below information:

Information:

{info}

Question: {question}"""
)
generator = (prompt | ChatOpenAI() | StrOutputParser()).with_config(
    run_name="generator"
)
# 创建聊天提示模板和生成器
chain = (
    RunnablePassthrough.assign(info=(lambda x: x["question"]) | retriever) | generator
)
# 创建完整的链,包括检索和生成步骤

运行示例

通过链运行示例。这可以是手动的,也可以使用示例列表或生产流量

chain.invoke({"question": "what is a horror movie released in early 2000s"})
# 使用链调用一个示例问题

标注

现在,转到 LangSmith 并将这些示例标注为正确或不正确

创建数据集

我们现在可以从这些运行中创建数据集。

我们要做的是找到标记为正确的运行,然后从中获取子链。具体来说,是查询生成器子链和最终生成步骤

from langsmith import Client

client = Client()
# 创建 LangSmith 客户端
runs = list(
    client.list_runs(
        project_name="movie-qa",
        execution_order=1,
        filter="and(eq(feedback_key, 'correctness'), eq(feedback_score, 1))",
    )
)

len(runs)
# 获取标记为正确的运行列表并打印数量
gen_runs = []
query_runs = []
for r in runs:
    gen_runs.extend(
        list(
            client.list_runs(
                project_name="movie-qa",
                filter="eq(name, 'generator')",
                trace_id=r.trace_id,
            )
        )
    )
    query_runs.extend(
        list(
            client.list_runs(
                project_name="movie-qa",
                filter="eq(name, 'query_constructor')",
                trace_id=r.trace_id,
            )
        )
    )
# 从正确的运行中提取生成器运行和查询构造器运行
runs[0].inputs
# 打印第一个运行的输入
runs[0].outputs
# 打印第一个运行的输出
query_runs[0].inputs
# 打印第一个查询运行的输入
query_runs[0].outputs
# 打印第一个查询运行的输出
gen_runs[0].inputs
# 打印第一个生成运行的输入
gen_runs[0].outputs
# 打印第一个生成运行的输出

创建数据集

我们现在可以为查询生成和最终生成步骤创建数据集。
我们这样做是因为:(1) 我们可以检查数据点,(2) 如果需要,我们可以编辑它们,(3) 我们可以随时间添加到它们

client.create_dataset("movie-query_constructor")

inputs = [r.inputs for r in query_runs]
outputs = [r.outputs for r in query_runs]

client.create_examples(
    inputs=inputs, outputs=outputs, dataset_name="movie-query_constructor"
)
# 创建查询构造器数据集
client.create_dataset("movie-generator")

inputs = [r.inputs for r in gen_runs]
outputs = [r.outputs for r in gen_runs]

client.create_examples(inputs=inputs, outputs=outputs, dataset_name="movie-generator")
# 创建生成器数据集

使用少量示例

我们现在可以下载数据集并在未来的链中使用它们作为少量示例

examples = list(client.list_examples(dataset_name="movie-query_constructor"))
# 获取查询构造器数据集的示例列表
import json

def filter_to_string(_filter):
    if "operator" in _filter:
        args = [filter_to_string(f) for f in _filter["arguments"]]
        return f"{_filter['operator']}({','.join(args)})"
    else:
        comparator = _filter["comparator"]
        attribute = json.dumps(_filter["attribute"])
        value = json.dumps(_filter["value"])
        return f"{comparator}({attribute}, {value})"
# 定义一个函数将过滤器转换为字符串
model_examples = []

for e in examples:
    if "filter" in e.outputs["output"]:
        string_filter = filter_to_string(e.outputs["output"]["filter"])
    else:
        string_filter = "NO_FILTER"
    model_examples.append(
        (
            e.inputs["query"],
            {"query": e.outputs["output"]["query"], "filter": string_filter},
        )
    )
# 创建模型示例列表
retriever1 = SelfQueryRetriever.from_llm(
    llm,
    vectorstore,
    document_content_description,
    metadata_field_info,
    verbose=True,
    chain_kwargs={"examples": model_examples},
)
# 使用模型示例创建新的检索器
chain1 = (
    RunnablePassthrough.assign(info=(lambda x: x["question"]) | retriever1) | generator
)
# 创建新的链
chain1.invoke(
    {"question": "what are good action movies made before 2000 but after 1997?"}
)
# 使用新链调用一个示例问题

总结

本文档介绍了如何使用 LangChain 和 LangSmith 来优化问答链。主要步骤包括:

  1. 设置环境和加载数据
  2. 创建初始检索链
  3. 运行示例并进行标注
  4. 创建数据集
  5. 使用少量示例进行优化

扩展知识

  1. LangChain:是一个用于开发由语言模型驱动的应用程序的框架。它提供了一套工具和抽象,使得构建复杂的 AI 应用变得更加简单。

  2. LangSmith:是一个开发平台,用于构建、测试和监控基于 LLM 的应用程序。它提供了可视化和分析工具,帮助开发者优化他们的 LLM 应用。

  3. 自查询检索器(SelfQueryRetriever):这是一种高级检索器,能够理解自然语言查询并将其转换为结构化查询,以便从向量存储中检索相关文档。

  4. 少样本学习(Few-shot learning):这是一种机器学习技术,模型可以从很少的训练样本中学习执行新任务。在本文中,我们使用少量示例来改进查询构造和生成过程。

  5. 向量存储:这是一种特殊类型的数据库,专门用于存储和检索向量嵌入。在本文中,我们使用 Chroma 作为向量存储。

这些技术和工具共同工作,可以创建更智能、更高效的问答系统,特别是在处理特定领域(如电影信息)的查询时。

;