Возможности
ВОЗМОЖНОСТИ

Поиск в интернете

Server-side инструмент — модель сама ищет в интернете и пишет ответ с источниками. Никакого ручного исполнения у вас на стороне.

В отличие от обычных функций (Вызов инструментов), которые исполняете вы, веб-поиск — это server-side инструмент: вы добавляете его в tools[], модель сама вызывает поиск, получает результаты, формирует ответ и присылает его одним финальным сообщением. Вашему клиенту ничего исполнять не нужно — никаких лишних round-trip'ов и role: "tool" сообщений.

Работает с большинством моделей. Под капотом наш слой маршрутизации выбирает поисковый движок — у части моделей он встроен в саму модель, у части подключается отдельно. Для вас разница только в наборе источников, которые попадут в ответ.

Минимальный пример

Передайте в tools[] элемент с типом hubris:web_search (это наш namespace для server-side инструментов — отличает их от обычных function-tool):

curl -s https://api.hubris.pw/v1/chat/completions \
  -H "Authorization: Bearer sk-gw-..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "openai/gpt-4o-mini",
    "messages": [
      {"role": "user", "content": "Что произошло на рынке российских облигаций на прошлой неделе?"}
    ],
    "tools": [
      { "type": "hubris:web_search" }
    ]
  }'

Ответ — обычный chat-ответ, но с дополнительным полем annotations[], где перечислены источники:

{
  "id": "chatcmpl-...",
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "На прошлой неделе ставка ОФЗ выросла до 16.3% на длинных бумагах...",
      "annotations": [
        {
          "type": "url_citation",
          "url_citation": {
            "url": "https://www.cbr.ru/press/event/?id=12345",
            "title": "ЦБ РФ — Информация о денежно-кредитной политике",
            "content": "Совет директоров Банка России 17 мая 2026 года...",
            "start_index": 42,
            "end_index": 87
          }
        }
      ]
    },
    "finish_reason": "stop"
  }],
  "usage": {
    "prompt_tokens": 4521,
    "completion_tokens": 318,
    "total_tokens": 4839
  }
}

tool_calls остаётся пустым — поиск отрабатывается полностью на стороне нашего слоя маршрутизации, наружу не вылезает.

Параметры поиска

Можно тонко настроить поведение:

{
  "type": "hubris:web_search",
  "parameters": {
    "max_results": 5,
    "search_context_size": "medium"
  }
}
ПараметрЗначенияОписание
max_results1–10 (по умолчанию 5)Сколько источников использовать для составления ответа
search_context_size"low" / "medium" / "high"Сколько контекста из каждого источника подгружать в промпт. Больше — точнее ответ, но дороже

Все параметры опциональны.

Аннотации

annotations[] приходит на сообщении в формате:

{
  "type": "url_citation",
  "url_citation": {
    "url": "https://...",
    "title": "Заголовок страницы",
    "content": "Релевантный фрагмент текста источника",
    "start_index": 42,
    "end_index": 87
  }
}

start_index / end_index — позиция (в символах) в message.content, к которой относится цитата. Удобно для подсветки источников рядом с текстом в UI.

Стриминг

stream: true работает. Особенность: annotations[] приходит полным массивом сразу в первом чанке (внутри delta.annotations), ещё до самого текста. Дальше идут обычные текстовые delta.content чанки.

data: {"choices":[{"index":0,"delta":{"role":"assistant","annotations":[{"type":"url_citation","url_citation":{...}}]}}]}
data: {"choices":[{"index":0,"delta":{"content":"На прошлой "}}]}
data: {"choices":[{"index":0,"delta":{"content":"неделе ставка ОФЗ "}}]}
...
data: {"choices":[{"index":0,"finish_reason":"stop"}]}
data: [DONE]

Это удобно: в UI можно сразу показать «по N источникам» — а текст подгружать по мере прихода.

OpenAI SDK

В официальных SDK OpenAI поле tools поддерживается, но проверки типа стандартные function — поэтому server-tool придётся передавать через extra_body (Python) или приведением типов (TypeScript).

Python:

from openai import OpenAI

client = OpenAI(
    base_url="https://api.hubris.pw/v1",
    api_key="sk-gw-...",
)

resp = client.chat.completions.create(
    model="openai/gpt-4o-mini",
    messages=[{"role": "user", "content": "Курс доллара ЦБ на сегодня?"}],
    extra_body={
        "tools": [
            {"type": "hubris:web_search", "parameters": {"max_results": 3}}
        ]
    },
)

msg = resp.choices[0].message
print(msg.content)

annotations = msg.model_extra.get("annotations", [])
for ann in annotations:
    cit = ann["url_citation"]
    print(f"  - {cit['title']}: {cit['url']}")

TypeScript:

import OpenAI from 'openai';

const client = new OpenAI({
  baseURL: 'https://api.hubris.pw/v1',
  apiKey: 'sk-gw-...',
});

const resp = await client.chat.completions.create({
  model: 'openai/gpt-4o-mini',
  messages: [{ role: 'user', content: 'Курс доллара ЦБ на сегодня?' }],
  tools: [
    { type: 'hubris:web_search', parameters: { max_results: 3 } },
  ],
} as any);

const msg = resp.choices[0].message;
console.log(msg.content);

const annotations = (msg as any).annotations ?? [];
for (const ann of annotations) {
  console.log(`  - ${ann.url_citation.title}: ${ann.url_citation.url}`);
}

Что важно знать

  • prompt_tokens будут раздуты. Результаты поиска инжектятся в промпт — это нормально, что на коротком запросе вернутся 3–10 тысяч prompt-токенов. Ответ модели опирается на эти источники.
  • tool_calls пустой. Не пытайтесь обработать веб-поиск как обычный function-call — поиск отрабатывает полностью на нашей стороне.
  • Совмещение с обычными функциями работает. В одном tools[] можно передать и server-tool, и свои function-tools — модель выберет, что вызывать.
  • /v1/responses со стримингом — известное ограничение. В non-stream ответе /v1/responses элемент в output[] приходит с типом hubris:web_search. В streaming-режиме на этом эндпоинте имя пока не нормализуется обратно — в SSE-событиях response.output_item.added / response.output_item.done префикс типа отличается от того, что вы отправили в запросе. Будет исправлено. На /v1/chat/completions (любой режим) и на non-stream /v1/responses всё корректно.

Биллинг

Веб-поиск тарифицируется не только токенами:

  • Токены — обычным образом (prompt + completion). С учётом раздутого prompt после инжекта результатов.
  • Плата за поиск — фиксированная сумма за запрос (зависит от модели и search_context_size).

В большинстве случаев бо́льшая часть стоимости запроса — это именно плата за поиск, не токены. Итоговая сумма по запросу видна в разделе «Расходы».

Списание происходит после успешного ответа. Транзакция атомарна — баланс, usage_log и движение по балансу пишутся одной БД-транзакцией.

Что дальше

Обновлено:

Поиск в интернете · Hubris