内置Chain组件的使用与解读

Chain简介与使用

Chain基本介绍

在许多编程语言和库中,Chain 通常用来描述一系列的操作或函数,这些操作或函数按照特定的顺序依次执行,前一个操作的输出会作为后一个操作的输入。这种模式也被称为管道(Pipeline)或链式调用(Chain Calling)。

而在 LangChain 库中,也有 Chain 的概念,用于在复杂场景下,将 LLM 组件、提示词模板、向量存储、记忆、输出解析器等多个组件联起来一起使用,在 LCEL 表达式出现之前,LangChain 就为 Chain 设计了相应的接口,并且为不同场景设计封装了大量 Chain 组件。

在 LangChain 中,存在两种类型的链:

  1. [推荐] 使用 LCEL 构建的链(顺序可执行链);
  2. [遗产] 通过 Chain 类子类构建的链,这些链不使用 LCEL,而是独立的类。

例如下方是 0.1.0 版本之前的 Chain 基类的定义:

class Chain(BaseModel, ABC):
    """基础接口定义,所有任务Chain都会实现这些接口"""

    memory: BaseMemory
    callbacks: callbacks

    def __call__(
        self,
        inputs: Any,
        return_only_outputs: bool = False,
        callbacks: callbacks = None,
    ) -> Dict[str, Any]:
        ...

而在 0.1.0 版本之后,Chain 基类也继承了 RunnableSerializable,同样也是一个可运行组件,在使用上了 LCEL 表达式构建的链一模一样,屏蔽了使用的差异,不过在后续的更新中可能会做破坏性的调整。

利用 Chain 类构建链应用

LLMChain 是 LangChain 的最基本 Chain 实现,这个 Chain 封装的任务是:使用用户输入参数格式化提示模板,然后将提示词传递给大语言模型,接下来从大语言模型中获取响应。

基础示例如下:

import dotenv
from langchain.chains.llm import LLMChain
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()

llm = ChatOpenAI(model="gpt-3.5-turbo-16k")
prompt = ChatPromptTemplate.from_template("请讲一个关于{subject}的冷笑话")

chain = LLMChain(
    llm=llm,
    prompt=prompt,
)

而基于 Chain 类构建的链,不涵盖 LCEL 提供的运行方式,Chain 类拥有运行方式有高达 5 种!极大提升了 Chain 使用的复杂性!

print(chain("程序员"))
print(chain.run("程序员"))
print(chain.apply([{"subject": "程序员"}]))
print(chain.generate([{"subject": "程序员"}]))
print(chain.predict(subject="程序员"))

一个简单的记忆方法,目前Chain类也是Runnable的子类,所有可以直接使用Runnable提供的通用运行方法

print(chain.invoke({"subject":"程序员"}))

内置的Chain

虽然目前推荐使用 LCEL 表达式的方式来创建链应用,传统链会从 0.3.0 开始逐渐淘汰,但是在 LangChain 封装的 Chain 组件中,仍然有绝大部分是基于传统链,有不小的参考价值。

LangChain 内置 Chain 组件文档:https://python.langchain.com/v0.1/docs/modules/chains/

LCEL Chain

在 LangChain 中,封装的所有 LCEL 链,都是通过函数的方式去调用创建的,通过传递特定的函数,即可快速创建定义好的链应用。

例如 create_stuff_documents_chain 函数会创建一个文档对话链,可以将文档列表作为参数传递给 Prompt,而不需要额外的处理步骤,使用示例如下:

import dotenv
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()

prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个强大的聊天机器人,能根据对应的上下文信息回复用户问题。\n\n<context>{context}</context>"),
    ("human", "{query}"),
])

llm = ChatOpenAI(model="gpt-3.5-turbo-16k")

chain = create_stuff_documents_chain(llm=llm, prompt=prompt)

documents = [
    Document(page_content="小明喜欢绿色,但不喜欢黄色"),
    Document(page_content="小王喜欢粉色,也有一点喜欢红色"),
    Document(page_content="小泽喜欢蓝色,但更喜欢青色")
]

resp = chain.invoke({"query": "大家都喜欢什么颜色", "context": documents})
print(resp)

输出内容:

根据提供的信息:
小明喜欢绿色,不喜欢黄色。
小王喜欢粉色,也有一点喜欢红色。
小泽喜欢蓝色,但更喜欢青色。
可以总结每个人的喜好:
小明:绿色
小王:粉色、红色(稍微)
小泽:青色、蓝色
所以,小明喜欢绿色,小王喜欢粉色和红色(少许),小泽喜欢青色和蓝色。

遗产Chain

在 LangChain 中,使用 Chain 类封装的链,被官方文档称为遗产链,在未来的版本会被逐渐抛弃,和 LCEL 不同,Chain 类封装的链绝大部分都是以实例化 Chain 类的方式来实现的。

例如 ConversationChain 就是一个对话链,通过传递对应的提示模板、大语言模型、记忆组件、记忆键信息等内容即可快速创建一个对话链,使用示例:

import dotenv
from langchain.chains.conversation.base import ConversationChain
from langchain_openai import ChatOpenAI

dotenv.load_dotenv()

chain = ConversationChain(llm=ChatOpenAI(model="gpt-4o"))

content = chain.invoke({"input": "你好,我是慕小课,我喜欢打球+游泳,你喜欢什么呢?"})
print(content)

content = chain.invoke({"input": "请统计一下我的爱好有什么"})
print(content)

输出:

{
    'input': '你好,我是慕小课,我喜欢打球+游泳,你喜欢什么呢?',
    'history': '',
    'response': '你好,慕小课!很高兴认识你。我是一个人工智能,所以我没有像人类那样的爱好。不过,如果我能有爱好的话,我觉得我会喜欢阅读和学习,因为我能通过处理大量的信息来帮助人们解答各种问题。不知道你平时都喜欢读什么书呢,或者有没有什么特别感兴趣的领域?'
}
Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐