Alejandro Rioja.
AI Agents

为什么你的AI代理在生产环境中持续失败(以及如何修复)

Alejandro Rioja
Alejandro Rioja
2 分钟阅读
TL;DR

大多数生产代理失败来自五个原因:无法处理边缘情况的脆弱提示词、缺少瞬态API错误的重试逻辑、无法看到故障的可观察性缺失、没有退出条件的失控循环,以及足够模糊使模型选错的工具定义。五个问题都可以在不更换模型或框架的情况下修复。

免费新闻通讯

每周三。28,400+ 读者。纯干货。

目录

2026年6月更新。

TL;DR: 大多数生产代理失败来自五个原因:无法处理边缘情况的脆弱提示词、缺少瞬态API错误的重试逻辑、无法看到故障的可观察性缺失、没有退出条件的失控循环,以及足够模糊使模型选错的工具定义。五个问题都可以在不更换模型或框架的情况下修复。

【运营者视角】 我在生产中运行了30多个代理。我经历过所有这些失败。那些浪费时间最多的不是那些奇特的问题——而是我以为已经处理好的无聊基础设施故障。

失败1:在边缘情况输入上崩溃的脆弱提示词

在测试用例上有效的提示词会在你未预料的输入上失败。这不是模型限制——这是指令编写问题。

症状: 代理产生无意义输出、调用错误工具,或在输入与测试内容略有不同时输出格式错误的JSON。

根本原因: 你的系统提示词只描述了快乐路径。它没有告诉模型当数据缺失、格式错误或模糊时该做什么。

修复: 在系统提示词中添加明确的边缘情况处理:

code
If the input data is missing a required field, return:
{ "status": "error", "reason": "missing_field", "field": "<fieldname>" }
Do NOT attempt to infer or hallucinate missing values.

If you are uncertain which tool to call, call no tool and return:
{ "status": "clarification_needed", "question": "..." }

模型可靠地遵循边缘情况的明确指令。错误在于假设它会将快乐路径指令推广到处理混乱情况。

失败2:瞬态API错误的重试逻辑缺失

你的代理调用的每个外部API最终都会失败。Claude API、Meta Graph API、你的数据库——它们都会返回5xx错误、超时或限流。如果代理没有重试逻辑,一个瞬态错误就会终止整个运行。

症状: 代理运行在不同步骤随机失败。日志显示503或429,没有后续尝试。

修复: 用指数退避重试包装每个外部调用:

typescript
async function withRetry<T>(fn: () => Promise<T>, retries = 3, baseDelayMs = 500): Promise<T> {
  for (let attempt = 0; attempt <= retries; attempt++) {
    try {
      return await fn();
    } catch (err: any) {
      const isTransient = err.status === 429 || err.status >= 500 || err.code === "ECONNRESET";
      if (!isTransient || attempt === retries) throw err;
      const delay = baseDelayMs * Math.pow(2, attempt) + Math.random() * 100;
      await new Promise((r) => setTimeout(r, delay));
    }
  }
  throw new Error("unreachable");
}

// Usage
const result = await withRetry(() => client.messages.create({ ... }));

三次指数退避重试处理约99%的瞬态失败。将此添加到每个外部调用,你的一半随机失败会消失。

失败3:无可观察性——你看不到什么在崩溃

这是生产中最常见的失败模式,也是调试成本最高的:代理静默失败或产生错误输出,你不知道链条中哪里出了问题。

症状: 你知道有问题但无法确定步骤。你添加console.log语句并手动重新运行尝试复现。

修复: 每个步骤的结构化日志记录,带有追踪整个执行的运行ID:

typescript
function createLogger(runId: string, agentName: string) {
  return {
    step: (step: string, data: object) =>
      console.log(JSON.stringify({ runId, agent: agentName, step, ts: new Date().toISOString(), ...data })),
    error: (step: string, err: unknown) =>
      console.error(JSON.stringify({ runId, agent: agentName, step, error: String(err), ts: new Date().toISOString() })),
  };
}

const log = createLogger(crypto.randomUUID(), "newsletter-agent");
log.step("fetch_topic", { topicId: topic.id, topic: topic.name });
// ... do work ...
log.step("draft_complete", { subject: draft.subject, wordCount: draft.body.split(" ").length });

如果你在Cloudflare Workers上,这些日志会进入Logpush或Workers Tail。本地运行或VPS上,将其传输到日志聚合器。结构化JSON意味着你可以按runId过滤,看到单次运行中发生的确切情况。

失败4:没有退出条件的失控循环

代理循环——模型调用工具并迭代直到满足条件——如果条件从未满足或模型错误识别它,可能永远运行下去。

症状: 代理在超时前花费数百美元的API费用。或者它一遍又一遍地运行相同的工具调用而不取得任何进展。

修复: 始终设置硬性迭代上限和进度检查:

typescript
const MAX_ITERATIONS = 10;
let iterations = 0;
let lastToolCallName = "";
let sameToolCallCount = 0;

while (true) {
  iterations++;
  if (iterations > MAX_ITERATIONS) {
    log.error("loop", { reason: "exceeded_max_iterations" });
    break;
  }

  const response = await client.messages.create({ ... });

  // Detect stuck loops: same tool called 3x in a row
  const toolCall = response.content.find(b => b.type === "tool_use");
  if (toolCall?.name === lastToolCallName) {
    sameToolCallCount++;
    if (sameToolCallCount >= 3) {
      log.error("loop", { reason: "stuck_loop", tool: toolCall.name });
      break;
    }
  } else {
    sameToolCallCount = 0;
    lastToolCallName = toolCall?.name ?? "";
  }

  if (response.stop_reason === "end_turn") break;
}

这捕获了”运行太长”和”原地打转”两种失败模式。上限应该对快乐路径足够宽松,但足够紧以限制爆炸半径。

失败5:模型错误解析的模糊工具定义

如果给模型两个描述重叠的工具,它有时会调用错误的那个。这在search_databaseget_recordsend_emailcreate_draft等工具中尤为常见。

症状: 模型调用了正确类别的工具,但选了错误的具体工具。或者在错误的上下文中调用工具(在只应读取时使用写入工具)。

修复: 使工具描述互斥,并明确添加”何时不要使用”:

typescript
const tools = [
  {
    name: "get_subscriber",
    description: "Fetch a single subscriber record by email. Use ONLY when you have a specific email address. Do NOT use for searching or listing subscribers.",
    input_schema: { ... }
  },
  {
    name: "search_subscribers",
    description: "Search subscribers by tag, segment, or status. Use when you need to find subscribers matching a criteria — NOT when you have a specific email address.",
    input_schema: { ... }
  }
];

“当X时不要使用”条款是大多数人跳过的部分。这是最重要的部分。模型比从正面描述中推断更擅长遵循明确的负面约束。

还有一件事:在糟糕的输入上测试代理

大多数代理只在干净的快乐路径输入上测试。生产环境有脏输入:空字符串、null字段、Unicode边缘情况、返回200但响应模式意外的API。

添加明确测试以下内容的测试套件:

如果你的代理在任何这些情况下崩溃,在上线前修复它。生产环境会找到你做的每一个假设。

运营者的最终结论

大多数生产代理失败是伪装成模型问题的基础设施问题。在切换模型之前,向提示词添加重试、结构化日志记录、循环上限和明确的边缘情况处理。修复模糊的工具定义。然后在糟糕的输入上测试。在责怪模型之前做所有这些——以我的经验,模型通常是最后需要改变的东西。

继续阅读

将AI实战手册发送到您的邮箱

每周三。28,400+ 读者。纯干货。

↵ 查看全部结果 esc esc 关闭