Day 4: Clawdbot 失控现场实录:论 run_id 的重要性

Day 4: Clawdbot 失控现场实录:论 run_id 的重要性

标题备选

  1. Clawdbot 失控现场实录:论 run_id 的重要性
  2. 如果你的 AI Agent 没有日志,那你就是在"盲跑"
  3. 这里的 Bug 没法 Print:如何构建 AI 工作流的可观测性?

正文内容

当你把一个巨大的任务拆解成"微链架构"(Micro-Chain)后,你会面临一个新的挑战:信息碎片化

以前,程序挂了,你看一眼控制台最后一行报错就行。 现在,你有三个 Agent 在跑,任务 A 传给了 B,B 传给了 C。 用户反馈说"没收到邮件"。 是 A 没抓到数据?是 B 把数据筛空了?还是 C 发送失败了?

如果你的回答是"我得去看看代码/重新跑一遍",那你的工作流还处于"石器时代"。你不能靠猜来调试。

状态管理靠记忆 = 灾难

在 GitHub 上,我看到很多 Clawdbot 用户的脚本里,状态管理基本靠"全局变量"或者"临时文件"。 比如 temp_result.json。 第二次运行的时候,直接覆盖了这个文件。 如果我想知道昨天为什么跑失败了?对不起,证据已经被销毁了。

引入核心概念:Trace ID (或 run_id)

在分布式系统里,有一个概念叫 Trace ID。在 AI Agent 工作流里,我们也需要它。 每一次运行(Run),都必须生成一个唯一的身份证号(UUID)。

所有的日志、中间产物、最终结果,甚至消耗的 Token 数量,都要挂在这个 run_id 下面。

一个理想的日志结构设计

不要只打印 print("Error")。你的日志应该记录完整的上下文。

{
  "run_id": "20240127-x8y9z0",
  "timestamp": "2024-01-27T10:00:00Z",
  "status": "FAILED",
  "total_cost_usd": 0.15,
  "steps": [
    {
      "step_name": "Scraper",
      "status": "SUCCESS",
      "input_url": "https://example.com",
      "output_count": 50,
      "duration_ms": 1200,
      "model": "claude-3-haiku"
    },
    {
      "step_name": "Analyst",
      "status": "SUCCESS",
      "input_count": 50,
      "filtered_count": 0,  // <--- 疑点在这里!
      "reasoning": "No items price < 10", // Agent 的思考过程
      "model": "claude-3-5-sonnet"
    },
    {
      "step_name": "Sender",
      "status": "SKIPPED",
      "reason": "No data to send"
    }
  ]
}

为什么这对 Clawdbot 至关重要?

  1. 还原现场: 看到上面的日志,你一眼就能明白:不是系统挂了,而是数据被筛选空了。 为什么空了?看 reasoning 字段,原来是价格阈值设置太低。 如果没有这个记录,你会以为是网络问题,傻傻地去重试抓取。

  2. 计费与成本控制: 挂载 run_id 还能让你统计每次运行的成本。 "为什么昨天的 Token 消耗暴涨?" 查一下日志发现,昨天某个网页死循环了,Agent 尝试了 50 次重试。

  3. 人工介入(Human-in-the-loop): 当任务失败时,你可以设计一个简单的脚本,读取这个 run_id 的所有中间文件。你可以手动修正"分析"步骤的结果,然后告诉系统:"从这一步继续往下跑"。

怎么做?(Python 示例)

不需要引入复杂的 APM 工具(如 Datadog)。对于个人或小团队,一个简单的 SQLite 数据库就足够了。

import sqlite3
import uuid
import json

def start_run():
    run_id = str(uuid.uuid4())
    conn = sqlite3.connect('workflow_logs.db')
    cursor = conn.cursor()
    cursor.execute('''
        INSERT INTO runs (run_id, status, start_time) 
        VALUES (?, 'STARTED', CURRENT_TIMESTAMP)
    ''', (run_id,))
    conn.commit()
    return run_id

def log_step(run_id, step_name, status, details):
    conn = sqlite3.connect('workflow_logs.db')
    conn.execute('''
        INSERT INTO steps (run_id, step_name, status, details_json)
        VALUES (?, ?, ?, ?)
    ''', (run_id, step_name, status, json.dumps(details)))
    conn.commit()

记住一句话:Logs are for code; Traces are for Agents. 代码逻辑是确定的,看 Log 就行。但 Agent 的行为是概率性的,只有完整的 Trace 才能让你理解它为什么做出了那个决定。

明天,我们讨论当 Agent 真的发疯(比如开始胡编乱造)时,我们该怎么自动拦截它——容错与熔断机制


标签:#可观测性 #DevOps #AI调试 #Clawdbot #日志管理

← 返回博客列表