Kurze Antwort
Wie füge ich einem OpenAI-Modell mit Function Calling eine Websuche hinzu?
Deklarieren Sie eine search_web-Funktion mit einem JSON-Schema, und wenn das Modell sie aufruft, senden Sie die Suchanfrage per POST an https://llmwebsearch.com/api/v1/search von LLMWebSearch. Geben Sie die nummerierten Quellen, mit den Top-3-Seiten als Markdown, als function_call_output zurück; das Modell antwortet und zitiert sie. Eine Suchanfrage kostet 1 Credit, ab 1,20 $/1K Suchanfragen Stand Oktober 2026. Ohne KYC; mit Krypto bezahlen.
Was Sie brauchen
| Pakete | openai und requests (oder openai-agents für die Agents-SDK-Version) |
|---|---|
| Schlüssel | OPENAI_API_KEY für das Modell, LLMWEBSEARCH_API_KEY für die Suche |
| Modell | Jedes Modell, das Function Calling unterstützt, festgelegt in OPENAI_MODEL |
| Verwendete API | Responses API: client.responses.create mit tools und input |
| Abrechnung auf unserer Seite | Ein Credit bezahlt eine Suchanfrage: bis zu 20 gerankte Ergebnisse, wobei die Seiten der ersten 3 gelesen und als Markdown zurückgegeben werden. Werden 4 bis 10 Seiten gelesen, sind es 2 Credits. Ein Aufruf, der mit einem Fehler endet, kostet nichts |
Wie sieht das Tool-Schema aus?
Ein Function-Tool hat ein name, eine description und JSON Schema parameters. Halten Sie die Beschreibung konkret: was das Tool zurückgibt und dass Quellen nach Nummer zitiert werden sollen. Zwei Parameter reichen für die meisten Agents: die query und wie viele Top-Ergebnisse vollständig read werden sollen.
Das Modell sieht Ihren API-Schlüssel nie. Es schlägt nur Argumente vor; Ihr Code entscheidet, wie die API aufgerufen wird und was zurückgesendet wird.
Wie funktioniert die Aufrufschleife?
Senden Sie die Nachricht des Nutzers mit den Tools. Enthält die Antwort Items vom Typ function_call, parsen Sie das JSON arguments jedes einzelnen, führen Sie die Suchanfrage aus und hängen Sie ein function_call_output mit derselben call_id an. Rufen Sie dann das Modell erneut mit der längeren Eingabeliste auf. Enthält eine Antwort keinen Funktionsaufruf, ist ihr Text die Antwort.
Die Schleife unten stoppt nach fünf Runden, als Schutz gegen ein Modell, das immer weitersucht. Der Function-Calling-Leitfaden von OpenAI beschreibt dieselben function_call- und function_call_output-Items, gelesen am 22. Oktober 2026.
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})
Gibt es eine kürzere Version mit dem Agents SDK?
Ja. Das OpenAI Agents SDK macht mit einem Decorator aus einer Python-Funktion ein Tool und führt die Aufrufschleife für Sie aus. Seine Tools-Dokumentation, gelesen am 22. Oktober 2026, zeigt den @tool-Decorator aus agents.decorators, wobei die Beschreibung aus dem Docstring stammt und die Beschreibung jedes Arguments aus seinem Args-Abschnitt. Dieselbe Seite erwähnt auch die @function_tool-Form.
Runner.run_sync führt den Agent aus, bis er eine endgültige Antwort liefert, und ruft dabei search_web so oft auf, wie das Modell es verlangt.
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)
Warum ein API-Tool statt der eingebauten Websuche von OpenAI verwenden?
Vor allem für die Kontrolle darüber, was das Modell liest und was es kostet. Unser Tool übergibt Ihrem Code die Rohquellen mit URLs und Seitentext, sodass Sie entscheiden, was in den Kontext gelangt, es protokollieren und speichern können. Wir trainieren kein Modell mit Ihren Suchanfragen oder mit dem, was die API zurückgibt, und wir stellen keine eigene Regel gegen das Speichern von Ergebnissen auf; die Rechte der Websites, deren Seiten Sie lesen, gelten weiterhin.
Der Preis ist außerdem pro Suchanfrage fest statt davon abhängig, wie viel abgerufenen Inhalt das Modell liest. Der Vergleich der eingebauten Tools stellt die Preise pro Aufruf von OpenAI, Anthropic, Google und anderen mit Datumsangaben nebeneinander.
Wie verhindere ich, dass das Modell zu viel sucht?
Sagen Sie es in den Anweisungen: einmal suchen, die Quellen lesen, nur erneut suchen, wenn sie sich widersprechen oder den Punkt verfehlen. Begrenzen Sie die Anzahl der Runden in Ihrer eigenen Schleife, so wie es das obige Skript mit fünf tut.
Halten Sie max_chars maßvoll. Drei Seiten mit 5.000 Zeichen sind etwa 4.000 Tokens pro Suchanfrage, und jede Runde fügt sie dem Input des nächsten Modellaufrufs hinzu.
OpenAI Function Calling: häufige Fragen
Funktioniert das mit der Chat Completions API?
Ja, mit dem älteren tools-Format von Chat Completions, bei dem die Funktion in einem function-Objekt sitzt und die Ergebnisse als tool-Nachrichten zurückkommen. Die Suchfunktion selbst ändert sich nicht.
Kann das Modell eine Seite lesen, die es in einer Quelle findet?
Fügen Sie eine zweite Funktion hinzu, read_urls, die URLs an https://llmwebsearch.com/api/v1/extract sendet. Das Lesen von Seiten, deren URLs Sie bereits haben, kostet 1 Credit für jede angefangene Gruppe von 3 Seiten, die mit Inhalt zurückgegeben werden, bis zu 20 URLs pro Aufruf; abgelehnte oder fehlgeschlagene URLs werden nicht gezählt.
Welches Modell sollte ich verwenden?
Jedes Modell, das Function Calling unterstützt. Probieren Sie zwei oder drei mit Ihren eigenen Fragen aus: Die Antwort hängt von den Quellen ebenso ab wie vom Modell, und ein kleineres Modell kostet weniger pro Runde.
Quellen (2)
- Function Calling — OpenAI API docs (read 2026-10-22)
- Tools (OpenAI Agents SDK) — OpenAI Agents SDK docs (read 2026-10-22)