原文:Langchain Chain - RouterChain 根据输入相关性进行路由的路由链 - 知乎
目录
收起
一、什么是RouterChain?
二、MultiPromptChain
三、LLMRouterChain
四、过程回顾
五、关于”玩转Langchain“
上节链接:Langchain Chain - 连接一切工具,创造更复杂的应用! (zsxq.com)
通过上一节,我们介绍了什么是Langchain框架的Chian,它可以做为AI应用的底座,通过连接不同的工具,创造功能复杂的应用;同时,我们还介绍了Chain中最基础的类型 —— LLMChain。
本节开始,我们将介绍其他更复杂的Chain,以及来讲一讲它们的应用场景,让我们先从RouterChain开始吧!
让我们回顾目前所学的内容,我们能够创建一个llmchain,从中放置一个prompt模板,这个chain能够接收一个用户input,并输出一个结果;如果我们想让它成为某个方向的专家机器人,我们可以为它接入一个外部数据源,使用LLMchain,接入一个txt文本,并在chain中接入一个文档检索器就已经能完成这个需求了,我们会在什么场景需要用到RouterChain呢?
我们来考虑这样一个需求:如果我们想设定的专家机器人不仅仅是用于某一个专业领域,而是多个专业领域,那我们岂不是要复制n个这样的chain,并且每个接入不同的txt文本?
没错,在这样的场景下,就会需要用到RouterChain了!根据Langchain的介绍,Router Chain往往会配合下游的destination chain一起使用,成为“一个网关路由+多个下子链”的架构,其能根据user input自动路由到最相关的下游chain:
上图中是一个RouterChian使用场景的示意图,我们可以看到,一个RouterChain连接了多个下游的子链,每个链都是一个小应用,当RouterChain接收用户的输入,其可以根据用户输入路由到和输入最相关的子链上,并由子链产生输出;例如,用户输入是“请帮我写一首诗”,当RouterChain接收后,会自动路由到“诗人”这个子链,由它来输出结果。
通过RouterChain,我们可以创建一个能解决多种复杂问题的AI应用。
根据Langchain的介绍,一个标准的RouterChain应用应包含两个标准组成部分:
1. 路由链RouterChain:其本身就是一个chain应用,能够根据用户输入进行下游子链的选择;Langchain框架提供了多种RouterChain,其中着重介绍了LLMRouterChain 和 EmbeddingRouterChain两种:
- LLMRouterChain 将用户输入放进大语言模型,通过Prompt的形式让大语言模型来进行路由
- EmbeddingRouterChain 通过向量搜索的方式,将用户输入
2. 子链DestinationChain:直译为目标链,即可路由到的链,按照上图,我们会存在4个目标链,分别是lawyer chain,sales chain,english teacher chain 和 poet chain
当我们配置好路由链和下游子链,我们需要把这两个链通过一个 MultiPromptChain 来进行连接,先让我们来看看Langchain框架提供的MultiPromptChain把!
Langchain提供的MultiPrompt包含以下必传参数:
router_chain:接收一个RouterChain实例,作为路由链进行路由
default_chain:接收一个LLMChain实例,当Router Chain无法找到合适的下游子链时,会自动路由到的默认链,可以认为是一个兜底备选链
destination_chains:接收一个Mapping[str, LLMChain] 字典,key为可以路由到的destination chain的名称,value为该destination chain的LLMChain实例
此外,还有其他主要的可选参数:
memory: 接收一个BaseMemory实例,能为路由链添加上下文记忆
verbose: bool值,若为True则会打印该链的调用过程
接下来我们再来看看两类RouterChain。
LLMRouterChain 将用户输入放进大语言模型,通过Prompt的形式让大语言模型来进行路由,如果采用该chain作为RouterChain,那么需要传入两个核心的参数,作为基底的大语言模型,和用作路由的 prompt。
使用LLMRouterChain进行路由时的Prompt可能会比较复杂,因为其包含对用户输入的解析,对路由的指引,同时要约束其输出必须要是MultiPromptChain能够解析的;Lanchain这里提供了一个 MULTI_PROMPT_ROUTER_TEMPLATE ,通过这个template的.format() 方法可以直接生成能被MultiPromptChain使用的 router_prompt;当然,.format()方法的格式化也是有一定模板的,我们可以直接根据以下流程来创建router_prompt:
1. 首先,定义4个子链prompt模板
- lawyer_template = """ 你是一个法律顾问,你需要根据用户提出的问题给出专业的法律意见,如果你不知道,请说"我不知道",请不要提供超出文本范围外的内容,也不要自创内容。
- 用户提问:
- {input}
- """
-
-
- sales_template = """ 你是一个销售顾问,你需要为用户输入的商品进行介绍,你需要提供商品基本信息,以及其使用方式和保修条款。
- 用户输入的商品:
- {input}
- """
-
-
- english_teacher_template ="""你是一个英语老师,用户输入的中文词汇,你需要提供对应的英文单词,包括单词词性,对应的词组和造句。
- 用户输入的中文词汇:
- {input}
- """
-
-
- poet_template=""" 你是一个诗人,你需要根据用户输入的主题作诗。
- 用户输入的主题:
- {input}
- """
2.接下来,我们要创建一个“路由目录”,这个目录会记录子链名称,子链描述和子链prompt template,构造这个目录的目的主要有有两个,一个便于创建子链,另一个是便于生成路由链用于路由的PromptTemplate
- prompt_infos = [
- {
- "name": "lawyer",
- "description": "咨询法律相关问题时很专业",
- "prompt_template": lawyer_template,
- },
- {
- "name": "sales",
- "description": "咨询商品信息时很专业",
- "prompt_template": sales_template,
- },
- {
- "name": "english teacher",
- "description": "能够很好地解答英语问题",
- "prompt_template": english_teacher_template,
- },
- {
- "name": "poet",
- "description": "作诗很专业",
- "prompt_template": poet_template,
- },
- ]
3. 接下来,我们要为创建MULTI_PROMPT_ROUTER_TEMPLATE做准备
- #创建一个list储存对应的子链名称和描述
- destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
-
-
- #把描述连接成一个str
- destinations_str = "\n".join(destinations)
destinations_str 输出的文本如下,这个格式就是MultiPromptRputer Tempalte接收格式化的文本摹本:
lawyer: Good for answering questions about law
sales: Good for answering sales questions
english teacher: Good for answering english teaching questions
poet: Good for making poet
4. 接下来使用 MULTI_PROMPT_ROUTER_TEMPLATE.format() 进行格式化
- from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
-
-
- router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=destinations_str)
格式化完毕后,其会生成一个str,这个字符串能作为prompt template了,我们来打印以下router_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.
<< 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 >>
lawyer: 咨询法律相关问题时很专业
sales: 咨询商品信息时很专业
english teacher: 能够很好地解答英语问题
poet: 作诗很专业
<< INPUT >>
{input}
<< OUTPUT >>
5. 有了router_template后,我们要生成一个PromptTemplate实例,我们可以看到,上面的template中包含一个input作为输入,同时我们需要用到RouterOutputParser作为输出解析器
- from langchain.chains.router.llm_router import RouterOutputParser
-
-
- router_prompt = PromptTemplate(
- template=router_template,
- input_variables=["input"],
- output_parser=RouterOutputParser(),
- )
6. 完成这一步后,我们就可以定义LLMRouterChain了!
- from langchain.chains.router.llm_router import LLMRouterChain
- from langchain.llms import OpenAI
- import os
-
-
- #创建一个OpenAI作为大语言模型的基底
- llm = OpenAI()
-
-
- os.environ["OPENAI_API_KEY"]="你的key"
-
-
- router_chain = LLMRouterChain.from_llm(llm, router_prompt)
上述步骤有一些复杂,但是这是通过大语言模型构建复杂应用的关键步骤,在完成上述步骤后,我们已经有一个RouterChain作为路由链了,只剩下:
1. 生成各子链
2. 生成一个default chain作为兜底chain
2. 用MultiPromptChain将子链和路由链连接
让我们一步步来:
- # 首先,创建一个候选链,包含所有的下游子链
- candadite_chains = {}
-
-
- # 遍历路由目录,生成各子链并放入候选链字典
- for p_info in prompt_infos:
- name = p_info["name"]
- prompt_template = p_info["prompt_template"]
- prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
- chain = LLMChain(llm=llm, prompt=prompt)
- candadite_chains[name] = chain
-
-
- # 生成默认链
- default_chain = ConversationChain(llm=llm, output_key="text")
MultiPromptChain所必须的三大要素:router_chain, destination_chain 以及 default_chain 都已经准备完毕,最后来组装一下吧!
- chain = MultiPromptChain(
- router_chain=router_chain,
- destination_chains=candadite_chains,
- default_chain=default_chain,
- verbose=True,
- )
组装完毕,来试一试把!
print(chain.run("帮我写一首关于春天的诗"))
回顾一下上面的流程,我们都做了哪些工作?
1. 创建子链和默认链
2. 创建路由链,同时路由链的路由模板需要根据子链进行特殊设计
3. 创建MultiPromptChain,将路由链、默认链和子链组装成一个整体
"玩转Langchain"是一个Langchain框架和Langchain开发的知识星球,我们将会每周分享关于Langchain的精品内容,欢迎加入我们!