LangChain LangGraph
Module 1 Module 2 Module 3
Module 1 · 基础能力
1 1.1a 基础模型
2 1.1b Prompting
3 1.2a Tools
4 1.2b Web Search
5 1.3 Memory
6 1.4 Multimodal
7 1.5 Personal Chef
SUM Module Summary
HomeLangChainModule 1 · 基础能力1.4 Multimodal
📓 Notebook 📖 Explained
LANGCHAIN MODULE 1 · LESSON 1.4

多模态消息
文本 · 图片 · 音频 · content 列表格式

HumanMessage 的 content 不只是字符串——它可以是一个包含文本、图片、音频的列表,让 Agent 理解多种媒体类型的输入。

1 什么是多模态消息?content 列表格式详解

在前面的课程里,HumanMessage(content="...")content 是一个字符串。但 LangChain 的消息系统支持一种更强大的格式:content 列表——把多种媒体类型组合在同一条消息里。

content 列表的每个元素是一个字典,包含 type 字段,用于区分内容类型:

type 值内容类型关键字段
"text" 纯文本 text: 文本内容字符串
"image_url" 图片(URL 或 base64) image_url.url: 图片 URL 或 data URI
"input_audio" 音频数据 data: base64 音频,format: 格式
为什么用列表格式

同一条消息里可以同时包含文字问题和图片,例如:"这张图片里有什么?(附图)"。列表格式允许混合多种内容类型,比单一字符串更灵活。字符串格式是列表格式的简写——LangChain 内部会把字符串自动转换为 [{"type": "text", "text": "..."}]

2 文本输入:最基础的 content 列表格式

即使是纯文本消息,也可以用显式的列表格式写——这在需要与图片或音频组合时非常有用:

from langchain.messages import HumanMessage
from langchain.agents import create_agent

agent = create_agent(
    model=deepseek_model(),
    system_prompt="You are a science fiction writer, create a capital city at the users request.",
)

# 显式的 content 列表格式(等价于 content="What is the capital of The Moon?")
question = HumanMessage(content=[
    {"type": "text", "text": "What is the capital of The Moon?"}
])

response = agent.invoke({"messages": [question]})
print(response['messages'][-1].content)
执行输出
**Selenopolis** — nestled in the Sea of Tranquility, where humanity first set foot on the Moon...

文本列表格式和字符串格式效果完全相同,只是写法不同。当需要在同一条消息里加入图片时,就需要在这个列表里追加图片元素。

3 图片输入:Base64 编码 + image_url 类型

把图片发送给多模态 LLM 的标准方式是:将图片转为 Base64 编码,然后用 Data URI 格式嵌入消息:

import base64

# 假设 img_bytes 是图片的 bytes 数据
img_b64 = base64.b64encode(img_bytes).decode("utf-8")

question = HumanMessage(content=[
    {
        "type": "text",
        "text": "What is in this image?"    # 文字问题
    },
    {
        "type": "image_url",
        "image_url": {
            "url": f"data:image/png;base64,{img_b64}"  # Data URI 格式
        }
    }
])

Data URI 格式解析

data:image/png;base64,{img_b64} 这个字符串的结构是:

部分含义示例
data: Data URI 协议前缀 固定写法
image/png MIME 类型(图片格式) image/jpegimage/webp
;base64, 编码方式声明 固定写法
{img_b64} 实际的 base64 编码内容 很长的字符串
也可以直接用图片 URL

如果图片可以公开访问,可以直接传图片 URL:
{"type": "image_url", "image_url": {"url": "https://example.com/image.png"}}
这种方式无需 base64 编码,但要求图片必须能被 API 服务器访问到(公开 URL)。

4 图片上传流程:FileUpload → bytes → base64

在 Jupyter Notebook 里,可以用 ipywidgets.FileUpload 提供图片上传 UI,然后读取上传的文件数据:

from ipywidgets import FileUpload
from IPython.display import display
import base64

# 显示上传按钮
uploader = FileUpload(accept='.png', multiple=False)
display(uploader)

# 上传完成后(用户选择文件后):
uploaded_file = uploader.value[0]  # 第一个上传的文件

# 关键:uploaded_file["content"] 是 memoryview,不是 bytes
content_mv = uploaded_file["content"]
img_bytes = bytes(content_mv)   # memoryview → bytes

# Base64 编码
img_b64 = base64.b64encode(img_bytes).decode("utf-8")
memoryview 坑点

FileUpload 返回的 content 字段是 memoryview 对象,不是 bytes。必须先用 bytes(content_mv)content_mv.tobytes() 转换,再进行 base64 编码。直接对 memoryview 调用 base64.b64encode() 会报错。

上传的文件信息结构

print(uploader.value[0])
文件信息字典
{
  'name': 'i1.png',
  'type': 'image/png',
  'size': 5769586,
  'content': <memory at 0x1184bd840>, # memoryview,需转换
  'last_modified': datetime.datetime(2026, 4, 22, 6, 29, ...)
}

5 音频输入:录音 + WAV 编码 + base64

同样的思路——录制麦克风音频,转为 base64,发送给支持音频输入的 LLM:

import sounddevice as sd
from scipy.io.wavfile import write
import base64
import io

# 录制 5 秒音频
duration = 5
sample_rate = 44100

print("Recording...")
audio = sd.rec(int(duration * sample_rate), samplerate=sample_rate, channels=1)
sd.wait()  # 等待录音完成
print("Done.")

# 写入内存 WAV 缓冲区
buf = io.BytesIO()
write(buf, sample_rate, audio)  # scipy 写 WAV 格式
wav_bytes = buf.getvalue()

# Base64 编码
aud_b64 = base64.b64encode(wav_bytes).decode("utf-8")

音频消息格式(针对支持音频的模型):

question = HumanMessage(content=[
    {
        "type": "input_audio",
        "data": aud_b64,
        "format": "wav"
    }
])

6 模型多模态支持现状:DeepSeek vs 专用多模态模型

并非所有模型都支持图片或音频输入。notebook 里明确提醒了这一点:

DeepSeek 当前 API 限制

DeepSeek 的公开聊天 API 是文本/工具调用导向的,官方 API 文档中没有暴露图片输入或音频输入的模型接口。
如果需要图片理解或音频理解,需要使用专用的多模态提供商(如 GPT-4V、Claude 3、Gemini 等)。

多模态支持矩阵

模型/提供商文本图片音频
DeepSeek(当前公开 API)
GPT-4o / GPT-4V
Claude 3.x (Sonnet/Opus)
Gemini 1.5 / 2.0
LangChain 的多模态抽象价值

虽然不同模型的多模态能力不同,但 LangChain 的消息格式(content 列表)是统一的。你用同一套代码结构,只需切换 model_provider 和模型名称,就能在不同的多模态 LLM 之间切换,不需要改消息构造逻辑。这就是 LangChain 抽象层的价值所在。

7 多模态消息格式速查

Lesson 1.4 核心要点
  • content 列表格式
    • 每个元素是 {"type": ..., ...} 字典
    • 支持文本、图片、音频混合
    • 字符串是列表的简写形式
  • 图片传输
    • 本地图片:bytes → base64 → Data URI
    • 注意:memoryview 需先转 bytes
    • 公开图片可直接传 URL
  • 模型选择
    • 图片/音频需要多模态模型
    • DeepSeek 当前仅支持文本
    • GPT-4o / Gemini 支持图片+音频
  • LangChain 统一格式
    • 消息格式跨 provider 统一
    • 只需切换模型,消息结构不变
    • 减少迁移成本