Responses API web_search
Χρησιμοποιήστε φιλοξενούμενη αναζήτηση ιστού με PHP cURL και αποκωδικοποιήστε με ασφάλεια απαντήσεις είτε JSON είτε SSE.
Responses API web_search
Χρησιμοποιήστε το φιλοξενούμενο web_search εργαλείο όταν μια απάντηση εξαρτάται από τις τρέχουσες δημόσιες πληροφορίες: πρόσφατες εκδόσεις, ενημερώσεις τεκμηρίωσης, τιμές, χρονοδιαγράμματα, κανονισμούς, αλλαγές προϊόντων ή άλλα στοιχεία που μπορεί να έχουν αλλάξει μετά την περίοδο εκπαίδευσης του μοντέλου.
Αυτή η σελίδα περιγράφει web_search επί POST /v1/responses. Είναι διαφορετικό από το αυτόνομο `POST /v1/web-search` τελικό σημείο.
Προτεινόμενο αίτημα
Στείλτε έναν κανονικό πίνακα εισόδου Responses API. Απαιτήστε το εργαλείο όταν η απάντηση πρέπει να γειωθεί σε ζωντανές πηγές.
{
"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
}
Γιατί αυτά τα πεδία έχουν σημασία
toolsκαθιστά διαθέσιμη τη φιλοξενούμενη αναζήτηση στο μοντέλο.tool_choice: requiredαποτρέπει μια απάντηση μόνο στη μνήμη όταν η ζωντανή επαλήθευση είναι υποχρεωτική.include: ["web_search_call.action.sources"]ζητά από το API να συμπεριλάβει τις διευθύνσεις URL που λαμβάνονται υπόψη από την κλήση αναζήτησης.stream: falseζητά μια απάντηση χωρίς ροή, αλλά οι συμβατές πύλες ενδέχεται να επιστρέψουν μια ροή συμβάντων Απαντήσεων. Ο πελάτης σας πρέπει να υποστηρίζει και τις δύο μορφές.
Μην προσθέτετε παραμέτρους δημιουργίας για συγκεκριμένο πάροχο, εκτός εάν το επιλεγμένο μοντέλο τις υποστηρίζει. Οι δρομολογημένοι πάροχοι ενδέχεται να απορρίψουν παραμέτρους όπως temperature, background, ή στοιχεία ελέγχου με διακριτικό εξόδου.
Ολοκληρωμένο παράδειγμα PHP cURL
Το ακόλουθο παράδειγμα αποστέλλει το αίτημα, διατηρεί το σώμα σφάλματος ανοδικής ροής και αποκωδικοποιεί είτε μια κανονική απόκριση JSON είτε μια απόκριση συμβάντων που απεστάλησαν από διακομιστή.
<?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;
}
Σπουδαίος: stream: false μπορεί ακόμα να επιστρέψει SSE
stream: false εκφράζει τον τρόπο απόκρισης που ζητήθηκε. Ένας συμβατός διακομιστής μεσολάβησης ή δρομολογημένος ανάντη μπορεί ακόμα να σειριοποιήσει τον ολοκληρωμένο κύκλο ζωής των απαντήσεων ως text/event-stream με εκδηλώσεις όπως:
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",...}}
Μην τρέχετε json_decode() απευθείας σε ολόκληρο το σώμα μέχρι να προσδιορίσετε εάν είναι JSON ή SSE. Για SSE, η συλλογή ολοκληρώθηκε response.output_item.done είδη και το τελικό response.completed μεταδεδομένα.
Επικύρωση πηγής
Για τρέχουσες ροές εργασίας πληροφοριών, επικυρώστε όλα τα παρακάτω προτού αποδεχτείτε την απάντηση:
- Τουλάχιστον ένα ολοκληρωμένο
web_search_callυπάρχει. - Υπάρχει τουλάχιστον μία διεύθυνση URL
web_search_call.action.sourcesή έναoutput_textΠαράθεση διεύθυνσης URL. - Η τελική κατάσταση απάντησης είναι
completed. - Οι πηγές υποστηρίζουν τους ισχυρισμούς που σκοπεύετε να χρησιμοποιήσετε.
- Τα σημαντικά γεγονότα επιβεβαιώνονται κατά προτίμηση από επίσημη ή πρωτογενή πηγή.
Μην αντιμετωπίζετε μια ευχάριστη απάντηση ως απόδειξη ότι η αναζήτηση ολοκληρώθηκε. Ελέγξτε την έξοδο του εργαλείου μέσω προγραμματισμού.
Διαχείριση σφαλμάτων και επανάληψη
- Διατηρήστε τα σώματα απόκρισης που δεν είναι 2xx. Περιέχουν χρήσιμα μηνύματα επικύρωσης παρόχου.
- Μην δοκιμάσετε ξανά το HTTP
400χωρίς αλλαγή του απορριφθέντος αιτήματος. - Δοκιμάστε ξανά παροδικό
429,502,503,504, και αστοχίες μεταφοράς με εκθετική υποχώρηση και jitter. - Χρησιμοποιήστε ένα κλειδί αδυναμίας στη δική σας εφαρμογή όταν μια επανάληψη θα μπορούσε να προκαλέσει μια επιχειρηματική ενέργεια.
- Διατηρήστε τα χρονικά όρια εφαρμογής, PHP, αντίστροφου διακομιστή μεσολάβησης και πύλης ευθυγραμμισμένα για αιτήματα μεγάλης διάρκειας αναζήτησης.
- Ποτέ μην εκθέτετε κλειδιά API ή καταγεγραμμένα ακατέργαστα ωφέλιμα φορτία στους τελικούς χρήστες.