大模型本身是个封闭的知识库——训练数据截止后发生的事,它一概不知。想让 Agent 处理"今天的热点""最新的库版本""竞品最新定价"这类任务,必须给它接上搜索引擎。Strands Agents SDK 最近加入了 Exa 搜索集成,两行配置就能让 Agent 拿到实时网页数据,本文走一遍完整搭建流程和实际用法。
为什么选 Exa 而不是随便一个搜索 API
普通搜索 API(比如某大厂的自建搜索)返回的是十条蓝链接和摘要片段,Agent 拿到后还得自己爬页面、提取正文——多一步就多一个失败点。Exa 的设计思路不同:它直接返回结构化的高质量内容,语义搜索能力强,对"找一篇关于 X 的深度技术文章"这类意图型查询效果明显好于关键词匹配。对 Agent 来说,少一步后处理就意味着更少的 token 消耗和更低的出错率。
两件核心武器:search 与 retrieve
Strands 的 Exa 集成暴露两个工具给 Agent 调用:
| 工具 | 作用 | 典型调用时机 |
|---|---|---|
search |
语义搜索网页,返回匹配结果的标题、URL 和摘要 | Agent 需要发现信息源,比如"最近有哪些 Rust 异步框架的评测文章" |
retrieve |
给定 URL 列表,拉取页面正文内容(清洗后的文本) | Agent 已经知道要读哪几页,需要拿到完整内容做分析 |
Agent 的典型工作流:先用 search 找到相关页面,再用 retrieve 深读其中几篇,最后综合输出答案。两步分离的好处是——搜索结果可能很多,Agent 可以只选最相关的几篇去深读,避免无差别拉取浪费 token。
最小可运行示例
下面是一个完整可运行的 Strands Agent,配备 Exa 搜索能力,执行一个多步任务:查找某个技术话题的最新文章并总结要点。
先安装依赖:
pip install strands-agents exa-py
然后准备环境变量(Exa API Key 在 exa.ai 注册后获取):
export EXA_API_KEY="your_exa_api_key_here"
export OPENAI_API_KEY="your_openai_key_here"
# 或其他支持的模型 provider key
主程序 agent_search.py:
from strands import Agent
from strands_tools import exa
# 创建带 Exa 搜索工具的 Agent
agent = Agent(
model="gpt-4o", # 或其他支持的模型
tools=[exa], # 注册 exa 工具包(包含 search + retrieve)
system_prompt=(
"你是一个技术调研助手。"
"当用户提出需要最新信息的问题时,"
"先用 search 工具查找相关网页,"
"再用 retrieve 工具读取最相关的 2-3 篇文章内容,"
"最后基于读取的内容给出结构化总结。"
"不要凭记忆回答涉及最新动态的问题。"
),
)
# 执行一个需要实时信息的任务
result = agent(
"帮我调研 2025 年 Python async 框架的最新性能对比,"
"找出 asyncio、uvloop、anyio 的基准测试结果,"
"总结各框架的优劣势。"
)
print(result)
运行:
python agent_search.py
Agent 会自动按 system_prompt 的指引,先调 search 找到相关文章,再调 retrieve 深读,最后输出总结。整个过程你不需要手动编排工具调用顺序。
更精细的控制:手动拆解搜索与读取
如果你想在中间步骤加判断逻辑(比如只读取搜索结果中评分最高的页面),可以分开调用两个工具:
from strands import Agent
from strands_tools import exa
agent = Agent(
model="gpt-4o",
tools=[exa],
)
# 第一步:搜索
search_result = agent.tool.exa_search(
query="uvloop vs asyncio benchmark 2025",
num_results=5,
use_autoprompt=True, # 让 Exa 自动优化查询语义
type="auto", # 自动选择 keyword/neural 模式
)
# 从搜索结果中挑选最相关的 URL
urls_to_read = [
r["url"] for r in search_result["results"][:3]
]
# 第二步:只读选中的几篇
content_result = agent.tool.exa_retrieve(
urls=urls_to_read,
max_content_length=2000, # 每篇最多 2000 字,控制 token
)
# 第三步:让 Agent 基于内容做分析
analysis = agent(
f"基于以下内容,对比 uvloop 和 asyncio 的性能差异:\n\n"
f"{content_result}"
)
print(analysis)
这种方式适合需要人工审核搜索结果再决定是否深读的场景,也方便你控制 token 预算。
实际场景中的用法
几个真实任务中 Agent + 搜索组合的典型模式:
竞品监控——每周让 Agent 搜索竞品官网和新闻,retrieve 产品页,提取定价和功能变更,生成 diff 报告。
技术选型调研——Agent 搜索某类库的评测与 issue 讨论,retrieve GitHub issue 和博客,输出"推荐/不推荐"及理由。
实时问答——用户问"今天 XX 发布了什么",Agent 先搜索新闻源,retrieve 原文,再回答,避免幻觉。
关键原则:凡是涉及"最新""最近""今天"的时间敏感信息,都应该走搜索路径,而不是让模型凭训练数据猜测。
采纳建议与注意事项
| 维度 | 建议 |
|---|---|
| 成本控制 | retrieve 的 max_content_length 参数务必设置上限,一篇长文可能消耗数千 token;先 search 筛选,再 retrieve 精读 |
| 搜索质量 | Exa 的 use_autoprompt=True 能自动把自然语言查询转为语义最优的搜索词,建议默认开启 |
| 结果可信度 | Agent 拿到网页内容后仍可能误读或过度概括;对关键决策,建议让 Agent 输出原文 URL 供人工复核 |
| 延迟 | search + retrieve 两步网络调用,总延迟通常在 2-5 秒;对实时对话场景,可考虑只调 search 用摘要回答,省掉 retrieve |
| 隐私 | 搜索查询会发送到 Exa 服务;内部敏感话题(非公开信息)不适合走外部搜索,应走内部知识库 |
一个简单的自检清单:
- ✅ 涉及时间敏感信息 → 必须走搜索
- ✅ 需要完整原文做分析 → search + retrieve
- ✅ 只需要快速概览 → search 即可,跳过 retrieve
- ✅ 查询含敏感关键词 → 考虑内部 RAG 替代
- ✅ token 预算紧张 → 设置
max_content_length,限制 retrieve 数量
Strands + Exa 的组合把"Agent 上网"这件事从手动拼 API 调用降到了两行配置的程度。如果你的 Agent 场景里有任何"需要知道最新状态"的环节,这个集成是目前最省事的接入方式之一。