Node给AI接口做SSE代理与鉴权

先把结论甩前面:别让浏览器直连大模型流式接口。中间架一层 Node 做 SSE 反向代理,顺手把鉴权和限流挡在这层,前端只管 fetch 自己后端,密钥不落地、谁能调你说了算。我上周刚因为没这层,被一个实习生用 devtools 把 key 抠出来,半天烧了三十多块 token,血的教训。

事情起因是这样。我们有个内部的写作辅助小工具,前端直接拿着模型的 stream endpoint 在跑。能用,但有两个雷:一是 key 写在前端,F12 一翻就有;二是没人拦着,谁手快谁多调,某天账单突然鼓了一截才发现。于是我把流量全收回 Node 这层。核心就是把上游的 text/event-stream 原样往下透,中间插一道鉴权和一个最朴素的令牌桶。代码大概长这样,没多少行:

javascript 复制代码
const express = require('express')
const app = express()

// 极简内存令牌桶,够内部用了
const buckets = new Map()
function rateLimit(uid, cap = 20, refill = 1) {
  const now = Date.now()
  const b = buckets.get(uid) || { tokens: cap, ts: now }
  b.tokens = Math.min(cap, b.tokens + (now - b.ts) / 1000 * refill)
  b.ts = now
  buckets.set(uid, b)
  if (b.tokens < 1) return false
  b.tokens -= 1
  return true
}

app.post('/api/chat', async (req, res) => {
  const uid = verifyToken(req.headers.authorization) // 自己的鉴权,过不了直接 401
  if (!uid) return res.status(401).end()
  if (!rateLimit(uid)) return res.status(429).end('慢点儿')

  const upstream = await fetch(UPSTREAM_URL, {
    method: 'POST',
    headers: { Authorization: `Bearer ${process.env.MODEL_KEY}`, 'Content-Type': 'application/json' },
    body: JSON.stringify(req.body),
  })

  // SSE 透传的关键:头要对,别被 nginx/代理缓冲住
  res.setHeader('Content-Type', 'text/event-stream')
  res.setHeader('Cache-Control', 'no-cache')
  res.setHeader('X-Accel-Buffering', 'no')
  res.flushHeaders()

  for await (const chunk of upstream.body) {
    res.write(chunk)         // 上游 data: 行原样往下倒
  }
  res.end()
})

踩过的坑提两个。X-Accel-Buffering: no 这行别漏,我们线上挂了 nginx,不加它流式会被攒成一坨一次性吐出来,前端那个逐字蹦的效果全没了,体验直接退化成转圈等。还有客户端断开要监听 req.on('close') 去 abort 上游,不然用户关了页面你这边还在烧 token------上面那段为了短我省了,真上线得补。学习曲线不算陡,但 SSE 透传这块第一次弄总会卡在缓冲和断连上,大半天就耗这儿了。

代理这层稳了之后我有点上头,想着前端那个写作工具的"脑子"能不能也别自己硬编。正好试了个零代码搭智能体的东西,拖一拖配一配,挂个现成模型、把我们团队的文案规范喂成知识库,十几分钟就出了个能用的小助手,发布成一个 API 接口。我那个 Node 代理后面接的上游,从直连模型换成了这玩意儿给的 endpoint,前端代码一行没动。说实话当时有点惊到------我对它说"按我们这套 tone 重写这段",它真就照知识库里的规矩改了,不是泛泛的客气话。缺点也有,第一版回答太干、太"正确",得反复调提示词和知识库切片才有人味儿;而且它只干内容生成这种杂活,工程的脏活累活还是 Node 这层自己扛。但能让我这种懒人不写后端逻辑就攒出个有业务知识的智能体,挺香。(模型/API 我走的讯飞 MaaS,现成调,没自己部署算力。)

绕回来说一句:鉴权限流这种事,放前端是侥幸,放 Node 这层才睡得着。你们的大模型流式接口是直连还是也加了代理层?评论区聊聊各自的令牌桶都用啥扛的。

相关推荐
QC777LX9 小时前
中文专业在AI行业的新岗位:从Prompt到人文训练
人工智能
知几蜗牛9 小时前
2.8万亿参数上云之后,Kimi K3的门槛变低了吗
人工智能
jingli99 小时前
微信自动回复怎么设置?重复问题怎么自动回答——私域FAQ自动应答搭建全流程(关键词匹配+知识库+语义AI,2026实操版)
人工智能·自动化·用户运营
Ai_easygo9 小时前
AI下半场_09_CSDN版_Agent安全攻防
人工智能
速易达网络9 小时前
宇树机器人和Microduck仿真区别
人工智能
PNP Robotics9 小时前
【PNP具身解读】GPT6 Astra:具身智能新范式,大模型 + Franka机器人快速落地验证一、GPT6 Astra 背后的布局、数据与具身方向
人工智能·学习·机器学习·机器人
一木 之林9 小时前
手搓家庭服务器:二手装机、Docker部署、端口映射与frp内网穿透
服务器·人工智能·git·编辑器
咖啡星人k9 小时前
MCP 协议入门:从 tools/list 到一次真实调用
人工智能·mcp·ai工程
YOLO数据集集合9 小时前
光伏板红外-可见光配对缺陷检测数据集 | 光伏板缺陷 红外可见光配准 多模态检测 无人机巡检 目标检测 YOLO格式9094期
人工智能·目标检测·计算机视觉·目标跟踪·红外·无人机视角·太阳能板
知几蜗牛9 小时前
Agent会互相调用了,但A2A 1.0真正解决的是协作边界
人工智能