Іде ключовий співробітник — іде експертиза
База знань залишається, але знайти в ній відповіді — проблема. Стандартний LLM не знає вашу термінологію та процеси. AI-агент, навчений на даних цього співробітника, може відповідати так, як відповів би він сам. Ми розробляємо таких агентів під ключ: від збору даних до production. Головна мета — збереження експертизи співробітника, який йде з компанії.
Чому стандартний LLM не справляється?
Загальна модель дає узагальнені відповіді з інтернету. Вона не знає, що у вашій компанії "затвердження договору" проходить через три рівні погодження. Не знає, що в Confluence є шаблон звіту, а в Jira — історія аналогічних завдань. Точність відповідей на корпоративні процеси без донавчання — близько 67%. Агент, навчений на ваших даних, піднімає її до 91%, тобто в 1.36 рази точніше.
Проблеми, які ми вирішуємо
- Втрата експертизи при звільненні співробітника. Його унікальні знання з процесів, рішень і контактів залишаються в головах. AI-агент фіксує їх у моделі та RAG-індексі.
- Неструктурованість даних. 80% корпоративних знань — у Confluence, Jira, email. Ми збираємо, очищаємо та індексуємо їх у векторну базу (Qdrant, pgvector).
- Довгий пошук відповідей. Замість 15 хвилин на пошук у Confluence — один запит агенту. Зниження тікетів у техпідтримку на 34%.
Порівняння підходів навчання AI-агента
| Аспект | RAG | Fine-tuning | Гібрид (наш вибір) |
|---|---|---|---|
| Швидкість впровадження | 1–2 тижні | 4–6 тижнів | 8–13 тижнів |
| Точність термінології | низька (43%) | висока (97%) | висока (97%) |
| Актуальність знань | актуальні (індекс) | заморожені на дату зрізу | актуальні (RAG + fine-tune) |
| Вимоги до даних | не потребує | ~тис. прикладів | ~тис. прикладів + документи |
| Витрати на GPU | ні | розраховуються індивідуально | розраховуються індивідуально |
Гібридний підхід — єдиний, що забезпечує і точність стилю, і актуальність. Саме його ми використовуємо у всіх production-проєктах. Докладніше про RAG можна прочитати в Wikipedia. Наприклад, fine-tuning дає в 2.26 рази кращу точність термінології, ніж RAG, а гібридний підхід перевершує обидва методи за актуальністю.
Як ми збираємо та готуємо дані?
Джерела даних: Confluence (сторінки), Jira (вирішені тікети), email-листування (анонімізовані), корпоративні файли (PDF, DOCX).
Процес підготовки:
- Збір даних через API (Confluence REST, Jira API, IMAP для пошти).
- Очищення: html-to-text, дедуплікація, фільтр якості (видаляємо відповіді коротші 50 токенів).
- Генерація синтетичних Q&A з документів — використовуємо GPT-4o-mini для створення до 10 пар на документ.
- Розмітка формату: OpenAI messages format (system/user/assistant).
Процес навчання на корпоративних даних включає збір даних з Confluence, Jira та email, а потім генерацію синтетичних Q&A пар за допомогою GPT-4o-mini. Для оркестрації агента використовується LangChain, а векторну базу Qdrant. Ми дотримуємося MLOps практик для відстеження експериментів.
Приклад коду збірника даних:
from pathlib import Path
from typing import Generator
import json
class CorporateDataCollector:
"""Збір даних з корпоративних джерел"""
async def collect_from_confluence(self, space_keys: list[str]) -> list[dict]:
"""Сторінки Confluence"""
docs = []
for space in space_keys:
pages = await confluence_client.get_all_pages(space)
for page in pages:
content = await confluence_client.get_page_content(page["id"])
docs.append({
"source": "confluence",
"id": page["id"],
"title": page["title"],
"content": html_to_text(content),
"updated_at": page["version"]["when"],
"labels": page.get("labels", []),
"space": space,
})
return docs
async def collect_from_email_threads(
self,
email_accounts: list[str],
filter_subjects: list[str] = None,
anonymize_pii: bool = True,
) -> list[dict]:
"""Email-листування як навчальні дані для діалогів"""
threads = []
for account in email_accounts:
emails = await gmail_client.get_threads(account, filter_subjects)
for thread in emails:
if len(thread["messages"]) >= 2:
# Перетворюємо листування у формат діалогу
dialog = self.format_as_dialog(thread["messages"])
if anonymize_pii:
dialog = await self.anonymize_pii(dialog)
threads.append(dialog)
return threads
async def collect_from_tickets(
self,
jira_project: str,
status: str = "Done",
limit: int = 5000,
) -> list[dict]:
"""Вирішені тікети як Q&A пари"""
tickets = await jira_client.get_issues(
jql=f"project={jira_project} AND status={status}",
fields=["summary", "description", "comments", "resolution"],
limit=limit,
)
qa_pairs = []
for ticket in tickets:
if ticket.get("comments"):
qa_pairs.append({
"question": f"{ticket['summary']}\n{ticket.get('description', '')[:500]}",
"answer": self.extract_resolution(ticket),
"source": "jira",
"ticket_id": ticket["id"],
})
return qa_pairs
class FinetuningDatasetBuilder:
async def build_instruction_dataset(
self,
raw_docs: list[dict],
qa_pairs: list[dict],
target_format: str = "openai", # "openai", "alpaca", "sharegpt"
) -> list[dict]:
dataset = []
# З документів — генеруємо Q&A через LLM
for doc in raw_docs:
qa_from_doc = await self.generate_qa_from_document(doc["content"])
for qa in qa_from_doc:
if target_format == "openai":
dataset.append({
"messages": [
{"role": "system", "content": "Ти — корпоративний асистент компанії. Відповідай на запитання співробітників."},
{"role": "user", "content": qa["question"]},
{"role": "assistant", "content": qa["answer"]},
]
})
# З тікетів — готові пари
for qa in qa_pairs:
if target_format == "openai":
dataset.append({
"messages": [
{"role": "system", "content": "Ти — асистент технічної підтримки."},
{"role": "user", "content": qa["question"]},
{"role": "assistant", "content": qa["answer"]},
]
})
# Дедуплікація та фільтрація
dataset = self.deduplicate(dataset)
dataset = self.filter_quality(dataset, min_answer_length=50)
return dataset
async def generate_qa_from_document(self, document_text: str) -> list[dict]:
"""Генерує Q&A пари з документа"""
response = await openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{
"role": "user",
"content": f"""Створи 5-10 запитань і відповідей із наступного документа.
Запитання мають бути такими, як їх задають реальні співробітники.
Відповіді — повними та точними.
Документ:
{document_text[:3000]}
Поверни JSON: [{"question": "...", "answer": "..."}]"""
}],
)
return json.loads(response.choices[0].message.content)
def filter_quality(self, dataset: list[dict], min_answer_length: int) -> list[dict]:
"""Фільтрує дані низької якості"""
filtered = []
for item in dataset:
messages = item.get("messages", [])
assistant_msg = next((m for m in messages if m["role"] == "assistant"), None)
if assistant_msg and len(assistant_msg["content"]) >= min_answer_length:
filtered.append(item)
return filtered
Як працює гібридна архітектура: fine-tune + RAG
Fine-tune модель знає стиль і термінологію компанії. RAG додає актуальні документи. Об'єднуємо їх в одному агенті:
from sentence_transformers import SentenceTransformer
from openai import OpenAI
from qdrant_client import QdrantClient
class HybridCorporateAgent:
"""Об'єднує файнтюн-модель зі стилем компанії та RAG з актуальними знаннями"""
def __init__(self):
# Файнтюн-модель знає стиль і термінологію компанії
self.finetuned_client = OpenAI(base_url="http://vllm-server:8000/v1")
self.finetuned_model = "company-assistant-ft-v2"
# RAG для актуальних документів
self.embed_model = SentenceTransformer("BAAI/bge-m3")
self.vector_db = QdrantClient(host="qdrant-server")
async def answer(self, question: str, user_context: dict = None) -> dict:
# Крок 1: Пошук релевантних документів
query_embedding = self.embed_model.encode(question)
relevant_docs = self.vector_db.search(
collection_name="corporate_docs",
query_vector=query_embedding,
limit=5,
score_threshold=0.6,
query_filter=self.build_access_filter(user_context), # Права доступу
)
# Крок 2: Формування контексту
context = "\n\n".join([
f"[{doc.payload['title']}]: {doc.payload['content']}"
for doc in relevant_docs
])
# Крок 3: Відповідь файнтюн-моделлю з RAG-контекстом
response = self.finetuned_client.chat.completions.create(
model=self.finetuned_model,
messages=[{
"role": "system",
"content": f"Ти — корпоративний асистент. Використовуй документи як джерело істини.\n\nДокументи:\n{context}"
}, {
"role": "user",
"content": question,
}],
temperature=0.1,
)
return {
"answer": response.choices[0].message.content,
"sources": [{"title": d.payload["title"], "score": d.score} for d in relevant_docs],
}
def build_access_filter(self, user_context: dict):
"""Фільтрація за правами доступу — співробітник бачить тільки свої документи"""
if not user_context:
return None
department = user_context.get("department", "all")
clearance = user_context.get("clearance", "public")
return {
"must": [
{"key": "access_level", "match": {"any": [clearance, "public"]}},
{"key": "departments", "match": {"any": [department, "all"]}},
]
}
Приклад розгорнутої архітектури
Агент використовує vLLM для інференсу файнтюн-моделі, Qdrant для векторного пошуку та ONNX Runtime для ембеддингів. Всі компоненти розгортаються в Kubernetes з autoscaling по GPU utilization.
Для зменшення витрат на інференс ми застосовуємо квантування (bitsandbytes) та LoRA (Low-Rank Adaptation) для параметро-ефективного донавчання. Це дозволяє знизити latency на 40% та використовувати менші GPU.
Кейс з нашої практики: IT-компанія, 300 співробітників
Наш клієнт — IT-компанія з 300 співробітників — звільнявся старший розробник, який вів ключові процеси. За 5 років він накопичив 8 000 сторінок у Confluence, брав участь у вирішенні 12 000 тікетів Jira. Ми зібрали та очистили дані за 3 тижні, згенерували 45 000 синтетичних Q&A пар, донавчили GPT-4o-mini на 60 000 прикладах (3 епохи), завантажили всі документи в Qdrant. Результат: точність відповідей на корпоративні процеси 91% проти 67% у базового GPT-4o (у 1.36 рази точніше), правильна термінологія 97% проти 43%, зниження тікетів у техпідтримку на 34%. В результаті клієнт економить понад $50,000 на рік завдяки зниженню навантаження на техпідтримку.
Що входить у роботу
| Етап | Тривалість | Що отримуєте |
|---|---|---|
| Аналітика та збір даних | 2–4 тижні | Карта джерел, очищений датасет |
| Генерація synthetic Q&A | 1–2 тижні | 10k-100k пар запитання-відповідь |
| Fine-tuning та RAG-індексація | 2–3 тижні | Навчена модель, векторний індекс |
| Тестування та калібрування | 2 тижні | Звіт з метриками (accuracy, hallucination rate) |
| Деплой та документація | 1 тиждень | API-доступ, дашборд моніторингу, адміністративний інтерфейс |
Скільки часу займає розробка?
Від 8 до 13 тижнів залежно від обсягу та якості даних. Більшу частину часу займає збір і очищення — їх не прискорити без втрати якості. Вартість розраховується індивідуально, виходячи з кількості джерел та необхідної точності. Типовий бюджет проєкту — від $15,000 до $30,000.
Типові помилки при навчанні AI-агента
- Ігнорування прав доступу. Без фільтрації співробітник може побачити документи іншого відділу. Наша архітектура включає
build_access_filter, який перевіряє департамент і рівень допуску. - Використання тільки RAG. Без fine-tuning модель не засвоює корпоративний стиль — відповіді звучать як з інтернету, а не від колеги.
- Слабкий quality filtering. Якщо в датасет потрапляють погані відповіді (коротші 50 токенів або без фактів), якість падає. Ми фільтруємо за мінімальною довжиною та семантичною близькістю.
- Відсутність тестування на реальних запитаннях. Синтетичні тести не показують прогалини. Перевіряємо на 500+ питаннях від майбутніх користувачів.
Чому варто замовити розробку у нас?
5 років досвіду в NLP та MLOps, 30+ впроваджених AI-агентів. Гарантуємо, що агент відповідатиме з точністю не нижче 85% на заявлені процеси. Використовуємо тільки перевірені інструменти: Hugging Face, Qdrant, vLLM, ONNX Runtime. Після здачі — підтримка та донавчання в міру накопичення нових даних.
Отримайте консультацію — ми проаналізуємо ваші дані та запропонуємо оптимальний підхід. Розробка під ключ з нуля або інтеграція в існуючу інфраструктуру. Замовте розробку AI-агента — збережіть експертизу вашої команди.







