提起大模型应用开发,大家脑海中首先出现的技术和框架是什么?是python?还是langchain? langchain开发大模型应用的常用框架,基于python开发,langchain至于LLMs就如jquery至于javascript,spring至于java。 langchain简化了大模型应用开发,同时又统一和规范了不同大模型的调用,提供了一些链式组装。让复杂的逻辑变得结构化、易组合、易拓展。
一、langchain
langchain使用场景
- 文档问答助手
- 智能助理开发
- 对话聊天机器人
- 数据分析和洞察生成
- 多模态应用
等等
langchain 安装
我们默认使用conda 安装
conda install langchain
二、langchain之Model I/O
所谓Model I/O,包含了输入提示、调用模型。输入解析,分别对应着PromptTemplate、Model和output Parser
langchain支持的三大类模型
模型的输入/输出和模型的类型有关系。langchain支持的模型有三大类:
- LLMs 非对话模型,输入为文本或者promptValue对象,输出总是返回文本字符串,不支持多轮对话,适合单词文本生成任务
- Chat Models 对话模型,输入为PromptValue或者消息列表,输出的是消息对象,通常是AIMessage.支持多轮对话,适合用作对话系统和交互助手
- Embedding Model 嵌入模型,输入为文本,输出返回浮点数列表
模型调用
我们先来看一下基础的模型调用案例,主要讲对话模型的调用
import os
import dotenv
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
// 模型实例化
chat=ChatOpenAI(model="gpt-4o-mini")
// 模型输入
ms=chat.invoke("你是谁")
// 模型输出ms
print(ms)
我们从代码可以看出模型的调用由三个部分组成:模型对象创建、模型输入、模型输出。我们分这三个部分分别梳理相应的知识点
1. 模型引入及模型对象创建
// 依赖导入
import os
// 读取环境变量的依赖包
import dotenv
// 导入模型核心依赖
from langchain_openai import ChatOpenAI
// 从项目根目录.env文件读取环境变量
dotenv.load_dotenv()
// 模型对象创建
chat=ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=200,
api_key=os.getenv("OPENAI_API_KEY"),
base_url=os.getenv("OPENAI_BASE_URL")
)
模型对象创建的参数
| 参数名称 | 参数描述 |
|---|---|
| model/model_name | 要用的模型名称 |
| base_url | 模型的调用基础路径,不设置时从环境变量OPENAI_BASE_URL中获取 |
| api_key | 模型的调用api_key,从模型平台获取,不设置时从环境变量OPENAI_API_KEY中获取 |
| temperature | 温度,控制生成文本的“随机性”,取值范围为0~1,数字越大,随机性越高。较高的值(如0.9)会生成更多样化的回答,较低的值(如0.3)则生成更确定的回答。 |
| max_tokens | 限制生成文本的最大长度,防止输出过长。tokens是API中的文本单位。不同平台计算方案不同,中文一般一个token=1-1.8汉字,1token=3-4字母 |
| streaming | 布尔值,默认为false,为true时代表流式输出 |
*** 特别说明 base_url和api_key赋值有三种方式
- 直接赋值, 不建议这种方式
chat=ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=200,
api_key="sk-rQEb2bj9s20fFdsh4tZdueKUHxxxx",
base_url="https://api.openai-proxy.org/v1"
)
- 从本地环境变量中获取,一般本地联调使用没问题,如果本地环境变量名称是标准的OPENAI_API_KEY和OPENAI_BASE_URL,可以不用显式配置,langchain会自动从环境变量读取
chat=ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=200,
api_key=os.environ["OPENAI_API_KEY"],
base_url=os.environ['OPENAI_BASE_URL']
)
//也可以省略为
chat=ChatOpenAI(
model="gpt-4o-mini",
temperature=0.7,
max_tokens=200
)
- 从本地环境变量文件.env中获取,这种是
推荐用法
import os
import dotenv
from langchain_openai import ChatOpenAI
//从本地.env的文件中读取变量,并赋值给环境变量OPENAI_API_KEY,OPENAI_BASE_URL,这样创建模型可以不用设置对应值
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat=ChatOpenAI(model="gpt-4o-mini")
ms=chat.invoke("你是谁")
print(ms)
2. 模型输入:模型提示词模板及模型调用方式
PromptTemplate类
PromptTemplate类参数
| 参数名称 | 作用和格式 |
|---|---|
| template | 提示词模板字符串,可以 包含'{name}'格式的占位符 |
| input_variables | 数组列表,指定了模板中使用的变量名称,在调用模板时被替换 |
| partial_variables | 给部分变量设置初始值或者说默认值,这样不需要每次调用都传值 |
- PromptTemplate类的两种实例化方式
方式1:使用构造方法
from langchain_core.prompts import PromptTemplate
pt=PromptTemplate(
template="请介绍{name}的种植方法",
input_variables=['name']
)
方式2:使用from_template
from langchain_core.prompts import PromptTemplate
pt=PromptTemplate.from_template("请介绍{name}的种植方法")
使用from_template方法实例化,相比构造方法,不需要单独设定input_variables,会自动将模板中的占位符作为input_variables
- PromptTemplate实例的两种替换占位符方式
方式1:format()
from langchain_core.prompts import PromptTemplate
pt=PromptTemplate(
template="请介绍{name}的种植方法",
input_variables=['name']
)
ptv=pt.format(name="桂花")
print(type(ptv))
方式2:invoke()
from langchain_core.prompts import PromptTemplate
pt=PromptTemplate(
template="请介绍{name}的种植方法",
input_variables=['name']
)
# pt=PromptTemplate.from_template("请介绍{name}的种植方法以及{tip}")
ptv=pt.invoke({"name":"桂花"})
print(type(ptv))
PromptTemplate实例format()和invoke()有两个差异
- format() 传递的是关键词参数,返回的是字符串
- invoke()传递的是字典类型的参数,返回的是PromptValue类型的数据
ChatPromptTemplate类
ChatPromptTemplate类实例化参数
| 参数 | 参数描述 | ||
|---|---|---|---|
| messages | 形参,是一个列表,列表项支持message实例,元组,元组格式为(role:str,content: str | list[dict] | list[object]) ,role的字符串值如:'system'、'human'、'ai' |
- ChatPromptTemplate类的两种实例化方法
方法1:使用构造方法
from langchain_core.prompts import ChatPromptTemplate
chat=ChatPromptTemplate([
("system","你是一个智能机器人"),
("human","请问如何造一艘飞船?"),
])
print(chat)
方法2:使用from_messages()
from langchain_core.prompts import ChatPromptTemplate
//这种方式支持字符串模板
chat=ChatPromptTemplate.from_messages([
("system","你是一个智能机器人"),
("human","请问如何造一艘飞船?"),
])
//这种方式支持字符串模板
chat=ChatPromptTemplate.from_messages([
("system","你是一个智能机器人{name}"),
("human","请问如何造一艘飞船?"),
])
from langchain_core.messages import SystemMessage,HumanMessage
//这种方式不支持字符串模板
chat=ChatPromptTemplate.from_messages([
SystemMessage("你是一个智能机器人"),
HumanMessage("请问如何造一艘飞船?"),
])
// 这种方式支持字符串模板
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate
chat=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("你是一个智能机器人{name}"),
HumanMessagePromptTemplate.from_template("请问如何造一艘飞船?")
])
print(chat.format(name="小明"))
- ChatPromptTemplate模板实例调用的几种方式
方法1:format(),传递关键字参数,调用方式跟PromptTemplate的方法一致,返回字符串
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate
chat=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("你是一个智能机器人{name}"),
HumanMessagePromptTemplate.from_template("请问如何造一艘飞船?")
])
print(chat.format(name="小明"))
方法2:invoke(),传递字典桉树,返回PromptValue类型
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate
chat=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("你是一个智能机器人{name}"),
HumanMessagePromptTemplate.from_template("请问如何造一艘飞船?")
])
print(type(chat.invoke({"name":"小明"})))
方法3:format_messages(),传参方式跟format一样,传递关键字参数,返回的是消息列表,这是推荐用法
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate
chat=ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template("你是一个智能机器人{name}"),
HumanMessagePromptTemplate.from_template("请问如何造一艘飞船?")
])
print(chat.format_messages(name="小明"))
少量样本示例的提示词模板
少量样本示例的提示词模板,在构建提示词时,通过构建一个少量示例列表去进一步格式化提示词,用于指导大模型模型生成。基于非对话模型和对话模型,可以分别使用FewShotPromptTemplate或者FewShotChatMessagePromptTemplate FewShotPromptTemplate
参数说明
| 参数名 | 参数描述 |
|---|---|
| examples | 示例列表,对应示例问题和对应示例回答 |
| example_prompt | 示例的提示词模板 |
| suffix | 提示词模板,会被放在示例后面作为需要模型回答的新问题 |
| input_variables | 提示词模板的占位符变量 |
import os
import dotenv
from langchain_core.prompts import PromptTemplate
from langchain_core.prompts.few_shot import FewShotPromptTemplate
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
examples = [
{
"input":'西瓜',
"output":'水果'
},
{
"input":'茄子',
"output":'蔬菜'
},
{
"input":'蓝莓',
"output":'水果'
},
{
"input":'包菜',
"output":'蔬菜'
},
{
"input":'牛肉',
"output":'肉类'
},
{
"input":'三文鱼',
"output":'海鲜'
},
{
"input":'马鲛鱼',
"output":'海鲜'
},
]
example_prompt = PromptTemplate.from_template("输入:{input}\n输出:{output}")
few_shot = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
suffix="输入:{input}\n输出:",
input_variables=['input']
)
prompt = few_shot.format(input="鲍鱼")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
print(chat.invoke(prompt))
这个示例中,通过少量示例的指导,大模型会输出“海鲜” 。 少量示例的作用在于指引大模型应该按哪个方向去回答相关的问题 FewShotChatMessagePromptTemplate
import os
import dotenv
from langchain_core.prompts import ChatPromptTemplate,FewShotChatMessagePromptTemplate
from langchain_openai import ChatOpenAI
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
examples = [
{
"input":'西瓜',
"output":'水果'
},
{
"input":'茄子',
"output":'蔬菜'
},
{
"input":'蓝莓',
"output":'水果'
},
{
"input":'包菜',
"output":'蔬菜'
},
{
"input":'牛肉',
"output":'肉类'
},
{
"input":'三文鱼',
"output":'海鲜'
},
{
"input":'马鲛鱼',
"output":'海鲜'
},
]
example_prompt = ChatPromptTemplate.from_messages([
('human','{input}'),
('ai','{output}')
])
few_shot = FewShotChatMessagePromptTemplate(
examples=examples,
example_prompt=example_prompt
)
prompt = ChatPromptTemplate.from_messages([
('system','你是一个幼师,正在教幼儿认识食材分类'),
few_shot,
('human',"请问{input}是")
]
)
chat = ChatOpenAI(
model='gpt-4o-mini'
)
print(chat.invoke(prompt.invoke(input="鲍鱼")))
3. 模型输出及输出解析器
模型返回的格式通常是字符串类型,但在实际开发中,希望得到更加直观的格式,输出解析器就是负责做这个格式转换的。输出解析器常用的有:StrOutputParser,JsonOutputParser,DatetimeOutputParser,CommaSeparatedListOutputParser,XMLOutputParser。
- StrOutputParser:将模型输出的结果,简单的获取content,返回字符串
import os
import dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import StrOutputParser
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
result=chat.invoke("鲸鱼是哺乳动物吗?")
print(type(result))
//result是AiMessage类型
parser=StrOutputParser()
newResult= parser.invoke(result)
print(newResult)
//newResult是str类型,
//内容为“是的,鲸鱼是哺乳动物。尽管它们生活在水中,鲸鱼与其他哺乳动物有很多相似之处,比如它们用肺呼吸空气、通过母乳哺育幼崽、以及体温的恒定等。鲸鱼属于鲸目(Cetacea),这一类包括了如蓝鲸、座头鲸和抹香鲸等多种类型。”
- JsonOutputParser: 将模型输出结果,转为JSON格式
特别说明:当我们希望模型输出结果为我们制定的格式时,可以配合输出解析器实例的get_format_instructions方法,在提示词中注入格式说明,用于指导模型生成制定格式的结果
import os
import dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
temp = ChatPromptTemplate.from_messages([
('system','你是一个百科专家'),
('human','{question}{parser_intro}')
])
parser=JsonOutputParser()
prompt=temp.format(question="鲸鱼有哪些类型",parser_intro=parser.get_format_instructions())
result=chat.invoke(prompt)
print(type(result))
newResult= parser.invoke(result)
print(newResult)
//输出结果:
//
{
'鲸鱼类型': [{
'类型': '蓝鲸',
'学名': 'Balaenoptera musculus',
'特征': '世界上最大的动物,体长可达30米,主要以磷虾为食。'
}, {
'类型': '座头鲸',
'学名': 'Megaptera novaeangliae',
'特征': '以长的波状鳍和复杂的歌声闻名,通常为14-18米长。'
}, {
'类型': '抹香鲸',
'学名': 'Physeter macrocephalus',
'特征': '有着巨大的头部和独特的深潜能力,体长可达20米。'
}, {
'类型': '灰鲸',
'学名': 'Eschrichtius robustus',
'特征': '以底栖生物为食,进行长距离迁徙,体长约14-15米。'
}, {
'类型': '弓头鲸',
'学名': 'Balaena mysticetus',
'特征': '生活在北极地区,以浮游生物为食,体长可达18米。'
}, {
'类型': '哺乳鲸',
'学名': '有多种多样的类型',
'特征': '包括各种小型鲸鱼,例如海豚、虎鲸等,体型较小且社会性强。'
}]
}
- XMLOutputParser:输出xml格式的数据,XMLOutputParser不会直接保持模型输出为原始XML字符,而是会把xml转为结构化的python字典
import os
import dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import XMLOutputParser
from langchain_core.prompts import ChatPromptTemplate
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
temp = ChatPromptTemplate.from_messages([
('system','你是一个百科专家'),
('human','{question}{parser_intro}')
])
parser=XMLOutputParser()
prompt=temp.format(question="鲸鱼有哪些类型",parser_intro=parser.get_format_instructions())
result=chat.invoke(prompt)
print(result)
newResult= parser.invoke(result)
print(newResult)
LCEL语法
使用LCEL语法之前代码示例
import os
import dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import XMLOutputParser
from langchain_core.prompts import ChatPromptTemplate
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
temp = ChatPromptTemplate.from_messages([
('system','你是一个百科专家'),
('human','{question}{parser_intro}')
])
parser=XMLOutputParser()
prompt=temp.invoke(question="鲸鱼有哪些类型",parser_intro=parser.get_format_instructions())
result=chat.invoke(prompt)
newResult= parser.invoke(result)
print(newResult)
使用LCEL语法之后代码示例
import os
import dotenv
from langchain_openai import ChatOpenAI
from langchain_core.output_parsers import XMLOutputParser
from langchain_core.prompts import ChatPromptTemplate
dotenv.load_dotenv()
os.environ["OPENAI_API_KEY"] =os.getenv("OPENAI_API_KEY")
os.environ['OPENAI_BASE_URL'] =os.getenv("OPENAI_BASE_URL")
chat = ChatOpenAI(
model='gpt-4o-mini'
)
temp = ChatPromptTemplate.from_messages([
('system','你是一个百科专家'),
('human','{question}{parser_intro}')
])
parser=XMLOutputParser()
//prompt=temp.invoke(question="鲸鱼有哪些类型",parser_intro=parser.get_format_instructions())
//result=chat.invoke(prompt)
//newResult= parser.invoke(result)
print(newResult)
//组合为链
chain= temp | chat | parser
//执行链
chain.invoke(question="鲸鱼有哪些类型",parser_intro=parser.get_format_instructions())
总结
本章我们主要学习了langchain 的model I/O 相关知识
讲了四种提示词模板
-
- PromptTemplate
-
- ChatPromptTemplate
-
- FewShotPromptTemplate
-
- FewShotChatMessagePromptTemplate
以及模型返回结果的几种输出解析器
-
- StrOutputParser
-
- JsonOutputParser
-
- XMLOutputParser
下一章将继续学习langchain的chains相关知识