LangChain Model IO
环境
- Anaconda:v25.5.1
- Python:v3.13.9
- LangChain:v1.0.3
- langchain-openai:v1.0.2
模型(Model)
模型是 AI 应用的核心,提供语言的理解和生成能力,LangChain 允许集成多种不同的模型,这些模型从大类可以分为两类:LLM 语言模型和 Chat Model 聊天模型,这俩模型本质上都是大语言模型,不同之处在于 LLM 是做文本补全的模型,而 Chat Model 是那些在对话方面进行了调优的模型。由于它俩的作用不同,一个擅长补全,另一个擅长对话,所以它俩的使用接口也是不太一样的,换句话说它俩接收和返回数据的方式有所区别。具体来讲,LLM 接受一个字符串作为输入,返回一个字符串作为输出,而 Chat Model 接收一个消息列表作为输入,返回一个消息作为输出。本文主要以 OpenAI 的 Chat Model 为基础结合 LangChain 来介绍如何通过 LangChain 来使用 OpenAI 的聊天模型。
首先通过以下命令安装 langchain_openai 库:
pip install langchain_openai
然后新建文件将下面代码拷贝至文件中。
from langchain_openai import ChatOpenAI
from langchain.messages import SystemMessage,HumanMessage
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
system_message = SystemMessage(content="请你作为我的物理助教老师,用通俗易懂的语言解释物理概念。")
human_message = HumanMessage(content="什么是波粒二象性?")
messages = [system_message, human_message]
response = model.invoke(messages)
print(response)
print(response.content)
上述代码中我们首先引入了 langchain_openai 库中的聊天模型 ChatOpenAI,然后创建了一个聊天模型的实例,在创建实例时可以看到传入的参数都是之前文章介绍过的参数,也就是调用 OpenAI API 的 client.chat.completions.create() 方法时传入的参数,此处因为我们之前已经将申请过的 OpenAI API Key 配置到环境变量中了,所以无需在代码中显式传参。
有了聊天模型的实例之后,我们还需要创建一个消息列表,因为刚才有介绍说聊天模型将一个消息列表作为输入,然后将一个消息作为输出。聊天列表需要通过引入langchain.messages 中的 SystemMessage 和 HumanMessage 来创建,除此之外还有 AIMessage,这三个分别代表系统消息(表示对 AI 系统的指令)、来自人类的聊天消息和来自 AI 的聊天消息。这三个消息类型其实跟调用 client.chat.completions.create() 方法时传入的 messages 中的 role 三个类型(system、user、assistant)相似。
有了消息列表之后,我们就可以调用模型的 invoke() 方法来得到相应的回答。
提示模板(Prompt Template)
提示指的是用户给大语言模型的输入内容,通常我们在使用 ChatGPT 时都是直接给它输入一大串文本作为提示,如果有重复度高的文本的话还是需要每次一个个的手动输入,但是用代码的话就可以将这些重复度较高的文本单独抽离出来形成一个模板,在这个模板里面只需要将那些变化的文本作为变量即可,类似于下面这种提示:
你是一位专业的翻译,能够将{input_language}翻译成{output_language},并且输出文本会根据用户要求的任何语言风格进行调整。请只输出
翻译后的文本,不要有任何其他内容。
文本:{text}
风格:{style}
上述提示文本其实就是一个提示模板,我们只需要每次使用时动态传入不同的变量即可,其余部分直接复用就行。
LangChain 提供了相应的 API 用于动态构建给模型的消息,即提示模板(Prompt Template)。LangChain 提供的聊天模型的提示模板包括 SystemMessagePromptTemplate、HumanMessagePromptTemplate 和 AIMessagePromptTemplate,它们都在 langchain_core.prompts 模块下,分别对应系统消息、人类消息和 AI 消息的模板。这些消息提示模板都有一个 from_template() 方法,它接收一个字符串作为提示的内容,这个字符串就跟刚才我们介绍的一样,它里面可以有用花括号包裹的变量名,我们后续就可以对这些变量进行动态赋值,例如针对上述的提示模板,我们通过动态给 input_language 和 output_language 两个变量进行赋值,既可以做中文翻译法语,也可以做葡萄牙语翻译英语。给提示模板里面的变量赋值需要用到提示模板的 format() 方法,在这个方法里面传入变量的值即可,相关代码如下:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate,AIMessagePromptTemplate
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
system_template_text = "你是一位专业的翻译,能够将{input_language}翻译成{output_language},并且输出文本会根据用户要求的任何语言风格进行调整。请只输出翻译后的文本,不要有任何其他内容。"
system_prompt_template = SystemMessagePromptTemplate.from_template(system_template_text)
system_prompt = system_prompt_template.format(input_language="英语", output_language="汉语")
human_template_text = "文本:{text}\n语言风格:{style}"
human_prompt_template = HumanMessagePromptTemplate.from_template(human_template_text)
human_prompt = human_prompt_template.format(text="LangChain provides the engineering platform and open source frameworks developers use to build, test, and deploy reliable AI agents.", style="文言文")
messages = [system_prompt, human_prompt]
response = model.invoke(messages)
print(response)
print(response.content)
提示模板的 format() 方法返回各自对应的 Message 消息类型,例如 SystemMessage 和 HumanMessage,我们只需要把返回的消息以列表的形式传入到模型的 invoke() 即可,最终输出结果如下:
content='LangChain者,工匠之台,开放之框架,开发者使之以构建、试验及部署可靠之人工智能使者也。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 35, 'prompt_tokens': 89, 'total_tokens': 124, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-CaMPymQOAOLTgBbHdsoPdphRdnoJ0', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--9acb0c0a-07ad-4f38-9d48-cd1cd7031c0f-0' usage_metadata={'input_tokens': 89, 'output_tokens': 35, 'total_tokens': 124, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
LangChain者,工匠之台,开放之框架,开发者使之以构建、试验及部署可靠之人工智能使者也。
LangChain 提供的聊天模型的提示模板除了上述 SystemMessagePromptTemplate、HumanMessagePromptTemplate 和 AIMessagePromptTemplate 三个角色分明的提示模板之外,还提供了一个更加通用的提示模板——ChatPromptTemplate,它有一个 from_messages() 方法,接收一个列表作为参数,列表中的每一个元素都代表一条消息模板,每一条消息模板都是一个元组,元组的第一个元素代表角色,第二个元素代表内容。给消息模板内的变量赋值的话可以调用模板的 invoke() 方法,该方法的参数是一个字典,模板内变量和变量的值就可以通过字典的键值对的形式传入,相关代码如下:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import SystemMessagePromptTemplate,HumanMessagePromptTemplate,AIMessagePromptTemplate,ChatPromptTemplate
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
"""
system_template_text = "你是一位专业的翻译,能够将{input_language}翻译成{output_language},并且输出文本会根据用户要求的任何语言风格进行调整。请只输出翻译后的文本,不要有任何其他内容。"
system_prompt_template = SystemMessagePromptTemplate.from_template(system_template_text)
system_prompt = system_prompt_template.format(input_language="英语", output_language="汉语")
human_template_text = "文本:{text}\n语言风格:{style}"
human_prompt_template = HumanMessagePromptTemplate.from_template(human_template_text)
human_prompt = human_prompt_template.format(text="LangChain provides the engineering platform and open source frameworks developers use to build, test, and deploy reliable AI agents.", style="文言文")
messages = [system_prompt, human_prompt]
"""
prompt_template = ChatPromptTemplate.from_messages([
("system", "你是一位专业的翻译,能够将{input_language}翻译成{output_language},并且输出文本会根据用户要求的任何语言风格进行调整。请只输出翻译后的文本,不要有任何其他内容。"),
("human", "文本:{text}\n语言风格:{style}")
])
prompt_value = prompt_template.invoke({
"input_language": "英语",
"output_language": "汉语",
"text": "LangChain provides the engineering platform and open source frameworks developers use to build, test, and deploy reliable AI agents.",
"style": "文言文"
})
response = model.invoke(prompt_value)
print(response)
print(response.content)
运行上述代码,会得到与之前相同的结果。
小样本提示模板(FewShotChatMessagePromptTemplate)
我们在《提示工程》一文中介绍过小样本提示,它主要是通过给 AI 传入一系列的示范值,从而引导 AI 按照我们预期的格式输出内容,在此过程中我们给模型传入的消息列表中有很多重复度较高的消息,同样的,我们可以利用提示模板的方式简化我们的代码。代码如下:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import FewShotChatMessagePromptTemplate,ChatPromptTemplate
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
example_prompt = ChatPromptTemplate.from_messages(
[
("human", "格式化以下客户信息:\n姓名 -> {customer_name}\n年龄 -> {customer_age}\n 城市 -> {customer_city}"),
("ai", "##客户信息\n- 客户姓名:{formatted_name}\n- 客户年龄:{formatted_age}\n- 客户所在地:{formatted_city}")
]
)
examples = [
{
"customer_name": "张三",
"customer_age": "27",
"customer_city": "长沙",
"formatted_name": "张三",
"formatted_age": "27岁",
"formatted_city": "湖南省长沙市"
},
{
"customer_name": "李四",
"customer_age": "42",
"customer_city": "广州",
"formatted_name": "李四",
"formatted_age": "42岁",
"formatted_city": "广东省广州市"
},
]
few_shot_template = FewShotChatMessagePromptTemplate(
example_prompt=example_prompt,
examples=examples,
)
final_prompt_template = ChatPromptTemplate.from_messages(
[
few_shot_template,
("human", "{input}"),
]
)
final_prompt = final_prompt_template.invoke({"input": "格式化以下客户信息:\n姓名 -> 王五\n年龄 -> 31\n 城市 -> 郑州'"})
response = model.invoke(final_prompt)
print(response)
print(response.content)
上述代码中我们通过使用 LangChain 提供的 FewShotChatMessagePromptTemplate API 结合 ChatPromptTemplate API,将小样本提示中的示范值进行了模板抽离,公用模板用 example_prompt 变量保存,小样本的示范值则用一个 examples 变量保存,然后将这两个变量传入 FewShotChatMessagePromptTemplate 得到一个小样本提示模板,最后结合 ChatPromptTemplate 将用户提示模板传入,调用模型的 invoke() 方法得到最终输出。由此一来大大简化了我们的代码,提高了扩展性,后续想增加示范值的话只需要往 examples 列表增加元素即可,无需改动其他的代码。
输出解析器(Output Parser)
输出解析器会做下面两件事情:
- 给模型下指令,要求模型按照指定的格式输出;
- 解析模型的输出,提取所需的信息。
例如下面代码:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import CommaSeparatedListOutputParser
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
prompt = ChatPromptTemplate.from_messages([
("system", "{parser_instructions}"),
("human", "列出5个{subject}色系的十六进制颜色码。")
])
output_parser = CommaSeparatedListOutputParser()
parser_instructions = output_parser.get_format_instructions()
final_prompt = prompt.invoke({"subject": "莫兰迪", "parser_instructions": parser_instructions})
response = model.invoke(final_prompt)
print(response)
print(response.content)
result = output_parser.invoke(response)
print(result)
上述代码中我们使用 ChatPromptTemplate 构建了一个提示模板,与之前的提示模板不同的是,对于系统消息我们使用了 LangChain 提供的 CommaSeparatedListOutputParser API 中的 get_format_instructions() 方法得到了一个输出指令,然后将这个输出指令连同用户提示一起传给模型,最终得到一个包含有 5 个十六进制颜色码并且通过逗号连接的输出。CommaSeparatedListOutputParser 的 get_format_instructions() 方法就是告诉模型最终的输出会是由逗号分隔的元素所组成的字符串。
除此之外,我们再来看一个示例:
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from typing import List
from pydantic import BaseModel, Field
model = ChatOpenAI(model="gpt-4o-mini",
temperature=1.2,
max_tokens=300,
frequency_penalty=0.2
)
class BookInfo(BaseModel):
book_name: str = Field(
description="书籍的名字",
examples=["百年孤独"]
)
author_name: str = Field(
description="书籍的作者",
examples=["加西亚·马尔克斯"]
)
genres: List[str] = Field(
description="书籍的体裁",
examples=[["小说", "文学"]]
)
output_parser = PydanticOutputParser(pydantic_object=BookInfo)
prompt = ChatPromptTemplate.from_messages([
("system", "{parser_instructions} 你输出的结果请使用中文。"),
("human", "请你帮我从书籍概述中,提取书名、作者,以及书籍的体裁。书籍概述会被三个#符号包围。\n###{book_introduction}###")
])
book_introduction = """《明朝那些事儿》,作者是当年明月。2006年3月在天涯社区首次发表,2009年3月21日连载完毕,边写作边集结成书出版发行,一共7本。
《明朝那些事儿》主要讲述的是从1344年到1644年这三百年间关于明朝的一些故事。以史料为基础,以年代和具体人物为主线,并加入了小说的笔法,语言幽默风趣。对明朝十六帝和其他王公权贵和小人物的命运进行全景展示,尤其对官场政治、战争、帝王心术着墨最多,并加入对当时政治经济制度、人伦道德的演义。
它以一种网络语言向读者娓娓道出三百多年关于明朝的历史故事、人物。其中原本在历史中陌生、模糊的历史人物在书中一个个变得鲜活起来。《明朝那些事儿》为读者解读历史中的另一面,让历史变成一部活生生的生活故事。
"""
final_prompt = prompt.invoke({"book_introduction": book_introduction,
"parser_instructions": output_parser.get_format_instructions()})
response = model.invoke(final_prompt)
print(response)
print(response.content)
result = output_parser.invoke(response)
print(result)
上述代码运行后会得到一个 JSON 格式的输出:
content='{\n "book_name": "明朝那些事儿",\n "author_name": "当年明月",\n "genres": [\n "历史",\n "文学"\n ]\n}' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 40, 'prompt_tokens': 557, 'total_tokens': 597, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_provider': 'openai', 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-CaNloTCBKc9XpsTxoDzGPCuNG0Dxj', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None} id='lc_run--be3ef20a-b9d5-4ece-842d-b6e983bbaf8e-0' usage_metadata={'input_tokens': 557, 'output_tokens': 40, 'total_tokens': 597, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}
{
"book_name": "明朝那些事儿",
"author_name": "当年明月",
"genres": [
"历史",
"文学"
]
}
跟上一个示例不同的是,我们在上述代码中使用了 PydanticOutputParser 这个解析器,此解析器会告诉 AI 给我们生成的结果按 JSON 格式输出。
链(Chain)
回顾前面几个例子我们发现,不管是在模型、提示模板还是使用输出解析器时,我们或多或少都会用到 invoke() 这个方法,这个是 LangChain 特意设计的,不管是模型、提示模板,还是输出解析器,他们都实现了 LangChain 的 Runnable 接口、都具有 invoke() 方法。invoke() 方法是 LangChain 框架中 Runnable 接口的通用调用方法。对于提示模板来说,invoke() 接收表示输入变量值的字典,返回 prompt value 提示值;对于模型来说,invoke() 接收提示值或消息列表,返回聊天信息;对于输出解析器来说,invoke() 接收聊天信息,返回解析后的结果。由此可以看到,提示模板 invoke() 的输出是模型 invoke() 的输入,模型 invoke() 的输出又是解析器 invoke() 的输入,所以我们可以一次性调用多次 invoke() 直接得到最终结果,如下代码所示:
result = output_parser.invoke(
model.invoke(
prompt.invoke(
{"subject": "莫兰迪",
"parser_instructions": parser_instructions})))
除了上述层层调用 invoke() 方法之外,LangChain 还支持下面这种调用方式:
chat_model_chain = prompt | model | output_parser
result = chat_model_chain.invoke(
{"subject": "莫兰迪",
"parser_instructions": parser_instructions})
上述代码中“竖线”的含义是把前面组件的输出作为后面组件的输入,它有一个很形象的名称,叫做“管道操作符”。上面代码的意思是把提示模板的输出传给模型作为输入,然后把模型的输出传给解析器作为输入,这种写法叫做 LangChain 表达式语言,简称 LCEL。像上面这种多个组件的一系列调用我们就称之为“链”,要想得到链最终的执行结果,我们还是调用 invoke() 方法来获取,invoke()方法里面传入的参数就是第一个组件的参数,因为在链中前一个组件的输出是后一个组件的输入,因此整条链只有第一个组件是没有参数传入的,所以就在调用 invokde() 方法时传入。
LangChain 中链的使用不仅限于此,通过链我们可以组合出很多复杂的流程,在后续的文章中我们逐步学习和实践。
总结
本节我们介绍了 LangChain 框架中的模型、提示模板、输出解析器以及链,每一个知识点都用了一个小示例来做简单介绍,大家对其有一个基础的认识即可,下一篇文章我们将通过两个小项目来练手,将本节所介绍的知识点运用到实际项目开发中。
更多推荐



所有评论(0)