【LangChain】langchain_core.messages.ToolMessage 类:工具调用结果的消息类

langchain_core.messages.ToolMessage 是 LangChain 库中用于表示工具调用结果的消息类,继承自 BaseMessage。它的主要作用是将外部工具(如搜索、计算器、数据库查询)的执行结果传递回语言模型(LLM)或代理(Agent),以便模型根据这些结果生成进一步的响应。以下是对 ToolMessage 的详细介绍,涵盖其定义、功能、属性、方法、使用方式、应用场景、优化建议和注意事项。


1. 什么是 ToolMessage

ToolMessagelangchain_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 提供了以下主要功能:

  1. 封装工具结果
    • 将工具执行的输出存储在 content 字段,通常为字符串(如 "4"),但也可以是结构化数据(如 JSON)。
    • 示例:计算器工具返回 2 + 2 = 4,存储为 content="4"
  2. 关联工具调用
    • 使用 tool_call_id 字段将结果与原始工具调用请求(来自 AIMessagetool_calls)关联。
    • 支持多工具并发调用,避免结果混淆。
  3. 存储完整输出
    • 通过 artifact 字段存储工具的完整输出(如图像、原始数据),即使 content 只包含简化结果。
    • 示例:搜索工具返回网页内容和图片,content 存储文本摘要,artifact 存储完整数据。
  4. 元数据支持
    • 使用 additional_kwargs 存储额外信息,如时间戳、工具版本或执行状态。
  5. 序列化与反序列化
    • 支持将消息转换为 JSON 格式,或从 JSON 还原,便于存储对话历史或跨系统传输。
  6. 对话上下文
    • 作为对话历史的一部分,传递给模型或代理,增强上下文理解。

特点

  • 标准化:提供统一的工具结果表示,兼容不同模型和工具。
  • 模块化:与 LangChain 的聊天模型、代理、记忆等模块无缝集成。
  • 类型安全:使用 Pydantic 模型,确保字段验证和类型一致性。
  • 灵活性:支持简单和复杂工具输出,适应多种场景。

3. 核心属性与方法

核心属性

ToolMessage 继承了 BaseMessage 的属性,并添加了特定字段。以下是其主要属性:

属性类型描述示例
contentUnion[str, List[Dict]]工具调用的主要输出结果,通常为字符串,供模型直接使用。"4"[{"result": "data"}]
tool_call_idstr工具调用的唯一标识符,用于关联请求和响应。"call_123"
artifactOptional[Any]工具执行的完整输出(如图像、原始数据),可选字段。{"stdout": "data", "image": "..."}
additional_kwargsDict[str, Any]附加元数据,如时间戳或工具参数。{"timestamp": "2025-05-15"}
rolestr消息角色,固定为 "tool""tool"
typestr消息类型,固定为 "tool""tool"

属性说明

  • content:模型直接使用的结果,通常是简化的输出(如字符串)。复杂输出应存储在 artifact 中。
  • tool_call_id:关键字段,确保工具调用请求和结果的正确匹配。缺失或错误可能导致模型无法处理结果。
  • artifact:用于存储完整或复杂输出,适合需要进一步处理的情况(如多媒体数据)。
  • additional_kwargs:提供扩展性,存储任意元数据。
  • roletype:固定值,标识消息为工具结果。
核心方法

ToolMessage 继承了 BaseMessage 的方法,以下是主要方法:

方法描述输入输出示例
to_json将消息序列化为 JSON 格式。Dictmessage.to_json()
from_json从 JSON 还原消息对象(类方法)。DictToolMessageToolMessage.from_json(json_data)
__str__返回消息的字符串表示,便于调试。strstr(message)
__eq__比较两个消息是否相等。ToolMessageboolmessage1 == message2

方法说明

  • to_jsonfrom_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。

说明

  • AIMessagetool_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 在以下场景中广泛应用:

  1. 代理工作流
    • 代理调用工具后,ToolMessage 将结果反馈给模型。
    • 示例:代理调用搜索工具获取量子计算的最新进展。
  2. 多工具并发
    • 在模型同时调用多个工具时,tool_call_id 确保结果正确匹配。
    • 示例:代理同时调用搜索和计算工具。
  3. 复杂工具输出
    • 处理包含多媒体或结构化数据的工具结果。
    • 示例:分析工具返回文本和图像,artifact 存储完整数据。
  4. 对话历史管理
    • 将工具结果存储在对话历史中,供后续对话使用。
    • 示例:用户询问计算结果后继续提问相关问题。
  5. 调试与日志
    • 使用 additional_kwargs 记录工具调用的元数据。
    • 示例:记录工具执行时间或状态。
  6. 数据管道
    • 在数据处理管道中传递工具结果。
    • 示例:将数据库查询结果传递给模型进行总结。

6. 优化建议

以下是使用 ToolMessage 时的优化建议,帮助提高效率和可靠性:

(1) 确保 tool_call_id 正确性
  • 始终验证 tool_call_idAIMessagetool_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
  • 将完整或复杂输出存储在 artifactcontent 只包含模型所需部分。
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_idAIMessagetool_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}")
    
  • 安全性
    • 避免在 contentadditional_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 封装工具执行结果,与 AIMessagetool_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。

说明

  • 代理同时调用 SearchWebCalculator 工具。
  • 每个工具的结果通过 ToolMessage 反馈,tool_call_id 确保正确匹配。
  • 模型根据多个 ToolMessage 生成综合回答。

10. 学习资源


11. 总结

  • 定义ToolMessage 是 LangChain 中用于封装工具调用结果的消息类,继承自 BaseMessage
  • 核心属性
    • content:工具结果(通常为字符串)。
    • tool_call_id:工具调用 ID。
    • artifact:完整输出(可选)。
    • additional_kwargs:元数据。
    • roletype:固定为 "tool"
  • 核心方法
    • to_jsonfrom_json:序列化/反序列化。
    • __str____eq__:字符串表示和比较。
  • 功能:封装工具结果、关联请求与响应、存储复杂输出、支持序列化、增强对话上下文。
  • 应用场景:代理工作流、多工具并发、复杂工具输出、对话历史管理、调试与日志。
  • 优化建议:确保 tool_call_id 正确、优化 content 格式、高效使用 artifact、序列化优化、监控与调试。
  • 注意事项:工具调用一致性、内容格式、artifact 使用、序列化安全、安全性、版本兼容性、模型支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

彬彬侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值