API-ul răspunsuri web_search
Utilizați căutarea web găzduită cu PHP cURL și decodați în siguranță răspunsurile fie JSON, fie SSE.
API de răspunsuri web_search
Utilizați găzduit web_search instrument atunci când un răspuns depinde de informațiile publice curente: versiuni recente, actualizări ale documentației, prețuri, programe, reglementări, modificări ale produsului sau alte fapte care s-ar putea fi schimbat după perioada de instruire a modelului.
Această pagină descrie web_search pe POST /v1/responses. Este diferit de cel independent `POST /v1/web-search` punctul final.
Cerere recomandată
Trimiteți o matrice de intrare API-ul Responses canonic. Solicitați instrumentul atunci când răspunsul trebuie să fie bazat pe surse live.
{
"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
}
De ce contează aceste domenii
toolspune la dispoziția modelului căutarea găzduită.tool_choice: requiredprevine un răspuns numai de memorie atunci când verificarea live este obligatorie.include: ["web_search_call.action.sources"]solicită API-ului să includă adresele URL luate în considerare de apelul de căutare.stream: falsesolicită un răspuns non-streaming, dar gateway-urile compatibile pot returna în continuare un flux de evenimente Responses. Clientul dvs. trebuie să accepte ambele formate.
Nu adăugați parametri de generare specifici furnizorului decât dacă documentele modelului selectat îi acceptă. Furnizorii direcționați pot respinge parametri precum temperature, background, sau comenzile jetonului de ieșire.
Exemplu complet PHP cURL
Următorul exemplu trimite cererea, păstrează corpul erorii din amonte și decodifică fie un răspuns JSON normal, fie un răspuns la Evenimente trimise de server.
<?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;
}
Important: stream: false mai poate returna SSE
stream: false exprimă modul de răspuns solicitat. Un proxy compatibil sau direcționat în amonte poate serializa în continuare ciclul de viață a răspunsurilor finalizat ca text/event-stream cu evenimente precum:
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",...}}
Nu alerga json_decode() direct pe întregul corp până când ați determinat dacă este JSON sau SSE. Pentru SSE, colectare finalizată response.output_item.done articole și finala response.completed metadate.
Validarea sursei
Pentru fluxurile de lucru cu informații actuale, validați toate următoarele înainte de a accepta răspunsul:
- Cel puțin unul finalizat
web_search_callexistă. - Există cel puțin o adresă URL în
web_search_call.action.sourcessau unoutput_textcitare URL. - Starea finală a răspunsului este
completed. - Sursele susțin afirmațiile pe care intenționați să le utilizați.
- Faptele importante sunt de preferință confirmate de o sursă oficială sau primară.
Nu tratați un răspuns fluent ca o dovadă că căutarea a avut loc. Verificați rezultatul instrumentului în mod programat.
Gestionarea erorilor și reîncercări
- Păstrați corpurile de răspuns non-2xx. Acestea conțin mesaje utile de validare a furnizorului.
- Nu reîncercați HTTP
400fără modificarea cererii respinse. - Reîncercați tranzitoriu
429,502,503,504, și eșecuri de transport cu backoff exponențial și jitter. - Utilizați o cheie de idempotency în propria aplicație atunci când o reîncercare ar putea declanșa o acțiune de afaceri.
- Păstrați aliniate intervalele de timp pentru aplicație, PHP, proxy invers și gateway pentru cererile de căutare lungi.
- Nu expuneți niciodată utilizatorilor finali cheile API sau încărcăturile utile brute capturate.