#Веб-поиск + LLM (RAG)

Связка `POST /v1/search` с `POST /v1/ai/chat/completions` для ответов LLM, опирающихся на свежие источники из интернета.

#Сценарий

  1. AI-агент получает вопрос от пользователя.
  2. Делает веб-поиск через /v1/search — получает массив results со ссылками на релевантные страницы.
  3. Передаёт LLM системный промпт с инструкцией цитировать источники по [N] и блок Sources: со списком id, title, url, content.
  4. LLM формирует ответ с маркерами [N], которые соответствуют results[].id.

#Полный пример

javascript
const VIBE_KEY = process.env.VIBE_API_KEY
const BASE = 'https://vibecode.bitrix24.tech'

// ── 1. Веб-поиск ────────────────────────────────────────────
const searchRes = await fetch(`${BASE}/v1/search`, {
  method: 'POST',
  headers: {
    'X-Api-Key': VIBE_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    query: 'обновления Битрикс24 за последний месяц',
    search_depth: 'advanced',
    max_results: 5,
    lang: 'ru',
  }),
})
const search = await searchRes.json()

// ── 2. Собираем блок источников для LLM ─────────────────────
const sourcesBlock = search.results
  .map((r) => `[${r.id}] ${r.title}\n${r.url}\n${r.content}`)
  .join('\n\n')

// ── 3. Запрос к LLM с инструкцией цитировать ────────────────
const chatRes = await fetch(`${BASE}/v1/ai/chat/completions`, {
  method: 'POST',
  headers: {
    'X-Api-Key': VIBE_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    model: 'bitrix/bitrixgpt-5.5',
    messages: [
      {
        role: 'system',
        content:
          'Отвечай только на основе источников ниже. Каждый факт сопровождай маркером [N], где N — id источника.',
      },
      {
        role: 'user',
        content: `Вопрос: ${search.query}\n\nИсточники:\n${sourcesBlock}`,
      },
    ],
  }),
})
const chat = await chatRes.json()

console.log(chat.choices[0].message.content)

#Стоимость одного цикла

  • Поиск: 5 Ꝟ для bitrix-search в режиме advanced. Для BYOK Tavily / Brave — 0 Ꝟ.
  • LLM: зависит от модели и количества токенов. Прайс — в AI Router.

#Какой провайдер выбрать

  • Русскоязычные источникиprovider: "bitrix-search" (используется по умолчанию). Выдача уже содержит синтезированный answer с маркерами [N] — иногда LLM-шаг можно пропустить.
  • Англоязычные источники с фильтрами по доменам или времениprovider: "tavily" (BYOK). Нужен токен Tavily — добавляется через `POST /v1/search/credentials`.
  • Англоязычный поиск без синтеза, для RAG поверх своей LLMprovider: "brave" (BYOK).

Подробнее о различиях — Web Search для AI.

#Потоковый вариант

Когда интерфейс показывает прогресс (chat-приложение, ассистент), используйте stream: true — события приходят по мере готовности.

javascript
const streamRes = await fetch(`${BASE}/v1/search`, {
  method: 'POST',
  headers: {
    'X-Api-Key': VIBE_KEY,
    'Content-Type': 'application/json',
    Accept: 'text/event-stream',
  },
  body: JSON.stringify({
    query: 'обновления Битрикс24 за последний месяц',
    search_depth: 'advanced',
    stream: true,
  }),
})

const reader = streamRes.body.getReader()
const decoder = new TextDecoder()
let buffer = ''

while (true) {
  const { value, done } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })

  let idx
  while ((idx = buffer.indexOf('\n\n')) !== -1) {
    const block = buffer.slice(0, idx)
    buffer = buffer.slice(idx + 2)

    const eventLine = block.match(/^event: (.+)$/m)
    const dataLine = block.match(/^data: (.+)$/m)
    if (!eventLine || !dataLine) continue

    const event = eventLine[1]
    const payload = JSON.parse(dataLine[1])

    if (event === 'thinking') process.stdout.write('.')
    if (event === 'answer_delta') process.stdout.write(payload.content)
    if (event === 'done') console.log('\nГотово')
  }
}

Полный список SSE-событий и полей — `POST /v1/search`.

#Смотрите также