学习 LangChain Day 1:从模型调用到消息结构
今天不急着做一个复杂的 Agent,而是先把 LangChain 最基础、也最容易混淆的几个概念弄清楚:它是什么、环境如何搭建、模型如何初始化、一次调用到底发生了什么,以及消息对象为什么不只是一个字符串。
学习日期:2026-10-05
一、为什么要学习 LangChain?
刚开始接触大模型开发时,我们通常会直接调用某个模型厂商提供的 SDK。代码可能只有几行:准备 API Key,传入一段文本,等待模型返回答案。这种方式非常适合入门,但当需求变复杂后,问题就会逐渐出现:
- 不同模型厂商的 SDK 写法不一样,切换模型时需要重写代码;
- 模型调用、提示词、工具调用、数据库检索和结果解析混在一起,代码越来越难维护;
- 想查看一次请求中到底调用了哪些工具、花了多少时间、消耗了多少 token,不容易;
- 当模型需要多轮对话时,单纯拼接字符串会让角色、上下文和工具结果变得混乱;
- 当一个任务需要多个步骤时,很难清晰地表达"先检索,再判断,最后回答"的流程。
LangChain 的价值,就在于它为这些常见能力提供了统一的抽象。它不是一个"大模型",也不是某一家模型服务,而是一个帮助我们构建 LLM 应用的开发框架。
可以把它理解成一层应用开发接口:底层可以接入不同的聊天模型,上层可以组合提示词、消息、工具、检索器、结构化输出和工作流。这样,我们关注的是"应用要完成什么任务",而不是把所有精力都放在某个供应商的请求格式上。
一个典型的 LangChain 应用可能长这样:
text
用户问题
↓
消息与提示词整理
↓
调用聊天模型
↓
模型判断是否需要工具
↓
调用搜索、数据库或业务 API
↓
把工具结果重新交给模型
↓
生成最终答案
这里的每一步都可以独立替换。例如,可以把一个模型换成另一个模型,把向量数据库换成普通数据库,把搜索工具换成企业内部 API,而不用完全重写上层业务代码。
1. LangChain 中最核心的几个概念
1.1 Model:模型
模型负责根据输入生成输出。聊天模型通常接收一组带有角色的消息,并返回一条 AI 消息。
1.2 Message:消息
消息不仅包含文本,还包含角色、消息 ID、工具调用、token 使用情况和供应商返回的附加信息。消息是聊天模型应用中的基本数据结构。
1.3 Runnable:可运行对象
LangChain 中许多组件都遵循 Runnable 接口,因此可以使用统一的 invoke、batch、stream 和异步方法。模型、提示词模板、输出解析器,甚至由多个组件组合出的链,都可以被当作 Runnable 使用。
1.4 Tool:工具
工具是模型可以请求调用的函数。例如搜索天气、查询订单、读取数据库、执行计算等。模型本身通常不会直接执行函数,而是先返回一个工具调用请求,由应用程序真正执行工具,再把结果作为 ToolMessage 交回模型。
1.5 Retriever:检索器
检索器负责从文档库、向量数据库或其他数据源中找出与问题相关的内容。它经常和 RAG 应用一起使用:先找到资料,再让模型基于资料回答问题。
二、LangChain 家族的四大支柱
学习资料中经常把 LangChain 生态概括为四个部分:LangChain、LangGraph、LangSmith 和 LangServe。它们分别对应应用组件、流程编排、开发观测和服务化。
2.1 LangChain:应用组件层
LangChain 是最基础、最常用的部分。它提供了模型、消息、提示词模板、工具、检索器、输出解析器等组件,并规定了较统一的调用方式。
如果把开发一个大模型应用比作搭积木,那么 LangChain 就提供了许多已经做好的积木:
- 模型积木:连接不同厂商的聊天模型;
- 消息积木:管理系统消息、用户消息、AI 消息和工具消息;
- 提示词积木:把变量填充到固定模板中;
- 工具积木:将普通 Python 函数包装成模型可调用的工具;
- 检索积木:从文档和数据库中查找相关信息;
- 解析积木:把模型输出转换为字符串、JSON 或自定义对象;
- 组合积木:把多个 Runnable 串接成一个完整流程。
2.2 LangGraph:复杂流程与 Agent 编排层
如果任务只有"输入问题 → 调用模型 → 返回回答"三步,普通 LangChain 就足够了。但如果任务开始出现循环、分支、状态保存和人工确认,就需要更强的流程编排能力。
例如一个客服 Agent 可能需要:
- 先识别用户意图;
- 如果是订单问题,查询订单系统;
- 如果是退款问题,检查退款规则;
- 如果金额较大,暂停并等待人工确认;
- 工具失败时自动重试;
- 最后总结处理结果。
这种流程不是一条简单的直线,而是一张有节点、有边、有状态的图。LangGraph 用图结构表达这些步骤,并支持循环、持久化、暂停、恢复和人工介入。
2.3 LangSmith:开发、调试与评估层
LangSmith 可以理解为大模型应用的"运行记录和质量分析平台"。当应用出现问题时,我们不仅想知道最终回答错了,还想知道:
- 当时传给模型的完整消息是什么?
- 模型实际返回了什么?
- 是否调用了工具?工具参数是否正确?
- 哪一步耗时最长?
- 总共消耗了多少 token?
- 新版本提示词是否比旧版本更好?
LangSmith 可以通过追踪记录这些运行过程,也可以帮助我们构造数据集和评估流程。
2.4 LangServe:服务化层
LangServe 的目标是把 LangChain 的 Runnable 暴露成 HTTP 接口,让其他前端或业务系统可以通过 API 调用。它适合把一个已经写好的链快速包装为服务。
不过需要注意,生态项目会随着版本变化。学习资料里仍然常见"四大支柱"的说法,但实际开发时应该以当前官方文档和项目状态为准。LangServe 的仓库目前已归档,新项目进行部署时要优先查看官方推荐的 LangChain 或 LangGraph 部署方式。
2.5 四者之间如何配合?
可以用下面的方式理解:
text
LangChain:提供组件
LangGraph:组织复杂流程
LangSmith:观察和评估运行过程
LangServe:把流程提供给外部系统调用
例如,一个企业知识库问答应用可以这样组合:
text
LangChain:模型 + 文档加载 + 检索器 + 提示词
LangGraph:多轮检索、重写问题、判断是否继续检索
LangSmith:查看每次检索和模型调用,评估回答质量
服务层:将最终问答流程提供给前端或其他业务系统
三、Conda 安装与虚拟环境配置
3.1 为什么需要虚拟环境?
Python 项目经常会依赖第三方库,而不同项目可能依赖同一个库的不同版本。例如:
- 项目 A 需要
langchain==某个版本; - 项目 B 需要另一个版本;
- 某个模型集成包又依赖特定版本的
pydantic或httpx。
如果所有项目都安装在系统 Python 中,升级一个项目的依赖可能导致另一个项目无法运行。虚拟环境的作用,就是为每个项目创建一个相对独立的 Python 和依赖空间。
Conda 不仅能创建 Python 虚拟环境,还能管理一些 Python 之外的二进制依赖。对于刚开始学习大模型应用的同学,使用 Miniconda 就足够了;如果希望安装很多科学计算工具,也可以选择 Anaconda Distribution。
3.2 安装 Conda
Windows 上常见的两种选择是:
- Miniconda:体积小,只包含 Conda 和基础 Python 环境,安装什么由自己决定;
- Anaconda:预装较多数据科学软件,占用空间更大,但开箱即用。
安装后打开 Anaconda Prompt 或 PowerShell,执行:
powershell
conda --version
如果能看到版本号,说明 Conda 已经可以使用。如果 PowerShell 提示找不到 conda,可以先打开 Anaconda Prompt;也可以执行一次 Shell 初始化:
powershell
conda init powershell
执行后关闭并重新打开 PowerShell。
3.3 创建项目环境
powershell
conda create -n langchain-day1 python=3.11 -y
命令中:
create表示创建环境;-n langchain-day1表示环境名称为langchain-day1;python=3.11表示在环境中安装 Python 3.11;-y表示自动确认安装。
启用环境:
powershell
conda activate langchain-day1
成功后,命令行前面通常会出现环境名:
text
(langchain-day1) PS C:\Users\...
确认当前 Python 来自虚拟环境:
powershell
python --version
where.exe python
3.4 安装 LangChain 相关依赖
先升级 pip:
powershell
python -m pip install --upgrade pip
再安装基础包和 OpenAI 聊天模型集成:
powershell
python -m pip install -U langchain langchain-openai
这里建议使用 python -m pip,而不是直接输入 pip。这样可以明确表示:使用当前虚拟环境中的 Python 来执行 pip,避免系统中有多个 Python 时装错位置。
后续根据模型供应商安装对应的集成包。LangChain 的核心包和模型供应商集成通常是分开的,例如:
powershell
# 以下仅为示意,请根据实际供应商选择
python -m pip install -U langchain-anthropic
python -m pip install -U langchain-google-genai
3.5 API Key 的安全配置
不要这样写:
python
model = ChatOpenAI(api_key="sk-真实密钥")
因为代码一旦上传到 GitHub、发送给他人或出现在截图中,密钥就可能泄露。更安全的做法是使用环境变量:
powershell
$env:OPENAI_API_KEY = "你的_API_Key"
Python 代码只负责读取:
python
import os
print(bool(os.getenv("OPENAI_API_KEY")))
如果使用 .env 文件:
text
OPENAI_API_KEY=你的_API_Key
安装并读取:
powershell
python -m pip install python-dotenv
python
from dotenv import load_dotenv
load_dotenv()
.env 必须加入 .gitignore:
gitignore
.env
.venv/
__pycache__/
3.6 退出、删除和导出环境
退出当前环境:
powershell
conda deactivate
查看已有环境:
powershell
conda env list
删除环境:
powershell
conda remove -n langchain-day1 --all
导出环境依赖,便于在另一台机器上复现:
powershell
conda env export -n langchain-day1 > environment.yml
根据文件创建环境:
powershell
conda env create -f environment.yml
四、两种模型初始化方式
LangChain 中的聊天模型不是简单的字符串处理函数。初始化模型时,我们是在创建一个可以接受消息、生成 AI 消息,并且支持统一 Runnable 调用接口的对象。
下面以 ChatOpenAI 为例。模型名只是示例,实际使用时应替换成当前账号和服务商支持的模型。
4.1 直接实例化具体模型类
python
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
max_tokens=512,
timeout=30,
max_retries=2,
)
response = model.invoke("用一句话解释 LangChain。")
print(response.content)
这种方式的特点是:直接导入某个供应商对应的模型类。代码读起来很直观,别人一看就知道项目使用的是哪个模型集成。
它特别适合下面的情况:
- 项目确定只使用一个模型供应商;
- 需要使用该供应商的专属功能;
- 需要查看该集成特有的参数和返回字段;
- 希望类型提示和 IDE 补全更加明确。
4.2 使用 init_chat_model
python
from langchain.chat_models import init_chat_model
model = init_chat_model(
"openai:gpt-4o-mini",
temperature=0.2,
max_tokens=512,
timeout=30,
max_retries=2,
)
response = model.invoke("用一句话解释 LangChain。")
print(response.content)
init_chat_model 提供了一个相对统一的初始化入口。它将供应商和模型写在模型标识中,例如:
text
provider:model_name
这样的写法有利于把模型名称放进配置文件,或者根据环境变量选择不同模型:
python
import os
from langchain.chat_models import init_chat_model
model_name = os.getenv("CHAT_MODEL", "openai:gpt-4o-mini")
model = init_chat_model(model_name, temperature=0.2)
4.3 两者的本质区别
两种方式最终都希望得到一个聊天模型对象,后续通常都可以调用 invoke、stream、batch 等方法。主要区别在于初始化阶段的抽象层次不同:
| 对比项 | 直接实例化模型类 | init_chat_model |
|---|---|---|
| 代码形式 | ChatOpenAI(...) |
init_chat_model("openai:...") |
| 抽象程度 | 更具体 | 更统一 |
| 供应商特性 | 更容易访问 | 需要确认统一入口是否暴露 |
| 切换模型 | 通常需要修改导入和初始化代码 | 可通过配置修改模型标识 |
| 适用项目 | 供应商固定、需要专属能力 | 教程、平台化封装、多供应商切换 |
需要注意:统一初始化不代表不同供应商的参数完全一致。一个供应商支持的参数,另一个供应商可能不支持;即使参数名称相同,含义也可能略有差异。因此切换模型时,除了改模型名称,还要检查 API Key、模型能力、参数兼容性和返回格式。
4.4 模型初始化常用参数
model
指定模型名称。模型名通常由供应商定义,不同服务商不能混用。模型名称写错时,常见结果是请求失败或返回模型不存在。
temperature
控制输出的随机性。一般来说:
- 较低的值:回答更稳定、格式更容易控制;
- 较高的值:表达更丰富,但可能更发散;
- 事实抽取、分类、代码生成等任务通常会使用较低值;
- 创意写作、头脑风暴可以适当提高。
它不是"智力参数",调高并不意味着模型一定更聪明。
max_tokens
限制模型最多生成多少 token。它通常只限制输出长度,不等于上下文窗口大小。输入消息本身也会占用上下文空间。
timeout
请求超时时间。网络不稳定、模型响应慢或服务端排队时,超时可以防止程序无限等待。
max_retries
请求失败时的最大重试次数。重试适合处理临时网络问题或服务端限流,但不是所有错误都应该重试,例如 API Key 错误、模型名称错误通常需要直接修正配置。
api_key
身份验证密钥。建议通过环境变量传入,而不是硬编码到源代码。
base_url
自定义 API 地址。某些兼容 OpenAI API 的服务可以使用它,但要确认该服务的路径、认证方式和响应格式与当前集成兼容。
4.5 初始化参数与调用配置的区别
下面两段代码解决的是不同问题:
python
model = ChatOpenAI(
model="gpt-4o-mini",
temperature=0.2,
)
这是初始化模型,设置模型的默认行为。
python
response = model.invoke(
"介绍 LangChain。",
config={
"tags": ["day1"],
"metadata": {"source": "blog"},
},
)
这是配置某一次 Runnable 运行 ,设置运行名称、标签、元数据或并发行为。后文会详细说明 config。
五、模型调用方式:普通、流式、批量和异步
LangChain 的模型对象通常遵循 Runnable 接口,因此调用方法具有比较统一的命名。可以把它们理解为三种维度:
- 调用一条还是多条输入;
- 一次性返回还是边生成边返回;
- 使用同步代码还是异步代码。
5.1 invoke:一次调用一次返回
python
response = model.invoke("什么是 LangChain?")
print(type(response))
print(response.content)
返回值通常不是普通字符串,而是 AIMessage 或类似的消息对象。直接打印 response 可以看到更多元数据;只想获取回答文本时,可以读取 response.content。
也可以传入消息列表:
python
from langchain_core.messages import SystemMessage, HumanMessage
messages = [
SystemMessage(content="你是一位简洁的 Python 老师。"),
HumanMessage(content="解释列表和元组的区别。"),
]
response = model.invoke(messages)
print(response.content)
5.2 stream:边生成边显示
普通调用需要等模型生成结束后才能看到结果,流式调用则会把结果拆成多个块陆续返回:
python
for chunk in model.stream("用三句话介绍 LangChain。"):
print(chunk.text, end="", flush=True)
print()
流式调用适合聊天窗口,因为用户不需要等待整段回答生成完毕。用户先看到开头内容,会感觉系统响应更快。
但流式返回的每个 chunk 不一定是完整的词、句子或消息。有的块可能只有文本增量,有的块可能包含工具调用片段、元数据或空内容。因此实际项目中要根据返回类型处理,而不能默认每个块都有完整的 content。
将文本收集成完整答案:
python
chunks = []
for chunk in model.stream("解释什么是流式输出。"):
print(chunk.text, end="", flush=True)
chunks.append(chunk.text)
full_text = "".join(chunks)
print("\n\n完整答案:", full_text)
5.3 batch:批量处理多条输入
当有多条互相独立的问题时,可以使用 batch:
python
questions = [
"LangChain 是什么?",
"LangGraph 解决什么问题?",
"LangSmith 有什么用?",
]
responses = model.batch(questions)
for question, response in zip(questions, responses):
print("问题:", question)
print("回答:", response.content)
print("---")
batch 能让代码表达得更清楚:我们不是手动写三次 invoke,而是明确告诉 LangChain"这里有一批独立输入"。实际底层是否合并成一个供应商请求,取决于具体 Runnable 和模型集成,不能简单认为一定只发出一次 HTTP 请求。
5.4 并发控制:max_concurrency
批量任务数量较大时,不应该无节制地同时请求模型,否则可能触发限流:
python
responses = model.batch(
questions,
config={"max_concurrency": 3},
)
这里表示尽量将同时执行的任务数限制在 3 个。它不是模型参数,而是 Runnable 的运行配置。
5.5 异步调用
异步方法适合异步 Web 框架、批量网络请求或需要同时等待多个模型任务的程序。方法名称通常在同步方法前加 a:
python
import asyncio
async def main():
response = await model.ainvoke("什么是异步调用?")
print(response.content)
asyncio.run(main())
异步批量调用:
python
async def main():
questions = [
"什么是 LangChain?",
"什么是 LangGraph?",
"什么是 LangSmith?",
]
responses = await model.abatch(
questions,
config={"max_concurrency": 3},
)
for response in responses:
print(response.content)
异步流式调用:
python
async def main():
async for chunk in model.astream("用一句话介绍 LangSmith。"):
print(chunk.text, end="", flush=True)
print()
5.6 方法对应关系
| 需求 | 同步方法 | 异步方法 |
|---|---|---|
| 单次完整调用 | invoke |
ainvoke |
| 单次流式调用 | stream |
astream |
| 多条输入 | batch |
abatch |
| 多条输入流式处理 | stream/组合 Runnable |
astream/异步事件接口 |
同步和异步不是"哪个更高级",而是适用于不同的程序结构。简单脚本使用同步方法更容易理解;异步 Web 服务和高并发任务则更适合异步方法。
六、图片补充内容:profile、model_kwargs、extra_body 和 config
这一部分按图片标注内容做一个简要说明,不展开供应商的全部专属参数。
6.1 profile
profile 用来查看模型能力或限制信息,例如上下文长度、token 限制、工具调用或多模态能力等。可以这样查看:
python
print(getattr(model, "profile", None))
不同模型集成和版本提供的字段可能不同,因此不能把 profile 当作所有模型都完全一致的配置表。
6.2 model_kwargs
model_kwargs 通常用于传递底层模型集成支持的额外模型参数:
python
model = ChatOpenAI(
model="你的模型名",
model_kwargs={"top_p": 0.9},
)
具体参数必须查看当前模型集成的文档。
6.3 extra_body
extra_body 常用于向兼容 OpenAI API 的服务传递供应商自定义的请求体字段:
python
model = ChatOpenAI(
model="你的模型名",
extra_body={"top_k": 40},
)
它并不是所有模型都支持的通用参数,能否生效取决于服务端。
6.4 调用时的 config
config 是一次 Runnable 运行的配置:
python
response = model.invoke(
"介绍一下 LangChain。",
config={
"run_name": "day1-introduction",
"tags": ["tutorial", "day1"],
"metadata": {"chapter": 1},
},
)
它常用于追踪、筛选、元数据记录和并发控制。可以记成:
text
模型初始化参数:决定模型默认怎么工作
调用 config:描述这一次运行如何被记录和执行
七、LangSmith 是什么?
LangSmith 是 LangChain 生态中的开发和观测平台。它的作用可以简单概括为:把一次大模型应用运行过程记录下来,让开发者能够调试、评估和改进应用。
7.1 为什么需要 LangSmith?
假设用户说:"你的回答不准确。"只看最终答案,我们很难判断问题出在哪里。可能是:
- 提示词写得不清楚;
- 历史消息没有正确传入;
- 检索器找到的资料不相关;
- 工具返回了错误数据;
- 模型没有正确理解工具调用结果;
- 同一个问题在不同模型上的表现不同。
如果记录了完整运行链路,就可以逐步检查每个环节,而不是凭感觉修改代码。
7.2 主要功能
运行追踪
查看一次调用经过了哪些步骤,包括输入、输出、耗时、模型名称和工具调用。
调试
定位提示词、消息结构、工具参数和模型输出中的问题。
评估
准备一批测试问题,使用规则或评估模型比较输出质量,观察新版本是否真的变好了。
项目管理
把不同实验归类到不同项目,便于比较不同模型、提示词和代码版本。
7.3 开启追踪
powershell
$env:LANGSMITH_TRACING = "true"
$env:LANGSMITH_API_KEY = "你的_LangSmith_API_Key"
$env:LANGSMITH_PROJECT = "langchain-day1"
只在本地或安全的部署环境中保存 API Key,不能把密钥提交到公开仓库。
八、消息格式与消息对象字段
8.1 为什么模型输入不是一个字符串?
聊天模型需要区分"谁说了什么"。同一句话,如果是系统指令、用户问题或上一轮 AI 回复,含义可能完全不同。因此聊天模型通常接收消息列表:
python
[
SystemMessage(...),
HumanMessage(...),
AIMessage(...),
ToolMessage(...),
]
消息列表不仅保存对话文本,还能表达工具调用和工具结果。
8.2 四种常见消息类型
SystemMessage
用于设置模型的行为和规则,例如角色、回答风格、输出格式和限制条件:
python
from langchain_core.messages import SystemMessage
SystemMessage(content="你是一名严谨的 Python 教师。")
系统消息不是用户问题,而是对模型的整体行为说明。
HumanMessage
表示用户输入:
python
from langchain_core.messages import HumanMessage
HumanMessage(content="解释一下列表和元组的区别。")
AIMessage
表示模型生成的消息。它的 content 可以是回答文本,也可以同时包含工具调用信息。
ToolMessage
表示工具执行结果。它通常需要通过 tool_call_id 与之前 AI 消息中的工具调用对应起来。
8.3 常见消息字段
| 字段 | 说明 | 常见消息 |
|---|---|---|
type |
消息类型,如 system、human、ai、tool |
所有消息 |
content |
消息主体,可以是字符串或内容块列表 | 所有消息 |
name |
可选名称,用于标识消息来源 | 部分消息 |
id |
消息唯一标识 | 部分消息 |
tool_calls |
AI 请求调用工具的信息 | AIMessage |
tool_call_id |
工具结果对应的调用 ID | ToolMessage |
response_metadata |
模型或供应商返回的附加信息 | AIMessage |
usage_metadata |
输入、输出和总 token 使用量等信息 | AIMessage |
additional_kwargs |
集成保留的供应商特有字段 | 视集成而定 |
8.4 一个完整的消息调用示例
python
from langchain_core.messages import SystemMessage, HumanMessage
messages = [
SystemMessage(
content="你是一名学习助手。回答要分点,并给出一个简单例子。"
),
HumanMessage(
content="什么是 Runnable?"
),
]
response = model.invoke(messages)
print("消息类型:", response.type)
print("回答内容:", response.content)
print("响应元数据:", response.response_metadata)
print("Token 使用:", response.usage_metadata)
这些字段并不一定全部有值。是否返回 token 使用量、模型名称、结束原因和供应商原始字段,取决于具体模型集成和服务端响应。
8.5 工具调用的消息流
当模型使用工具时,对话可能是:
text
HumanMessage:帮我查询订单 1001 的状态
↓
AIMessage:请调用 get_order(order_id="1001")
↓
ToolMessage:订单 1001 当前状态为"已发货"
↓
AIMessage:订单 1001 已发货,预计明天送达
这里的关键是:工具结果不是普通字符串随便拼接进去,而是使用工具消息,并通过调用 ID 建立对应关系。这样模型和框架才能知道这个结果属于哪一次工具调用。
九、content 与 content_blocks 的区别
9.1 content 是什么?
content 是消息对象的主体内容。最简单的文本回答通常是一个字符串:
python
response = model.invoke("你好")
print(response.content)
对于多模态或包含特殊内容的消息,content 也可能是一个列表,里面包含文本、图片、音频或供应商特有的数据结构。
9.2 content_blocks 是什么?
content_blocks 可以理解为一种更结构化的内容访问方式。它把消息主体拆分为若干内容块,并尽量使用统一的类型表达,例如文本块、图片块、工具调用块等。
这样做的好处是,应用程序可以根据块的类型处理内容,而不需要把所有返回值都当作字符串:
python
for block in response.content_blocks:
print(block)
具体块的字段会受到模型、供应商和 LangChain 版本的影响,因此写代码时应先打印实际结构,再决定如何解析。
9.3 对比表
| 对比项 | content |
content_blocks |
|---|---|---|
| 定位 | 消息的原始主体 | 结构化访问内容的接口 |
| 最适合 | 纯文本回答、快速读取 | 多模态内容、工具调用、统一解析 |
| 数据形态 | 字符串或列表 | 内容块列表 |
| 兼容性 | 可能保留供应商原始格式 | 更适合跨模型处理,但受集成支持影响 |
| 使用建议 | 简单场景优先读取 | 需要区分内容类型时使用 |
9.4 调试时怎么判断?
python
response = model.invoke("介绍一下 LangChain")
print("content 的类型:", type(response.content))
print("content:", response.content)
print("content_blocks:", response.content_blocks)
可以这样做一个简单的文本提取:
python
texts = []
for block in response.content_blocks:
if block.get("type") == "text":
texts.append(block.get("text", ""))
print("".join(texts))
不过,不同版本和集成返回的块可能不是普通字典,也可能存在不同字段名,因此生产代码需要根据实际返回对象编写更稳健的解析逻辑。
9.5 什么时候用哪个?
- 只需要显示普通文本:优先使用
response.content; - 需要处理图片、音频或视频:检查
content_blocks; - 需要识别工具调用:查看
AIMessage.tool_calls,同时结合内容块; - 需要兼容多个模型供应商:不要假设所有模型的原始
content格式相同; - 正在调试模型响应:同时打印
content、content_blocks和完整消息对象。
十、一个可运行的 Day 1 示例
下面把本章最重要的知识串起来:初始化模型、构造消息、普通调用、流式调用和批量调用。
python
from langchain.chat_models import init_chat_model
from langchain_core.messages import SystemMessage, HumanMessage
model = init_chat_model(
"openai:gpt-4o-mini",
temperature=0.2,
max_tokens=512,
)
messages = [
SystemMessage(content="你是一名 LangChain 入门老师,请用清晰的中文回答。"),
HumanMessage(content="什么是 Runnable?"),
]
# 1. 普通调用
response = model.invoke(
messages,
config={
"run_name": "day1-invoke",
"tags": ["day1", "invoke"],
},
)
print("普通调用:")
print(response.content)
# 2. 流式调用
print("\n流式调用:")
for chunk in model.stream("请用三句话解释消息对象。"):
print(chunk.text, end="", flush=True)
print()
# 3. 批量调用
print("\n批量调用:")
questions = [
"LangChain 是什么?",
"LangSmith 是什么?",
]
responses = model.batch(questions, config={"max_concurrency": 2})
for item in responses:
print(item.content)
运行前需要确认:
- 已启用正确的 Conda 环境;
- 已安装
langchain和对应模型集成包; - API Key 已通过环境变量配置;
- 模型名称与当前服务商支持的模型一致;
- 网络可以访问对应模型服务。
十一、今日总结
今天的重点不是记住很多 API,而是建立一个正确的整体认识:
- LangChain 是大模型应用开发框架,不是模型本身。
- LangChain 生态可以从组件、流程、观测和服务化四个角度理解。
- Conda 虚拟环境可以隔离项目依赖,减少版本冲突。
- 直接实例化模型类更具体,
init_chat_model更适合统一封装和切换。 invoke、stream、batch和异步方法表达了不同的调用模式。- 模型初始化参数与 Runnable 的
config不是同一类配置。 - 消息对象不仅有文本,还可以承载工具调用、元数据和 token 使用信息。
- 简单文本可以读取
content,复杂或多模态内容需要关注content_blocks。 - LangSmith 可以帮助我们观察、调试和评估模型应用。
下一步学习建议
下一篇可以继续学习:
- Prompt Template 如何管理提示词;
prompt | model | parser这种 Runnable 链式组合;- 结构化输出与 Pydantic 模型;
- Tool Calling 的完整流程;
- 文档加载、向量化和 RAG;
- LangGraph 中的节点、边和状态。