# 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`; - отсутствие автоматических тестов. Для новичка проект уже читаемый, но поддерживать его дальше будет проще после разделения больших модулей на более мелкие.