让AI Agent学会“查资料“:我搭了一套搜索引擎工具链

上个月在做一个 AI 客服项目,用户问"你们支不支持 Webhook",理论上我应该去查文档。但 LLM 一本正经地告诉我"支持",实际上根本不支持。那一刻我意识到:AI 再强,不会查资料也是白搭。于是花了一周给它装了个"外挂大脑"。

一、背景:AI Agent 为什么需要搜索工具

传统的 LLM 调用流程是死的:用户提问 -> 模型生成 -> 返回。模型的知识截止到训练数据,2025 年后的技术栈、某家 SaaS 的最新定价、某个库的最新 API------它统统不知道。

AI Agent 的核心能力就是使用工具。而最常用的工具,就是搜索引擎。

需求很简单:Agent 收到问题后,自己判断需不需要搜外网,如果需要就调搜索引擎,把结果塞进上下文再生成答案。

二、选型思考:为什么用 SerpBase 做底层搜索

选搜索 API 时我列了几条标准:

  1. 按量付费:Agent 的搜索频次不可预测,按月订阅就是浪费
  2. 延迟低:Agent 链路本身就长,搜索再慢用户就跑了
  3. 返回结构化 JSON:别让我再解析 HTML
  4. 成本可控:独立项目烧不起大钱

看了一圈,SerpBase 最匹配:$3 起步、1.4s 平均延迟、JSON 直出、100 次免费试用。关键是没有月费,用多少扣多少。

我还看了 Serper.dev,延迟确实快一点(1.2s),但最低充值 $50,对早期项目不够友好。

三、架构设计:Tool-Use 搜索链路

3.1 整体流程

复制代码
用户输入
  -> Agent 判断是否调用搜索工具 (function calling)
  -> 提取/改写搜索 query
  -> 调 SerpBase API
  -> 清洗搜索结果
  -> 拼进 system prompt
  -> LLM 生成最终回答

3.2 搜索工具定义 (OpenAI Function Calling)

我用的是 OpenAI 的 function calling 机制。先定义一个 search 工具:

python 复制代码
search_tool = {
    "type": "function",
    "function": {
        "name": "web_search",
        "description": "当问题涉及最新信息、时效性内容、或知识库中不存在的内容时,调用搜索获取实时信息",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "搜索关键词,尽量简洁准确"
                },
                "num_results": {
                    "type": "integer",
                    "description": "返回结果数量,默认5",
                    "default": 5
                }
            },
            "required": ["query"]
        }
    }
}

3.3 Agent 核心循环

python 复制代码
import json
import requests
from openai import OpenAI

class SearchAgent:
    def __init__(self, openai_key: str, serpbase_key: str):
        self.llm = OpenAI(api_key=openai_key)
        self.serpbase_key = serpbase_key
        self.messages = []
    
    def _search(self, query: str, num: int = 5) -> list:
        headers = {
            "X-API-Key": self.serpbase_key,
            "Content-Type": "application/json"
        }
        body = {"q": query, "hl": "zh-CN", "gl": "cn", "page": 1}
        resp = requests.post(
            "https://api.serpbase.dev/google/search",
            headers=headers, json=body, timeout=30
        )
        data = resp.json()
        organic = data.get("organic", [])
        return [
            {"title": item["title"], "link": item["link"], "snippet": item["snippet"]}
            for item in organic[:num]
        ]
    
    def _search_tool_call(self, query: str, num: int) -> str:
        results = self._search(query, num)
        lines = []
        for i, r in enumerate(results, 1):
            lines.append(f"[{i}] {r['title']}\n   {r['snippet']}\n   来源: {r['link']}")
        return "\n\n".join(lines)
    
    def run(self, user_input: str) -> str:
        tools = [search_tool]
        
        response = self.llm.chat.completions.create(
            model="gpt-5",
            messages=[
                {"role": "system", "content": "你是一个智能助手,需要时可以使用搜索工具获取实时信息。"},
                {"role": "user", "content": user_input}
            ],
            tools=tools,
            tool_choice="auto"
        )
        
        msg = response.choices[0].message
        
        if msg.tool_calls:
            for tc in msg.tool_calls:
                if tc.function.name == "web_search":
                    args = json.loads(tc.function.arguments)
                    search_result = self._search_tool_call(
                        args["query"], args.get("num_results", 5)
                    )
                    
                    final_response = self.llm.chat.completions.create(
                        model="gpt-5",
                        messages=[
                            {"role": "system", "content": "基于以下搜索结果回答用户问题,注明信息来源。"},
                            {"role": "user", "content": user_input},
                            {"role": "assistant", "content": f"搜索结果如下:\n{search_result}"}
                        ]
                    )
                    return final_response.choices[0].message.content
        
        return msg.content

# 使用
agent = SearchAgent(openai_key="sk-xxx", serpbase_key="YOUR_KEY")
print(agent.run("2026年最流行的Python Web框架是什么?"))

四、实际效果和坑

4.1 效果对比

同一个问题"Next.js 15 有什么新特性",不加搜索 vs 加搜索:

维度 不搜索 加搜索
回答准确性 混用14/15特性 对应15的具体更新
API 签名正确率 ~60% ~95%
幻觉率 经常胡编 极少
响应时间 ~1s ~3s(含搜索)

4.2 踩坑记录

坑1:Agent 过度调用搜索

一开始没有控制,Agent 有时候连"你好"都去搜一下。解决方案是加一个前置判断:只有问题涉及时效性信息时才触发搜索工具。我在 system prompt 里加了一段话:

复制代码
仅当问题涉及以下内容时调用搜索:
- 具体的版本号、发布日期
- 当前的新闻、事件、趋势
- 你不知道的事实类信息
- 用户明确要求"搜索一下"

坑2:搜索结果太长塞爆上下文

5条结果的 snippet 加起来大约 800-1000 token,再加 URL 和标题,大概 1200 token。如果 Agent 连续调用多次搜索,上下文很快就爆了。

解决方案:每次搜索后 truncate 摘要到 100 字符以内,只保留最关键的部分。

python 复制代码
def truncate_snippet(text: str, max_len: int = 100) -> str:
    if len(text) <= max_len:
        return text
    return text[:max_len] + "..."

坑3:并发调用时 QPS 限制

如果 Agent 同时发起多个搜索请求(比如并行搜不同关键词),可能触发限流。我在客户端做了排队:

python 复制代码
import time
from threading import Lock

class RateLimiter:
    def __init__(self, qps: int = 2):
        self.qps = qps
        self.lock = Lock()
        self.last_call = 0
    
    def wait(self):
        with self.lock:
            elapsed = time.time() - self.last_call
            min_interval = 1.0 / self.qps
            if elapsed < min_interval:
                time.sleep(min_interval - elapsed)
            self.last_call = time.time()

五、成本统计

跑了半个月,日均调用 150-300 次搜索:

复制代码
日均搜索量: ~200 次
月搜索量: ~6000 次
SerpBase 费用: $3 Starter Boost(1万次)完全够用
OpenAI API 费用: ~$15(含搜索场景的 token 消耗)
总计月成本: ~$18

对于一个独立的 AI Agent 项目来说,这个成本结构非常健康。如果量再大 10 倍,SerpBase 上 $10 的 Starter 包(2万次),成本也就多个几刀。

六、总结

给 AI Agent 加搜索工具这件事,技术门槛其实不高。核心就是三步:

  1. 定义好 function calling 的 schema
  2. 接上搜索 API(SerpBase 这类按量付费的就很合适)
  3. 控制好触发条件和上下文长度

难点不在代码,而在怎么让 Agent 知道什么时候该搜、搜到什么程度。这个需要根据你的业务场景反复调 prompt。

如果你也在做 AI Agent 项目,建议先拿 100 次免费额度跑通链路,再逐步优化触发逻辑。搜索这个工具,用好了能让你的 Agent 水平上一个台阶。


下一步我准备给它加上记忆功能------搜索过的问题和结果缓存起来,同领域问题直接走缓存,进一步降本提速。

相关推荐
liguojun20252 小时前
软硬一体智慧场馆系统推荐——助力场馆数字化高效升级
java·大数据·人工智能·物联网·1024程序员节
阿里云大数据AI技术2 小时前
从图片到声音、视频:MaxCompute MaxFrame 多模态算子模块,让海量多模态数据_跑_起来
大数据·人工智能·阿里云·多模态·maxcompute
云道轩2 小时前
Langflow 1.9 发布:Langflow 助手、流程 DevOps 工具包,以及面向 IDE 和编码代理的 MCP 支持
人工智能·智能体
l1t2 小时前
DeepSeek总结的DuckDB CLAUDE.md
数据库·人工智能
烤麻辣烫2 小时前
计算机思维--经典互联网应用
开发语言·学习·搜索引擎·数据库开发
星纬智联技术2 小时前
内容监控怎么做才能持续提升AI搜索引擎引用率?从引用偏好识别到数据驱动的优化闭环
人工智能·aigc·geo
工业机器人销售服务2 小时前
铸件去毛刺,伯朗特机器人带气动打磨头,恒力去除浇口残余
运维·服务器·人工智能
恋猫de小郭2 小时前
2026 Google I/O ,意料之外的 Antigravity 2.0 和消失的 Gemini CLI
前端·人工智能·ai编程
fuquxiaoguang2 小时前
从“能跑起来”到“能跑稳”:Google Genkit如何用中间件思维改写AI工程化规则
人工智能·中间件·genkit