去年我给一个问答机器人接实时搜索,当时觉得"不就是调个 API 嘛",结果上线第一个月被连续打脸。这篇文章把我踩过的坑记下来,给准备做同样事的人提个醒。

先说结论:给 AI Agent 接实时搜索,难点从来不在"怎么调通",而在调通之后那一堆你没想过的边角情况。下面 5 个坑,按遇到顺序排。

坑一:HTTP 200 不代表成功

这是我掉的第一个坑,也是最隐蔽的。

有一次 Agent 返回了空答案,我查日志发现接口返回的是 HTTP 200,看起来一切正常。后来才知道,服务端是把"被限流了""暂时没数据"这种情况也包在 200 里返回的——只是响应体里的 status 字段不是 0。

也就是说,只看 HTTP 状态码是分不清"真成功"和"假成功"的。我给所有调用加了一层检查:

def parse_response(resp):
    data = resp.json()
    # 业务状态码,不是 HTTP 状态码
    if data.get("status") != 0:
        raise Exception(f"search failed, status={data.get('status')}")
    return data

如果你也遇到"偶尔拿到空结果、但又没报错",先查这个。

坑二:Agent 会把一次查询烧出 80 次调用

这是最肉疼的一个坑。

我的 Agent 支持多轮对话,用户在长对话里反复问同一个问题。结果我发现,同一个关键词,Agent 因为措辞不同、翻页不同,在一个对话里调了 80 多次搜索,而且几乎拿到的都是同一个 SERP。

排查完加了三层防护:

  1. query 归一化:把空格、大小写、全半角统一,"上海 装修""上海装修" 走同一个 key。
  2. 结果缓存:同 query 5 分钟内直接返回缓存,不重新调。
  3. 对话内去重:同一轮对话里,同一个 query 只调一次。

这三层加完,单次对话的搜索调用从 80 次降到了个位数。成本也是这么下来的。

坑三:失败请求在悄悄扣钱

我在用老供应商的时候,发现了一个让我很不舒服的事:请求超时或者失败了,积分照样扣。我一个月有 3% 左右的请求会因为网络问题失败,这部分钱全白花了。

后来换的时候,我专门测了这一点——失败请求到底扣不扣。我现在的供应商(SerpBase)是失败自动退积分的,超时的请求不会计入 credits_charged。这个点不写在价格表上,得自己实测才知道。

给个建议:换供应商前,专门跑一轮"故意失败"的测试,看失败请求会不会扣费。对会重试的 Agent 来说,这个差异直接决定月成本。

坑四:返回的字段名跟你想的不一样

每家 SERP API 的字段命名都不一样。有的叫 position,有的叫 rank,有的叫 order。我一开始解析器写死了一个字段名,结果换了一家直接崩。

现在我的做法是:解析层兼容主字段和别名。

def extract(item):
    return {
        # rank 是主字段,position 是别名,两个都兼容
        "rank": item.get("rank", item.get("position", 0)),
        "title": item.get("title", ""),
        "link": item.get("link", item.get("url", "")),
    }

SerpBase 这个点做得比较省心,它的 organic 结果同时返回 rankposition 别名、linkurl 别名,我用哪套写法都能解析。但别指望所有供应商都这样,解析层做好兼容永远是稳妥的。

坑五:上下文塞太满,Token 烧得快

Agent 拿到搜索 JSON 后,经常是整包往 LLM 上下文里塞。SERP 返回的 JSON 很大——一条 organic 结果可能带 title、snippet、link、url、display_url、sitelinks、icon 一堆字段。5 条结果加起来,可能吃掉几千 token。

我现在的处理是喂给 LLM 前先裁剪,只留标题、摘要、链接三样:

def slim_results(organic, limit=5):
    out = []
    for item in organic[:limit]:
        out.append({
            "title": item.get("title", ""),
            "snippet": item.get("snippet", ""),
            "link": item.get("link", item.get("url", "")),
        })
    return out

snippet 太长的再截到 80 个字符。这样一次 grounding 的 token 从几千降到几百,LLM 成本跟着降了一大截,而且答案质量没下降——LLM 本来也不需要看 sitelinks 和 icon 这些字段。

为什么我会选现在这套

说回开头那句话,“不就是调个 API 嘛”——真正做了才发现,选哪个 API 决定你后面要填多少坑。我最后用的是 SerpBase,几个原因都比较实际:

  • 6 个端点一个 key:Search、Images、News、Videos、Maps Search、Maps Detail 全在一个 API 里。我既有搜索需求又有本地(牙科、装修)需求,不用为 Maps 再单独接一家。
  • 失败退积分:就是坑三说的,超时请求自动退,不会白花钱。
  • 预付费积分不过期:我买的积分放着不会作废,Agent 这种不规律、时多时少的用量,不会被月付套餐卡脖子。
  • 有 MCP server 和 Agent Skill:要接 Claude / Cursor / Codex 这类,配置一下就能让 Agent 直接调搜索,省了写胶水代码。

它也不是没有短板。功能上偏精简,没有 safe 过滤、排除站点这类高级参数,学术和购物端点也还没有,企业级 SSO 在路线图上。如果你的需求里这些是硬需求,那它不适合你。

最后

给 AI Agent 接实时搜索,真正花时间的是这些边角:业务状态码、缓存、失败计费、字段兼容、上下文裁剪。把这五件事想清楚,选哪个 API 都是顺的;没想清楚,再好的 API 也会被用成灾难。

我也不是非要安利谁,只是把我踩过的坑和最终的选择写出来。如果你也在这条路上,希望这些能让你少走一点弯路。

参考资料

  • SerpBase 官网:serpbase.dev
  • SerpBase API 文档:serpbase.dev/docs
  • 相关服务官方文档:SerpApi、Serper.dev、DataForSEO

以上链接仅作资料索引。文中结论基于我个人的使用经验,不一定适合所有人的场景,落地前请结合自己的需求判断。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐