Responses API web_search
Izmantojiet mitinātu tīmekļa meklēšanu ar PHP cURL un droši atšifrējiet JSON vai SSE atbildes.
Atbilžu API web_search
Izmantojiet mitināto web_search rīks, kad atbilde ir atkarīga no pašreizējās publiskās informācijas: jaunākajiem izlaidumiem, dokumentācijas atjauninājumiem, cenām, grafikiem, noteikumiem, produktu izmaiņām vai citiem faktiem, kas var būt mainījušies pēc modeļa apmācības perioda.
Šajā lapā ir aprakstīts web_search ieslēgts POST /v1/responses. Tas atšķiras no atsevišķa `POST /v1/web-search galapunkts.
Ieteicamais pieprasījums
Nosūtiet kanonisko Responses API ievades masīvu. Pieprasīt rīku, ja atbildei jābūt balstītai uz dzīvajiem avotiem.
{
"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
}
Kāpēc šie lauki ir svarīgi
toolspadara modelim pieejamu mitināto meklēšanu.tool_choice: requirednovērš tikai atmiņas atbildi, ja tiešraides pārbaude ir obligāta.include: ["web_search_call.action.sources"]lūdz API iekļaut meklēšanas izsaukumā ņemtos URL.stream: falsepieprasa atbildi, kas nav straumēta, taču saderīgas vārtejas joprojām var atgriezt atbilžu notikumu straumi. Jūsu klientam ir jāatbalsta abi formāti.
Nepievienojiet pakalpojumu sniedzējam raksturīgus ģenerēšanas parametrus, ja vien atlasītais modelis tos neatbalsta. Maršrutētie pakalpojumu sniedzēji var noraidīt tādus parametrus kā temperature, background, vai izvades marķiera vadīklas.
Pilnīgs PHP cURL piemērs
Tālāk sniegtajā piemērā tiek nosūtīts pieprasījums, tiek saglabāts augšējās kļūdas pamatteksts un atšifrēta vai nu parasta JSON atbilde, vai servera nosūtīto notikumu atbilde.
<?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;
}
Svarīgi: stream: false joprojām var atgriezties SSE
stream: false izsaka pieprasīto atbildes režīmu. Saderīgs starpniekserveris vai maršrutēts augšup pa straumi joprojām var serializēt pabeigto atbilžu dzīves ciklu kā text/event-stream ar tādiem notikumiem kā:
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",...}}
Neskrien json_decode() tieši uz visa ķermeņa, līdz esat noteicis, vai tas ir JSON vai SSE. SSE gadījumā savākšana ir pabeigta response.output_item.done preces un fināls response.completed metadati.
Avota validācija
Pašreizējās informācijas darbplūsmās pirms atbildes pieņemšanas pārbaudiet visu tālāk norādīto.
- Vismaz viens pabeigts
web_search_callpastāv. - Vismaz viens URL pastāv
web_search_call.action.sourcesvai anoutput_textURL citāts. - Galīgais atbildes statuss ir
completed. - Avoti apstiprina apgalvojumus, ko plānojat izmantot.
- Svarīgus faktus vēlams apstiprināt no oficiāla vai primārā avota.
Neuzskatiet raitu atbildi kā pierādījumu tam, ka meklēšana ir notikusi. Programmiski pārbaudiet instrumenta izvadi.
Apstrādājot kļūdas un atkārtojot mēģinājumus
- Saglabāt atbildes korpusus, kas nav 2xx. Tie satur noderīgus pakalpojumu sniedzēja validācijas ziņojumus.
- Nemēģiniet vēlreiz HTTP
400nemainot noraidīto pieprasījumu. - Mēģiniet vēlreiz pārejošs
429,502,503,504, un transportēšanas kļūmes ar eksponenciālu atkāpšanos un nervozitāti. - Izmantojiet idempotences atslēgu savā lietojumprogrammā, ja atkārtots mēģinājums var izraisīt biznesa darbību.
- Pielāgojiet lietojumprogrammu, PHP, reversā starpniekservera un vārtejas taimautus ilgiem meklēšanas pieprasījumiem.
- Nekad nepakļaujiet gala lietotājiem API atslēgas vai uzņemtās neapstrādātās slodzes.