Message 与 Tool 的时序关系

985 字
5 分钟
Message 与 Tool 的时序关系

Message 与 Tool 的时序关系#

工具调用分为两个阶段:

阶段一:LLM 生成参数(message_* 事件) 阶段二:Agent 执行工具(tool_execution_* 事件)

message_end 的 stopReason=“toolUse” 是两者的边界。

为什么分两阶段?#

用具体场景说明:用户问”北京天气”,AI 调用工具查询。

LLM 只负责”说”要调用什么工具——生成工具名称和参数。 Agent 才负责实际”执行”工具函数——调用代码,获取结果。

这个分离是职责边界:

  • LLM 是生成者(生成调用意图)
  • Agent 是执行者(执行调用动作)

完整时序图#

用户输入: "北京今天天气怎么样"
├── turn_start ← 开始本轮对话
├── message_start ← LLM 开始生成
│ │ message: AssistantMessage(初始空状态)
├── message_update (多次) ← 流式生成工具调用参数
│ │ assistantMessageEvent: toolcall_start
│ │ assistantMessageEvent: toolcall_delta '{"query"'
│ │ assistantMessageEvent: toolcall_delta ': "北京天气"'
│ │ assistantMessageEvent: toolcall_end
│ │ → toolCall完整: { id: "tc1", name: "search", arguments: { query: "北京天气" } }
├── message_end ← 参数生成完成
│ │ message.content[0]: ToolCall { name: "search", ... }
│ │ stopReason: "toolUse" ← 关键:表示需要执行工具
│ ─────────── 阶段边界 ───────────
│ LLM 完成 → Agent 开始执行
│ ─────────── 阶段边界 ───────────
├── tool_execution_start ← Agent 执行工具
│ │ toolCallId: "tc1"
│ │ toolName: "search"
│ │ args: { query: "北京天气" }
├── tool_execution_end ← 工具执行完成
│ │ result: "北京晴天,25°C"
│ │ isError: false
├── turn_end ← 本轮结束
│ │ toolResults: [工具结果消息]
│ ─────── Agent 用结果继续生成 ───────
├── turn_start(新一轮)
├── message_start
├── message_update (text_delta)
├── message_end ← stopReason = "stop"
├── turn_end
└── agent_end ← 所有消息汇总

时序关键点

  • message_end 之后才有 tool_execution(阶段边界)
  • tool_execution_end 之后可能触发新一轮 turn(用结果继续生成)
  • agent_end 是最终汇总点

两阶段监听协作#

一个完整的工具调用监听,展示两阶段如何衔接:

agent.subscribe((event) => {
// ===== 阶段一:LLM生成参数 =====
if (event.type === "message_update") {
const e = event.assistantMessageEvent;
if (e.type === "toolcall_start") {
console.log("[LLM] 开始生成:", e.contentIndex);
}
if (e.type === "toolcall_delta") {
// 参数JSON片段,如 '{"query": "天气"'
console.log("[LLM] 参数增量:", e.delta);
}
if (e.type === "toolcall_end") {
// 参数完整,即将进入阶段二
console.log("[LLM] 参数完整:", e.toolCall.arguments);
console.log("─── 阶段边界 ───");
}
}
if (event.type === "message_end") {
// stopReason = "toolUse" 表示即将执行工具
if (event.message.stopReason === "toolUse") {
console.log("[边界] LLM完成,Agent开始执行");
}
}
// ===== 阶段二:Agent执行工具 =====
if (event.type === "tool_execution_start") {
console.log("[Agent] 执行:", event.toolName);
}
if (event.type === "tool_execution_end") {
console.log("[Agent] 结果:", event.result);
}
});

阶段边界判断

  • message_end.stopReason === "toolUse" → 即将执行工具
  • tool_execution_start → 开始执行
  • tool_execution_end → 执行完成

一个 message 可能包含多个工具调用#

LLM 可以在一次响应中生成多个工具调用,Agent 会并行执行:

message_update:
toolcall_end (contentIndex=0) → 工具 A: search
toolcall_end (contentIndex=1) → 工具 B: translate
toolcall_end (contentIndex=2) → 工具 C: summarize
message_end:
message.content = [ToolCall_A, ToolCall_B, ToolCall_C]
stopReason = "toolUse"
────────────────── 然后并行执行 ──────────────────
tool_execution_start → 工具 A 开始
tool_execution_start → 工具 B 开始
tool_execution_start → 工具 C 开始
tool_execution_end → 工具 A 完成
tool_execution_end → 工具 B 完成
tool_execution_end → 工具 C 完成
turn_end:
toolResults = [结果_A, 结果_B, 结果_C]

监听时,通过 contentIndex 区分不同的工具调用:

agent.subscribe((event) => {
if (event.type === "message_update") {
const e = event.assistantMessageEvent;
if (e.type === "toolcall_end") {
console.log(`[工具${e.contentIndex}] ${e.toolCall.name}`);
}
}
if (event.type === "tool_execution_start") {
console.log(`[执行] ${event.toolName} (${event.toolCallId})`);
}
});

选择指南:监听哪层?#

需求监听哪层具体事件
显示”正在生成工具调用…”底层message_update + toolcall_start
显示参数生成过程(预览)底层message_update + toolcall_delta
显示”正在执行工具…”高层tool_execution_start
获取工具执行结果高层tool_execution_end
判断本轮是否有工具调用高层turn_end.toolResults.length
处理工具执行错误高层tool_execution_end.isError

原则

  • 需要实时预览参数生成过程 → 监听底层(toolcall_*)
  • 其他情况 → 优先用高层(tool_execution_*)

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!

赞助
Message 与 Tool 的时序关系
https://firefly.cuteleaf.cn/posts/learn-pi/event&message/05-时序关系理解/
作者
AltumSisy
发布于
2026-05-30
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
AltumSisy
Hello, I'm AltumSisy.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
32
分类
3
标签
25
总字数
68,347
运行时长
0
最后活动
0 天前

文章目录