03LangChain核心:“链”(实战课9-10)| 豆包MarsCode AI刷题

153 阅读8分钟

在现代应用中,特别是基于大语言模型(LLM)的智能系统,任务复杂性日益增加。单一的模型调用和提示生成方法已经无法满足复杂场景的需求。LangChain 提供了 “链” 的概念,以模块化和流程化的方式组织模型操作。这种方式让开发者可以更高效地处理多步骤任务。

以下是本文将要探索的链的类型和功能:

  1. LLMChain:将提示、模型调用和输出解析整合成单一链。
  2. SequentialChain:将多个链组合成串行的流程。
  3. RouterChain:动态分配任务,解决多任务场景的问题。

通过这些链的使用,开发者可以快速构建更智能、更复杂的应用程序。


什么是 Chain?

Chain 是 LangChain 的核心概念之一,它通过链接多个组件(例如提示模板、语言模型、输出解析器等),将复杂的任务分解并组织成模块化的链条。这个设计思路简单但功能强大,它具备以下优点:

  1. 简化复杂任务:将多步骤操作封装成单一的调用链。
  2. 模块化设计:便于调试、维护和扩展应用程序。
  3. 多功能组合:支持模型间的链接或与其他工具的结合。

LangChain 提供了多种预置链,帮助开发者快速构建各种类型的应用程序。


LLMChain:最简单的链

LLMChain 是 LangChain 中最常用的链之一,它将提示模板、语言模型以及输出解析器整合在一起,封装了 Model I/O 的整个流程。

使用链前的传统写法

如果不使用链,生成“鲜花花语”的代码可能如下:

# 创建提示模板
from langchain import PromptTemplate
template = "{flower}的花语是?"
prompt_temp = PromptTemplate.from_template(template)
prompt = prompt_temp.format(flower='玫瑰')

# 调用语言模型
from langchain import OpenAI
model = OpenAI(temperature=0)
result = model(prompt)
print(result)

输出

玫瑰的花语是?
爱情、浪漫、美丽、永恒、誓言、坚贞不渝。

这种写法虽然清晰,但提示模板的构建与模型调用是分开的。对于更复杂的任务,代码可能变得冗长且难以管理。


使用 LLMChain

使用 LLMChain,我们可以将上述逻辑封装到一个链中:

from langchain import PromptTemplate, OpenAI, LLMChain

template = "{flower}的花语是?"
llm = OpenAI(temperature=0)

llm_chain = LLMChain(
    llm=llm,
    prompt=PromptTemplate.from_template(template)
)

result = llm_chain({"flower": "玫瑰"})
print(result)

输出

{'flower': '玫瑰', 'text': '\n\n爱情、浪漫、美丽、永恒、誓言、坚贞不渝。'}

通过 LLMChain,我们将提示模板的构建和模型调用封装在一起,简化了代码结构。


链的调用方式

1. 直接调用

直接调用链对象时,实际上执行的是链内部的 __call__ 方法。例如:

llm_chain({"flower": "玫瑰"})

2. 使用 run 方法

run 方法等价于直接调用 __call__ 方法:

llm_chain.run("玫瑰")

3. 使用 predict 方法

predict 方法允许通过关键字参数传递输入变量,而非字典:

result = llm_chain.predict(flower="玫瑰")
print(result)

4. 使用 apply 方法

apply 方法支持批量处理输入数据:

input_list = [
    {"flower": "玫瑰"},
    {"flower": "百合"},
    {"flower": "郁金香"}
]
result = llm_chain.apply(input_list)
print(result)

输出

[{'text': '爱情、浪漫、美丽。'}, {'text': '纯洁、高雅、友谊。'}, {'text': '完美、胜利。'}]

5. 使用 generate 方法

generate 方法返回一个包含额外信息的 LLMResult 对象,例如令牌使用量:

result = llm_chain.generate(input_list)
print(result)

SequentialChain:串联多个链

SequentialChain 是一种顺序链,用于将多个链按照特定顺序组合在一起。

场景示例:生成鲜花文案

我们希望通过三个步骤生成一篇鲜花运营文案:

  1. 植物学家生成鲜花的知识性说明。
  2. 鲜花评论家基于说明撰写评论。
  3. 社交媒体经理根据评论撰写运营文案。

实现步骤

  1. 创建第一个链:生成鲜花的知识性说明。
llm = OpenAI(temperature=0.7)
template = """
你是一个植物学家。给定花的名称和颜色,你需要为这种花写一个200字左右的介绍。

花名: {name}
颜色: {color}
植物学家: 这是关于上述花的介绍:"""
prompt_template = PromptTemplate(input_variables=["name", "color"], template=template)
introduction_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="introduction")
  1. 创建第二个链:基于说明生成评论。
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。

鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate(input_variables=["introduction"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")
  1. 创建第三个链:根据评论生成社交媒体文案。
template = """
你是一家花店的社交媒体经理。给定一种花的介绍和评论,你需要为这种花写一篇社交媒体的帖子,300字左右。

鲜花介绍:
{introduction}
花评人对上述花的评论:
{review}

社交媒体帖子:
"""
prompt_template = PromptTemplate(input_variables=["introduction", "review"], template=template)
social_post_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="social_post_text")
  1. 将三个链组合成顺序链
from langchain.chains import SequentialChain

overall_chain = SequentialChain(
    chains=[introduction_chain, review_chain, social_post_chain],
    input_variables=["name", "color"],
    output_variables=["introduction", "review", "social_post_text"],
    verbose=True
)

result = overall_chain({"name": "玫瑰", "color": "黑色"})
print(result)

输出

{'name': '玫瑰', 'color': '黑色', 
 'introduction': '黑色玫瑰是一种...',
 'review': '黑色玫瑰不仅是一种花...',
 'social_post_text': '欢迎来到我们的平台...'}

RouterChain:动态任务分配的智能链

RouterChain 是 LangChain 中的高级链类型,专注于动态任务分配。它通过分析用户输入,将问题自动路由到最合适的处理链,尤其适合多任务、多场景的应用。


为什么需要 RouterChain?

在多任务场景中,直接使用单一链或顺序链无法满足需求。例如,一个鲜花运营 ChatBot 面临以下两种典型任务:

  1. 鲜花护理:如“如何为玫瑰浇水?”
  2. 鲜花装饰:如“如何为婚礼场地布置鲜花?”

传统方法需要手动判断任务类型并选择合适的链,而 RouterChain 可以通过大语言模型的能力动态完成任务分类,自动路由到对应的链。


RouterChain 的核心概念

RouterChain 的工作流程包含以下三个步骤:

  1. 任务分类:分析用户输入并判断其任务类型。
  2. 路由任务:将输入发送到合适的目标链。
  3. 默认处理:当输入无法分类时,调用默认链进行处理。

RouterChain 的实现步骤

我们以一个鲜花运营 ChatBot 为例,展示如何使用 RouterChain 实现动态任务分配。


1. 定义场景的处理模板

为每种任务类型定义独立的提示模板。

# 定义鲜花护理场景的模板
flower_care_template = """你是一个经验丰富的园丁,擅长解答关于养花育花的问题。
下面是需要你来回答的问题:
{input}"""

# 定义鲜花装饰场景的模板
flower_deco_template = """你是一位网红插花大师,擅长解答关于鲜花装饰的问题。
下面是需要你来回答的问题:
{input}"""

# 提示信息
prompt_infos = [
    {
        "key": "flower_care",
        "description": "适合回答关于鲜花护理的问题",
        "template": flower_care_template,
    },
    {
        "key": "flower_decoration",
        "description": "适合回答关于鲜花装饰的问题",
        "template": flower_deco_template,
    }
]

2. 构建目标链

使用 LLMChain 为每种场景创建独立的目标链。

from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
import os

# 设置 API Key
os.environ["OPENAI_API_KEY"] = '你的OpenAI Key'
llm = OpenAI()

# 构建目标链
chain_map = {}
for info in prompt_infos:
    prompt = PromptTemplate(template=info['template'], input_variables=["input"])
    chain = LLMChain(llm=llm, prompt=prompt, verbose=True)
    chain_map[info["key"]] = chain

3. 构建路由链

使用 LLMRouterChain 实现输入分析和任务分类。

from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE as RouterTemplate

# 构建路由模板
destinations = [f"{p['key']}: {p['description']}" for p in prompt_infos]
router_template = RouterTemplate.format(destinations="\n".join(destinations))

router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser(),
)

# 创建路由链
router_chain = LLMRouterChain.from_llm(llm, router_prompt, verbose=True)

路由链根据用户输入分析任务类型,并动态选择目标链。


4. 构建默认链

定义一个默认链,处理无法分类的问题。

from langchain.chains import ConversationChain

# 构建默认链
default_chain = ConversationChain(llm=llm, output_key="text", verbose=True)

5. 组合所有链

使用 MultiPromptChain 将路由链、目标链和默认链整合成一个完整的系统。

from langchain.chains.router import MultiPromptChain

# 构建多提示链
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=chain_map,
    default_chain=default_chain,
    verbose=True
)

测试 RouterChain 的功能

我们分别测试鲜花护理问题、鲜花装饰问题以及无关问题。

测试 1:鲜花护理问题

print(chain.run("如何为玫瑰浇水?"))

输出

目标链:鲜花护理
回答:根据天气情况和土壤湿度,每次浇水约200ml,避免浇水过多导致根系腐烂。

测试 2:鲜花装饰问题

print(chain.run("如何为婚礼场地装饰花朵?"))

输出

目标链:鲜花装饰
回答:可以选择白色玫瑰搭配满天星,以突出婚礼的浪漫氛围。

测试 3:无关问题

print(chain.run("如何考入哈佛大学?"))

输出

目标链:默认链
回答:抱歉,我无法回答这个问题,但我建议你可以参考哈佛大学官网的申请指南。