LangChain v1.3.4 笔记 - 01 模型的连接/输出、消息、提示词模板
本文详细介绍了 LangChain 框架的核心模块、模型连接与调用方法、消息构建以及提示词模板的使用技巧,帮助你快速上手并构建强大的 AI 应用。
核心模块概览
LangChain 生态体系本身包含 4 个核心模块,LangChain | LangGraph | LangSmith | DeepAgents,它们共同构成了一个完整的 AI 应用开发栈:
- : 基础能力层,提供大模型链接、工具调用、数据加载等核心功能。
LangChain - : 任务流程编排,管理状态,支持多智能体协作。
LangGraph - : 应用监控,性能评估,问题调试。
LangSmith - : 自主决策,专门处理复杂任务拆解,基于
DeepAgentsLangGraph增加了规划功能、上下文管理的文件系统、子袋里等。
你可以按照前端渐进式框架的概念来理解 LangChain -> LangGraph -> DeepAgents 的关系。
一个典型的应用流程是:使用 LangChain 快速构建一个 Agent,通过 LangGraph 运行时编排增加决策、状态等能力,再通过 DeepAgents 处理复杂的任务,最后使用 LangSmith 调试和监控 Agent 的运行状况。
模型的连接
连接模型有两种主要方式:使用 init_chat_model 通用函数,或者直接使用特定模型的 SDK,例如 ChatOpenAI、ChatDeepSeek 等。
注意: 通用函数 init_chat_model 的底层是基于特定模型 SDK 实现的,因此在使用时,必须确保对应的 SDK 库已经安装。
专有对话模型连接
首先,安装社区支持的模型库:
uv add langchain-community
安装后,可以导入并使用如 ChatZhipuAI 等专有模型,支持百炼、智谱等平台。如果不指定 API Key,它会自动从环境变量中读取。
from langchain_community.chat_models import ChatZhipuAI
# 如果不指定 env 就会自己从 env 环境中获取对应的 key 作为模型连接的 key 和 base_url
# ZHIPUAI_API_KEY
# ZHIPUAI_API_BASE
zhipuai_chat = ChatZhipuAI(model="glm-4",
# api_key="your-api-key",
# api_base="...",
# other params...
)
对于主流模型,可以安装对应的 SDK:
uv add langchain-deepseek | langchain-openai
然后按如下方式导入和使用:
from langchain_openai import ChatOpenAI
from langchain_deepseek import ChatDeepSeek
# 和上面一样会找默认的 key 和 base_url
# DEEPSEEK_API_BASE
# DEEPSEEK_API_KEY
# OPENAI_API_BASE
# OPENAI_API_KEY
通用连接方案 init_chat_model
init_chat_model 是对对话连接的封装,它整合了多种模型,并根据模型内部自动选择对应的对话模型去完成连接。如果某些模型提供了 OpenAI 的适配版本,也可以使用 OpenAI 协议来驱动不同的模型。
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
API_KEY = os.environ.get("OPENAI_API_KEY")
BASE_URL = os.environ.get("OPENAI_API_BASE")
# 通过 openai 协议调用 kimi 的模型
init_chat_model(model="kimi-k2.6",
model_provider="openai", # 因为你使用了 openai 协议调用模型,所以要依赖 langchain-openai
api_key=API_KEY, # 因为使用了标准的 OPENAI_API_KEY 实际上可以直接不传也可以,会自动读取
base_url=BASE_URL # base_url 必须传,因为 kimi 提供的兼容方案 base_url 与 openai 并不相同
)
# https://api.moonshot.cn/v1 -> kimi
# https://api.openai.com/v1 -> openai
# 当不使用 model_provider 也想指定提供商的时候,可以直接通过 model 简写
init_chat_model(model="openai:kimi-k2.6", base_url=BASE_URL)
对于不需要转发即可直接使用的模型,可以通过设置专用的环境变量,达到和专用对话模型一样的效果。
from langchain.chat_models import init_chat_model
# 会直接从环境变量中查找 DEEPSEEK_API_KEY 和 DEEPSEEK_BASE_URL
# 依赖 langchain-deepseek 需要提前下载
# 内部会根据模型名称查找模型的归属然后进行分发,或者 init_chat_model("deepseek:deepseek-chat")
init_chat_model("deepseek-chat")
同样的,如果想通过 OpenAI 协议转发,也需要提供对应的 api_key 和 base_url,除非变量名和值都符合 OpenAI 协议,但 base_url 通常不会满足。
import os
from dotenv import load_dotenv
from langchain.chat_models import init_chat_model
load_dotenv()
DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY")
DEEPSEEK_BASE_URL = os.environ.get("DEEPSEEK_BASE_URL")
init_chat_model(model="openai:deepseek-chat",
api_key=DEEPSEEK_API_KEY,
base_url=DEEPSEEK_BASE_URL
)
常用参数如下:
model: str模型名称,可以和model_provider合并使用。model_provider: str模型提供商。api_key: str模型秘钥。base_url: str模型地址。max_tokens: int模型输出的最大 token 数。timeout: float模型超时时间(秒)。max_retries: int = 6最大重试次数。temperature: float = 0.7温度,范围 0-2 之间的小数,用来控制模型输出的随机性。
temperature 参数详解:
- 0.0 ~ 0.3:需要一致性、准确性的任务(数学计算、数据提取、分类、代码生成)。
- 0.5 ~ 0.7:平衡创造性和一致性(聊天、问答)。
- 0.8 ~ 1.5:创造性任务(写作、头脑风暴)。
- 1.5 ~ 2.0:高度创造性(诗歌、故事创作)。
模型的调用
LangChain 提供了三种调用模型的方式:invoke | stream | batch,分别对应阻塞式、流式和批量处理。这些方法通过模型连接对象的返回值进行调用,如 init_chat_model 或 ChatOpenAI 的实例。
invoke 阻塞式
接收消息和一个配置对象,消息支持多种数据格式。
# config 默认就是 None
res = model.invoke("你好,海绵宝宝,我是派大星。", config=None)
也可以传递复杂的列表类型,将完整的对话记录传入,让模型拥有上下文记忆。
# 消息的种类分为四种
# system 系统消息 SystemMessage
# user 用户消息 HumanMessage
# assistant AI 消息 AIMessage
# tool 工具调用 (后续会碰到) ToolMessage
messages = [{
"role": "system", "content": "现在是比奇堡角色扮演场景"
}, {
"role": "user", "content": "你好,海绵宝宝,我是派大星。"
}, {
"role": "assistant", "content": "早啊,派大星"
}, {
"role": "user", "content": "我们一起去抓水母吧"
}]
# 如果对象比较麻烦,同样提供了内置的构造类
from langchain.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
messages = [
SystemMessage('现在是比奇堡角色扮演场景'),
HumanMessage('你好,海绵宝宝,我是派大星。'),
AIMessage('早啊,派大星'),
HumanMessage('我们一起去抓水母吧')
]
res = model.invoke(messages)
小提示: 使用内置的消息类(如 HumanMessage)可以让代码更清晰,并支持类型检查。
stream 流式
返回一个迭代器,可以通过循环进行输出,实时展示模型的推理过程。
res = model.stream(messages)
for chunk in res:
# chunk.content 也可以
print(chunk.text, end="", flush=True)
模型的输出
直接打印返回的 AIMessage 对象信息比较冗余,可以通过 content 属性获取模型的回复内容,或使用 pretty_print() 方法打印可视化的输出。
# 直接打印信息太过冗余,可以直接从 content 获取模型的回复内容
print(res.content)
# 使用 pretty_print() 打印可视化的输出
print(res.pretty_print())
通过 content_blocks 可以获取模型的推理信息,返回的是一个列表对象。这些方法同样适用于 stream 模式。
res = model.stream(messages)
for chunk in res:
for message in chunk.content_blocks:
if len(message):
if message["type"] == "reasoning":
print(f"{message["reasoning"]}", end="", flush=True)
if message["type"] == "text":
print(f"{message["text"]}", end="", flush=True)
# invoke 打印内容如下
# [
# {
# 'type': 'reasoning',
# 'reasoning': '(派大星说要去抓水母!这可是我们最喜欢的活动了。他说"我们一起去",说明他想和我一起玩。.....'
# },
# {'type': 'text', 'text': '好啊好啊!我马上去拿我的捕水母网!(兴奋地蹦跳着)今天一定能抓到超级多的水母!'}
# ]
阻塞式 invoke 的返回值是一个 AIMessage 对象,显示比较复杂。可以使用 uv add rich 进行格式化的输出,便于辨认。
AIMessage(
# 返回的内容
content='哇呜!太棒了!(兴奋地跳起来)我今天早上刚刚擦亮了我的捕虫网,就等你来叫我啦!',
# 模型拒绝回答的情况 None 标识正常回答
additional_kwargs={'refusal': None},
response_metadata={
# token 使用情况
'token_usage': {
'completion_tokens': 28, # 回答消耗的 token
'prompt_tokens': 37, # 输入消耗的 token
'total_tokens': 65, # 本次交互使用 token
'completion_tokens_details': {
'accepted_prediction_tokens': None, # 预测性生成 token
'audio_tokens': None, # 音频消耗 token
'reasoning_tokens': 123, # 推理消耗 token (深度思考)
'rejected_prediction_tokens': None # 被拒绝预测 token
},
# 输入中的音频 token, 缓存命中的 token
'prompt_tokens_details': {
'audio_tokens': None, 'cache_write_tokens': None, 'cached_tokens': 0
},
'prompt_cache_hit_tokens': 0,
'prompt_cache_miss_tokens': 37
},
# 模型提供商
'model_provider': 'openai',
# 模型名称
'model_name': 'deepseek-v4-flash',
# 系统指纹:追踪模型后端配置变更
'system_fingerprint': 'fp_8b330d02d0_prod0820_fp8_kvcache_20260402',
# api 响应 id
'id': 'ca8e630d-822c-40de-b2d3-0e6a56b84b15',
# 停止原因:stop(自然结束)、length(长度受限)
'finish_reason': 'stop',
# 对数概率(通常用于分析词汇选择的可能性)
'logprobs': None
},
# LangChain 内部标识
id='lc_run--019f4b2b-3c0d-7732-a2df-2710ccd08a29-0',
# 工具调用信息
tool_calls=[],
# 工具调用失败的信息
invalid_tool_calls=[],
# langchain 标准化后的 token 用量
usage_metadata={'input_tokens': 37, 'output_tokens': 28, 'total_tokens': 65, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}
)
batch
用于处理一组消息,会按照顺序返回数据。也可以通过 batch_as_completed 按照完成顺序返回。
适用场景: 文档摘要、批量问答、数据预处理、多样本分类等。
messages = [
"你好,你是谁?",
"2 + 3 * 5 = ?",
"中国首都在哪里?"
]
res = model.batch(messages)
res = model.batch_as_completed(messages)
for r in res:
print(r)
消息
消息是与模型交互的基本单位,通过 role 来区分不同的角色。
{"role": "system", "content": "你是个精通编程的软件架构师"}
{"role": "user", "content": "你好啊~"}
// 模型消息有两种,带工具调用和不带
{"role": "assistant", "content": "我也很高兴认识你"}
{"role": "assistant",
"content": "",
"tool_calls": [{"name": "get_weather",
"args": {"location": "北京"},
"id": "call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"}]}
// 工具类型消息
{"role": "tool", "content": "今天天气很好", "tool_call_id":"call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s"}
或者通过消息对象来构造对应的消息,推荐使用这种方式,因为它更清晰且支持类型检查。
from langchain.messages import HumanMessage, AIMessage, SystemMessage, ToolMessage
# content 是第一个参数可以不传递
SystemMessage(content="你是个善解人意的助手")
HumanMessage(content="你好啊~")
AIMessage("我也很高兴认识你")
ToolMessage(content="<工具输出>", tool_call_id="call_00_nUD2NC9QRN5Cg1GaoIkBJQ4s" # 一定要和AI消息中的调用ID匹配)
AIMessage 特有属性
res = model.invoke(messages)
if isinstance(res, AIMessage):
print(res.response_metadata) # 模型元数据
print(res.usage_metadata) # 用量数据
print(res.tool_calls) # 工具调用
HumanMessage 的多模态
多模态包括图片、文件、音视频的解析。以图片为例,支持两种场景:base64 编码和 URL 链接。
# base64
def encode_image():
"""纯粹返回 Base64 编码字符串,不带 Data URI 前缀"""
with open(f"{os.getcwd()}/assets/01.png", "rb") as img_file:
return base64.b64encode(img_file.read()).decode("utf-8")
res = model.invoke([
HumanMessage([
{"type": "text", "text": "描述一下图片的内容"},
{"type": "image", "base64": encode_image(), "mime_type": "image/png"}
])
# 二选一
# {
# "role": "user",
# "content": [
# {"type": "text", "text": "描述一下图片的内容"},
# {"type": "image", "base64": encode_image(), "mime_type": "image/png"}
# ]
# }
])
# url
res = model.invoke([
HumanMessage([
{"type": "text", "text": "描述一下图片的内容"},
{"type": "url", "url": "https://xxx.png"}
]),
# 二选一
# {
# "role": "user",
# "content": [
# {"type": "text", "text": "描述一下图片内容"},
# {"type": "image", "url": "https://xxx.png"}
# ]
# }
])
提示词模板
常规的提示词书写方案是使用模板字符串:
value = '老师'
skill = '教书育人'
prompt = f"""你现在是一名 {value}你拥有 {skill} 的能力"""
LangChain 提供了提示词模板,可以预先定义好提示词,在使用时再具名传入对应的内容。
from langchain_core.prompts import ChatPromptTemplate
# 指定提示词模板,将动态内容通过 {} 预留
# 支持元组和消息对象的使用
template = ChatPromptTemplate([
("system", "你是一名 {value}, 拥有 {skill} 的能力"),
("user", "如何学习 {content}")
])
# 也支持使用类方法 from_messages 调用, 二者参数一致
template = ChatPromptTemplate.from_messages([
("system", "你是一名 {value}, 拥有 {skill} 的能力"),
("user", "如何学习 {content}")
])
# 调用 invoke 方法
res = template.invoke({
"value": "老师", "skill": "教书育人", "content": "古诗词"
})
# print(res) 输出的为一个 message 对象可以直接被模型使用
# messages=[
# SystemMessage(content='你是一名 老师, 拥有 教书育人 的能力', additional_kwargs={}, response_metadata={}),
# HumanMessage(content='如何学习 {content}',additional_kwargs={}, response_metadata={})
# ]
除了元组,还支持字典和 Message 对象方案,但 注意:Message 对象中的占位符会失效。
template = ChatPromptTemplate.from_messages([
{"role": "system", "content": "你是一名 {value}, 拥有 {skill} 的能力"},
{"role": "user", "content": "如何学习 {content}"}
# 传递 value | skill | content 参数也不会生效
# SystemMessage("你是一名 {value}, 拥有 {skill} 的能力"),
# HumanMessage("如何学习 {content}")
])
获取消息的方法:invoke, format, format_messages
# 调用 invoke 方法
res = template.invoke({
"value": "老师", "skill": "教书育人", "content": "古诗词"
})
print(type(res), res) # 返回 langchain_core.prompt_values.ChatPromptValue 类型的对象
# format 方法
res1 = template.format(value="老师", skill="教书育人", content="古诗词")
print(type(res1), res1) # 返回字符串内容
# format_messages 方法
res2 = template.format_messages(value="老师", skill="教书育人", content="古诗词")
print(type(res2), res2) # 返回列表
# [
# SystemMessage(content='你是一名 老师, 拥有 教书育人 的能力', additional_kwargs={}, response_metadata={}),
# HumanMessage(content='如何学习 古诗词', additional_kwargs={}, response_metadata={})
# ]
常用提示词模板
# 针对 ai, human, system 的提示词模板
from langchain_core.prompts import HumanMessagePromptTemplate, AIMessagePromptTemplate, SystemMessagePromptTemplate
# 支持数组,文字,还提供一个 from_template_file 处理文件
res = HumanMessagePromptTemplate.from_template(["你好", "我好", "大家好"])
res = AIMessagePromptTemplate.from_template("你好")
res = SystemMessagePromptTemplate.from_template("你好")
# 通过 format_messages 和 format 获取消息内容
res.format()
res.format_messages() # 对应类型的消息对象
消息预填充 partial
像函数的默认参数一样,可以预先指定部分或全部默认值。
# 指定部分或者全部的默认值
support_template = template.partial(content="背诗词")
# 当调用时,如果不传递就会使用默认值
res = support_template.format_messages(value="老师", skill="教书育人", content="学数学")
消息占位符 MessagesPlaceholder
有 JSON 和 Message 两种形式,一般用于插入历史消息,补充上下文。
from langchain_core.prompts import MessagesPlaceholder
template = ChatPromptTemplate.from_messages([
("system", "现在的比奇堡模仿大赛"),
# 二者都可
# ("placeholder", "{history}"),
MessagesPlaceholder('history'),
("user", "{value}")
])
res = template.format_messages(
# 将老的上下文填充进来,不可以在进行 {} 占位
history=[
("user", "派大星我们一起去抓水母吧"),
("assistant", "好哇,一起去抓水母"),
("user", "把水母送给蟹老板,让他变成伪蟹,哈哈哈"),
AIMessage("抓多点水母,把比奇堡的人都变成伪人吧~")
],
value="那我们需要抓多少水母呢?比奇堡都有谁在哇~"
)
常见问题 (FAQ)
1. 使用 init_chat_model 时报错 “ModuleNotFoundError”,怎么办?
这是因为 init_chat_model 的底层依赖了特定模型的 SDK。例如,要连接 DeepSeek 模型,需要确保安装了 langchain-deepseek 库。请运行 pip install langchain-deepseek 或 uv add langchain-deepseek 来安装对应的依赖。
2. 使用 ChatOpenAI 连接模型时,如何设置环境变量?
你可以在代码中直接传递 api_key 和 base_url 参数,或者在环境变量中设置 OPENAI_API_KEY 和 OPENAI_API_BASE。如果使用 init_chat_model 并指定了 model_provider="openai",它会自动读取这些环境变量。
3. invoke 和 stream 返回的模型输出内容不一致,为什么?
invoke 返回的是完整的 AIMessage 对象,包含了所有元数据。而 stream 返回的是一个迭代器,每次迭代返回一个消息块(chunk)。你可以通过 chunk.content 或 chunk.text 获取文本内容,使用 chunk.usage_metadata 获取 token 用量信息。
4. 提示词模板中的占位符为什么没有生效?
请确保你使用的是元组 ("role", "content") 或字典格式,而不是 Message 对象。例如,SystemMessage("你是一名 {value}") 中的 {value} 不会被替换,因为 Message 对象不会执行占位符替换。
5. 如何实现多轮对话的记忆?
可以将历史的 HumanMessage 和 AIMessage 列表传递给 model.invoke(messages) 方法,或者使用 MessagesPlaceholder 在提示词模板中插入历史消息。例如,将所有历史消息作为 history 参数传递给模板。
通过以上分步指南,你应该已经掌握了如何使用 LangChain 框架连接、调用模型,并利用提示词模板构建结构化对话。祝你开发顺利!