Справочник
Документация API
Как подключить внешнее приложение к RAG-сервису: авторизация, поиск, чат-стриминг, работа с данными.
Подключение
Базовый URL https://rag.vai8.io
Резервный адрес https://rag.89.124.93.204.sslip.io
Внутри сервера http://127.0.0.1:8100
Авторизация Authorization: Bearer rag_live_… (раздел «API-доступ»)
Тип содержимого Content-Type: application/json
Ключ может быть общим (доступ ко всем коллекциям) или ограниченным списком коллекций — это выбирается при создании в разделе «API-доступ». Запрос ограниченным ключом к чужой коллекции возвращает 403 . Если в /api/search или /api/chat не передан collection_id , ключ с одной коллекцией подставляет её автоматически, а ключ с несколькими получит 400 «укажите collection_id». Изменение настроек (PUT /api/settings ) доступно только из панели управления.
Поиск — POST /api/search
Параметр Тип Описание
query string Поисковый запрос. обязательный
collection_id number Ограничить поиск одной коллекцией (опционально)
top_k number Сколько результатов вернуть, 1–50; по умолчанию 10
mode string hybrid | vector | bm25; по умолчанию hybrid
Пример запроса:
Копировать
POST /api/search
Authorization: Bearer rag_live_ВАШ_КЛЮЧ
Content-Type: application/json
{"query": "передатчик видео 50 км", "top_k": 5, "mode": "hybrid"}
Пример ответа:
Копировать
{
"took_ms": 42,
"results": [
{
"chunk_id": 1287,
"text": "COFDM-передатчик видео дальностью до 50 км…",
"score": 0.7312,
"scores": {"vector": 0.71, "bm25": 0.54},
"sku": "TDN-VTX-50",
"product_name": "Передатчик видео 50 км",
"meta": {"price_min": 49900, "slug": "vtx-50km", "image": "https://…/vtx.jpg"},
"document": {"id": 12, "title": "Каталог ТДН"}
}
]
}
RAG-чат — POST /api/chat
Тело запроса: {query, collection_id?, model?, history?} , где history — массив {role, content} . Ответ приходит SSE-потоком: сначала событие sources (найденные фрагменты, до генерации), затем серия delta {text} , в конце done {model} или error {message} .
Копировать
event: sources
data: {"sources":[{"n":1,"score":0.73,"text":"…","document":{"id":12,"title":"Каталог ТДН"}}]}
event: delta
data: {"text":"Для дальности 50 км подойдёт…"}
event: done
data: {"model":"qwen2.5-32b"}
Данные
Эндпоинт Описание
GET /api/collections Список коллекций
GET /api/collections/{id}/documents?offset&limit&q Документы коллекции: постранично, фильтр по названию q
GET /api/documents/{id}/chunks Чанки документа
POST /api/collections/{id}/documents Загрузка: JSON {title, text, doc_type} или multipart-файл → {job_id}
GET /api/jobs/{id} Статус индексации: queued / running / done / error
Коды ошибок
Код Значение
400 Ошибка валидации запроса
401 Нет ключа или ключ неверный / отозван
403 Операция доступна только из панели управления
404 Объект не найден
409 Конфликт: имя коллекции занято или не совпадает модель эмбеддингов
429 Слишком много попыток входа (rate-limit)
Ограничения
Файлы до 20 МБ, форматы: txt, md, html, json.
top_k — не больше 50.
История чата — учитываются последние 6 сообщений.
Эмбеддинги локальные: multilingual MiniLM, dim 384.
Гибридный поиск = RRF (вектор + BM25) + приоритет точного совпадения SKU.
Пример: Python (httpx)
Копировать
import httpx, json
BASE = "https://rag.vai8.io"
HEADERS = {"Authorization": "Bearer rag_live_ВАШ_КЛЮЧ"}
# Поиск
r = httpx.post(f"{BASE}/api/search", headers=HEADERS,
json={"query": "передатчик видео 50 км", "top_k": 5})
for hit in r.json()["results"]:
print(hit["score"], hit["sku"], hit["text"][:80])
# RAG-чат (SSE-стрим)
with httpx.stream("POST", f"{BASE}/api/chat", headers=HEADERS,
json={"query": "что посоветуете для 50 км?"},
timeout=120) as resp:
event = None
for line in resp.iter_lines():
if line.startswith("event:"):
event = line[6:].strip()
elif line.startswith("data:"):
data = json.loads(line[5:])
if event == "delta":
print(data["text"], end="", flush=True)
elif event == "error":
print("Ошибка:", data["message"])
Пример: JavaScript (fetch)
Копировать
const BASE = 'https://rag.vai8.io';
const HEADERS = {
'Authorization': 'Bearer rag_live_ВАШ_КЛЮЧ',
'Content-Type': 'application/json',
};
// Поиск
const search = await fetch(BASE + '/api/search', {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ query: 'передатчик видео 50 км', top_k: 5 }),
});
const { results } = await search.json();
// RAG-чат (чтение SSE-потока)
const res = await fetch(BASE + '/api/chat', {
method: 'POST', headers: HEADERS,
body: JSON.stringify({ query: 'что посоветуете для 50 км?' }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buf = '', answer = '';
for (;;) {
const { done, value } = await reader.read();
if (done) break;
buf += decoder.decode(value, { stream: true });
let i;
while ((i = buf.indexOf('\n\n')) !== -1) {
const block = buf.slice(0, i);
buf = buf.slice(i + 2);
const event = (block.match(/^event: ?(.+)$/m) || [])[1] || 'message';
const raw = (block.match(/^data: ?(.+)$/m) || [])[1];
if (!raw) continue;
const data = JSON.parse(raw);
if (event === 'delta') answer += data.text;
if (event === 'error') console.error(data.message);
}
}
console.log(answer);