В прошлом примере я рассказал о том, как можно использовать YDB в качестве векторной базы знаний. Сегодня расскажу про то, как использовать Yandex Embedder.
Но сначала несколько слов о том, почему Embedder — это очень важно.
Итак, у нас есть задача: есть некоторая внутренняя система (у моем случае — YouTrack), есть ИИ (не важно какой). Я хочу получать ответы от ИИ с использованием данных из внутренней системы. Классическое решение — это выгрузка данных из внутренней системы в RAG и использование этих данных для контекста в запросе к ИИ. В качестве RAG используется векторная база данных (например YDB). Важным моментом является то, что в контекст запроса к LLM передается не вся база — а только некоторое количество наиболее близких документов («близость» как раз определяется сравнением векторов). То есть, работает так:
мы пишем запрос
по этому запросу находим релевантные документы в RAG
отправляем запрос в LLM передавая найденные документы
Так вот. То, что вам ответит ИИ зависит от того, что вы найдете в RAG. А это в свою очередь зависит от того, насколько «хорошо» будут сгенерированы векторы. За генерацию векторов как раз и отвечает Embedder.
В моем прошлом примере класс сохранения данных в YDB использовал простой embedder из библиотеки ‘@xenova/transformers’ и результат поиска, если честно, был так себе. Поэтому следующей задачей стало использование уже более «правильного» Embedder-а. В моем случае, так как я в данном прототипе ориентирован на максимально использование сервисов Yandex Cloud то и выбор пал на Yandex Embedder. Доступные модели можно посмотреть в AI Studio в каталоге моделей.

Нам нужна та, которая называется Yandex Text embedding (query) 1
Ниже приведен код класса для работы с Yandex Embedder:
// yandex-embedder.js export class YandexEmbedder { constructor(options = {}) { this.apiKey = options.apiKey; this.baseURL = options.baseURL; this.modelUri = options.modelUri; this.requestsPerSecond = options.requestsPerSecond || 8; this.delayBetweenRequests = 1000 / this.requestsPerSecond; this.lastRequestTime = 0; console.log(‘🔧 Yandex Embedder configured:’); console.log(‘ — Model Uri:’, this.modelUri); console.log(‘ — Base URL:’, this.baseURL); console.log(‘ — delayBetweenRequests:’, this.delayBetweenRequests + ‘ms’); } async generateEmbedding(text) { const now = Date.now(); const timeSinceLastRequest = now — this.lastRequestTime; // console.log(Date.now() + » timeSinceLastRequest: » + timeSinceLastRequest); if (timeSinceLastRequest < this.delayBetweenRequests) { const waitTime = this.delayBetweenRequests — timeSinceLastRequest; await new Promise(resolve => setTimeout(resolve, waitTime)); } // console.log(Date.now() + » After timer:»); this.lastRequestTime = Date.now(); return await this.makeRequest(text); } async makeRequest(text) { const controller = new AbortController(); try { // console.log(`🔍 Generating embedding for text: «${text.substring(0, 50)}${text.length > 50 ? ‘…’ : »}»`); const response = await fetch(this.baseURL, { method: ‘POST’, headers: { ‘Authorization’: `Api-Key ${this.apiKey}`, ‘Content-Type’: ‘application/json’ }, body: JSON.stringify({ modelUri: this.modelUri, text: text }) }); if (!response.ok) { const errorText = await response.text(); throw new Error(`Yandex API error: ${response.status} — ${errorText}`); } const data = await response.json(); if (!data.embedding) { throw new Error(‘No embedding in Yandex API response’); } // console.log(`✅ Embedding generated, dimension: ${data.embedding.length}`); return data.embedding; } catch (error) { console.error(‘❌ Error generating Yandex embedding:’, error.message); throw error; } } // Batch генерация эмбеддингов async generateEmbeddings(texts) { console.log(`🔍 Generating embeddings for ${texts.length} texts…`); const embeddings = []; for (let i = 0; i < texts.length; i++) { try { const embedding = await this.generateEmbedding(texts[i]); embeddings.push(embedding); if ((i + 1) % 10 === 0 || i === texts.length — 1) { console.log(`📊 Progress: ${i + 1}/${texts.length}`); } } catch (error) { console.error(`❌ Failed to generate embedding for text ${i + 1}:`, error.message); embeddings.push(null); } } console.log(`✅ Generated ${embeddings.filter(e => e !== null).length}/${texts.length} embeddings`); return embeddings; } }
При инициализации классу необходимо передать несколько параметров:
apiKey — ключ созданный для сервисного аккаунта от имени которого вы будете обращаться к сервису. Генерируется, например, в консоли Yandex Cloud
baseURL — URL сервиса — чаще всего это https://llm.api.cloud.yandex.net/foundationModels/v1/textEmbedding
modelUri — указатель на используемую модель. Чаще всего будет emb://${YourYandexCloudFolderID}/text-search-query/latest — но тоже могут быть нюансы (теоретически вы можете развернуть свою версию модели)
requestsPerSecond — количество запросов в сек, которые можно отправить к сервису.
Вот об этом чуть подробней. Сервис Яндекса обрабатывает не более 10-ти запросов в секунду. Так что запросы надо выстраивать в очередь. Реализация очереди может быть самой разной. Так как в моем случае проект в стадии «прототипа» — то я просто поставил таймер между запросами, но в production решении конечно надо придумывать что-то более надежное.
Могу сказать что с использованием Yandex Embedder-а качество ответов, которые стала выдавать мне программа стало прям на порядок лучше.
Надеюсь, этот пример будет кому-то полезен и съэкономит время. Только учтите — это именно пример на уровне прототипа.
Источник: habr.com



























