0


人工智能的黄金链条:LangChain核心模块Chains解析

简介

Chain 作为 LangChain 的核心模块之一,重要性不言而喻,它相当于是所有复杂逻辑的基础,用来将每个零散的逻辑串联成一整个业务流程,Chain 的设计非常巧妙,可以说是大模型应用的最佳实践之一。

设计思路

Chain 的设计非常巧妙,也非常值得借鉴,也是为什么 LangChain 能火爆的主要原因之一。

Chain 的一个主要作用,就是根据需求,可以将各种能力拼接整合。比如上图,LangChain 内部包含多个模块,可以定制 LLMChain 只使用 Prompt 和 LLM 模块,也可以定制一个HogwartsChain 使用了 LLM、Parser、Agent模块。

实践演练

Chains主要包含以下几个模块,接下来的实践演练,会分别演示这几个模块的使用。

LLMChain

LLMChain是一个整合语言模型和提示模板的最简单链。

  1. # LangChain相关模块的导入
  2. from langchain import LLMChain
  3. from langchain.chat_models import ChatOpenAI
  4. from langchain.prompts import ChatPromptTemplate
  5. # 加载个人的OpenAI Token
  6. key = 'open_ai_key'
  7. # 创建OpenAI调用实例
  8. # 本示例中为了让结果更具有创造性,temperature设置为0.9
  9. llm = ChatOpenAI(temperature=0.9, openai_api_key=key)
  10. # 根据prompt模板生成prompt实例
  11. prompt = ChatPromptTemplate.from_template(
  12. "请给生产: {product} 的工厂起一个恰当的厂名,并给出一句广告语。"
  13. )
  14. # 组合大模型实例和prompt实例,生成LLMChain实例,将结构固定,方便复用
  15. chain = LLMChain(
  16. # 大模型实例
  17. llm=llm,
  18. # prompt实例
  19. prompt=prompt,
  20. # 开启详细模式,会将大模型调用细节输出到控制台
  21. verbose=True
  22. )
  23. # 通过run方法,传入模版中需要的参数,调用大模型获取结果
  24. product = "IPhone999 Pro Max Ultra"
  25. res = chain.run(product)
  26. print(res)

返回数据示例:

其中绿色的部分就是请求大模型时发送的完整数据,通过查看发送的数据,可以更有效的了解调用细节、排查问题。

SimpleSequentialChain

串联式调用语言模型链的一种,简单的串联每个步骤(Chain 实例),每个步骤都有单一的输入/输出,并且一个步骤的输入是下一个步骤的输出。

  1. # LangChain相关模块的导入
  2. from langchain import LLMChain
  3. from langchain.chat_models import ChatOpenAI
  4. from langchain.prompts import ChatPromptTemplate
  5. from langchain.chains import SimpleSequentialChain
  6. # 加载个人的OpenAI Token
  7. key = 'open_ai_key'
  8. # 创建OpenAI调用实例
  9. # temperature用来设置大模型返回数据的随机性和创造性,较低的数值返回的数据就更贴近现实。
  10. llm = ChatOpenAI(temperature=0.9, openai_api_key=key)
  11. # 第一个LLM请求的prompt模板
  12. first_prompt = ChatPromptTemplate.from_template(
  13. "请给生产 {product} 的工厂起一个恰当的厂名"
  14. )
  15. # 第一个Chain,接收外部输入,根据模版请求大模型获取输出,作为第二个Chain的输入
  16. chain_one = LLMChain(llm=llm, prompt=first_prompt, verbose=True)
  17. # 第二个大模型请求的prompt模版
  18. second_prompt = ChatPromptTemplate.from_template(
  19. "为厂名写一段不少于20字的广告语: {company_name}"
  20. )
  21. # 第二个Chain,接收第一个Chain的输出,根据模版请求大模型获取输出
  22. chain_two = LLMChain(llm=llm, prompt=second_prompt, verbose=True)
  23. # 将请求拆分成两个Chain,可以针对每段请求细化相应的prompt内容,得到更准确更合理的结果,并且也可以复用其中的每个Chain实例
  24. # 使用SimpleSequentialChain将两个Chain串联起来,其中每个Chain都只支持一个输入和一个输出,根据chains列表中的顺序,将前一个Chain的输出作为下一个Chain的输入
  25. overall_simple_chain = SimpleSequentialChain(
  26. chains=[chain_one, chain_two],
  27. verbose=True
  28. )
  29. # 第一个Chain需要的输入
  30. product = "IPhone999 Pro Max Ultra"
  31. # 通过run方法,传入参数,逐个运行整个Chain后,获取最终的结果
  32. res = overall_simple_chain.run(product)
  33. print(res)

返回数据示例如图:

SequentialChain

串联式调用语言模型链的一种,序列中的每个 Chain 实例都支持多个输入和输出,最终 SequentialChain 运行时根据 Chains 参数和每个 Chain 示例中设定的参数,分析每个实例所需的参数并按需传递。代码示例:

  1. import langchain
  2. # LangChain相关模块的导入
  3. from langchain import LLMChain
  4. from langchain.chat_models import ChatOpenAI
  5. from langchain.prompts import ChatPromptTemplate
  6. from langchain.chains import SequentialChain
  7. # 在全局范围开启详细模式,能将调用大模型时发送的数据打印到控制台,绿色文本
  8. langchain.verbose = True
  9. key = 'open_ai_key'
  10. # 本示例中为了让结果更具有创造性,temperature设置为0.9
  11. llm = ChatOpenAI(temperature=0.9, openai_api_key=key)
  12. # Chain1 语言转换,产生英文产品名
  13. prompt1 = ChatPromptTemplate.from_template(
  14. "将以下文本翻译成英文: {product_name}"
  15. )
  16. chain1 = LLMChain(
  17. # 使用的大模型实例
  18. llm=llm,
  19. # prompt模板
  20. prompt=prompt1,
  21. # 输出数据变量名
  22. output_key="english_product_name",
  23. )
  24. # Chain2 根据英文产品名,生成一段英文介绍文本
  25. prompt2 = ChatPromptTemplate.from_template(
  26. "Based on the following product, give an introduction text about 100 words: {english_product_name}"
  27. )
  28. chain2 = LLMChain(
  29. llm=llm,
  30. prompt=prompt2,
  31. output_key="english_introduce"
  32. )
  33. # Chain3 找到产品名所属的语言
  34. prompt3 = ChatPromptTemplate.from_template(
  35. "下列文本使用的语言是什么?: {product_name}"
  36. )
  37. chain3 = LLMChain(
  38. llm=llm,
  39. prompt=prompt3,
  40. output_key="language"
  41. )
  42. # Chain4 根据Chain2生成的英文介绍,使用产品名称原本的语言生成一段概述
  43. prompt4 = ChatPromptTemplate.from_template(
  44. "使用语言类型为: {language} ,为下列文本写一段不多于50字的概述: {english_introduce}"
  45. )
  46. chain4 = LLMChain(
  47. llm=llm,
  48. prompt=prompt4,
  49. output_key="summary"
  50. )
  51. # 标准版的序列Chain,SequentialChain,其中每个chain都支持多个输入和输出,
  52. # 根据chains中每个独立chain对象,和chains中的顺序,决定参数的传递,获取最终的输出结果
  53. overall_chain = SequentialChain(
  54. chains=[chain1, chain2, chain3, chain4],
  55. input_variables=["product_name"],
  56. output_variables=["english_product_name", "english_introduce", "language", "summary"],
  57. verbose=True
  58. )
  59. product_name = "黄油啤酒"
  60. res = overall_chain(product_name)
  61. print(res)

运行过程和返回数据示例如图:

LLMRouteChain

以下是一段非常简单的 Python 代码。实现的主要是分支判断的作用。

  1. if a == 1:
  2. print("我爱吃苹果")
  3. elif b == 1:
  4. print("我爱吃香蕉")
  5. else:
  6. print("我爱吃西瓜")
  7. LLMRouteChain 的主要作用是能根据提示词的不同而选择不同的Chain进行执行。而实现这一需求,需要以下3个模块结合完成,也是MultiPromptChain的三个参数,以下内容是摘取的部分源码:
  8. class MultiPromptChain(MultiRouteChain):
  9. """A multi-route chain that uses an LLM router chain to choose amongst prompts."""
  10. router_chain: RouterChain
  11. """Chain for deciding a destination chain and the input to it."""
  12. destination_chains: Mapping[str, LLMChain]
  13. """Map of name to candidate chains that inputs can be routed to."""
  14. default_chain: LLMChain
  15. """Default chain to use when router doesn't map input to one of the destinations."""

加载 Token,创建大模型实例。

  1. # 加载个人的OpenAI Token
  2. key = "open_ai_key"
  3. # temperature用来设置大模型返回数据的随机性和创造性,较低的数值返回的数据就更贴近现实。
  4. llm = ChatOpenAI(temperature=0.0, openai_api_key=key)

创建 destination_chains,目的是构造各个分支,以及其对应的Prompt。

  1. # 2个角色prompt模板,作为请求处理的2个分支
  2. physics_template = """你是一位非常聪明的物理学家,非常擅长回答物理相关的问题,\
  3. 并且会以一种简洁易懂的方式对问题做出讲解。\
  4. 当你无法回答问题的时候,就会主动承认无法回答问题。\
  5. 以下是具体问题:
  6. {input}"""
  7. math_template = """你是一位非常棒的数学家,非常擅长回答数学相关的问题。\
  8. 你之所以这么棒,是因为你能够将难题拆解成它们的组成部分,\
  9. 对组成部分分别作答后,再将它们组合起来最终成功的回答出最初的原始问题。\
  10. 以下是具体问题:
  11. {input}"""
  12. # 将角色prompt模板和对应的描述、名称组装成列表,方便遍历
  13. prompt_info = [
  14. {
  15. "name": "物理学家",
  16. "description": "擅长回答物理方面的问题",
  17. "prompt_template": physics_template
  18. },
  19. {
  20. "name": "数学家",
  21. "description": "擅长回答数学方面的问题",
  22. "prompt_template": math_template
  23. },
  24. ]
  25. # 名称和大模型Chain映射关系的字典
  26. destination_chains = {}
  27. # 根据prompt_info中的信息,创建对应的LLMChain实例,并放入映射字典中
  28. for p_info in prompt_info:
  29. # destination_chains最终的结构为: {name: 对应的LLMChain实例}
  30. name = p_info["name"]
  31. prompt_template = p_info["prompt_template"]
  32. prompt = ChatPromptTemplate.from_template(template=prompt_template)
  33. # 给每个角色创建一个自己的大模型Chain实例
  34. chain = LLMChain(llm=llm, prompt=prompt)
  35. # 组装成字典,方便router根据逻辑选择分支之后,能够找到分支对应调用的Chain实例
  36. destination_chains[name] = chain

通过模板和大模型对象,生成LLMRouterChain,用于实现分支逻辑。而其能实现分支逻辑的原理,也是通过Prompt 实现,注意示例中的变量

  1. MULTI_PROMPT_ROUTER_TEMPLATE

对应的内容以及信息:

  1. # 生成destinations
  2. destinations = [f"{p['name']}: {p['description']}" for p in prompt_info]
  3. # 转换成多行字符串,每行一句对应关系
  4. destinations_str = "\n".join(destinations)
  5. # 真正实现 if 分支判断的地方
  6. # 选择使用哪个人设模型进行处理的prompt模板
  7. MULTI_PROMPT_ROUTER_TEMPLATE = """Given a raw text input to a \
  8. language model select the model prompt best suited for the input. \
  9. You will be given the names of the available prompts and a \
  10. description of what the prompt is best suited for. \
  11. You may also revise the original input if you think that revising\
  12. it will ultimately lead to a better response from the language model.
  13. << FORMATTING >>
  14. Return a markdown code snippet with a JSON object formatted to look like:
  15. ```json {{{{ "destination": string \ name of the prompt to use or "DEFAULT" "next_inputs": string \ a potentially modified version of the original input }}}} ```
  16. REMEMBER: "destination" MUST be one of the candidate prompt \
  17. names specified below OR it can be "DEFAULT" if the input is not\
  18. well suited for any of the candidate prompts.
  19. REMEMBER: "next_inputs" can just be the original input \
  20. if you don't think any modifications are needed.
  21. << CANDIDATE PROMPTS >>
  22. {destinations}
  23. << INPUT >>
  24. {{input}}
  25. << OUTPUT (remember to include the ```json)>>"""
  26. # 在router模版中先补充部分数据
  27. router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(
  28. destinations=destinations_str
  29. )
  30. # 组装一个基础的prompt模板对象,通过更多的参数设置更多的信息
  31. router_prompt = PromptTemplate(
  32. # 基础模板
  33. template=router_template,
  34. # 输入参数名称
  35. input_variables=["input"],
  36. # 输出数据解析器
  37. output_parser=RouterOutputParser(),
  38. )
  39. # 通过模板和大模型对象,生成LLMRouterChain,用于支持分支逻辑
  40. router_chain = LLMRouterChain.from_llm(llm, router_prompt)

创建一个默认的LLMChain实例,作为上述匹配未命中时的默认调用目标,避免调用最终没有逻辑去处理的情况出现。

  1. # 创建 else 场景
  2. default_prompt = ChatPromptTemplate.from_template("{input}")
  3. default_chain = LLMChain(llm=llm, prompt=default_prompt)

通过 MultiPromptChain 将以上三个模块整合,实现一个支持分支判断的Chain:

  1. # 把 分支、 else 的情况 以及 做if判断的语句结合到一起
  2. # 将多个chain组装成完整的chain对象,完成带有逻辑的请求链
  3. chain = MultiPromptChain(
  4. router_chain=router_chain,
  5. destination_chains=destination_chains,
  6. default_chain=default_chain,
  7. verbose=True
  8. )
  9. # 提出一个物理问题
  10. physics_res = chain.run("什么是黑体辐射?")
  11. print(physics_res)
  12. # 提出一个数学问题
  13. math_res = chain.run("2的4次方是多少?")
  14. print(math_res)

最终可以看到请求的时候,如果问题与之前给出的角色有相关,会使用到该角色的 llm 实例进行实际问题的解答。相关完整代码如下:

  1. 相关完整代码如下:
  2. from langchain import LLMChain
  3. from langchain.chat_models import ChatOpenAI
  4. from langchain.chains.router import MultiPromptChain
  5. from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
  6. from langchain.prompts import PromptTemplate, ChatPromptTemplate
  7. # 加载个人的OpenAI Token
  8. key = "open_ai_key"
  9. # temperature用来设置大模型返回数据的随机性和创造性,较低的数值返回的数据就更贴近现实。
  10. llm = ChatOpenAI(temperature=0.0, openai_api_key=key)
  11. # 2个角色prompt模板,作为请求处理的2个分支
  12. physics_template = """你是一位非常聪明的物理学家,非常擅长回答物理相关的问题,\
  13. 并且会以一种简洁易懂的方式对问题做出讲解。\
  14. 当你无法回答问题的时候,就会主动承认无法回答问题。\
  15. 以下是具体问题:
  16. {input}"""
  17. math_template = """你是一位非常棒的数学家,非常擅长回答数学相关的问题。\
  18. 你之所以这么棒,是因为你能够将难题拆解成它们的组成部分,\
  19. 对组成部分分别作答后,再将它们组合起来最终成功的回答出最初的原始问题。\
  20. 以下是具体问题:
  21. {input}"""
  22. # 将角色prompt模板和对应的描述、名称组装成列表,方便遍历
  23. prompt_info = [
  24. {
  25. "name": "物理学家",
  26. "description": "擅长回答物理方面的问题",
  27. "prompt_template": physics_template
  28. },
  29. {
  30. "name": "数学家",
  31. "description": "擅长回答数学方面的问题",
  32. "prompt_template": math_template
  33. },
  34. ]
  35. # 名称和大模型Chain映射关系的字典
  36. destination_chains = {}
  37. # 根据prompt_info中的信息,创建对应的LLMChain实例,并放入映射字典中
  38. for p_info in prompt_info:
  39. # destination_chains最终的结构为: {name: 对应的LLMChain实例}
  40. name = p_info["name"]
  41. prompt_template = p_info["prompt_template"]
  42. prompt = ChatPromptTemplate.from_template(template=prompt_template)
  43. # 给每个角色创建一个自己的大模型Chain实例
  44. chain = LLMChain(llm=llm, prompt=prompt)
  45. # 组装成字典,方便router根据逻辑选择分支之后,能够找到分支对应调用的Chain实例
  46. destination_chains[name] = chain
  47. # 生成destinations
  48. destinations = [f"{p['name']}: {p['description']}" for p in prompt_info]
  49. # 转换成多行字符串,每行一句对应关系
  50. destinations_str = "\n".join(destinations)
  51. # 真正实现 if 分支判断的地方
  52. # 选择使用哪个人设模型进行处理的prompt模板
  53. MULTI_PROMPT_ROUTER_TEMPLATE = """Given a raw text input to a \
  54. language model select the model prompt best suited for the input. \
  55. You will be given the names of the available prompts and a \
  56. description of what the prompt is best suited for. \
  57. You may also revise the original input if you think that revising\
  58. it will ultimately lead to a better response from the language model.
  59. << FORMATTING >>
  60. Return a markdown code snippet with a JSON object formatted to look like:
  61. ```json {{{{ "destination": string \ name of the prompt to use or "DEFAULT" "next_inputs": string \ a potentially modified version of the original input }}}} ```
  62. REMEMBER: "destination" MUST be one of the candidate prompt \
  63. names specified below OR it can be "DEFAULT" if the input is not\
  64. well suited for any of the candidate prompts.
  65. REMEMBER: "next_inputs" can just be the original input \
  66. if you don't think any modifications are needed.
  67. << CANDIDATE PROMPTS >>
  68. {destinations}
  69. << INPUT >>
  70. {{input}}
  71. << OUTPUT (remember to include the ```json)>>"""
  72. # 在router模版中先补充部分数据
  73. router_template = MULTI_PROMPT_ROUTER_TEMPLATE.format(
  74. destinations=destinations_str
  75. )
  76. # 组装一个基础的prompt模板对象,通过更多的参数设置更多的信息
  77. router_prompt = PromptTemplate(
  78. # 基础模板
  79. template=router_template,
  80. # 输入参数名称
  81. input_variables=["input"],
  82. # 输出数据解析器
  83. output_parser=RouterOutputParser(),
  84. )
  85. # 通过模板和大模型对象,生成LLMRouterChain,用于支持分支逻辑
  86. router_chain = LLMRouterChain.from_llm(llm, router_prompt)
  87. # 创建 else 场景
  88. # 创建一个默认的LLMChain实例,作为上述匹配未命中时的默认调用目标,避免调用最终没有逻辑去处理的情况出现
  89. # 上述的匹配规则可以看成一组if elif的逻辑匹配规则,default作为最后的else负责处理所有未命中的情况
  90. default_prompt = ChatPromptTemplate.from_template("{input}")
  91. default_chain = LLMChain(llm=llm, prompt=default_prompt)
  92. # 把 分支、 else 的情况 以及 做if判断的语句结合到一起
  93. # 将多个chain组装成完整的chain对象,完成带有逻辑的请求链
  94. chain = MultiPromptChain(
  95. router_chain=router_chain,
  96. destination_chains=destination_chains,
  97. default_chain=default_chain,
  98. verbose=True
  99. )
  100. # 提出一个物理问题
  101. physics_res = chain.run("什么是黑体辐射?")
  102. print(physics_res)
  103. # 提出一个数学问题
  104. math_res = chain.run("2的4次方是多少?")
  105. print(math_res)

后续在练习的过程中,尝试基于以上的示例代码再添加几条分支逻辑。

更多Python基础语法趣味学习视频,请点击!


本文转载自: https://blog.csdn.net/cebawuyue/article/details/134967925
版权归原作者 霍格沃兹-慕漓 所有, 如有侵权,请联系我们删除。

“人工智能的黄金链条:LangChain核心模块Chains解析”的评论:

还没有评论