我在过去的两年里帮助超过50个团队搭建了 AI Agent 监控系统,一个血淋淋的事实是:80%的团队在遇到线上故障时,第一反应是“我不知道发生了什么”。今天这篇文章,我会用真实的代码和踩坑经验,帮你从零构建一套完整的 AI Agent 可观测性方案。
先算一笔账:为什么可观测性直接影响你的 API 成本
先来看一组 2026 年主流模型的 output 价格对比:
- GPT-4.1 output: $8/MTok
- Claude Sonnet 4.5 output: $15/MTok
- Gemini 2.5 Flash output: $2.50/MTok
- DeepSeek V3.2 output: $0.42/MTok
假设你每月消耗 100 万 output token,用 DeepSeek V3.2 只要 $0.42,换成 Claude Sonnet 4.5 就变成 $15——相差整整 35 倍!通过 HolySheep 中转,按 ¥1=$1 结算(官方汇率 ¥7.3=$1),国内开发者实际节省超过 85%。
但这不是今天的主题。我想问你:你知道这 100 万 token 里,有多少是重复调用、失败重试、无效轮次吗?没有可观测性,你就是在盲目烧钱。
什么是 AI Agent 可观测性?三大支柱
可观测性(Observability)不是监控(Monitoring),它包含三个维度:
- 日志(Logs):离散的事件记录,告诉你“发生了什么”
- 指标(Metrics):聚合的数值,告诉你“表现如何”
- 链路追踪(Traces):请求的完整路径,告诉你“为什么会这样”
对于 AI Agent,链路追踪尤其重要,因为你的 Agent 可能会:多轮对话、调用工具、调用外部 API、生成子任务。如果我们用 HolySheep 的 base_url: https://api.holysheep.ai/v1 作为统一入口,配合链路追踪,就能清晰看到每一次 LLM 调用的 token 消耗、延迟和响应质量。
实战:构建 AI Agent 可观测性系统
1. 结构化日志:JSON 格式 + 标准字段
我在生产环境里踩过的第一个坑就是用文本日志。有一天凌晨三点,系统报警,我花了 2 小时才从 10G 的日志里找到问题。后来我改用结构化 JSON 日志,查询效率提升了 100 倍。
import json
import logging
from datetime import datetime
from typing import Optional, Dict, Any
import hashlib
class AIAgentLogger:
"""
AI Agent 结构化日志记录器
包含: trace_id, span_id, token_count, latency, model, status
"""
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
handler = logging.StreamHandler()
handler.setFormatter(self._json_formatter)
self.logger.addHandler(handler)
@staticmethod
def _json_formatter(record: logging.LogRecord) -> str:
"""统一的 JSON 日志格式"""
log_data = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"level": record.levelname,
"service": record.name,
"message": record.getMessage(),
"trace_id": getattr(record, "trace_id", "N/A"),
"span_id": getattr(record, "span_id", "N/A"),
"model": getattr(record, "model", "N/A"),
"input_tokens": getattr(record, "input_tokens", 0),
"output_tokens": getattr(record, "output_tokens", 0),
"latency_ms": getattr(record, "latency_ms", 0),
"status": getattr(record, "status", "unknown"),
"error": getattr(record, "error", None),
}
return json.dumps(log_data, ensure_ascii=False)
def log_llm_call(
self,
trace_id: str,
span_id: str,
model: str,
input_tokens: int,
output_tokens: int,
latency_ms: float,
status: str = "success",
error: Optional[str] = None,
):
"""记录 LLM 调用日志"""
extra = {
"trace_id": trace_id,
"span_id": span_id,
"model": model,
"input_tokens": input_tokens,
"output_tokens": output_tokens,
"latency_ms": round(latency_ms, 2),
"status": status,
"error": error,
}
log_level = logging.ERROR if status == "error" else logging.INFO
self.logger.log(
log_level,
f"LLM Call: {model} | Tokens: {input_tokens}+{output_tokens}={input_tokens+output_tokens} | Latency: {latency_ms}ms",
extra=extra
)
使用示例
logger = AIAgentLogger("production-agent")
logger.log_llm_call(
trace_id="tr_abc123",
span_id="sp_001",
model="deepseek-v3.2",
input_tokens=150,
output_tokens=320,
latency_ms=850.5,
status="success"
)
2. 分布式链路追踪:OpenTelemetry 集成
AI Agent 的链路追踪比普通微服务复杂,因为它有独特的“思维链”。我的经验是:用 OpenTelemetry 作为标准,通过 span attributes 记录每个关键节点。
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter
from opentelemetry.trace import Status, StatusCode
from opentelemetry.sdk.resources import Resource
import time
import uuid
初始化 OpenTelemetry
resource = Resource.create({"service.name": "ai-agent", "service.version": "1.0.0"})
provider = TracerProvider(resource=resource)
开发环境用 Console 输出,生产环境替换为 OTLP Collector
processor = BatchSpanProcessor(ConsoleSpanExporter())
provider.add_span_processor(processor)
trace.set_tracer_provider(provider)
tracer = trace.get_tracer(__name__)
class TracingAIAgent:
"""带链路追踪的 AI Agent 基类"""
def __init__(self, agent_id: str):
self.agent_id = agent_id
self.tracer = trace.get_tracer(f"agent.{agent_id}")
def create_trace_context(self) -> dict:
"""创建新的链路上下文"""
return {
"trace_id": str(uuid.uuid4())[:16],
"start_time": time.time()
}
async def call_llm_with_tracing(
self,
trace_ctx: dict,
messages: list,
model: str = "deepseek-v3.2",
base_url: str = "https://api.holysheep.ai/v1",
api_key: str = "YOUR_HOLYSHEEP_API_KEY",
):
"""通过 HolySheep API 调用 LLM,带完整链路追踪"""
with self.tracer.start_as_current_span(f"llm.{model}") as span:
# 设置 span 属性
span.set_attribute("agent.id", self.agent_id)
span.set_attribute("trace.id", trace_ctx["trace_id"])
span.set_attribute("llm.model", model)
span.set_attribute("messages.count", len(messages))
start_time = time.time()
try:
import aiohttp
async with aiohttp.ClientSession() as session:
payload = {
"model": model,
"messages": messages,
"max_tokens": 2048,
"temperature": 0.7
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
async with session.post(
f"{base_url}/chat/completions",
json=payload,
headers=headers,
timeout=aiohttp.ClientTimeout(total=60)
) as response:
latency_ms = (time.time() - start_time) * 1000
if response.status == 200:
result = await response.json()
# 记录 token 使用量
usage = result.get("usage", {})
input_tokens = usage.get("prompt_tokens", 0)
output_tokens = usage.get("completion_tokens", 0)
span.set_attribute("llm.input_tokens", input_tokens)
span.set_attribute("llm.output_tokens", output_tokens)
span.set_attribute("llm.total_tokens", input_tokens + output_tokens)
span.set_attribute("llm.latency_ms", round(latency_ms, 2))
span.set_attribute("llm.status", "success")
span.set_status(Status(StatusCode.OK))
return result
else:
error_text = await response.text()
span.set_attribute("llm.status", "error")
span.set_attribute("llm.error", error_text)
span.set_status(Status(StatusCode.ERROR, error_text))
raise Exception(f"API Error {response.status}: {error_text}")
except Exception as e:
latency_ms = (time.time() - start_time) * 1000
span.set_attribute("llm.latency_ms", round(latency_ms, 2))
span.set_attribute("llm.status", "exception")
span.set_status(Status(StatusCode.ERROR, str(e)))
raise
使用示例
agent = TracingAIAgent("customer-support-v1")
async def main():
trace_ctx = agent.create_trace_context()
messages = [
{"role": "system", "content": "你是一个专业的客服助手"},
{"role": "user", "content": "我的订单号是 12345,什么时候发货?"}
]
result = await agent.call_llm_with_tracing(
trace_ctx=trace_ctx,
messages=messages,
model="deepseek-v3.2" # 低成本高性价比选择
)
print(f"Trace ID: {trace_ctx['trace_id']}")
print(f"Response: {result['choices'][0]['message']['content']}")
asyncio.run(main())
3. 可视化仪表盘:Prometheus + Grafana
from prometheus_client import Counter, Histogram, Gauge, generate_latest
from fastapi import FastAPI, Response
import time
app = FastAPI()
定义 Prometheus 指标
llm_requests_total = Counter(
'llm_requests_total',
'Total LLM API requests',
['model', 'status', 'agent_id']
)
llm_tokens_used = Counter(
'llm_tokens_used_total',
'Total tokens used',
['model', 'token_type', 'agent_id'] # token_type: input/output
)
llm_request_duration_seconds = Histogram(
'llm_request_duration_seconds',
'LLM request latency in seconds',
['model', 'agent_id'],
buckets=[0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0]
)
active_agents = Gauge(
'active_agents',
'Number of active AI agents',
['agent_id']
)
@app.get("/metrics")
async def metrics():
"""Prometheus 抓取接口"""
return Response(generate_latest(), media_type="text/plain")
@app.post("/agent/completion")
async def agent_completion(request: dict):
"""Agent 完成请求,自动记录指标"""
start = time.time()
model = request.get("model", "deepseek-v3.2")
agent_id = request.get("agent_id", "default")
try:
# 调用 LLM...
# 假设返回结果
input_tokens = 200
output_tokens = 450
duration = time.time() - start
# 记录指标
llm_requests_total.labels(
model=model, status="success", agent_id=agent_id
).inc()
llm_tokens_used.labels(
model=model, token_type="input", agent_id=agent_id
).inc(input_tokens)
llm_tokens_used.labels(
model=model, token_type="output", agent_id=agent_id
).inc(output_tokens)
llm_request_duration_seconds.labels(
model=model, agent_id=agent_id
).observe(duration)
return {"status": "success", "duration_ms": duration * 1000}
except Exception as e:
llm_requests_total.labels(
model=model, status="error", agent_id=agent_id
).inc()
raise
Grafana Dashboard JSON 配置(关键查询)
GRAFANA_QUERIES = """
Token 消耗速率
sum(rate(llm_tokens_used_total[5m])) by (model)
P99 延迟
histogram_quantile(0.99,
sum(rate(llm_request_duration_seconds_bucket[5m])) by (le, model)
)
错误率
sum(rate(llm_requests_total{status="error"}[5m])) by (model)
/
sum(rate(llm_requests_total[5m])) by (model)
"""
@app.get("/dashboard/config")
async def get_dashboard_config():
"""返回 Grafana Dashboard 配置"""
return {"queries": GRAFANA_QUERIES}
我的实战经验:可观测性落地的三个阶段
我在帮助团队落地可观测性时,发现大家都会经历三个阶段:
第一阶段:日志优先。不要一上来就搞分布式追踪,先把日志结构化。我见过太多团队用 print() 调试生产问题,这是灾难的开始。结构化 JSON 日志是最低成本的起步。
第二阶段:关键指标。当你能回答“今天 API 花费多少”、“P99 延迟是多少”这两个问题时,你就已经比 60% 的团队强了。通过 HolySheep 的统一入口,所有模型的调用都会被标准化记录,你甚至可以在一个 Dashboard 里看到 GPT-4.1 和 DeepSeek V3.2 的对比——这是我认为最有价值的场景。
第三阶段:全链路追踪。当你开始有多个 Agent 协作、工具调用、子任务分解时,你就需要 OpenTelemetry 了。我的经验是,用 trace_id 串联所有日志和指标,排查问题时只要搜一个 ID,就能看到完整的请求生命周期。
还有一个实战建议:不要忽略失败请求的追踪。我曾经花了一周优化 Token 消耗,结果发现 30% 的请求其实在重试失败——优化重试逻辑后,API 成本直接降了 25%。
常见报错排查
下面是我整理的 5 个高频报错,配合 HolySheep 使用时的常见问题:
错误 1:401 Authentication Error
# 错误信息
{
"error": {
"message": "Incorrect API key provided",
"type": "invalid_request_error",
"code": "invalid_api_key"
}
}
❌ 错误示例:直接用官方格式
base_url = "https://api.openai.com/v1" # 不要用这个!
api_key = "sk-xxxx" # 不要用这种方式
✅ 正确示例:使用 HolySheep 中转
BASE_URL = "https://api.holysheep.ai/v1"
API_KEY = "YOUR_HOLYSHEEP_API_KEY" # 从 HolySheep 控制台获取
验证 Key 格式
if not API_KEY.startswith("hs_"):
raise ValueError("请使用 HolySheep 平台生成的 API Key,格式为 hs_xxxx")
错误 2:429 Rate Limit Exceeded
# 错误信息
{
"error": {
"message": "Rate limit reached for gpt-4.1 in region...",
"type": "requests_error",
"code": "rate_limit_exceeded"
}
}
✅ 解决方案:实现指数退避重试
import asyncio
from typing import Optional
async def call_with_retry(
messages: list,
model: str = "deepseek-v3.2",
max_retries: int = 3,
base_delay: float = 1.0,
) -> Optional[dict]:
"""
带指数退避的重试机制
HolySheep 各模型默认限流:
- DeepSeek V3.2: 1000 RPM
- GPT-4.1: 500 RPM
- Claude Sonnet 4.5: 200 RPM
"""
for attempt in range(max_retries):
try:
response = await make_api_request(messages, model)
return response
except Exception as e:
if "rate_limit" not in str(e).lower():
raise # 非限流错误,直接抛出
if attempt == max_retries - 1:
raise Exception(f"重试 {max_retries} 次后仍失败: {e}")
# 指数退避: 1s, 2s, 4s, 8s...
delay = base_delay * (2 ** attempt)
# 添加 jitter 避免惊群效应
import random
delay = delay * (0.5 + random.random())
print(f"触发限流,等待 {delay:.2f}s 后重试 (尝试 {attempt+1}/{max_retries})")
await asyncio.sleep(delay)
return None
使用信号量控制并发
semaphore = asyncio.Semaphore(50) # 限制同时最多 50 个请求
async def controlled_call(messages: list):
async with semaphore:
return await call_with_retry(messages, model="deepseek-v3.2")
错误 3:Request Timeout
# 错误信息
asyncio.exceptions.CancelledError: Request timeout
✅ 解决方案:合理的超时配置 + 超时处理
import aiohttp
async def robust_api_call(
messages: list,
model: str,
timeout_seconds: float = 30.0,
):
"""
健壮的 API 调用
不同模型典型延迟参考(通过 HolySheep 国内直连):
- DeepSeek V3.2: P50 约 400ms, P95 约 1200ms
- GPT-4.1: P50 约 800ms, P95 约 2500ms
- Gemini 2.5 Flash: P50 约 300ms, P95 约 900ms
"""
# 根据模型动态设置超时
timeout_config = {
"deepseek-v3.2": 30.0,
"gpt-4.1": 60.0,
"gpt-4o": 60.0,
"claude-sonnet-4.5": 90.0,
"gemini-2.5-flash": 30.0,
}
timeout = aiohttp.ClientTimeout(
total=timeout_config.get(model, 60.0)
)
try:
async with aiohttp.ClientSession(timeout=timeout) as session:
# 异步调用逻辑...
pass
except asyncio.TimeoutError:
# 记录超时事件
logger.error(
f"API 调用超时 | Model: {model} | Timeout: {timeout.total}s",
extra={
"trace_id": trace_id,
"model": model,
"timeout_seconds": timeout.total,
"status": "timeout"
}
)
raise
except asyncio.CancelledError:
# 确保资源清理
print(f"请求被取消: {trace_id}")
raise
错误 4:Invalid Request - Context Length Exceeded
# 错误信息
{
"error": {
"message": "Maximum context length is 128000 tokens",
"type": "invalid_request_error",
"param": "messages",
"code": "context_length_exceeded"
}
}
✅ 解决方案:智能上下文管理 + 历史摘要
from typing import List, Dict, Any
class ConversationManager:
"""对话上下文管理器,自动处理超长上下文"""
def __init__(self, max_tokens: int = 100000):
# 各模型上下文窗口
self.context_limits = {
"deepseek-v3.2": 64000,
"gpt-4.1": 128000,
"claude-sonnet-4.5": 200000,
"gemini-2.5-flash": 1000000,
}
self.max_tokens = max_tokens
def estimate_tokens(self, messages: List[Dict]) -> int:
"""简单估算 token 数量(实际应使用 tiktoken)"""
total = 0
for msg in messages:
# 估算: 每4个字符约1个token
total += len(str(msg.get("content", ""))) // 4
# 添加消息结构开销
total += 10
return total
def truncate_messages(
self,
messages: List[Dict],
model: str,
preserve_system: bool = True
) -> List[Dict]:
"""截断消息以适应上下文窗口"""
limit = self.context_limits.get(model, 128000)
# 预留 2000 token 给输出
available = limit - 2000
if self.estimate_tokens(messages) <= available:
return messages
result = []
system_msg = None
if preserve_system and messages[0].get("role") == "system":
system_msg = messages[0]
messages = messages[1:]
# 从最新的消息开始保留
for msg in reversed(messages):
result.insert(0, msg)
if self.estimate_tokens(result) + (len(str(system_msg["content"])) // 4 if system_msg else 0) > available:
result.pop(0)
break
if system_msg:
result.insert(0, system_msg)
return result
使用示例
manager = ConversationManager()
messages = load_conversation_history(user_id="12345") # 可能很长
safe_messages = manager.truncate_messages(
messages,
model="deepseek-v3.2",
preserve_system=True
)
response = await agent.call_llm_with_tracing(trace_ctx, safe_messages)
错误 5:Connection Error - 国内网络问题
# 错误信息
aiohttp.client_exceptions.ClientConnectorError: Cannot connect to host api.openai.com:443
✅ 解决方案:使用 HolySheep 国内直连节点
import aiohttp
class HolySheepClient:
"""
HolySheep API 客户端
优势:国内直连 <50ms | 汇率 ¥1=$1 | 无需科学上网
"""
BASE_URL = "https://api.holysheep.ai/v1"
def __init__(self, api_key: str):
self.api_key = api_key
self._session: Optional[aiohttp.ClientSession] = None
async def __aenter__(self):
# 配置连接器,优化国内访问
connector = aiohttp.TCPConnector(
limit=100, # 连接池大小
ttl_dns_cache=300, # DNS 缓存 5 分钟
ssl=True,
force_close=False, # 保持连接复用
)
self._session = aiohttp.ClientSession(
connector=connector,
timeout=aiohttp.ClientTimeout(total=60)
)
return self
async def __aexit__(self, *args):
if self._session:
await self._session.close()
async def chat_completion(
self,
model: str,
messages: list,
**kwargs
) -> dict:
"""发送聊天完成请求"""
headers = {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json",
}
payload = {
"model": model,
"messages": messages,
**kwargs
}
async with self._session.post(
f"{self.BASE_URL}/chat/completions",
json=payload,
headers=headers
) as response:
if response.status == 200:
return await response.json()
else:
error = await response.json()
raise Exception(f"API Error: {error}")
@staticmethod
def get_supported_models() -> list:
"""返回支持的模型列表及价格"""
return [
{"id": "deepseek-v3.2", "name": "DeepSeek V3.2", "price_per_mtok": "$0.42"},
{"id": "gpt-4.1", "name": "GPT-4.1", "price_per_mtok": "$8.00"},
{"id": "gpt-4o", "name": "GPT-4o", "price_per_mtok": "$6.00"},
{"id": "claude-sonnet-4.5", "name": "Claude Sonnet 4.5", "price_per_mtok": "$15.00"},
{"id": "gemini-2.5-flash", "name": "Gemini 2.5 Flash", "price_per_mtok": "$2.50"},
]
使用示例
async def main():
async with HolySheepClient("YOUR_HOLYSHEEP_API_KEY") as client:
response = await client.chat_completion(
model="deepseek-v3.2",
messages=[{"role": "user", "content": "你好"}],
temperature=0.7,
max_tokens=500
)
print(response["choices"][0]["message"]["content"])
asyncio.run(main())
总结:可观测性是 AI Agent 的生命线
回顾一下今天的核心内容:
- 可观测性包含日志、指标、链路追踪三个维度,缺一不可
- 结构化 JSON 日志是最低成本的起步,用
trace_id串联一切 - OpenTelemetry 是工业标准的链路追踪方案
- Prometheus + Grafana 让你对成本和性能一目了然
- 使用 HolySheep 中转,统一入口 + 汇率优势(¥1=$1)+ 国内直连,是国内开发者的最优选
我见过太多团队因为缺少可观测性,在线上故障时手足无措,在月底结账时目瞪口呆。投资 2 天搭建可观测性系统,每年至少能帮你节省 30% 的 API 成本,这还不算故障恢复时间和其他隐性损失。
现在就去 HolySheep 注册,获取免费试用额度,把这篇文章的代码跑起来,你就已经赢在起跑线上了。