前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
返回 AI 情报前线
All News · 全部资讯9301
  • EU AI Act水印规定生效:AI生成内容标记可被伪造吗
  • Java 21 + Spring Boot + React 19 构建AI提示词管理平台
  • 30个AI API价格实测:价差高达350倍
  • 向量数据库深度解析与DataLoader实现
  • Pizza-Builder 法则:结构化提示词设计避免 AI 反复猜错
  • Anthropic API Prompt Cache深度分析:22%命中率才回本
  • AI Agent 自报完成不可信:两种独立验证方法
  • AI生成代码的隐性风险:我们正在交付看不见的假设
  • Python 内容审核:Schema 门控批量分类 + 人工复核队列
  • Anthropic Claude 大规模宕机,多项服务受影响
  • 生产环境 LLM 选型:别只看基准分,场景上下文才是关键
  • AI写的汇编不可信:三行命令验证真伪
  • 崩溃路由迷信 0.97 置信度:单点模型决策的风险
  • AI编程代理55种低层代码失败案例与124个修复技能
  • TTFT 与 TTFB 的区别:45 个 AI API 四区域实测数据
  • Gemini 3.7 Flash 发布:编程+Agent 能力大幅提升,价格腰斩
  • LLM Agent 重试机制的可观测性设计实践
  • Qwen 3.8 27B 发布:本地运行出色但默认过度思考
  • 客服AI智能体架构详解:从问答型到工作流执行型
  • Anthropic在Claude输出中嵌入水印引发写作伦理争议
  • 免费AI接口429错误被吞?用Relay转发元数据
  • 用AI高效写API文档的5个实战技巧
  • 我用Python写了个检测硬编码密钥的CLI工具
  • Stripe超70亿美元收购AI网关OpenRouter
  • AI Agent盘活多仓库遗留项目:OpenCode+SpecKit实战
  • AI Agent五层架构详解:Graph Engineering为何是缺失的那环
  • GhostSplice攻击:利用MCP协议碎片化提示窃取SSH密钥
  • 本地优先AI宣言:模型主权与隐私保护新思路
  • 紧急:SharePoint认证绕过漏洞CVE-2026-55040正被积极利用
  • Agent的State、Memory、Checkpointing不是一回事
  • 受监管行业LLM基础设施检查清单
  • 无限画布性能架构:视口虚拟化与空间索引
  • AI 编程 Agent 的 harness 才是决定因素:同一模型最大 23.8 分差距来自它
  • OpenCode 源码解读:MIT 开源 AI Agent Harness 内部机制全解析
  • AI Coding Agent 的 prompt 格式正在标准化:规则/流程/记忆如何引导代码修改
  • Same input, same receipt:让 AI 基准测试结果可验证、可复现
  • Claude Mythos + Project Glasswing 已发现超一万个高危漏洞
  • GraphQL N+1 排查新法:数 Resolver 调用次数而非响应延迟
  • Grok 4.6:后训练优化突破Scaling Law,成本不变性能跃升
  • 用免费模型从数据库迁移文件自动生成Seed数据,告别手写Fixture
  • AI生成服务的安全审计:systemd能力边界检查实战
  • Attention机制原理深度解析:逐行代码讲解Transformer核心
  • 2026年AI API成本实测:开源开发者的省钱指南
  • 9个多模态API实战对比:成本与效果实测
  • Kimi K3的2.8T参数不是最难部分:MoE推理系统工程分析
  • 做AI产品别先上RAG:汽车检测AI架构教训
  • 向量检索的精确匹配盲区:RAG检索架构补全方案
  • AI日报:DeepSeek调价、智谱发布最强代码模型、Anthropic隐藏模型
  • AI生成文本中的隐藏字符:零宽空格等陷阱
  • 安全运行AI生成代码:E2B/Modal/Piston实战对比
  • DebugClip:把 DevTools 上下文一键贴给 AI 的 Chrome 插件
  • 已加载 51 / 9301
8.0
热点
AI SCORE
编程提效2026-08-17 05:44

免费AI接口429错误被吞?用Relay转发元数据

dev.to · AI#API调试#代理层#错误处理
Editor brief · 编辑速览

免费AI模型的实际失败原因往往是配额/限流状态码被代理层吞噬,而非模型本身能力耗尽。浏览器收到的是通用500而非可区分的429/402,导致用户无法判断该重试还是该等待。

文章思维导图
Knowledge map
拖拽缩放
Full translation

完整中文译文

免费 AI 额度在代理吞掉 Retry-After 头时悄然失效

免费 AI 端点通常不会因为模型能力耗尽而失败。它们失败是因为配额、速率限制或上游网关返回了浏览器代码从未学会读取的状态码。修复方案不是一个更精致的错误提示,而是一个小小的中转层——它转发你所需的操作元数据,把一个静默的限制转变为一个可操作的状态。

本周的 AI 信息流被水印和 Agent 工具门控占据,但免费模型演示中真正常见的失败要低调得多。上游提供的 429 或 402 状态到达浏览器时变成了开发者自己服务器的通用 500,于是用户看到"Something went wrong",无法区分"等待并重试"、"你已经用完了配额"和"连接实际上已断开"。

MonkeyCode 定位为一个开源选项,提供免费模型额度(目前声明为 3000 万 Token),以及一条免费服务器路径,你可以用它来测试这个中转层。披露:本文是 MonkeyCode 产品推广的一部分。请把这个 Token 数字当作需要在你自己仪表盘中确认的数据,而非永久合约;下面的实现与提供商无关。

中转层拥有失败契约

一旦你在浏览器和模型之间加入自己的服务器,你就承担了通常会破坏失败契约的那部分。浏览器 fetch 只能检查你返回给它的响应。如果你的中转层捕获了上游的 429、记录了它,然后返回 res.status(500).json({ error: 'Upstream failed' }),你就删掉了本应告诉客户端等待的信息。

app.post('/api/generate', async (req, res) => {
  const upstream = await fetch(process.env.MODEL_URL, {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify(req.body),
  });

  const retryAfter = upstream.headers.get('retry-after');
  res.status(upstream.status);
  if (retryAfter) res.set('retry-after', retryAfter);

  for await (const chunk of upstream.body) {
    res.write(chunk);
  }
  res.end();
});

重要的一行是 res.status(upstream.status),而不是流转发。当你的服务器意外地把所有上游状态都压缩成 500 时,前端只能猜测,而猜测产生的是最糟糕的重试按钮:它发起一个全新请求,而配额仍然已耗尽。

把状态映射为状态,而非消息

在客户端,在写任何 DOM 更新之前先把响应建模为一个状态机。速率限制不是错误;它是一个带时钟的临时状态。已耗尽的配额在本次会话中是终结性的。传输失败则是完全不同的问题。

type ChatState =
  | { name: 'streaming'; controller: AbortController }
  | { name: 'rateLimited'; retryAt: number }
  | { name: 'quotaExhausted' }
  | { name: 'failed'; message: string };

function handleResponse(res: Response): ChatState | null {
  if (res.status === 429) {
    const seconds = Number(res.headers.get('retry-after') ?? 0);
    return { name: 'rateLimited', retryAt: Date.now() + seconds * 1000 };
  }
  if (res.status === 402) return { name: 'quotaExhausted' };
  if (!res.ok) return { name: 'failed', message: `HTTP ${res.status}` };
  return null;
}

屏幕阅读器用户不应该从红色边框或消失的提示中推断这些状态。在 DOM 中放置一个单独的 aria-live="polite" 区域,然后不要再替换它。当状态改变时,写一个句子,而不是状态名称:"Rate limit reached. You can try again in 18 seconds." 对于已耗尽的配额,说"Free allowance used. Start a new session or switch the model route." 这就是可恢复的等待和模糊失败之间的区别。

让焦点与播报分离

不要仅仅因为感觉有用就把焦点移到错误消息上。焦点移动是一个导航动作;实时区域是一个更新。对于配额耗尽这类终结性状态,你可以把焦点移到重试或设置按钮,但要只移一次,而且要等到播报有机会运行之后。键盘用户不应该在流还在 settle 的时候被从对话记录中拽走。

当重试开始时,对前一个流调用 controller.abort() 并替换对话记录写入器。免费端点有时会在客户端已经继续之后仍交付若干 chunk。如果你在多个提示之间共享一个 reader 变量,可能会导致旧 Token 出现在新提示之后,屏幕阅读器会读出一个不再匹配用户意图的对话记录。

不花付费 key 就能测试四种状态

使用免费服务器选项而不是直接从浏览器调用模型有两个实际好处。你把凭证从前端代码中隔离出来,而且你创建了一个统一的地方来标准化任何提供商的上游状态码。这就是让重试逻辑可测试而不是成为埋在 UI 中的特殊案例的原因。

针对同一个接口运行四种上游条件:一次成功的流、一个带有效 Retry-After 的 429、一个不带 Retry-After 的 429,以及一个终结性配额响应(如 402)。在每种情况下,先只用键盘测试,然后在 macOS 上用 VoiceOver、在 Windows 上用 NVDA 或 Narrator 测试。记录精确的播报字符串以及焦点停留的位置。如果屏幕阅读器对所有四种状态都重复相同的"Something went wrong"短语,那么中转层或客户端映射就是错的。

这种方法的局限

这个设计不会让免费额度变大,如果上游提供商把它隐藏在某层负载均衡器后面,或者免费服务器本身引入了一个你没有考虑到的独立速率限制,它也无法提供帮助。如果 Retry-After 缺失了,展示一个估计的倒计时,但要标注为估计值。永远不要让 UI 暗示用户可以跳过提供商未披露的等待。

有价值的工作不是重试按钮。它是把上游的操作响应保持足够长的时间,让键盘或屏幕阅读器用户能够据此行动。针对 MonkeyCode 免费层和免费服务器路径跑一次矩阵,你会在付费用户之前捕获配额 bug。

Original source

本文由 AI 翻译整理自 dev.to · AI,原文版权归原作者所有。

阅读英文原文
上一篇
Anthropic在Claude输出中嵌入水印引发写作伦理争议
下一篇
用AI高效写API文档的5个实战技巧