# OpenAI function calling: dai al modello un tool search_web

> Ricerca web per OpenAI function calling: uno schema JSON search_web, il loop della Responses API in Python e lo stesso tool nell'Agents SDK. Senza KYC.

- URL: https://llmwebsearch.com/it/docs/openai/
- Ultimo aggiornamento: 2026-10-22
- Editore: LLMWebSearch (https://llmwebsearch.com)

Descrivi al modello una funzione `search_web`, esegui la chiamata che richiede contro la nostra API e invia indietro le fonti. Il modello risponde poi con le citazioni. Questa pagina mostra lo schema JSON, il loop della Responses API e la versione più breve con OpenAI Agents SDK.

**Risposta rapida — Come aggiungo la ricerca web a un modello OpenAI con function calling?**

Dichiara una funzione `search_web` con uno schema JSON e, quando il modello la chiama, invia la query al `https://llmwebsearch.com/api/v1/search` di **LLMWebSearch** con POST. Restituisci le fonti numerate, con le prime 3 pagine in Markdown, come `function_call_output`; il modello risponde e le cita. Una ricerca costa 1 credito, a partire da 1,20 USD/1K ricerche a ottobre 2026. Senza KYC; paga in criptovalute.

## Cosa ti serve

| Attributo | Valore |
| --- | --- |
| Pacchetti | `openai` e `requests` (o `openai-agents` per la versione con Agents SDK) |
| Chiavi | `OPENAI_API_KEY` per il modello, `LLMWEBSEARCH_API_KEY` per la ricerca |
| Modello | Qualsiasi modello che supporti function calling, impostato in `OPENAI_MODEL` |
| API usata | Responses API: `client.responses.create` con `tools` e `input` |
| Fatturazione dalla nostra parte | Un credito paga una ricerca: fino a 20 risultati classificati, con le pagine dei primi 3 lette e restituite come Markdown. Leggere da 4 a 10 pagine costa 2 crediti. Una chiamata che termina in errore non costa nulla |

## Come appare lo schema del tool?

Un function tool ha un `name`, una `description` e una `parameters` JSON Schema. Mantieni la descrizione concreta: cosa restituisce il tool e che le fonti vanno citate per numero. Due parametri bastano per la maggior parte degli agenti: la `query` e quanti risultati principali `read` per intero.

Il modello non vede mai la tua chiave API. Propone solo argomenti; il tuo codice decide come chiamare l'API e cosa inviare indietro.

## Come funziona il loop di chiamata?

Invia il messaggio dell'utente con i tool. Se la risposta contiene elementi di tipo `function_call`, analizza il JSON `arguments` di ciascuno, esegui la ricerca e aggiungi un `function_call_output` con lo stesso `call_id`. Poi chiama di nuovo il modello con la lista di input più lunga. Quando una risposta non contiene alcuna chiamata di funzione, il suo testo è la risposta.

Il loop qui sotto si ferma dopo cinque round, una protezione contro un modello che continua a cercare. La guida al function calling di OpenAI descrive gli stessi elementi `function_call` e `function_call_output`, letta il 22 ottobre 2026.

*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})

```

## Esiste una versione più breve con l'Agents SDK?

Sì. L'OpenAI Agents SDK trasforma una funzione Python in un tool con un decorator ed esegue il loop di chiamata per te. La sua documentazione dei tool, letta il 22 ottobre 2026, mostra il decorator `@tool` da `agents.decorators`, con la descrizione presa dal docstring e la descrizione di ogni argomento dalla sua sezione `Args`. La stessa pagina menziona anche la forma `@function_tool`.

`Runner.run_sync` esegue l'agente finché non produce una risposta finale, chiamando `search_web` tutte le volte che il modello richiede.

*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)

```

## Perché usare un tool API invece della ricerca web integrata di OpenAI?

Soprattutto per avere il controllo su ciò che il modello legge e su quanto costa. Il nostro tool consegna al tuo codice le fonti grezze, con URL e testo della pagina, così decidi tu cosa entra nel contesto, puoi registrarlo nei log e salvarlo. Non addestriamo alcun modello sulle tue query né su ciò che l'API restituisce, e non aggiungiamo regole nostre contro la conservazione dei risultati; restano validi i diritti dei siti di cui leggi le pagine.

Inoltre il prezzo è fisso per ricerca invece di dipendere da quanto contenuto recuperato legge il modello. Il [confronto dei tool integrati](https://llmwebsearch.com/it/compare/built-in-web-search-tools/) mette a confronto i prezzi per chiamata di OpenAI, Anthropic, Google e altri, con le date.

## Come faccio a impedire al modello di cercare troppo?

Dillo nelle istruzioni: cerca una volta, leggi le fonti, cerca di nuovo solo se sono in disaccordo o non colgono il punto. Limita il numero di round nel tuo ciclo, come fa lo script qui sopra con cinque.

Mantieni `max_chars` contenuto. Tre pagine da 5.000 caratteri sono circa 4.000 token per ricerca, e ogni round li aggiunge all'input della chiamata successiva al modello.

## OpenAI function calling: domande frequenti

### Funziona con l'API Chat Completions?

Sì, con il vecchio formato `tools` di Chat Completions, in cui la funzione sta dentro un oggetto `function` e i risultati tornano come messaggi `tool`. La funzione di ricerca in sé non cambia.

### Il modello può leggere una pagina che trova in una fonte?

Aggiungi una seconda funzione, `read_urls`, che invia gli URL a `https://llmwebsearch.com/api/v1/extract`. Leggere pagine di cui hai già gli URL costa 1 credito per ogni gruppo iniziato di 3 pagine restituite con contenuto, fino a 20 URL per chiamata; gli URL rifiutati o falliti non vengono contati.

### Quale modello dovrei usare?

Qualsiasi modello che supporti il function calling. Provane due o tre sulle tue domande: la risposta dipende dalle fonti tanto quanto dal modello, e un modello più piccolo costa meno per round.

## Fonti

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

---

LLMWebSearch: LLMWebSearch è un'API di ricerca web per agenti AI che restituisce risultati classificati con il contenuto delle pagine principali in Markdown, pagata in criptovalute senza caricare documenti d'identità. I crediti prepagati da 1,20 USD/1K ricerche non scadono mai. Senza KYC — niente documento, niente selfie, niente documenti. Paghi in criptovalute.
