Agent 工作原理中的 Message、Part 与 Span
本文通过一次天气查询,解释 Agent 如何组织模型调用、工具执行与消息历史,帮助读者理解 LLM Trace 中的内容层级和 Span 边界。
0x01 Agent 的执行循环
Message(消息)是一条带角色的消息,Part(片段)是消息内部的内容单元。一条模型消息可以同时包含文本和工具调用请求;这些片段属于同一条消息,但承担不同用途。
Agent 通常运行在一个循环里:
用户提出问题
↓
Agent 将指令、历史消息和可用工具发给模型
↓
模型返回消息
├─ 给出答案 → 结束
└─ 请求调用工具
↓
Agent 执行工具
↓
把工具结果加入消息历史
└────────→ 再次调用模型2
3
4
5
6
7
8
9
10
11
12
模型生成工具调用请求后,Agent 程序负责执行工具,再将结果加入消息历史,发起下一次模型调用。每次模型请求和每次工具执行分别记录为独立 Span。
0x02 一次天气查询的完整调用
用户提问:“深圳明天天气怎么样,需要带伞吗?”假设 Agent 查询一次天气后给出回答,这段执行包含三个子 Span:
Agent Span
├─ 模型调用 Span A:生成工具调用请求
├─ 工具调用 Span B:执行天气查询
└─ 模型调用 Span C:读取工具结果,生成最终回答2
3
4
示例按执行顺序展开,省略可用工具定义等请求参数。
a. 模型调用 Span A:决定查询天气
第一次模型调用的输入包含系统指令和用户消息:
消息 [0] · 系统指令
片段 [0] · 文本:“你是天气助手。”
消息 [1] · 用户消息
片段 [0] · 文本:“深圳明天天气怎么样,需要带伞吗?”2
3
4
5
Span A 返回的一条模型消息包含两个片段:
输出消息 [0]
片段 [0] · 文本:“我先查询一下天气。”
片段 [1] · 工具调用:
name = get_weather
arguments = {"city": "深圳", "date": "明天"}2
3
4
5
此时模型只生成了 get_weather 的调用请求,还没有获得天气结果。文本片段和工具调用片段都属于 Span A 的输出。
b. 工具调用 Span B:执行天气查询
Agent 程序识别到 tool_call 片段后,调用天气工具。实际执行过程记录在独立的工具 Span B 中:
工具:get_weather
输入:{"city": "深圳", "date": "明天"}
输出:{"weather": "雨", "rain_probability": 0.9}2
3
Agent 将 Span B 的输出包装成工具消息,与 Span A 的模型输出一起加入消息历史。
c. 模型调用 Span C:读取结果并回答
第二次模型调用使用新的 Span C。它的输入携带前面的消息历史,包括模型发出的工具调用请求和刚获得的工具结果:
消息 [0] · 系统指令
片段 [0] · 文本:“你是天气助手。”
消息 [1] · 用户消息
片段 [0] · 文本:“深圳明天天气怎么样,需要带伞吗?”
消息 [2] · 模型消息
片段 [0] · 文本:“我先查询一下天气。”
片段 [1] · 工具调用:
name = get_weather
arguments = {"city": "深圳", "date": "明天"}
消息 [3] · 工具消息
片段 [0] · 工具返回:{"weather": "雨", "rain_probability": 0.9}2
3
4
5
6
7
8
9
10
11
12
13
14
模型读到查询结果后,在 Span C 的输出中生成“预计有雨,建议带伞”的最终回答。
工具结果同时出现在 Span B 的输出和 Span C 的输入,分别记录工具返回与模型接收这两个环节。工具只执行了一次,输入历史里的工具消息本身不会额外创建一个 Span。
消息 [3] 表示它在 Span C 输入消息列表中的位置,编号从 0 开始。它是该列表的第四条消息,不是 Trace 中的第四个 Span,也不代表 Agent 的第四轮执行。
0x03 在 Trace 详情中理解内容层级
| 概念 | 在执行过程中的含义 |
|---|---|
| Agent Span | 一段 Agent 执行过程,可能包含多次模型和工具调用 |
| 模型调用 Span | 一次模型请求及响应;输入可能携带很多历史消息 |
| Message/消息 | 一条带角色的消息,例如用户、模型或工具消息 |
| Part/片段 | 消息内部的一个内容单元,例如文本、图片、工具调用或工具返回 |
| 工具调用 Span | Agent 实际执行一次工具的过程 |
按用户消息、推理过程、规划的工具调用等分类打平,是在单个 Span 的输入或输出内对 Part 重新分组。类内保留原有消息和片段的先后顺序,并用“消息编号+片段编号”标明来源位置。
模型 Span 输出中的“规划的工具调用”表示模型发出了调用请求;工具 Span 记录该请求的实际执行。保留两者的关联,可以从模型决策定位到工具执行,同时保持各个 Span 的独立边界。