# OpenAI function calling：モデルに search_web ツールを渡す

> OpenAI function calling 向けのWeb検索：search_web の JSON schema、Python での Responses API ループ、そして Agents SDK での同じツール。KYC不要、暗号資産で支払い。

- URL: https://llmwebsearch.com/ja/docs/openai/
- 最終更新: 2026-10-22
- 発行者: LLMWebSearch (https://llmwebsearch.com)

`search_web`関数をモデルに説明し、モデルが要求する呼び出しを当社のAPIに対して実行し、ソースを返します。モデルは出典付きで回答します。このページでは JSON schema、Responses API ループ、そしてより短い OpenAI Agents SDK 版を紹介します。

**クイック回答 — function calling で OpenAI モデルにWeb検索を追加するには？**

JSON schema で`search_web`関数を宣言し、モデルがそれを呼び出したら検索クエリを **LLMWebSearch** の`https://llmwebsearch.com/api/v1/search`に POST します。番号付きのソースを、上位3ページを Markdown として`function_call_output`で返します。モデルはそれらを引用して回答します。検索1回で1クレジット、2026年10月時点の$1.20/1K検索から。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クレジットで検索1回分です。最大20件のランク付けされた結果を取得でき、上位3ページは本文抽出されてMarkdownで返されます。4〜10ページを読む場合は2クレジットです。エラーで終わった呼び出しは無料です |

## ツールスキーマはどのような形？

関数ツールには`name`、`description`、そして JSON Schema の`parameters`があります。説明は具体的に：ツールが何を返すか、そしてソースは番号で引用すべきこと。ほとんどのエージェントには2つのパラメータで十分です：`query`と、上位何件の結果を全文で`read`か。

モデルがAPIキーを見ることはありません。モデルは引数を提案するだけで、APIをどう呼び、何を返すかはコードが決めます。

## 呼び出しループはどう動く？

ツールを付けてユーザーのメッセージを送ります。レスポンスに型が`function_call`の項目が含まれていれば、それぞれの JSON `arguments`を解析し、検索を実行し、同じ`call_id`を持つ`function_call_output`を追加します。そして、より長くなった入力リストでモデルを再度呼びます。レスポンスに関数呼び出しが含まれなければ、そのテキストが回答です。

以下のループは5ラウンド後に停止します。検索し続けるモデルへのガードです。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`は、エージェントが最終回答を生成するまで実行し、モデルが要求するたびに`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)

```

## OpenAIの組み込みWeb検索ではなくAPIツールを使う理由は？

主に、モデルが何を読み、いくらかかるかを制御するためです。当社のツールは生のソースをURLとページ本文とともにコードに渡すので、何をコンテキストに入れるかを自分で決められ、ログに残し、保存することもできます。当社はあなたの検索クエリやAPIが返す内容でモデルを学習させることはなく、結果の保存を禁じる独自のルールも設けていません。ただし、あなたが読むページのサイトの権利は引き続き適用されます。

また、料金は取得したコンテンツをモデルがどれだけ読むかに依存せず、検索ごとに固定です。[組み込みツール比較](https://llmwebsearch.com/ja/compare/built-in-web-search-tools/)では、OpenAI、Anthropic、Googleなどの1回あたりの料金を、確認日とともに並べて比較しています。

## モデルが検索しすぎるのを防ぐには？

instructionsに書きます。1回検索し、ソースを読み、それらが食い違うか要点を外している場合にのみ再検索する、と。自分のループでラウンド数を上限設定します。上のスクリプトでは5にしています。

`max_chars`は控えめに保ちます。5,000文字のページ3枚で1回の検索あたり約4,000トークンになり、各ラウンドでそれが次のモデル呼び出しの入力に加算されます。

## OpenAI function calling：よくある質問

### Chat Completions APIで動作しますか？

はい、Chat Completionsの従来の`tools`形式で動作します。functionは`function`オブジェクトの中に置き、結果は`tool`メッセージとして返します。検索関数自体は変わりません。

### モデルはソース内で見つけたページを読めますか？

2つ目の関数`read_urls`を追加し、URLを`https://llmwebsearch.com/api/v1/extract`にPOSTします。URLをすでに持っているページを読む場合は、コンテンツ付きで返される3ページごとの開始グループにつき1クレジットで、1回の呼び出しにつき最大20 URLです。拒否されたURLや失敗したURLはカウントされません。

### どのモデルを使えばいいですか？

function callingをサポートするモデルならどれでも。自分の質問で2〜3試してください。答えはモデルと同じくらいソースにも左右され、小さいモデルほど1ラウンドあたりのコストが低くなります。

## 出典

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エージェント向けのWeb検索APIです。ランキング済みの検索結果を、上位ページの本文をMarkdownに変換した内容とともに返し、暗号資産で支払えます。本人確認書類のアップロードは不要です。プリペイドのクレジットは$1.20/1K検索から購入でき、有効期限はありません。KYC不要 — 身分証も自撮りも書類も不要。暗号資産で支払い。
