上个月在做一个 AI 客服项目,用户问"你们支不支持 Webhook",理论上我应该去查文档。但 LLM 一本正经地告诉我"支持",实际上根本不支持。那一刻我意识到:AI 再强,不会查资料也是白搭。于是花了一周给它装了个"外挂大脑"。
一、背景:AI Agent 为什么需要搜索工具
传统的 LLM 调用流程是死的:用户提问 -> 模型生成 -> 返回。模型的知识截止到训练数据,2025 年后的技术栈、某家 SaaS 的最新定价、某个库的最新 API------它统统不知道。
AI Agent 的核心能力就是使用工具。而最常用的工具,就是搜索引擎。
需求很简单:Agent 收到问题后,自己判断需不需要搜外网,如果需要就调搜索引擎,把结果塞进上下文再生成答案。
二、选型思考:为什么用 SerpBase 做底层搜索
选搜索 API 时我列了几条标准:
- 按量付费:Agent 的搜索频次不可预测,按月订阅就是浪费
- 延迟低:Agent 链路本身就长,搜索再慢用户就跑了
- 返回结构化 JSON:别让我再解析 HTML
- 成本可控:独立项目烧不起大钱
看了一圈,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 加搜索工具这件事,技术门槛其实不高。核心就是三步:
- 定义好 function calling 的 schema
- 接上搜索 API(SerpBase 这类按量付费的就很合适)
- 控制好触发条件和上下文长度
难点不在代码,而在怎么让 Agent 知道什么时候该搜、搜到什么程度。这个需要根据你的业务场景反复调 prompt。
如果你也在做 AI Agent 项目,建议先拿 100 次免费额度跑通链路,再逐步优化触发逻辑。搜索这个工具,用好了能让你的 Agent 水平上一个台阶。
下一步我准备给它加上记忆功能------搜索过的问题和结果缓存起来,同领域问题直接走缓存,进一步降本提速。