עוזר AI לתיעוד מוצר על בסיס RAG

משתמשים מבזבזים זמן בניווט בתיעוד מפוזר, והתמיכה ממשיכה לענות על אותן שאלות. אנחנו בונים עוזר AI מבוסס RAG שמוצא מיידית תשובות מדויקות באתר התיעוד שלך ומציג אותן בצ'אט עם ציטוטי מקור. הצוות שלנו מספק את הפרויקט במפתח מלא—מביקורת תיעוד ועד אינטגרציה ותמיכה שוטפת.

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1331
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    783
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

עוזר AI לתיעוד מוצר מבוסס RAG

משתמשים מבזבזים עד 30 דקות בחיפוש תשובות בתיעוד מפוזר — פותחים עשרות עמודים אך לא מוצאים את מה שהם צריכים. צוותי התמיכה טובעים בשאלות חוזרות כמו 'איך מגדירים X' ו'איפה מוצאים Y'. אנו בונים עוזר AI שמוציא תשובות מדויקות מאתר התיעוד ומציג אותן בצ'אט. ללא הזיות: כל תשובה מגובה בציטוט מהתיעוד.

לפי מחקר של Forrester, העובד הממוצע מבלה 22% מזמנו בחיפוש מידע פנימי בחברה. עבור תיעוד מוצר — עם 300+ עמודים וגרסאות מרובות — נתון זה מגיע ל-30%. עוזר המבוסס על RAG (יצירה מוגברת שליפה) מצמצם את זמן החיפוש לשניות ואת עומס התמיכה בעד 60%.

אילו בעיות העוזר ה-AI פותר?

עומס מידע. תיעוד המוצר עשוי להכיל 300+ עמודים, גרסאות מרובות ושפות. המשתמש לא יודע איזה קטע לפתוח. העוזר מוצא את הקטע הרלוונטי בשניות ומציג אותו בתשובה.

תשובות מיושנות. כאשר התיעוד מתעדכן, מדדי החיפוש הופכים למיושנים. העוזר שלנו עובד על מסד נתונים וקטורי עדכני — אינדוקס מחדש רץ אוטומטית בכל פריסת CI/CD.

עומס תמיכה. לפי הנתונים שלנו, לאחר הטמעת העוזר, מספר הפניות עם שאלות 'איך עושים' יורד ב-50–60%. זה משחרר את מהנדסי התמיכה למשימות מורכבות.

מנגנון RAG: כיצד לחסל הזיות

הרכיב המרכזי הוא יצירה מוגברת שליפה (RAG). אנחנו לא נותנים ל-LLM לענות מהזיכרון שלו; במקום זאת, אנו מספקים לו הקשר מהתיעוד. מסד הנתונים הווקטורי (ChromaDB, Qdrant, או pgvector) מאחסן קטעי טקסט עם מטא-דאטה: גרסה, כותרת, URL. בשאילתה, חיפוש סמנטי שולף עד 5 קטעים רלוונטיים ביותר, והמודל מנסח את התשובה אך ורק על סמך נתונים אלה. גישה זו יעילה פי 5 מחיפוש מילות מפתח סטנדרטי וכמעט מבטלת לחלוטין הזיות.

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]
            )

ווידג'ט לאתר התיעוד

המשתמש צריך להיות מסוגל לשאול שאלה מבלי לעזוב את עמוד התיעוד. אנו מטמיעים ווידג'ט צ'אט שמתחבר ל-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
});

מדוע גרסת תגובה היא קריטית?

אם המוצר בפיתוח פעיל, משתמש בגרסה ישנה יקבל תשובה שגויה אם העוזר מסתמך על התיעוד העדכני. אנו מאחסנים שדה מטא-דאטה 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", } לכל קטע ומסננים את החיפוש לפי הגרסה שהלקוח מספק (או קובעים אותה דרך user-agent). דיוק התשובה עולה ל-97% לעומת 82% ללא סינון.

מה כלול?

רכיב תיאור משך
אינדוקס תיעוד ניתוח כל עמודי התיעוד, פיצול לקטעים, יצירת embeddings, טעינה למסד נתונים וקטורי 2–3 ימים
backend של RAG שירות FastAPI עם LangChain, אינטגרציה עם LLM (Claude, GPT-4), סינון גרסאות, טיפול בשגיאות 3–5 ימים
ווידג'ט לאתר ווידג'ט JS מוכן עם סגנונות הניתנים להתאמה אישית, תמיכה במצב כהה, אנליטיקה 2–3 ימים
אינטגרציה עם Helpdesk הסלמה ל-Zendesk / Freshdesk / Intercom, העברת היסטוריית דיאלוג 1–2 שבועות
תיעוד והדרכה הוראות לעדכוני תוכן, פריסת גרסה חדשה, לוח מחוונים למדדים 2 ימים

השוואת גישות לבניית עוזר

גישה דיוק זמן הטמעה הזיות
RAG (שלנו) 95–97% 1–2 שבועות כמעט אין
כוונון עדין של LLM 80–85% 2–4 שבועות אפשריות
LLM טהור ללא הקשר 70–75% נמוך תכופות

גישת RAG מציעה את השילוב הטוב ביותר של דיוק ומהירות הטמעה, והכי חשוב — היא כמעט מבטלת לחלוטין הזיות מכיוון שהמודל מסתמך על מסמכים אמיתיים.

מקרה מעשי: מוצר SaaS עם 8,000 משתמשים

תיעוד: 320 עמודי GitBook, 5 גרסאות מוצר. הלקוח שלנו — פלטפורמת SaaS לניהול פרויקטים — התמודד עם 40% מהפניות שהיו שאלות בסיסיות. הטמענו את העוזר ה-AI ב-10 ימים. תוצאות:

  • פניות תמיכה כמו 'איך מגדירים X' ירדו ב-58%.
  • TTFR (זמן לתגובה ראשונה) ירד מ-4 שעות ל-2 שניות — משתמשים מקבלים תשובות מיידיות.
  • שביעות רצון מהתיעוד (CSAT) עלתה מ-3.2 ל-4.4 מתוך 5.
  • חיסכון בתמיכה: הלקוח העריך שהפחתת הפניות חסכה $10,000 בחודש על ידי צמצום מספר הנציגים.

כיצד העוזר מתקשר עם נציג תמיכה חי?

אם העוזר אינו בטוח בתשובתו (ביטחון "נמוך"), הוא מציע למשתמש לפנות לתמיכה. כפתור ההסלמה מעביר את היסטוריית הדיאלוג ל-helpdesk (Zendesk, Freshdesk, Intercom). הנציג רואה את ההקשר: שאלת המשתמש, קטעי התיעוד שנמצאו, והתשובה שנוצרה. זה מבטל שאלות חוזרות ומאיץ את הפתרון.

לוח זמנים ותמחור

לוח זמנים משוער — בין 3 ימים לשבועיים בהתאם למורכבות. התמחור מחושב באופן אישי — צרו קשר כדי לדון במקרה שלכם. אנו מבטיחים: העוזר לא יזהה, תומך בגרסאות, וקל לעדכון.

צרו קשר — קבלו ייעוץ ארכיטקטוני והדגמה לאתר התיעוד שלכם. ניתן להשיק אותו תוך שבוע, ובתוך חודש תוכלו למדוד את ההפחתה בעומס התמיכה.