Antwort-API web_search
Nutzen Sie die gehostete Websuche mit PHP cURL und dekodieren Sie JSON- oder SSE-Antworten sicher.
Antwort-API web_search
Verwenden Sie das gehostete web_search Tool, wenn eine Antwort von aktuellen öffentlichen Informationen abhängt: aktuelle Versionen, Dokumentationsaktualisierungen, Preise, Zeitpläne, Vorschriften, Produktänderungen oder andere Fakten, die sich nach der Modellschulungsphase geändert haben könnten.
Diese Seite beschreibt web_search An POST /v1/responses. Es unterscheidet sich vom Standalone `POST /v1/web-search` Endpunkt.
Empfohlene Anfrage
Senden Sie ein kanonisches Antwort-API-Eingabearray. Erfordern Sie das Tool, wenn die Antwort auf Live-Quellen basieren muss.
{
"model": "gpt-5.5",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "Find the current stable PHP version. Use web search and cite the official sources."
}
]
}
],
"tools": [
{
"type": "web_search"
}
],
"tool_choice": "required",
"include": [
"web_search_call.action.sources"
],
"stream": false
}
Warum diese Bereiche wichtig sind
toolsstellt dem Modell die gehostete Suche zur Verfügung.tool_choice: requiredverhindert eine Nur-Speicher-Antwort, wenn eine Live-Überprüfung obligatorisch ist.include: ["web_search_call.action.sources"]fordert die API auf, die vom Suchaufruf berücksichtigten URLs einzubeziehen.stream: falsefordert eine Nicht-Streaming-Antwort an, kompatible Gateways geben jedoch möglicherweise dennoch einen Antwort-Ereignisstrom zurück. Ihr Client muss beide Formate unterstützen.
Fügen Sie keine anbieterspezifischen Generierungsparameter hinzu, es sei denn, die ausgewählten Modelldokumente unterstützen sie. Geroutete Anbieter lehnen möglicherweise Parameter wie ab temperature, backgroundoder Ausgabetoken-Steuerelemente.
Vollständiges PHP-cURL-Beispiel
Das folgende Beispiel sendet die Anfrage, behält den Upstream-Fehlertext bei und dekodiert entweder eine normale JSON-Antwort oder eine Antwort auf vom Server gesendete Ereignisse.
<?php
declare(strict_types=1);
$baseUrl = 'https://api.model-gate.com';
$apiKey = getenv('MODEL_GATE_API_KEY') ?: '';
if ($apiKey === '') {
throw new RuntimeException('MODEL_GATE_API_KEY is not configured.');
}
$url = rtrim($baseUrl, '/') . '/v1/responses';
$request = [
'model' => 'gpt-5.5',
'input' => [[
'role' => 'user',
'content' => [[
'type' => 'input_text',
'text' => 'Find the current stable PHP version. '
. 'Use web search and cite official sources.',
]],
]],
'tools' => [[
'type' => 'web_search',
]],
'tool_choice' => 'required',
'include' => [
'web_search_call.action.sources',
],
'stream' => false,
];
$json = json_encode(
$request,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR
);
$responseHeaders = [];
$ch = curl_init($url);
if ($ch === false) {
throw new RuntimeException('Unable to initialize cURL.');
}
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 15,
CURLOPT_TIMEOUT => 900,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey,
'Content-Type: application/json',
'Accept: application/json, text/event-stream',
],
CURLOPT_POSTFIELDS => $json,
CURLOPT_HEADERFUNCTION => static function ($ch, string $line) use (&$responseHeaders): int {
$length = strlen($line);
$line = trim($line);
if ($line === '' || !str_contains($line, ':')) {
return $length;
}
[$name, $value] = array_map('trim', explode(':', $line, 2));
$responseHeaders[strtolower($name)] = $value;
return $length;
},
]);
$responseBody = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlErrorNumber = curl_errno($ch);
$curlError = curl_error($ch);
curl_close($ch);
if ($responseBody === false) {
throw new RuntimeException(
sprintf('cURL error %d: %s', $curlErrorNumber, $curlError)
);
}
if ($httpCode < 200 || $httpCode >= 300) {
// Keep the exact upstream body. It usually contains the parameter or model error.
throw new RuntimeException(
"API returned HTTP {$httpCode}: {$responseBody}"
);
}
$response = decodeResponsesBody(
$responseBody,
$responseHeaders['content-type'] ?? ''
);
$text = '';
$sources = [];
$searchCalls = 0;
foreach (($response['output'] ?? []) as $item) {
if (($item['type'] ?? null) === 'web_search_call') {
$searchCalls++;
foreach (($item['action']['sources'] ?? []) as $source) {
$url = trim((string) ($source['url'] ?? ''));
if ($url !== '') {
$sources[$url] = $url;
}
}
}
if (($item['type'] ?? null) !== 'message') {
continue;
}
foreach (($item['content'] ?? []) as $part) {
if (($part['type'] ?? null) !== 'output_text') {
continue;
}
$text .= (string) ($part['text'] ?? '');
foreach (($part['annotations'] ?? []) as $annotation) {
if (($annotation['type'] ?? null) !== 'url_citation') {
continue;
}
$url = trim((string) ($annotation['url'] ?? ''));
if ($url !== '') {
$sources[$url] = $url;
}
}
}
}
if ($searchCalls === 0) {
throw new RuntimeException('The response did not contain web_search_call.');
}
if ($sources === []) {
throw new RuntimeException('The response did not contain auditable source URLs.');
}
echo $text . PHP_EOL;
echo "Sources:" . PHP_EOL;
foreach ($sources as $sourceUrl) {
echo '- ' . $sourceUrl . PHP_EOL;
}
function decodeResponsesBody(string $body, string $contentType): array
{
$trimmed = ltrim($body);
$looksLikeSse = str_contains(strtolower($contentType), 'text/event-stream')
|| str_starts_with($trimmed, 'event:')
|| str_starts_with($trimmed, 'data:');
if (!$looksLikeSse) {
$decoded = json_decode($body, true, 512, JSON_THROW_ON_ERROR);
if (!is_array($decoded)) {
throw new RuntimeException('Responses API returned a non-object JSON value.');
}
return $decoded;
}
return decodeResponsesSse($body);
}
function decodeResponsesSse(string $body): array
{
$output = [];
$completedResponse = [];
$eventName = '';
$dataLines = [];
$flush = static function () use (&$eventName, &$dataLines, &$output, &$completedResponse): void {
if ($dataLines === []) {
$eventName = '';
return;
}
$rawData = implode("\n", $dataLines);
$dataLines = [];
if ($rawData === '[DONE]') {
$eventName = '';
return;
}
$event = json_decode($rawData, true, 512, JSON_THROW_ON_ERROR);
$type = (string) ($event['type'] ?? $eventName);
if ($type === 'response.output_item.done' && is_array($event['item'] ?? null)) {
$output[] = $event['item'];
}
if ($type === 'response.completed' && is_array($event['response'] ?? null)) {
$completedResponse = $event['response'];
}
$eventName = '';
};
foreach (preg_split('/\r\n|\r|\n/', $body) ?: [] as $line) {
if ($line === '') {
$flush();
continue;
}
if (str_starts_with($line, 'event:')) {
$eventName = trim(substr($line, 6));
continue;
}
if (str_starts_with($line, 'data:')) {
$dataLines[] = ltrim(substr($line, 5));
}
}
$flush();
if ($completedResponse === [] && $output === []) {
throw new RuntimeException('No completed Responses API event was found.');
}
// Some compatible gateways send an empty output array in response.completed.
// Preserve the completed metadata but reconstruct output from output_item.done events.
$completedResponse['output'] = $output !== []
? $output
: ($completedResponse['output'] ?? []);
return $completedResponse;
}
Wichtig: stream: false kann immer noch SSE zurückgeben
stream: false drückt den angeforderten Antwortmodus aus. Ein kompatibler Proxy oder gerouteter Upstream kann den abgeschlossenen Antwortlebenszyklus weiterhin als serialisieren text/event-stream mit Veranstaltungen wie:
event: response.output_item.done
data: {"type":"response.output_item.done","item":{"type":"web_search_call",...}}
event: response.completed
data: {"type":"response.completed","response":{"status":"completed",...}}
Laufen Sie nicht json_decode() direkt auf den gesamten Körper, bis Sie festgestellt haben, ob es sich um JSON oder SSE handelt. Für SSE ist die Erfassung abgeschlossen response.output_item.done Artikel und das Finale response.completed Metadaten.
Quellenvalidierung
Überprüfen Sie bei Arbeitsabläufen mit aktuellen Informationen alle folgenden Punkte, bevor Sie die Antwort akzeptieren:
- Mindestens eine abgeschlossen
web_search_callexistiert. - Mindestens eine URL existiert in
web_search_call.action.sourcesoder einoutput_textURL-Zitat. - Der endgültige Antwortstatus ist
completed. - Die Quellen stützen die Behauptungen, die Sie verwenden möchten.
- Wichtige Fakten werden vorzugsweise von einer offiziellen oder primären Quelle bestätigt.
Betrachten Sie eine fließende Antwort nicht als Beweis dafür, dass die Suche durchgeführt wurde. Überprüfen Sie die Werkzeugausgabe programmgesteuert.
Fehlerbehandlung und Wiederholungsversuche
- Behalten Sie Nicht-2xx-Antworttexte bei. Sie enthalten nützliche Meldungen zur Anbietervalidierung.
- HTTP nicht erneut versuchen
400ohne die abgelehnte Anfrage zu ändern. - Transient erneut versuchen
429,502,503,504und Transportfehler mit exponentiellem Backoff und Jitter. - Verwenden Sie einen Idempotenzschlüssel in Ihrer eigenen Anwendung, wenn ein Wiederholungsversuch eine Geschäftsaktion auslösen könnte.
- Halten Sie die Anwendungs-, PHP-, Reverse-Proxy- und Gateway-Timeouts für lange Suchanfragen aufeinander abgestimmt.
- Geben Sie niemals API-Schlüssel oder erfasste Rohdaten an Endbenutzer weiter.