GenAI 可观测性不再盲人摸象:LoongSuite GenAI SemConv 把 AI Agent 指标拉回标准轨道

2026-05-13 23 预计阅读时间:1 分钟
来源:my.oschina.net AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:10 分钟

调一次大模型,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.modelgen_ai.response.model 同一个 Prompt 给不同模型,输出和成本差异巨大,必须区分
Prompt gen_ai.prompt.type(system/user/assistant)、gen_ai.prompt.template Prompt 是 GenAI 的"代码",模板版本和类型直接影响行为
Token gen_ai.usage.input_tokensgen_ai.usage.output_tokens Token 是计费和性能的硬指标,输入/输出必须分开统计
Tool Calling gen_ai.tool.call.namegen_ai.tool.call.argumentsgen_ai.tool.call.result Agent 调用外部工具的"子调用",是链路追踪的关键节点
Agent gen_ai.agent.idgen_ai.agent.type 多 Agent 协作时,谁做了什么决策必须可追溯
Memory / Session gen_ai.session.idgen_ai.memory.type(short_term/long_term) 多轮对话的上下文绑定和记忆检索,是 Agent 行为一致性的保障

这些属性不是随意命名的字符串,而是遵循 OpenTelemetry SemConv 的命名规范——gen_ai.* 前缀、小写、点分层级。这意味着任何支持 OpenTelemetry 的后端(Jaeger、Prometheus、SkyWalking 等)都能直接识别,不需要额外适配。

和 OpenTelemetry 官方 SemConv 的关系

OpenTelemetry社区本身也在推进 GenAI SemConv(目前仍在实验阶段),LoongSuite 并不是另起炉灶,而是在官方草案基础上做了两件事:

  1. 补齐 Agent 维度:官方草案偏重单次 LLM 调用的观测,LoongSuite 加入了 Agent、Memory、Session、Tool Calling 等多步编排场景的属性,覆盖了 Agent 系统的完整生命周期。
  2. 给出采集实现参考:光有属性定义不够,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:

  1. ✅ 确认你的 Trace 后端支持 OpenTelemetry OTLP 协议
  2. ✅ 在 Agent 入口 Span 标注 gen_ai.session.idgen_ai.agent.id
  3. ✅ 每次 LLM 调用标注 gen_ai.request.model + Token 用量
  4. ✅ Tool Calling 单独建 Span,标注 gen_ai.tool.call.name 和返回结果
  5. ✅ 建立仪表盘:按 Model 分组统计 Token 消耗,按 Session 追踪多轮对话链路
  6. ⚠️ 关注 OpenTelemetry GenAI SemConv 的版本演进,及时对齐属性名变更

标准化不是终点,而是让后续的仪表盘、告警、成本分析都能站在同一个地基上。LoongSuite GenAI SemConv 把 GenAI 可观测性从"各家自写日志"拉到了"统一语义约定"的起点,下一步就看社区和框架厂商能不能把 Instrumentation 自动化真正铺开。


相关推荐