1374 lines
35 KiB
Markdown
1374 lines
35 KiB
Markdown
# 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
|
||
Лучше меньше категорий и источников, но чтобы бот не выдумывал статьи и всегда показывал, откуда взял правовую норму.
|
||
```
|