调一次大模型,Token 花了多少?哪个 Prompt 触发了 Tool Calling?Agent 的多轮 Session 在哪一步卡住了?——这些问题在传统可观测体系里没有标准答案。HTTP 请求有 OpenTelemetry 的 SemConv(语义约定),数据库调用有 JDBC 的规范,但 GenAI 领域长期处于"各家自扫门前雪"的状态:每家平台各定义一套字段名,指标对不上、日志拼不拢、链路串不起来。
阿里与蚂蚁开源的 LoongSuite GenAI SemConv,正是要补上这块缺口。它把 Model、Prompt、 Token、Tool Calling、Agent、Memory、Session 这些 GenAI 系统的核心概念,定义成一套统一的语义属性和指标,让采集、存储、展示都走同一条轨道。
GenAI 可观测性的核心对象有哪些
传统微服务的可观测性围绕"请求"展开——一次 HTTP 调用从入口到数据库再到响应,链路清晰。GenAI Agent 系统的观测对象则完全不同,LoongSuite 把它们归纳为以下几类:
| 类别 | 核心属性 | 为什么重要 |
|---|---|---|
| Model | gen_ai.request.model、gen_ai.response.model |
同一个 Prompt 给不同模型,输出和成本差异巨大,必须区分 |
| Prompt | gen_ai.prompt.type(system/user/assistant)、gen_ai.prompt.template |
Prompt 是 GenAI 的"代码",模板版本和类型直接影响行为 |
| Token | gen_ai.usage.input_tokens、gen_ai.usage.output_tokens |
Token 是计费和性能的硬指标,输入/输出必须分开统计 |
| Tool Calling | gen_ai.tool.call.name、gen_ai.tool.call.arguments、gen_ai.tool.call.result |
Agent 调用外部工具的"子调用",是链路追踪的关键节点 |
| Agent | gen_ai.agent.id、gen_ai.agent.type |
多 Agent 协作时,谁做了什么决策必须可追溯 |
| Memory / Session | gen_ai.session.id、gen_ai.memory.type(short_term/long_term) |
多轮对话的上下文绑定和记忆检索,是 Agent 行为一致性的保障 |
这些属性不是随意命名的字符串,而是遵循 OpenTelemetry SemConv 的命名规范——gen_ai.* 前缀、小写、点分层级。这意味着任何支持 OpenTelemetry 的后端(Jaeger、Prometheus、SkyWalking 等)都能直接识别,不需要额外适配。
和 OpenTelemetry 官方 SemConv 的关系
OpenTelemetry社区本身也在推进 GenAI SemConv(目前仍在实验阶段),LoongSuite 并不是另起炉灶,而是在官方草案基础上做了两件事:
- 补齐 Agent 维度:官方草案偏重单次 LLM 调用的观测,LoongSuite 加入了 Agent、Memory、Session、Tool Calling 等多步编排场景的属性,覆盖了 Agent 系统的完整生命周期。
- 给出采集实现参考:光有属性定义不够,LoongSuite 提供了基于 OpenTelemetry SDK 的 Instrumentation 实现范例,让开发者能直接抄到项目里用。
简单说:官方定义了"砖的规格",LoongSuite 不仅确认了规格,还把"怎么砌墙"的图纸一起给了出来。
实战:用 LoongSuite SemConv 给一次 Agent 调用加上标准链路
下面是一个最小可运行的 Python 示例,展示如何用 OpenTelemetry SDK + LoongSuite GenAI SemConv 的属性约定,把一次"LLM 调用 + Tool Calling"的完整过程记录为一条标准 Trace。
先安装依赖:
pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp
然后运行以下代码(需要先启动一个 OTLP Receiver,最简单的方式是用 Jaeger 的 all-in-one 镜像):
docker run -d --name jaeger \
-p 16686:16686 \
-p 4317:4317 \
jaegertracing/all-in-one:1.57
Python 示例:
"""
用 LoongSuite GenAI SemConv 属性标注一次 Agent 调用的 Trace。
假设:用户提问 → LLM 决定调用 search_weather 工具 → 工具返回结果 → LLM 生成最终回答。
运行前确保 OTLP Receiver(如 Jaeger)已在 localhost:4317 就绪。
"""
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
# ---------- 1. 初始化 Tracer ----------
resource = Resource.create({"service.name": "genai-agent-demo"})
provider = TracerProvider(resource=resource)
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint="http://localhost:4317"))
)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer("agent-demo", "0.1.0")
# ---------- 2. SemConv 属性常量(LoongSuite GenAI SemConv 约定) ----------
GEN_AI_REQUEST_MODEL = "gen_ai.request.model"
GEN_AI_RESPONSE_MODEL = "gen_ai.response.model"
GEN_AI_USAGE_INPUT_TOK = "gen_ai.usage.input_tokens"
GEN_AI_USAGE_OUTPUT_TOK = "gen_ai.usage.output_tokens"
GEN_AI_PROMPT_TYPE = "gen_ai.prompt.type"
GEN_AI_TOOL_CALL_NAME = "gen_ai.tool.call.name"
GEN_AI_TOOL_CALL_RESULT = "gen_ai.tool.call.result"
GEN_AI_SESSION_ID = "gen_ai.session.id"
GEN_AI_AGENT_ID = "gen_ai.agent.id"
# ---------- 3. 模拟一次 Agent 调用 ----------
def mock_llm_call(prompt: str, model: str) -> dict:
"""模拟 LLM 返回:这里假装模型决定调用 search_weather 工具"""
return {
"tool_call": {"name": "search_weather", "arguments": {"city": "北京"}},
"input_tokens": 58,
"output_tokens": 12,
"response_model": model,
}
def search_weather(city: str) -> str:
"""模拟外部工具调用"""
return f"{city}:晴,气温 28°C"
def run_agent(user_input: str, session_id: str):
model = "qwen-plus"
agent_id = "weather-agent-01"
# --- Span 1: 整体 Agent Session ---
with tracer.start_as_current_span(
"gen_ai.agent.session",
attributes={
GEN_AI_SESSION_ID: session_id,
GEN_AI_AGENT_ID: agent_id,
},
) as session_span:
# --- Span 2: LLM 推理调用 ---
with tracer.start_as_current_span(
"gen_ai.llm.call",
attributes={
GEN_AI_REQUEST_MODEL: model,
GEN_AI_PROMPT_TYPE: "user",
},
) as llm_span:
result = mock_llm_call(user_input, model)
llm_span.set_attributes({
GEN_AI_RESPONSE_MODEL: result["response_model"],
GEN_AI_USAGE_INPUT_TOK: result["input_tokens"],
GEN_AI_USAGE_OUTPUT_TOK: result["output_tokens"],
})
# --- Span 3: Tool Calling ---
with tracer.start_as_current_span(
"gen_ai.tool.call",
attributes={
GEN_AI_TOOL_CALL_NAME: result["tool_call"]["name"],
},
) as tool_span:
tool_result = search_weather(result["tool_call"]["arguments"]["city"])
tool_span.set_attribute(GEN_AI_TOOL_CALL_RESULT, tool_result)
# --- Span 4: LLM 生成最终回答 ---
with tracer.start_as_current_span(
"gen_ai.llm.call",
attributes={
GEN_AI_REQUEST_MODEL: model,
GEN_AI_PROMPT_TYPE: "assistant",
},
) as final_span:
final_span.set_attributes({
GEN_AI_USAGE_INPUT_TOK: 72,
GEN_AI_USAGE_OUTPUT_TOK: 45,
})
print(f"Agent 完成,session={session_id}")
# ---------- 4. 执行 ----------
if __name__ == "__main__":
run_agent("北京今天天气怎么样?", session_id="sess-20240610-001")
运行后打开 http://localhost:16686,在 Jaeger UI 中搜索 gen_ai.agent.session,你会看到一条包含四个子 Span 的完整链路,每个 Span 都携带了 LoongSuite SemConv 定义的标准属性——模型名、Token 用量、工具调用名和结果、Session 和 Agent ID 一目了然。
要改什么才能跑真实场景? 把 mock_llm_call 替换成对 OpenAI / DashScope SDK 的真实调用,把 search_weather 替换成你的实际 Tool 函数,其余 Trace 结构和属性标注完全不用改。
采纳建议与边界认知
什么时候值得引入?
- 你的 Agent 系统已经超过单轮对话,涉及 Tool Calling、多步编排、多 Agent 协作——这时候没有标准 SemConv,链路基本不可读。
- 你需要跨团队、跨平台对比不同模型的 Token 成本和响应质量——统一属性名是前提。
- 你的可观测后端已经支持 OpenTelemetry(Jaeger、SkyWalking、Datadog 等),引入 LoongSuite SemConv 只是在 Span 上多加几个 attribute,几乎零成本。
当前边界和风险:
- OpenTelemetry 官方的 GenAI SemConv 还在实验阶段,属性名可能调整。LoongSuite 尽量对齐官方,但后续需要跟进变更。
- SemConv 解决的是"字段叫什么、记在哪",不解决"怎么自动采集"。如果你用的是 LangChain、AutoGen 等框架,目前需要手动埋点或等待框架层面的 Instrumentation 支持。
- Token 计数依赖模型 API 返回的
usage字段,不同厂商的返回格式不完全一致,采集层需要做适配。
落地 Checklist:
- ✅ 确认你的 Trace 后端支持 OpenTelemetry OTLP 协议
- ✅ 在 Agent 入口 Span 标注
gen_ai.session.id和gen_ai.agent.id - ✅ 每次 LLM 调用标注
gen_ai.request.model+ Token 用量 - ✅ Tool Calling 单独建 Span,标注
gen_ai.tool.call.name和返回结果 - ✅ 建立仪表盘:按 Model 分组统计 Token 消耗,按 Session 追踪多轮对话链路
- ⚠️ 关注 OpenTelemetry GenAI SemConv 的版本演进,及时对齐属性名变更
标准化不是终点,而是让后续的仪表盘、告警、成本分析都能站在同一个地基上。LoongSuite GenAI SemConv 把 GenAI 可观测性从"各家自写日志"拉到了"统一语义约定"的起点,下一步就看社区和框架厂商能不能把 Instrumentation 自动化真正铺开。