langchain大模型开发技术快速入门(一)

3 阅读11分钟

提起大模型应用开发,大家脑海中首先出现的技术和框架是什么?是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支持的模型有三大类:

  1. LLMs 非对话模型,输入为文本或者promptValue对象,输出总是返回文本字符串,不支持多轮对话,适合单词文本生成任务
  2. Chat Models 对话模型,输入为PromptValue或者消息列表,输出的是消息对象,通常是AIMessage.支持多轮对话,适合用作对话系统和交互助手
  3. 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()有两个差异

  1. format() 传递的是关键词参数,返回的是字符串
  2. invoke()传递的是字典类型的参数,返回的是PromptValue类型的数据
ChatPromptTemplate类

ChatPromptTemplate类实例化参数

参数参数描述
messages形参,是一个列表,列表项支持message实例,元组,元组格式为(role:str,content: strlist[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. 模型输出及输出解析器

模型返回的格式通常是字符串类型,但在实际开发中,希望得到更加直观的格式,输出解析器就是负责做这个格式转换的。输出解析器常用的有:StrOutputParserJsonOutputParserDatetimeOutputParserCommaSeparatedListOutputParserXMLOutputParser

  • 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 相关知识

讲了四种提示词模板

    1. PromptTemplate
    1. ChatPromptTemplate
    1. FewShotPromptTemplate
    1. FewShotChatMessagePromptTemplate

以及模型返回结果的几种输出解析器

    1. StrOutputParser
    1. JsonOutputParser
    1. XMLOutputParser

下一章将继续学习langchain的chains相关知识