langchain_core.messages.ToolMessage
是 LangChain 库中用于表示工具调用结果的消息类,继承自 BaseMessage
。它的主要作用是将外部工具(如搜索、计算器、数据库查询)的执行结果传递回语言模型(LLM)或代理(Agent),以便模型根据这些结果生成进一步的响应。以下是对 ToolMessage
的详细介绍,涵盖其定义、功能、属性、方法、使用方式、应用场景、优化建议和注意事项。
1. 什么是 ToolMessage
?
ToolMessage
是 langchain_core.messages
模块中的一个具体消息类,设计用于封装工具调用的执行结果。它在 LangChain 的代理工作流中扮演关键角色,帮助模型或代理处理外部工具的输出。ToolMessage
的核心目标是:
- 传递工具结果:将工具的输出(如计算结果、搜索数据)反馈给模型。
- 关联请求与响应:通过
tool_call_id
确保工具调用请求和结果的正确匹配。 - 支持复杂输出:允许存储完整工具输出(如图像、原始数据),同时提供简化的内容供模型使用。
背景:
- 在 LangChain 中,工具调用是代理或链的重要功能。例如,模型可能需要调用搜索工具获取实时数据或使用计算器解决数学问题。
ToolMessage
取代了旧版的FunctionMessage
,适配现代工具调用 API(如 OpenAI 的工具调用接口),在 LangChain 0.2.17 及以上版本中引入。- 它通常与
AIMessage
(包含工具调用请求)配合使用,形成完整的工具调用工作流。
简单比喻:
可以将 ToolMessage
想象为一个“快递包裹”,其中:
content
是包裹里的主要物品(模型直接使用的工具结果)。tool_call_id
是包裹的追踪号码,确保送达正确的目的地。artifact
是额外的附件(完整的工具输出,可能包含复杂数据)。
2. 核心功能
ToolMessage
提供了以下主要功能:
- 封装工具结果:
- 将工具执行的输出存储在
content
字段,通常为字符串(如"4"
),但也可以是结构化数据(如 JSON)。 - 示例:计算器工具返回
2 + 2 = 4
,存储为content="4"
。
- 将工具执行的输出存储在
- 关联工具调用:
- 使用
tool_call_id
字段将结果与原始工具调用请求(来自AIMessage
的tool_calls
)关联。 - 支持多工具并发调用,避免结果混淆。
- 使用
- 存储完整输出:
- 通过
artifact
字段存储工具的完整输出(如图像、原始数据),即使content
只包含简化结果。 - 示例:搜索工具返回网页内容和图片,
content
存储文本摘要,artifact
存储完整数据。
- 通过
- 元数据支持:
- 使用
additional_kwargs
存储额外信息,如时间戳、工具版本或执行状态。
- 使用
- 序列化与反序列化:
- 支持将消息转换为 JSON 格式,或从 JSON 还原,便于存储对话历史或跨系统传输。
- 对话上下文:
- 作为对话历史的一部分,传递给模型或代理,增强上下文理解。
特点:
- 标准化:提供统一的工具结果表示,兼容不同模型和工具。
- 模块化:与 LangChain 的聊天模型、代理、记忆等模块无缝集成。
- 类型安全:使用 Pydantic 模型,确保字段验证和类型一致性。
- 灵活性:支持简单和复杂工具输出,适应多种场景。
3. 核心属性与方法
核心属性
ToolMessage
继承了 BaseMessage
的属性,并添加了特定字段。以下是其主要属性:
属性 | 类型 | 描述 | 示例 |
---|---|---|---|
content | Union[str, List[Dict]] | 工具调用的主要输出结果,通常为字符串,供模型直接使用。 | "4" 或 [{"result": "data"}] |
tool_call_id | str | 工具调用的唯一标识符,用于关联请求和响应。 | "call_123" |
artifact | Optional[Any] | 工具执行的完整输出(如图像、原始数据),可选字段。 | {"stdout": "data", "image": "..."} |
additional_kwargs | Dict[str, Any] | 附加元数据,如时间戳或工具参数。 | {"timestamp": "2025-05-15"} |
role | str | 消息角色,固定为 "tool" 。 | "tool" |
type | str | 消息类型,固定为 "tool" 。 | "tool" |
属性说明:
content
:模型直接使用的结果,通常是简化的输出(如字符串)。复杂输出应存储在artifact
中。tool_call_id
:关键字段,确保工具调用请求和结果的正确匹配。缺失或错误可能导致模型无法处理结果。artifact
:用于存储完整或复杂输出,适合需要进一步处理的情况(如多媒体数据)。additional_kwargs
:提供扩展性,存储任意元数据。role
和type
:固定值,标识消息为工具结果。
核心方法
ToolMessage
继承了 BaseMessage
的方法,以下是主要方法:
方法 | 描述 | 输入 | 输出 | 示例 |
---|---|---|---|---|
to_json | 将消息序列化为 JSON 格式。 | 无 | Dict | message.to_json() |
from_json | 从 JSON 还原消息对象(类方法)。 | Dict | ToolMessage | ToolMessage.from_json(json_data) |
__str__ | 返回消息的字符串表示,便于调试。 | 无 | str | str(message) |
__eq__ | 比较两个消息是否相等。 | ToolMessage | bool | message1 == message2 |
方法说明:
to_json
和from_json
:支持消息存储和传输,常用于对话历史的持久化。__str__
:提供人类可读的表示,适合日志记录或调试。__eq__
:用于比较消息内容,通常在测试或验证中应用。
4. 使用方式与代码示例
以下是通过代码示例展示 ToolMessage
的使用方式,涵盖基本创建、工具调用工作流、复杂输出处理和序列化等场景。
示例 1:基本 ToolMessage
创建
创建一个简单的 ToolMessage
表示计算器工具的结果:
from langchain_core.messages import ToolMessage
from langchain_core.tools import tool
# 定义工具
@tool
def calculator(expression: str) -> str:
"""执行数学计算"""
return str(eval(expression))
# 模拟工具调用
tool_call_id = "call_123"
tool_result = calculator.invoke("2 + 2")
# 创建 ToolMessage
tool_message = ToolMessage(content=tool_result, tool_call_id=tool_call_id)
# 打印消息
print(tool_message)
输出:
content='4' role='tool' additional_kwargs={} artifact=None tool_call_id='call_123'
说明:
content
存储工具结果"4"
。tool_call_id
标识工具调用。artifact
未使用,适合简单场景。
示例 2:工具调用工作流
在代理中结合 ToolMessage
处理工具调用:
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain_core.tools import tool
# 定义工具
@tool
def calculator(expression: str) -> str:
"""执行数学计算"""
return str(eval(expression))
# 初始化模型并绑定工具
llm = ChatOpenAI(api_key="your-openai-key").bind_tools([calculator])
# 创建消息
messages = [HumanMessage(content="计算 5 + 3")]
# 调用模型
response = llm.invoke(messages)
# 处理工具调用
if response.tool_calls:
tool_call = response.tool_calls[0]
tool_result = calculator.invoke(tool_call["args"])
messages.extend([
AIMessage(content="", tool_calls=[tool_call]),
ToolMessage(content=tool_result, tool_call_id=tool_call["id"])
])
# 继续对话
final_response = llm.invoke(messages)
print(final_response.content)
输出:
计算结果为 8。
说明:
AIMessage
的tool_calls
包含工具调用请求(如调用calculator
)。ToolMessage
封装工具结果,并通过tool_call_id
关联请求。- 模型根据
ToolMessage
生成最终响应。
示例 3:复杂工具输出
处理包含多部分输出的工具(如搜索返回文本和图像):
from langchain_core.messages import ToolMessage
# 模拟复杂工具输出
tool_output = {
"stdout": "量子计算的最新进展包括超导量子比特的突破。",
"stderr": None,
"artifacts": {"type": "image", "base64_data": "/9j/4gIcSU..."}
}
# 创建 ToolMessage
tool_message = ToolMessage(
content=tool_output["stdout"],
artifact=tool_output,
tool_call_id="call_456"
)
# 打印消息
print(tool_message)
输出:
content='量子计算的最新进展包括超导量子比特的突破。' role='tool' additional_kwargs={} artifact={'stdout': '量子计算的最新进展包括超导量子比特的突破。', 'stderr': None, 'artifacts': {'type': 'image', 'base64_data': '/9j/4gIcSU...'}} tool_call_id='call_456'
说明:
content
存储模型所需的简化结果(文本摘要)。artifact
存储完整输出(包括图像数据)。tool_call_id
确保结果与请求匹配。
示例 4:序列化与反序列化
将 ToolMessage
序列化为 JSON 并还原:
from langchain_core.messages import ToolMessage
# 创建 ToolMessage
message = ToolMessage(
content="4",
tool_call_id="call_123",
additional_kwargs={"timestamp": "2025-05-15"}
)
# 序列化为 JSON
json_data = message.to_json()
print(json_data)
# 从 JSON 还原
restored_message = ToolMessage.from_json(json_data)
print(restored_message.content, restored_message.tool_call_id)
输出:
{'type': 'tool', 'content': '4', 'additional_kwargs': {'timestamp': '2025-05-15'}, 'tool_call_id': 'call_123'}
4 call_123
说明:
to_json
生成 JSON 表示,适合存储或传输。from_json
还原为ToolMessage
实例,保留所有字段。
示例 5:结合代理的完整工作流
在代理中处理多工具调用:
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain_core.tools import Tool
from langchain_core.callbacks import StdOutCallbackHandler
from langchain_core.runnables import RunnableConfig
import os
import getpass
# 配置 API 密钥
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("请输入 OpenAI API 密钥:")
# 定义工具
def search_web(query: str) -> str:
return f"搜索结果:{query} 的最新信息..."
search_tool = Tool(name="SearchWeb", func=search_web, description="搜索网络信息")
# 初始化模型
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 设置提示模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个研究助手,擅长搜索和总结信息。"),
MessagesPlaceholder(variable_name="messages"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# 创建代理
agent = create_openai_tools_agent(llm, [search_tool], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[search_tool], verbose=True)
# 配置 RunnableConfig
config = RunnableConfig(callbacks=[StdOutCallbackHandler()], max_iterations=3)
# 创建消息
messages = [HumanMessage(content="量子计算的最新进展是什么?")]
# 执行任务
response = agent_executor.invoke({"messages": messages}, config=config)
print(response["output"])
输出:
[AgentExecutor] 正在执行...
[Tool: SearchWeb] 输入:量子计算的最新进展
[Tool Output] 搜索结果:量子计算的最新信息...
[Final Answer] 量子计算的最新进展包括超导量子比特的突破...
说明:
ToolMessage
自动由代理生成,封装SearchWeb
工具的结果。tool_call_id
由代理管理,确保结果与请求匹配。- 模型根据
ToolMessage
生成最终回答。
5. 应用场景
ToolMessage
在以下场景中广泛应用:
- 代理工作流:
- 代理调用工具后,
ToolMessage
将结果反馈给模型。 - 示例:代理调用搜索工具获取量子计算的最新进展。
- 代理调用工具后,
- 多工具并发:
- 在模型同时调用多个工具时,
tool_call_id
确保结果正确匹配。 - 示例:代理同时调用搜索和计算工具。
- 在模型同时调用多个工具时,
- 复杂工具输出:
- 处理包含多媒体或结构化数据的工具结果。
- 示例:分析工具返回文本和图像,
artifact
存储完整数据。
- 对话历史管理:
- 将工具结果存储在对话历史中,供后续对话使用。
- 示例:用户询问计算结果后继续提问相关问题。
- 调试与日志:
- 使用
additional_kwargs
记录工具调用的元数据。 - 示例:记录工具执行时间或状态。
- 使用
- 数据管道:
- 在数据处理管道中传递工具结果。
- 示例:将数据库查询结果传递给模型进行总结。
6. 优化建议
以下是使用 ToolMessage
时的优化建议,帮助提高效率和可靠性:
(1) 确保 tool_call_id
正确性
- 始终验证
tool_call_id
与AIMessage
的tool_calls
匹配。
if tool_call["id"] != expected_id:
raise ValueError("工具调用 ID 不匹配")
- 在多工具场景中,检查所有
tool_calls
是否都有对应的ToolMessage
。
missing_ids = [call["id"] for call in response.tool_calls if call["id"] not in tool_message_ids]
if missing_ids:
raise ValueError(f"缺少工具调用结果:{missing_ids}")
(2) 优化 content
格式
- 确保
content
是模型可理解的格式(如简洁的字符串或 JSON)。 - 示例:将复杂输出简化为关键字段。
content = json.dumps({"result": tool_output["key_data"]})
tool_message = ToolMessage(content=content, tool_call_id=tool_call["id"])
- 避免在
content
中包含冗余或无关信息,减少 token 消耗。
(3) 高效使用 artifact
- 将完整或复杂输出存储在
artifact
,content
只包含模型所需部分。
tool_message = ToolMessage(
content=tool_output["summary"],
artifact=tool_output,
tool_call_id=tool_call["id"]
)
- 仅在需要时使用
artifact
,避免不必要的内存占用。
if not needs_full_output:
tool_message = ToolMessage(content=tool_result, tool_call_id=tool_call["id"])
(4) 序列化优化
- 批量序列化消息,减少 I/O 开销。
json_data = [msg.to_json() for msg in messages]
- 使用高效的 JSON 库(如
orjson
)提高序列化性能。
import orjson
json_data = orjson.dumps([msg.to_json() for msg in messages])
(5) 监控与调试
- 使用回调记录
ToolMessage
的创建和传递。
from langchain_core.callbacks import BaseCallbackHandler
class ToolCallback(BaseCallbackHandler):
def on_tool_end(self, output, **kwargs):
print(f"工具输出:{output}")
config = {"callbacks": [ToolCallback()]}
- 结合 LangSmith 分析工具调用性能。
from langsmith import Client
config = {"callbacks": [Client(api_key="your-langsmith-key")]}
- 记录
additional_kwargs
中的元数据,便于调试。
tool_message = ToolMessage(
content=tool_result,
tool_call_id=tool_call["id"],
additional_kwargs={"execution_time": "0.5s"}
)
(6) 上下文优化
- 动态调整
content
内容,根据模型需求提供最相关的信息。
content = f"结果:{tool_output['summary']}" if model_needs_summary else tool_output["raw"]
- 使用
additional_kwargs
存储上下文信息,如工具版本或用户 ID。
tool_message = ToolMessage(
content=tool_result,
tool_call_id=tool_call["id"],
additional_kwargs={"tool_version": "1.0", "user_id": "user_001"}
)
7. 注意事项
以下是使用 ToolMessage
时需要注意的关键点:
- 工具调用一致性:
- 确保
tool_call_id
与AIMessage
的tool_calls
字段匹配,否则模型可能无法正确处理结果。 - 示例:检查
tool_call_id
是否存在。
if not tool_call.get("id"): raise ValueError("缺少工具调用 ID")
- 确保
- 内容格式:
content
应为模型可理解的格式,避免过于复杂的数据结构。- 示例:将复杂 JSON 简化为字符串。
content = str(tool_output["result"])
- Artifact 使用:
- 仅在需要存储完整输出时使用
artifact
,避免不必要的内存占用。 - 示例:仅存储多媒体或原始数据。
- 仅在需要存储完整输出时使用
- 序列化安全:
- 确保 JSON 数据格式正确,避免反序列化错误。
try: msg = ToolMessage.from_json(json_data) except ValueError as e: print(f"反序列化失败:{e}")
- 安全性:
- 避免在
content
或additional_kwargs
中包含敏感信息(如 API 密钥)。 - 验证工具输出,防止注入攻击。
if "<script>" in tool_result: raise ValueError("工具输出包含不安全内容")
- 避免在
- 版本兼容性:
ToolMessage
在 LangChain 0.2.17 中引入,确保使用兼容版本(>=0.2.17)。FunctionMessage
已标记为废弃,优先使用ToolMessage
。
- 模型支持:
- 确保使用的模型支持工具调用(如 GPT-4、Claude)。
- 某些开源模型可能不支持
tool_calls
,需检查文档。
8. 与 LangChain 生态的结合
ToolMessage
与 LangChain 生态中的其他组件紧密集成,以下是主要结合点:
- 聊天模型(Chat Models):
ToolMessage
作为对话历史的一部分,传递给BaseChatModel
实例(如ChatOpenAI
)。
llm.invoke(messages + [tool_message])
- 代理(Agents):
- 在代理工作流中,
ToolMessage
反馈工具结果,帮助代理继续推理。
agent.invoke({"messages": messages + [tool_message]})
- 在代理工作流中,
- 工具(Tools):
ToolMessage
封装工具执行结果,与AIMessage
的tool_calls
配合。
tool_message = ToolMessage(content=tool_result, tool_call_id=tool_call["id"])
- 记忆(Memory):
- 结合
ConversationBufferMemory
存储ToolMessage
,维护对话历史。
from langchain.memory import ConversationBufferMemory memory = ConversationBufferMemory(return_messages=True) memory.save_context({"input": "计算"}, {"output": tool_message})
- 结合
- 提示模板(Prompt Templates):
- 使用
ChatPromptTemplate
管理包含ToolMessage
的对话。
from langchain_core.prompts import ChatPromptTemplate prompt = ChatPromptTemplate.from_messages([("human", "{input}"), ("tool", "{tool_result}")])
- 使用
- 回调(Callbacks):
- 使用
RunnableConfig
配置回调,监控ToolMessage
处理。
from langchain_core.callbacks import StdOutCallbackHandler config = {"callbacks": [StdOutCallbackHandler()]}
- 使用
9. 综合示例:多工具代理
以下是一个结合 ToolMessage
、多工具调用和代理的完整示例,展示复杂工作流:
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, AIMessage, ToolMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.agents import create_openai_tools_agent, AgentExecutor
from langchain_core.tools import Tool
from langchain_core.callbacks import StdOutCallbackHandler
from langchain_core.runnables import RunnableConfig
import os
import getpass
# 配置 API 密钥
if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("请输入 OpenAI API 密钥:")
# 定义工具
def search_web(query: str) -> str:
return f"搜索结果:{query} 的最新信息..."
def calculator(expression: str) -> str:
return str(eval(expression))
search_tool = Tool(name="SearchWeb", func=search_web, description="搜索网络信息")
calc_tool = Tool(name="Calculator", func=calculator, description="执行数学计算")
# 初始化模型
llm = ChatOpenAI(model="gpt-4o", temperature=0)
# 设置提示模板
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个研究助手,擅长搜索和计算。"),
MessagesPlaceholder(variable_name="messages"),
MessagesPlaceholder(variable_name="agent_scratchpad")
])
# 创建代理
agent = create_openai_tools_agent(llm, [search_tool, calc_tool], prompt)
agent_executor = AgentExecutor(agent=agent, tools=[search_tool, calc_tool], verbose=True)
# 配置 RunnableConfig
config = RunnableConfig(callbacks=[StdOutCallbackHandler()], max_iterations=5)
# 创建消息
messages = [HumanMessage(content="量子计算的最新进展是什么?另外,计算 5 + 3。")]
# 执行任务
response = agent_executor.invoke({"messages": messages}, config=config)
print(response["output"])
输出:
[AgentExecutor] 正在执行...
[Tool: SearchWeb] 输入:量子计算的最新进展
[Tool Output] 搜索结果:量子计算的最新信息...
[Tool: Calculator] 输入:5 + 3
[Tool Output] 8
[Final Answer] 量子计算的最新进展包括超导量子比特的突破。5 + 3 的计算结果为 8。
说明:
- 代理同时调用
SearchWeb
和Calculator
工具。 - 每个工具的结果通过
ToolMessage
反馈,tool_call_id
确保正确匹配。 - 模型根据多个
ToolMessage
生成综合回答。
10. 学习资源
- 官方文档:https://python.langchain.com/api_reference/core/messages/langchain_core.messages.tool.ToolMessage.html
- 消息概述:https://python.langchain.com/docs/concepts/messages/
- GitHub 源码:https://github.com/langchain-ai/langchain/tree/master/libs/core/langchain_core/messages
- LangSmith:用于调试消息流(https://smith.langchain.com)
- 社区教程:LangChain 官方博客、YouTube 视频
11. 总结
- 定义:
ToolMessage
是 LangChain 中用于封装工具调用结果的消息类,继承自BaseMessage
。 - 核心属性:
content
:工具结果(通常为字符串)。tool_call_id
:工具调用 ID。artifact
:完整输出(可选)。additional_kwargs
:元数据。role
和type
:固定为"tool"
。
- 核心方法:
to_json
、from_json
:序列化/反序列化。__str__
、__eq__
:字符串表示和比较。
- 功能:封装工具结果、关联请求与响应、存储复杂输出、支持序列化、增强对话上下文。
- 应用场景:代理工作流、多工具并发、复杂工具输出、对话历史管理、调试与日志。
- 优化建议:确保
tool_call_id
正确、优化content
格式、高效使用artifact
、序列化优化、监控与调试。 - 注意事项:工具调用一致性、内容格式、
artifact
使用、序列化安全、安全性、版本兼容性、模型支持。