Chain的基础功能与实践

141 阅读9分钟

链的实现和使用:

  • 通过LangChain实现一个链,链可以接受输入,使用PromptTemplate对其进行格式化,然后将格式化的响应传递给LLM。
  • 实现链的具体功能后,可以通过将多个链组合在一起,或者将链与其他组件组合起来构建更复杂的链。

image.png

  1. LLMChain

    最简单最基本的链。对输入格式化、传入模型、等待LLM响应,同时解析输出。

  2. SequentialChain(顺序链)

    当想要获取一个调用的输出并将其用作另一个调用的输入时,顺序链就特别有用。

    其中每个步骤都有一个单一的输入/输出,并且一个步骤的输出是下一步的输入。

  3. TransformChain(转换链)4

    通过设置转换函数,对输入文本进行一系列的格式转换。

    例如,可以用它就收一个很长的衔环相关的文档,给文档分割成句子,仅仅保留前面N句,以满足LLM的令牌数量的限制。

  4. RouterChain(路由链)

    包含条件判断和一系列的目标链。通过调用LLM,路由链能动态判断条件,以确定调用后续哪一个目标Chain

  5. 其他工具链

一、LLMChain——最基础的链

LLMChain整合了PromptTemplate、语言模型和Output Parser,相当于把Model I/O放在一个链中整体操作。它使用提示模板格式化输入,将格式化的字符传递给LLM,并返回LLM输出。

import os
from langchain import PromptTemplate,OpenAI,LLMChain
template = "{flower}的花语是什么?"

os.environ["OPENAI_API_KEY"] = 'sk-mduGNGc05FuHrfR5URGnAY9Ir9Rbho24186WfLBfKvNUYioS'
LLM = OpenAI(temperature=0,base_url='<https://api.openai-proxy.org/v1>')

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

result = llm_chain("玫瑰")
print(result)
from langchain import PromptTemplate,OpenAI,LLMChain
prompt = PromptTemplate(
    input_variables = ["flower","scene"],
    template = "{flower}在{scene}场景下的花语是?"
)
llm_chain = LLMChain(llm=LLM,prompt = prompt)
print(llm_chain({
    'flower':'玫瑰',
    'scene':'商务'
}))

几个小方法

llm_chain({'flower':"玫瑰",'scene':'采矿'}) # 等价于llm_chain.run()

# predict方法类似于run,知识输入键北指定为关键字参数而不受Python字典
result = llm_chain.predict(flower="玫瑰",scene="爱情")
print(result)

# apply方法,可以一次处理多个输入
input_list = [
    {"flower": "玫瑰",'scene': "夏季"},
    {"flower": "百合",'scene': "春季"},
    {"flower": "郁金香",'scene': "秋季"}
]

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

# generate方法类似于apply,只不过它返回一个LLMResult对象,而不是字符串.里面有许多生成信息
result = llm_chain.generate(input_list)
print(result)

二、 顺序链

import os
os.environ["OPENAI_API_KEY"] = 'sk-mduGNGc05FuHrfR5URGnAY9Ir9Rbho24186WfLBfKvNUYioS'
LLM = OpenAI(temperature=0,base_url='<https://api.openai-proxy.org/v1>')

from langchain.llms import OpenAI
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains import SequentialChain
#%%
# 添加第一个llmchain
template = """
你是一个植物学家。给定花的名称和类型,你需要为这种话协议额200字左右的介绍

花名:{name}
颜色:{color}
植物学家:这是关于上述花的介绍:
"""
prompt_template = PromptTemplate(input_variables=["name","color"],template=template)
introduction_chain = LLMChain(llm=LLM,prompt=prompt_template,output_key="introduction")
#%%
# 添加第二个chain
LLM = OpenAI(temperature=0.7,base_url='<https://api.openai-proxy.org/v1>')
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。

鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate(input_variables=["introduction"],template=template)
review_chain = LLMChain(llm=LLM,prompt=prompt_template,output_key="review")
#%%
# 第三个Chain
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")
#%%
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)

链——下

首先,还是先看一下今天要完成一个什么样的任务。

这里假设咱们的鲜花运营智能客服ChatBot通常会接到两大类问题。

  1. 鲜花养护(保持花的健康、如何浇水、施肥等)
  2. 鲜花装饰(如何搭配花、如何装饰场地等)

需求是,如果接到的是第一类问题,你要给ChatBot A指示;如果接到第二类的问题,你要给ChatBot B指示

整体框架

RouterChain,也叫路由链,能动态选择用于给定输入的下一个链。我们会根据用户的问题内容,首先使用路由器链确定问题更适合哪个处理模板,然后将问题发送到该处理模板进行回答。如果问题不适合任何已定义的处理模板,它会被发送到默认链。

在这里,我们会用LLMRouterChain和MultiPromptChain(也是一种路由链)组合实现路由功能,该MultiPromptChain会调用LLMRouterChain选择与给定问题最相关的提示,然后使用该提示回答问题。

具体步骤如下:

  1. 构建处理模板:为鲜花护理和鲜花装饰分别定义两个字符串模板
  2. 提示信息:使用一个列表来组织和存储这两个处理模板的关键信息
  3. 初始化语言模型
  4. 构建目标链:根据提示信息种的每个模板构建了对应的LLMChain,并存储在一个字典中
  5. 构建LLM路由链:首先,它根据提示信息构建了一个路由模板,然后使用这个模板创建了一个LLMRouterChain
  6. 构建默认链:如果输入不符合任何已定义的处理模板,则这个默认链会被触发
  7. 构建多提示链:使用MultiPromptChain将LLM路由链、目标链和默认链组合在一起

路由模板

# 构建路由链
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))
print("路由模板:\n",router_template)
router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser(),)
print("路由提示:\n",router_prompt)
router_chain = LLMRouterChain.from_llm(llm, 
                                       router_prompt,
                                       verbose=True)

输出结果:

路由模板:
 Given a raw text input to a language model select the model prompt best suited for the input. You will be given the names of the available prompts and a description of what the prompt is best suited for. You may also revise the original input if you think that revising it will ultimately lead to a better response from the language model.

<< FORMATTING >>
Return a markdown code snippet with a JSON object formatted to look like:
```json
{{
    "destination": string \ name of the prompt to use or "DEFAULT"
    "next_inputs": string \ a potentially modified version of the original input
}}

REMEMBER: "destination" MUST be one of the candidate prompt names specified below OR it can be "DEFAULT" if the input is not well suited for any of the candidate prompts. REMEMBER: "next_inputs" can just be the original input if you don't think any modifications are needed.

<< CANDIDATE PROMPTS >> flower_care: 适合回答关于鲜花护理的问题 flower_decoration: 适合回答关于鲜花装饰的问题

<< INPUT >> {input}

<< OUTPUT (must include json at the start of the response) >> << OUTPUT (must end with ) >>

路由提示: input_variables=['input'] output_parser=RouterOutputParser() template='Given a raw text input to a language model select the model prompt best suited for the input. You will be given the names of the available prompts and a description of what the prompt is best suited for. You may also revise the original input if you think that revising it will ultimately lead to a better response from the language model.\n\n<< FORMATTING >>\nReturn a markdown code snippet with a JSON object formatted to look like:\njson\n{{\n "destination": string \\ name of the prompt to use or "DEFAULT"\n "next_inputs": string \\ a potentially modified version of the original input\n}}\n\n\nREMEMBER: "destination" MUST be one of the candidate prompt names specified below OR it can be "DEFAULT" if the input is not well suited for any of the candidate prompts.\nREMEMBER: "next_inputs" can just be the original input if you don't think any modifications are needed.\n\n<< CANDIDATE PROMPTS >>\nflower_care: 适合回答关于鲜花护理的问题\nflower_decoration: 适合回答关于鲜花装饰的问题\n\n<< INPUT >>\n{input}\n\n<< OUTPUT (must include json at the start of the response) >>\n<< OUTPUT (must end with ) >>\n'


组成:

1. 引言

```python
Given a raw text input to a language model select the model prompt best suited for the input.

这是一个简单的引导语句,告诉模型你将给它一个输入,它需要根据这个输入选择最适合的模型提示。

  • 格式说明<< FORMATTING >>

指导模型如何格式化其输出,使其以特定的方式返回结果。

Return a markdown code snippet with a JSON object formatted to look like:
  • 额外的说明和要求
REMEMBER: "destination" MUST be one of the candidate prompt names specified below OR it can be "DEFAULT"...
  • 输入/输出部分

    << INPUT >>\n{input}\n\n<< OUTPUT >>\n:
    
  1. 路由提示的解释

    路由提示 (router_prompt)则根据路由模板,生成了具体传递给LLM的路由提示信息。

    • 其中input_variables 指定模板接收的输入变量名,这里只有 "input"
    • output_parser 是一个用于解析模型输出的对象,它有一个默认的目的地和一个指向下一输入的键。
    • template 是实际的路由模板,用于给模型提供指示。这就是刚才详细解释的模板内容。
    • template_format 指定模板的格式,这里是 "f-string"
    • validate_template 是一个布尔值,如果为 True,则会在使用模板前验证其有效性。

    简而言之,这个构造允许你将用户的原始输入送入路由器,然后路由器会决定将该输入发送到哪个具体的模型提示,或者是否需要对输入进行修订以获得最佳的响应。

构建默认链

除了处理目标链和路由链之外,我们还需要准备一个默认链。如果路由链没有找到适合的链,那么,就以默认链进行处理。

# 构建默认链
from langchain.chains import ConversationChain
default_chain = ConversationChain(llm=llm, 
                                  output_key="text",
                                  verbose=True)

构建多提示链

最后,我们使用MultiPromptChain类把前几个链整合在一起,实现路由功能。这个MultiPromptChain类是一个多路选择链,它使用一个LLM路由器链在多个提示之间进行选择。

MultiPromptChain中有三个关键元素。

  • router_chain(类型RouterChain):这是用于决定目标链和其输入的链。当给定某个输入时,这个router_chain决定哪一个destination_chain应该被选中,以及传给它的具体输入是什么。
  • destination_chains(类型Mapping[str, LLMChain]):这是一个映射,将名称映射到可以将输入路由到的候选链。例如,你可能有多种处理文本输入的方法(或“链”),每种方法针对特定类型的问题。destination_chains可以是这样一个字典:{'weather': weather_chain, 'news': news_chain}。在这里,weather_chain可能专门处理与天气相关的问题,而news_chain处理与新闻相关的问题。
  • default_chain(类型LLMChain):当 router_chain 无法将输入映射到destination_chains中的任何一个链时,LLMChain 将使用此默认链。这是一个备选方案,确保即使路由器不能决定正确的链,也总有一个链可以处理输入。

它的工作流程如下:

  1. 输入首先传递给router_chain。
  2. router_chain根据某些标准或逻辑决定应该使用哪一个destination_chain。
  3. 输入随后被路由到选定的destination_chain,该链进行处理并返回结果。
  4. 如果router_chain不能决定正确的destination_chain,则输入会被传递给default_chain。