langchain

一、模型I/O
我们可以把对模型的使用过程拆解成三块,分别是输入提示(对应图中的 Format)、调用模型(对应图中的 Predict)和输出解析(对应图中的 Parse)。这三块形成了一个整体,因此在 LangChain 中这个过程被统称为 Model I/O(Input/Output)。

● 提示模板:使用模型的第一个环节是把提示信息输入到模型中,你可以创建 LangChain 模板,根据实际需求动态选择不同的输入,针对特定的任务和应用调整输入。
● 语言模型:LangChain 允许你通过通用接口来调用语言模型。这意味着无论你要使用的是哪种语言模型,都可以通过同一种方式进行调用,这样就提高了灵活性和便利性。
● 输出解析:LangChain 还提供了从模型输出中提取信息的功能。通过输出解析器,你可以精确地从模型的输出中获取需要的信息,而不需要处理冗余或不相关的数据,更重要的是还可以把大模型给回的非结构化文本,转换成程序可以处理的结构化数据。
1.1 提示词工程
1.1.1 提示词模板

1.1.1.1 PromptTemplate
from langchain import PromptTemplate

my_template=“”"
你是业务咨询顾问。你给一个销售{product}的电商公司,起一个好的名字?
“”"

方式一:自动形成模板参数

prompt = PromptTemplate.from_template(my_template)
print(prompt.format(product=“鲜花”))

方式二:手动设置模板参数

prompt = PromptTemplate(
input_variables=[“product”,“market”],
template=“你是业务咨询顾问。对于一个面向{market}市场的,专注于销售{product}的公司,你会推荐哪个名字?”
)
print(prompt.format(product=“鲜花”, market=“高端”))
1.1.1.2 ChatPromptTemplate
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
AIMessagePromptTemplate
)

系统提示词模板构建

template=“你是一位专业顾问,负责为专注于{product}的公司起名。”
system_message_template = SystemMessagePromptTemplate.from_template(template)

用户提示词模板构建

human_template=“公司主打产品是{product_detail}。”
human_message_template = HumanMessagePromptTemplate.from_template(human_template)

ChatPromptTemplate构建

prompt_template = ChatPromptTemplate.from_messages([system_message_template,human_message_template])
message = prompt_template.format_prompt(product=“鲜花装饰”,product_detail=“创新的鲜花设计”).to_messages()
print(message)
print(“=====================”)
from langchain_wenxin.chat_models import ChatWenxin

WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”

chat = ChatWenxin(
temperature=0.9,
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET,
verbose=True,
)

chat(message)
1.1.1.3 FewShotPromptTemplate
from langchain import FewShotPromptTemplate

1. 创建一些示例

samples = [
{
“flower_type”: “玫瑰”,
“occasion”: “爱情”,
“ad_copy”: “玫瑰,浪漫的象征,是你向心爱的人表达爱意的最佳选择。”
},
{
“flower_type”: “康乃馨”,
“occasion”: “母亲节”,
“ad_copy”: “康乃馨代表着母爱的纯洁与伟大,是母亲节赠送给母亲的完美礼物。”
},
{
“flower_type”: “百合”,
“occasion”: “庆祝”,
“ad_copy”: “百合象征着纯洁与高雅,是你庆祝特殊时刻的理想选择。”
},
{
“flower_type”: “向日葵”,
“occasion”: “鼓励”,
“ad_copy”: “向日葵象征着坚韧和乐观,是你鼓励亲朋好友的最好方式。”
}
]

2. 创建一个提示模板

from langchain.prompts.prompt import PromptTemplate
template=“鲜花类型: {flower_type}\n场合: {occasion}\n文案: {ad_copy}”
prompt_sample = PromptTemplate(
input_variables=[“flower_type”, “occasion”, “ad_copy”],
template=template
)
print(prompt_sample.format(**samples[0]))
print(“==============================”)

3. 创建一个FewShotPromptTemplate对象

prompt = FewShotPromptTemplate(
# 示例
examples=samples,
example_prompt=prompt_sample,
# 问题
suffix=“请按照示例补充文案,鲜花类型为{flower_type};场合为{occasion}”,
input_variables=[“flower_type”,“occasion”]
)
print(prompt.format(flower_type=“野玫瑰”, occasion=“爱情”))
print(“==============================”)

model = Wenxin(
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)
model(prompt.format(flower_type=“野玫瑰”, occasion=“爱情”))
1.1.1.4 PipelinePrompt
● 通过 PipelinePrompt 可以将多个提示组合在一起,可以重复使用部分提示词
● 入参:
○ Final prompt: 返回的最终提示,比如以下示例中的full_prompt;
○ Pipeline prompts:元组列表(string name,prompt template)
from langchain_core.prompts import PipelinePromptTemplate
from langchain.prompts.prompt import PromptTemplate

整体提示词模板

full_template =“”"
introduction:{introduction}

example:{example}

{start}
“”"
full_prompt = PromptTemplate.from_template(full_template)

部分提示词模板

introduction_template = “”“You are impersonating {person}.”“”
introduction_prompt = PromptTemplate.from_template(introduction_template)

example_template = “”"
Here’s an example of an interaction:

Q: {example_q}
A: {example_a}“”"
example_prompt = PromptTemplate.from_template(example_template)

start_template = “”"
Now, do this for real!

Q: {input}
A:
“”"
start_prompt = PromptTemplate.from_template(start_template)

使用PipelinePromptTemplate组合所有提示模板

input_prompts = [
(“introduction”, introduction_prompt),
(“example”, example_prompt),
(“start”, start_prompt)
]
pipeline_prompt = PipelinePromptTemplate(final_prompt = full_prompt, pipeline_prompts = input_prompts)
input = pipeline_prompt.format(
person=“Elon Musk”,
example_q=“What’s your favorite car?”,
example_a=“Tesla”,
input=“What’s your favorite social media site?”
)
print(input)
model = Wenxin(
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)
model(input)
1.1.1.5 自定义提示词模板
https://www.langchain.com.cn/modules/prompts/prompt_templates/examples/custom_prompt_template
1.1.2 示例选择器
以LengthBasedExampleSelector为例演示示例选择器的使用,更多示例选择器参考:https://www.langchain.com.cn/modules/prompts/example_selectors
from langchain.prompts import PromptTemplate
from langchain.prompts import FewShotPromptTemplate
from langchain.prompts.example_selector import LengthBasedExampleSelector

examples = [
{“input”: “happy”, “output”: “sad”},
{“input”: “tall”, “output”: “short”},
{“input”: “energetic”, “output”: “lethargic”},
{“input”: “sunny”, “output”: “gloomy”},
{“input”: “windy”, “output”: “calm”},
]

example_prompt = PromptTemplate(
input_variables=[“input”, “output”],
template=“Input: {input}\nOutput: {output}”,
)

example_selector = LengthBasedExampleSelector(
# These are the examples it has available to choose from.
examples=examples,
# This is the PromptTemplate being used to format the examples.
example_prompt=example_prompt,
# This is the maximum length that the formatted examples should be.
# Length is measured by the get_text_length function below.
max_length=10,
)

dynamic_prompt = FewShotPromptTemplate(
# We provide an ExampleSelector instead of examples.
example_selector=example_selector,
example_prompt=example_prompt,
prefix=“Give the antonym of every input”,
suffix=“Input: {adjective}\nOutput:”,
input_variables=[“adjective”],
)
print(dynamic_prompt.format(adjective=“big”))
print(“*********”)

添加新示例

new_example = {“input”: “big”, “output”: “small”}
dynamic_prompt.example_selector.add_example(new_example)
print(dynamic_prompt.format(adjective=“enthusiastic”))
1.1.3 提示工程理论
1.1.3.1 思维链(Chain of Thought)
● CoT:即思维链。如果生成一系列的中间推理步骤,就能够显著提高大型语言模型进行复杂推理的能力。
1.1.3.1.1 Few-Shot CoT

  1. 含义:简单的在提示中提供了一些链式思考示例(Chain-of-Thought Prompting),大语言模型的推理能力就能够被增强。简单说,就是给出一两个示例,然后在示例中写清楚推导的过程。
  2. 其实 LangChain 的核心组件 Agent 的本质就是进行好的提示工程,并大量地使用预置的 FewShot 和 CoT 模板

1.1.3.1.2 Zero-Shot CoT
● 在 Zero-Shot CoT 中,你只要简单地告诉模型“让我们一步步的思考(Let’s think step by step)”,模型就能够给出更好的答案

1.1.3.2 思维树(Tree of Thoughts,ToT)
● ToT 是一种解决复杂问题的框架,它在需要多步骤推理的任务中,引导语言模型搜索一棵由连贯的语言序列(解决问题的中间步骤)组成的思维树,而不是简单地生成一个答案。ToT 框架的核心思想是:让模型生成和评估其思维的能力,并将其与搜索算法(如广度优先搜索和深度优先搜索)结合起来,进行系统性地探索和验证。

1.2 模型调用
1.2.1 调用大模型服务
from langchain_wenxin.chat_models import ChatWenxin
from langchain.schema import (HumanMessage, SystemMessage)

WENXIN_APP_Key = “HmGXqCkw4KYfvH2GR2TGKjfl”
WENXIN_APP_SECRET = “DpllkA0shyj3ZtzVkg7qEfygPY2Cwtm1”

一、聊天功能

chat = ChatWenxin(
temperature=0.9,
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET,
verbose=True,
)

message = [SystemMessage(content=“你是一个很棒的助手”), HumanMessage(content=“请给我的花店起个名”)]
responcse = chat(message)
print(responcse)
1.2.2 调用本地模型
参考链接:https://time.geekbang.org/column/article/702140
1.3 输出解析
1.3.1 什么是输出解析器
输出解析器是一种专用于处理和构建语言模型响应的类。一个基本的输出解析器类通常需要实现两个核心方法。

  1. get_format_instructions:这个方法需要返回一个字符串,用于指导如何格式化语言模型的输出,告诉它应该如何组织并构建它的回答。

  2. parse:这个方法接收一个字符串(也就是语言模型的输出)并将其解析为特定的数据结构或格式。这一步通常用于确保模型的输出符合我们的预期,并且能够以我们需要的形式进行后续处理。
    还有一个可选方法

  3. parse_with_prompt:这个方法接收一个字符串(也就是语言模型的输出)和一个提示(用于生成这个输出的提示),并将其解析为特定的数据结构。这样,你可以根据原始提示来修正或重新解析模型的输出,确保输出的信息更加准确和贴合要求。
    class OutputParser:
    def init(self):
    pass

    def get_format_instructions(self):
    # 返回一个字符串,指导如何格式化模型的输出
    pass

    def parse(self, model_output):
    # 解析模型的输出,转换为某种数据结构或格式
    pass

    def parse_with_prompt(self, model_output, prompt):
    # 基于原始提示解析模型的输出,转换为某种数据结构或格式
    pass
    1.3.2 langchain内置的输出解析器

  4. 列表解析器(List Parser):这个解析器用于处理模型生成的输出,当需要模型的输出是一个列表的时候使用。例如,如果你询问模型“列出所有鲜花的库存”,模型的回答应该是一个列表。

  5. 日期时间解析器(Datetime Parser):这个解析器用于处理日期和时间相关的输出,确保模型的输出是正确的日期或时间格式。

  6. 枚举解析器(Enum Parser):这个解析器用于处理预定义的一组值,当模型的输出应该是这组预定义值之一时使用。例如,如果你定义了一个问题的答案只能是“是”或“否”,那么枚举解析器可以确保模型的回答是这两个选项之一。

  7. 结构化输出解析器(Structured Output Parser):这个解析器用于处理复杂的、结构化的输出。如果你的应用需要模型生成具有特定结构的复杂回答(例如一份报告、一篇文章等),那么可以使用结构化输出解析器来实现。

  8. Pydantic(JSON)解析器:这个解析器用于处理模型的输出,当模型的输出应该是一个符合特定格式的 JSON 对象时使用。它使用 Pydantic 库,这是一个数据验证库,可以用于构建复杂的数据模型,并确保模型的输出符合预期的数据模型。

  9. 自动修复解析器(Auto-Fixing Parser):这个解析器可以自动修复某些常见的模型输出错误。例如,如果模型的输出应该是一段文本,但是模型返回了一段包含语法或拼写错误的文本,自动修复解析器可以自动纠正这些错误。

  10. 重试解析器(RetryWithErrorOutputParser):这个解析器用于在模型的初次输出不符合预期时,尝试修复或重新生成新的输出。例如,如果模型的输出应该是一个日期,但是模型返回了一个字符串,那么重试解析器可以重新提示模型生成正确的日期格式。
    1.3.2.1 Pydantic(JSON)解析器实战
    Pydantic 是一个 Python 数据验证和设置管理库,主要基于 Python 类型提示。尽管它不是专为 JSON 设计的,但由于 JSON 是现代 Web 应用和 API 交互中的常见数据格式,Pydantic 在处理和验证 JSON 数据时特别有用。
    Pydantic 有这样几个特点。

  11. 数据验证:当你向 Pydantic 类赋值时,它会自动进行数据验证。例如,如果你创建了一个字段需要是整数,但试图向它赋予一个字符串,Pydantic 会引发异常。

  12. 数据转换:Pydantic 不仅进行数据验证,还可以进行数据转换。例如,如果你有一个需要整数的字段,但你提供了一个可以转换为整数的字符串,如 “42”,Pydantic 会自动将这个字符串转换为整数 42。

  13. 易于使用:创建一个 Pydantic 类就像定义一个普通的 Python 类一样简单。只需要使用 Python 的类型注解功能,即可在类定义中指定每个字段的类型。

  14. JSON 支持:Pydantic 类可以很容易地从 JSON 数据创建,并可以将类的数据转换为 JSON 格式。

from langchain_wenxin import Wenxin

WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”

model = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

定义我们想要接收的数据格式

from pydantic import BaseModel, Field
class FlowerDescription(BaseModel):
flower_type: str = Field(description=“鲜花的种类”)
price: int = Field(description=“鲜花的价格”)
description: str = Field(description=“鲜花的描述文案”)
reason: str = Field(description=“为什么要这样写这个文案”)

from langchain.output_parsers import PydanticOutputParser

创建输出解析器

parser = PydanticOutputParser(pydantic_object=FlowerDescription)

获得输出格式描述

format_instructions = parser.get_format_instructions()
print(“输出格式为:\n”, format_instructions)

创建提示模板

from langchain import PromptTemplate
prompt_template = “”“您是一位专业的鲜花店文案撰写员。
对于售价为 {price} 元的 {flower} ,您能提供一个吸引人的简短中文描述吗?
{format_instructions}”“”

prompt = PromptTemplate.from_template(
prompt_template,
partial_variables={“format_instructions”: parser.get_format_instructions()}
)
print(“提示词模板为:\n”, prompt)

input = prompt.format(flower = “玫瑰”, price = “50”)
print(“输入提示词为:\n”, input)

output = model(input)
print(“输出为:\n”, output)

parsed_output = parser.parse(output)
print(type(parsed_output))
print(parsed_output)
● Pydantic(JSON)解析器增加的提示词如下:

langchain固定增加的提示词

The output should be formatted as a JSON instance that conforms to the JSON schema below.

As an example, for the schema
{
“properties”:{
“foo”:{
“title”:“Foo”,
“description”:“a list of strings”,
“type”:“array”,
“items”:{
“type”:“string”
}
}
},
“required”:[
“foo”
]
}
the object
{
“foo”:[
“bar”,
“baz”
]
} is a well-formatted instance of the schema.

The object
{
“properties”:{
“foo”:[
“bar”,
“baz”
]
}
} is not well-formatted.

Here is the output schema:

langchain根据要解析的目标对象,格式化后的信息

{
    "properties":{
        "flower_type":{
            "title":"Flower Type",
            "description":"\u9c9c\u82b1\u7684\u79cd\u7c7b",
            "type":"string"
        },
        "price":{
            "title":"Price",
            "description":"\u9c9c\u82b1\u7684\u4ef7\u683c",
            "type":"integer"
        },
        "description":{
            "title":"Description",
            "description":"\u9c9c\u82b1\u7684\u63cf\u8ff0\u6587\u6848",
            "type":"string"
        },
        "reason":{
            "title":"Reason",
            "description":"\u4e3a\u4ec0\u4e48\u8981\u8fd9\u6837\u5199\u8fd9\u4e2a\u6587\u6848",
            "type":"string"
        }
    },
    "required":[
        "flower_type",
        "price",
        "description",
        "reason"
    ]
}```
1.3.2.2 自动修复解析器(OutputFixingParser)实战
● 该输出解析器封装另一个输出解析器,并尝试修复任何错误
● 在 OutputFixingParser 内部,调用了原有的 PydanticOutputParser。如果成功,就返回;如果失败,它会将格式错误的输出以及格式化的指令传递给大模型,并要求 LLM 进行相关的修复。
from typing import List

# 使用Pydantic创建一个数据格式,表示花
class Flower(BaseModel):
    name: str = Field(description="name of a flower")
    colors: List[str] = Field(description="the colors of this flower")

# 定义一个格式不正确的输出
misformatted = "{'name': '康乃馨', 'colors': ['粉红色','白色','红色','紫色','黄色']}"
# 创建一个用于解析输出的Pydantic解析器,此处希望解析为Flower格式
parser = PydanticOutputParser(pydantic_object=Flower)

# 使用Pydantic解析器解析不正确的输出
# parser.parse(misformatted)

from langchain.output_parsers import OutputFixingParser
from langchain_wenxin import ChatWenxin
WENXIN_APP_Key = "xxx"
WENXIN_APP_SECRET = "xxx"
# 设置OpenAI API密钥
chat = ChatWenxin(
    temperature=0.9,
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET,
    verbose=True,
)


new_parser = OutputFixingParser.from_llm(parser=parser, llm=chat)
result = new_parser.parse(misformatted)
print(result)
print(type(result))
1.3.2.3 重试解析器(RetryWithErrorOutputParser)实战
# 定义一个模板字符串,这个模板将用于生成提问
template = """Based on the user question, provide an Action and Action Input for what step should be taken.
{format_instructions}
Question: {query}
Response:"""

# 定义一个Pydantic数据格式,它描述了一个"行动"类及其属性
from pydantic import BaseModel, Field
class Action(BaseModel):
    action: str = Field(description="action to take")
    action_input: str = Field(description="input to the action")

# 使用Pydantic格式Action来初始化一个输出解析器
from langchain.output_parsers import PydanticOutputParser
parser = PydanticOutputParser(pydantic_object=Action)

# 定义一个提示模板,它将用于向模型提问
from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
    template=template,
    input_variables=["query"],
    partial_variables={"format_instructions": parser.get_format_instructions()},
)
prompt_value = prompt.format_prompt(query="What are the colors of Orchid?")
print("=========提示词:==========")
print(prompt_value)

# 定义一个错误格式的字符串
bad_response = '{"action": "search"}'
# parser.parse(bad_response) # 如果直接解析,它会引发一个错误


from langchain_wenxin import ChatWenxin
WENXIN_APP_Key = "xxx"
WENXIN_APP_SECRET = "xxx"
# 设置OpenAI API密钥
chat = ChatWenxin(
    temperature=0.9,
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET,
    verbose=True,
)


fix_parser = OutputFixingParser.from_llm(parser=parser, llm=chat)
parse_result = fix_parser.parse(bad_response)
print("==============OutputFixingParser的parse结果====================")
print(parse_result)

from langchain.output_parsers import RetryWithErrorOutputParser
retry_parser = RetryWithErrorOutputParser.from_llm(parser=parser, llm=chat)
parse_result = retry_parser.parse_with_prompt(bad_response, prompt_value)
print("==============OutputFixingParser的parse结果====================")
print(parse_result)

1.3.3 总结
1. 结构化解析器和 Pydantic 解析器:都旨在从大型语言模型中获取格式化的输出。结构化解析器更适合简单的文本响应,而 Pydantic 解析器则提供了对复杂数据结构和类型的支持。选择哪种解析器取决于应用的具体需求和输出的复杂性
2. 自动修复解析器和重试解析器:自动修复解析器主要适用于纠正小的格式错误,它更加“被动”,仅在原始输出出现问题时进行修复。重试解析器则可以处理更复杂的问题,包括格式错误和内容缺失,它通过重新与模型交互,使得输出更加完整和符合预期。
二、链(chain)
链在内部把一系列的功能进行封装,而链的外部则又可以组合串联。链其实可以被视为 LangChain 中的一种基本功能单元。

2.1 链的种类
2.1.1 简单链LLMChain
LLMChain整合了 PromptTemplate、语言模型(LLM 或聊天模型)和 Output Parser,相当于把 Model I/O 放在一个链中整体操作。它使用提示模板格式化输入,将格式化的字符串传递给 LLM,并返回 LLM 输出。
1. 使用model I/O进行模型调用
# 导入所需的库
from langchain import PromptTemplate, LLMChain
from langchain_wenxin import Wenxin

WENXIN_APP_Key = "HmGXqCkw4KYfvH2GR2TGKjfl"
WENXIN_APP_SECRET = "DpllkA0shyj3ZtzVkg7qEfygPY2Cwtm1"
#----第一步 创建提示
# 原始字符串模板
template = "{flower}的花语是?"
# 创建LangChain模板
prompt_temp = PromptTemplate.from_template(template) 
# 根据模板创建提示
prompt = prompt_temp.format(flower='玫瑰')
# 打印提示的内容
print(prompt)

#----第二步 创建并调用模型 
# 创建模型实例
model = Wenxin(
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET
)
# 传入提示,调用模型,返回结果
result = model(prompt)
print(result)
2. 使用LLMChain:把提示模板的构建和模型的调用封装在一起
# 原始字符串模板
template = "{flower}的花语是?"
# 创建模型实例
llm = Wenxin(
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET
)
# 创建LLMChain
llm_chain = LLMChain(
    llm=llm,
    prompt=PromptTemplate.from_template(template)
)
# 调用LLMChain,返回结果
result = llm_chain("玫瑰")
print(result)
2.1.2 顺序链SequentialChain
将多个链串联起来,一个链的输出作为下一个链的输入
需求示例:
● 第一步,我们假设大模型是一个植物学家,让他给出某种特定鲜花的知识和介绍。
● 第二步,我们假设大模型是一个鲜花评论者,让他参考上面植物学家的文字输出,对鲜花进行评论。
● 第三步,我们假设大模型是易速鲜花的社交媒体运营经理,让他参考上面植物学家和鲜花评论者的文字输出,来写一篇鲜花运营文案。
from langchain.chains import SequentialChain

llm = Wenxin(
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET
)

# 这是第一个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")

# 这是第二个LLMChain,用于根据鲜花的介绍写出鲜花的评论
template = """
你是一位鲜花评论家。给定一种花的介绍,你需要为这种花写一篇200字左右的评论。

鲜花介绍:
{introduction}
花评人对上述花的评论:"""
prompt_template = PromptTemplate(input_variables=["introduction"], template=template)
review_chain = LLMChain(llm=llm, prompt=prompt_template, output_key="review")

# 这是第三个LLMChain,用于根据鲜花的介绍和评论写出一篇自媒体的文案
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"]
)

# 运行链,并打印结果
result = overall_chain({"name":"玫瑰", "color": "黑色"})
print(result)
2.1.3 对话链ConversationChain
● 提供了包含 AI 前缀和人类前缀的对话摘要格式,支持记忆功能
# 初始化语言模型
from langchain_wenxin import Wenxin
WENXIN_APP_Key = "HmGXqCkw4KYfvH2GR2TGKjfl"
WENXIN_APP_SECRET = "DpllkA0shyj3ZtzVkg7qEfygPY2Cwtm1"
llm = Wenxin(
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET
)   

from langchain.chains import ConversationChain
conversation_chain = ConversationChain(llm=llm, verbose=True)
# conversation_chain("你是谁")
print(conversation_chain.prompt.template)
● 其内置对话模板如下
# 这里的提示为人类(我们)和人工智能(text-davinci-003)之间的对话设置了一个基本对话框架:
# 这是人类和 AI 之间的友好对话。AI 非常健谈并从其上下文中提供了大量的具体细节
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. 
# 同时,这个提示试图通过说明以下内容来减少幻觉,也就是尽量减少模型编造的信息:
# 如果 AI 不知道问题的答案,它就会如实说它不知道
If the AI does not know the answer to a question, it truthfully says it does not know.

Current conversation:
{history}
Human: {input}
AI:
2.1.4 路由链RouterChain
1. RouterChain,也叫路由链,能动态选择用于给定输入的下一个链。我们会根据用户的问题内容,首先使用路由器链确定问题更适合哪个处理模板,然后将问题发送到该处理模板进行回答。如果问题不适合任何已定义的处理模板,它会被发送到默认链。
2. 常用类:
  a. LLMRouterChain:用来决定调用哪个模板
  b. MultiPromptChain:组合路由链和目标链,返回最终结果
    ⅰ. 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 将使用此默认链。这是一个备选方案,确保即使路由器不能决定正确的链,也总有一个链可以处理输入。
    ⅱ. MultiPromptChain的工作流程
      1. 输入首先传递给 router_chain
      2. router_chain 根据调用大模型决定应该使用哪一个 destination_chain
      3. 输入随后被路由到选定的 destination_chain,该链进行处理并返回结果
      4. 如果 router_chain 不能决定正确的 destination_chain,则输入会被传递给 default_chain
● 代码示例
# 构建两个场景的模板
flower_care_template = """你是一个经验丰富的园丁,擅长解答关于养花育花的问题。
                        下面是需要你来回答的问题:
                        {input}"""

flower_deco_template = """你是一位网红插花大师,擅长解答关于鲜花装饰的问题。
                        下面是需要你来回答的问题:
                        {input}"""

# 构建提示信息
prompt_infos = [
    {
        "key": "flower_care",
        "description": "适合回答关于鲜花护理的问题",
        "template": flower_care_template,
    },
    {
        "key": "flower_decoration",
        "description": "适合回答关于鲜花装饰的问题",
        "template": flower_deco_template,
    }]

# 构建路由链
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
destinations = [f"{p['key']}: {p['description']}" for p in prompt_infos]
print(destinations)
router_template = MULTI_PROMPT_ROUTER_TEMPLATE.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)

# 初始化语言模型
from langchain_wenxin import Wenxin
WENXIN_APP_Key = "xxx"
WENXIN_APP_SECRET = "xxx"
llm = Wenxin(
    model="ernie-bot-turbo",
    baidu_api_key=WENXIN_APP_Key,
    baidu_secret_key=WENXIN_APP_SECRET
)   

router_chain = LLMRouterChain.from_llm(llm, 
                                       router_prompt,
                                       verbose=True)
print("路由链返回信息:\n {}".format(router_chain("如何为玫瑰浇水?")))

# 构建目标链
from langchain.chains.llm import LLMChain
from langchain.prompts import PromptTemplate
chain_map = {}
for info in prompt_infos:
    prompt = PromptTemplate(template=info['template'], 
                            input_variables=["input"])
    print("目标提示:\n",prompt)
    chain = LLMChain(llm=llm, prompt=prompt,verbose=True)
    chain_map[info["key"]] = chain
    
# 构建默认链
from langchain.chains import ConversationChain
default_chain = ConversationChain(llm=llm, 
                                  verbose=True)

# 构建多提示链
from langchain.chains.router import MultiPromptChain
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=chain_map,
    default_chain=default_chain,
    verbose=False)

print(chain.run("如何为玫瑰浇水?"))
print(chain.run("如何为婚礼场地装饰花朵?"))
print(chain.run("如何考入哈佛大学?"))
● 路由链提示词原理
# 一个简单的引导语句,告诉模型你将给它一个输入,它需要根据这个输入选择最适合的模型提示。
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 >>
# 表示模型的输出应该是一个 Markdown 代码片段,其中包含一个特定格式的 JSON 对象
Return a markdown code snippet with a JSON object formatted to look like:
# 下面的代码块显示了期望的 JSON 结构,
# 其中 destination 是模型选择的提示名称(或“DEFAULT”),而 next_inputs 是可能被修订的原始输入。
```json
{{
    "destination": string \ name of the prompt to use or "DEFAULT"
    "next_inputs": string \ a potentially modified version of the original input
}}

这是一个重要的指导,提醒模型 “destination” 字段的值必须是下面列出的提示之一或是 “DEFAULT”。

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 >>
{input}

输出:限制输出格式为json

<< OUTPUT (must include json at the start of the response) >> << OUTPUT (must end with ) >>
2.1.5 其它类型的链(learning…)
2.2 链的调用
2.2.1 直接调用
● 直接调用的链对象。当我们像函数一样调用一个对象时,它实际上会调用该对象内部实现的 call 方法
● 如果你的提示模板中包含多个变量,在调用链的时候,可以使用字典一次性输入它们。
llm_chain(“玫瑰”)
llm_chain({ ‘flower’: “玫瑰”, ‘season’: “夏季” })
2.2.2 通过 run 方法
● 等价于直接调用 call 函数
llm_chain.run(“玫瑰”)
2.2.3 通过 predict 方法
● 类似于 run,只是输入键被指定为关键字参数而不是 Python 字典。
result = llm_chain.predict(
flower= “玫瑰”,
season= “夏季”
)
2.2.4 通过 apply 方法
● apply 方法允许我们针对输入列表运行链,一次处理多个输入。

apply允许您针对输入列表运行链

input_list = [
{“flower”: “玫瑰”,‘season’: “夏季”},
{“flower”: “百合”,‘season’: “春季”},
{“flower”: “郁金香”,‘season’: “秋季”}
]
result = llm_chain.apply(input_list)
print(result)
2.2.5 通过 generate 方法
● generate 方法类似于 apply,只不过它返回一个 LLMResult 对象,而不是字符串。LLMResult 通常包含模型生成文本过程中的一些相关信息,例如令牌数量、模型名称等。
input_list = [
{“flower”: “玫瑰”,‘season’: “夏季”},
{“flower”: “百合”,‘season’: “春季”},
{“flower”: “郁金香”,‘season’: “秋季”}
]
result = llm_chain.generate(input_list)
print(result)
2.2.6 代码示例

原始字符串模板

prompt = PromptTemplate(
input_variables=[“flower”, “season”],
template=“{flower}在{season}的花语是?”
)

创建模型实例

llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

创建LLMChain

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

result = llm_chain({

‘flower’: “玫瑰”,

‘season’: “夏季”

})

result = llm_chain.predict(

flower= “玫瑰”,

season= “夏季”

)

apply允许您针对输入列表运行链

input_list = [

{“flower”: “玫瑰”,‘season’: “夏季”},

{“flower”: “百合”,‘season’: “春季”},

{“flower”: “郁金香”,‘season’: “秋季”}

]

result = llm_chain.apply(input_list)

input_list = [
{“flower”: “玫瑰”,‘season’: “夏季”},
{“flower”: “百合”,‘season’: “春季”},
{“flower”: “郁金香”,‘season’: “秋季”}
]
result = llm_chain.generate(input_list)

print(result)
三、记忆
3.1 缓冲记忆ConversationBufferMemory
● 在 LangChain 中,通过 ConversationBufferMemory(缓冲记忆)可以实现最简单的记忆机制。
● 每次调用时,ConversationBufferMemory都会将自身存储的所有历史消息传入模板中

初始化语言模型

from langchain_wenxin import Wenxin
WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”
llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationBufferMemory
conversation_chain = ConversationChain(llm=llm, memory=ConversationBufferMemory(), verbose=True)
conversation_chain(“你是谁”)
print(“第一次对话后的记忆:\n”, conversation_chain.memory.buffer)
● 优点:有了记忆机制,LLM 能够了解之前的对话内容,这样简单直接地存储所有内容为 LLM 提供了最大量的信息
● 缺点:输入中包含了更多的 Token(所有的聊天历史记录),这意味着响应时间变慢和更高的成本。而且,当达到 LLM 的令牌数(上下文窗口)限制时,太长的对话无法被记住
3.2 缓冲窗口记忆ConversationBufferWindowMemory
● ConversationBufferWindowMemory 是缓冲窗口记忆,它的思路就是只保存最新最近的几次人类和 AI 的互动。因此,它在之前的“缓冲记忆”基础上增加了一个窗口值 k。这意味着我们只保留一定数量的过去互动,然后“忘记”之前的互动。

初始化语言模型

from langchain_wenxin import Wenxin
WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”
llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferWindowMemory

设置 k=1,这意味着窗口只会记住与 AI 之间的最新的互动,即只保留上一次的人类回应和 AI 的回应。

conversation_chain = ConversationChain(llm=llm, memory=ConversationBufferWindowMemory(k=1), verbose=True)
print(conversation_chain(“我姐姐明天要过生日,我需要一束生日花束。”))
● 这种方法不适合记住遥远的互动,但它非常擅长限制使用的 Token 数量
3.3 对话总结记忆ConversationSummaryMemory
● ConversationSummaryMemory(对话总结记忆)的思路就是将对话历史进行汇总,然后再传递给 {history} 参数。这种方法旨在通过对之前的对话进行汇总来避免过度使用 Token。
● 核心特点:
a. 汇总对话:此方法不是保存整个对话历史,而是每次新的互动发生时对其进行汇总,然后将其添加到之前所有互动的“运行汇总”中。
b. 使用 LLM 进行汇总:该汇总功能由另一个 LLM 驱动,这意味着对话的汇总实际上是由 AI 自己进行的。
c. 适合长对话:对于长对话,此方法的优势尤为明显。虽然最初使用的 Token 数量较多,但随着对话的进展,汇总方法的增长速度会减慢。但是常规的缓冲内存模型会继续线性增长。
注意:帮我们总结对话的 LLM,和用来回答问题的 LLM,可以是同一个大模型,也可以是不同的大模型。

初始化语言模型

from langchain_wenxin import Wenxin
WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”
llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

from langchain.chains import ConversationChain
from langchain.memory import ConversationSummaryMemory
conversation_chain = ConversationChain(llm=llm, memory=ConversationSummaryMemory(llm=llm))
print(conversation_chain(“我姐姐明天要过生日,我需要一束生日花束。”))

print(“第一次对话后的记忆:\n”, conversation_chain.memory.buffer)

print(conversation_chain(“她喜欢粉色玫瑰,颜色是粉色的。”))
print(“第二次对话后的记忆:\n”, conversation_chain.memory.buffer)
● 优点:对于长对话,可以减少使用的 Token 数量,因此可以记录更多轮的对话信息,使用起来也直观易懂
● 缺点:
a. 对于较短的对话,可能会导致更高的 Token 使用
b. 对话历史的记忆完全依赖于中间汇总 LLM 的能力,还需要为汇总 LLM 使用 Token,这增加了成本,且并不限制对话长度
c. 总结的过程中并没有区分近期的对话和长期的对话(通常情况下近期的对话更重要)
3.4 对话总结缓冲记忆ConversationSummaryBufferMemory
● 结合了ConversationSummaryMemory 和 ConversationBufferWindowMemory 的特点。这种模型旨在在对话中总结早期的互动,同时尽量保留最近互动中的原始内容
● 它是通过 max_token_limit 这个参数做到这一点的。当最新的对话文字长度在 max_token_limit 限制之内的时候,LangChain 会记忆原始对话内容;当对话文字超出了这个参数的长度,那么模型就会把所有超过预设长度的内容进行总结,以节省 Token 数量。

初始化语言模型

from langchain_wenxin import Wenxin
WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”
llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

from langchain.chains import ConversationChain
from langchain.chains.conversation.memory import ConversationSummaryBufferMemory

初始化对话链

conversation_chain = ConversationChain(
llm=llm,
memory=ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=300))
print(conversation_chain(“我姐姐明天要过生日,我需要一束生日花束。”))

print(“第一次对话后的记忆:\n”, conversation_chain.memory.buffer)

print(conversation_chain(“她喜欢粉色玫瑰,颜色是粉色的。”))
print(“第二次对话后的记忆:\n”, conversation_chain.memory.buffer)

print(conversation_chain(“我又来了,还记得我昨天为什么要来买花吗?”))
● ConversationSummaryBufferMemory 的优势是通过总结可以回忆起较早的互动,而且有缓冲区确保我们不会错过最近的互动信息。当然,对于较短的对话,ConversationSummaryBufferMemory 也会增加 Token 数量。
3.5 总结

  1. 四种记忆机制总结对比:

  2. 当对话轮次逐渐增加时,各种记忆机制对 Token 的消耗数量(估算)

四、代理(Agent)
4.1 ReAct 框架

  1. 解决一个问题的过程可以分为3步:观察环境 - 进行思考 - 采取行动。再进一步进行简化,就变成了推理 - 行动
    ○ 在推理阶段(Reasoning):模型对当前环境和状态进行观察,并生成推理轨迹,从而使模型能够诱导、跟踪和更新操作计划,甚至处理异常情况。
    ○ 在行动阶段(Acting):模型会采取下一步的行动,如与外部源(如知识库或环境)进行交互并收集信息,或给出最终答案。
  2. ReAct 框架的核心原理:大语言模型可以通过生成推理痕迹和任务特定行动来实现更大的协同作用。
  3. ReAct 框架会提示 LLMs 为任务生成推理轨迹(在chain中,推理估计由我们自己传递给大模型)和操作,这使得代理能系统地执行动态推理来创建、维护和调整操作计划,同时还支持与外部环境(例如 Google 搜索、Wikipedia)的交互,以将额外信息合并到推理中。
  4. Agent可以看作ReAct 框架的实现。LangChain 正是通过 Agent 类,将 ReAct 框架进行了完美封装和实现,这一下子就赋予了大模型极大的自主性(Autonomy),你的大模型现在从一个仅仅可以通过自己内部知识进行对话聊天的 Bot,飞升为了一个有手有脚能使用工具的智能代理。
  5. LangChain 中的“代理”和“链”的差异:在链中,一系列操作被硬编码(在代码中);而在代理中,语言模型被用作推理引擎来确定要采取哪些操作以及按什么顺序执行这些操作。

4.2 ZERO_SHOT_REACT_DESCRIPTION代理的使用
from langchain.agents import load_tools
from langchain.agents import initialize_agent
from langchain.agents import AgentType
from langchain_wenxin import Wenxin

初始化语言模型

WENXIN_APP_Key = “xxx”
WENXIN_APP_SECRET = “xxx”
llm = Wenxin(
model=“ernie-bot-turbo”,
baidu_api_key=WENXIN_APP_Key,
baidu_secret_key=WENXIN_APP_SECRET
)

百度搜索引擎工具

设置OpenAI和SERPAPI的API密钥

import os
os.environ[“SERPAPI_API_KEY”] = ‘SERPAPI_API_KEY’

serpapi(调用 Google 搜索引擎的工具)以及 llm-math(通过 LLM 进行数学计算的工具)

tools = load_tools([“serpapi”, “llm-math”], llm=llm)

agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=True)
agent.run(“目前市场上玫瑰花的平均价格是多少?如果我在此基础上加价15%卖出,应该如何定价?”)
大模型输出的思考与行动轨迹如下:

可以看到,ZERO_SHOT_REACT_DESCRIPTION 类型的智能代理在 LangChain 中,自动形成了一个完善的思考与行动链条,而且给出了正确的答案。每一步的详细解释如下:

4.3 AgentExcutor 的运行机制
4.3.1 Agent 的关键组件
在 LangChain 的代理中,有这样几个关键组件。

  1. 代理(Agent):这个类决定下一步执行什么操作。它由一个语言模型和一个提示(prompt)驱动。提示可能包含代理的性格(也就是给它分配角色,让它以特定方式进行响应)、任务的背景(用于给它提供更多任务类型的上下文)以及用于激发更好推理能力的提示策略(例如 ReAct)。LangChain 中包含很多种不同类型的代理
  2. 工具(Tools):工具是代理调用的函数。这里有两个重要的考虑因素:一是让代理能访问到正确的工具,二是以最有帮助的方式描述这些工具。如果你没有给代理提供正确的工具,它将无法完成任务。如果你没有正确地描述工具,代理将不知道如何使用它们。LangChain 提供了一系列的工具,同时你也可以定义自己的工具。
  3. 工具包(Toolkits):工具包是一组用于完成特定目标的彼此相关的工具,每个工具包中包含多个工具。比如 LangChain 的 Office365 工具包中就包含连接 Outlook、读取邮件列表、发送邮件等一系列工具。当然 LangChain 中还有很多其他工具包供你使用。
  4. 代理执行器(AgentExecutor):代理执行器是代理的运行环境,它调用代理并执行代理选择的操作。执行器也负责处理多种复杂情况,包括处理代理选择了不存在的工具的情况、处理工具出错的情况、处理代理产生的无法解析成工具调用的输出的情况,以及在代理决策和工具调用进行观察和日志记录。
    总的来说,代理就是一种用语言模型做出决策、调用工具来执行具体操作的系统。通过设定代理的性格、背景以及工具的描述,你可以定制代理的行为,使其能够根据输入的文本做出理解和推理,从而实现自动化的任务处理。而代理执行器(AgentExecutor)就是上述机制得以实现的引擎。
    4.3.2 AgentExcutor 的运行机制
  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值