字典格式(Dict 格式)与 对象格式(Object 格式)
这两种格式在 LangChain 中都非常常用,但使用场景不同,谈不上绝对的谁更好,而是“谁更适合什么场景”。
1. 它们的关系:输入宽松,输出严格
- 输入时(传给 Agent/LLM):LangChain 两种都支持。你传字典,框架底层会自动帮你转成对象。
- 输出时(Agent/Stream 返回的结果):一定是对象格式。无论是 invoke 拿到的最终结果,还是你在 for chunk in agent.stream() 里面拿到的 msg,它们全都是 HumanMessage、AIMessage 或 ToolMessage 实例。因为对象才能携带 tool_calls、response_metadata 等复杂属性。
2. 对比:哪种更好?
字典格式
{"role": "user", "content": raw_text}
- 优点:极度简洁。它和 OpenAI 官方 API 的格式一模一样,写起来字数少。如果你的数据是从数据库、前端接口直接传过来的 JSON,直接用这种格式最省事。
- 缺点:无法携带 LangChain 特有的复杂属性(比如你想手动给消息加一个 id,或者模拟一个带 tool_calls 的 AI 消息,用字典就很难写)。
- 适用场景:简单的文本对话输入(User 提问)。
对象格式
from langchain_core.messages import HumanMessage HumanMessage(content=raw_text)
- 优点:功能完整、类型安全。IDE 会有代码提示;在构建复杂的 Agent、多模态应用或手动拼接对话历史时,能用到非常丰富的属性(如 SystemMessage 设定角色,ToolMessage 携带 tool_call_id)。
- 缺点:代码写起来稍微长一点。
- 适用场景:处理输出结果、复杂的对话历史拼接、需要用到高级特性(工具调用、多模态)的场景。
消息片段(Chunk)与字典格式对照表
|
对象格式(LangChain 类)
|
对应角色 (Role)
|
字典格式示例 (Dict)
|
核心使用场景 / 说明
|
|
SystemMessageChunk
|
"system"
|
{"role": "system", "content": "你是助手..."}
|
极少在流式输出中出现
。通常在对话开始前一次性设定好,很少需要一点点传输系统指令。
|
|
HumanMessageChunk
|
"user"
|
{"role": "user", "content": "北京天气..."}
|
极少出现
。通常用户的输入是一次性提交的(除非你在做语音实时转文字输入)。
|
|
AIMessageChunk
|
"assistant"
|
{"role": "assistant", "content": "今天"}
{"role": "assistant", "content": "天气"}
(流式时内容会拆分成多个字典)
|
最常用
。用于实现打字机效果(逐字显示 AI 的回复)。如果模型流式输出工具调用,参数也会通过
tool_calls
字段分块返回。
|
|
ToolMessageChunk
|
"tool"
|
{"role": "tool", "content": "晴天", "tool_call_id": "call_abc"}
|
较少出现
。通常本地工具执行是瞬间完成的,直接返回完整结果。但如果你的工具也是流式(比如流式读取大文件),就会用到它。
|
核心注意点
- Chunk 对象的特有操作:拼接(+)字典格式接收到的流式数据(如 {"content": "今天"} 和 {"content": "天气"})需要你手动去拼接字符串。而 LangChain 的 Chunk 对象重载了加号,可以直接相加:
chunk1 = AIMessageChunk(content="今天") chunk2 = AIMessageChunk(content="天气") full_msg = chunk1 + chunk2 print(full_msg.content) # 输出 "今天天气"
- 底层 API 视角如果你绕过 LangChain,直接使用 requests 或 httpx 去请求 OpenAI/火山引擎的 API,并设置 stream=True,你收到的每一行 data: {...} 解析成 JSON 后,就是上表中间那一列的字典格式。LangChain 的 .stream() 内部会帮你把这些字典自动转成 Chunk 对象。
- 工具调用的流式字典长什么样?当 AI 决定调用工具且处于流式状态时,字典格式大概是这样的(会分多次返回):
// 第一个 chunk {"role": "assistant", "content": null, "tool_calls": [{"index": 0, "id": "call_1", "type": "function", "function": {"name": "get_weather", "arguments": ""}}]} // 第二个 chunk {"role": "assistant", "content": null, "tool_calls": [{"index": 0, "function": {"arguments": "{\"city\":"}}]} // 第三个 chunk {"role": "assistant", "content": null, "tool_calls": [{"index": 0, "function": {"arguments": "\"北京\"}"}}]}
LangChain 的 AIMessageChunk 会自动帮你把这些零碎的 JSON 拼接成完整的 tool_calls 字典列表,省去了大量手动解析 JSON 的麻烦。最终建议:怎么选?
- 作为输入(传给 Agent):用你截图里的字典格式就非常好,简短清晰。如果你想写得很规范,或者输入的是包含图片/音频的多模态内容,换成 HumanMessage(...) 更好。
- 作为输出(处理 Agent 的结果):必须用对象格式。比如你在遍历 updates 模式拿到的数据时,不能写成 msg["tool_calls"],必须写成 msg.tool_calls。
结论:只有在解析输出、或者需要处理带工具的复杂消息时,才需要转换成对象格式去读取属性。不用强行二选一,把它们当作用户输入的“简写”和框架内部的“全貌”来理解就行。

下面两种案例输出结果都是一样的
案例1:对象格式的message,查询天气
import os from dotenv import load_dotenv from langchain.agents import create_agent from langchain_openai import ChatOpenAI from langchain_core.tools import tool from langchain_core.messages import HumanMessage load_dotenv() # 火山引擎配置 api_key = os.getenv("ARK_API_KEY") base_url = "https://ark.cn-beijing.volces.com/api/v3" # ========================================== # 1. 初始化模型 # ========================================== llm = ChatOpenAI( model="deepseek-v4-1-flash-economy-261017", temperature=0, api_key=api_key, base_url=base_url, ) # ========================================== # 2. 定义工具 # ========================================== @tool def get_weather(city: str) -> str: """查询指定城市的天气。""" weather_data = {"北京": "晴天 25°C", "上海": "小雨 20°C"} return weather_data.get(city, "未知") # ========================================== # 3. 创建 Agent # ========================================== agent = create_agent( model=llm, tools=[get_weather], system_prompt="你是一个专业的气象助手。" # 用 system_prompt 替代 SystemMessage ) # ========================================== # 4. 调用 Agent(使用对象风格的 Message) # ========================================== print("=== 开始调用 Agent ===") # 核心变化:使用 HumanMessage 对象作为输入 result = agent.invoke({ "messages": [HumanMessage(content="北京天气怎么样?")] }) # ========================================== # 5. 获取并打印最终结果 # ========================================== final_ai_message = result["messages"][-1] print("\n=== 最终回复 ===") # 这里拿到的也是 AIMessage 对象 print(type(final_ai_message)) print(final_ai_message.content)
案例2:字典格式的message,查询天气
import os from dotenv import load_dotenv from langchain_openai import ChatOpenAI from langchain_core.tools import tool load_dotenv() api_key = os.getenv("ARK_API_KEY") base_url = "https://ark.cn-beijing.volces.com/api/v3" # ========================================== # 1. 初始化模型 # ========================================== llm = ChatOpenAI( model="deepseek-v4-1-flash-economy-261017", temperature=0, api_key=api_key, base_url=base_url, ) # ========================================== # 2. 定义工具并绑定到模型 # ========================================== @tool def get_weather(city: str) -> str: """查询指定城市的天气。""" weather_data = {"北京": "晴天 25°C", "上海": "小雨 20°C"} return weather_data.get(city, "未知") llm_with_tools = llm.bind_tools([get_weather]) # ========================================== # 3. 构建初始对话历史(使用字典格式) # ========================================== messages = [ {"role": "system", "content": "你是一个专业的气象助手。"}, {"role": "user", "content": "北京天气怎么样?"} ] print("=== 第一轮:模型思考 ===") # ========================================== # 4. 第一次调用模型 # ========================================== ai_response = llm_with_tools.invoke(messages) # 检查模型是否要调工具 if getattr(ai_response, "tool_calls", None): print("模型决定调用工具!") tool_call = ai_response.tool_calls[0] print(f"工具名称: {tool_call['name']}") print(f"工具参数: {tool_call['args']}") print(f"调用ID: {tool_call['id']}") # 将 AI 决定调工具的消息,转换为字典格式加入历史 messages.append({ "role": "assistant", "content": ai_response.content, "tool_calls": ai_response.tool_calls }) print("\n=== 第二轮:执行工具并返回结果 ===") # ========================================== # 5. 执行本地工具,构造 Tool 字典消息 # ========================================== # 执行工具 tool_result = get_weather.invoke(tool_call["args"]) print(f"本地工具执行结果: {tool_result}") # 构造 Tool 的字典消息(必须包含 tool_call_id) messages.append({ "role": "tool", "content": tool_result, "tool_call_id": tool_call["id"] }) print("\n=== 第三轮:模型生成最终回复 ===") # ========================================== # 6. 将完整的字典历史再发给模型,得到最终回复 # ========================================== final_response = llm_with_tools.invoke(messages) print(f"最终回复: {final_response.content}") else: print(f"模型直接回复: {ai_response.content}")
浙公网安备 33010602011771号