# OpenAI function calling：给模型一个 search_web 工具

> 面向 OpenAI function calling 的网页搜索：一个 search_web JSON schema、Python 中的 Responses API 循环，以及 Agents SDK 中的同一工具。无需 KYC，加密货币支付。

- URL: https://llmwebsearch.com/zh/docs/openai/
- 最后更新: 2026-10-22
- 发布者: LLMWebSearch (https://llmwebsearch.com)

向模型描述一个 `search_web` 函数，针对我们的 API 执行它请求的调用，并把来源发回。模型随后给出带引用的答案。本页展示 JSON schema、Responses API 循环以及更短的 OpenAI Agents SDK 版本。

**快速回答 — 如何通过 function calling 给 OpenAI 模型加上网页搜索？**

用 JSON schema 声明一个 `search_web` 函数，当模型调用它时，把查询 POST 到 **LLMWebSearch** 的 `https://llmwebsearch.com/api/v1/search`。把带编号的来源作为 `function_call_output` 返回，其中前 3 个页面为 Markdown；模型据此作答并引用。一次搜索花费 1 积分，截至 2026 年 10 月低至 US$1.20/千次搜索。无需 KYC；用加密货币支付。

## 你需要什么

| 属性 | 值 |
| --- | --- |
| 软件包 | `openai` 和 `requests`（Agents SDK 版本则为 `openai-agents`） |
| 密钥 | `OPENAI_API_KEY` 用于模型，`LLMWEBSEARCH_API_KEY` 用于搜索 |
| 模型 | 任何支持 function calling 的模型，在 `OPENAI_MODEL` 中设置 |
| 使用的 API | Responses API：`client.responses.create`，配合 `tools` 和 `input` |
| 我方计费 | 1 积分支付一次查询：最多 20 条排名结果，其中前 3 条的页面会被读取并以 Markdown 返回。读取 4 到 10 个页面为 2 积分。以错误结束的调用不收费 |

## 工具 schema 长什么样？

一个 function tool 有 `name`、`description` 和 JSON Schema `parameters`。描述要具体：工具返回什么，以及来源应按编号引用。对大多数 agent 来说，两个参数就够了：`query`，以及完整返回多少个头部结果 `read`。

模型永远看不到你的 API 密钥。它只提出参数；由你的代码决定如何调用 API、返回什么。

## 调用循环如何工作？

把用户的消息连同工具一起发送。如果响应中包含类型为 `function_call` 的条目，解析每个条目的 JSON `arguments`，执行搜索，并追加一个具有相同 `call_id` 的 `function_call_output`。然后用更长的输入列表再次调用模型。当响应中不包含函数调用时，其文本就是答案。

下面的循环在五轮后停止，以防模型不停搜索。OpenAI 的 function-calling 指南描述了同样的 `function_call` 和 `function_call_output` 条目，于 2026 年 10 月 22 日读取。

*search_tool_loop.py（Responses API）*

```python
import json
import os

import requests
from openai import OpenAI

client = OpenAI()
MODEL = os.environ["OPENAI_MODEL"]  # any model that supports function calling
SEARCH = "https://llmwebsearch.com/api/v1/search"

tools = [
    {
        "type": "function",
        "name": "search_web",
        "description": "Search the live web. Returns numbered sources with URLs; the first `read` include page text. Cite sources as [1], [2].",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string", "description": "What to search for, as a person would type it."},
                "read": {"type": "integer", "minimum": 0, "maximum": 10, "description": "How many top results to read in full."},
            },
            "required": ["query"],
            "additionalProperties": False,
        },
    }
]


def search_web(query, read=3):
    resp = requests.post(
        SEARCH,
        headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
        json={"q": query, "max_results": 5, "read": read, "max_chars": 5000},
        timeout=60,
    )
    if resp.status_code != 200:
        return "Search failed: " + resp.json().get("error", {}).get("code", str(resp.status_code))
    parts = []
    for i, r in enumerate(resp.json()["results"], start=1):
        text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
        parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
    return "\n\n".join(parts)


input_list = [{"role": "user", "content": "How should I size a Postgres connection pool?"}]
for _ in range(5):
    response = client.responses.create(model=MODEL, tools=tools, input=input_list)
    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        print(response.output_text)
        break
    input_list += response.output
    for item in calls:
        args = json.loads(item.arguments)
        output = search_web(args["query"], int(args.get("read", 3)))
        input_list.append({"type": "function_call_output", "call_id": item.call_id, "output": output})

```

## Agents SDK 有更短的版本吗？

有。OpenAI Agents SDK 通过一个装饰器把 Python 函数变成工具，并为你运行调用循环。其工具文档（于 2026 年 10 月 22 日读取）展示了来自 `agents.decorators` 的 `@tool` 装饰器，描述取自 docstring，每个参数的描述取自其 `Args` 部分。同一页面还提到了 `@function_tool` 形式。

`Runner.run_sync` 运行 agent 直到它给出最终答案，并按模型请求的次数调用 `search_web`。

*search_agent.py（OpenAI Agents SDK）*

```python
import os

import requests
from agents import Agent, Runner
from agents.decorators import tool


@tool
def search_web(query: str, read: int = 3) -> str:
    """Search the live web and return numbered sources with their URLs.

    Args:
        query: What to search for, as a person would type it.
        read: How many top results to read in full, from 0 to 10.
    """
    resp = requests.post(
        "https://llmwebsearch.com/api/v1/search",
        headers={"Authorization": "Bearer " + os.environ["LLMWEBSEARCH_API_KEY"]},
        json={"q": query, "max_results": 5, "read": max(0, min(read, 10)), "max_chars": 5000},
        timeout=60,
    )
    resp.raise_for_status()
    parts = []
    for i, r in enumerate(resp.json()["results"], start=1):
        text = r["content"] if r["content_status"] == "ok" else "(snippet only) " + r["snippet"]
        parts.append(f"[{i}] {r['title']} - {r['url']}\n{text}")
    return "\n\n".join(parts)


agent = Agent(
    name="Researcher",
    instructions="Answer from search_web results only and cite them as [1], [2].",
    tools=[search_web],
)
result = Runner.run_sync(agent, "How should I size a Postgres connection pool?")
print(result.final_output)

```

## 为什么用 API 工具而不用 OpenAI 内置的网页搜索？

主要是为了控制模型读取的内容以及花费。我们的工具把原始来源直接交给你的代码，带 URL 和网页正文，因此由你决定哪些内容进入上下文，也能记录和存储。我们不会用你的查询或 API 返回的内容训练任何模型，也不会自行添加任何禁止存储结果的规则；你读取的页面所属网站的权利仍然适用。

价格也是按每次查询固定收费，而不取决于模型读取了多少检索内容。[内置工具对比](https://llmwebsearch.com/zh/compare/built-in-web-search-tools/)把 OpenAI、Anthropic、Google 等的每次调用价格并排列出，并标注了日期。

## 如何避免模型搜索过多？

在指令里说清楚：只搜索一次，阅读来源，只有在来源互相矛盾或没有切中要点时才再搜索。在自己的循环里限制轮数，如上面的脚本限制为五轮。

让 `max_chars` 保持适中。三页、每页 5,000 字符，约合每次查询 4,000 tokens，而每一轮都会把它们加到下一次模型调用的输入里。

## OpenAI function calling：常见问题

### 这在 Chat Completions API 上能用吗？

可以，用的是 Chat Completions 较旧的 `tools` 格式，函数放在 `function` 对象里，结果以 `tool` 消息返回。搜索函数本身不变。

### 模型能读取在来源中发现的页面吗？

再加一个函数 `read_urls`，把 URL 提交给 `https://llmwebsearch.com/api/v1/extract`。读取你已持有 URL 的页面：每开始一组 3 个返回内容的页面计 1 积分，每次调用最多 20 个 URL；被拒绝或失败的 URL 不计入。

### 该用哪个模型？

任何支持 function calling 的模型都可以。拿你自己的问题试两三个：答案既取决于来源，也取决于模型，而更小的模型每轮成本更低。

## 来源

1. [Function calling](https://developers.openai.com/api/docs/guides/function-calling) — OpenAI API docs (read 2026-10-22)
2. [工具（OpenAI Agents SDK）](https://openai.github.io/openai-agents-python/tools/) — OpenAI Agents SDK docs (read 2026-10-22)

---

LLMWebSearch: LLMWebSearch 是一款面向 AI 智能体的网页搜索 API，返回排序后的搜索结果，并附带热门页面的 Markdown 格式内容，采用加密货币支付，无需上传证件。预付费积分低至 US$1.20/千次搜索，永不过期。无 KYC——无需证件、无需自拍、无需提交任何文件。加密货币支付。
