# MVP Telegram-бота: юридический ИИ-консультант по законам РФ Версия: 1.0 География: только Российская Федерация Основная функция: юридическая консультация через RAG по базе законов РФ LLM-интеграция: OpenRouter или любой OpenAI-compatible API Язык интерфейса: русский Платформа: Telegram --- ## 1. Что делаем в MVP MVP — это Telegram-бот, который: 1. принимает юридический вопрос пользователя; 2. уточняет категорию права и регион РФ; 3. формирует поисковые запросы для RAG; 4. ищет релевантные нормы в локальной базе законов; 5. отвечает простым языком; 6. показывает найденные источники; 7. сохраняет консультацию в историю. Главная ценность: > Пользователь задает вопрос, бот находит применимые нормы закона РФ, объясняет их простыми словами и дает базовый план действий. --- ## 2. Что НЕ входит в MVP В MVP НЕ делаем: - проверку PDF/DOCX-документов; - генерацию исков; - генерацию договоров; - генерацию претензий; - оплату и тарифы; - личный кабинет юриста; - передачу живому специалисту; - голосовые сообщения; - OCR; - сложную админ-панель; - судебную практику как обязательный источник; - автоматическое полное обновление всей правовой базы. --- ## 3. Главное меню Telegram-бота Минимальное меню: ```text ⚖️ Задать вопрос 📚 Мои консультации 👤 Профиль ℹ️ Помощь ``` --- ## 4. Раздел «⚖️ Задать вопрос» ### 4.1. Категории MVP После нажатия «Задать вопрос» бот показывает категории: ```text Выберите категорию вопроса: 💼 Работа 🛒 Защита прав потребителей 🏠 Жилье / аренда 👪 Семья 💰 Долги / займы 📄 Договоры ⚖️ Суд / процесс ❓ Другое ``` Для MVP не нужно покрывать все право. Достаточно основных бытовых категорий. --- ### 4.2. Базовый сценарий консультации ```mermaid flowchart TD A[Пользователь нажал Задать вопрос] --> B[Выбор категории] B --> C[Бот просит описать ситуацию] C --> D[Пользователь пишет вопрос] D --> E[Бот уточняет регион РФ] E --> F[Пользователь указывает регион] F --> G[Создание консультации в БД] G --> H[LLM классифицирует вопрос] H --> I[LLM формирует RAG-запросы] I --> J[Hybrid search по базе законов] J --> K[Rerank найденных чанков] K --> L[LLM формирует ответ только по источникам] L --> M[Бот отправляет ответ] M --> N[Сохранение ответа и источников в БД] ``` --- ### 4.3. Сообщения бота #### Старт ```text Здравствуйте. Я юридический ИИ-консультант по законам РФ. Я могу помочь: — разобраться в ситуации; — найти применимые нормы закона; — объяснить их простыми словами; — дать базовый план действий. Ответ носит информационный характер и не заменяет консультацию юриста. ``` #### Запрос описания ```text Опишите ситуацию одним сообщением. Постарайтесь указать: — что произошло; — когда произошло; — с кем спор; — чего вы хотите добиться. ``` #### Запрос региона ```text Укажите регион РФ, где произошла ситуация. Например: Москва Санкт-Петербург Краснодарский край Республика Татарстан ``` --- ## 5. Формат ответа бота Бот должен отвечать в одном стабильном формате: ```text ⚖️ Краткий вывод ... 📌 Что говорит закон ... ✅ Что можно сделать 1. ... 2. ... 3. ... ⚠️ Риски и ограничения ... 📚 Найденные источники 1. ... 2. ... 3. ... ❗ Важно Я ИИ-консультант, а не адвокат. Ответ носит информационный характер. Для суда, крупных сумм, уголовных дел и сложных споров лучше обратиться к юристу. ``` --- ## 6. Главное правило RAG Строгое правило: > Если источник не найден в RAG — бот не имеет права ссылаться на него. Нельзя писать: ```text Возможно, применяется статья 450 ГК РФ. ``` Нужно писать только так: ```text Найденные источники: — ГК РФ, статья 450 — основания изменения и расторжения договора ``` Если источник не найден: ```text Я не нашел в базе надежную норму по этому вопросу. Могу дать только общий комментарий без ссылки на конкретную статью. ``` --- ## 7. Как RAG будет искать подходящие нормы ### 7.1. Входные данные На вход RAG-пайплайна идут: ```json { "user_question": "Работодатель не выплатил зарплату за последний месяц. Что делать?", "category": "labor", "region": "Москва", "jurisdiction": "RU", "user_type": "physical_person" } ``` --- ### 7.2. Шаг 1 — классификация вопроса LLM получает вопрос и возвращает структурированный JSON: ```json { "legal_domain": "labor_law", "jurisdiction": "RU", "region": "Москва", "issue_type": "salary_delay", "needs_clarification": false, "search_intents": [ "срок выплаты заработной платы", "ответственность работодателя за задержку зарплаты", "компенсация за задержку заработной платы", "жалоба в трудовую инспекцию" ] } ``` Если данных мало: ```json { "legal_domain": "consumer_protection", "needs_clarification": true, "questions": [ "Товар был с недостатком или качественный?", "Сколько дней прошло с покупки?", "Покупка была онлайн или в магазине?" ] } ``` В этом случае бот сначала задает уточняющие вопросы, а не запускает финальный ответ. --- ### 7.3. Шаг 2 — генерация поисковых запросов LLM формирует 3–5 поисковых запросов: ```json { "queries": [ "ТК РФ срок выплаты заработной платы", "ТК РФ задержка заработной платы компенсация", "ответственность работодателя за невыплату зарплаты", "куда жаловаться если не выплатили зарплату" ], "filters": { "jurisdiction": "RU", "law_type": ["labor"], "is_active": true } } ``` Важно: запросы генерирует LLM, но поиск выполняется не по интернету, а по локальной базе законов. --- ### 7.4. Шаг 3 — hybrid search Для юридического поиска нельзя полагаться только на embeddings. Нужно использовать гибридный поиск: 1. `vector search` — поиск по смыслу; 2. `full-text / BM25` — поиск по точным словам; 3. `metadata filters` — фильтр по юрисдикции, отрасли права, актуальности; 4. `reranker` — финальная сортировка найденных чанков. Минимальная схема: ```mermaid flowchart LR A[LLM search queries] --> B[Vector search] A --> C[Full-text search] B --> D[Merge results] C --> D D --> E[Metadata filters] E --> F[Reranker] F --> G[Top 3-7 chunks for answer] ``` --- ### 7.5. Шаг 4 — rerank Поиск может вернуть 20–50 фрагментов. В ответ LLM нельзя отправлять всё. Рекомендуемый режим MVP: ```text vector_top_k = 20 text_top_k = 20 merged_top_k = 30 rerank_top_k = 5 ``` В финальный промпт LLM отправляем только 3–7 самых релевантных чанков. --- ### 7.6. Шаг 5 — генерация ответа LLM получает: ```json { "user_question": "...", "category": "...", "region": "...", "retrieved_sources": [ { "source_title": "Трудовой кодекс РФ", "article": "136", "article_title": "Порядок, место и сроки выплаты заработной платы", "text": "..." }, { "source_title": "Трудовой кодекс РФ", "article": "236", "article_title": "Материальная ответственность работодателя за задержку выплаты заработной платы", "text": "..." } ] } ``` Системное правило для LLM: ```text Отвечай только на основании retrieved_sources. Не придумывай статьи, номера законов и судебную практику. Если источников недостаточно, прямо скажи, что данных в базе недостаточно. ``` --- ## 8. Раздел «📚 Мои консультации» Пользователь видит список своих консультаций: ```text 📚 Ваши консультации: 1. Невыплата зарплаты — 23.05.2026 2. Возврат товара — 22.05.2026 3. Аренда квартиры — 21.05.2026 ``` Кнопки по консультации: ```text 👁 Открыть 🔁 Продолжить 🗑 Удалить ``` ### 8.1. Продолжить консультацию Если пользователь нажал «Продолжить», новые сообщения добавляются к старой консультации. В RAG отправляется: 1. новый вопрос; 2. краткая история консультации; 3. старые найденные источники; 4. при необходимости — новый поиск. --- ## 9. Раздел «👤 Профиль» Минимальный профиль: ```text 👤 Профиль Страна: Россия Регион: Москва Тип пользователя: Физлицо ``` Кнопки: ```text 🌍 Изменить регион 👔 Изменить тип пользователя 🗑 Удалить мои данные ``` Тип пользователя: ```text Физлицо ИП ООО ``` Для MVP тип пользователя нужен только как контекст для LLM. --- ## 10. Раздел «ℹ️ Помощь» ```text ℹ️ Как пользоваться ботом 1. Нажмите «Задать вопрос». 2. Выберите категорию. 3. Опишите ситуацию. 4. Укажите регион РФ. 5. Получите ответ с найденными источниками. Пример хорошего вопроса: «Работодатель не выплатил зарплату за апрель. Работаю официально, Москва. Что делать?» Бот не заменяет юриста и не гарантирует результат спора. ``` --- ## 11. Архитектура MVP ### 11.1. Минимальная архитектура ```mermaid flowchart TD TG[Telegram] --> BOT[bot-service aiogram] BOT --> DB[(PostgreSQL)] BOT --> RAG[RAG service] RAG --> VDB[(pgvector / Qdrant)] RAG --> DB RAG --> LLM[OpenRouter / OpenAI-compatible API] BOT --> LLM ``` Для самого простого MVP можно сделать монолит: ```text one Python app: — aiogram bot — FastAPI optional — RAG logic — PostgreSQL access — OpenAI-compatible client ``` --- ### 11.2. Рекомендуемый стек ```text Python 3.12+ aiogram 3.x SQLAlchemy 2.x Alembic PostgreSQL 16+ pgvector или Qdrant Redis optional OpenAI Python SDK OpenRouter или OpenAI-compatible LLM Docker Compose ``` --- ## 12. Хранилище ### 12.1. Таблицы MVP ```text users consultations messages law_sources law_chunks rag_queries ``` --- ### 12.2. users ```sql CREATE TABLE users ( id BIGSERIAL PRIMARY KEY, telegram_id BIGINT UNIQUE NOT NULL, username TEXT, first_name TEXT, country TEXT NOT NULL DEFAULT 'Россия', region TEXT, user_type TEXT NOT NULL DEFAULT 'physical_person', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` `user_type`: ```text physical_person individual_entrepreneur company ``` --- ### 12.3. consultations ```sql CREATE TABLE consultations ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE CASCADE, category TEXT NOT NULL, title TEXT, region TEXT, status TEXT NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` --- ### 12.4. messages ```sql CREATE TABLE messages ( id BIGSERIAL PRIMARY KEY, consultation_id BIGINT NOT NULL REFERENCES consultations(id) ON DELETE CASCADE, role TEXT NOT NULL, content TEXT NOT NULL, sources_json JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` `role`: ```text user assistant system ``` --- ### 12.5. law_sources ```sql CREATE TABLE law_sources ( id BIGSERIAL PRIMARY KEY, title TEXT NOT NULL, source_type TEXT NOT NULL, jurisdiction TEXT NOT NULL DEFAULT 'RU', law_type TEXT, document_number TEXT, adoption_date DATE, publication_date DATE, effective_date DATE, source_url TEXT, official_publication_number TEXT, version_hash TEXT, is_active BOOLEAN NOT NULL DEFAULT true, loaded_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` Примеры `law_type`: ```text civil labor consumer family housing tax procedural administrative ``` --- ### 12.6. law_chunks Для pgvector: ```sql CREATE EXTENSION IF NOT EXISTS vector; CREATE TABLE law_chunks ( id BIGSERIAL PRIMARY KEY, source_id BIGINT NOT NULL REFERENCES law_sources(id) ON DELETE CASCADE, chunk_index INT NOT NULL, article_number TEXT, article_title TEXT, chunk_text TEXT NOT NULL, metadata JSONB, embedding vector(1536), tsv tsvector, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` Индексы: ```sql CREATE INDEX law_chunks_source_id_idx ON law_chunks(source_id); CREATE INDEX law_chunks_article_number_idx ON law_chunks(article_number); CREATE INDEX law_chunks_tsv_idx ON law_chunks USING GIN(tsv); CREATE INDEX law_chunks_embedding_idx ON law_chunks USING ivfflat (embedding vector_cosine_ops); ``` Размерность `embedding vector(1536)` нужно заменить под выбранную embedding-модель. --- ### 12.7. rag_queries ```sql CREATE TABLE rag_queries ( id BIGSERIAL PRIMARY KEY, consultation_id BIGINT REFERENCES consultations(id) ON DELETE CASCADE, user_message_id BIGINT REFERENCES messages(id) ON DELETE CASCADE, generated_queries JSONB NOT NULL, retrieved_chunks JSONB NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); ``` Нужно для отладки качества RAG. --- ## 13. LLM-интеграция OpenRouter / OpenAI-compatible OpenRouter поддерживает OpenAI-compatible API-стиль. Поэтому в коде лучше сразу писать через `openai` Python SDK с настраиваемыми: ```text OPENAI_BASE_URL OPENAI_API_KEY LLM_MODEL EMBEDDING_MODEL ``` ### 13.1. Пример клиента ```python from openai import OpenAI import os client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL", "https://openrouter.ai/api/v1"), ) response = client.chat.completions.create( model=os.getenv("LLM_MODEL", "openai/gpt-4.1-mini"), messages=[ {"role": "system", "content": "Ты юридический ИИ-консультант по законам РФ."}, {"role": "user", "content": "Работодатель не выплатил зарплату. Что делать?"} ], temperature=0.2, ) ``` Для OpenAI напрямую: ```env OPENAI_BASE_URL=https://api.openai.com/v1 OPENAI_API_KEY=... LLM_MODEL=gpt-4.1-mini ``` Для OpenRouter: ```env OPENAI_BASE_URL=https://openrouter.ai/api/v1 OPENAI_API_KEY=... LLM_MODEL=openai/gpt-4.1-mini ``` --- ## 14. Structured output для классификации Классификацию лучше получать строго в JSON. ### 14.1. Prompt: классификатор ```text Ты классификатор юридических вопросов по законам РФ. Верни только JSON без markdown. Поля: - legal_domain - issue_type - jurisdiction - region - needs_clarification - clarification_questions - search_queries - filters Правила: 1. jurisdiction всегда RU. 2. Если данных недостаточно, needs_clarification = true. 3. search_queries должны быть пригодны для поиска по базе законов. 4. Не придумывай статьи. 5. Не давай юридический ответ на этом этапе. ``` ### 14.2. Пример результата ```json { "legal_domain": "labor", "issue_type": "salary_delay", "jurisdiction": "RU", "region": "Москва", "needs_clarification": false, "clarification_questions": [], "search_queries": [ "ТК РФ сроки выплаты заработной платы", "ТК РФ задержка заработной платы компенсация", "ответственность работодателя за задержку зарплаты" ], "filters": { "law_type": ["labor"], "jurisdiction": "RU", "is_active": true } } ``` --- ## 15. Prompt для финального ответа ```text Ты юридический ИИ-консультант по законам РФ. Твоя задача — ответить пользователю простым языком на основании найденных источников. Жесткие правила: 1. Используй только SOURCES. 2. Не придумывай статьи, номера законов, судебную практику и сроки. 3. Если источников недостаточно, прямо скажи об этом. 4. Не обещай победу в суде. 5. Не выдавай себя за адвоката. 6. Не помогай обходить закон. 7. В конце добавь дисклеймер. Формат ответа: ⚖️ Краткий вывод ... 📌 Что говорит закон ... ✅ Что можно сделать 1. ... 2. ... 3. ... ⚠️ Риски и ограничения ... 📚 Найденные источники 1. [Название, статья, краткое описание] 2. ... ❗ Важно Ответ носит информационный характер и не заменяет консультацию юриста. ``` --- ## 16. Источники законов РФ для базы ### 16.1. Основной официальный источник Основной источник для MVP: ```text Официальный интернет-портал правовой информации https://pravo.gov.ru/ https://publication.pravo.gov.ru/ ``` Что важно: - это официальный контур опубликования правовых актов РФ; - на `publication.pravo.gov.ru` есть раздел официального опубликования; - есть раздел открытых данных; - есть API-интерфейс; - есть поиск по документам. Практически для MVP: ```text 1. Использовать publication.pravo.gov.ru как первичный источник новых опубликованных актов. 2. Забрать нужные кодексы и федеральные законы. 3. Сохранять source_url, дату публикации, номер документа, hash версии. 4. Не пытаться на первом этапе спарсить всё право РФ. ``` Полезные URL: ```text https://publication.pravo.gov.ru/ https://publication.pravo.gov.ru/OpenData https://publication.pravo.gov.ru/help ``` --- ### 16.2. Дополнительный официальный источник Дополнительный источник: ```text Портал Минюста России «Нормативные правовые акты в Российской Федерации» https://pravo.minjust.ru/ ``` Что полезно: - федеральное законодательство; - законодательство субъектов РФ; - муниципальные акты; - текущие и предыдущие редакции НПА. Полезные URL: ```text https://pravo.minjust.ru/ https://pravo.minjust.ru/about_project https://pravo.minjust.ru/about_project/setting_portal ``` --- ### 16.3. Что парсить для MVP Не нужно сразу загружать все законы. Для MVP достаточно: ```text 1. Конституция РФ 2. Гражданский кодекс РФ 3. Трудовой кодекс РФ 4. Семейный кодекс РФ 5. Жилищный кодекс РФ 6. Закон РФ «О защите прав потребителей» 7. Гражданский процессуальный кодекс РФ — базовые статьи 8. КоАП РФ — только базовые бытовые составы, если нужен административный блок ``` Для категорий MVP этого достаточно: | Категория в боте | Источники | |---|---| | Работа | ТК РФ | | Потребители | Закон о защите прав потребителей, ГК РФ | | Жилье / аренда | ГК РФ, ЖК РФ | | Семья | СК РФ | | Долги / займы | ГК РФ | | Договоры | ГК РФ | | Суд / процесс | ГПК РФ | --- ### 16.4. Почему не стоит «парсить вообще всё» в MVP Полная база законодательства РФ — это сложно, потому что: - много редакций документов; - есть федеральный, региональный и муниципальный уровни; - есть акты, утратившие силу; - у документов есть изменения и редакции; - важна дата актуальности; - часть пользовательских вопросов требует не только закон, но и практику применения. Для MVP правильнее сделать узкую, но качественную базу: ```text Кодексы + ключевые федеральные законы + метаданные + актуальность + хороший поиск. ``` --- ## 17. Ingestion pipeline законов ### 17.1. Схема загрузки ```mermaid flowchart TD A[Источник: publication.pravo.gov.ru / pravo.minjust.ru] --> B[Fetcher] B --> C[Raw storage] C --> D[Text extractor / HTML parser] D --> E[Normalizer] E --> F[Split by articles] F --> G[Chunker] G --> H[Embeddings] H --> I[(Vector DB / pgvector)] E --> J[(PostgreSQL law_sources)] ``` --- ### 17.2. Этапы #### 1. Fetcher Задача: ```text — скачать HTML/PDF/текст документа; — сохранить оригинальный файл; — сохранить URL; — сохранить дату загрузки; — сохранить hash. ``` #### 2. Raw storage Сохранять оригиналы обязательно: ```text /data/raw_laws/ pravo/ 2026-05-23/ document_0001.html document_0001.json ``` #### 3. Normalizer Приводит документ к единому виду: ```json { "title": "Трудовой кодекс Российской Федерации", "document_number": "197-ФЗ", "adoption_date": "2001-12-30", "source_url": "...", "text": "...", "articles": [] } ``` #### 4. Split by articles Кодексы нужно резать по статьям. Пример структуры: ```json { "article_number": "136", "article_title": "Порядок, место и сроки выплаты заработной платы", "text": "..." } ``` #### 5. Chunker Если статья длинная, делить на чанки. Рекомендация: ```text chunk_size = 1000-1800 tokens chunk_overlap = 100-200 tokens ``` Для коротких статей — один chunk = одна статья. #### 6. Embeddings Для каждого чанка: ```text embedding = embedding_model(chunk_text + article_title + source_title) ``` #### 7. Full-text index Дополнительно делать `tsvector` для PostgreSQL: ```sql UPDATE law_chunks SET tsv = to_tsvector('russian', chunk_text); ``` --- ## 18. Актуальность законов Для каждого источника хранить: ```text source_url publication_date effective_date loaded_at version_hash is_active ``` Если документ обновился: ```text 1. старая версия помечается is_active = false; 2. новая версия добавляется как новая запись; 3. чанки старой версии исключаются из поиска; 4. история консультаций сохраняет ссылки на версию, которая использовалась на момент ответа. ``` --- ## 19. Retrieval API внутри проекта Минимальная функция: ```python async def retrieve_law_chunks( queries: list[str], law_types: list[str], jurisdiction: str = "RU", top_k: int = 5, ) -> list[LawChunk]: ... ``` Внутри: ```text 1. Создать embedding для каждого query. 2. Выполнить vector search. 3. Выполнить full-text search. 4. Слить результаты. 5. Убрать дубли. 6. Отфильтровать is_active = true. 7. Прогнать reranker. 8. Вернуть top_k чанков. ``` --- ## 20. Пример SQL для hybrid search ### 20.1. Full-text search ```sql SELECT lc.id, lc.chunk_text, lc.article_number, lc.article_title, ls.title AS source_title, ts_rank(lc.tsv, plainto_tsquery('russian', :query)) AS score FROM law_chunks lc JOIN law_sources ls ON ls.id = lc.source_id WHERE ls.jurisdiction = 'RU' AND ls.is_active = true AND lc.tsv @@ plainto_tsquery('russian', :query) ORDER BY score DESC LIMIT 20; ``` ### 20.2. Vector search ```sql SELECT lc.id, lc.chunk_text, lc.article_number, lc.article_title, ls.title AS source_title, 1 - (lc.embedding <=> :query_embedding) AS score FROM law_chunks lc JOIN law_sources ls ON ls.id = lc.source_id WHERE ls.jurisdiction = 'RU' AND ls.is_active = true ORDER BY lc.embedding <=> :query_embedding LIMIT 20; ``` --- ## 21. FSM-состояния aiogram Минимальные состояния: ```python class AskQuestion(StatesGroup): choosing_category = State() waiting_question = State() waiting_region = State() processing = State() ``` Сценарий: ```text /start -> main_menu Задать вопрос -> choosing_category Выбрана категория -> waiting_question Пользователь написал вопрос -> waiting_region Пользователь указал регион -> processing -> RAG -> answer -> main_menu ``` --- ## 22. Ограничения MVP ### 22.1. Лимиты Чтобы не сжечь бюджет LLM: ```text Free MVP: — 5 консультаций в день на пользователя — максимум 5 сообщений в одной консультации — максимум 7 retrieved chunks в одном ответе — timeout LLM-запроса 60 секунд ``` ### 22.2. Rate limit Ключ: ```text rate_limit:{telegram_id}:{date} ``` Хранить можно в Redis или PostgreSQL. --- ## 23. Логирование Логировать: ```text telegram_id consultation_id category region user_question generated_queries retrieved_chunk_ids llm_model tokens_input tokens_output latency_ms errors ``` Не логировать лишние персональные данные без необходимости. --- ## 24. Safety rules Бот не должен: ```text — гарантировать победу в суде; — обещать точный исход дела; — советовать подделывать документы; — помогать скрывать доходы; — помогать обходить закон; — составлять фиктивные схемы; — выдавать себя за адвоката; — давать категоричные инструкции по уголовным делам без рекомендации юриста. ``` В сложных случаях: ```text По этому вопросу лучше обратиться к юристу/адвокату, потому что ошибка может повлечь серьезные последствия. ``` --- ## 25. Минимальный docker-compose ```yaml services: bot: build: . env_file: - .env depends_on: - postgres restart: unless-stopped postgres: image: pgvector/pgvector:pg16 environment: POSTGRES_DB: legal_bot POSTGRES_USER: legal_bot POSTGRES_PASSWORD: legal_bot_password volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" restart: unless-stopped volumes: postgres_data: ``` --- ## 26. .env ```env BOT_TOKEN= DATABASE_URL=postgresql+asyncpg://legal_bot:legal_bot_password@postgres:5432/legal_bot OPENAI_BASE_URL=https://openrouter.ai/api/v1 OPENAI_API_KEY= LLM_MODEL=openai/gpt-4.1-mini EMBEDDING_PROVIDER=openai EMBEDDING_MODEL=text-embedding-3-small DAILY_CONSULTATION_LIMIT=5 ``` --- ## 27. MVP roadmap ### Этап 1 — Telegram shell ```text — /start — главное меню — выбор категории — ввод вопроса — ввод региона — сохранение пользователя ``` ### Этап 2 — база законов ```text — загрузить 5–8 основных источников — распарсить по статьям — сохранить law_sources — сохранить law_chunks — построить embeddings — построить full-text index ``` ### Этап 3 — RAG ```text — классификатор вопроса — генератор search_queries — hybrid search — rerank — финальный ответ с источниками ``` ### Этап 4 — история ```text — список консультаций — открыть консультацию — продолжить консультацию — удалить консультацию ``` ### Этап 5 — защита от мусора ```text — лимиты — логирование — обработка ошибок LLM — fallback если RAG ничего не нашел — дисклеймер ``` --- ## 28. Критерии готовности MVP MVP можно считать готовым, если: ```text 1. Пользователь может задать вопрос. 2. Бот уточняет категорию и регион. 3. LLM генерирует поисковые запросы. 4. RAG возвращает реальные статьи из базы. 5. Ответ содержит только найденные источники. 6. История консультации сохраняется. 7. Пользователь может открыть старую консультацию. 8. Если источников нет, бот честно говорит об этом. ``` --- ## 29. Короткое ТЗ в одну фразу Сделать Telegram-бота на Python/aiogram, который консультирует пользователей по законам РФ: принимает вопрос, уточняет категорию и регион, через OpenAI-compatible LLM формирует RAG-запросы, ищет по локальной базе законов РФ через hybrid search, отвечает простым языком с найденными источниками и сохраняет консультации в историю. --- ## 30. Главный принцип разработки Не делать «ChatGPT в Telegram». Делать юридический workflow: ```text Категория → Вопрос → Регион → Классификация → RAG → Ответ с источниками → История ``` Самое важное качество MVP: ```text Лучше меньше категорий и источников, но чтобы бот не выдумывал статьи и всегда показывал, откуда взял правовую норму. ```