上文我们介绍过LangChain的基本框架和其中包含的主要模块。从今天开始,我们开始学习各个模块,深入了解,同时进行相应实战练习。
本文学习 LangChain 中的 模型 I/O 封装模块。
0. 模块介绍
任何AI大模型应用程序的核心元素都是大模型。LangChain提供了与各种大模型接口进行交互的封装。
这张图生动地展现了LangChain对于I/O(输入输出)的封装。
- 首先是 Format 部分,这部分的作用是组装用户输入和Prompt模板,作为大模型的输入。
- 然后是 Predict 部分,这部分就是调用大模型接口获得结果
- 最后是 Parse 部分,这部分的作用是对大模型的结果进行解析,将大模型的输出转换到要求的格式(如json)上,或者对输出进行校验等等
1. Format部分:Prompt模板封装
1.1 PromptTemplate:创建一个字符串类型的Prompt
PromptTemplate 可以在模板中自定义变量
python代码解读复制代码import os
# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
from langchain_openai import ChatOpenAI
llm = ChatOpenAI() # 默认是gpt-3.5-turbo
prompt_template = """
我的名字叫【{name}】,我的个人介绍是【{description}】。
请根据我的名字和介绍,帮我想一段有吸引力的自我介绍的句子,以此来吸引读者关注和点赞我的账号。
"""
from langchain.prompts import PromptTemplate
template = PromptTemplate.from_template(prompt_template)
print(template.input_variables)
prompt = template.format(name='同学小张', description='热爱AI,持续学习,持续干货输出')
print(prompt)
response = llm.invoke(prompt)
print(response.content)
1.2 ChatPromptTemplate:创建一个Prompt的Message数组
python代码解读复制代码...... 省略llm的引入代码,可参考前文 ......
from langchain.prompts import ChatPromptTemplate
from langchain.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate
template = ChatPromptTemplate.from_messages(
[
SystemMessagePromptTemplate.from_template("你是【{name}】的个人助手,你需要根据用户输入,来替用户生成一段有吸引力的自我介绍的句子,以此来吸引读者关注和点赞用户的账号。"),
HumanMessagePromptTemplate.from_template("{description}"),
]
)
prompt = template.format(name="同学小张", description="热爱AI,持续学习,持续干货输出")
print(prompt)
response = llm.invoke(prompt)
print(response.content)
运行后输出结果如下,可以看到Prompt中带入了 System、Human这样的角色名,区分Prompt的来源。
1.3 FewShotPromptTemplate:给例子的Prompt模板
在之前文章Prompt优化中,我们提到Prompt中给几个例子可以让大模型更好地生成正确的结果。这个模板就是给例子的。
python代码解读复制代码from langchain.prompts import PromptTemplate
from langchain.prompts.few_shot import FewShotPromptTemplate
#例子(few-shot)
examples = [
{
"input": "北京天气怎么样",
"output" : "北京市"
},
{
"input": "南京下雨吗",
"output" : "南京市"
},
{
"input": "江城热吗",
"output" : "武汉市"
}
]
#例子拼装的格式
example_prompt = PromptTemplate(input_variables=["input", "output"], template="Input: {input}\nOutput: {output}")
#Prompt模板
prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="Input: {input}\nOutput:",
input_variables=["input"]
)
prompt = prompt.format(input="羊城多少度")
print("===Prompt===")
print(prompt)
response = llm.invoke(prompt)
print("===Response===")
print(response)
以上代码为FewShotPromptTemplate的使用示例,总结为以下关键点:
- 例子(few-shot)用数组表示:examples
- 用PromptTemplate表示examples中的格式:Input后跟着output,注意:input_variables中的变量与examples中每个元素的key保持一致。
- 通过 FewShotPromptTemplate 将以上元素组合起来
- 同时传入 suffix 参数,该参数是接收用户的输入,组装提问的prompt模板。
- 然后input_variables表示用户输入的参数变量名
运行结果如下:红框内是通过FewShotPromptTemplate 将examples、example_prompt、suffix组合起来后最终的给大模型的Prompt。
1.4 从文件加载Prompt模板
我们还可以将Prompt模板单独存放在一个文件中,在程序运行时通过加载文件来导入Prompt模板。
这种方式很好地实现了 Prompt 和程序的分离,使得两者可以分别单独修改。甚至你可以将Prompt单独放在一个线上服务或数据库中,单独维护。
下面来看怎么实现。
1.4.1 Prompt模板文件格式
Prompt模板文件支持两种格式:yaml格式和json格式
- yaml格式:
python代码解读复制代码 _type: prompt
input_variables:
["name", "description"]
template:
我的名字叫【{name}】,我的个人介绍是【{description}】。\n 请根据我的名字和介绍,帮我想一段有吸引力的自我介绍,以此来吸引读者关注和点赞我的账号。
- json格式
python代码解读复制代码{
"_type": "prompt",
"input_variables": ["name", "description"],
"template": "我的名字叫【{name}】,我的个人介绍是【{description}】。\n 请根据我的名字和介绍,帮我想一段有吸引力的自我介绍,以此来吸引读者关注和点赞我的账号。"
}
1.4.2 加载文件
使用 LangChain的load_prompt进行加载。
python代码解读复制代码from langchain.prompts import load_prompt
prompt = load_prompt("D:\GitHub\LEARN_LLM\langchain\langchain_prompt_file_test.json")
prompt_str = prompt.format(name="同学小张", description="热爱AI,持续学习,持续干货输出")
print(prompt_str)
response = llm.invoke(prompt_str)
print(f"\n{response}")
1.4.3 更进一步:文件套文件
LangChain也允许你在Prompt文件中再套Prompt文件:将文件中的template字段单独放一个txt文件使用。拆分后文件如下:
- prompt_template_test.txt
python
代码解读
复制代码我的名字叫【{name}】,我的个人介绍是【{description}】。\n 请根据我的名字和介绍,帮我想一段有吸引力的自我介绍,以此来吸引读者关注和点赞我的账号。
- langchain_prompt_file_test.json
python代码解读复制代码{
"_type": "prompt",
"input_variables": ["name", "description"],
"template_path": "D:\\GitHub\\LEARN_LLM\\langchain\\prompt_template_test.txt"
}
注意:json里面的
template
字段换成了template_path
字段
1.5 其它Prompt模板
还有一些其它的Prompt模板,就不详细介绍了,都差不多。
- FewShotChatMessagePromptTemplate
- ChatMessagePromptTemplate:可以自定义Prompt的角色名,如之前的“System”、“AI”、“Human”都是角色。
- …
总结:把Prompt模板看作带有参数的函数
2. Predict部分:大模型接口封装
这部分主要看下LangChain对大模型的两种封装:llm 和 chat_model。
python代码解读复制代码from langchain_openai import ChatOpenAI
from langchain_openai import OpenAI
llm = OpenAI()
chat_model = ChatOpenAI()
from langchain.schema import HumanMessage
text = "What would be a good company name for a company that makes colorful socks?"
messages = [HumanMessage(content=text)]
llm.invoke(text)
# >> Feetful of Fun
chat_model.invoke(messages)
# >> AIMessage(content="Socks O'Color")
可以看到 llm 和 chat_model 的区别,一个输出字符串,一个输出message。
3. Parse部分:输出结果校验的封装
LangChain封装了一些对于大模型输出结果的约定和校验能力。下面以PydanticOutputParser
为例演示一下Parse部分的使用方法和作用。
3.1 使用步骤
(1)首先定义一个你期望返回的数据结构
下面代码中,我们定义了一个Joke数据结构,它里面包含的信息有:
- 两个变量名:setup 和 punchline,大模型的返回需要以这两个名称作为key来组织答案
- 一个自定义的校验函数:
question_ends_with_question_mark
,校验信息是否符合你的要求,如果不符合,则报错。
@validator("setup")
表示校验结果中的setup字段。也就是说,首先大模型回复的答案中,首先必须是个json结构,才能解析出setup的内容。其次,json数据结构中必须有setup的字段。最后,setup的内容必须符合函数中定义的规则。这样才算通过,否则报错。
python代码解读复制代码from langchain_core.pydantic_v1 import BaseModel, Field, validator
# 定义你期望的数据结构
class Joke(BaseModel):
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")
# 使用Pydantic添加自定义的校验逻辑,如下为检测内容最后一个字符是否为问号,不为问号则提示错误.
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field
(2)生成一个解析器的实例
python
代码解读
复制代码parser = PydanticOutputParser(pydantic_object=Joke)
(3)生成 Prompt 模板
在这个Prompt模板中:
- 通过
template
指定Prompt的框架 input_variables
指定用户输入的信息放到这个变量名中partial_variables
是提前填充部分Prompt变量,这里通过parser.get_format_instructions()
获取PydanticOutputParser
中封住好的Prompt部分。
python代码解读复制代码prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
看下parser.get_format_instructions()
的内容:
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:
{"properties": {"setup": {"title": "Setup", "description": "question to set up a joke", "type": "string"}, "punchline": {"title": "Punchline", "description": "answer to resolve the joke", "type": "string"}}, "required": ["setup", "punchline"]}
可以看到,LangChain内部将咱们上面定义的Joke数据结构填到了里面,并要求大模型输出json结构。
(4)加上用户的提问,调用大模型获取回复
python代码解读复制代码prompt_str = prompt.format(query="Tell me a joke.")
response = llm.invoke(prompt_str)
完整Prompt如下:
运行结果如下:
(5)校验输出结果是否符合要求
python
代码解读
复制代码parser_result = parser.invoke(response) ## 调用parser的invoke,校验结果是否符合要求
上面的结果明显符合要求,最终输出如下:
python
代码解读
复制代码#>> setup="Why don't scientists trust atoms?" punchline='Because they make up everything!'
3.2 不符合要求的情况
为了看一下不符合要求时会发生什么,我在大模型返回后手动改了下结果,让它不符合要求(要求是问句结尾必须是问号,下面我将问号删掉了)。
运行结果:报错了
如果大模型返回的结果不是json结构,也会报错:
3.3 不符合要求怎么办?Auto-Fixing Parser帮你自动修复错误
基本用法如下:
python代码解读复制代码## 1. 引入OutputFixingParser
from langchain.output_parsers import OutputFixingParser
## 2. 使用之前的parser和llm,构建一个OutputFixingParser实例
new_parser = OutputFixingParser.from_llm(parser=parser, llm=llm)
## 3. 用OutputFixingParser自动修复并解析
parser_result = new_parser.parse(response.content)
print("===重新解析结果===")
print(parser_result)
为了展示它的效果,我还是手动将结果改错了。
输出结果如下:可以看到重新解析后结果正确了。
重新解析为什么就正确了?其实是OutputFixingParser内部又重新调用了一遍大模型。
3.4 完整代码
python代码解读复制代码import os
# 加载 .env 到环境变量
from dotenv import load_dotenv, find_dotenv
_ = load_dotenv(find_dotenv())
from langchain_openai import ChatOpenAI
llm = ChatOpenAI() # 默认是gpt-3.5-turbo
def output_parse_test():
from langchain.output_parsers import PydanticOutputParser
from langchain_core.pydantic_v1 import BaseModel, Field, validator
from langchain.prompts import PromptTemplate
# 定义你期望的数据结构
class Joke(BaseModel):
setup: str = Field(description="question to set up a joke")
punchline: str = Field(description="answer to resolve the joke")
# 使用Pydantic添加自定义的校验逻辑,如下为检测内容最后一个字符是否为问号,不为问号则提示错误.
@validator("setup")
def question_ends_with_question_mark(cls, field):
if field[-1] != "?":
raise ValueError("Badly formed question!")
return field
# 生成一个解析器的实例
parser = PydanticOutputParser(pydantic_object=Joke)
# 生成 Prompt 模板
prompt = PromptTemplate(
template="Answer the user query.\n{format_instructions}\n{query}\n",
input_variables=["query"],
partial_variables={"format_instructions": parser.get_format_instructions()},
)
print(f"\n{parser.get_format_instructions()}")
prompt_str = prompt.format(query="Tell me a joke.")
print(prompt_str)
response = llm.invoke(prompt_str)
print(f"\n{response.content}")
# response.content = response.content.replace("?", "") ## 认为改错结果,测试后面的OutputFixingParser
try:
parser_result = parser.invoke(response)
print(f"\n{parser_result}")
except Exception as e:
print("===出现异常===")
print(e)
## 1. 引入OutputFixingParser
from langchain.output_parsers import OutputFixingParser
## 2. 使用之前的parser和llm,构建一个OutputFixingParser实例
new_parser = OutputFixingParser.from_llm(parser=parser, llm=llm)
## 3. 用OutputFixingParser自动修复并解析
parser_result = new_parser.parse(response.content)
print("===重新解析结果===")
print(parser_result)
output_parse_test()
关于更多 OutputParser 的说明,可以看官方文档:python.langchain.com/docs/module…
4. 总结
本文我们全面学习了LangChain的模型 I/O 封装模块。
- LangChain 提供了各种 PromptTemplate 类,可以自定义带变量的模板
- LangChain 统一封装了各种模型的调用接口,包括llm型和chat_model型两种,区别见上文。
- LangChain 提供了一系列输出解析器,用于将大模型的输出解析成结构化对象;额外带有自动修复功能。
大模型资源分享
针对所有自学遇到困难的同学,我为大家系统梳理了大模型学习的脉络,并且分享这份LLM大模型资料:其中包括LLM大模型书籍、640套大模型行业报告、LLM大模型学习视频、LLM大模型学习路线、开源大模型学习教程等。😝有需要的小伙伴,可以扫描下方二维码免费领取↓↓↓
一、全套 AGI 大模型学习路线
AI 大模型时代的精彩学习之旅:从根基铸就到前沿探索,牢牢掌握人工智能核心技能!
二、640 套 AI 大模型报告合集
此套涵盖 640 份报告的精彩合集,全面涉及 AI 大模型的理论研究、技术实现以及行业应用等诸多方面。无论你是科研工作者、工程师,还是对 AI 大模型满怀热忱的爱好者,这套报告合集都将为你呈上宝贵的信息与深刻的启示。
三、AI 大模型经典 PDF 书籍
伴随人工智能技术的迅猛发展,AI 大模型已然成为当今科技领域的一大热点。这些大型预训练模型,诸如 GPT-3、BERT、XLNet 等,凭借其强大的语言理解与生成能力,正在重塑我们对人工智能的认知。而以下这些 PDF 书籍无疑是极为出色的学习资源。
阶段 1:AI 大模型时代的基础认知
-
目标:深入洞悉 AI 大模型的基本概念、发展历程以及核心原理。
-
内容
:
- L1.1 人工智能概述与大模型起源探寻。
- L1.2 大模型与通用人工智能的紧密关联。
- L1.3 GPT 模型的辉煌发展历程。
- L1.4 模型工程解析。
- L1.4.1 知识大模型阐释。
- L1.4.2 生产大模型剖析。
- L1.4.3 模型工程方法论阐述。
- L1.4.4 模型工程实践展示。
- L1.5 GPT 应用案例分享。
阶段 2:AI 大模型 API 应用开发工程
-
目标:熟练掌握 AI 大模型 API 的运用与开发,以及相关编程技能。
-
内容
:- L2.1 API 接口详解。
- L2.1.1 OpenAI API 接口解读。
- L2.1.2 Python 接口接入指南。
- L2.1.3 BOT 工具类框架介绍。
- L2.1.4 代码示例呈现。
- L2.2 Prompt 框架阐释。
- L2.2.1 何为 Prompt。
- L2.2.2 Prompt 框架应用现状分析。
- L2.2.3 基于 GPTAS 的 Prompt 框架剖析。
- L2.2.4 Prompt 框架与 Thought 的关联探讨。
- L2.2.5 Prompt 框架与提示词的深入解读。
- L2.3 流水线工程阐述。
- L2.3.1 流水线工程的概念解析。
- L2.3.2 流水线工程的优势展现。
- L2.3.3 流水线工程的应用场景探索。
- L2.4 总结与展望。
阶段 3:AI 大模型应用架构实践
-
目标:深刻理解 AI 大模型的应用架构,并能够实现私有化部署。
-
内容
:- L3.1 Agent 模型框架解读。
- L3.1.1 Agent 模型框架的设计理念阐述。
- L3.1.2 Agent 模型框架的核心组件剖析。
- L3.1.3 Agent 模型框架的实现细节展示。
- L3.2 MetaGPT 详解。
- L3.2.1 MetaGPT 的基本概念阐释。
- L3.2.2 MetaGPT 的工作原理剖析。
- L3.2.3 MetaGPT 的应用场景探讨。
- L3.3 ChatGLM 解析。
- L3.3.1 ChatGLM 的特色呈现。
- L3.3.2 ChatGLM 的开发环境介绍。
- L3.3.3 ChatGLM 的使用示例展示。
- L3.4 LLAMA 阐释。
- L3.4.1 LLAMA 的特点剖析。
- L3.4.2 LLAMA 的开发环境说明。
- L3.4.3 LLAMA 的使用示例呈现。
- L3.5 其他大模型介绍。
阶段 4:AI 大模型私有化部署
-
目标:熟练掌握多种 AI 大模型的私有化部署,包括多模态和特定领域模型。
-
内容
:- L4.1 模型私有化部署概述。
- L4.2 模型私有化部署的关键技术解析。
- L4.3 模型私有化部署的实施步骤详解。
- L4.4 模型私有化部署的应用场景探讨。
学习计划:
- 阶段 1:历时 1 至 2 个月,构建起 AI 大模型的基础知识体系。
- 阶段 2:花费 2 至 3 个月,专注于提升 API 应用开发能力。
- 阶段 3:用 3 至 4 个月,深入实践 AI 大模型的应用架构与私有化部署。
- 阶段 4:历经 4 至 5 个月,专注于高级模型的应用与部署。