面向行业Agent的全链路可观测性设计:Trace、Metrics与Feedback闭环

一、引言:Agent最大的风险是"你不知道它在干什么"

我参与构建过多个面向行业的Agent应用------财务对账、合规审查、能源异常分析。这些系统有一个共同点:上线初期运行得"挺好",直到某天业务方拿着一个错误结果来质问,而我们在日志里什么都找不到

传统应用的日志里,一行请求就有明确的调用栈:哪个接口、哪个参数、哪个返回值、哪次异常。但Agent完全不同------一次任务可能包含数十次LLM调用、十几次工具调用、若干次重试和计划修订,每一步的"推理"是模型内部的概率过程,输出受Prompt、上下文窗口、随机性影响,同样的输入可能得到完全不同的执行路径。它本质是一个黑盒里的随机游走,而传统观测手段是为确定性调用链设计的。

这就是行业Agent对可观测性提出更高要求的根本原因。本文记录我在实践中沉淀的一套方案:用Trace追踪每一次推理与动作,用Metrics量化系统的健康度与成本,用Feedback闭环让结果质量可度量、可回灌,三者共同构成一条"看得见、可度量、能改进"的完整链路。

二、行业Agent的观测需求与传统方案的差距

先明确问题。我把行业Agent的可观测性需求归纳为四类,而传统监控(APM、日志平台)在这四类上都有明显缺口:

观测需求 传统方案 缺口
链路追踪 按HTTP调用链追踪 Agent内部是LLM调用+工具调用+重试的复合图,不是简单请求链
性能监控 接口延迟、错误率 缺Token消耗、上下文长度、工具调用耗时等Agent特有指标
质量评估 靠断言与告警 输出"正确性"无法自动判定,需结合业务校验与用户反馈
成本归因 按服务/接口 一次任务跨多个模型、多个工具,成本需按任务维度归集

最关键的差距在最后两项:传统可观测性回答"系统坏没坏",而行业Agent还要回答"结果对不对、贵不贵"。这决定了Agent可观测性的设计必须从"监控请求"升级为"度量一次智能决策的全过程"。

三、Trace层:一次任务的完整"旅行记录"

Trace解决"看得见"的问题:一次Agent任务从收到请求到返回结果,期间发生的每一次LLM调用、工具调用、重试、计划修订,都要有迹可循,且能按任务关联成一颗树。

3.1 Span模型设计

我定义Agent特有的Span类型,而不是复用HTTP Span了事:

python 复制代码
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Optional

@dataclass
class AgentSpan:
    span_id: str
    trace_id: str
    parent_id: Optional[str]
    kind: str                  # llm_call | tool_call | plan_revise | retry | guard_check
    name: str                  # 如 "llm:gpt-4o", "tool:query_balance"
    start_ts: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
    end_ts: Optional[datetime] = None
    status: str = "running"    # running/ok/error/timeout
    attributes: dict = field(default_factory=dict)

    def finish(self, status: str, **attrs):
        self.end_ts = datetime.now(timezone.utc)
        self.status = status
        self.attributes.update(attrs)
        emit(self)             # 异步上报到Trace后端

    @property
    def duration_ms(self) -> float:
        if not self.end_ts:
            return 0.0
        return (self.end_ts - self.start_ts).total_seconds() * 1000

每个Span的attributes按kind承载关键信息:LLM调用 记录模型名、输入输出Token数、温度、Prompt前若干字符(注意脱敏,见后文);工具调用 记录工具名、参数摘要、返回值摘要、错误码;计划修订记录修订前后计划差异、修订原因。

3.2 上下文传播:Tracer贯穿整个执行

为了让所有Span挂在同一棵树上,需要一个上下文感知的Tracer------Agent执行到哪里,当前Span就自动成为新Span的父节点:

python 复制代码
import contextvars

class Tracer:
    _current = contextvars.ContextVar("agent_span", default=None)

    @classmethod
    def start(cls, kind: str, name: str, **attrs) -> AgentSpan:
        parent = cls._current.get()
        span = AgentSpan(
            span_id=new_id(),
            trace_id=parent.trace_id if parent else new_id(),
            parent_id=parent.span_id if parent else None,
            kind=kind, name=name, attributes=attrs,
        )
        cls._current.set(span)
        return span

    @classmethod
    def end(cls, span: AgentSpan, status: str = "ok", **attrs):
        span.finish(status, **attrs)
        # 恢复父Span为当前上下文,保证并发/嵌套正确
        parent = find_span(span.parent_id)
        cls._current.set(parent)

# 用法:包装LLM与工具调用
def traced_llm_call(messages, **kwargs):
    span = Tracer.start("llm_call", "llm:gpt-4o",
                        model=kwargs.get("model"),
                        prompt_chars=len(json.dumps(messages)))
    try:
        resp = llm_chat(messages, **kwargs)
        Tracer.end(span, "ok",
                   in_tokens=resp.usage.prompt_tokens,
                   out_tokens=resp.usage.completion_tokens)
        return resp
    except Exception as e:
        Tracer.end(span, "error", error=str(e))
        raise

contextvars而非全局变量,是为了保证并发执行多个子任务(能源分析里并行跑多个台区)时,每个执行流各自的Span树互不串扰------这是Agent并行化后最容易踩的坑:并发一开,Span全串到同一棵树上。

3.3 结构化日志与语义事件

Span是"链路骨架",但链路里的"肉"是推理过程。Thought(推理)、Action(动作)、Observation(观察)这些ReAct循环中的中间产物,必须以结构化事件落盘。我把它们设计成与Span绑定的语义事件:

python 复制代码
@dataclass
class AgentEvent:
    trace_id: str
    span_id: str
    seq: int                 # 事件序号,还原执行顺序
    event_type: str          # thought | action | observation | guard | feedback
    content: dict            # 结构化内容,禁止自由文本

def log_event(trace_id: str, span_id: str, event_type: str, **content):
    event = AgentEvent(trace_id=trace_id, span_id=span_id,
                       seq=next_seq(trace_id), event_type=event_type,
                       content=content)
    append_events(trace_id, event)   # JSON Lines 落盘/上送

三个纪律:内容必须结构化(自由文本没法检索与统计)、必须脱敏(见后文)、必须带序号(Agent执行顺序对还原推理至关重要)。

四、Metrics层:系统的"仪表盘"与"温度计"

Trace回答"这一单发生了什么",Metrics回答"整体健康吗"。行业Agent的Metrics不是简单照搬RED(请求率、错误率、延迟),而是围绕任务生命周期重新定义。

4.1 核心指标集

我按四类维护指标,全部按任务维度打点:

python 复制代码
class AgentMetrics:
    def __init__(self, registry):
        self.registry = registry    # Prometheus等注册表

    def observe_task(self, task_id: str, result: dict, meta: dict):
        # 1. 流量与成功率
        self.registry.counter("agent_tasks_total",
                              labels={"agent": meta["agent"]}).inc()
        ok = 1 if result.get("ok") else 0
        self.registry.counter("agent_tasks_success",
                              labels={"agent": meta["agent"]}).inc(ok)

        # 2. 延迟分布(含"首Token延迟"与"全任务延迟"两个维度)
        self.registry.histogram("agent_task_duration_seconds",
                                labels={"agent": meta["agent"]}).observe(
                                    meta["duration_s"])

        # 3. Token与成本(按模型维度归因)
        for model, usage in meta["token_usage"].items():
            self.registry.counter("agent_tokens_total",
                                  labels={"agent": meta["agent"], "model": model}).inc(usage)
            self.registry.counter("agent_cost_usd_total",
                                  labels={"agent": meta["agent"], "model": model}).inc(
                                      usage * PRICE[model])

        # 4. 质量代理指标:重试率、修订率、守卫拦截率
        self.registry.counter("agent_retries_total",
                              labels={"agent": meta["agent"]}).inc(meta.get("retries", 0))
        self.registry.counter("agent_plan_revisions_total",
                              labels={"agent": meta["agent"]}).inc(meta.get("revisions", 0))

4.2 指标的告警语义必须"面向业务"

行业Agent的告警不能只写"错误率>5%"。我沉淀了一套业务语义化的告警规则,举三个真实案例:

  • "同一条任务连续N次修订仍失败" → 告警"该业务场景Agent能力不足",触发的是能力复盘,而不是简单重试;
  • "平均每任务Token消耗环比+30%" → 告警"成本异常",往往对应Prompt膨胀或上下文泄漏,需要检查是否把历史对话全量塞进了每次调用;
  • "守卫层拦截率骤升" → 告警"输入质量恶化或新注入手法出现",需要人工检查被拦截样本,更新注入规则。

这类告警的共同点是:指标只是引线,背后要挂一个明确的"人工动作"。没有动作的告警只会变成告警疲劳。

五、Feedback闭环:让"结果对不对"可度量、可回灌

Trace和Metrics回答"发生了什么、整体如何",但行业场景最尖锐的问题它们回答不了:这个结果到底对不对? 对账Agent说"差异已处理",它处理对了吗?预测Agent说"明日峰值8500kW",它预测准了吗?

答案是Feedback闭环------把"事后才知道的对错"转化为"可度量的信号",再回灌进系统改进质量。我把它拆成四个环节。

5.1 自动校验反馈:结果在返回前先自证

能自动判定的反馈,绝不等到用户来点。我把业务校验器注册进闭环,结果生成后立即校验:

python 复制代码
@dataclass
class Feedback:
    trace_id: str
    task_id: str
    source: str            # auto_check | user_rating | business_outcome
    score: float           # 0.0 ~ 1.0
    comment: str
    created_at: datetime

class FeedbackLoop:
    def __init__(self, validators: list[Callable], store):
        self.validators = validators
        self.store = store

    def collect_auto(self, task: dict, result: dict) -> list[Feedback]:
        fb = []
        for v in self.validators:
            ok, reason, score = v(task, result)   # 业务校验器
            fb.append(Feedback(
                trace_id=task["trace_id"], task_id=task["id"],
                source=f"auto:{v.__name__}",
                score=score if ok else 0.0,
                comment=reason))
        return fb

校验器示例:对账任务校验"差异数是否为0、调整单是否都有关联凭证";预测任务校验"预测值与真实值的MAPE"(真实值到达后延迟触发)。

5.2 用户反馈:结构化而非"点个赞"

行业场景下,"点赞/点踩"远远不够。我用的反馈表单是结构化的:结果可读性、数据准确性、处理完整性、是否需人工兜底,以及一条必填或选填的文本说明。结构化的价值在于可统计、可聚类------"处理完整性"连续两周得分低于0.6,就能定位到某个Agent的某个步骤,而不是靠用户吐槽才知道。

5.3 业务结果反馈:终极裁判是业务指标

最硬核的反馈来自业务结果本身。能源预测Agent的反馈,不是用户打分,而是"第二天真实负荷出来后,预测的MAPE是多少";对账Agent的反馈,是"财务复核时发现的漏单数"。这类反馈需要延迟关联------任务早就结束了,真实结果在几天后才产生,闭环必须支持"按trace_id补记反馈":

python 复制代码
def record_delayed_feedback(trace_id: str, source: str, score: float, comment: str):
    store.upsert(Feedback(trace_id=trace_id, task_id="",
                          source=source, score=score,
                          comment=comment, created_at=now()))

5.4 反馈回灌:从信号到改进

收集反馈不是目的,回灌才是。我做了三层回灌,由近及远:

第一层,实时纠错。 单条反馈得分过低(如自动校验不通过),立即触发重试、修订或转人工,不等用户发现。

第二层,质量画像。 反馈按Agent、按Skill、按任务类型聚合,形成"质量热力图":哪个Agent、哪个步骤、哪类输入最常出问题。每月生成一份质量报告,直接驱动下个月的优化排期。

第三层,数据与模型回灌。 低分样本经脱敏与人工复核后,进入两类资产:一是Few-shot示例库 ,改进Prompt;二是评测集,任何Prompt、模型、技能升级都必须先过评测集------分数不降才允许上线。这是闭环的最终形态:系统每天产生数据,数据反过来改进系统。

python 复制代码
def build_eval_set(feedback: list[Feedback], min_score: float = 0.3,
                   sample_size: int = 200) -> list[dict]:
    """低分样本 → 脱敏 → 人工复核 → 评测集"""
    bad = [f for f in feedback if f.score < min_score]
    sampled = random.sample(bad, min(sample_size, len(bad)))
    return [redact_and_review(f) for f in sampled]   # 人工复核后入库

六、贯穿三层的关键横切:脱敏与成本

三根支柱之上,还有两个必须贯穿始终的横切关注点。

脱敏。 Trace里的Prompt片段、工具参数、反馈内容,几乎必然包含业务敏感信息。我的纪律是:Span属性与事件内容在写入前先过脱敏层(PII正则+关键词),保证观测系统自身不成为泄密源。宁可损失一点可读性,也不能让监控日志成为安全事件。

成本。 Token消耗天然是Agent最重要的Metrics之一,但它的价值不止于"省钱":Token消耗异常往往是质量问题的最早信号------同一任务Token翻倍,通常意味着Agent在绕圈子、上下文在膨胀、或计划在反复修订。我把"成本异常"与"质量下降"绑定告警,而不是单独看数字。

七、工程实践的五个关键决策

1. 观测先于上线,而不是补课。 Trace埋点、指标打点、反馈通道,在Agent框架层就内置好,而不是等出事后从日志里捞。框架层统一埋点,业务代码零侵入------这是"能坚持观测"的前提,靠自觉埋点是坚持不下去的。

2. 一个trace_id贯穿到底。 从用户请求、到Agent编排、到每次LLM与工具调用、再到延迟反馈,共享同一个trace_id。行业排查问题的标准动作就是"拿着trace_id查全链路",没有它,跨系统对账就是噩梦。

3. 采样要有策略,但不能丢尾部。 高流量下全量Trace成本太高,我用"头部全采样+尾部全采样":常规任务按比例采样,但失败、超时、高成本任务100%采样------你永远要为最坏情况保留完整现场

4. 指标要能"下钻"到单次任务。 仪表盘上的聚合数字只是入口,每个指标都必须能点进对应的一批trace_id。聚合给你趋势,下钻给你真相,两者缺一不可。

5. Feedback是观测的最终交付物。 如果Trace和Metrics最后没有汇聚成"结果质量可度量、改进有数据支撑"的闭环,那观测就只是"更贵的日志"。把Feedback闭环当作系统的一等公民来设计,而不是事后追加的功能。

八、总结

行业Agent的可观测性,本质上是在回答三个递进的问题:它做了什么(Trace)、系统健康吗(Metrics)、结果对吗(Feedback)。前两个是传统可观测性工程的延伸,第三个才是Agent带来的新命题------它要求观测系统从"监控基础设施"升级为"度量智能决策质量的工程基础设施"。

这套体系上线后,我们第一次能做到:任何一条业务投诉,都能在五分钟内还原任务的完整执行路径与推理过程;每个Agent的质量与成本都有月度画像;每次Prompt或模型升级,都有评测集把关。Agent不再是一个"感觉还行"的黑盒,而是一个可度量、可审计、可改进的工程系统。

可观测性不会让Agent变得更聪明,但它让Agent变得可以被信任。在行业落地的语境里,后者比前者更重要。

相关推荐
虎虎(_ _)。゜zzZ1 天前
重构 Agent 思维链:LangGraph 核心架构的深度解构与实战
大模型应用·langgraph·agent框架·ai工程化·状态机 python
观测云2 天前
Fastjson 高危 RCE(CVE-2026-16723):如何用观测云快速发现并完成修复
网络·安全·可观测性·观测云
ylj_dev2 天前
从 0 构建 AI Workload Platform(七):可观测性、故障注入与性能验证
go·prometheus·可观测性·opentelemetry·故障注入
寻道码路2 天前
大模型工程化实战(五):LLM 网关到底怎么选 - 2026 自研 / LiteLLM/Portkey/Kong/One API 全面对比
大模型·llmops·llm网关·ai网关·ai工程化
ZGi.ai3 天前
ZGI 运行日志:还原任务与节点状态
可观测性·工作流·企业ai·zgi·运行日志
Akiyama_Mio-Kon5 天前
GitHub Code Quality 有了趋势面板后:怎样把 Agent 修复从“刷绿”变成可验证的质量闭环
devsecops·可观测性·代码质量·ai agent·codeql·github code·工程治理
寻道码路9 天前
大模型工程化实战(三):LLM 灰度发布 + 自动熔断,1% 流量试错、指标劣化自动回滚
大模型·mlops·ai工程化·llm灰度发布·自动熔断·金丝雀·生产落地
ManageEngine卓豪11 天前
全栈可见性落地指南:可观测性投入为何难以转化为实际可视能力
aiops·可观测性·应用性能监控·运维监控·全栈可见性
小马过河R12 天前
Graph Engineering 深度解析:模型越强,越需要给它画好“地图”
人工智能·langchain·graph·ai工程化·harness·驾驭工程