Responses API web_search
Használjon hostolt webes keresést a PHP cURL-lel, és biztonságosan dekódolja a JSON- vagy az SSE-válaszokat.
Válaszok API web_search
Használja a hostot web_search eszköz, amikor a válasz az aktuális nyilvános információktól függ: a legutóbbi kiadásoktól, a dokumentáció frissítéseitől, az áraktól, a menetrendektől, a szabályozásoktól, a termékváltozásoktól vagy egyéb olyan tényektől, amelyek a modellképzési időszak után megváltozhattak.
Ez az oldal leírja web_search -on POST /v1/responses. Ez különbözik az önállótól `POST /v1/web-search` végpont.
Ajánlott kérés
Kanonikus Responses API bemeneti tömb küldése. Kérje meg az eszközt, ha a válasznak élő forrásban kell lennie.
{
"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
}
Miért fontosak ezek a mezők?
toolselérhetővé teszi a hosztolt keresést a modell számára.tool_choice: requiredmegakadályozza a csak memóriaalapú választ, ha az élő ellenőrzés kötelező.include: ["web_search_call.action.sources"]kéri az API-t, hogy tartalmazza a keresési hívás által figyelembe vett URL-eket.stream: falsenem adatfolyamos választ kér, de a kompatibilis átjárók továbbra is visszaküldhetnek egy Válasz eseményfolyamot. Az ügyfélnek mindkét formátumot támogatnia kell.
Ne adjon hozzá szolgáltató-specifikus generálási paramétereket, hacsak a kiválasztott modell nem támogatja ezeket. Az irányított szolgáltatók elutasíthatnak olyan paramétereket, mint pl temperature, background, vagy kimeneti token vezérlők.
Teljes PHP cURL példa
A következő példa elküldi a kérést, megőrzi a felfelé irányuló hibatörzset, és dekódolja a normál JSON-választ vagy a kiszolgáló által küldött események válaszát.
<?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;
}
Fontos: stream: false továbbra is visszaadhatja az SSE-t
stream: false a kért válaszmódot fejezi ki. Egy kompatibilis proxy vagy irányított upstream továbbra is sorba állíthatja a befejezett válaszok életciklusát text/event-stream olyan eseményekkel, mint:
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",...}}
Ne fuss json_decode() közvetlenül az egész testen, amíg meg nem határozta, hogy JSON vagy SSE. SSE esetén az összegyűjtés befejezve response.output_item.done tételek és a döntő response.completed metaadatokat.
Forrás érvényesítése
Az aktuális információs munkafolyamatok esetében a válasz elfogadása előtt ellenőrizze az alábbiak mindegyikét:
- Legalább egy elkészült
web_search_calllétezik. - Legalább egy URL létezik itt
web_search_call.action.sourcesvagy egyoutput_textURL hivatkozás. - A végső válasz állapota
completed. - A források alátámasztják az Ön által felhasználni kívánt állításokat.
- A fontos tényeket lehetőleg hivatalos vagy elsődleges forrás erősítse meg.
A gördülékeny választ ne tekintse annak bizonyítékaként, hogy a keresés lefutott. Ellenőrizze a szerszám kimenetét programozottan.
Hibakezelés és újrapróbálkozás
- A nem 2xx választestek megőrzése. Hasznos szolgáltatói érvényesítési üzeneteket tartalmaznak.
- Ne próbálja újra a HTTP-t
400az elutasított kérés megváltoztatása nélkül. - Próbálja újra tranziens
429,502,503,504és szállítási hibák exponenciális hátrálással és jitterrel. - Használjon idempotencia kulcsot saját alkalmazásában, ha egy újrapróbálkozás üzleti műveletet válthat ki.
- Tartsa összehangolva az alkalmazás, a PHP, a fordított proxy és az átjáró időtúllépéseit a hosszú keresési kérésekhez.
- Soha ne tegye ki az API-kulcsokat vagy a rögzített nyers hasznos adatokat a végfelhasználóknak.