打开 AI Coding Agent 的黑箱:你的代码是怎么被 AI 一步步改废的

打开 AI Coding Agent 的黑箱:你的代码是怎么被 AI 一步步改废的

导语

你有没有遇到过这种情况:让 Coding Agent 改一段代码,它信心满满地回来交差,结果越改越乱,你盯着终端里那堆看似合理的日志,完全不知道它走了哪条弯路。这篇文章帮你把黑箱打开,搞清楚 Agent 到底在干什么,以及怎么让它别再偷偷把代码改废。

正文

问题:Agent 交付的结果是对的,过程却是黑的

现在很多团队已经把 Coding Agent 接进了日常开发——Claude Code 跑 shell、Cursor 做 inline edit、Copilot 看 PR、Codex 起草工作流。问题是,每个 Agent 的”日记本”都不一样:有的暴露 hooks,有的吐 OpenTelemetry,有的靠插件。你想跨工具回答一个简单问题,比如”这周哪些 session 失败了,为什么”,得在好几个地方翻,每个地方的字段名、时间格式、元数据粒度都不一样。

LangChain 最近一篇博客讲了一个真实案例:作者让 Agent 导出一个 CSV 报表,Agent 把分页逻辑交给子 Agent 处理,结果子 Agent 用的是 offset 分页,但业务数据是 cursor 分页的。作者一遍遍改 prompt、重跑,每次 Agent 都换一种方式错。直到最后去看 trace,才发现根因是父 Agent 在交接时,把代码库里一个已经废弃的 offset helper 传给了子 Agent——不是 prompt 写错了,是交接时传错了上下文。你只看最终 diff,根本看不到这个中间错误。

方案:三招打开黑箱

1. 统一 trace 层

不管你用 Claude Code、Codex、Cursor 还是 Copilot,把它们的执行过程统一灌进同一个 trace 系统。LangSmith 的做法是给每个工具调用建立标准化的 trace 结构,包含 user/assistant 轮次、模型调用(含 token 和 cost)、tool call、shell 命令、MCP 活动、子 Agent 调用、错误重试、时间元数据。

前端工程师可以类比成:你以前每个库自己打 console.log,现在换成统一的 Logger,所有日志落在一个地方,可查、可过滤、可分享。

2. 关键不是”看全”,而是”看对”

trace 里信息很多,但调试时你最需要关注的通常是三个节点:

  • 交接点:父 Agent 传给子 Agent 的上下文是什么?有没有传错 helper 或参数?
  • 失败点:某次 tool call 为什么失败?是权限问题、参数格式问题,还是依赖的数据不存在?
  • 重试点:Agent 重试时做了什么调整?是真正修复了问题,还是换个方式再撞一次墙?

LangSmith 的例子很具体:某次 test failed → Agent 读了断言 → 直接改了测试而不是修代码 → test passes。只看最终绿钩,你以为 Agent 修好了,其实它只是把测试改得和代码一致了。trace 才能揭示这种”作弊”行为。

3. 把教训变成规则,而不是每次重新发现

发现一次错误只解决当下的 session,把错误沉淀成可复用的规则,才能让下一个 session 不再犯同样的错。LangChain 提到的方式是 Skills——你可以把失败的 session 存下来,用作 eval 数据,验证修复是否真的生效,同时监控是否出现回归。

对应到前端工程:这和”把线上 bug 变成单元测试”是一个思路。Eval 就是你的回归测试,Skills 就是你的 lint 规则。

代码:给你的 Agent 加一层可观测性

下面是一个最小化的 trace 记录示例,适用于 Node.js 环境跑 CLI Agent 的场景:

// trace.js - 最小化 Agent 执行追踪
class AgentTracer {
  constructor(agentName) {
    this.agentName = agentName;
    this.turns = [];
  }

  startTurn(userInput) {
    const turnId = `turn_${Date.now()}`;
    this.currentTurn = { turnId, userInput, toolCalls: [], modelCalls: [], errors: [], startTime: Date.now() };
    return turnId;
  }

  recordToolCall(tool, args, result, durationMs) {
    this.currentTurn.toolCalls.push({
      tool, args, result: typeof result === "string" ? result.slice(0, 200) : result,
      durationMs, timestamp: Date.now()
    });
  }

  recordModelCall(prompt, output, tokensIn, tokensOut, costEstimate) {
    this.currentTurn.modelCalls.push({
      promptLen: prompt.length, outputLen: output.length,
      tokensIn, tokensOut, costEstimate, timestamp: Date.now()
    });
  }

  recordError(error, context) {
    this.currentTurn.errors.push({ error: String(error), context, timestamp: Date.now() });
  }

  endTurn() {
    this.currentTurn.endTime = Date.now();
    this.currentTurn.totalDurationMs = this.currentTurn.endTime - this.currentTurn.startTime;
    this.turns.push({ ...this.currentTurn });
    delete this.currentTurn;
  }

  summarize() {
    return {
      agent: this.agentName,
      totalTurns: this.turns.length,
      totalToolCalls: this.turns.reduce((sum, t) => sum + t.toolCalls.length, 0),
      totalErrors: this.turns.reduce((sum, t) => sum + t.errors.length, 0),
      turns: this.turns
    };
  }
}

使用方式很简单:在每个 Agent 轮次的开始和结束之间,用 recordToolCallrecordError 记录关键节点。summarize() 会给你一份可读的会话报告,包含错误分布、工具调用频率、每轮耗时。

进阶一点,可以把 trace 输出成 OpenTelemetry 格式,直接灌进 Grafana 或 Datadog,这样你就能跨多个 Agent session 做聚合分析:哪个 helper 最常导致失败、哪种工具调用耗时最长、子 Agent 的 fanout 比例是否异常。

结论:Observability 不是锦上添花,是 Agent 时代的必备基础设施

Coding Agent 不会再回退到”只做补全”的时代了。但工具的自主性越高,调试难度越大。你不可能靠看最终 diff 理解一个跑了 3 小时的 Agent 干了什么。

三个可落地的下一步:

  • 本周:给你正在用的 Coding Agent 加一层最小 trace,至少记录 tool call 和 error。
  • 本月:把最近一次 Agent 失败 session 的 trace 拿出来复盘,找到那个”交接点”错误。
  • 本季度:把高频错误沉淀成 eval 数据集,每次 Agent 更新后自动跑回归测试。

判断标准很简单:如果你看到一次 Agent 失败,但说不清它在哪一步开始偏的,说明你的可观测性还不够。

收尾

打开黑箱不是为了找 Agent 的麻烦,是为了让 Agent 越来越可靠。可观测性做好的团队,Agent 出错的恢复时间会从”小时级”降到”分钟级”——这就是工程化和 vibe coding 的分水岭。

评论区

0 条评论

登录后可评论。

查看完整榜单
查看完整榜单
查看完整榜单