自动选择不同的大模型
在先前的文章中,我们学会了可以让 Agent 自动选择不同的工具来处理不同的问题。
在现实场景中,我们可能还会面临另外一种场景是,使用不同的大模型来处理用户的问题,
比如根据用户输入的不同问题选择使用 OpenAI 或者是本地部署的大模型。
RouterChain
为了解决这个问题,langchain 引入了 RouterChain
,它是一个可以自动选择不同大模型(实际上是 chain
)的工具。
比如我们有两个大模型,一个是 OpenAI 的 GPT-3.5,擅长解答关于 Python 的问题;另一个是 OpenAI 的 gpt-4
,擅长解答关于 Golang 的问题。
我们可以根据用户的输入来选择是使用 GPT-3.5 还是 GPT-4 来回答用户的问题。
比如:
- 输入:“python 如何写入文件”,那么选择的应该是 GPT-3.5。
- 输入:“Golang 中如何启动协程”,那么选择的应该是 GPT-4。
整体框架
RouterChain
,也叫路由链,能动态选择用于给定输入的下一个链。我们会根据用户的问题内容,首先使用路由链确定问题更适合哪个处理模板,
然后将问题发送到该处理模板进行回答。如果问题不适合任何已定义的处理模板,它会被发送到默认链。
在这里,我们会使用 LLMRouterChain
和 MultiPromptChain
(也是一种路由链)组合实现路由功能,
该 MultiPromptChain
会调用 LLMRouterChain
选择与给定问题最相关的提示,然后使用该提示回答问题。
具体步骤如下:
- 构建处理模板:为 “解答 python 问题” 和 “解答 Golang 问题” 分别构建一个目标链(
LLMChain
),并存储在一个字典中。 - 构建
LLM
路由链:这是决策的核心部分。首先,它根据提示信息构建了一个路由模板,然后使用这个模板创建了一个LLMRouterChain
。 - 构建默认链:如果输入不适合任何已定义的处理模板,这个默认链会被触发。
- 构建多提示链:使用
MultiPromptChain
将LLMRouterChain
和默认链组合在一起,形成一个完整的决策系统。
具体实现
先定义两个 LLMChain
下面是两个 LLMChain 的定义,一个是用于回答 Python 问题的,另一个是用于回答 Golang 问题的。
from langchain.chains.llm import LLMChain from langchain_community.llms import OpenAI from langchain_core.prompts import PromptTemplate # 创建一个 GPT-3.5 的 LLMChain def create_py_chain() -> LLMChain: prompt_template = """ 你是一名 Python 工程师,擅长解答关于 Python 编程的问题。 下面是需要你来回答的问题: {input} """ prompt = PromptTemplate( template=prompt_template, input_variables=["input"], ) llm = OpenAI() return LLMChain(llm=llm, prompt=prompt, verbose=True) # 创建一个 GPT-4 的 LLMChain def create_go_chain() -> LLMChain: prompt_template = """ 你是一名 Golang 工程师,擅长解答关于 Golang 编程的问题。 下面是需要你来回答的问题: {input} """ prompt = PromptTemplate( template=prompt_template, input_variables=["input"], ) llm = OpenAI() return LLMChain(llm=llm, prompt=prompt, verbose=True) # 创建两个 LLMChain chain_map = { "Python": create_py_chain(), "Golang": create_go_chain(), }
定义一个 RouterChain
RouterChain 是一个可以自动选择不同大模型(实际上是 chain
)的工具。
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE destinations = [ "Python: 适合回答关于 Python 编程的问题", "Golang: 适合回答关于 Golang 编程的问题", ] router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations="\n".join(destinations)) router_prompt = PromptTemplate( template=router_template, input_variables=["input"], output_parser=RouterOutputParser(), ) router_chain = LLMRouterChain.from_llm( llm=OpenAI(), prompt=router_prompt, verbose=True )
这其实是本文关键的地方,router_prompt
实际上是一个 Prompt
:
- 其中
input_variables
是输入的变量,这里只有一个input
。 output_parser
是输出解析器,这里使用了RouterOutputParser
。template
是一个模板,用于生成提示。
简而言之,这个 RouterChain
允许你将用户的输入送入路由器,然后路由器会决定将该输入发送到哪个具体的模型,或者是否需要对输入进行修订以获得最佳的响应。
定义一个默认 chain
如果输入不适合任何已定义的 chain,这个默认 chain 会被使用。
default_chain = ConversationChain(llm=OpenAI(), output_key="text", verbose=True)
定义一个 MultiPromptChain
MultiPromptChain
根据用户输入尝试选择一个 destination_chains
中的 chain 来处理问题。
如果没有找到合适的 chain,会使用 default_chain
来处理问题。
from langchain.chains.router import MultiPromptChain chain = MultiPromptChain( router_chain=router_chain, default_chain=default_chain, destination_chains=chain_map, verbose=True )
MultiPromptChain
有三个关键元素:
router_chain
(类型RouterChain
):这是用于决定目标链和其输入的链。当给定某个输入时,这个router_chain
决定哪一个destination_chain
会被使用,以及传给它的具体输入是什么。destination_chains
(类型Mapping[str, LLMChain]
):这是一个映射,将名称映射到可以将输入路由到的候选链。例如,你可能有多种处理文本数据的方法(或 “链”),每种方法针对特定类型的问题。default_chain
(类型LLMChain
):当router_chain
无法将输入映射到destination_chains
中的任何链时,LLMChain
将使用此默认链。
它的工作流程如下:
- 输入首先传递给
router_chain
。 router_chain
根据某些标准或逻辑决定应该使用哪一个destination_chain
。- 输入随后被路由到选定的
destination_chain
,该链进行处理并返回结果。 - 如果
router_chain
不能决定正确的destination_chain
,则输入将被传递到default_chain
。
这样,MultiPromptChain
就为我们提供了一个在多个处理链之间动态路由输入的机制,以得到最相关或最优的输出。
调用
下面是一个调用的例子:
print(chain.invoke({"input": "如何在 Python 中定义一个函数?"}))
这会使用 Python
的 LLMChain 来回答这个问题。
我们会看到有类似如下的输出:
Prompt after formatting: 你是一名 Python 工程师,擅长解答关于 Python 编程的问题。 下面是需要你来回答的问题: 如何在 Python 中定义一个函数?
原理
本质上,其实是在 RouterChain 中定义了一个 Prompt
,让 LLM 来判断分析使用哪一个 destination_chain
。
我们可以打印一下看看:
print(router_prompt.format(input="如何在 Python 中定义一个函数?"))
输出如下:
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 >> Python: 适合回答关于 Python 编程的问题 Golang: 适合回答关于 Golang 编程的问题 << INPUT >> 如何在 Python 中定义一个函数? << OUTPUT (must include ```json at the start of the response) >> << OUTPUT (must end with ```) >>
这个 Prompt
包含了如下内容:
- 先是一个简单的引导语句,告诉模型你将给它一个输入,它需要根据这个输入选择最适合的模型。
- 进一步提醒模型,它将获得各种模型提示的名称和描述。同时告诉模型,还有一个可选的步骤,它可以更改原始输入,以便最终获得更好的响应。
- 接下来是格式说明:指导模型如何格式化其输出,使其以特定的方式返回结果:表示模型的输出应该是一个 Markdown 格式,其中包含一个 JSON 对象。然后指定了 JSON 的格式。
- 额外的说明和要求:告诉模型,如果输入不适合任何候选提示,则应该返回
DEFAULT
。 - 候选提示:列出了所有可用的模型及其描述。
- 输入:给出了一个示例输入。
这个模板的目的是让模型知道如何处理用户的输入,并根据提供的提示列表选择一个最佳的模型提示来回应。
总结
RouterChain
是一个可以自动选择不同大模型(实际上是 chain
)的工具,可以根据用户的输入来选择使用不同的大模型来回答用户的问题。