一个独立开发者在上线前一天发来日志截图:/api/chat 路由在本地 next dev 跑得好好的,部署到 Vercel 之后每次问到长 prompt 就 504,短句没问题。Vercel Dashboard 的 Functions 日志里只写了 Task timed out after 25.0 seconds,没有任何额外错误码。
OpenAI 的 GPT-4o 生成一段 1000 token 的回复通常只需要 5-15 秒,为什么还会超时?答案在于 Vercel Edge Function 计时的是「你的代码开始执行到第一个响应字节发出」的总时长,而不是 OpenAI API 本身的生成时间。
EDGE_FUNCTION_INVOCATION_TIMEOUT 触发的三条路径
Vercel 的 Edge 超时报错不止一种。同一个现象(504)背后可能对应三个不同的触发路径,排查顺序错了会浪费几个小时。
| 错误码 | 触发条件 | 日志里怎么识别 |
|---|---|---|
EDGE_FUNCTION_INVOCATION_TIMEOUT | 25 秒内没有任何响应字节发出 | Task timed out after 25.0 seconds |
INTERNAL_EDGE_FUNCTION_INVOCATION_TIMEOUT | 函数甚至没有开始执行(冷启动 + 队列等待) | Function did not begin responding within 25s |
| 504 Gateway Timeout(idle 版) | 已经开始 streaming,但连续 10-15 秒没有新 chunk | 函数日志不报错,客户端收到截断的响应 |
最常见的场景是第一种:你在 POST handler 里 await 了整个 OpenAI 非流式调用,API 耗时 18 秒 + Vercel 到 OpenAI 的网络往返 4 秒 + 冷启动 2 秒 = 24 秒,刚好擦过 25 秒上限。长 prompt 或 tool call 多一轮就直接超。
30 秒自检
在开始改代码之前,先确认这四件事:
- 确认 runtime 类型:打开
/api/chat/route.ts,检查是否有export const runtime = 'edge';。如果没有这一行,你跑的是 Node.js runtime,超时原因完全不同(看下一节)。 - 检查 OpenAI 调用方式:是
stream: true还是stream: false?如果是stream: false,改这里是第一步。 - 排查 tool call 循环:如果用了 Vercel AI SDK 的
streamText且接了 tools,检查是否设了maxSteps。没设的话,一次 tool call 往返可能吃掉 8-15 秒。 - 确认 region:Edge Function 默认跑在离用户最近的节点;如果你在国内开发、Vercel 把请求路由到了美西节点,网络延迟会多 200-400ms,但这种量级通常不是 timeout 主因。
streaming 三件套:先发 header、pipe 原流、加 keep-alive
Edge Function 的 25 秒只在意「第一个响应字节」。一旦 new Response(stream) 返回,函数就不再受这个硬上限约束——它可以持续运行,只在连续空闲(idle timeout)时才会被终止。
// app/api/chat/route.ts
export const runtime = 'edge';
export async function POST(req: Request) {
const { messages } = await req.json();
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
// 第一步:立即发出一个初始化 chunk,打破 25 秒倒计时
controller.enqueue(encoder.encode('{"status":"started"}\n'));
// 第二步:设置 10 秒一次的 keep-alive,防 idle timeout
const keepAlive = setInterval(() => {
controller.enqueue(encoder.encode(': ping\n\n'));
}, 10000);
try {
const openaiRes = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.OPENAI_API_KEY}`,
},
body: JSON.stringify({
model: 'gpt-4o',
messages,
stream: true,
}),
});
const reader = openaiRes.body?.getReader();
if (!reader) throw new Error('No readable body');
clearInterval(keepAlive); // OpenAI 流到达后停掉人工心跳
while (true) {
const { done, value } = await reader.read();
if (done) break;
controller.enqueue(value);
}
} finally {
clearInterval(keepAlive);
controller.close();
}
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache, no-transform',
'Connection': 'keep-alive',
},
});
}
这里 Content-Type: text/event-stream 是关键:Vercel 的 CDN 看到这个 header 才会启用流式传输模式,否则可能 buffer 完整的响应体再一次性发出去——这就等于主动放弃了 streaming 的优势。
单纯 OpenAI 生成慢 vs tool call 循环,别混在一起排查
如果你用的是 Vercel AI SDK,超时可能出现在两个不同的阶段:
- OpenAI 生成慢:模型收到 prompt 后一直在推理,但 stream 里已经有 token 在输出。这种情况加上 keep-alive 就够了。
- tool call 循环卡住:模型输出了一个 tool call 然后停下来等结果;你的代码拿到 tool 结果后又调了一次
generateText,而不是把它喂回同一个 stream 实例。这导致 25 秒倒计时被重置。
第二种情况用 maxSteps 解决:
const result = await streamText({
model: openai('gpt-4o'),
messages,
tools: { search: tool({ ... }) },
maxSteps: 5, // SDK 自动完成 tool → 模型 → 回复,最多 5 轮
});
什么时候应该放弃 Edge,切到 Node.js + Fluid Compute
Edge Function 的 streaming 方案足以应对 90% 的 OpenAI 调用场景,但有些情况应该直接选择更长的超时预算。
| 场景 | 推荐 runtime | 最长超时 | 理由 |
|---|---|---|---|
| 单次问答,无 tool call | Edge + streaming | 不限(只要持续发 chunk) | 延迟最低,CDN 边缘节点直接处理 |
| tool call 1-3 轮 | Edge + maxSteps + keep-alive | 同上 | 可以覆盖,但注意每轮 tool 执行时间 |
| tool call 4 轮以上 | Node.js + maxDuration: 300 | 300 秒 | 多轮 tool call 累计容易触发 idle timeout |
| 需要调第三方 API > 15 秒 | Node.js + Fluid Compute | 800 秒 | 例如调 Google Search、数据库批量查询 |
| 部署在 Pro 套餐上 | Node.js + Fluid Compute | 800 秒 | Pro 才有 Fluid Compute,Hobby 最长 60 秒 |
如果你部署在 Hobby 套餐上且确实需要超过 60 秒的后端处理(比如长期运行的 agent 循环),一种折中方案是把 Edge Function 拆成两个路由:一个流式 API 路由负责向客户端推流,另一个后台任务路由用 Node.js runtime 做重计算,两者之间通过 Vercel KV 或 Redis 通信。
独立开发者在 Vercel 和 OpenAI 之间调试超时问题,经常需要反复部署、改环境变量、看日志。如果网络本身不稳定,Vercel 到 OpenAI API 的 HTTPS 请求会额外增加 1-3 秒的 TCP + TLS 握手,这一部分也会计入 25 秒预算。需要一个海外银行 + Stripe + AI 工具全场景的稳定开发环境来减少这类额外延迟。
Hobby 计划下再被截断怎么办:Serverless Function 也是备选项
有些独立开发者第一反应是把 Edge runtime 全改为 Node.js runtime,但这不一定对。
Node.js runtime 的默认 maxDuration 在 Hobby 计划下是 10 秒,比 Edge 的 25 秒更短。你需要在 vercel.json 或路由段配置里显式声明:
{
"functions": {
"app/api/chat/route.ts": {
"maxDuration": 60
}
}
}
Hobby 计划最大 60 秒,Pro 最大 300 秒(Serverless)或 800 秒(Fluid Compute)。如果你的 Hobby 计划 60 秒仍然不够,有两个选择:升级 Pro 开 Fluid Compute,或者把任务拆成异步 job 队列,即刻返回 202 Accepted 并在后台处理。
不过 Node.js runtime 还有一个隐形成本:冷启动。Hobby 计划下冷启动通常在 500ms-2s,对 OpenAI API 的 5-15 秒生成时间来说占比不大,但如果你的 route 是低频访问的 api/chat,每次冷启动都会把这 2 秒算进你的 25/60 秒预算。