问答
智能体超时后重试工具调用,会不会被重复扣费?
简短回答
只要重试时带上和第一次相同的幂等 key,就不会:okdata 会把成功的结果保存 24 小时,重试时原样返回、不再扣费,所以一次 X 搜索重试几遍也只收 $0.006。失败的调用从不扣费;超时之后,用同一个 key 再发一次,就能知道第一次到底有没有成功。
典型的出错过程是这样的:智能体请求 X 搜索的第 3 页,客户端等了 60 秒放弃,服务器其实已经跑完了,智能体又请求一遍。没有保护的话,第 3 页你付了两次钱,智能体还可能把同一批帖子处理两遍。MCP 协议本身也还在讨论这件事:有个公开 issue 演示了响应丢失导致工具调用被重复执行,请求幂等的提案也还没定稿。在客户端解决之前,得靠你调用的接口来兜底。
okdata 的重试规则
每个逻辑请求生成一个随机 key,这个请求的每次重试都用同一个:
- HTTP: 请求头
Idempotency-Key。 - MCP:
call工具的idempotency_key参数。
规则如下(详见错误码页):
- 24 小时内同一个 key、同样的请求:直接返回保存的结果,
meta.replayed: true,meta.billing.amount: "0",request_id和第一次相同,响应头带Idempotent-Replayed: true。不再扣费。 - 同一个 key、不同的请求:返回
invalid_params,所以 key 用错了也不会拿到别人的数据。 - 失败的调用不保存。用同一个 key 重试失败的请求,会重新执行一次;反正失败本来也没扣钱。
- 第一次还在执行时就来了重试,会收到
rate_limited,retry_after_seconds: 1。等一秒,用同一个 key 再发。
各种结果怎么处理
| 返回 | 是否扣费 | 下一步 |
|---|---|---|
ok |
扣一次 | 直接用 |
timeout(504) |
暂时未知(charged: null) |
用同一个 key 重发,或到 /v1/calls/{request_id} 查 |
upstream_busy、upstream_failed、internal_error |
不扣 | 用同一个 key 重试 |
rate_limited(429) |
不扣 | 等 retry_after_seconds 秒,用同一个 key 重试 |
invalid_params、insufficient_balance |
不扣 | 先改请求或充值 |
not_found |
不扣 | 不要重试 |
带不带 key 差多少
| 任务:X 搜索翻 5 页 | 计费次数 | 费用 |
|---|---|---|
| 每页重试 3 次,没带 key | 15 | $0.09 |
| 同样的重试,带 key | 5 | $0.03 |
抖音视频搜索每页 $0.02,X 帖子搜索和其他接口也是同样的道理;接口越贵,漏带 key 的代价越大。
代码
import os, time, uuid
import requests
URL = "https://api.okdata.online/v1/x/posts/search"
H = {"Authorization": f"Bearer {os.environ['OKDATA_API_KEY']}"}
def search_page(query, cursor=None):
key = str(uuid.uuid4()) # 这一页一个 key,下面重试都用它
params = {"query": query, "sort": "latest", **({"cursor": cursor} if cursor else {})}
for attempt in range(4):
try:
body = requests.get(URL, headers={**H, "Idempotency-Key": key},
params=params, timeout=60).json()
except requests.Timeout:
continue # 同一个 key:已完成的调用会免费重放
if body["code"] == "ok":
return body
if body["retry"] in ("yes", "same_key"):
time.sleep(body.get("retry_after_seconds") or 2 ** attempt)
continue
raise RuntimeError(f"{body['code']}: {body['message']}")
raise RuntimeError("重试 4 次仍无结果")
如果你的智能体走 MCP,在系统提示词里写一句:"重试 call 时,传和第一次相同的 idempotency_key。"
其他办法
自己在代码里按请求内容做去重缓存,可以避免同一个进程里重复处理,但如果第一次的响应根本没收到,它挡不住第二次扣费。有些接口完全不支持幂等,用那类接口时,超时设长一点,花钱的调用不要自动重试。请求格式和请求头说明见请求与响应。
更新于