Vastuste API web_search
Kasutage hostitud veebiotsingut PHP cURL-iga ja dekodeerige turvaliselt kas JSON- või SSE-vastused.
Vastuste API web_search
Kasutage hostitud web_search tööriist, kui vastus sõltub praegusest avalikust teabest: hiljutised väljaanded, dokumentatsiooni värskendused, hinnad, ajakavad, määrused, tootemuudatused või muud faktid, mis võisid pärast mudeli koolitusperioodi muutuda.
Sellel lehel kirjeldatakse web_search sisse POST /v1/responses. See erineb eraldiseisvast "POSTITA /v1/web-search". lõpp-punkt.
Soovitatav taotlus
Saatke kanooniline Responses API sisendmassiivi. Nõua tööriista, kui vastus peab olema maandatud otseallikatest.
{
"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
}
Miks need väljad on olulised?
toolsteeb mudelile kättesaadavaks hostitud otsingu.tool_choice: requiredtakistab ainult mäluga vastust, kui reaalajas kinnitamine on kohustuslik.include: ["web_search_call.action.sources"]palub API-l lisada otsingukutses arvesse võetud URL-id.stream: falsetaotleb mittevoogesituse vastust, kuid ühilduvad lüüsid võivad siiski tagastada sündmuste voo vastused. Teie klient peab toetama mõlemat vormingut.
Ärge lisage pakkujapõhiseid genereerimisparameetreid, välja arvatud juhul, kui valitud mudel neid toetab. Suunatud pakkujad võivad keelduda sellistest parameetritest nagu temperature, backgroundvõi väljundmärgi juhtelemendid.
Täielik PHP cURL-i näide
Järgmine näide saadab päringu, säilitab ülesvoolu veakeha ja dekodeerib kas tavalise JSON-vastuse või serveri saadetud sündmuste vastuse.
<?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;
}
Tähtis: stream: false võib siiski tagastada SSE
stream: false väljendab nõutud vastuse režiimi. Ühilduv puhverserver või ülesvoolu suunatav saab siiski lõpetatud vastuste elutsükli järjestada kui text/event-stream selliste sündmustega nagu:
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",...}}
Ära jookse json_decode() otse kogu kehale, kuni olete kindlaks teinud, kas see on JSON või SSE. SSE puhul kogumine lõpetatud response.output_item.done esemed ja finaal response.completed metaandmed.
Allika kinnitamine
Jooksva teabe töövoogude puhul kinnitage enne vastusega nõustumist kõik järgmised.
- Vähemalt üks on valmis
web_search_callon olemas. - At least one URL exists in
web_search_call.action.sourcesvõi anoutput_textURL-i tsitaat. - Lõplik vastuse olek on
completed. - Allikad toetavad väiteid, mida kavatsete kasutada.
- Olulisi fakte kinnitab eelistatavalt ametlik või esmane allikas.
Ärge käsitlege ladusat vastust kui tõendit, et otsing toimis. Kontrollige tööriista väljundit programmiliselt.
Vigade käsitlemine ja uuesti proovimine
- Säilitage mitte-2xx-vastuskehad. Need sisaldavad kasulikke pakkuja valideerimissõnumeid.
- Ärge proovige HTTP uuesti
400ilma tagasilükatud taotlust muutmata. - Proovi uuesti mööduvat
429,502,503,504ja transporditõrked eksponentsiaalse tagasilöögi ja värinaga. - Kasutage oma rakenduses idempotentsusvõtit, kui korduskatse võib käivitada äritegevuse.
- Hoidke rakenduste, PHP, pöördpuhverserveri ja lüüsi ajalõpud pikkade otsingupäringute jaoks joondatud.
- Ärge kunagi avaldage lõppkasutajatele API võtmeid ega jäädvustatud toorkoormust.