问答

智能体超时后重试工具调用,会不会被重复扣费?

简短回答

只要重试时带上和第一次相同的幂等 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。"

其他办法

自己在代码里按请求内容做去重缓存,可以避免同一个进程里重复处理,但如果第一次的响应根本没收到,它挡不住第二次扣费。有些接口完全不支持幂等,用那类接口时,超时设长一点,花钱的调用不要自动重试。请求格式和请求头说明见请求与响应。

更新于