Files
LawBot/SYSTEM_AUDIT.md
2026-05-25 01:12:43 +03:00

488 lines
24 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`;
- отсутствие автоматических тестов.
Для новичка проект уже читаемый, но поддерживать его дальше будет проще после разделения больших модулей на более мелкие.