Проблема: почему передача дел похожа на археологические раскопки
Новый CEO приходит в компанию. Или старший инженер уходит в другой проект. Или вы просто хотите передать часть задач коллеге. Что получает человек? Папку с документами. Нет, не красивый Confluence с навигацией. Обычную папку в Google Drive или, что хуже, архив на почте.
Внутри: PDF-отчеты за 2021 год, скриншоты Slack-переписок, таблицы Excel с непонятными формулами, заметки в формате «мысль дня». Никакой структуры. Никаких связей. Попробуйте найти ответ на вопрос «почему мы используем именно эту версию библиотеки?» — придется перелопатить десяток файлов.
Типичная ошибка: думать, что «документы есть, значит знания сохранены». Это иллюзия. Знания существуют в связях между документами, в контексте, в неявных договоренностях. Папка с файлами — это кладбище знаний.
Решение: не Confluence, а живой сайт с мозгом
Создаем не просто статичный сайт с навигацией. Создаем интерактивную систему, где можно:
- Задать вопрос на естественном языке («Как мы выбирали поставщика облака?»)
- Получить ответ со ссылками на исходные документы
- Видеть связи между темами (финансы → выбор облака → конкретный контракт)
- Обновлять информацию добавлением новых документов
Техническая основа — RAG (Retrieval-Augmented Generation). Тот же принцип, что в ChatGPT, но вместо общего интернета — ваши документы. ИИ не генерирует ответы из воздуха, а ищет релевантные фрагменты в ваших файлах и строит связный ответ.
Шаг 0: Подготовка — собираем все, что есть
Не фильтруйте. Не пытайтесь сразу структурировать. Скачайте все:
- PDF-отчеты, презентации
- Документы Word, Google Docs (экспорт в PDF или текст)
- Таблицы Excel/Google Sheets
- Скриншоты переписок (да, их тоже можно обработать, если есть текст)
- Даже голосовые заметки — есть инструменты для транскрибации
Сложите все в одну папку. Назовите ее raw_documents. Если файлов больше 100, разделите на подпапки по годам или темам — это упростит дальнейшую обработку.
1 Извлекаем текст: из PDF, картинок, таблиц
Здесь нужны инструменты OCR и парсеры. Не пытайтесь делать все вручную.
Для PDF используйте pdftotext (из пакета poppler) или библиотеку PyPDF2/PyMuPDF в Python. Для таблиц — pandas. Для изображений со текстом — Tesseract OCR.
Пример скрипта на Python для обработки папки:
import os
import fitz # PyMuPDF
from PIL import Image
import pytesseract
import pandas as pd
def extract_text_from_pdf(pdf_path):
doc = fitz.open(pdf_path)
text = ""
for page in doc:
text += page.get_text()
return text
def process_folder(input_folder, output_folder):
os.makedirs(output_folder, exist_ok=True)
for filename in os.listdir(input_folder):
filepath = os.path.join(input_folder, filename)
if filename.endswith('.pdf'):
text = extract_text_from_pdf(filepath)
# Добавьте обработку других форматов
output_path = os.path.join(output_folder, f"{os.path.splitext(filename)[0]}.txt")
with open(output_path, 'w', encoding='utf-8') as f:
f.write(text)
Не забудьте про кодировки. Русский текст в старых документах часто сохраняется в cp1251. Используйте chardet для автоматического определения кодировки.
2 Разбиваем на фрагменты: почему нельзя кормить ИИ целыми документами
Большой документ на 50 страниц — это слишком много для контекста модели. Нужно разбить текст на фрагменты по 500-1000 символов с перекрытием (overlap) в 100 символов. Перекрытие нужно, чтобы контекст не обрывался на середине предложения.
Используйте библиотеку LangChain или простой скрипт:
def split_text(text, chunk_size=1000, overlap=100):
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunk = text[start:end]
chunks.append(chunk)
start += chunk_size - overlap
return chunks
Сохраняйте метаданные для каждого фрагмента: исходный файл, номер страницы (для PDF), дата документа. Потом это понадобится для ссылок в ответах.
3 Векторная база: превращаем текст в числа для поиска
Здесь начинается магия. Текст преобразуется в векторы (эмбендинги) — числовые представления смысла. Похожие по смыслу фрагменты будут иметь близкие векторы.
Локальный вариант: Ollama с моделью nomic-embed-text (хорошо работает с русским). Облачный вариант: OpenAI API (text-embedding-3-small) или бесплатные альтернативы типа Jina Embeddings.
База данных для векторов:
- Chroma — проще всего, работает из коробки
- Qdrant — производительнее, есть Docker-образ
- Weaviate — больше возможностей для фильтрации
Пример с Chroma:
import chromadb
from chromadb.config import Settings
from sentence_transformers import SentenceTransformer
# Инициализация
client = chromadb.Client(Settings(persist_directory="./chroma_db"))
collection = client.create_collection(name="knowledge_base")
# Модель для эмбеддингов (локальная)
model = SentenceTransformer('sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2')
# Преобразование и сохранение
for i, chunk in enumerate(text_chunks):
embedding = model.encode(chunk).tolist()
collection.add(
documents=[chunk],
embeddings=[embedding],
metadatas=[{"source": filename, "page": page_num}],
ids=[f"doc_{i}"]
)
4 Сервер вопросов и ответов: мозг системы
Теперь нужен сервер, который будет принимать вопросы, искать релевантные фрагменты в векторной базе и генерировать ответы.
FastAPI + LangChain — отличный выбор:
from fastapi import FastAPI
from pydantic import BaseModel
from langchain.chains import RetrievalQA
from langchain.llms import Ollama
from langchain.embeddings import OllamaEmbeddings
from langchain.vectorstores import Chroma
app = FastAPI()
# Загрузка векторной базы
embeddings = OllamaEmbeddings(model="nomic-embed-text")
vectorstore = Chroma(persist_directory="./chroma_db", embedding_function=embeddings)
retriever = vectorstore.as_retriever(search_kwargs={"k": 5}) # 5 наиболее релевантных фрагментов
# Инициализация локальной модели через Ollama
llm = Ollama(model="llama3.1", temperature=0.1) # temperature=0.1 для более точных ответов
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
class Question(BaseModel):
query: str
@app.post("/ask")
async def ask_question(question: Question):
result = qa_chain({"query": question.query})
return {
"answer": result["result"],
"sources": [
{
"source": doc.metadata["source"],
"page": doc.metadata.get("page", "N/A")
}
for doc in result["source_documents"]
]
}
Запустите этот сервер локально или в Docker-контейнере. Теперь у вас есть API, который отвечает на вопросы на основе ваших документов.
5 Интерфейс: простой веб-сайт вместо сложного чат-бота
Многие останавливаются на этапе API и делают чат-интерфейс. Ошибка. Для базы знаний нужен именно сайт с:
- Поисковой строкой
- Древовидной навигацией по темам
- Страницами с автоматически сгенерированными статьями
- Ссылками между статьями
Используйте Streamlit для прототипа или Next.js + Tailwind CSS для более серьезного решения.
Ключевая идея: генерируйте не только ответы на вопросы, но и статические страницы по основным темам. Как найти темы? Попросите ИИ проанализировать документы и выделить основные категории:
prompt = """
Проанализируй следующие документы и выдели 10 основных тем, которые в них обсуждаются.
Верни результат в формате JSON:
{
"topics": [
{
"name": "Название темы",
"description": "Краткое описание",
"keywords": ["ключевое", "слово"]
}
]
}
Документы:
{тексты_документов}
"""
Для каждой темы создайте страницу с обзором, используя ИИ. Затем свяжите эти страницы гиперссылками.
Ошибки, которые гарантированно испортят результат
| Ошибка | Почему это проблема | Как исправить |
|---|---|---|
| Кормить ИИ сырыми документами без обработки | Модель теряет контекст, отвечает ерундой или игнорирует важные детали | Обязательно разбивайте на фрагменты с перекрытием |
| Использовать только OpenAI без fallback | API может упасть, закончатся деньги, а доступ к знаниям нужен сейчас | Используйте локальные модели (Ollama + локальные модели) как основной вариант |
| Игнорировать метаданные | Пользователь получает ответ, но не понимает, откуда он взят | Всегда сохраняйте источник, дату, автора, тип документа |
| Не настраивать параметры поиска | Система возвращает слишком много или слишком мало результатов | Экспериментируйте с k (количество фрагментов) и score_threshold (порог схожести) |
Что делать, если ИИ генерирует ерунду
Такое случается. Особенно с локальными моделями. Вместо того чтобы сдаваться, сделайте три вещи:
- Проверьте качество фрагментов. Если фрагмент обрывается на середине предложения, ИИ не поймет контекст. Увеличьте перекрытие до 150-200 символов.
- Добавьте system prompt. Явно укажите модели: «Ты — помощник, который отвечает только на основе предоставленных документов. Если в документах нет информации, скажи 'Информация не найдена'».
- Используйте цепочки (chains). Вместо одного запроса к модели разбейте задачу: сначала поиск релевантных фрагментов, затем их суммаризация, затем ответ на вопрос.
Если ИИ продолжает галлюцинировать, возможно, проблема в самой модели. Попробуйте другие — сравнение моделей для кодинга поможет выбрать подходящую.
Деплой: как сделать базу знаний доступной для команды
Локальный сервер на вашем ноутбуке бесполезен. Нужно развернуть систему так, чтобы доступ был у всех, кому нужен.
Варианты:
- Docker Compose — все компоненты (векторная база, бэкенд, фронтенд) в контейнерах. Развертывание одной командой.
- Cloud Run (GCP) или Railway — если не хотите возиться с сервером.
- На собственном сервере — больше контроля, но нужно следить за обновлениями.
Пример docker-compose.yml для базового стека:
version: '3.8'
services:
qdrant:
image: qdrant/qdrant
ports:
- "6333:6333"
volumes:
- ./qdrant_storage:/storage
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- QDRANT_HOST=qdrant
- QDRANT_PORT=6333
depends_on:
- qdrant
frontend:
build: ./frontend
ports:
- "3000:3000"
depends_on:
- backend
Не забывайте про безопасность. Не выставляйте API публично без авторизации. Используйте базовую HTTP-аутентификацию или JWT-токены.
Что дальше: от статичной базы к живой системе
Сайт готов. Но знания не статичны. Новые документы появляются каждый день. Как обновлять базу?
1. Автоматическое добавление. Настройте webhook, который при появлении нового документа в Google Drive/Dropbox автоматически обрабатывает его и добавляет в векторную базу.
2. Обратная связь. Добавьте кнопки «Полезно»/«Не полезно» под ответами. Собирайте feedback для улучшения поиска.
3. Визуализация связей. Используйте граф знаний — покажите, как документы связаны между собой. Это помогает понять контекст лучше, чем простой список.
4. Мультимодальность. Добавьте обработку не только текста, но и изображений, видео, аудио. Как это работает — в статье про Multi-modal RAG.
Самое главное — не пытайтесь сделать идеальную систему с первого раза. Соберите минимально рабочую версию за два вечера. Дайте ее коллегам. Посмотрите, какие вопросы они задают. Какие ответы получают. Итеративно улучшайте.
Потому что база знаний, которой никто не пользуется, — это просто еще один забытый документ в папке.