При разработке ИИ чатов существует два способа интеграции внешних данных: RAG хранилища и Fine tuning. Для не технаря отличия не очевидны, я столкнулся с мнением менеджера проекта, что первое это новая версия второго. Это не так. Поэтому, я сделал short summary, чтобы по существу изложить плюсы и минусы двух решений
Что такое RAG?
Языковые модели умеют запускать python/javascript
функции через tool_calls. Делается такая функция, ей на вход аргумент search
import { addTool } from "agent-swarm-kit";
addTool({
type: "function",
call: async ({ params }) => {
console.log(params.search) // Строка, может содержать имя телефона, его модель,
// его характеристики. Человеческими словами
т
},
function: {
name: ToolName.SearchPhoneTool,
description: "Allows finding a phone using contextual search",
parameters: {
type: "object",
properties: {
search: {
type: "string",
description:
"A set of keywords for embedding search. Write the query in Russian",
},
},
required: ["search"],
},
},
});
Далее эта строка search
передается в базу данных. База данных делает следующее
const createEmbedding: async (text: string): number[] => {
//
// В проде лучше использовать OpenAI
// https://platform.openai.com/docs/guides/embeddings/embedding-models
//
const { embedding } = await ollama.embeddings({
model: "nomic-embed-text",
prompt: text,
});
return embedding;
};
const calculateSimilarity = async (a: number[], b: number): number => {
const cosineSimilarity = 0.42; // Схожесть двух embedding вычисляется сложной
// математической формулой легко генерируемой
// в Grok...
//
// Люди не нужны, особенно профессора вышмата
return cosineSimilarity;
};
...
const sortedItems = new SortedArray<IStorageItem>();
const searchVector = await createVector(filterData.search);
for (const item of data) {
//
// Если тупо склеить строки это исказит смысл и данные не найдутся.
// Соответственно, для ИИ поиска вычислять вектор нужно на каждую
// колонку базы, по которой будет поиск
//
const modelVector = await createEmbedding(item.model);
const descriptionVector = await createEmbedding(item.description);
const titleVector = await createEmbedding(item.title);
const [
modelSimilarify,
descriptionSimilarify,
titleSimilarify,
] = await Promise.all([
calculateSimilarity(searchVector, modelVector),
calculateSimilarity(searchVector, descriptionVector),
calculateSimilarity(searchVector, titleVector),
]);
const similarity = Math.max(
modelSimilarify,
descriptionSimilarify,
titleSimilarify
);
//
// Фокус: векторный поиск технически не может точно сказать
// то ли он нашел. Поэтому, в выборку поподает отсортированный
// ТОП элементов по убыванию `cosineSimilarity`
//
if (similarity > VECTOR_SEARCH_SIMILARITY) {
sortedItems.push(item, similarity);
}
}
return sortedItems.toArray();
Итого, если в базу запросить телефон (избежав англицизмы) под именем Гугол Пихель, будет успешно найден Google Pixel 8 Pro, так как при поиске используется modelVector
const createEmbedding = async (text: string) => {
const words = text.toLowerCase().trim().split(/s+/);
const vector = new Float32Array(4);
words.forEach((word, index) => {
let sum = 0;
for (let i = 0; i < word.length; i++) {
sum += word.charCodeAt(i) / 1000;
}
vector[index % 4] += sum / (word.length || 1);
});
const magnitude = Math.sqrt(vector.reduce((sum, val) => sum + val * val, 0));
return await Array.fromAsync(
magnitude ? vector.map((v) => v / magnitude) : vector
);
};
Реализовать такой поиск можно без ИИ вообще, использовав как плоскость координат для вектора номер символа в таблице кодировки. Если вы понимаете этот код, можете претендовать на зарплату 6 тысяч долларов так как вы LLM Engineer
Что такое Fine Tuning?
Fine tuning это когда мы пишем сегмент переписки с желательным и не желательным ответом и дообучаем на этом языковую модель. Детальная инструкция как дообучить модель вы можете посмотреть по ссылке

Заполняете переписку инструментом по ссылке выше, загружаете её на сайт OpenAI и готово… К слову (далее уделено отдельное внимание), можно генерировать синтетические данные переписок по базе, и обновлять знания чата фоновым скриптом:
Плюсы и минусу двух методов
Тезисы, исходя из которых я делаю вердикт
RAG подразумевает tool_call, помимо оплаты генерации embedding.
– Это не ИИ вовсе, а обычная реляционная/nosql база данных
– Это дороже в эксплуатации так как генерация вектора на каждый поисковой запрос пользователя в RAG базу платная (см генерация embeddings)
– Это дороже в поддержке, так как чтобы сменить модель embeddings придется заного генерировать все векторные индексы. Они не универсальные, это вендор лок. 20 колонок поиска – 20 запросов в OpenAI на create/update одной строки
– Это дороже в программировании, так как это нужно программировать и администрировать в класическом понимании (DevOps, DBA)
– Это медленней на порядок, так как tool_calling подразумевает запуск LLM модели второй раз после получения ответа
– Это медленно на долгую перспективу, так как Embedding поиск подразумевает большой трафик памяти (2 миллиона записей будут обрабатываться 30 секунд)
– Это плохо применимо к русскому языку (или платите деньги яндексу), так как nomic-embed-text релизнулся 2023-11-01 после одной всем известной ситуации
Fine tuning подразумевает, что думает сам ИИ, а не база данных
– Малому и среднему бизнесу дешего взять студента текстовика, который подпишется работать дешего, а может и бесплатно. Дно курсов программирования тут работодателю на руку
– Базу данных не нужно мониторить на предмет поломки в сравнении с RAG, так как её нет: нейронка ничего не пишет на жесткий диск вовсе, а вся работает в оперативе видеокарты как stateless.
– Обновление дообучение модели можно автоматизировать скриптом
Вердикт
Если вы разрабатываете новый продукт, сфокусируйтесь на fine tuning. Начать можно бесплатно, если запрячь студентов. Как будут собраны структурированные данные в количестве, подразумевающем серьезность намерений, можно автоматизировать процесс актуализации знаний модели через автоматизацию fine tuning
Если вы банк с деньгами и со стеком Java Enterprise, вам ничего не остается, кроме как пытаться в RAG как адаптивную прослойку между ИИ и ванильным программированием
Автор: tripolskypetr