Коли я вперше почув про технологію RAG (Retrieval-Augmented Generation), то був вражений її потенціалом. Це був той технологічний підхід, який міг вирішити багато проблем сучасних AI-систем, зокрема проблему “галюцинацій” у відповідях великих мовних моделей. Я вирішив не просто вивчити цю технологію теоретично, але й створити власну практичну реалізацію. Ця стаття – це мій досвід, виклики та знахідки на шляху розробки простої, але функціональної RAG системи.
Що таке RAG і чому це важливо?
RAG (Retrieval-Augmented Generation) – це архітектура, яка комбінує пошук інформації з зовнішніх джерел даних та генерацію тексту моделями штучного інтелекту. Замість того, щоб покладатися лише на знання, “вшиті” в нейронну мережу під час тренування, RAG-система спочатку шукає релевантну інформацію у зовнішній базі знань, а потім використовує цю інформацію як контекст для генерації відповіді.
Ця архітектура вирішує три важливі проблеми:
- Обмеженість знань – модель отримує доступ до зовнішніх джерел інформації
- Застарілість даних – база знань може постійно оновлюватися
- “Галюцинації” – модель відповідає на основі фактів, а не вигадує інформацію
Початок роботи: архітектурні рішення
Я вирішив створювати свою RAG-систему на JavaScript, оскільки це дозволяло мені легко інтегрувати її з веб-інтерфейсом у майбутньому. Архітектура системи складалася з трьох основних компонентів:
- База знань – колекція документів, яка зберігає інформацію для пошуку
- Компонент пошуку (Retriever) – функція, яка знаходить релевантні документи за запитом
- Компонент генерації (Generator) – функція, яка формує фінальну відповідь на основі знайденої інформації
Для спрощення я вирішив використовувати невелику статичну базу знань, що складається з коротких текстових документів про Україну.
Розробка пошукового компонента: виклики та рішення
Перший варіант пошукового алгоритму був дуже простим: я розбивав запит користувача на слова і шукав документи, які містять ці слова. Однак швидко виявилося, що такий підхід має суттєві обмеження:
- Проблема з службовими словами – слова “про”, “що”, “які”, “як” тощо створювали велику кількість помилкових збігів
- Проблема з відмінками слів – “Україна”, “України”, “Україні” система сприймала як різні слова
- Проблема з пунктуацією – символи
?,.,,заважали правильному розбору тексту
Для вирішення цих проблем я модифікував алгоритм:
function retrieveDocuments(query, documents, topK = 2) {
// Видаляємо зайві символи та розбиваємо на слова
const queryWords = query.toLowerCase().replace(/[?.,!;]/g, '').split(/\s+/);
// Обчислюємо релевантність для кожного документа
const scoredDocs = documents.map(doc => {
const docText = doc.text.toLowerCase();
let matchScore = 0;
// Рахуємо кількість слів з запиту, які є в документі
queryWords.forEach(word => {
// Пропускаємо короткі та службові слова
if (word.length <= 2 || ['про', 'що', 'які', 'яка', 'таке'].includes(word)) {
return;
}
if (docText.includes(word)) {
matchScore += 1;
}
});
return {
...doc,
score: matchScore
};
});
// Сортуємо за релевантністю та вибираємо topK результатів
return scoredDocs
.sort((a, b) => b.score - a.score)
.slice(0, topK)
.filter(doc => doc.score > 0);
}
Цей підхід значно покращив якість пошуку, але я розумів, що для реальної системи потрібні більш складні алгоритми, такі як TF-IDF або навіть векторне представлення тексту з використанням ембедінгів.
Компонент генерації відповідей: від простого до складного
Найпростіший підхід до генерації відповіді – це просто об’єднати знайдені документи і подати їх користувачу. Але це не дуже елегантно. Я вирішив створити функцію, яка імітує роботу мовної моделі, аналізуючи запит і формуючи осмислену відповідь:
function generateAnswer(query, retrievedDocs) {
// Якщо нічого не знайдено
if (retrievedDocs.length === 0) {
return "На жаль, я не маю достатньо інформації для відповіді на це питання.";
}
// Створюємо контекст з релевантних документів
const context = retrievedDocs.map(doc => doc.text).join(" ");
// Імітуємо роботу LLM шляхом аналізу запиту та відповідного форматування відповіді
if (query.toLowerCase().includes("найвища гора")) {
return "Найвища гора України - Говерла, що знаходиться в Карпатах на заході країни.";
}
else if (query.toLowerCase().includes("столиця")) {
return "Столицею України є місто Київ.";
}
else if (query.toLowerCase().includes("чорне море")) {
return "Чорне море розташоване на півдні України. Найбільшим українським портом на Чорному морі є Одеса.";
}
else if (query.toLowerCase().includes("україн")) {
return "Україна - найбільша країна, що повністю розташована в Європі. Її столиця - Київ...";
}
// Для інших запитів повертаємо загальну відповідь з використанням контексту
return На основі доступної мені інформації: ${context};
}
У реальній системі замість такої функції використовувався б API виклик до LLM, наприклад до GPT-4 або Claude, з передачею знайденого контексту разом із запитом.
Тестування та налагодження
Тестування виявило кілька цікавих моментів. Наприклад, запит “Розкажи про Україну” спочатку не давав жодних результатів, хоча в базі були документи про Україну. Проблема полягала в тому, що алгоритм шукав точний збіг слова “Україна”, а в тексті воно могло бути в різних відмінках.
Ще однією проблемою було те, що система іноді поверталa нерелевантні документи через випадкові збіги слів. Для вирішення цієї проблеми я додав фільтрацію службових слів та встановив мінімальний поріг релевантності.
Висновки та майбутні покращення
Створення власної RAG-системи, навіть простої, дало мені глибше розуміння того, як працюють сучасні AI-системи, що надають відповіді на основі фактів. Я зрозумів, що якість RAG-системи значною мірою залежить від трьох факторів:
- Якість та повнота бази знань
- Ефективність алгоритму пошуку
- Здатність моделі використовувати контекст
У майбутньому я планую покращити систему, впровадивши:
- Векторний пошук – представлення документів у вигляді векторів для пошуку за семантичною схожістю
- Багаторівневу базу знань – комбінування різних джерел інформації
- Інтеграцію з сучасними LLM через API – використання GPT-4 або Claude для генерації відповідей
- Персистентне зберігання даних – використання бази даних для зберігання великої кількості документів
Поради для тих, хто хоче створити власну RAG-систему
На основі мого досвіду, кілька порад для початківців:
- Почніть з малого – створіть просту систему і поступово її вдосконалюйте
- Зосередьтеся на алгоритмі пошуку – якість пошуку критично важлива для всієї системи
- Використовуйте реальні запити для тестування – це допоможе виявити слабкі місця вашої системи
- Не нехтуйте обробкою природної мови – врахуйте особливості мови (відмінки, службові слова тощо)
- Документуйте кожен крок – це допоможе вам швидше відлагоджувати систему
Розробка RAG-системи – це захоплюючий процес на перетині інформаційного пошуку та генеративного AI. Сподіваюся, мій досвід допоможе вам у створенні власних проектів у цій галузі!
Про автора
Я інженер та ентузіаст штучного інтелекту, який вірить у практичне навчання через створення реальних проектів. Цей проект – один із багатьох експериментів, спрямованих на розуміння передових технологій AI та їх застосування в реальному світі.