OA0
OA0 是一个探索 AI 的社区
现在注册
已注册用户请  登录
OA0  ›  代码  ›  exaPy — 面向 AI 应用的 Exa 搜索 Python SDK

exaPy — 面向 AI 应用的 Exa 搜索 Python SDK

 
  forge ·  2026-10-23 11:00:19 · 35 次点击  · 0 条评论  

Exa Python SDK

PyPI version

Exa 的官方 Python SDK。Exa 是面向 AI 的网页搜索 API。

文档 | 控制台

安装

pip install exa-py

需要 Python 3.9+

快速开始

from exa_py import Exa

exa = Exa(api_key="your-api-key")

# 搜索网页
results = exa.search(
    "blog post about artificial intelligence",
    type="auto",
    contents={"highlights": True}
)

# 提问
response = exa.answer("What is the capital of France?")

搜索

results = exa.search(
    "machine learning startups",
    contents={"highlights": True}
)
results = exa.search(
    "climate tech news",
    num_results=20,
    start_published_date="2024-01-01",
    include_domains=["techcrunch.com", "wired.com"],
    contents={"highlights": True}
)
results = exa.search(
    "What are the latest battery breakthroughs?",
    type="auto",
    system_prompt="Prefer official sources and avoid duplicate results",
    output_schema={
        "type": "object",
        "properties": {
            "summary": {"type": "string"},
            "key_companies": {"type": "array", "items": {"type": "string"}},
        },
        "required": ["summary", "key_companies"],
    },
)
print(results.output.content if results.output else None)
for chunk in exa.stream_search(
    "What are the latest battery breakthroughs?",
    type="auto",
):
    if chunk.content:
        print(chunk.content, end="", flush=True)

搜索的 output_schema 模式:
- {"type": "text", "description": "..."}:在 output.content 中返回纯文本
- {"type": "object", ...}:在 output.content 中返回结构化 JSON

所有搜索类型都支持 system_prompt 和 output_schema。
搜索流式输出通过 stream_search(...) 提供,它会逐条产出 OpenAI 风格的 chat completion 分块。

对于 type: "object",搜索目前有以下限制:
- 最大嵌套深度:2
- 最大属性总数:10

支持 additional_queries 的深度搜索变体:
- deep-lite
- deep
- deep-reasoning

原始请求体字段

使用 extra_body 可按调用覆盖搜索请求体。其他方法也接受该参数,详见请求选项。

result = exa.search("AI research", extra_body={"futureField": True})

内容获取(Contents)

results = exa.get_contents(
    ["https://exa.ai/docs"],
    text=True
)
results = exa.get_contents(
    ["https://arxiv.org/abs/2303.08774"],
    highlights=True
)

问答(Answer)

response = exa.answer("What caused the 2008 financial crisis?")
print(response.answer)
for chunk in exa.stream_answer("Explain quantum computing"):
    print(chunk, end="", flush=True)

网页搜索与内容获取工具

可以在 OpenAI 或 Anthropic 的调用循环中将 Exa 作为 web_search 工具使用。无参调用 web_search() 可获得 Exa 为智能体搜索推荐的设置(type="auto" 和 contents={"highlights": True})。

from exa_py import Exa
from openai import OpenAI

exa = Exa()
openai_client = OpenAI()

messages = [{"role": "user", "content": "What's the latest on AI chips?"}]

completion = openai_client.chat.completions.create(
    model="gpt-5.6",
    messages=messages,
    tools=[exa.openai.web_search()],
)

message = completion.choices[0].message
messages.append(message)
messages += exa.openai.handle_tool_calls(message)
import anthropic

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=messages,
    tools=[exa.anthropic.web_search()],
)

传入 name(以及可选的 description)可重命名工具。Anthropic 要求工具名唯一,因此自定义名称可以让 Exa 工具与 Anthropic 内置的 web_search_20250305 工具共存:

response = client.messages.create(
    model="claude-sonnet-4-5",
    max_tokens=1024,
    messages=messages,
    tools=[
        exa.anthropic.web_search(name="exa_web_search"),
        {"type": "web_search_20250305", "name": "web_search", "max_uses": 5},
    ],
)

get_contents 在同一命名空间中同样可用,可让模型读取它已拥有 URL 的页面。它接收一个 URL 列表,并接受 Exa.get_contents 的所有选项:

completion = openai_client.chat.completions.create(
    model="gpt-5.6",
    messages=messages,
    tools=[
        exa.openai.web_search(),
        exa.openai.get_contents(summary=True, livecrawl="preferred"),
    ],
)

对于 OpenAI Responses API,请使用 exa.openai.responses.web_search() 和相同的 handle_tool_calls 辅助函数。这些处理器会应答每一个工具调用:无法解析工具名的调用会得到 Error: unknown tool "<name>" 输出,而不会被丢弃,从而保证后续请求有效。如果你同时使用 Exa 之外的其他工具,请在下次请求前将这些错误输出替换为你自己的结果。

Agent API

Agent API 无需 beta 请求头即可使用。

run = exa.agent.runs.create(
    query="Find engineering leaders at AI infrastructure companies that raised a Series A or B in the last 6 months.",
    output_schema={
        "type": "object",
        "properties": {
            "people": {
                "type": "array",
                "maxItems": 10,
                "items": {
                    "type": "object",
                    "properties": {
                        "name": {"type": "string"},
                        "contact_email": {"type": "string", "format": "email"},
                        "linkedin_url": {"type": "string", "format": "uri"},
                    },
                    "required": ["name", "linkedin_url"],
                },
            }
        },
        "required": ["people"],
    },
    effort="auto",
)

run = exa.agent.runs.poll_until_finished(run.id)
print(run.output.structured if run.output else None)

如需 Agent Ultra,请设置 effort="ultra":

from exa_py import Exa

exa = Exa()
run = exa.agent.runs.create(
    query="Find all companies building browser automation tools in the United States.",
    effort="ultra",
    budget={"maxCostDollars": 10},
)

Agent 监控器(Beta)

Agent 监控器使用 beta 命名空间,需要 beta 标识 AGENT_MONITORS_BETA_HEADER(agent-monitors-2026-08-04)。

Agent 监控器会按固定周期持续刷新一张“实体 × 字段”表格。

from exa_py.agent import AGENT_MONITORS_BETA_HEADER

betas = [AGENT_MONITORS_BETA_HEADER]

# 创建监控器。创建为异步操作:返回时状态为 "creating",
# 首次刷新完成后变为 "active"。
monitor = exa.beta.agent.monitors.create(
    betas=betas,
    cadence="7d",
    entities=[
        {"name": "Acme Corp", "domain": "acme.com"},
        {"name": "Globex", "domain": "globex.com"},
    ],
    fields=[
        {"name": "funding", "description": "New funding rounds"},
        {"name": "ceo", "description": "The company's current CEO"},
    ],
    idempotency_key="my-monitor-1",  # 安全重试:相同 key 返回同一监控器
)

# 分页获取监控器当前实体及其内容。
for view in exa.beta.agent.monitors.entities.list_all(monitor.id, betas=betas):
    print(view.entity.name, view.contents)

# 跟随内容变更流(之后可从页面的 next_cursor 继续)。
changes = exa.beta.agent.monitors.changes.list(
    monitor.id,
    betas=betas,
    since="2026-01-01T00:00:00Z",
)

# 对过去某段新闻窗口做一次性回测——不会持久化监控器。
backtest = exa.beta.agent.monitors.backtests.create_and_wait(
    betas=betas,
    entities=[{"name": "Acme Corp", "domain": "acme.com"}],
    fields=[{"name": "funding", "description": "New funding rounds"}],
    start_time="2026-01-01T00:00:00Z",
    end_time="2026-01-08T00:00:00Z",
)
print(backtest.data)

# 添加实体、查看刷新进度、清理。
exa.beta.agent.monitors.entities.add(
    monitor.id,
    betas=betas,
    entities=[{"name": "Initech", "domain": "initech.com"}],
)
current = exa.beta.agent.monitors.get(monitor.id, betas=betas)
print(current.status, current.refresh, current.usage)
exa.beta.agent.monitors.delete(monitor.id, betas=betas)

异步

from exa_py import AsyncExa

exa = AsyncExa(api_key="your-api-key")

results = await exa.search("async search example", contents={"highlights": True})

请求选项

Exa 与 AsyncExa 的每个 API 方法在自身参数之外都接受三个关键字参数:

参数 作用
extra_body 合并到 JSON 请求体之上的额外字段
extra_headers 额外的请求头
extra_query 额外的查询参数
# 发送 SDK 尚未类型化的字段。
exa.get_contents("https://example.com", text=True, extra_body={"futureField": True})

exa.websets.list(
    limit=10,
    extra_query={"futureParam": "value"},
    extra_headers={"X-Request-Source": "nightly"},
)

它们会附加到方法构建的请求上。若与方法自身设置的名称冲突,以你的值为准。

  • extra_body 会随 POST 和 PATCH 请求发送。其他请求会忽略它,请改用 extra_query。
  • extra_query 会跳过值为 None 的参数,并在两个客户端中都将 True/False 发送为 true/false。
  • SDK 设置的请求头(如 User-Agent)无论大小写都会被替换。设为 None 的请求头会被跳过。
  • 是否流式响应由你调用的方法决定。stream 永远不会从 extra_body 或 extra_query 中获取,额外的 Accept 请求头既不会启动流,也不会替换流式方法的 Accept:请调用流式方法。
  • 会发起多次请求的辅助方法(list_all、get_all、poll_until_finished、create_and_wait、wait_until_idle)会对每次请求应用这些选项,因此 create_and_wait 只在创建请求中发送 extra_body,轮询时不发送。extra_query 中的 cursor 用于选择 list_all 与 get_all 的第一页,辅助方法会从此处继续。
  • CSV 导入的上传会发往预签名 URL,不接收上述三项。
  • LLM 工具辅助方法(exa.tools、exa.openai、exa.anthropic)会将创建工具时传入的选项和 timeout 应用到其发起的每个请求,例如 exa.openai.web_search(timeout=10, extra_headers={"X-Request-Source": "agent"})。run、execute 以及 handle_tool_calls / handle_tool_use 辅助方法不支持按调用传入。

超时与取消

同步与异步客户端默认使用 600 秒的网络不活动超时(而非总耗时上限)。可设置客户端级的值,也可在搜索、内容获取、问答或流式调用中覆盖;构造时使用 None 可禁用。按调用传入 None 会继承客户端设置。

from exa_py import Exa

exa = Exa(api_key="your-api-key", timeout=30)
results = exa.search("latest AI developments", timeout=5)

超时的请求会抛出 ExaTimeoutError,同步与异步客户端一致:

from exa_py import Exa, ExaTimeoutError

exa = Exa(api_key="your-api-key")
try:
    results = exa.search("latest AI developments", timeout=5)
except ExaTimeoutError:
    results = None

该错误同时也是 HTTP 库所抛异常的实例,因此为其编写的处理器仍然有效:

  • AsyncExa:一个 httpx.TimeoutException。
  • Exa:连接或等待响应开始期间为 requests.exceptions.Timeout;响应开始后停滞时为 requests.exceptions.ConnectionError(Requests 的报告方式)。

使用 AsyncExa 时,可通过取消等待该操作的 task 来取消进行中的操作:

import asyncio
from exa_py import AsyncExa

exa = AsyncExa(api_key="your-api-key")
task = asyncio.create_task(exa.search("latest AI developments"))
task.cancel()

提前停止异步搜索/问答流时,请调用 await stream.aclose() 释放 HTTP 连接;在等待下一分块时取消也会关闭连接。同步客户端无法从其他线程取消阻塞读取;超时约束的是每段网络不活动的时间。构造函数与按调用覆盖若非 None,都要求正且有限的超时值。

警告

API 可能返回关于某个请求的 warnings,例如某个被忽略的字段。撰写本文时,搜索响应(包括 stream_search)会携带它们。警告从不会改变状态码或结果。它面向构建请求的代码维护者。请修正请求,不要将警告传给模型。响应会将其保存在 warnings 中。

客户端还会通过 Python 的 warnings 模块将其作为 ExaWarning 发出,归因于发起调用的代码行:

app.py:12: ExaWarning: [exa] POST /search IGNORED_FIELD at /startCrawlDate: startCrawlDate is no longer supported and is ignored. Remove it from the request.

Python 的默认过滤器对每个调用行只显示一次给定警告。可静默它们,或将其转入日志:

import logging
import warnings

from exa_py import ExaWarning

warnings.filterwarnings("ignore", category=ExaWarning)  # 静默
logging.captureWarnings(True)  # 或发送到 "py.warnings" logger

每个 ExaWarning 都有 warning、endpoint、method 和 request_id 属性。warning 是 ApiWarning,包含其 code、path 和 message。

已弃用的选项会在请求发送前抛出 DeprecationWarning,无论你是作为选项设置还是通过 extra_body 传入。撰写本文时,包括 start_crawl_date、end_crawl_date,以及 highlights 选项 num_sentences 和 highlights_per_url。Python 默认仅对以 __main__ 运行的代码显示 DeprecationWarning。pytest 等测试运行器也会显示它们。

更多

查看完整文档了解包括 websets、过滤器及高级选项在内的所有功能。

35 次点击  ∙  0 人收藏  
登录后收藏  
0 条回复
关于 ·  帮助 ·  PING ·  隐私 ·  条款   
OA0 - Omni AI 0 一个探索 AI 的社区
沪ICP备2024103595号-2
耗时 21 ms
Developed with Cursor