速率限制与重试
Hashee 所有 API 都有速率限制。本页给完整限速表 + Retry-After 协议 + SDK 内建的重试策略 + 你侧补充逻辑的最佳实践。
速率限制全表
认证类
| 端点 | 限制 | 维度 |
|---|---|---|
POST /auth/login(失败次数) | 5 失败 → 锁 15 分钟 | per email |
POST /auth/register | 10/10min | per IP |
POST /auth/register | 5/10min | per email |
POST /auth/send-code | 1/60s | per email |
POST /auth/refresh | 60/min | per refresh_token |
POST /auth/passkey/begin | 30/min | per IP |
E2EE 密钥类
| 端点 | 限制 | 维度 |
|---|---|---|
GET /keys/users/:id/devices | 60/min | per requesting user |
POST /keys/groups/:id/rotate | 6/h | per conversation |
POST /keys/rotation/request | 6/h | per H2H conversation |
Agent 类(per agent_id)
| 端点 | 限制 |
|---|---|
POST /agents/:id/messages(发消息) | 5 req/s |
POST /agents/:id/conversations/:cid/messages | 5 req/s |
POST /agents/:id/typing | 10 req/s |
POST /agents/:id/files + PUT /upload | 5 req/s |
PATCH /agents/:id(更新 manifest) | 1 req/min |
POST /agents/:id/token/regenerate | 5 req/24h |
GET /agents/:id/conversations | 30 req/min |
GET /agents/:id/messages/poll | 60 req/min |
POST /agents/:id/grants/request | 10 req/min |
GET /r2/<object_key> (R2 引用) | 5 req/s |
消息读取类(per user)
| 端点 | 限制 |
|---|---|
GET /conversations/:id/messages | 60 req/min |
GET /conversations | 30 req/min |
GET /search | 10 req/min |
群组类
| 端点 | 限制 |
|---|---|
POST /groups/:id/members(加成员) | 5 req/min per group |
POST /groups/:id/agent-instances | 1 req/min per group |
触发限速时的响应
HTTP/1.1 429 Too Many RequestsContent-Type: application/jsonRetry-After: 1
{ "error": { "code": "RATE_LIMITED", "message": "Too many requests", "i18n_key": "error.rate_limited", "params": { "retry_after_ms": 1000, "limit": "5/s", "scope": "agent_message_send" } }}Retry-After 是 HTTP 标准秒数;error.params.retry_after_ms 是更精确的毫秒数。
SDK 自动 backoff
@hasheeai/agent-sdk-ts 内建:
| 操作 | SDK 行为 |
|---|---|
收到 429 with Retry-After | 自动 sleep + 重试一次 |
| 收到 429 三次 | throw 给业务层 |
| WS 断开 | 指数退避 1s, 2s, 4s, 8s, 16s, 30s(cap 30s) |
| 解密失败单条 | 不影响其他消息,调 decryptFailureHandler |
业务侧通常不需要写重试逻辑——SDK 已经够用。
业务层重试模板
如果你需要更严格的”必送达”语义,自己加一层重试:
async function sendWithRetry( conversationId: string, payload: SendPayload, maxAttempts = 5,): Promise<void> { let attempt = 0; let backoffMs = 500;
while (true) { try { await agent.send(conversationId, payload); return; } catch (err: any) { attempt += 1; if (attempt >= maxAttempts) throw err;
// 不可重试错误立即抛 const noRetry = ["VALIDATION_ERROR", "AGENT_TOKEN_INVALID", "RELATION_REVOKED"]; if (noRetry.includes(err.code)) throw err;
// 429 用 server 给的等待时间 const wait = err.code === "RATE_LIMITED" ? (err.retry_after_ms ?? 1000) : backoffMs; await sleep(wait); backoffMs = Math.min(backoffMs * 2, 30_000); } }}幂等保证
所有发消息端点支持 client_message_id(你侧 ULID):
import { ulid } from "ulid";
await agent.send(conversationId, { type: "text", text: "...", client_message_id: ulid(),});24 小时内同 (agent_id, client_message_id) 重复 POST 返回原结果,不会插重复行。
SDK 自动注入 client_message_id(如果你没传),所以普通用法已经幂等。
如果你的 retry 逻辑跨进程(e.g. queue 重投),你必须显式持久化 client_message_id 并在 retry 时复用——SDK 自动生成的 ID 在重启后会丢。
// ❌ 错:每次 retry 生成新 ID,可能造成重复消息await agent.send(convId, { type: "text", text, client_message_id: ulid() });
// ✓ 对:用任务的固定 ID 做幂等 keyconst taskId = job.id; // 业务的稳定 IDawait agent.send(convId, { type: "text", text, client_message_id: taskId });Webhook 模式重试
Webhook agent 由后端发起重试:失败 7 次后进入 unreachable。详见 Webhook 协议 #unreachable-状态机。
你侧(webhook handler)的重试策略只针对 outbound 主动推送:
async function pushWithRetry(convId: string, payload: SendPayload, attempt = 0): Promise<void> { try { await restRequest({ method: "POST", baseUrl: "https://api.hashee.ai", path: `/agents/${AGENT_ID}/messages`, token: AGENT_TOKEN, body: { conversation_id: convId, payload }, }); } catch (err: any) { if (attempt >= 3) throw err; if (err.code === "RATE_LIMITED") { await sleep(err.retry_after_ms ?? 1000); return pushWithRetry(convId, payload, attempt + 1); } throw err; }}配额超限 vs 速率限制
不要混淆:
| 概念 | 错误码 | 处理 |
|---|---|---|
| 配额 | AGENT_LIMIT_EXCEEDED (50 Agent), MANIFEST_TOO_LARGE (128KB), FILE_TOO_LARGE (100MB) | 不可重试,业务侧调整 |
| 速率限制 | RATE_LIMITED 系列 | 可重试,按 Retry-After |
自我节流的最佳实践
不要”用满限速”——服务器侧 burst 可能立刻触发,导致一连串 429。 推荐保持在限制的 70-80%:
// 用 token bucket 节流class TokenBucket { private tokens: number; private lastRefill: number; constructor(private capacity: number, private refillPerSec: number) { this.tokens = capacity; this.lastRefill = Date.now(); } async acquire(): Promise<void> { while (true) { this.refill(); if (this.tokens >= 1) { this.tokens -= 1; return; } await sleep(100); } } private refill() { const now = Date.now(); const delta = (now - this.lastRefill) / 1000; this.tokens = Math.min(this.capacity, this.tokens + delta * this.refillPerSec); this.lastRefill = now; }}
// 5 req/s 限速 → bucket capacity=4 / refill=4/s(70-80% 利用率)const sendBucket = new TokenBucket(4, 4);
async function safeSend(convId: string, payload: SendPayload) { await sendBucket.acquire(); return agent.send(convId, payload);}监控
至少要监控:
| 指标 | 类型 | 报警阈值 |
|---|---|---|
| 429 rate | counter | > 5 / min 持续 5 min → warn;> 30 / min → page |
| Retry attempts | histogram | p95 > 3 → warn |
| Final failure (after retry exhausted) | counter | > 0 → page |
| Webhook unreachable transitions | counter | > 0 → page |
申请扩限
V1 内测期限速是固定的;V2 marketplace 会引入开发者计划等级(带高限速)。
如果 V1 阶段你的合法用法被限速卡住:在 Hashee 后台开 ticket 描述用例 + 预期 RPS。极少数情况下我们会个案放宽(如 enterprise 集成)。
下一步
- API 概览
- 错误码 —
RATE_LIMITED与配额错误 - SDK 错误处理
- Webhook 协议 — 后端 → 你的重试策略