# SYSTEM AUDIT: LawBot

## 1. Что это за проект

`LawBot` — это Telegram-бот, который:

1. принимает вопрос пользователя;
2. ищет подходящие нормы закона РФ;
3. достает текст из своей базы;
4. передает найденные фрагменты в LLM;
5. возвращает пользователю ответ простыми словами.

Проект состоит из 4 основных частей:

| Часть | Папка | Что делает |
|---|---|---|
| Telegram-бот | `bot/` | Общается с пользователем в Telegram |
| API для RAG | `api/` | Делает поиск по законам и генерацию ответа |
| Парсер законов | `parser/` | Скачивает и нормализует документы |
| Общий слой БД | `shared/` | Хранит модели БД и методы работы с БД |

---

## 2. Архитектура проекта

### 2.1. Общая схема

```mermaid
flowchart LR
    User["Пользователь Telegram"]
    Bot["bot/\nAiogram bot"]
    Api["api/\nFastAPI RAG API"]
    Parser["parser/\nCLI parser"]
    Shared["shared/\nDB layer"]
    Pg["PostgreSQL"]
    Redis["Redis"]
    Chroma["ChromaDB"]
    LLM["OpenRouter LLM"]
    Embed["Local embedding model"]
    Consultant["consultant.ru"]

    User --> Bot
    Bot --> Api
    Bot --> Shared
    Bot --> Redis

    Api --> Shared
    Api --> Chroma
    Api --> LLM
    Api --> Embed

    Shared --> Pg

    Parser --> Consultant
    Parser --> Shared
    Parser --> Pg
```

### 2.2. Что запускается в Docker Compose

Файл: [compose.yml](/home/ruby/Desktop/DockerProjects/LawBot/compose.yml:1)

| Сервис | Образ / Dockerfile | Назначение | Порт |
|---|---|---|---|
| `tgbot` | `bot/Dockerfile` | Telegram-бот | нет внешнего порта |
| `api` | `api/Dockerfile` | FastAPI RAG API | `8080` |
| `postgredb` | `postgres:16-alpine` | Основная БД | нет внешнего порта |
| `redisdb` | `redis:6-alpine` | FSM/storage для aiogram | нет внешнего порта |
| `chromadb` | `chromadb/chroma:1.0.12` | Векторный поиск | `8000` |

Важно: отдельного контейнера для `parser/` нет. Парсер запускается командой внутри контейнера `tgbot`.

---

## 3. Структура директорий

### 3.1. Корень проекта

| Путь | Назначение |
|---|---|
| `compose.yml` | Запуск всех сервисов |
| `README.md` | Краткая инструкция по запуску |
| `mvp.md` | Текстовое описание MVP |
| `postgres.env` | Настройки подключения к PostgreSQL |
| `postgres.env.example` | Пример env для PostgreSQL |
| `SYSTEM_AUDIT.md` | Этот аудит |
| `volumes/` | Постоянные данные контейнеров |
| `protocol/` | Документы для курсовой |

### 3.2. Папка `api/`

| Путь | Назначение |
|---|---|
| `api/main.py` | Точка входа FastAPI |
| `api/config.py` | Чтение env-переменных API |
| `api/deps.py` | Создание singleton-зависимостей |
| `api/schemas.py` | Pydantic-схемы API |
| `api/logging.py` | Настройка логов API |
| `api/routers/` | HTTP-роуты (`health`, `indexing`, `rag`) |
| `api/services/` | Основная логика: embeddings, retrieval, indexing, LLM |
| `api/clients/` | Клиенты для OpenRouter и Chroma |
| `api/prompts/` | Промпты для классификации и ответа |
| `api/requirements.txt` | Зависимости API |
| `api/Dockerfile` | Сборка контейнера API |

### 3.3. Папка `bot/`

| Путь | Назначение |
|---|---|
| `bot/aiogram_run.py` | Точка входа Telegram-бота |
| `bot/create_bot.py` | Создание Bot / Dispatcher / Redis / ORM |
| `bot/handlers/` | Обработчики команд, кнопок и сценариев |
| `bot/keyboards/` | Reply и inline клавиатуры |
| `bot/middlewares/` | Антифлуд, blacklist, album middleware |
| `bot/states/` | FSM состояния aiogram |
| `bot/utils/rag_api.py` | HTTP-клиент бота для вызова `api` |
| `bot/utils/text_tools.py` | Форматирование и нарезка текста для Telegram |
| `bot/templates/` | Шаблоны файлов (`users.xlsx`) |
| `bot/requirements.txt` | Зависимости бота |
| `bot/Dockerfile` | Сборка контейнера бота |
| `bot/webhooks.py` | Отдельный тестовый FastAPI webhook, не подключен к `compose.yml` |

### 3.4. Папка `parser/`

| Путь | Назначение |
|---|---|
| `parser/__main__.py` | Позволяет запускать `python -m parser` |
| `parser/cli.py` | CLI-команды `discover/fetch/normalize/ingest/run` |
| `parser/config.py` | Список целевых документов и пути сохранения |
| `parser/discovery.py` | Проверка категорий на `consultant.ru/popular/` |
| `parser/fetcher.py` | Скачивание root HTML и страниц статей |
| `parser/normalizer.py` | Разбор HTML и сборка нормализованного JSON |
| `parser/ingest.py` | Запись документов и чанков в PostgreSQL |
| `parser/utils.py` | Общие функции парсера |

### 3.5. Папка `shared/`

| Путь | Назначение |
|---|---|
| `shared/models.py` | SQLAlchemy-модели |
| `shared/repositories.py` | Класс `ORM` и методы работы с БД |
| `shared/engine.py` | Создание async engine и sessionmaker |
| `shared/types.py` | Вспомогательные типы |
| `shared/__init__.py` | Единая точка импорта DB-слоя |

### 3.6. Папка `protocol/`

| Путь | Назначение |
|---|---|
| `protocol/generate_protocol_docx.py` | Генератор `.docx` протокола курсовой |
| `protocol/Протокол_курсовой_работы_LawBot.docx` | Готовый документ |
| `protocol/task/` | Входные материалы задания |

### 3.7. Папка `volumes/`

| Путь | Назначение |
|---|---|
| `volumes/postgres/` | Данные PostgreSQL |
| `volumes/chroma/` | Данные ChromaDB |
| `volumes/huggingface/` | Кеш embedding-модели |
| `volumes/parser/` | Артефакты парсера: raw, normalized, state |

---

## 4. Ключевые сервисы, модули и точки входа

### 4.1. Точки входа

| Компонент | Точка входа | Что происходит |
|---|---|---|
| Telegram-бот | [bot/aiogram_run.py](/home/ruby/Desktop/DockerProjects/LawBot/bot/aiogram_run.py:1) | Поднимает bot, middleware, routers и polling |
| FastAPI API | [api/main.py](/home/ruby/Desktop/DockerProjects/LawBot/api/main.py:1) | Поднимает API, проверяет БД, запускает автоиндексацию |
| Парсер | [parser/__main__.py](/home/ruby/Desktop/DockerProjects/LawBot/parser/__main__.py:1) -> [parser/cli.py](/home/ruby/Desktop/DockerProjects/LawBot/parser/cli.py:1) | Запускает CLI пайплайн |

### 4.2. Основные модули API

| Модуль | Файл | Роль |
|---|---|---|
| Конфиг | `api/config.py` | Читает env и хранит настройки |
| Роуты | `api/routers/rag.py` | `/search` и `/answer` |
| Индексация | `api/routers/indexing.py` | `/api/v1/index/rebuild` |
| Retrieval | `api/services/retrieval.py` | Гибридный поиск: Postgres FTS + Chroma |
| Индексация | `api/services/indexing.py` | Перенос чанков из Postgres в Chroma |
| Embeddings | `api/services/local_embeddings.py` | Локальные эмбеддинги через `sentence-transformers` |
| LLM-логика | `api/services/legal_ai.py` | Классификация, генерация ответа, заголовка консультации |
| Векторное хранилище | `api/clients/chroma_store.py` | Работа с Chroma |
| LLM-клиент | `api/clients/openrouter_client.py` | AsyncOpenAI-клиент для OpenRouter |

### 4.3. Основные модули бота

| Модуль | Файл | Роль |
|---|---|---|
| Создание бота | `bot/create_bot.py` | Bot, Dispatcher, RedisStorage, ORM, timezone |
| Главный запуск | `bot/aiogram_run.py` | Сборка всех роутеров |
| Пользовательский сценарий | `bot/handlers/client/main.py` | Вопрос -> регион -> ответ -> история |
| Админка | `bot/handlers/admin/*.py` | Статистика, blacklist, рассылка, настройки |
| HTTP-клиент к API | `bot/utils/rag_api.py` | Отправляет запросы в FastAPI |
| Форматирование | `bot/utils/text_tools.py` | Подготовка текста под Telegram HTML |

### 4.4. Основные модули парсера

| Модуль | Файл | Роль |
|---|---|---|
| Discovery | `parser/discovery.py` | Собирает manifest целевых документов |
| Fetch | `parser/fetcher.py` | Скачивает страницы документов |
| Normalize | `parser/normalizer.py` | Превращает HTML в нормализованный JSON |
| Ingest | `parser/ingest.py` | Пишет `law_sources` и `law_chunks` в БД |

### 4.5. Основной DB-слой

| Модуль | Файл | Роль |
|---|---|---|
| ORM | `shared/repositories.py` | Все операции с PostgreSQL |
| Модели | `shared/models.py` | Таблицы пользователей, консультаций, законов, чанков |

---

## 5. Кто за что отвечает

| Сервис / модуль | Отвечает за |
|---|---|
| `tgbot` | UI для пользователя и администратора |
| `api` | Поиск по законам и генерацию ответа |
| `parser` | Наполнение базы законами |
| `shared` | Единый доступ к PostgreSQL |
| `postgredb` | Хранение пользователей, консультаций, законов и чанков |
| `redisdb` | Хранение FSM-состояний aiogram |
| `chromadb` | Векторный индекс чанков |
| `OpenRouter` | LLM для классификации и ответа |
| Локальная embedding-модель | Преобразование текста в векторы |

---

## 6. Переменные окружения

### 6.1. `bot/.env`

Источник: [bot/.env.example](/home/ruby/Desktop/DockerProjects/LawBot/bot/.env.example:1)

| Переменная | Назначение |
|---|---|
| `TOKEN` | Telegram bot token |
| `TELEGRAM_BOT_PROXY` | Прокси для Telegram Bot API |
| `BASE_ADMIN` | Telegram ID администратора |
| `RAG_API_URL` | URL FastAPI сервиса |
| `REDIS_URL` | Подключение к Redis |
| `POSTGRES_DB` | Имя БД |
| `POSTGRES_USER` | Логин БД |
| `POSTGRES_PASSWORD` | Пароль БД |
| `POSTGRES_HOST` | Хост PostgreSQL |
| `POSTGRES_PORT` | Порт PostgreSQL |
| `TIMEZONE` | Часовой пояс для отображения дат |

### 6.2. `api/.env`

Источник: [api/.env.example](/home/ruby/Desktop/DockerProjects/LawBot/api/.env.example:1)

| Переменная | Назначение |
|---|---|
| `OPENAI_BASE_URL` | Базовый URL OpenRouter |
| `OPENAI_API_KEY` | Ключ доступа к OpenRouter |
| `LLM_MODEL` | Модель LLM для классификации и ответа |
| `EMBEDDING_MODEL` | Локальная embedding-модель |
| `EMBEDDING_DEVICE` | Устройство (`cpu`) |
| `CHROMA_HOST` | Хост ChromaDB |
| `CHROMA_PORT` | Порт ChromaDB |
| `CHROMA_COLLECTION` | Название коллекции Chroma |
| `CHROMA_SSL` | Использовать ли SSL |
| `RAG_TOP_K` | Дефолтное число результатов RAG |
| `FTS_TOP_K` | Сколько брать из full-text поиска |
| `VECTOR_TOP_K` | Сколько брать из vector search |
| `INDEX_BATCH_SIZE` | Размер батча для индексации и embeddings |
| `LLM_TIMEOUT_SECONDS` | Таймаут LLM-запросов |
| `API_HOST` | Хост FastAPI |
| `API_PORT` | Порт FastAPI |
| `LOG_LEVEL` | Уровень логов |
| `AUTO_INDEX_ON_STARTUP` | Запускать ли автоиндексацию при старте API |
| `AUTO_INDEX_ONLY_IF_EMPTY` | Индексировать только если Chroma пустая/не синхронизирована |
| `AUTO_INDEX_RESET_COLLECTION` | Сбрасывать ли коллекцию при автоиндексации |
| `AUTO_INDEX_RETRY_DELAY_SECONDS` | Пауза между попытками автоиндексации |
| `AUTO_INDEX_MAX_ATTEMPTS` | Максимум попыток автоиндексации |

### 6.3. `postgres.env`

Источник: [postgres.env.example](/home/ruby/Desktop/DockerProjects/LawBot/postgres.env.example:1)

| Переменная | Назначение |
|---|---|
| `POSTGRES_DB` | Имя БД PostgreSQL |
| `POSTGRES_USER` | Пользователь БД |
| `POSTGRES_PASSWORD` | Пароль БД |
| `POSTGRES_HOST` | Хост БД |
| `POSTGRES_PORT` | Порт БД |

---

## 7. Основные таблицы данных

Источник: [shared/models.py](/home/ruby/Desktop/DockerProjects/LawBot/shared/models.py:1)

| Таблица | Назначение |
|---|---|
| `users` | Пользователи Telegram |
| `admins` | Администраторы |
| `blacklist` | Заблокированные пользователи |
| `settings` | Настройки бота |
| `consultations` | Диалоги пользователя с ботом |
| `messages` | Сообщения внутри консультаций |
| `rag_queries` | История поисковых запросов и найденных чанков |
| `law_sources` | Документы законов |
| `law_chunks` | Чанки статей законов |

---

## 8. Потоки данных

### 8.1. Поток ответа пользователю

1. Пользователь пишет в Telegram.
2. `bot/handlers/client/main.py` принимает сообщение.
3. Бот вызывает `bot/utils/rag_api.py`.
4. HTTP-запрос уходит в `POST /api/v1/rag/answer`.
5. `api/services/legal_ai.py` классифицирует вопрос.
6. `api/services/retrieval.py` ищет чанки:
   - через PostgreSQL full-text search;
   - через Chroma vector search.
7. `api/services/legal_ai.py` генерирует ответ через OpenRouter.
8. Если `save_history=true`, API сохраняет:
   - консультацию;
   - сообщения;
   - rag-запрос.
9. Бот получает ответ, форматирует его и отправляет в Telegram.

### 8.2. Поток загрузки законов

1. Команда `python -m parser run`.
2. `parser/discovery.py` собирает manifest.
3. `parser/fetcher.py` скачивает root HTML и статьи.
4. `parser/normalizer.py` делает нормализованный JSON.
5. `parser/ingest.py` записывает документы и чанки в PostgreSQL.
6. `api/services/indexing.py` переносит чанки в Chroma.

### 8.3. Схема зависимости модулей

```text
bot/handlers/client/main.py
  -> bot/utils/rag_api.py
  -> api/routers/rag.py
  -> api/services/legal_ai.py
  -> api/services/retrieval.py
  -> shared/repositories.py
  -> PostgreSQL / ChromaDB / OpenRouter

parser/cli.py
  -> parser/discovery.py
  -> parser/fetcher.py
  -> parser/normalizer.py
  -> parser/ingest.py
  -> shared/repositories.py
  -> PostgreSQL
```

---

## 9. Используемые технологии и зависимости

### 9.1. По сервисам

| Область | Технологии |
|---|---|
| Bot | `aiogram`, `aiohttp`, `httpx`, `redis`, `uvloop` |
| API | `FastAPI`, `uvicorn`, `openai`, `chromadb`, `sentence-transformers`, `SQLAlchemy`, `asyncpg` |
| Parser | `requests`, `beautifulsoup4` |
| DB | `PostgreSQL`, `SQLAlchemy`, `asyncpg` |
| Vector search | `ChromaDB` |
| LLM | `OpenRouter` через `AsyncOpenAI` |
| Embeddings | `sentence-transformers` + локальная модель |

### 9.2. Замеченные версии

Источники:
- [api/requirements.txt](/home/ruby/Desktop/DockerProjects/LawBot/api/requirements.txt:1)
- [bot/requirements.txt](/home/ruby/Desktop/DockerProjects/LawBot/bot/requirements.txt:1)

| Пакет | Версия |
|---|---|
| `fastapi` | `0.115.9` |
| `uvicorn` | `0.34.2` |
| `openai` | `1.82.0` |
| `chromadb` | `1.0.12` |
| `SQLAlchemy` | `2.0.38` |
| `asyncpg` | `0.30.0` |
| `sentence-transformers` | `5.1.2` |
| `aiogram` | `3.17.0` |
| `httpx` | `0.28.1` |
| `redis` | `5.2.1` |
| `beautifulsoup4` | `4.13.4` |

---

## 10. Что выглядит слабо или странно

Ниже только то, что видно по текущему коду.

| Проблема | Где видно | Почему это слабое место |
|---|---|---|
| Нет отдельного сервиса `parser` | `compose.yml` | Парсер живет внутри контейнера бота. Это смешивает роли и зависимости. |
| Нет миграций БД | `shared/repositories.py:97` | Схема создается через `create_all()` и `ALTER TABLE`, что плохо масштабируется. |
| Один большой репозиторий БД | `shared/repositories.py` | Класс `ORM` содержит слишком много обязанностей: users, laws, consultations, search. |
| Один большой сервис LLM | `api/services/legal_ai.py` | В одном файле смешаны классификация, ответ, fallback, sanitizing и генерация заголовка. |
| `bot/requirements.txt` содержит лишнее | `bot/requirements.txt` | Там есть `fastapi`, `uvicorn`, parser-зависимости. Контейнер бота тяжелее, чем должен быть. |
| `cfg_loader.py` выглядит мертвым кодом | `bot/utils/cfg_loader.py` | Ищет `cfg/config.json`, но такой папки в проекте нет и импортов почти нет. |
| `bot/webhooks.py` не подключен | `bot/webhooks.py`, `compose.yml` | В проекте есть отдельный FastAPI webhook-файл, но он не участвует в основном запуске. |
| Нет тестов | по структуре файлов | В проекте не найдено папки `tests/` или явных unit/integration тестов. |
| Парсер завязан на HTML-структуру сайта | `parser/fetcher.py`, `parser/normalizer.py` | Любое изменение верстки `consultant.ru` может сломать парсинг. |
| Full-text поиск только на русском tsvector | `shared/repositories.py` | Это нормально для РФ, но поиск по сложным формулировкам может быть нестабилен без доп. нормализации. |
| В `api` автоиндексация идет при старте | `api/main.py` | Удобно для MVP, но на большом объеме это может тормозить старт сервиса. |
| В репозитории есть `.venv/` | структура проекта | Локальное окружение лежит внутри проекта. Это легко случайно потащить в архив или deploy-контекст. |

### 10.1. Неясные или частично подтвержденные зоны

| Наблюдение | Что именно неясно |
|---|---|
| Админская часть | Полный функционал не анализировался построчно во всех admin handlers |
| `protocol/` | Это не runtime-часть приложения, а документы для курсовой |
| `bot/keyboards/inline_keyboards.py` | Файл существует, но по структуре почти пустой и не играет заметной роли |

---

## 11. Рекомендации по улучшению структуры

### 11.1. Что стоит сделать в первую очередь

| Рекомендация | Зачем |
|---|---|
| Выделить `parser` в отдельный контейнер | Чтобы бот не таскал лишние parser-зависимости |
| Ввести миграции (`alembic`) | Чтобы схема БД менялась предсказуемо |
| Разбить `shared/repositories.py` на несколько репозиториев | Например: `users`, `consultations`, `laws`, `rag` |
| Разбить `api/services/legal_ai.py` | Отдельно: classifier, answer renderer, title generator, fallback |
| Добавить `tests/` | Минимум smoke-тесты для `api`, `parser`, `shared` |
| Вынести parser-зависимости из `bot/requirements.txt` | Уменьшить размер и сложность контейнера бота |

### 11.2. Что улучшит понятность проекта

| Рекомендация | Зачем |
|---|---|
| Добавить `docs/architecture.md` | Новичку будет проще понять проект |
| Сделать единый `settings`-слой и для `bot`, и для `parser` | Сейчас конфигурация читается разными способами |
| Удалить или подключить мертвые файлы | Например `bot/webhooks.py`, `bot/utils/cfg_loader.py` |
| Добавить схему “как идет запрос” в README | Снизит порог входа |

### 11.3. Что важно для production

| Рекомендация | Зачем |
|---|---|
| Ограничить и логировать ошибки внешних API | OpenRouter и Consultant могут вести себя нестабильно |
| Добавить rate limiting и retry policy на уровне API-клиентов | Улучшит устойчивость |
| Отдельно мониторить время поиска и генерации | Позволит понять, где тормозит система |
| Добавить backup-стратегию для `volumes/postgres` и `volumes/chroma` | Сейчас данные локально персистентны, но не защищены от потери |

---

## 12. Краткий вывод

Проект уже собран как рабочий MVP:

- есть Telegram-бот;
- есть FastAPI-сервис для RAG;
- есть парсер законов;
- есть PostgreSQL и ChromaDB;
- есть единый DB-слой.

Главная архитектурная идея понятная:

1. `parser` наполняет PostgreSQL законами;
2. `api` строит векторный индекс и отвечает на вопросы;
3. `bot` общается с пользователем;
4. `shared` соединяет все это с БД.

Главные слабые места сейчас:

- смешение ролей `bot` и `parser`;
- отсутствие миграций;
- слишком крупные файлы `shared/repositories.py` и `api/services/legal_ai.py`;
- отсутствие автоматических тестов.

Для новичка проект уже читаемый, но поддерживать его дальше будет проще после разделения больших модулей на более мелкие.
