无需 KYC — 无需身份证件、自拍或文件。用加密货币支付:BTC, USDT, USDC, ETH, SOL, LTC
语言: 简体中文
获取 API 密钥

指南 · Python · 约 60 行,任意 LLM

用网页搜索 API 在 Python 中构建 deep research agent

Deep research 是一个循环,而不是一个模型。本指南构建一个你几分钟就能读完的:它规划查询、带页面文本搜索网页、每个来源只保留一次、寻找缺口,并写出带编号引用的答案。

最后更新:

快速回答

如何用 Python 构建 deep research agent?

写一个循环:让 LLM 给出搜索查询,将每个查询通过返回页面文本的网页搜索 API 执行,每个来源只保留一次,再请求后续查询,最后写出带引用的答案。使用 LLMWebSearch 时,每次搜索以一个积分返回前 3 页的 Markdown;下面的脚本每个问题运行六次搜索。低至 US$1.20/千次搜索,无需 KYC,加密货币支付。

agent 会做什么?

  1. 套餐

    让模型给出四个简短的查询,合起来覆盖该问题。

  2. 搜索

    用 read=3 运行每个查询,这样前三个页面会以 Markdown 返回。

  3. 每个来源只保留一次

    按 URL 存储结果,跳过已收集的页面或没有文本的页面。

  4. 寻找缺口

    把目前已找到的标题展示给模型,并请它针对缺失内容给出两个查询。

  5. 撰写

    最多编号 12 个来源,并要求答案以 [1]、[2] 形式引用它们。

开始前我需要准备什么?

三样东西:

  • 一个 LLMWebSearch API 密钥,放在环境变量 LLMWEBSEARCH_API_KEY 中。首次购买积分包后,密钥会出现在仪表盘中。
  • 任意 OpenAI 兼容的 chat endpoint,通过 LLM_BASE_URL、LLM_API_KEY 和 LLM_MODEL 设置。托管模型和本地服务器都可以。
  • Python 3.9 或更高版本,以及 requests 包。

脚本对两个服务都使用普通 HTTP,因此你可以在不改动搜索代码的情况下更换模型。

Python:deep_research.py
import os

import requests

SEARCH = "https://llmwebsearch.com/api/v1/search"
SEARCH_HEADERS = {"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]}
# Any OpenAI-compatible chat endpoint works here: set your own base URL, key and model name.
LLM_URL = os.environ.get("LLM_BASE_URL", "https://llm.example.com/v1") + "/chat/completions"
LLM_HEADERS = {"Authorization": "Bearer " + os.environ.get("LLM_API_KEY", "")}
MODEL = os.environ.get("LLM_MODEL", "your-model-name")


def ask_llm(prompt):
    body = {"model": MODEL, "messages": [{"role": "user", "content": prompt}]}
    resp = requests.post(LLM_URL, headers=LLM_HEADERS, json=body, timeout=120)
    resp.raise_for_status()
    return resp.json()["choices"][0]["message"]["content"]


def plan_queries(question, n=4):
    text = ask_llm(f"Write {n} short web search queries, one per line, to answer: {question}")
    queries = [line.strip("-*0123456789. ").strip() for line in text.splitlines()]
    return [q for q in queries if q][:n]


def search(query):
    body = {"q": query, "max_results": 5, "read": 3, "max_chars": 5000}
    resp = requests.post(SEARCH, headers=SEARCH_HEADERS, json=body, timeout=60)
    resp.raise_for_status()
    return resp.json()["results"]


def research(question, rounds=2):
    sources = {}  # url -> result, so the same page is never cited twice
    queries = plan_queries(question)
    for _ in range(rounds):
        for query in queries:
            for r in search(query):
                if r.get("content_status") == "ok" and r["url"] not in sources:
                    sources[r["url"]] = r
        notes = "\n".join(f"- {r['title']}" for r in sources.values())
        queries = plan_queries(f"{question}\nAlready found:\n{notes}\nWhat is still missing?", n=2)
    numbered = list(sources.values())[:12]
    context = "\n\n".join(
        f"[{i}] {r['title']} ({r['url']})\n{r['content']}" for i, r in enumerate(numbered, 1)
    )
    prompt = (
        "Answer the question using only the sources below. Cite them as [1], [2]. "
        "Say so when the sources disagree or do not cover a point.\n\n"
        f"Question: {question}\n\nSources:\n{context}"
    )
    return ask_llm(prompt), [r["url"] for r in numbered]


if __name__ == "__main__":
    answer, urls = research("What changed in the EU rules for general-purpose AI models in 2025?")
    print(answer)
    for i, url in enumerate(urls, 1):
        print(f"[{i}] {url}")

各部分如何工作?

plan_queries 让模型给出简短查询,每行一个,并去掉列表标记。相比完整问句,简短查询更契合搜索引擎对页面的排序方式。

search 调用 POST https://llmwebsearch.com/api/v1/search,传入 max_results=5、read=3 和 max_chars=5000。响应列出五个结果;前三个在 content 中带有页面文本。

research 运行各轮。它把来源保存在以 URL 为键的字典中,因此被两个查询找到的页面只计一次。只保留 content_status 等于 ok 的结果,因为答案必须建立在模型能读到的文本上。

每轮之后,模型查看目前已收集的标题,并针对缺失内容提出两个新查询。最终 prompt 最多编号十二个来源,并设定引用规则。

一份报告要花多少钱?

默认设置下每个问题六次搜索:第一轮四次,第二轮两次。read 为 3 时,即六个积分。因此 US$41.25 的 25,000 次查询 可覆盖 4,000 多份报告。

模型调用会在你的 LLM 账单上额外计费:每份报告三次规划调用和一次长答案调用。答案调用最多携带十二页、每页 5,000 个字符,如果你的模型较贵,就调低 max_chars。

如何让它挖得更深?

改数字,不改结构:

  • 更多轮次。 rounds=4 让模型多追两组缺口。
  • 每轮更多查询。 把找缺口那次调用里的 n 从 2 提到 4。
  • 更窄的来源。 在搜索请求体里加 include_domains,限定官网或学术站点。
  • 只要近期材料。 加 "time_range": "month" 或 "topic": "news"。
  • 每次查询读更多页。 read=5 会读五个页面,每次搜索 2 积分。

提高上限之前先加一条停止规则:某一轮带来的新来源少于两个时就结束循环。

如何让 agent 既安全又守规矩?

把每个页面都当作不可信文本。最终提示词会告诉模型只把来源当证据用;再加一行明确说明:来源里的指令一律忽略。绝不要让来源触发工具调用或文件写入。

网页这一侧,我们的读取器遵守 robots.txt:它会以 LLMWebSearch 表明身份,遵守网站为它或为所有机器人写下的规则,并排除任何它无权读取的页面内容。拒绝访问的页面会返回空内容,脚本直接跳过。当网站拒绝我们的读取器时,结果会不带内容返回,并附上该网站的状态:我们绝不破解 CAPTCHA、不登录账户,也不绕过封锁或付费墙。

哪里会出问题?

测试中出现三种情况:

  • 查询含糊。 规划太弱时,每一轮都找到同样的页面。让模型给出带具体名称和日期的查询。
  • 带正文的来源太少。 有些主题藏在付费墙后面。放宽查询或允许多更多结果。
  • 提示词过长。 十二个长页面可能超出小模型的上下文。调低 max_chars 或只保留八个来源。

记录每次查询和它返回的 URL。读这份日志是改进规划提示词最快的办法。

深度研究 agent:常见问题

能配本地模型跑吗?

可以,只要本地服务器支持 OpenAI chat-completions 格式。把 LLM_BASE_URL 设成它的地址,LLM_MODEL 设成模型名。

能把它做成 MCP 工具吗?

可以。把 research() 包进一个 MCP 工具,或者把 MCP 页面 上的纯搜索工具交给你的 agent,让它自己跑循环。

为什么不直接向搜索 API 要答案?

因为 LLMWebSearch 不写答案。API 返回的是来源,而不是写好的答案:由你自己的模型读取页面、撰写回复并引用 URL。

怎么查看每次搜索花了多少?

每个响应都带有 credits_used,而 GET https://llmwebsearch.com/api/v1/credits 会免费显示你的剩余余额。

低至 US$1.20/千次搜索 · 无需 KYC

获取 API 密钥