# OpenAI function calling: narzędzie read_url dla Twojego modelu

> OpenAI function calling z narzędziem read_url: schemat JSON i pętla w Pythonie, która zamienia linki w czysty Markdown dla modelu. Bez KYC, płatność krypto.

- URL: https://webreaderapi.com/pl/docs/openai/
- Ostatnia aktualizacja: 2026-10-22
- Wydawca: WebReaderAPI (https://webreaderapi.com)

Opisz jedną funkcję, `read_url`, i uruchom ją, gdy model o to poprosi. Model wybiera, które strony czytać; Twój kod wywołuje API i zwraca Markdown. Ten sam schemat działa z innymi modelami, które obsługują tool calling.

**Szybka odpowiedź — Jak pozwolić modelowi OpenAI czytać strony internetowe za pomocą function calling?**

Zadeklaruj funkcję `read_url` na liście `tools`, a następnie uruchom pętlę: gdy model zwróci tool call, wywołaj endpoint `/read` w **WebReaderAPI** z adresem URL i odeślij Markdown jako wiadomość `tool`. Każda strona kosztuje 1 kredyt, 2 z przeglądarką, od 0,07 USD/1K stron według stanu na październik 2026. Bez KYC; płatność kryptowalutą.

## Jak wygląda schemat read_url?

Wystarczą dwa parametry: `url` do odczytania, wymagany, oraz opcjonalny `max_chars`, który pozwala modelowi skrócić długą stronę. Opis jest najważniejszą częścią: mówi modelowi, co zwraca narzędzie i kiedy go użyć. Trzymaj go rzeczowo i krótko.

Nazywamy narzędzie `read_url`, a nie `web_fetch`, żeby uniknąć kolizji z narzędziami, które niektóre środowiska uruchomieniowe agentów już udostępniają pod tą nazwą.

*Definicja narzędzia*

```json
{
  "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
    }
  }
}
```

## Jak działa pętla tool calling?

Wyślij rozmowę z listą `tools`. Jeśli odpowiedź zawiera tool calls, uruchom każde z nich, dołącz wynik jako wiadomość z rolą `tool` i pasującym `tool_call_id`, a następnie ponownie zapytaj model. Gdy odpowiedź wraca bez tool call, jest to odpowiedź końcowa.

Ogranicz liczbę rund. Trzy lub cztery w zupełności wystarczą na pytanie o kilka stron, a limit chroni Cię przed modelem, który wciąż prosi o więcej.

*Python: pełna pętla z pakietem openai*

```python
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)})

```

## Jak ograniczyć zużycie tokenów i koszty?

Liczą się trzy ustawienia. Trzymaj `max_chars` na umiarkowanym poziomie, około 8 000 do 20 000 znaków na stronę, bo każdy zwracany znak czyta model. Zachowaj domyślne `main_content=true`, żeby menu i stopki nigdy nie trafiały do kontekstu. I ogranicz pętlę, jak wyżej, żeby jedno pytanie nie wywołało dziesiątek odczytów.

Po naszej stronie koszt jest za stronę, nie za znak: 1 kredyt = 1 odczytana strona bez przeglądarki; 2 kredyty, gdy strona wymaga przeglądarki do wyrenderowania JavaScript. Zapytanie, które się nie powiedzie lub które zostanie odrzucone przez robots.txt witryny, nie jest obciążane opłatą. Asystent pomocy technicznej, który czyta 300 stron dziennie, zużywa około 9 000 kredytów miesięcznie, w ramach 10 000 stron (3,75 USD).

## Czy schemat działa z innymi modelami?

Tak, przy drobnych zmianach w wrapperze. Większość API obsługujących tool calling przyjmuje JSON Schema dla parametrów oraz nazwę i opis narzędzia, więc `read_url` przenosi się między dostawcami bez zmian. [Strona MCP](https://webreaderapi.com/pl/docs/mcp/) pakuje tę samą funkcję dla klientów obsługujących Model Context Protocol, a [strona LangChain](https://webreaderapi.com/pl/docs/langchain/) opakowuje ją dla agentów LangChain i LlamaIndex.

## Co model powinien zobaczyć, gdy strona jest odrzucona?

Krótkie, jasne zdanie, tak jak zwraca to powyższa pętla: „Could not read the page: blocked_by_robots”. Modele radzą sobie z tym dobrze i sięgają do innych źródeł albo informują użytkownika. Respektujemy robots.txt: nasz crawler identyfikuje się jako WebReaderAPI, przestrzega reguł napisanych dla niego lub dla wszystkich robotów i nie czyta strony, której witryna zabrania. Odrzucone strony nie są obciążane opłatą. Jeśli witryna odrzuci nasze zapytanie, zwracamy jej odpowiedź: nigdy nie rozwiązujemy CAPTCHA, nie logujemy się do kont ani nie próbujemy obejść blokady lub paywalla. Żadna z tych odmów nie zużywa kredytu.

## Function calling: najczęstsze pytania

### Czy potrzebne jest Responses API czy Chat Completions?

Przykład używa Chat Completions z parametrem `tools`. Ta sama definicja funkcji i to samo ciało `read_url` działają z innymi interfejsami OpenAI, które przyjmują function tools; zmienia się tylko pętla wokół nich.

### Czy model może przeczytać kilka stron w jednej turze?

Tak. Odpowiedź może zawierać kilka tool calls; pętla uruchamia każde i zwraca wszystkie wyniki, zanim ponownie zapyta model. Ustaw `max_chars` niżej, gdy model czyta wiele stron naraz.

### Czy klucz API jest widoczny dla modelu?

Nie. Klucz pozostaje w środowisku Twojego kodu. Model widzi tylko nazwę narzędzia, jego opis i tekst, który zwraca Twoja funkcja.

### Ile kosztuje odrzucona strona?

Nic. 1 kredyt = 1 odczytana strona bez przeglądarki; 2 kredyty, gdy strona wymaga przeglądarki do wyrenderowania JavaScript. Zapytanie, które się nie powiedzie lub które zostanie odrzucone przez robots.txt witryny, nie jest obciążane opłatą.

---

WebReaderAPI: WebReaderAPI to API czytnika stron internetowych dla agentów AI, które zamienia każdą publiczną stronę internetową w czysty Markdown, z płatnością kryptowalutą i bez przesyłania dokumentów tożsamości. Przedpłacone kredyty od 0,07 USD/1K stron nigdy nie wygasają, a robots.txt jest respektowany. Bez KYC — bez dowodu, bez selfie, bez dokumentów. Płatność kryptowalutą.
