Голос и изображения
Поиск по записи и по картинке — POST /voice и POST /image, оба сначала превращают вход в текстовый запрос.
Оба эндпоинта решают одну задачу: в индексе текст, а у пользователя — что-то другое. Платформа преобразует вход в запрос и выполняет обычный поиск с фильтром тенанта, поэтому фильтры, фасеты и квоты ведут себя так же, как при набранном запросе.
Голос
curl -sS -X POST "$AAC_API/indexes/documentation/voice" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile audio clip.webm \
'{ audio: ($audio | @base64), mime_type: "webm", language: "ru" }')"{
"index": "documentation",
"mode": "ai",
"transcript": "сколько стоит тариф pro",
"found": 3,
"hits": [
{
"id": "pricing",
"score": 2180,
"document": { "title": "Тарифы", "url": "https://example.com/pricing" },
"highlights": []
}
],
"latency_ms": 940,
"charged_cents": 0,
"request_id": "req_1a3c5e7b9d0f2a4c6e8b0d1f"
}| Поле | Тип | Комментарий |
|---|---|---|
audio | string | Аудио в base64, от 16 символов до 8 МБ |
mime_type | string | wav (по умолчанию), mp3, webm, ogg, m4a |
language | string ≤ 8 | Подсказка языка в формате ISO |
model_id | string ≤ 128 | Голосовая модель движка; использует настроенную голосовую модель |
query_by | string | Поля для получившегося запроса |
Два режима работы
- Распознавание ИИ (
mode: "ai"). Если функция настроена, платформа распознаёт аудио и ищет по полученному тексту. Ответ содержитtranscriptи обычные результаты поиска. - Управляемое распознавание (
mode: "native"). Настроенная голосовая модель распознаёт аудио. Полученный текст проходит общий поиск с проверкой организации, доступа к документам, приватных полей и квот. Ответ использует контракт поиска приложения:
{
"index": "documentation",
"mode": "native",
"transcript": "example query",
"found": 2,
"hits": [],
"result": { "transcription": "example query", "results": [{ "found": 2, "hits": [] }] },
"request_id": "req_7c9e1b3d5f7a9c1e3b5d7f9a"
}Ошибки
| Статус | Код | Причина |
|---|---|---|
422 | EMPTY_TRANSCRIPTION | Аудио декодировано, но речи в нём нет |
422 | INVALID_AUDIO | Полезную нагрузку не удалось декодировать как аудио |
413 | AUDIO_TOO_LARGE | Фрагмент превышает допустимый размер |
503 | VOICE_MODEL_NOT_CONFIGURED | Голосовой поиск не настроен для этого развёртывания |
Голосовой поиск списывает одну единицу поиска, а транскрипт попадает в журнал аудита — не отправляйте аудио с секретами, которые не хотели бы видеть в логах.
Изображение
curl -sS -X POST "$AAC_API/indexes/documentation/image" \
-H "Authorization: Bearer $AAC_KEY" \
-H "Content-Type: application/json" \
-d '{
"image": "https://example.com/uploads/price-table.png",
"hint": "скриншот таблицы тарифов",
"query_by": "title,content",
"per_page": 5
}'{
"index": "documentation",
"mode": "ai",
"description": "таблица сравнения тарифов с помесячной оплатой",
"model": "gpt-4o-mini",
"found": 2,
"hits": [
{
"id": "pricing",
"score": 1540,
"document": { "title": "Тарифы", "url": "https://example.com/pricing" },
"highlights": []
}
],
"latency_ms": 1180,
"charged_cents": 0,
"request_id": "req_3e5b7d9f1a3c5e7b9d1f3a5c"
}| Поле | Тип | Комментарий |
|---|---|---|
image | string | Base64 или http(s)-URL, от 16 символов до 12 МБ |
mime_type | string | Опционально; для URL определяется автоматически |
hint | string ≤ 200 | Дополнительный контекст для модели («какой тариф выделен?») |
query_by | string | Поля для сгенерированного запроса |
filter_by | string ≤ 1000 | Сузить набор кандидатов до ранжирования |
per_page | int 1–50 | По умолчанию 10, жёсткий потолок 50 |
description — промежуточный запрос, который построила модель. Его стоит показывать в отладочной панели, писать в лог или возвращать пользователю как «ищем: …» — тогда странная выдача становится объяснимой.
Ошибки
Поиску по изображению нужен провайдер, запасного пути через движок нет:
{
"error": {
"code": "AI_NOT_CONFIGURED",
"message": "Set OPENAI_API_KEY to use image search",
"request_id": "req_9f1b3d5c7a9e1b3d5f7c9a1b"
}
}Это 503. Слишком большой или некорректный image отклоняется валидацией как 422 VALIDATION_ERROR ещё до вызова провайдера.
Что это и что это не
- Это текстовый поиск с другим вводом. Изображение описывается словами и сопоставляется с текстом. Скриншот графика не найдёт «документ с похожим графиком».
- Это не CLIP и не мультимодальный индекс. Векторы изображений не хранятся. Если нужна визуальная близость, держите свой конвейер векторов и используйте
vector_queryв/search. - Стоимость разная. Голосовые и графические вызовы списывают единицу поиска плюс AI-токены за описание или транскрипт;
charged_centsпоказывает списание с кошелька.
Как встроить в интерфейс
Браузерную часть для голоса умеет готовый виджет: включите Voice search в панели встраивания, и в строке поиска появится виджет voiceSearch из InstantSearch, использующий Speech Recognition браузера — на этом пути аудио вообще не покидает клиент. Для своего интерфейса:
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
const recorder = new MediaRecorder(stream, { mimeType: "audio/webm" });
const chunks: Blob[] = [];
recorder.ondataavailable = (event) => chunks.push(event.data);
recorder.onstop = async () => {
const base64 = await new Promise<string>((resolve) => {
const reader = new FileReader();
reader.onload = () => resolve(String(reader.result).split(",")[1] ?? "");
reader.readAsDataURL(new Blob(chunks, { type: "audio/webm" }));
});
const response = await fetch("/api/v1/indexes/documentation/voice", {
method: "POST",
headers: { Authorization: `Bearer ${sessionKey}`, "Content-Type": "application/json" },
body: JSON.stringify({ audio: base64, mime_type: "webm" }),
});
const { transcript, hits } = await response.json();
render(transcript, hits);
};
recorder.start();Никогда не вызывайте этот эндпоинт из браузера с ключом aac_live_…: выпускайте ограниченный ключ на сервере (Сессия встраивания) или проксируйте вызов через бэкенд — для голоса и изображений это лучший вариант, потому что нагрузки большие, а вызовы дорогие.
RAG-ассистент
Ответы со ссылками на источники, диалоговый ассистент со стримингом и нативная беседа на стороне движка.
A/B-эксперименты
Поисковые эксперименты делят живой трафик между двумя профилями поиска с закреплением за посетителем, показывают клики и uplift и позволяют принять или откатить победителя. Также описано старое разделение через settings.abTest.