从日志到Dashboard:跨境电商AI Agent运维面板搭建实录
·
一、为什么AI Agent需要专属运维面板?
假设这样一个深夜场景:你被值班电话叫醒,客服反馈Agent开始"胡说八道"——把$299的商品报价成¥299、把"有货"说成"缺货"。你登录查看,发现除了几条零散的ERROR日志,没有任何能帮你定位问题的信息。你只能硬着头皮翻看几千行日志,试图还原故障现场。
这并非虚构。AI Agent的运维复杂度远超传统微服务:
- 执行链路长:一次用户请求可能包含多轮ReAct推理,每轮包含LLM调用、工具执行、结果评估
- 故障点分散:问题可能出在意图识别、工具选择、参数解析、LLM幻觉、API超时等任意环节
- 数据维度多:除了常规的QPS、延迟、错误率,还需要追踪Token消耗、模型选择、工具调用成功率
传统APM工具(如Prometheus + Grafana)只能告诉你"出事了",但无法回答"为什么出事"。一套为AI Agent量身定制的运维面板,是规模化落地的前提条件。
本文将从零搭建一套完整的跨境电商AI Agent运维面板,涵盖:
- 日志采集与结构化——让日志可检索、可分析
- 核心指标体系——Agent特有的黄金指标
- 可视化Dashboard——Grafana + 自定义数据源
- 告警规则配置——提前发现"翻车"苗头
二、日志系统:从"一锅粥"到结构化
2.1 日志结构化改造
传统日志是纯文本字符串,难以检索和分析。我们需要为Agent的每个执行环节输出结构化JSON日志:
import json
import logging
import uuid
from datetime import datetime
from typing import Dict, Any
class AgentLogger:
"""Agent专用结构化日志"""
def __init__(self, service_name: str = "ai-agent"):
self.service_name = service_name
self.logger = logging.getLogger(service_name)
self.logger.setLevel(logging.INFO)
# 配置JSON格式输出
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(
'{"timestamp": "%(asctime)s", "level": "%(levelname)s", "message": %(message)s}'
))
self.logger.addHandler(handler)
def _log(self, level: str, event_type: str, data: Dict[str, Any]):
"""输出结构化日志"""
log_entry = {
"timestamp": datetime.now().isoformat(),
"service": self.service_name,
"event_type": event_type,
"trace_id": data.get("trace_id", str(uuid.uuid4())),
"session_id": data.get("session_id"),
"user_id": data.get("user_id"),
"data": data
}
self.logger.log(
getattr(logging, level.upper()),
json.dumps(log_entry, ensure_ascii=False)
)
def request_start(self, user_input: str, session_id: str, user_id: str):
"""记录请求开始"""
trace_id = str(uuid.uuid4())
self._log("info", "request_start", {
"trace_id": trace_id,
"session_id": session_id,
"user_id": user_id,
"input": user_input[:200] # 截断防止过长
})
return trace_id
def llm_call(self, trace_id: str, model: str, prompt_tokens: int,
completion_tokens: int, latency_ms: float, status: str):
"""记录LLM调用"""
self._log("info", "llm_call", {
"trace_id": trace_id,
"model": model,
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": prompt_tokens + completion_tokens,
"latency_ms": latency_ms,
"status": status
})
def tool_call(self, trace_id: str, tool_name: str, args: Dict,
result_preview: str, latency_ms: float, status: str):
"""记录工具调用"""
self._log("info", "tool_call", {
"trace_id": trace_id,
"tool_name": tool_name,
"args": args,
"result_preview": result_preview[:200],
"latency_ms": latency_ms,
"status": status
})
def agent_step(self, trace_id: str, step_num: int, thought: str,
action: str = None, observation: str = None):
"""记录Agent的ReAct步骤"""
self._log("info", "agent_step", {
"trace_id": trace_id,
"step_num": step_num,
"thought": thought[:200],
"action": action,
"observation": observation[:200] if observation else None
})
def hallucination_detected(self, trace_id: str, risks: list, output: str):
"""记录幻觉检测"""
self._log("warning", "hallucination_detected", {
"trace_id": trace_id,
"risks": risks,
"output_preview": output[:300]
})
def request_end(self, trace_id: str, status: str, total_latency_ms: float,
output: str = None):
"""记录请求结束"""
self._log("info", "request_end", {
"trace_id": trace_id,
"status": status,
"total_latency_ms": total_latency_ms,
"output_preview": output[:300] if output else None
})
def error(self, trace_id: str, error_type: str, error_msg: str, stack: str = None):
"""记录错误"""
self._log("error", "error", {
"trace_id": trace_id,
"error_type": error_type,
"error_msg": error_msg,
"stack": stack[:500] if stack else None
})
2.2 日志采集与传输(ELK集成)
结构化日志输出到文件后,通过Filebeat采集并发送到Elasticsearch:
# filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/ai-agent/*.log
json.keys_under_root: true
json.overwrite_keys: true
output.elasticsearch:
hosts: ["localhost:9200"]
index: "ai-agent-logs-%{+yyyy.MM.dd}"
setup.template.name: "ai-agent"
setup.template.pattern: "ai-agent-logs-*"
2.3 日志查询与追踪链路还原
通过Elasticsearch的trace_id字段,可以一键还原完整执行链路:
# 查询示例:通过trace_id还原完整链路
def get_trace_by_id(trace_id: str):
"""在Kibana中查询所有该trace_id的日志"""
# Kibana Disco查询语句
query = f'trace_id:"{trace_id}"'
# 按时间排序,还原完整执行顺序
return query
# Kibana查询示例
# 搜索框输入: trace_id:"550e8400-e29b-41d4-a716-446655440000"
# 按 @timestamp 升序排列,即可看到该请求从开始到结束的所有步骤
三、核心指标采集:Agent专属的黄金指标
传统服务的黄金指标(延迟、流量、错误、饱和度)对Agent远远不够。我们需要扩展一套Agent专属指标体系:
3.1 指标定义与Prometheus埋点
from prometheus_client import Counter, Histogram, Gauge, Summary
import time
class AgentMetrics:
"""Agent核心指标采集器"""
def __init__(self, prefix: str = "agent"):
# 请求级指标
self.request_total = Counter(
f'{prefix}_requests_total',
'Agent请求总数',
['status'] # success, error, timeout, fallback
)
self.request_duration = Histogram(
f'{prefix}_request_duration_seconds',
'Agent请求耗时',
['model'],
buckets=[1, 2, 5, 10, 20, 30, 60, 120]
)
# LLM调用指标
self.llm_calls_total = Counter(
f'{prefix}_llm_calls_total',
'LLM调用总数',
['model', 'status']
)
self.llm_latency = Histogram(
f'{prefix}_llm_latency_seconds',
'LLM调用延迟',
['model'],
buckets=[0.1, 0.5, 1, 2, 5, 10, 20]
)
self.token_usage = Counter(
f'{prefix}_token_usage_total',
'Token消耗总数',
['type'] # prompt, completion
)
# 工具调用指标
self.tool_calls_total = Counter(
f'{prefix}_tool_calls_total',
'工具调用总数',
['tool', 'status']
)
self.tool_latency = Histogram(
f'{prefix}_tool_latency_seconds',
'工具调用延迟',
['tool'],
buckets=[0.1, 0.5, 1, 2, 5]
)
# Agent特有指标
self.agent_steps = Histogram(
f'{prefix}_agent_steps_total',
'Agent执行的ReAct轮数',
buckets=[1, 2, 3, 5, 8, 12]
)
self.hallucination_total = Counter(
f'{prefix}_hallucination_total',
'幻觉检测触发次数'
)
self.fallback_total = Counter(
f'{prefix}_fallback_total',
'降级触发次数',
['level'] # llm_only, rule_based, final
)
# 实时指标
self.active_sessions = Gauge(
f'{prefix}_active_sessions',
'当前活跃会话数'
)
self.rate_limit_remaining = Gauge(
f'{prefix}_rate_limit_remaining',
'API限流剩余配额',
['provider']
)
def record_request(self, status: str, duration_sec: float, model: str = "unknown"):
"""记录一次完整请求"""
self.request_total.labels(status=status).inc()
self.request_duration.labels(model=model).observe(duration_sec)
def record_llm_call(self, model: str, status: str, latency_sec: float,
prompt_tokens: int, completion_tokens: int):
"""记录LLM调用"""
self.llm_calls_total.labels(model=model, status=status).inc()
self.llm_latency.labels(model=model).observe(latency_sec)
self.token_usage.labels(type='prompt').inc(prompt_tokens)
self.token_usage.labels(type='completion').inc(completion_tokens)
def record_tool_call(self, tool: str, status: str, latency_sec: float):
"""记录工具调用"""
self.tool_calls_total.labels(tool=tool, status=status).inc()
self.tool_latency.labels(tool=tool).observe(latency_sec)
3.2 在Agent中埋点
class InstrumentedAgent:
"""带埋点的Agent包装器"""
def __init__(self, agent_executor, metrics: AgentMetrics, logger: AgentLogger):
self.agent = agent_executor
self.metrics = metrics
self.logger = logger
def invoke(self, input_data: Dict) -> Dict:
start_time = time.time()
trace_id = self.logger.request_start(
user_input=input_data.get("input"),
session_id=input_data.get("session_id", "unknown"),
user_id=input_data.get("user_id", "anonymous")
)
try:
# 执行Agent
result = self.agent.invoke(input_data)
# 记录指标
duration = time.time() - start_time
self.metrics.record_request(
status="success",
duration_sec=duration,
model="claude-sonnet-4"
)
self.metrics.active_sessions.set(self._get_active_sessions())
# 记录中间步骤
intermediate_steps = result.get("intermediate_steps", [])
self.metrics.agent_steps.observe(len(intermediate_steps))
# 记录结束日志
self.logger.request_end(
trace_id=trace_id,
status="success",
total_latency_ms=duration * 1000,
output=result.get("output")
)
return result
except Exception as e:
duration = time.time() - start_time
self.metrics.record_request(status="error", duration_sec=duration)
self.logger.error(trace_id, type(e).__name__, str(e))
raise
3.3 对接Prometheus
from prometheus_client import start_http_server
# 启动Prometheus指标暴露端口
start_http_server(8000)
# 访问 http://localhost:8000/metrics 即可看到所有指标
四、可视化Dashboard:Grafana面板搭建
4.1 数据源配置
Prometheus + Elasticsearch作为双数据源:
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'ai-agent'
static_configs:
- targets: ['localhost:8000']
- job_name: 'langchain-agent'
static_configs:
- targets: ['localhost:8001'] # VeilPiercer等工具暴露的指标
4.2 Grafana Dashboard关键面板设计
以下是用JSON定义的Dashboard核心面板(部分):
{
"title": "AI Agent 运维面板",
"panels": [
{
"title": "请求总量 & 成功率",
"type": "stat",
"targets": [
{
"expr": "sum(agent_requests_total)",
"legendFormat": "总请求"
},
{
"expr": "sum(agent_requests_total{status='success'}) / sum(agent_requests_total) * 100",
"legendFormat": "成功率"
}
]
},
{
"title": "请求延迟 P50/P90/P99",
"type": "graph",
"targets": [
{"expr": "histogram_quantile(0.50, rate(agent_request_duration_seconds_bucket[5m]))"},
{"expr": "histogram_quantile(0.90, rate(agent_request_duration_seconds_bucket[5m]))"},
{"expr": "histogram_quantile(0.99, rate(agent_request_duration_seconds_bucket[5m]))"}
]
},
{
"title": "Token消耗趋势",
"type": "graph",
"targets": [
{"expr": "rate(agent_token_usage_total[5m])", "legendFormat": "{{type}}"}
]
},
{
"title": "工具调用成功率",
"type": "piechart",
"targets": [
{"expr": "sum by (tool, status) (agent_tool_calls_total)"}
]
},
{
"title": "ReAct轮数分布",
"type": "heatmap",
"targets": [
{"expr": "rate(agent_agent_steps_total_bucket[5m])"}
]
},
{
"title": "降级触发次数",
"type": "stat",
"targets": [
{"expr": "sum(agent_fallback_total)"}
]
}
],
"refresh": "30s",
"time": {"from": "now-6h", "to": "now"}
}
4.3 典型的Dashboard布局
五、告警规则:提前发现"翻车"苗头
5.1 核心告警规则(Prometheus AlertManager)
# alert_rules.yml
groups:
- name: ai_agent_alerts
rules:
# P0: 成功率跌破90%
- alert: AgentSuccessRateLow
expr: |
(sum(rate(agent_requests_total{status='success'}[5m]))
/ sum(rate(agent_requests_total[5m])) * 100) < 90
for: 3m
labels:
severity: p0
annotations:
summary: "Agent成功率低于90%"
description: "当前成功率: {{ $value }}%,请立即排查"
# P1: 延迟超过阈值
- alert: AgentLatencyHigh
expr: |
histogram_quantile(0.95, rate(agent_request_duration_seconds_bucket[5m])) > 10
for: 2m
labels:
severity: p1
annotations:
summary: "Agent P95延迟超过10秒"
description: "当前P95延迟: {{ $value }}秒"
# P1: 幻觉检测频繁
- alert: FrequentHallucination
expr: rate(agent_hallucination_total[5m]) > 0.1
for: 5m
labels:
severity: p1
annotations:
summary: "幻觉检测频率过高"
description: "过去5分钟幻觉检测次数: {{ $value }}次/秒"
# P2: Token消耗异常(成本告警)
- alert: TokenUsageSpike
expr: rate(agent_token_usage_total[10m]) > 100000
for: 5m
labels:
severity: p2
annotations:
summary: "Token消耗异常增长"
description: "当前Token消耗速率: {{ $value }}/秒"
# P1: 降级触发频繁
- alert: FrequentFallback
expr: rate(agent_fallback_total[5m]) > 0.05
for: 5m
labels:
severity: p1
annotations:
summary: "降级触发过于频繁"
description: "过去5分钟降级次数: {{ $value }}次/秒"
# P0: 无请求(服务可能挂)
- alert: NoAgentRequests
expr: sum(rate(agent_requests_total[5m])) == 0
for: 5m
labels:
severity: p0
annotations:
summary: "Agent 5分钟内无请求"
description: "请检查Agent服务是否正常运行"
5.2 告警通知渠道
# alertmanager.yml
route:
group_by: ['alertname', 'severity']
group_wait: 30s
group_interval: 5m
repeat_interval: 30m
routes:
- match:
severity: p0
receiver: 'oncall-pagerduty'
continue: true
- match:
severity: p1
receiver: 'slack-critical'
- match:
severity: p2
receiver: 'email'
receivers:
- name: 'oncall-pagerduty'
pagerduty_configs:
- service_key: 'YOUR_PAGERDUTY_KEY'
- name: 'slack-critical'
slack_configs:
- api_url: 'YOUR_SLACK_WEBHOOK'
channel: '#agent-alerts'
title: '{{ .GroupLabels.alertname }}'
text: '{{ .CommonAnnotations.description }}'
- name: 'email'
email_configs:
- to: 'ops@example.com'
smarthost: 'smtp.example.com:587'
from: 'alert@example.com'
六、完整运维面板架构图
┌────────────────────────────────────────────────────────────────────┐
│ AI Agent 服务 │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ AgentExecutor (带埋点) │ │
│ │ ├── Logger (结构化JSON) ──► /var/log/agent/*.log │ │
│ │ └── Metrics (Prometheus) ──► :8000/metrics │ │
│ └─────────────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Filebeat │ │ Prometheus │ │ AlertManager │
│ (日志采集) │ │ (指标采集) │ │ (告警引擎) │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Elasticsearch │ │ Grafana │ │ 通知渠道 │
│ (日志存储) │ │ (可视化面板) │ │ 企业微信/Slack │
└────────┬────────┘ └────────┬────────┘ └─────────────────┘
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Kibana │ │ Dashboard │
│ (日志检索) │ │ (监控面板) │
└─────────────────┘ └─────────────────┘
七、总结
| 组件 | 技术选型 | 核心职责 |
|---|---|---|
| 日志采集 | Filebeat + Elasticsearch + Kibana | 结构化管理Agent执行链路日志 |
| 指标采集 | Prometheus + 自定义埋点 | 采集Agent黄金指标(成功率、延迟、Token、轮数) |
| 可视化 | Grafana | 多维度展示Agent健康度 |
| 告警 | AlertManager + 多渠道通知 | 提前发现异常,主动告警 |
核心原则:
- 先标准化,再可视化——没有结构化的日志和指标,任何面板都是空中楼阁
- Agent指标≠普通服务指标——必须增加Token、轮数、工具成功率、幻觉检测等特有维度
- 告警要有分层——P0短信电话、P1钉钉/企微、P2邮件,避免告警疲劳
- 链路追踪是关键——通过trace_id把散落的步骤串联起来,才能快速定位根因
记住这句话:当你的Agent半夜"翻车"时,一个清晰的运维面板,就是你最快找到问题的那盏灯。
更多推荐


所有评论(0)