为什么你的AI代理在生产环境中持续失败(以及如何修复)
大多数生产代理失败来自五个原因:无法处理边缘情况的脆弱提示词、缺少瞬态API错误的重试逻辑、无法看到故障的可观察性缺失、没有退出条件的失控循环,以及足够模糊使模型选错的工具定义。五个问题都可以在不更换模型或框架的情况下修复。
每周三。28,400+ 读者。纯干货。
✓ 请查收邮箱 — 点击确认链接以完成订阅。
✓ 订阅成功!
✓ 您已在订阅列表中。
目录
2026年6月更新。
TL;DR: 大多数生产代理失败来自五个原因:无法处理边缘情况的脆弱提示词、缺少瞬态API错误的重试逻辑、无法看到故障的可观察性缺失、没有退出条件的失控循环,以及足够模糊使模型选错的工具定义。五个问题都可以在不更换模型或框架的情况下修复。
【运营者视角】 我在生产中运行了30多个代理。我经历过所有这些失败。那些浪费时间最多的不是那些奇特的问题——而是我以为已经处理好的无聊基础设施故障。
失败1:在边缘情况输入上崩溃的脆弱提示词
在测试用例上有效的提示词会在你未预料的输入上失败。这不是模型限制——这是指令编写问题。
症状: 代理产生无意义输出、调用错误工具,或在输入与测试内容略有不同时输出格式错误的JSON。
根本原因: 你的系统提示词只描述了快乐路径。它没有告诉模型当数据缺失、格式错误或模糊时该做什么。
修复: 在系统提示词中添加明确的边缘情况处理:
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,没有后续尝试。
修复: 用指数退避重试包装每个外部调用:
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:
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费用。或者它一遍又一遍地运行相同的工具调用而不取得任何进展。
修复: 始终设置硬性迭代上限和进度检查:
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_database与get_record或send_email与create_draft等工具中尤为常见。
症状: 模型调用了正确类别的工具,但选了错误的具体工具。或者在错误的上下文中调用工具(在只应读取时使用写入工具)。
修复: 使工具描述互斥,并明确添加”何时不要使用”:
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。
添加明确测试以下内容的测试套件:
- 空或null输入
- 最大预期长度的输入
- 含特殊字符或非ASCII文本的输入
- 外部API返回意外响应形式
如果你的代理在任何这些情况下崩溃,在上线前修复它。生产环境会找到你做的每一个假设。
运营者的最终结论
大多数生产代理失败是伪装成模型问题的基础设施问题。在切换模型之前,向提示词添加重试、结构化日志记录、循环上限和明确的边缘情况处理。修复模糊的工具定义。然后在糟糕的输入上测试。在责怪模型之前做所有这些——以我的经验,模型通常是最后需要改变的东西。
每周三。28,400+ 读者。纯干货。
✓ 请查收邮箱 — 点击确认链接以完成订阅。
✓ 订阅成功!
✓ 您已在订阅列表中。
将AI实战手册发送到您的邮箱
每周三。28,400+ 读者。纯干货。
请查收邮箱。
我们已向您发送确认邮件 — 点击其中的链接以完成订阅。如果一分钟内没收到,请检查垃圾邮件。
订阅成功。
欢迎 — 下一期很快就会送达您的邮箱。
您已在订阅列表中 — 每周三留意查收。