Agent限流区分用户组,免费版限制工具调用次数
Agent 接入越来越多的用户后,如果不按用户组区分限流,免费用户很容易把共享额度耗尽,付费用户又可能被误伤。
本文要解决的就是在 Agent 网关或业务层,按用户组设置不同的工具调用次数限制,并针对免费版单独收紧额度,让限流策略精确到组而不是全局一刀切。
全文基于常见网关中间件和业务代码给出可直接复用的配置方法,零基础也能照着操作。
先想清楚限流维度:按用户组还是按单个用户
配置之前,先明确你的限流对象。
常见的做法有两种:
- 按用户组限流:同一组内所有用户共享一个额度池。适合免费版、体验版、企业版这种套餐维度控制。
- 按单个用户限流:每个用户独立计数,互不影响。适合内部系统或需要精细审计的场景。
如果你的目标是“免费版限制工具调用次数”,通常采用 用户组限流。
实现时,需要两个数据:用户所属组(免费组、付费组)和该组对应的次数阈值。
用户组可以从用户表中读取,也可以从 JWT 或会话缓存里直接拿。
建议在网关层统一解析,避免每个业务服务都重复写判断。
配置步骤:从用户组识别到限流规则生效
下面以常见的网关 + Redis 计数方案为例,演示完整配置过程。
假设你有两个组:free(免费版)和 premium(付费版),免费版每分钟最多调用 10 次工具,付费版每分钟 60 次。
1. 在用户表或认证信息中标记用户组
如果用户表还没分组字段,先加一列:
alter table users add column group_name varchar(20) default 'free';
已有用户可以手动更新,例如把内部账号划到付费组:
update users set group_name = 'premium' where email = 'admin@example.com';
实际生产环境建议让登录接口在生成 token 时把 group_name 写进 JWT claims,这样后续限流中间件不需要再查数据库。
2. 编写限流中间件,按组取阈值
使用 Node.js + Express 的示意代码如下:
const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis').RedisStore;
const redisClient = require('./redis');
const groupLimits = {
free: { windowMs: 60 * 1000, max: 10 },
premium: { windowMs: 60 * 1000, max: 60 }
};
function agentLimiter(req, res, next) {
const group = req.user?.group_name || 'free';
const limit = groupLimits[group] || groupLimits.free;
const limiter = rateLimit({
store: new RedisStore({
sendCommand: (...args) => redisClient.sendCommand(args)
}),
windowMs: limit.windowMs,
max: limit.max,
keyGenerator: (req) => `${req.user.id}:${group}:tools`, // 按用户+组组合计数
handler: (req, res) => {
res.status(429).json({ error: '工具调用次数超限,请升级套餐或稍后再试' });
}
});
return limiter(req, res, next);
}
app.use('/api/agent/tools', agentLimiter);
上面的配置里,keyGenerator 用了 用户 ID + 组名 + 动作类型 的组合,
这样每个用户在所属组内独立计数,
同时组名变化后计数键也会变化,
避免把免费用户升级后旧额度残留成问题。
3. 免费版的工具调用入口单独挂更严格的限制
如果你的免费版只开放一小部分工具,最好把免费版能调用的工具接口单独列出来,然后对这段路由使用更严格的限流。
例如:
app.use('/api/agent/tools/free', rateLimit({
windowMs: 60 * 1000,
max: 5,
message: '免费版每分钟仅支持 5 次工具调用,请升级后解锁更多次数'
}));
注意这里不要再叠加一个全局的 agentLimiter,否则会出现两次计数,用户还没到真实阈值就被拦截。
通常做法是:在网关层先路由到具体工具模块,再应用对应的限流规则。
验证限流是否生效:制造超限请求
配置完成后,用 curl 模拟不同的用户组进行验证。
先造一个免费版用户请求:
curl -H "Authorization: Bearer " \
http://localhost:3000/api/agent/tools/translate
连续请求超过 10 次后,应该看到 429 响应,并返回你自定义的 JSON 错误信息。
再换付费用户 token 测试,确认 60 次以内不拦截。
还可以直接查 Redis 里的计数键:
redis-cli keys '*tools*'
redis-cli get ":free:tools"
如果 key 存在且数值在递增,说明限流计数正在正常工作。
避坑要点:这几个问题最容易踩
用户组不存在时默认按免费组处理。
很多系统忘了处理未知组,导致认证信息异常的请求直接绕过限流。
建议中间件里对所有无法识别组名的请求,一律使用最低限额。
注意限流键是否包含用户 ID。
如果只按组名计数,那么一个免费用户狂刷会消耗整个免费组的额度,其他用户跟着遭殃。
如果只想控制单个用户,key 里就不要放组名;
如果想组内共享额度,key 只能放组名。
按你的业务需求二选一,别混用。
免费版限制工具调用次数后,要给出友好的返回提示。
不要只抛一个 429 状态码,用户不知道发生了什么。
返回体里最好写明剩余配额、重置时间或升级路径,这样运营压力会小很多。
升级用户组后,旧计数可能还残留。
因为 key 里带了组名,用户从 free 变成 premium 后,free 的 key 不再被使用,计数自然失效,这种情况是安全的。
但如果你的 key 只有用户 ID,升级后必须手动清掉旧 key。
判断限流策略是否合理:看两个指标
配置完成后,别急着上线,先观察一段时间。
重点看两个指标:
- 免费组命中限流的请求占比:如果超过 30%,说明免费额度设置得太低,或者恶意刷量太多,需要调整阈值或加验证码。
- 付费组是否出现误拦截:如果付费用户也频繁收到 429,检查是不是 key 设计共享了额度,或者时间窗口设置不合理。
建议在日志里把限流命中的用户 ID、组名、接口路径和 IP 都记录下来,方便后续分析。
日志格式可以这样:
{"time":"2025-03-01 12:00:01","user_id":10086,"group":"free","path":"/api/agent/tools/translate","action":"limited"}
以上是按用户组区分限流、免费版限制工具调用次数的基础配置方法。
实际部署时,如果并发量高,可以再引入令牌桶或滑动窗口算法,但对于大多数中小项目,Redis 固定窗口已经足够。
先把组维度拆清、阈值设好、提示写明白,限流就不会成为产品体验的拦路虎。
如果你正在调整 Agent 的免费版策略,建议先用一个测试用户组跑完整流程,确认计数、拦截、提示都符合预期后再全量放开,这样比直接改生产规则更稳妥。