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

  • tools pune la dispoziția modelului căutarea găzduită.
  • tool_choice: required previne 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: false solicită 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:

  1. Cel puțin unul finalizat web_search_call există.
  2. Există cel puțin o adresă URL în web_search_call.action.sources sau un output_text citare URL.
  3. Starea finală a răspunsului este completed.
  4. Sursele susțin afirmațiile pe care intenționați să le utilizați.
  5. 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 400 fă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.

Pagini înrudite