LangChain中message两种格式定义与案例

字典格式(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"}
较少出现
。通常本地工具执行是瞬间完成的,直接返回完整结果。但如果你的工具也是流式(比如流式读取大文件),就会用到它。
 

核心注意点

  1. Chunk 对象的特有操作:拼接(+)字典格式接收到的流式数据(如 {"content": "今天"} 和 {"content": "天气"})需要你手动去拼接字符串。而 LangChain 的 Chunk 对象重载了加号,可以直接相加:
    chunk1 = AIMessageChunk(content="今天")
    chunk2 = AIMessageChunk(content="天气")
    full_msg = chunk1 + chunk2 
    print(full_msg.content) # 输出 "今天天气"
  2. 底层 API 视角如果你绕过 LangChain,直接使用 requests 或 httpx 去请求 OpenAI/火山引擎的 API,并设置 stream=True,你收到的每一行 data: {...} 解析成 JSON 后,就是上表中间那一列的字典格式。LangChain 的 .stream() 内部会帮你把这些字典自动转成 Chunk 对象。
  3. 工具调用的流式字典长什么样?当 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 的麻烦。

    最终建议:怎么选?

    1. 作为输入(传给 Agent):用你截图里的字典格式就非常好,简短清晰。如果你想写得很规范,或者输入的是包含图片/音频的多模态内容,换成 HumanMessage(...) 更好。
    2. 作为输出(处理 Agent 的结果):必须用对象格式。比如你在遍历 updates 模式拿到的数据时,不能写成 msg["tool_calls"],必须写成 msg.tool_calls。
    结论:只有在解析输出、或者需要处理带工具的复杂消息时,才需要转换成对象格式去读取属性。不用强行二选一,把它们当作用户输入的“简写”和框架内部的“全貌”来理解就行。
     
     
     
 image
 下面两种案例输出结果都是一样的

案例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}")

 

 

 

 

 

 

 

 

 

 

 

posted on 2026-11-02 15:34  马丁的车夫  阅读(17)  评论(0)    收藏  举报