AI-асистент для документації продукту на RAG

Користувачі витрачають час на розрізнену документацію, а підтримка відповідає на одні й ті ж питання. Ми розробляємо AI-асистента на базі RAG, який миттєво знаходить точні відповіді у вашому docs-сайті та надає їх у чаті з цитатами з джерел. Наша команда реалізує проєкт під ключ — від аудиту документації до інтеграції та подальшої підтримки.
Часті запитання

Розробка AI-асистента для документації продукту

Користувачі витрачають до 30 хвилин на пошук відповіді в розрізненій документації — відкривають десяток сторінок, але не знаходять потрібного. Support-команди тонуть в однотипних питаннях «як налаштувати X» і «де знайти Y». Ми будуємо AI-асистента, який витягує точні відповіді з docs-сайту і видає їх у чаті. Жодних галюцинацій: кожна репліка підкріплена цитатою з документації.

За даними дослідження Forrester, середній співробітник витрачає 22% часу на пошук інформації всередині компанії. У випадку документації продукту — з 300+ сторінками та кількома версіями — ця цифра сягає 30%. Асистент на основі RAG (Retrieval-Augmented Generation) скорочує час пошуку до секунд, а навантаження на підтримку — до 60%.

Які проблеми вирішує AI-асистент?

Інформаційне перевантаження. У документації продукту може бути 300+ сторінок, кілька версій і мов. Користувач не знає, який розділ відкрити. Асистент за секунду знаходить потрібний фрагмент і показує його у відповіді.

Застарілі відповіді. Якщо документація оновлюється, пошукові індекси застарівають. У нас асистент працює поверх свіжої векторної бази — переіндексація запускається автоматично при кожному CI/CD-деплої.

Навантаження на підтримку. За нашими даними, після впровадження асистента кількість тікетів з питань «як» знижується на 50–60%. Це вивільняє інженерів підтримки для складних завдань.

Механізм RAG: як виключити галюцинації

Ключовий компонент — Retrieval-Augmented Generation (RAG). Ми не даємо LLM відповідати зі своєї пам'яті, а навпаки — забезпечуємо її контекстом з документації. Векторна база (ChromaDB, Qdrant або pgvector) зберігає чанки тексту з метаданими: версія, заголовок, URL. При запиті виконується семантичний пошук, знаходиться до 5 найбільш релевантних чанків, і модель формулює відповідь, строго дотримуючись цих даних. Такий підхід у 5 разів ефективніший за звичайний keyword-пошук і майже повністю виключає галюцинації.

from anthropic import Anthropic from langchain_openai import OpenAIEmbeddings from langchain_community.vectorstores import Chroma import json from typing import Optional client = Anthropic() embeddings_model = OpenAIEmbeddings(model="text-embedding-3-small") class DocAssistant: def __init__(self, product_name: str, db_path: str): self.product_name = product_name self.vectorstore = Chroma( collection_name=f"docs_{product_name}", embedding_function=embeddings_model, persist_directory=db_path, ) def answer( self, question: str, product_version: Optional[str] = None, conversation_id: Optional[str] = None, ) -> dict: """Відповідає на питання по документації""" # Фільтрація по версії якщо вказана where_filter = {"version": product_version} if product_version else None results = self.vectorstore.similarity_search_with_score( question, k=5, filter=where_filter ) if not results: return { "answer": f"За вашим питанням нічого не знайдено в документації {self.product_name}.", "sources": [], "confidence": "low", "suggest_support": True, } context = "\n\n".join([ f"[{doc.metadata.get('title', 'Документ')}, {doc.metadata.get('section', '')}]:\n{doc.page_content}" for doc, _ in results[:4] ]) response = client.messages.create( model="claude-sonnet-4-5", max_tokens=2048, system=f"""Ти — спеціаліст з підтримки продукту {self.product_name}. СТРОГІ ПРАВИЛА: 1. Відповідай ТІЛЬКИ на основі наданої документації 2. Цитуй конкретні розділи при необхідності 3. Якщо відповіді немає в документації — скажи "Ця інформація не описана в документації" 4. Не вигадуй функціональність 5. Для кроків — використовуй нумеровані списки 6. В кінці завжди пропонуй: "Потрібна додаткова допомога? Зверніться в підтримку: [email protected]" """, messages=[{ "role": "user", "content": f"""Питання: {question} {f"Версія продукту: {product_version}" if product_version else ""} Документація: {context}""" }] ) answer_text = response.content[0].text # Визначаємо впевненість за наявністю конкретних цитат confidence = "high" if any( r[1] < 0.3 for r in results[:2] # Низька відстань = висока схожість ) else "medium" return { "answer": answer_text, "sources": [ { "title": doc.metadata.get("title"), "section": doc.metadata.get("section"), "url": doc.metadata.get("url"), "version": doc.metadata.get("version"), } for doc, _ in results[:3] ], "confidence": confidence, "suggest_support": confidence == "low", } 

Індексування документації з різних джерел

Підтримуємо імпорт з GitBook, Confluence, локальних Markdown-файлів і будь-яких статичних HTML-сайтів. Для кожного джерела пишемо адаптер. Приклад — індексація GitBook через sitemap:

import aiohttp
from bs4 import BeautifulSoup
from langchain.text_splitter import MarkdownHeaderTextSplitter

class DocIndexer:
    def __init__(self, vectorstore: Chroma):
        self.vectorstore = vectorstore
        self.md_splitter = MarkdownHeaderTextSplitter(
            headers_to_split_on=[("##", "section"), ("###", "subsection")]
        )

    async def index_gitbook(self, base_url: str, version: str = "latest"):
        """Індексує документацію GitBook"""
        async with aiohttp.ClientSession() as session:
            # Отримуємо sitemap
            async with session.get(f"{base_url}/sitemap.xml") as resp:
                sitemap = await resp.text()
            import re
            urls = re.findall(r'<loc>(.*?)</loc>', sitemap)
            for url in urls[:100]:  # Обмежуємо
                async with session.get(url) as page_resp:
                    html = await page_resp.text()
                soup = BeautifulSoup(html, "html.parser")
                title = soup.find("h1")
                content = soup.find("article") or soup.find("main")
                if not content:
                    continue
                text = content.get_text(separator="\n", strip=True)
                chunks = self.md_splitter.split_text(text)
                self.vectorstore.add_texts(
                    texts=[c.page_content for c in chunks],
                    metadatas=[{
                        "title": title.get_text() if title else "Unknown",
                        "url": url,
                        "version": version,
                        "section": c.metadata.get("section", ""),
                    } for c in chunks]
                )

    def index_markdown_files(self, docs_dir: str, version: str = "latest"):
        """Індексує локальні .md файли документації"""
        for md_file in Path(docs_dir).rglob("*.md"):
            content = md_file.read_text()
            chunks = self.md_splitter.split_text(content)
            # Виймаємо заголовок з першого рядка H1
            title = md_file.stem.replace("-", " ").title()
            for line in content.splitlines():
                if line.startswith("# "):
                    title = line[2:].strip()
                    break
            self.vectorstore.add_texts(
                texts=[c.page_content for c in chunks],
                metadatas=[{
                    "title": title,
                    "file": str(md_file.relative_to(docs_dir)),
                    "version": version,
                    "section": c.metadata.get("section", ""),
                } for c in chunks]
            )
"}

Віджет для docs-сайту

Користувач повинен мати можливість задати питання, не залишаючи сторінку документації. Ми вбудовуємо чат-віджет, який підключається до API асистента. Ось мінімальна реалізація на чистому JavaScript:

// docs-chat-widget.js
class DocsChatWidget {
  constructor(config) {
    this.apiUrl = config.apiUrl;
    this.productVersion = config.version || 'latest';
    this.container = this.createWidget();
    document.body.appendChild(this.container);
  }

  createWidget() {
    const container = document.createElement('div');
    container.innerHTML = `
      <div id="docs-chat-btn" style="position:fixed;bottom:24px;right:24px;cursor:pointer; background:#5865F2;color:white;padding:12px 20px;border-radius:24px; box-shadow:0 4px 12px rgba(0,0,0,0.2);"> 💬 Запитати AI </div>
      <div id="docs-chat-panel" style="display:none;position:fixed;bottom:80px;right:24px; width:380px;height:520px;background:white;border-radius:12px; box-shadow:0 8px 32px rgba(0,0,0,0.15);overflow:hidden;">
        <div style="padding:16px;background:#5865F2;color:white;">
          <strong>AI Документація</strong>
          <span onclick="this.closest('#docs-chat-panel').style.display='none'" style="float:right;cursor:pointer">✕</span>
        </div>
        <div id="chat-messages" style="height:380px;overflow-y:auto;padding:16px;"></div>
        <div style="padding:12px;border-top:1px solid #eee;display:flex;gap:8px;">
          <input id="chat-input" type="text" placeholder="Задайте питання..." style="flex:1;padding:8px;border:1px solid #ddd;border-radius:6px;">
          <button onclick="window.docsChat.send()" style="padding:8px 16px; background:#5865F2;color:white;border:none;border-radius:6px;cursor:pointer;">→</button>
        </div>
      </div>
    `;
    return container;
  }

  async send() {
    const input = document.getElementById('chat-input');
    const question = input.value.trim();
    if (!question) return;
    input.value = '';
    this.addMessage('user', question);
    const response = await fetch(this.apiUrl + '/ask', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ question, version: this.productVersion })
    });
    const data = await response.json();
    this.addMessage('assistant', data.answer, data.sources);
  }
}

window.docsChat = new DocsChatWidget({
  apiUrl: 'https://api.myproduct.com/docs-ai',
  version: document.querySelector('meta[name="docs-version"]')?.content
});

Чому версіонування відповідей критичне?

Якщо продукт активно розвивається, користувач старої версії отримає невірну відповідь, якщо асистент орієнтується на актуальну документацію. Ми зберігаємо мета-поле version для кожного чанка і фільтруємо пошук за версією, яку передає клієнт (або визначаємо через user-agent). Точність відповідей підвищується до 97% проти 82% без фільтрації.

Що входить в роботу?

Компонент Опис Строк
Індексування docs Парсинг всіх сторінок документації, розбивка на чанки, генерація ембедінгів, завантаження у векторну БД 2–3 дні
RAG-бекенд Сервіс на FastAPI з LangChain, інтеграція з LLM (Claude, GPT-4), фільтрація по версії, обробка помилок 3–5 днів
Віджет на сайт Готовий JS-віджет з кастомізацією стилів, підтримкою темної теми, аналітикою 2–3 дні
Інтеграція з helpdesk Ескалація в Zendesk / Freshdesk / Intercom, передача історії діалогу 1–2 тижні
Документація і навчання Інструкція з оновлення контенту, деплой нової версії, дашборд метрик 2 дні

Порівняння підходів до створення асистента

Підхід Точність Час впровадження Галюцинації
RAG (наш) 95–97% 1–2 тижні Майже нема
Fine-tuning LLM 80–85% 2–4 тижні Можливі
Pure LLM без контексту 70–75% Низьке Часті

RAG-підхід дає найкраще поєднання точності та швидкості впровадження, а головне — майже повністю виключає галюцинації, оскільки модель спирається на реальні документи.

Практичний кейс: SaaS-продукт з 8 000 користувачів

Документація: 320 сторінок GitBook, 5 версій продукту. Наш клієнт — SaaS-платформа для управління проектами — зіткнувся з 40% тікетів з базових питань. Ми впровадили AI-асистента за 10 днів. Результат:

  • Support tickets типу "як налаштувати X" знизилися на 58%.
  • TTFR (time to first response) скоротився з 4 годин до 2 секунд — користувач отримує відповідь миттєво.
  • Задоволеність документацією (CSAT) зросла з 3.2 до 4.4 з 5.
  • Економія на підтримці: за оцінкою клієнта, зниження тікетів дозволило зекономити $10 000 на місяць за рахунок зменшення кількості операторів.

Як асистент взаємодіє з живим оператором?

Якщо асистент не впевнений у відповіді (confidence "low"), він пропонує користувачеві звернутися в підтримку. Кнопка ескалації передає історію діалогу в helpdesk (Zendesk, Freshdesk, Intercom). Оператор бачить контекст: питання користувача, знайдені фрагменти документації та згенеровану відповідь. Це виключає повторне опитування і прискорює вирішення.

Строки та вартість

Орієнтовні строки — від 3 днів до 2 тижнів залежно від складності. Вартість розраховується індивідуально — пишіть, обговоримо ваш кейс. Ми гарантуємо: асистент не галюцинує, версіонується, легко оновлюється.

Зв'яжіться з нами — отримайте консультацію по архітектурі та демо для вашого docs-сайту. Його можна запустити за тиждень і вже через місяць виміряти зниження навантаження на підтримку.

Напрямки AI-розробки

Останні роботи

  • Розробка сайту компанії B2B ADVANCE
    Розробка сайту компанії B2B ADVANCE
    1542
  • Розробка веб-додатків для компанії FEEDME
    Розробка веб-додатків для компанії FEEDME
    1394
  • Розробка веб-сайту для компанії БЕЛФІНГРУП
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    1081
  • Розробка інтернет магазину для компанії FURNORO
    Розробка інтернет магазину для компанії FURNORO
    1369
  • Розробка логотипу компанії B2B Advance
    Розробка логотипу компанії B2B Advance
    816
  • Розробка веб-додатків для компанії Enviok
    Розробка веб-додатків для компанії Enviok
    1142