LangChain content_blocks:统一处理多模态与跨模型厂商消息内容

目录

前言

  1. 在开发基于大语言模型的应用时,我们经常需要处理来自不同提供商(OpenAI、阿里云百炼、本地模型等)的消息内容。这些模型在消息格式上各有差异:有的将推理步骤放在专用字段,有的以不同结构表示多模态数据。如果直接使用原始的 content 字段,我们得写大量针对特定模型的适配代码。
  2. 说白了就是每个模型厂商都有自己的一套消息规范(content),我们调用模型厂商时需要按照每个厂商的规范定义content,接收模型消息后,也得根据这个规范解析内容
  3. LangChain 在 v1 版本引入的 content_blocks 正是为了解决这一问题。它提供了一套标准化的、类型安全的消息内容表示方式,我们能够用同一套代码轻松处理文本、推理、图片、音频、工具调用等各类内容,无缝兼容不同的模型提供商。

一、什么是 content_blocks?

每个 LangChain 消息对象(如 HumanMessage、AIMessage)都有一个 content 属性,它直接存储模型提供商原生格式的内容(可能是字符串或字典列表)。而 content_blocks 是这样发挥其作用的:

  1. 输入时:你只需要用 LangChain 的标准内容块构造消息(如 HumanMessage(content_blocks=[...])),LangChain 会根据目标模型自动将 content_blocks 转换为该模型厂商所需的原始 content 格式(可能是字符串、特定字典列表等),并填充到消息的 content 属性中。

  2. 输出时:模型返回的消息,其 content 属性存储的是厂商原生格式(如 OpenAI 的字典列表、Anthropic 的混合结构)。当你访问消息的 content_blocks 属性时,LangChain 会惰性解析这个原生 content,将其转换为统一的标准内容块列表供你使用。

    python 复制代码
    from langchain.messages import AIMessage
    
    # 假设这是从 Anthropic 模型返回的消息
    message = AIMessage(
        content=[
            {"type": "thinking", "thinking": "用户询问天气...", "signature": "..."},
            {"type": "text", "text": "今天天气晴朗。"}
        ],
        response_metadata={"model_provider": "anthropic"}
    )
    
    # 通过 content_blocks 获取标准化内容
    for block in message.content_blocks:
        if block["type"] == "reasoning":
            print(block["reasoning"])   # 输出: 用户询问天气...
        elif block["type"] == "text":
            print(block["text"])        # 输出: 今天天气晴朗。
  • 可以看到,无论原始 content 如何,content_blocks 都能将推理内容统一表示为 {"type": "reasoning", "reasoning": ...},将文本统一表示为 {"type": "text", "text": ...}

补充:content 与 content_blocks 的关系

那有人就会问:既然有了 content_blocks,为什么还要保留 content?我平时只用 content 传字符串也能正常工作,难道所有模型都接受纯字符串吗?

实际上,LangChain 的 content 参数非常灵活,它可以接受三种形式:

  • 纯字符串(如 "你好")
  • 厂商原生格式的列表(如 OpenAI 的 [{"type": "text", "text": "你好"}])
  • 标准内容块列表(如 [{"type": "text", "text": "你好"}])

当我们传入一个字符串时,LangChain 会根据目标模型的要求自动将其转换为该模型需要的格式。例如,Anthropic 要求 content 必须是数组,LangChain 就会将 "你好" 转换成 [{"type": "text", "text": "你好"}] 再发送。所以你感觉"直接传字符串就行",其实是 LangChain 在底层帮你做了格式适配。

content_blocks 参数是专门为标准块设计的 ,它强制要求传入符合标准块定义的列表 ,提供了更好的类型安全和代码可读性。在构造消息时,content 和 content_blocks 只需要传其中一个,如果同时传入,LangChain 通常会以 content_blocks 为准(因为它更明确)。为了代码清晰,建议:

  • 纯文本场景:用 content="...",最简单。
  • 需要多模态、推理等复杂场景:用 content_blocks=[...],意图一目了然。

另外,你也可能会看到这样的代码

python 复制代码
original_blocks = request.system_message.content_blocks
new_blocks = original_blocks + [{'type': "text", "text": addendum}]
new_system_message = SystemMessage(content=new_blocks)   # 把标准块传给了 content

这段代码虽然能工作,但混用了两种概念(用 content_blocks 读取,却用 content 写入)。更好的写法是保持统一:

python 复制代码
new_system_message = SystemMessage(content_blocks=new_blocks)

这样无论是读还是写,都通过 content_blocks 属性/参数,代码自文档化,避免歧义。

二、为什么需要 content_blocks?

  1. 跨模型兼容性

    不同模型对相同类型内容的表达方式截然不同。例如:

    • OpenAI 可能将推理内容放在 content 的一个专用块中,也可能通过 response_metadata 返回。
    • Anthropic 使用 thinking 块。
    • 本地模型 可能有完全自定义的格式。

    使用 content_blocks,你可以统一提取推理、工具调用等关键信息,无需关心底层模型是谁。

  2. 简化多模态输入

    当你需要向模型发送图片、音频、文件等时,不同模型对多模态内容的字段要求各异。例如 OpenAI 要求 {"type": "image_url", "image_url": {...}},而某些模型可能使用 {"type": "image", "url": ...}。content_blocks 让你用一套标准描述多模态内容,LangChain 会自动转换为目标模型所需的格式。

三、如何使用 content_blocks?

3.1 读取标准化内容

直接访问消息对象的 content_blocks 属性即可。

python 复制代码
for block in ai_message.content_blocks:
    if block["type"] == "reasoning":
        reasoning_text = block["reasoning"]
    elif block["type"] == "tool_call":
        tool_name = block["name"]
        tool_args = block["args"]

3.2 创建消息时使用标准化块

在创建 HumanMessage、SystemMessage 等消息对象时,可以直接传入 content_blocks 参数。LangChain 会自动填充 content 字段为模型可接受的格式。

python 复制代码
from langchain.messages import HumanMessage

message = HumanMessage(content_blocks=[
    {"type": "text", "text": "描述这张图片"},
    {"type": "image", "url": "https://example.com/photo.jpg"}
])
# message.content 会被自动转换成对应模型需要的格式

3.3 让模型直接返回标准化格式

通过设置 output_version="v1",模型输出的 AIMessage 的 content 字段本身就是标准内容块列表,方便序列化或后续处理。

python 复制代码
from langchain.chat_models import init_chat_model

model = init_chat_model("gpt-5-nano", output_version="v1")
response = model.invoke("你好")
# response.content 现在是一个标准内容块列表,而不是原始提供商格式

四、支持的内容块类型

类型 用途 关键字段
text 普通文本 text, annotations
reasoning 模型推理过程 reasoning, extras
image 图片数据 url / base64, mime_type
audio 音频数据 url / base64, mime_type
video 视频数据 url / base64, mime_type
file 通用文件(如 PDF) url / base64, mime_type
tool_call 模型请求调用工具 name, args, id
tool_result 工具执行结果 content, tool_call_id
tool_call_chunk 流式工具调用片段 name, args, id, index
server_tool_call 服务端工具调用 id, name, args

五、典型应用场景

场景1:统一处理来自不同模型的推理内容

假设你的应用同时使用了 OpenAI 和 Anthropic 的模型,并需要将模型的思考过程记录到日志中。

python 复制代码
def log_reasoning(ai_message: AIMessage):
    for block in ai_message.content_blocks:
        if block["type"] == "reasoning":
            print(f"推理: {block['reasoning']}")
            # 如果模型提供了额外元数据(如签名)
            if "signature" in block.get("extras", {}):
                print(f"签名: {block['extras']['signature']}")

场景2:构建包含多模态内容的用户消息

向模型发送一张本地图片和一段文本描述。

python 复制代码
import base64

with open("photo.jpg", "rb") as f:
    base64_image = base64.b64encode(f.read()).decode()

user_msg = HumanMessage(content_blocks=[
    {"type": "text", "text": "这张照片里有什么?"},
    {"type": "image", "base64": base64_image, "mime_type": "image/jpeg"}
])

response = model.invoke([user_msg])

场景3:统一提取工具调用

当模型请求调用工具时,content_blocks 会将工具调用统一为 tool_call 类型。

python 复制代码
# 假设模型返回了工具调用
response = model_with_tools.invoke("巴黎天气怎么样?")

for block in response.content_blocks:
    if block["type"] == "tool_call":
        print(f"调用工具: {block['name']}")
        print(f"参数: {block['args']}")
        # 执行工具并返回结果...

六、何时使用 content,何时使用 content_blocks?

场景 推荐使用
只有纯文本,且只使用一个模型 直接用 content 字符串即可,最简单。
需要多模态输入(图片、音频等) 推荐使用 content_blocks 构建,免去记忆不同模型格式的烦恼。
需要跨模型处理推理、工具调用等内容 必须使用 content_blocks,否则要写大量条件判断。
对返回的消息进行结构化处理 使用 content_blocks 获得统一表示。
希望代码具备更好的可维护性和类型提示 推荐使用 content_blocks。
相关推荐
Pu_Nine_93 小时前
前端SSE(Server-Sent Events)实现详解:从原理到前端AI对话应用
前端·langchain·sse·ai对话
无风听海3 小时前
Deep Agents 的 Planning Capabilities 技术解析
langchain·deep agents
Familyism12 小时前
langchain应用
langchain
chaors17 小时前
从零学RAG0x0f:RAG 评估指标提升实战
langchain·llm·ai编程
1941s18 小时前
Google Agent Development Kit (ADK) 指南 第五章:工具集成与自定义
人工智能·python·langchain·agent·adk
在未来等你19 小时前
AI Agent Skill Day 11:RAG Retrieval技能:检索增强生成的技能封装
langchain·知识库问答·向量检索·rag·ai agent·检索增强生成·技能开发
DamianGao20 小时前
MiniMax-M2.7 与 LangChain ToolStrategy 兼容性问题解决
python·langchain
勇往直前plus21 小时前
大模型开发手记(十二):langchain skills(上):讲清楚什么是skills,优势是什么
langchain
JaydenAI1 天前
[LangChain智能体本质论]中间件装饰器是如何将函数转换成AgentMiddleware的?
python·langchain·ai编程