【手搓 Agent 第2.2关】搭建 Agent 进阶能力:网络搜索工具(下)
承接中篇的智能纠错与熔断机制,我们已经让 Agent 拥有了工具故障自主处理能力,但原生手写爬虫的不稳定性,始终无法支撑真实业务落地。想要实现稳定、高效、可持续的联网检索能力,必须替换专业的生产级解决方案。
本篇我们彻底重构搜索工具,替换秘塔AI商用搜索API,实现标准化、高可用的联网检索能力,解决原生爬虫反爬、解析失败、数据杂乱的痛点。同时针对大模型“迭代式检索成瘾、无限调用工具”的行业通病,新增提示词软约束+次数硬熔断双机制,精准管控工具调用频次,兼顾检索效果与资源成本,完成工业级搜索工具最终落地。
一、生产改造:商用搜索 API
在理解了如何处理爬虫返回的异常后,我们可以使用生产级搜索 API 代替我们原本的爬虫脚本,返回更加稳定的数据。
1. 申请搜索 API
可使用秘塔 AI 搜索 (Metaso),博查搜索 (BoschSearch),Claw Search 等,均有免费额度。本文推荐秘塔 AI 搜索。
可在 秘塔AI搜索 申请 API Key,拿到后储存。

2. 新增环境变量(.env 文件)
# 原有变量保持不变,新增国内搜索API配置
METASO_API_KEY=your_metaso_api_key # 替换为你的秘塔API密钥(mk-开头)
3. 修改 web_search 函数
def web_search(query: str) -> str:
print(f"\n[执行工具] 正在秘塔AI搜索:{query}")
api_key = os.getenv("METASO_API_KEY")
if not api_key:
raise ValueError("请在.env文件中配置METASO_API_KEY(秘塔API密钥)")
try:
url = "https://metaso.cn/api/v1/search"
headers = {
"Authorization": f"Bearer {api_key}",
"Accept": "application/json",
"Content-Type": "application/json"
}
payload = json.dumps({
"q": query,
"scope": "webpage",
"includeSummary": False,
"size": "5",
"includeRawContent": False,
"conciseSnippet": False
})
response = requests.post(url, headers=headers, data=payload, timeout=15)
response.raise_for_status()
results = response.json()
records = results.get("data", results.get("results", results.get("searchResults", [])))
if not records or not isinstance(records, list):
return f"【秘塔AI搜索原始数据】\n{json.dumps(results, ensure_ascii=False)[:2500]}"
formatted = f"【秘塔AI搜索结果】关键词:{query}\n"
for i, item in enumerate(records[:5], 1):
formatted += f"### {i}. {item.get('title', '无标题')}\n"
snippet = item.get('snippet', item.get('abstract', item.get('content', '无摘要')))
formatted += f"摘要:{snippet}\n"
formatted += f"🔗 {item.get('url', '无链接')}\n"
return formatted[:3000]
except requests.exceptions.SSLError as e:
raise Exception(f"SSL连接失败:{str(e)}。建议:1.更新依赖库 2.切换网络 3.临时关闭代理")
except requests.exceptions.RequestException as e:
raise Exception(f"网络请求失败:{str(e)}")
except json.JSONDecodeError:
raise Exception("秘塔API返回格式错误,无法解析JSON")
except Exception as e:
raise Exception(f"秘塔搜索调用失败:{str(e)}")
结果:
- LLM 听到问题
- → LLM 调用本地知识库,发现无法完全解答
- → LLM 调用
web_search工具进行搜索 - → LLM 认为搜索的结果不够回答问题,更换关键词继续调用
web_search进行多次搜索 - → LLM 给出最终回答。
4. 迭代式检索问题
LLM 调用 web_search 工具后总觉得自己搜索到的信息不够完美,因此反复调用,即“迭代式检索(Iterative Retrieval)”。这样迭代未免太烧 token,因此,我们需要给大模型套上缰绳,让它适可而止。
软约束:修改系统提示词
之前的 Prompt 虽然告诉了它怎么用工具,但没有告诉它什么时候该停手。我们要给它下达明确的“见好就收”指令。
示例代码:
system_prompt = {
"role": "system",
"content": """你是一个严谨的资料研究助手。你有两个强大的工具:
1. query_knowledge_base:用于查询企业内部标准、本地文件。
2. web_search:用于查询最新的互联网公开信息。
执行原则:
1. 请自主判断用户的提问,灵活调用工具。
2. 【核心红线】:网络搜索的摘要可能是不完美的。当你经过 1 到 2 次的 web_search 后,即使信息不完全充分,你也必须**立刻停止调用搜索工具**!强制使用目前已收集到的知识进行逻辑推理和总结。绝对禁止为了追求完美答案而无休止地反复搜索。
3. 如果调用报错被熔断,也请立刻放弃,直接作答。
4. 回答末尾务必标注信息来源。"""
}
硬约束:动态没收工具
可以加一个针对“搜索成瘾”的“软熔断”。如果它搜索次数太多,我们就在代码里把它强行没收。
先在主循环外(MAX_STEPS 下)初始化一个专属计数器:total_web_searches = 0,来记录全局累计的网络搜索次数。
示例代码(写在 is_tool_allowed 前):
# 【核心防沉迷逻辑】:精准统计 web_search 次数
if func_name == "web_search":
total_web_searches += 1
# 如果累计搜索达到 2 次,立刻触发软熔断
if total_web_searches > 2:
current_tools = [t for t in current_tools if t['function']['name'] != 'web_search']
print(f"⚠️ [系统干预] 已累计搜索 {total_web_searches} 次!为防过度消耗,已没收 web_search 权限。")
这样,当系统多次调用 web_search 时,就会收到回复:
[系统干预] 已累计搜索 2 次!为防过度消耗,已没收 web_search 权限。
二、本篇总结 & 下期预告
至此,Stage 2 工具模块之网络搜索工具完整完结。我们从原生爬虫搭建、智能纠错熔断,到商用API生产落地,层层递进实现了 Agent 稳定、智能、可控的联网检索能力,补齐了智能体获取外部实时信息的核心短板。
网络搜索工具只是 Stage 2 工具生态的开篇,后续我们将继续拓展更多场景化自定义工具,丰富 Agent 的实操能力,让智能体可适配更多复杂业务场景,持续完善企业级Agent工具体系。

浙公网安备 33010602011771号