Files
2026-05-25 01:12:43 +03:00

35 KiB
Raw Permalink Blame History

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-бота

Минимальное меню:

⚖️ Задать вопрос
📚 Мои консультации
👤 Профиль
ℹ️ Помощь

4. Раздел «⚖️ Задать вопрос»

4.1. Категории MVP

После нажатия «Задать вопрос» бот показывает категории:

Выберите категорию вопроса:

💼 Работа
🛒 Защита прав потребителей
🏠 Жилье / аренда
👪 Семья
💰 Долги / займы
📄 Договоры
⚖️ Суд / процесс
❓ Другое

Для MVP не нужно покрывать все право. Достаточно основных бытовых категорий.


4.2. Базовый сценарий консультации

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. Сообщения бота

Старт

Здравствуйте. Я юридический ИИ-консультант по законам РФ.

Я могу помочь:
— разобраться в ситуации;
— найти применимые нормы закона;
— объяснить их простыми словами;
— дать базовый план действий.

Ответ носит информационный характер и не заменяет консультацию юриста.

Запрос описания

Опишите ситуацию одним сообщением.

Постарайтесь указать:
— что произошло;
— когда произошло;
— с кем спор;
— чего вы хотите добиться.

Запрос региона

Укажите регион РФ, где произошла ситуация.

Например:
Москва
Санкт-Петербург
Краснодарский край
Республика Татарстан

5. Формат ответа бота

Бот должен отвечать в одном стабильном формате:

⚖️ Краткий вывод
...

📌 Что говорит закон
...

✅ Что можно сделать
1. ...
2. ...
3. ...

⚠️ Риски и ограничения
...

📚 Найденные источники
1. ...
2. ...
3. ...

❗ Важно
Я ИИ-консультант, а не адвокат. Ответ носит информационный характер. Для суда, крупных сумм, уголовных дел и сложных споров лучше обратиться к юристу.

6. Главное правило RAG

Строгое правило:

Если источник не найден в RAG — бот не имеет права ссылаться на него.

Нельзя писать:

Возможно, применяется статья 450 ГК РФ.

Нужно писать только так:

Найденные источники:
— ГК РФ, статья 450 — основания изменения и расторжения договора

Если источник не найден:

Я не нашел в базе надежную норму по этому вопросу. Могу дать только общий комментарий без ссылки на конкретную статью.

7. Как RAG будет искать подходящие нормы

7.1. Входные данные

На вход RAG-пайплайна идут:

{
  "user_question": "Работодатель не выплатил зарплату за последний месяц. Что делать?",
  "category": "labor",
  "region": "Москва",
  "jurisdiction": "RU",
  "user_type": "physical_person"
}

7.2. Шаг 1 — классификация вопроса

LLM получает вопрос и возвращает структурированный JSON:

{
  "legal_domain": "labor_law",
  "jurisdiction": "RU",
  "region": "Москва",
  "issue_type": "salary_delay",
  "needs_clarification": false,
  "search_intents": [
    "срок выплаты заработной платы",
    "ответственность работодателя за задержку зарплаты",
    "компенсация за задержку заработной платы",
    "жалоба в трудовую инспекцию"
  ]
}

Если данных мало:

{
  "legal_domain": "consumer_protection",
  "needs_clarification": true,
  "questions": [
    "Товар был с недостатком или качественный?",
    "Сколько дней прошло с покупки?",
    "Покупка была онлайн или в магазине?"
  ]
}

В этом случае бот сначала задает уточняющие вопросы, а не запускает финальный ответ.


7.3. Шаг 2 — генерация поисковых запросов

LLM формирует 3–5 поисковых запросов:

{
  "queries": [
    "ТК РФ срок выплаты заработной платы",
    "ТК РФ задержка заработной платы компенсация",
    "ответственность работодателя за невыплату зарплаты",
    "куда жаловаться если не выплатили зарплату"
  ],
  "filters": {
    "jurisdiction": "RU",
    "law_type": ["labor"],
    "is_active": true
  }
}

Важно: запросы генерирует LLM, но поиск выполняется не по интернету, а по локальной базе законов.


Для юридического поиска нельзя полагаться только на embeddings.

Нужно использовать гибридный поиск:

  1. vector search — поиск по смыслу;
  2. full-text / BM25 — поиск по точным словам;
  3. metadata filters — фильтр по юрисдикции, отрасли права, актуальности;
  4. reranker — финальная сортировка найденных чанков.

Минимальная схема:

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:

vector_top_k = 20
text_top_k = 20
merged_top_k = 30
rerank_top_k = 5

В финальный промпт LLM отправляем только 3–7 самых релевантных чанков.


7.6. Шаг 5 — генерация ответа

LLM получает:

{
  "user_question": "...",
  "category": "...",
  "region": "...",
  "retrieved_sources": [
    {
      "source_title": "Трудовой кодекс РФ",
      "article": "136",
      "article_title": "Порядок, место и сроки выплаты заработной платы",
      "text": "..."
    },
    {
      "source_title": "Трудовой кодекс РФ",
      "article": "236",
      "article_title": "Материальная ответственность работодателя за задержку выплаты заработной платы",
      "text": "..."
    }
  ]
}

Системное правило для LLM:

Отвечай только на основании retrieved_sources.
Не придумывай статьи, номера законов и судебную практику.
Если источников недостаточно, прямо скажи, что данных в базе недостаточно.

8. Раздел «📚 Мои консультации»

Пользователь видит список своих консультаций:

📚 Ваши консультации:

1. Невыплата зарплаты — 23.05.2026
2. Возврат товара — 22.05.2026
3. Аренда квартиры — 21.05.2026

Кнопки по консультации:

👁 Открыть
🔁 Продолжить
🗑 Удалить

8.1. Продолжить консультацию

Если пользователь нажал «Продолжить», новые сообщения добавляются к старой консультации.

В RAG отправляется:

  1. новый вопрос;
  2. краткая история консультации;
  3. старые найденные источники;
  4. при необходимости — новый поиск.

9. Раздел «👤 Профиль»

Минимальный профиль:

👤 Профиль

Страна: Россия
Регион: Москва
Тип пользователя: Физлицо

Кнопки:

🌍 Изменить регион
👔 Изменить тип пользователя
🗑 Удалить мои данные

Тип пользователя:

Физлицо
ИП
ООО

Для MVP тип пользователя нужен только как контекст для LLM.


10. Раздел « Помощь»

ℹ️ Как пользоваться ботом

1. Нажмите «Задать вопрос».
2. Выберите категорию.
3. Опишите ситуацию.
4. Укажите регион РФ.
5. Получите ответ с найденными источниками.

Пример хорошего вопроса:
«Работодатель не выплатил зарплату за апрель. Работаю официально, Москва. Что делать?»

Бот не заменяет юриста и не гарантирует результат спора.

11. Архитектура MVP

11.1. Минимальная архитектура

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 можно сделать монолит:

one Python app:
— aiogram bot
— FastAPI optional
— RAG logic
— PostgreSQL access
— OpenAI-compatible client

11.2. Рекомендуемый стек

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

users
consultations
messages
law_sources
law_chunks
rag_queries

12.2. users

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:

physical_person
individual_entrepreneur
company

12.3. consultations

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

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:

user
assistant
system

12.5. law_sources

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:

civil
labor
consumer
family
housing
tax
procedural
administrative

12.6. law_chunks

Для pgvector:

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()
);

Индексы:

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

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 с настраиваемыми:

OPENAI_BASE_URL
OPENAI_API_KEY
LLM_MODEL
EMBEDDING_MODEL

13.1. Пример клиента

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 напрямую:

OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_API_KEY=...
LLM_MODEL=gpt-4.1-mini

Для OpenRouter:

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: классификатор

Ты классификатор юридических вопросов по законам РФ.

Верни только 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. Пример результата

{
  "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 для финального ответа

Ты юридический ИИ-консультант по законам РФ.

Твоя задача — ответить пользователю простым языком на основании найденных источников.

Жесткие правила:
1. Используй только SOURCES.
2. Не придумывай статьи, номера законов, судебную практику и сроки.
3. Если источников недостаточно, прямо скажи об этом.
4. Не обещай победу в суде.
5. Не выдавай себя за адвоката.
6. Не помогай обходить закон.
7. В конце добавь дисклеймер.

Формат ответа:

⚖️ Краткий вывод
...

📌 Что говорит закон
...

✅ Что можно сделать
1. ...
2. ...
3. ...

⚠️ Риски и ограничения
...

📚 Найденные источники
1. [Название, статья, краткое описание]
2. ...

❗ Важно
Ответ носит информационный характер и не заменяет консультацию юриста.

16. Источники законов РФ для базы

16.1. Основной официальный источник

Основной источник для MVP:

Официальный интернет-портал правовой информации
https://pravo.gov.ru/
https://publication.pravo.gov.ru/

Что важно:

  • это официальный контур опубликования правовых актов РФ;
  • на publication.pravo.gov.ru есть раздел официального опубликования;
  • есть раздел открытых данных;
  • есть API-интерфейс;
  • есть поиск по документам.

Практически для MVP:

1. Использовать publication.pravo.gov.ru как первичный источник новых опубликованных актов.
2. Забрать нужные кодексы и федеральные законы.
3. Сохранять source_url, дату публикации, номер документа, hash версии.
4. Не пытаться на первом этапе спарсить всё право РФ.

Полезные URL:

https://publication.pravo.gov.ru/
https://publication.pravo.gov.ru/OpenData
https://publication.pravo.gov.ru/help

16.2. Дополнительный официальный источник

Дополнительный источник:

Портал Минюста России «Нормативные правовые акты в Российской Федерации»
https://pravo.minjust.ru/

Что полезно:

  • федеральное законодательство;
  • законодательство субъектов РФ;
  • муниципальные акты;
  • текущие и предыдущие редакции НПА.

Полезные URL:

https://pravo.minjust.ru/
https://pravo.minjust.ru/about_project
https://pravo.minjust.ru/about_project/setting_portal

16.3. Что парсить для MVP

Не нужно сразу загружать все законы.

Для MVP достаточно:

1. Конституция РФ
2. Гражданский кодекс РФ
3. Трудовой кодекс РФ
4. Семейный кодекс РФ
5. Жилищный кодекс РФ
6. Закон РФ «О защите прав потребителей»
7. Гражданский процессуальный кодекс РФ — базовые статьи
8. КоАП РФ — только базовые бытовые составы, если нужен административный блок

Для категорий MVP этого достаточно:

Категория в боте Источники
Работа ТК РФ
Потребители Закон о защите прав потребителей, ГК РФ
Жилье / аренда ГК РФ, ЖК РФ
Семья СК РФ
Долги / займы ГК РФ
Договоры ГК РФ
Суд / процесс ГПК РФ

16.4. Почему не стоит «парсить вообще всё» в MVP

Полная база законодательства РФ — это сложно, потому что:

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

Для MVP правильнее сделать узкую, но качественную базу:

Кодексы + ключевые федеральные законы + метаданные + актуальность + хороший поиск.

17. Ingestion pipeline законов

17.1. Схема загрузки

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

Задача:

— скачать HTML/PDF/текст документа;
— сохранить оригинальный файл;
— сохранить URL;
— сохранить дату загрузки;
— сохранить hash.

2. Raw storage

Сохранять оригиналы обязательно:

/data/raw_laws/
  pravo/
    2026-05-23/
      document_0001.html
      document_0001.json

3. Normalizer

Приводит документ к единому виду:

{
  "title": "Трудовой кодекс Российской Федерации",
  "document_number": "197-ФЗ",
  "adoption_date": "2001-12-30",
  "source_url": "...",
  "text": "...",
  "articles": []
}

4. Split by articles

Кодексы нужно резать по статьям.

Пример структуры:

{
  "article_number": "136",
  "article_title": "Порядок, место и сроки выплаты заработной платы",
  "text": "..."
}

5. Chunker

Если статья длинная, делить на чанки.

Рекомендация:

chunk_size = 1000-1800 tokens
chunk_overlap = 100-200 tokens

Для коротких статей — один chunk = одна статья.

6. Embeddings

Для каждого чанка:

embedding = embedding_model(chunk_text + article_title + source_title)

7. Full-text index

Дополнительно делать tsvector для PostgreSQL:

UPDATE law_chunks
SET tsv = to_tsvector('russian', chunk_text);

18. Актуальность законов

Для каждого источника хранить:

source_url
publication_date
effective_date
loaded_at
version_hash
is_active

Если документ обновился:

1. старая версия помечается is_active = false;
2. новая версия добавляется как новая запись;
3. чанки старой версии исключаются из поиска;
4. история консультаций сохраняет ссылки на версию, которая использовалась на момент ответа.

19. Retrieval API внутри проекта

Минимальная функция:

async def retrieve_law_chunks(
    queries: list[str],
    law_types: list[str],
    jurisdiction: str = "RU",
    top_k: int = 5,
) -> list[LawChunk]:
    ...

Внутри:

1. Создать embedding для каждого query.
2. Выполнить vector search.
3. Выполнить full-text search.
4. Слить результаты.
5. Убрать дубли.
6. Отфильтровать is_active = true.
7. Прогнать reranker.
8. Вернуть top_k чанков.

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;
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

Минимальные состояния:

class AskQuestion(StatesGroup):
    choosing_category = State()
    waiting_question = State()
    waiting_region = State()
    processing = State()

Сценарий:

/start
  -> main_menu

Задать вопрос
  -> choosing_category

Выбрана категория
  -> waiting_question

Пользователь написал вопрос
  -> waiting_region

Пользователь указал регион
  -> processing
  -> RAG
  -> answer
  -> main_menu

22. Ограничения MVP

22.1. Лимиты

Чтобы не сжечь бюджет LLM:

Free MVP:
— 5 консультаций в день на пользователя
— максимум 5 сообщений в одной консультации
— максимум 7 retrieved chunks в одном ответе
— timeout LLM-запроса 60 секунд

22.2. Rate limit

Ключ:

rate_limit:{telegram_id}:{date}

Хранить можно в Redis или PostgreSQL.


23. Логирование

Логировать:

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

Бот не должен:

— гарантировать победу в суде;
— обещать точный исход дела;
— советовать подделывать документы;
— помогать скрывать доходы;
— помогать обходить закон;
— составлять фиктивные схемы;
— выдавать себя за адвоката;
— давать категоричные инструкции по уголовным делам без рекомендации юриста.

В сложных случаях:

По этому вопросу лучше обратиться к юристу/адвокату, потому что ошибка может повлечь серьезные последствия.

25. Минимальный docker-compose

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

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

— /start
— главное меню
— выбор категории
— ввод вопроса
— ввод региона
— сохранение пользователя

Этап 2 — база законов

— загрузить 5–8 основных источников
— распарсить по статьям
— сохранить law_sources
— сохранить law_chunks
— построить embeddings
— построить full-text index

Этап 3 — RAG

— классификатор вопроса
— генератор search_queries
— hybrid search
— rerank
— финальный ответ с источниками

Этап 4 — история

— список консультаций
— открыть консультацию
— продолжить консультацию
— удалить консультацию

Этап 5 — защита от мусора

— лимиты
— логирование
— обработка ошибок LLM
— fallback если RAG ничего не нашел
— дисклеймер

28. Критерии готовности MVP

MVP можно считать готовым, если:

1. Пользователь может задать вопрос.
2. Бот уточняет категорию и регион.
3. LLM генерирует поисковые запросы.
4. RAG возвращает реальные статьи из базы.
5. Ответ содержит только найденные источники.
6. История консультации сохраняется.
7. Пользователь может открыть старую консультацию.
8. Если источников нет, бот честно говорит об этом.

29. Короткое ТЗ в одну фразу

Сделать Telegram-бота на Python/aiogram, который консультирует пользователей по законам РФ: принимает вопрос, уточняет категорию и регион, через OpenAI-compatible LLM формирует RAG-запросы, ищет по локальной базе законов РФ через hybrid search, отвечает простым языком с найденными источниками и сохраняет консультации в историю.


30. Главный принцип разработки

Не делать «ChatGPT в Telegram».

Делать юридический workflow:

Категория → Вопрос → Регион → Классификация → RAG → Ответ с источниками → История

Самое важное качество MVP:

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