前言
我个人理解,黄老师课程的基础部分主要讲了六大板块:Model、Prompt、chain、memory、agent、toolkit。这六大板块刚好可以分为三类,每一类内部有较强的关联性,三类自前向后功能逐渐复杂。
基础类:Model、Prompt;这两个是大模型最基本的部分,有模型,有提示词,就可以输出一个结果。其他的如memory、agent等都是基于多样功能的需求,对这二者进行扩展。
进阶类:chain、memory;这二者是对大模型调用的扩展。chain重在复杂功能的实现,每个链都是一个功能,通过chain可以将简单的输入提示词、输出结果进行扩展,完成多样化的功能。memory则是侧重内容的延续,以memory作为新输出的参考。
高级类:agent、toolkit;这两个本质上是讲了同一件事:外部工具的调用。如果只有前四者,哪怕玩出花来,也不可能无中生有,输出大模型所不知道的知识;而这两者就是通过对外部工具进行访问,来获取额外的信息作为参考,以丰富输出的内容。
为了方便理解和代码使用,我在总结的时候把各部分内部所涉及的内容单独提取出来,形成模板。各个模板组合,就能形成一个完整项目代码。
1. Model
1.1. Input
我理解的input,就是调用大模型的过程。以gpt为例,调用大模型可以直接通过官方渠道即openai库调用,也可以通过Langchain转一手调用。
方法一:使用openai库直接调用大模型
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="**调用的大模型**",
messages=[
{"role": "system", "content": "##模型角色扮演##"},
{"role": "user", "content": "##提示内容##"},
],
temperature=0.8,
max_tokens=60
)
print(response.choices[0].message.content.strip())
方法二:通过Langchain调用大模型
from langchain_openai import ChatOpenAI
llm=ChatOpenAI(model="**调用的大模型**",temperature=0.7,max_tokens=300)
response=llm.invoke("##提示内容##")
print(response.content)
或者
from langchain_openai import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage
llm = ChatOpenAI(model="**调用的大模型**", temperature=0.7, max_tokens=300)
messages = [
SystemMessage(content="##模型角色扮演##"),
HumanMessage(content="##提示内容##"),
]
response = llm.invoke(messages)
print(response.content)
涉及到Langchain的大模型调用最好使用方法二,其作用获取一个名为llm的实例,并将提示词输入其中。至于提示词的构建方法、输入方式就属于其他部分的内容了。
1.2. Output
1)OutputParser(输出解析器)
输出解析器通过在提示词中限定输出结果的格式,结合输出解析函数.parse来实现对文本结果的解析与格式转换。
response_schemas内包含了输出格式要求,依据要求创建输出解析器output_parser。通过输出解析器获取格式指示format_instructions。格式指示本质是一段提示词,限定输出的格式,与response_schemas相对应。
# 导入结构化输出解析器和ResponseSchema
from langchain.output_parsers import StructuredOutputParser, ResponseSchema
# 定义我们想要接收的响应模式(description、reason可以替换为其他想要的内容)
response_schemas = [
ResponseSchema(name="description", description="##描述文案##"),
ResponseSchema(name="reason", description="##为什么要这样写这个文案##")
]
# 创建输出解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
# 获取格式指示
format_instructions = output_parser.get_format_instructions()
将格式指示format_instructions到提示词prompt上,方便解析。
# 导入LangChain中的提示模板
from langchain.prompts import PromptTemplate
# 创建原始提示模板
template = """##提示内容##
{format_instructions}"""
# 根据原始模板创建提示,同时在提示中加入输出解析器的说明
prompt = PromptTemplate.from_template(template,
partial_variables={"format_instructions": format_instructions})
调用大模型,具体步骤见Input。获取输出结果output。然后按下述步骤对输出结果进行解析。
# 解析模型的输出
parsed_output = output_parser.parse(output)
2)PydanticParser
Pydantic (JSON) 解析器可以将输出结果转化为json格式,方便输出数据的存储、使用和转化。
- 定义我们想要接收的数据格式
TypeDescription
# 定义我们想要接收的数据格式
from pydantic.v1 import BaseModel, Field
class TypeDescription(BaseModel):
feature1: str = Field(description="##描述内容##")
feature2: int = Field(description="##描述内容##")
- 创建Pydantic解析器
output_parser
from langchain.output_parsers import PydanticOutputParser
output_parser = PydanticOutputParser(pydantic_object=TypeDescription)
# 获取输出格式指示
format_instructions = output_parser.get_format_instructions()
- 使用解析器解析模型llm的输出
output,获得解析结果parsed_output
parse_output = output_parser.parse(output)
3)OutputFixingParser(自动修复解析器)
自动修复解析器可以修复输出json数据的格式错误。本质上是回用大模型纠错:如果调用原有解析器正确则不管,失败则将错误的输出重新传递给大模型,让大模型来检查、修改。
- 创建一个PydanticParser,名为
output_parser,方法见PydanticParser部分 - 创建OutputFixingParser;该解析器能够纠正格式不正确的输出。
from langchain.output_parsers import OutputFixingParser
fix_parser = OutputFixingParser.from_llm(
parser=output_parser, ##
llm="**调用的大模型**",
)
#修复解析结果
parse_result = fix_parser.parse(response)
4)RetryWithErrorOutputParser(重试解析器)
重试解析器可以帮助我们利用大模型的推理能力,根据原始提示找回相关信息。
- 创建一个PydanticParser,名为
output_parser,方法见PydanticParser部分 - 创建RetryWithErrorOutputParser;它会尝试再次提问来得到一个正确的输出。
from langchain.output_parsers import RetryWithErrorOutputParser
retry_parser = RetryWithErrorOutputParser.from_llm(
parser=output_parser, llm="**调用的大模型**"
)
#尝试重新获得输出
parse_result = retry_parser.parse_with_prompt(output, prompt)
2. Prompt
由于Prompt 部分涉及各种模板、提示词的转换,在此先统一下各个变量的定义,方便后续理解。
template:原始提示模板。
prompt_template:转换后的提示模板。
prompt:加入变量内容的提示词。
2.1. 提示模板的类型
langchain提示模板需要使用提示词模板都位于langchain.prompts.promt下,可以使用from langchain import xxx 直接导入,也可以使用from langchain.prompts import xxx,或者使用from langchain.prompts.prompt import xxx,三种方法导入都可以。第一种方法比较简洁,第二、三种方法可读性强且指明来源。
1)PromptTemplate(常规提示模板)
导入langchain提示模板类,构建外部模板。
form langchain import PromptTemplate
template = """
##提示内容与变量{variable}##
"""
然后有两种方法构建langchain的提示模板。
方法一:
prompt_template=PromptTemplate.from_template(template)
prompt=prompt_template.format(variable="##变量##")
方法二:
# 导入聊天消息类模板
prompt=PromptTemplate(
input_variables=["##变量1##", "##变量2##"]
template=template
)
2) ChatPromptTemplate(聊天提示模板)
想要聊天,就要有聊天对象。通过SystemMessagePromptTemplate构建AI聊天对象,再通过HumanMessagePromptTemplate构建人类聊天对象。彼此的沟通通过ChatPromptTemplate来实现。
导入聊天消息类模板
from langchain.prompts import (
ChatPromptTemplate,
SystemMessagePromptTemplate,
HumanMessagePromptTemplate,
)
构建聊天模板。系统角色和人类角色有各自的提示词,通过对应的类方法转为langchain提示模板后,使用ChatPromptTemplate将他们组合到一起,再通过.format_prompt()方法将变量组合进去。
system_template = "##输入系统角色的聊天内容与变量{variable1}##"
system_prompt_template = SystemMessagePromptTemplate.from_template(system_template)
human_template = "##输入人类角色的聊天内容与变量{variable2}##"
human_prompt_template = HumanMessagePromptTemplate.from_template(human_template)
prompt_template = ChatPromptTemplate.from_messages(
[system_prompt_template, human_prompt_template]
)
prompt = prompt_template.format_prompt(
variable1="##变量1##", variable2="##变量2##"
).to_messages()
获取整合后的提示词,输入大模型中。后面调用大模型与Input部分一样。
3)FewShotPromptTemplate(少样本提示模板)
- 少样本提示要有样本,即需要先创建一些示例
sample
sample=[
{"feature1":"示例A特征1","feature2":"示例A特征2",},
{"feature1":"示例B特征1","feature2":"示例B特征2",},
]
- 构建常规提示模板
prompt_template,对特征进行描述。
prompt_template = PromptTemplate(
input_variables=["feature1", "feature2"],
template="##特征1{feature1}与特征2{feature2}的相关描述##",
)
- 使用少样本提示模板
FewShotPromptTemplate,将常规提示模板prompt_template与示例sample组合到一起。
from langchain.prompts.few_shot import FewShotPromptTemplate
few_shot_prompt = FewShotPromptTemplate(
examples=samples,
example_prompt=prompt_template,
prefix='##通用要求##', #提示前缀,对每个样例都适用,可不要。
suffix="##与sample结构类似的输入内容与变量{variable}##", #提示后缀,对变量的要求
input_variables=["variable"],
)
prompt=few_shot_prompt.format(variable="##变量##"")
具体调用大模型输出见1.1.Input部分。
2.2. 思维连(COT)、思维树(TOT)
我理解的思维链、思维树,就是通过丰富提示词的内容,告诉大模型第一步、第二步、第三步该怎么做,让大模型按照步骤运行下去。这部分内容主要是在提示词内容的编写上,代码方面并没有太多模板化的构造。