概要
OpenAIのモデルにfunction callingでWebページを読ませるにはどうすればよいですか?
toolsリスト内でread_url関数を宣言し、ループを実行します。モデルがツール呼び出しを返したら、URLを指定してWebReaderAPIの/readエンドポイントを呼び出し、Markdownをtoolメッセージとして送り返します。各ページは1クレジット、ブラウザ使用時は2クレジットで、$0.07/1Kページ時点の価格は2026年10月現在です。KYC不要、暗号資産で支払い。
read_urlスキーマはどのような形ですか?
パラメータは2つで十分です。必須のurl(読み取る対象)と、モデルが長いページを短く保てる任意のmax_charsです。最も重要なのはdescriptionで、ツールが何を返すか、いつ使うべきかをモデルに伝えます。事実に基づいて簡潔に保ちましょう。
エージェントランタイムの中には、同名のツールをすでに提供しているものがあるため、ツール名はweb_fetchではなくread_urlとしています。
{
"type": "function",
"function": {
"name": "read_url",
"description": "Read a public web page and return its main content as Markdown, with its title and source URL. Use it when the user gives a link or when a page must be read to answer.",
"parameters": {
"type": "object",
"properties": {
"url": { "type": "string", "description": "The http or https address of the page." },
"max_chars": { "type": "integer", "description": "Most characters to return, 1000 to 500000. Default 20000." }
},
"required": ["url"],
"additionalProperties": false
}
}
}tool callingのループはどのように動作しますか?
toolsリストを含めて会話を送信します。応答にツール呼び出しが含まれていれば、それぞれを実行し、結果をrole toolと対応するtool_call_idを持つメッセージとして追加し、再度モデルに問い合わせます。ツール呼び出しを含まない応答が返ってきたら、それが最終回答です。
ラウンド数には上限を設けましょう。数ページに関する質問なら3〜4回で十分であり、上限はより多くの読み取りを要求し続けるモデルからあなたを守ります。
import json
import os
import requests
from openai import OpenAI
READ_URL = {
"type": "function",
"function": {
"name": "read_url",
"description": "Read a public web page and return its main content as Markdown, with its title and source URL.",
"parameters": {
"type": "object",
"properties": {
"url": {"type": "string", "description": "The http or https address of the page."},
"max_chars": {"type": "integer", "description": "Most characters to return. Default 20000."},
},
"required": ["url"],
"additionalProperties": False,
},
},
}
def read_url(url, max_chars=20000):
resp = requests.get(
"https://webreaderapi.com/api/v1/read",
headers={"Authorization": "Bearer " + os.environ["WEBREADER_API_KEY"]},
params={"url": url, "max_chars": max_chars},
timeout=75,
)
body = resp.json()
if resp.status_code != 200:
return "Could not read the page: " + body["error"]["code"]
return "# " + (body.get("title") or url) + "\nSource: " + body["final_url"] + "\n\n" + body["markdown"]
client = OpenAI()
model = os.environ.get("OPENAI_MODEL", "gpt-4.1-mini")
messages = [{"role": "user", "content": "What does https://example.com/blog/first-post announce? Answer in two sentences."}]
for _ in range(4):
reply = client.chat.completions.create(model=model, messages=messages, tools=[READ_URL])
msg = reply.choices[0].message
if not msg.tool_calls:
print(msg.content)
break
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
messages.append({"role": "tool", "tool_call_id": call.id, "content": read_url(**args)})
トークン使用量とコストを抑えるにはどうすればよいですか?
重要な設定は3つあります。max_charsを控えめに、1ページあたり約8,000〜20,000文字に保ちましょう。返すすべての文字がモデルに読まれるためです。デフォルトのmain_content=trueを維持すれば、メニューやフッターがコンテキストに入ることはありません。そして上記のようにループに上限を設ければ、1つの質問が数十回の読み取りを引き起こすことはありません。
当社側では、コストは文字数ではなくページ単位です: 1クレジット = ブラウザなしでページを1回読み取り。ページのJavaScriptレンダリングにブラウザが必要な場合は2クレジット。失敗したリクエスト、またはサイトのrobots.txtが拒否したリクエストは課金されません。1日に300ページを読むサポートアシスタントは月に約9,000クレジットを使用し、10,000ページ($3.75)の範囲内に収まります。
スキーマは他のモデルでも動作しますか?
はい、ラッパーに小さな変更を加えるだけで動作します。ほとんどのtool calling APIはパラメータ用のJSON Schemaと、ツールの名前・説明を受け取るため、read_urlはそのままプロバイダー間を移行できます。MCPページはModel Context Protocolを扱うクライアント向けに同じ関数をパッケージ化し、LangChainページはLangChainおよびLlamaIndexエージェント向けにラップします。
ページが拒否されたとき、モデルには何が見えるべきですか?
上記のループが返すような、短く明確な一文です: "Could not read the page: blocked_by_robots"。モデルはこれをうまく処理し、他のソースにフォールバックするか、ユーザーに伝えます。当社はrobots.txtを尊重します。クローラーはWebReaderAPIとして名乗り、自身向けまたはすべてのロボット向けに書かれたルールに従い、サイトが禁止したページは読み取りません。拒否されたページは課金されません。サイトが当社のリクエストを拒否した場合、その回答をそのまま返します。CAPTCHAを解いたり、アカウントにログインしたり、ブロックやペイウォールを回避しようとしたりすることは決してありません。これらの拒否はいずれもクレジットを消費しません。
Function calling: よくある質問
Responses APIとChat Completionsのどちらが必要ですか?
この例ではtoolsパラメータを指定したChat Completionsを使用しています。同じ関数定義と同じread_urlボディは、function toolsを受け入れる他のOpenAIインターフェースでも動作します。変わるのはそれらを囲むループだけです。
モデルは1ターンで複数のページを読めますか?
はい。応答には複数のツール呼び出しを含めることができ、ループはそれぞれを実行し、再度モデルに問い合わせる前にすべての結果を返します。モデルが一度に多くのページを読む場合は、max_charsを低めに保ちましょう。
APIキーはモデルに公開されますか?
いいえ。キーはあなたのコードの環境に留まります。モデルが見るのはツールの名前、説明、およびあなたの関数が返すテキストだけです。
拒否されたページのコストはいくらですか?
ゼロです。1クレジット = ブラウザなしでページを1回読み取り。ページのJavaScriptレンダリングにブラウザが必要な場合は2クレジット。失敗したリクエスト、またはサイトのrobots.txtが拒否したリクエストは課金されません。