回顾
AI菜鸟向前飞 — LangChain系列之六 - 深入浅出LCEL与Chain(上篇)
AI菜鸟向前飞 — LangChain系列之七 - 深入浅出LCEL与Chain(中篇)
AI菜鸟向前飞 — LangChain系列之八 - 深入浅出LCEL与Chain(下篇)
上一篇给大家把LangChain Expression Language的语法和原理给大家介绍完毕,这一篇给大家介绍一个比较有趣的“路由”(Router)chain,也算是比较常用,用下图来给大家讲解下,一图胜万言:)
简单来说,就是根据“用户输入”的信息,能够动态的选择交给“谁”去解决问题,在用户看来是个黑盒子。
带来的好处:
-
-
动态选择,免去了“用户输入"时,可能有一些专业术语没有加上,漏掉一些细节(如:一些小白用户常常踩的坑)—— 也是用Template帮大家提前加上
-
默认指定处理问题的Prompt(如:Prompt X),这样用户无需担心没人会给它处理问题了:)
-
如何使用
网上大部分资料,仅提供了一个例子(而且不一定每个人都能看得懂),我这里一股脑儿给大家提供四个,请君任选其一
By the way,在以下例子中,我用的是阿里千问LLM,因为我性能好的电脑在家没拿,没法在这台破电脑上本地运行Llama3,所以...
阿里千问还是很给力的:)
……
闲言少叙,Show me the code
方法一
程序
from langchain_community.llms.tongyi import Tongyi
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
from langchain.chains import ConversationChain
from langchain.chains.router import MultiPromptChain
# 不同的模版
template_dev = """
你是一名资深的开发专家,精通软件开发过程中的所有阶段以及注意事项,可以随时解答大家对于软件开发的疑问
{input}
"""
template_qa = """
你是一名资深的测试专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
template_acdc = """
你是一名资深的硬件电源开发专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
# 模板的描述与具体模版关联关系
prompt_infos = [
{
"name": "Dev",
"description": "我可以回答你关于开发各个阶段内容的解释",
"prompt_template": template_dev,
},
{
"name": "QA",
"description": "我可以回答你关于测试各个阶段内容的解释",
"prompt_template": template_qa,
},
{
"name": "ACDC",
"description": "我可以回答你关于硬件电源开发的所有问题",
"prompt_template": template_acdc,
}
]
llm = Tongyi(model_name="qwen-turbo")
chain_map = {}
for info in prompt_infos:
prompt = PromptTemplate(
template=info['prompt_template'],
input_variables=["input"]
)
# 请注意这里不能用 chain = prompt | llm 具体原因以后我给大家介绍
chain = LLMChain(
llm=llm,
prompt=prompt
)
# 构建目标链(不同的链名称与模板链的关联关系)
chain_map[info["name"]] = chain
destinations = [f"{p['name']}: {p['description']}" for p in prompt_infos]
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, router_prompt, verbose=True)
# 没有匹配上链,使用的默认链
default_chain = ConversationChain(
llm=llm,
output_key="text"
)
router_chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=chain_map,
default_chain=default_chain,
)
# 验证结果
response = router_chain.invoke("硬件电源开发分为哪几步?")
print(response)
输出结果
方法二
程序
from langchain_community.llms.tongyi import Tongyi
from langchain.chains.router.multi_prompt import MultiPromptChain
template_dev = """
你是一名资深的开发专家,精通软件开发过程中的所有阶段以及注意事项,可以随时解答大家对于软件开发的疑问
{input}
"""
template_qa = """
你是一名资深的测试专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
template_acdc = """
你是一名资深的硬件电源开发专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
prompt_infos = [
{
"name": "Dev",
"description": "我可以回答你关于开发各个阶段内容的解释",
"prompt_template": template_dev
},
{
"name": "QA",
"description": "我可以回答你关于测试各个阶段内容的解释",
"prompt_template": template_qa
},
{
"name": "ACDC",
"description": "我可以回答你关于硬件电源开发的所有问题",
"prompt_template": template_acdc
}
]
router_chain = MultiPromptChain.from_prompts(llm=Tongyi(model_name="qwen-turbo"), prompt_infos=prompt_infos, verbose=True)
response = router_chain.invoke("硬件电源开发分为哪几步?")
print(response)
输出结果
方法三
程序
from langchain.embeddings.dashscope import DashScopeEmbeddings
from langchain.utils.math import cosine_similarity
from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain_community.llms.tongyi import Tongyi
template_dev = """
你是一名资深的开发专家,精通软件开发过程中的所有阶段以及注意事项,可以随时解答大家对于软件开发的疑问
{input}
"""
template_qa = """
你是一名资深的测试专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
template_acdc = """
你是一名资深的硬件电源开发专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
questions_dev = [
"什么是软件开发?",
"软件开发包含哪些阶段?",
"什么是概要设计、详细设计?",
"什么是单元测试?"
]
questions_qa = [
"什么是软件测试?",
"软件测试包含哪些阶段?",
"什么是测试设计?",
"什么是回归测试?"
]
questions_acdc = [
"什么是硬件开发?",
"硬件开发包含哪些阶段?",
"电源开发包含哪些阶段?",
"什么是AC?什么是DC?"
]
embeddings = DashScopeEmbeddings()
embeddings_dev = embeddings.embed_documents(questions_dev)
embeddings_qa = embeddings.embed_documents(questions_qa)
embeddings_acdc = embeddings.embed_documents(questions_acdc)
llm = Tongyi(model_name="qwen-turbo")
def prompt_router(query):
query_embedding = embeddings.embed_query(query["input"])
similar_dev = cosine_similarity([query_embedding], embeddings_dev)[0]
similar_qa = cosine_similarity([query_embedding], embeddings_qa)[0]
similar_acdc = cosine_similarity([query_embedding], embeddings_acdc)[0]
max_similar = max(max(similar_dev), max(similar_qa), max(similar_acdc))
if max_similar == max(similar_dev):
print("使用软件开发模板(questions_dev)回答问题")
return PromptTemplate.from_template(template_dev)
elif max_similar == max(similar_qa):
print("使用软件测试模板(questions_qa)回答问题")
return PromptTemplate.from_template(template_qa)
elif max_similar == max(similar_acdc):
print("使用硬件电源开发模板(template_acdc)回答问题")
return PromptTemplate.from_template(template_acdc)
else:
print("默认使用软件测试模板(questions_qa)回答问题")
return PromptTemplate.from_template(template_qa)
router_chain = {"input": RunnablePassthrough()} | RunnableLambda(prompt_router) | llm
response = router_chain.invoke("硬件电源开发分为哪几步?")
print(response)
输出结果
方法四(使用classify)
程序
from langchain_community.llms.tongyi import Tongyi
from langchain.prompts import PromptTemplate
from langchain_core.runnables import RunnablePassthrough, RunnableLambda
from langchain.embeddings.dashscope import DashScopeEmbeddings
from langchain_core.prompts import PromptTemplate
template_dev = """
你是一名资深的开发专家,精通软件开发过程中的所有阶段以及注意事项,可以随时解答大家对于软件开发的疑问
{input}
"""
template_qa = """
你是一名资深的测试专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
template_acdc = """
你是一名资深的硬件电源开发专家,精通软件测试过程中的所有阶段以及注意事项,可以随时解答大家对于软件测试的疑问
{input}
"""
classify_x = """
使用classify来区分问题是属于哪种类型
<若问题是关于软件开发技术相关的内容,classify it as 'template_dev'>
<若问题是关于软件测试技术相关的内容,classify it as 'template_qa'>
<若问题是关于硬件电源技术相关的内容,classify it as 'template_acdc'>
{question}
"""
classification_template = PromptTemplate.from_template(classify_x)
llm = Tongyi(model_name="qwen-turbo")
classification_chain = classification_template | llm
def prompt_router(query):
classification = classification_chain.invoke({"question": query["input"]})
if classification == "template_dev":
print("使用软件开发模板(template_dev)回答问题")
return PromptTemplate.from_template(template_dev)
elif classification == "template_qa":
print("使用软件测试模板(template_qa)回答问题")
return PromptTemplate.from_template(template_qa)
elif classification == "template_acdc":
print("使用硬件电源开发模板(template_acdc)回答问题")
return PromptTemplate.from_template(template_acdc)
else:
print("默认使用软件测试模板(template_qa)回答问题")
return PromptTemplate.from_template(template_qa)
router_chain = {"input": RunnablePassthrough()} | RunnableLambda(prompt_router) | llm
response = router_chain.invoke("硬件电源开发分为哪几步?")
print(response)
输出结果
方法三 所提到的向量匹配相关内容,在后面的RAG篇会给大家详细介绍,莫急哈