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