🎯 课程主题
深入讲解 content 字段的字符串与字典列表两种格式(尤其是多模态场景),并引入 LangChain v1.2 新增的 content_blocks 字段,说明其在跨模型供应商时的标准化作用。
📝 核心知识点
1. content 字段的两种数据格式
- 概念说明:
content是弱类型字段,支持两种数据格式:- 字符串:用于纯文本消息。此时
content=可省略,直接写字符串。 - 字典列表(List[Dict]):用于多模态内容(文本 + 图片/音频等),每个字典描述一个内容块。
- 字符串:用于纯文本消息。此时
- 关键细节:当涉及图片等多模态数据时,必须使用字典列表格式,如
[{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {"url": "..."}}]。
2. content_blocks — LangChain v1.0 标准化的多模态字段
- 概念说明:LangChain v1.0 引入的重大升级,提供一种跨模型供应商统一的多模态数据结构。在 LangChain v1.2 中保留
content仅为向前兼容。 - 关键细节:
- 数据结构同样是字典列表,每个 block 有一个
type字段区分内容类型(text/image/...)。 - 输入格式化:相同的内容,不同模型供应商对
content格式支持不一致(如 OpenAI 支持图片 URL 解析,Anthropic 的 Claude 可能不支持),使用content_blocks的标准化格式可解决此问题。 - 输出格式化:不同模型输出结构各异(如 DeepSeek 的推理思考内容存储在
additional_kwargs的reasoning_content字段中)。使用response.content_blocks可直接提取包含推理内容的完整输出,而response.content只有正文部分。
- 数据结构同样是字典列表,每个 block 有一个
🏗️ 架构与工作流
输入侧(content_blocks 标准化):
[用户图片 + 文本] → 统一为 content_blocks 格式 → 发送给任意模型供应商 → 均能正确解析
输出侧(content_blocks 提取完整信息):
模型响应(AMessage) → .content → 仅正文文本
→ .content_blocks → 正文 + reasoning_content + ...
💻 代码实战
# ========== 1. content 多模态使用(图片解析)==========
import base64
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
model = ChatOpenAI(
model="gpt-4o",
base_url="https://api.claude.ai/v1",
api_key="your-api-key"
)
def encode_image_to_data_url(image_path):
"""将本地图片编码为 data URL 字符串"""
with open(image_path, "rb") as f:
image_data = base64.b64encode(f.read()).decode("utf-8")
return f"data:image/png;base64,{image_data}"
image_url = encode_image_to_data_url("images/perfume.png")
msg = HumanMessage(
content=[
{"type": "text", "text": "请描述这张图片的内容"},
{"type": "image_url", "image_url": {"url": image_url}}
]
)
response = model.invoke([msg])
print(response.content)
# 成功输出图片描述
# ========== 2. content_blocks 输入格式化(跨供应商兼容)==========
# 使用传统 content 格式调用 Anthropic Claude 模型 → 读取失败
anthropic_model = ChatOpenAI(
model="claude-3-haiku", # Anthropic 模型
base_url="https://api.claude.ai/v1",
api_key="your-api-key"
)
# 传统 content 写法 → Anthropic 模型可能无法读取图片
msg_old = HumanMessage(
content=[
{"type": "text", "text": "请描述这张图片"},
{"type": "image_url", "image_url": {"url": image_url}}
]
)
response = anthropic_model.invoke([msg_old])
print(response.content) # 可能输出: "我看不到您分享的图片"
# 使用 content_blocks 标准化写法 → 兼容所有模型
msg_new = HumanMessage(
content_blocks=[
{"type": "text", "text": "请描述这张图片"},
{"type": "image", "source": {
"type": "base64",
"media_type": "image/png",
"data": base64.b64encode(open("images/perfume.png", "rb").read()).decode("utf-8")
}}
]
)
response = anthropic_model.invoke([msg_new])
print(response.content) # → 成功读取图片内容
response = model.invoke([msg_new]) # OpenAI 模型同样兼容
print(response.content)
# ========== 3. content_blocks 输出格式化(提取完整输出)==========
# 以 DeepSeek 官方模型为例,模型返回包含 reasoning_content
from langchain_deepseek import ChatDeepSeek
deepseek_model = ChatDeepSeek(
model="deepseek-chat",
api_key="your-deepseek-api-key"
)
response = deepseek_model.invoke([HumanMessage(content="1+1等于多少?")])
print("=== content ===")
print(response.content)
# 只输出正文: "1+1等于2"
print("=== content_blocks ===")
print(response.content_blocks)
# 输出包含 reasoning_content 和正文的完整内容
# [{"type": "reasoning", "content": "这是一个简单的数学问题..."},
# {"type": "text", "content": "1+1等于2"}]
⚠️ 常见问题与避坑指南
- 多模态必须用字典列表:纯文本可省略
content=,但只要有图片/音频等多模态数据,必须显式使用content=[...]的字典列表格式。 - 跨供应商兼容性陷阱:同样的
content格式在不同模型供应商间的支持度不同(如 OpenAI 能读图片但 Anthropic 不行),切换模型时应优先使用content_blocks。 contentvscontent_blocks取值的差异:使用 DeepSeek 等带推理过程的模型时,.content只返回正文,.content_blocks才能获取完整的思维链(reasoning)内容,尤其是在需要多轮回传推理上下文时,必须使用content_blocks。- LangChain 1.2 中
content仍然有效:保留content是为了向前兼容,新项目推荐逐步迁移到content_blocks。
💡 个人总结与延伸
content_blocks 是 LangChain 在多模态和跨供应商方向走出的重要一步。它解决了输入侧的模型供应商碎片化问题(同一份代码适配不同平台),也解决了输出侧推理内容的标准化提取。在实际项目中,如果涉及多模态或多供应商切换场景,建议从一开始就使用 content_blocks 规范,避免后期兼容性改造。注意不同供应商对 content_blocks 内具体字段(如 source.type、media_type、data)的实现可能存在细微差异,开发时仍需查阅对应供应商文档。