过去我们把消息的内容当成一个字符串:content: "你好"。但要表达「一段文字 + 一张图片」这种混合内容时,字符串就无能为力了。于是 LangChain 引入了 content block(内容块):把消息内容从「一坨文本」升级成「一组带类型的结构化块」。这篇文章讲清楚它是什么、和 content 有什么区别、为什么要这么做、支持哪些类型,以及怎么用它传递图片等多模态数据。
一、content block 是什么
一条消息的 content 不仅可以是一个字符串,还可以是一个内容块数组。每个内容块是一个带 type 字段的对象,用来描述一种具体的内容:
content: [
{ type: "text", text: "这张图片里有什么?" },
{ type: "image", url: "https://example.com/cat.png" },
]
这里的每个 { type, ... } 就是一个 content block。文本块用 text 承载文字,图片块用 url 或 data 承载图像。
二、和 content 有什么区别
严格来说,它们不是并列关系,而是「容器」和「元素」的关系:
content是消息上的字段,类型是string | Array<ContentBlock>——既可以是纯字符串,也可以是内容块数组;- content block 是数组里的每一个元素,是结构化的、带
type的单个内容单元。
也就是说:content 是容器,content block 是里面的内容单元。 当 content 是数组时,它就是一个 content block 列表。
围绕这一点,LangChain 还提供了两个便利的读取方式:
message.content:原样返回你传入的内容(字符串或数组);message.contentBlocks:把内容规范化成统一的标准块数组,无论你当初用的是哪种写法;message.text:只提取所有文本块拼成字符串,自动忽略图片等非文本块。
一个直观的例子:用 OpenAI 的 v0 写法传图片:
const msg = new HumanMessage({
content: [
{ type: "text", text: "这是什么?" },
{ type: "image_url", image_url: { url: "https://example.com/cat.png" } },
],
});
msg.content // 原样:还是 image_url 那套
msg.contentBlocks // 规范化:[{ type:"text", ... }, { type:"image", url:"..." }]
msg.text // "这是什么?"
contentBlocks 会把它统一成标准形式,text 则帮你干净地拿到纯文本。
三、为什么要这么做
把一个字符串拆成一堆带类型的块,看起来是变复杂了,实际上解决了几类真实问题:
- 支持多模态:文字、图片、音频、视频可以在一条消息里混排,字符串做不到;
- 统一各家厂商的格式:OpenAI、Anthropic、Google 对「带图消息」的字段各不相同,content block 提供了一套标准表示(standard blocks),再各自转换到厂商格式,让上层代码不用关心差异;
- 承载更丰富的信息:除了文本和图像,还能表达模型的推理过程(
reasoning)、工具调用(tool_call)、文件等,让「一次回复」可以被结构化描述; - 便于流式与处理:内容块是独立单元,天然适合流式地增量产生和消费;程序也能按类型分别处理不同块。
一句话:用结构化的块,换取对多模态与多厂商的统一表达。
四、支持哪些类型
LangChain 通过 KNOWN_BLOCK_TYPES 暴露了它认识的标准块类型:
import { KNOWN_BLOCK_TYPES } from "@langchain/core/messages";
console.log(KNOWN_BLOCK_TYPES);
// text, reasoning, tool_call, tool_call_chunk, invalid_tool_call,
// server_tool_call, server_tool_call_chunk, server_tool_call_result,
// image, video, audio, text-plain, file
可以大致分成三组:
- 文本类:
text(文本)、text-plain(纯文本); - 多模态类:
image、video、audio、file; - 工具 / 推理类:
tool_call、tool_call_chunk、invalid_tool_call、reasoning,以及若干server_tool_call*。
其中图片块的标准形式有三种来源,取其一即可:
{ type: "image", url: "https://..." } // 用 URL
{ type: "image", mimeType: "image/png", data: b64 } // 原始 base64 + mimeType
{ type: "image", fileId: "file-abc" } // 已上传文件的 id
五、用 content block 传图片等多模态数据
同一条带图片的消息,有几种常见写法,最终都会被规范化成同一种标准块。
写法 1:v0 / OpenAI 原生写法
const msg = new HumanMessage({
content: [
{ type: "text", text: "这张图片里有什么?" },
{ type: "image_url", image_url: { url: "https://example.com/cat.png" } },
],
});
写法 2:标准图片块(推荐)
const msg = new HumanMessage({
content: [
{ type: "text", text: "这张图片里有什么?" },
{ type: "image", url: "https://example.com/cat.png" },
],
});
写法 3:原始 base64 + mimeType
适合本地图片,避免依赖外部 URL:
const b64 = "..."; // 图片的 base64 字符串(不含前缀)
const msg = new HumanMessage({
content: [
{ type: "text", text: "这张图片里有什么?" },
{ type: "image", mimeType: "image/png", data: b64 },
],
});
写法 4:base64 data URL
也可以直接把 data:image/png;base64,... 整串放进 url,LangChain 会自动解析出 mimeType 和 data:
const msg = new HumanMessage({
content: [
{ type: "text", text: "这张图片里有什么?" },
{ type: "image_url", image_url: { url: `data:image/png;base64,${b64}` } },
],
});
无论用哪种写法,读取 msg.contentBlocks 都会得到统一的标准块({ type:"image", url } 或 { type:"image", mimeType, data }),msg.text 则只返回文本部分。
一个重要的坑:camelCase vs snake_case
标准块用 camelCase:mimeType、data、url、fileId。
而早期还有一种已废弃的 data block,用 snake_case:source_type、mime_type:
// 旧式(不推荐,但兼容)
{ type: "image", source_type: "base64", mime_type: "image/png", data: b64 }
// 等效于 { type: "image", mimeType: "image/png", data: b64 }
// 或 { type: "image", url: "https://..." }
混用这两套字段是常见的踩坑点。建议统一用标准块,contentBlocks 会自动帮你规范化。
辅助函数
LangChain 还提供了几个处理内容块的工具函数:
import { isDataContentBlock, parseBase64DataUrl } from "@langchain/core/messages";
isDataContentBlock({ type: "image", source_type: "base64", ... }); // 判断旧式块
parseBase64DataUrl({ dataUrl: "data:image/png;base64,...." }); // 解析 data URL
六、注意:模型得支持对应模态
内容块只是表达,能不能处理取决于模型。比如 DeepSeek 的 deepseek-chat 是纯文本模型,不支持图片输入;要做图片理解,需要换成多模态模型(如 OpenAI 的 gpt-4o)。用法完全一样,只是把带图片的消息发给对应的模型即可:
import { ChatOpenAI } from "@langchain/openai";
const model = new ChatOpenAI({ model: "gpt-4o" });
const res = await model.invoke([
new HumanMessage({
content: [
{ type: "text", text: "请描述这张图片。" },
{ type: "image", url: "https://example.com/cat.png" },
],
}),
]);
console.log(res.content);
七、小结
content block 是 LangChain 用来表达消息内容的结构化单元:content 是容器(string | 内容块数组),content block 是数组里的元素。它的价值在于用统一的表示支持多模态(图片、音频、视频、文件)和多厂商(各自转换到不同 API),并承载文本、推理、工具调用等更丰富的语义。
使用时记住三点:传图片推荐用标准块 { type:"image", url } 或 { type:"image", mimeType, data };读取时用 contentBlocks 拿规范化结果、用 .text 拿纯文本;并且务必确认目标模型支持你要传的模态。