Публичная неаутентифицированная конечная точка для функции «Попробуйте сами» на лендинге. Ограничение запросов по IP, доступен только сценарий onboarding.
Конечные точки¶
POST /api/v1/demo/chat¶
Отправка запроса на естественном языке о демонстрационной кодовой базе (PostgreSQL 17.6).
Аутентификация: не требуется (публичная конечная точка) Ограничение: 30 запросов/минуту на IP
Запрос¶
{
"query": "Где определена главная функция?",
"language": "ru"
}
| Поле | Тип | Обязательно | По умолчанию | Описание |
|---|---|---|---|---|
query |
string | Да | — | Вопрос на естественном языке (1–500 символов) |
language |
string | Нет | "ru" |
Язык ответа ("en" или "ru") |
Ответ (200)¶
Успешный запрос:
{
"answer": "Главная функция определена в src/backend/main/main.c на строке 53...",
"scenario_id": "onboarding",
"processing_time_ms": 234.5
}
Отклонённый запрос (не по теме, неверный сценарий или заблокированное содержимое):
{
"answer": "### Запрос заблокирован\n\nЭтот тип запроса не поддерживается в демо-версии...",
"scenario_id": "demo_rejection",
"processing_time_ms": 12.3
}
Внутренняя ошибка (LLM недоступна и т.д.):
{
"answer": "К сожалению, система анализа временно недоступна. Попробуйте позже.",
"scenario_id": "error",
"processing_time_ms": 1502.7
}
| Поле | Тип | Описание |
|---|---|---|
answer |
string | Ответ LLM, сообщение об отклонении или текст ошибки |
scenario_id |
string | "onboarding" — успех, "demo_rejection" — запрос отклонён, "error" — внутренняя ошибка |
processing_time_ms |
float | Время обработки на сервере |
Ошибки¶
| Статус | Причина | Тело ответа |
|---|---|---|
| 400 | Запрос слишком длинный (>500 символов) | {"detail": "Запрос слишком длинный. Максимальная длина — 500 символов."} |
| 422 | Ошибка валидации Pydantic (пустой запрос, неверные типы) | {"detail": [...]} |
| 429 | Превышено ограничение на число запросов | {"detail": "Слишком много запросов"} |
| 503 | Демо-режим отключён | {"detail": "Демонстрационная конечная точка отключена"} |
Примечание: Нерелевантные запросы и запросы неверного сценария возвращают HTTP 200 с
scenario_id: "demo_rejection"и дружественным сообщением в полеanswer. Это НЕ HTTP-ошибки — такое поведение позволяет лендингу показывать полезные подсказки без срабатывания обработчиков ошибок.
GET /api/v1/demo/status¶
Проверка доступности и конфигурации демонстрационной конечной точки.
Аутентификация: не требуется
Ответ (200)¶
{
"enabled": true,
"rate_limit": "30/minute",
"max_query_length": 500,
"allowed_scenarios": ["onboarding"]
}
Конвейер валидации запросов¶
Входящие запросы проходят 3-этапную валидацию перед обработкой:
1. ЖЁСТКИЙ ОТКАЗ — явно вредоносное содержимое (regex-шаблоны из доменного плагина)
└─ Возврат 200 с scenario_id="demo_rejection", rejection_reason="blocked_content"
2. НЕВЕРНЫЙ СЦЕНАРИЙ — легитимный запрос за пределами onboarding
└─ Возврат 200 с scenario_id="demo_rejection", rejection_reason="wrong_scenario"
3. РЕЛЕВАНТНОСТЬ ДОМЕНУ — оценка по ключевым словам/шаблонам доменного плагина
└─ Оценка ≥ 0.5 (порог LOW) → запрос принят, направлен в обработчик onboarding
└─ Оценка < 0.5 → отклонение с scenario_id="demo_rejection", rejection_reason="off_topic"
ValidationResult¶
Внутренний dataclass ValidationResult (demo.py:122):
@dataclass
class ValidationResult:
is_valid: bool
confidence: float
rejection_reason: Optional[str] = None # "off_topic" | "wrong_scenario" | "blocked_content"
detected_scenario: Optional[str] = None
method: str = "keyword"
Пороги релевантности¶
Настраиваются в src/config/demo.yaml → relevance.thresholds:
| Порог | Значение | Условие |
|---|---|---|
high |
0.9 | 3+ совпадения ключевых слов |
medium |
0.75 | 2 совпадения |
low |
0.5 | 1 совпадение — граница отклонения |
minimal |
0.1 | 0 совпадений |
Запросы с оценкой ниже low (0.5) отклоняются как нерелевантные.
Методы доменного плагина¶
Шаблоны валидации загружаются из активного доменного плагина (DomainPluginV3):
| Метод | Возвращает | Назначение |
|---|---|---|
get_demo_keywords() |
List[str] |
Ключевые слова домена для оценки релевантности |
get_hard_reject_patterns() |
List[str] |
Regex-шаблоны для жёсткого отклонения |
get_wrong_scenario_patterns() |
List[Tuple[str, str]] |
Пары (шаблон, имя_сценария) для обнаружения неверного сценария |
Кеширование¶
Демонстрационная конечная точка кеширует ответы для снижения нагрузки на LLM:
| Кеш | Размер | TTL | Ключ |
|---|---|---|---|
| Кеш ответов | 100 записей (LRU) | 30 минут | query.lower().strip() |
Примечание: Кеш проверяется до валидации. Повторные запросы (даже нерелевантные, которые были ранее обработаны) возвращают кешированные результаты без повторной валидации.
Конфигурация¶
config.yaml¶
api:
demo:
enabled: true # Включить/отключить демо-конечную точку
rate_limit: 30/minute # Ограничение запросов на IP
max_query_length: 500 # Макс. длина запроса в символах
allowed_scenarios: # Разрешённые идентификаторы сценариев
- onboarding
src/config/demo.yaml¶
Отдельная конфигурация для кеширования и оценки релевантности:
cache:
dynamic_response:
maxsize: 100 # Размер LRU-кеша ответов
ttl_seconds: 1800 # 30 минут
judge:
maxsize: 1000 # Размер LRU-кеша (зарезервирован для будущего LLM-судьи)
ttl_seconds: 3600 # 1 час
llm_judge:
temperature: 0.1 # Зарезервировано для будущей реализации LLM-судьи
max_tokens: 50
model: yandexgpt-lite
relevance:
thresholds:
high: 0.9
medium: 0.75
low: 0.5 # Граница отклонения
minimal: 0.1
Переменные окружения¶
| Переменная | По умолчанию | Описание |
|---|---|---|
DEMO_ENABLED |
true |
Включить/отключить демо-конечную точку |
DEMO_RATE_LIMIT |
30/minute |
Ограничение запросов на IP |
Модели Pydantic¶
class DemoRequest(BaseModel):
query: str = Field(..., min_length=1, max_length=500, description="User query")
language: str = Field(default="ru", description="Response language")
class DemoResponse(BaseModel):
answer: str = Field(..., description="Response from the system")
scenario_id: str = Field(default="onboarding", description="Scenario used")
processing_time_ms: float = Field(..., description="Processing time in milliseconds")
Примеры использования¶
curl¶
# Корректный запрос
curl -X POST http://localhost:8000/api/v1/demo/chat \
-H "Content-Type: application/json" \
-d '{"query": "Как работает MVCC?", "language": "ru"}'
# Проверка статуса
curl http://localhost:8000/api/v1/demo/status
JavaScript (лендинг)¶
const response = await fetch('/api/v1/demo/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: document.getElementById('demo-input').value,
language: 'ru'
})
});
const data = await response.json();
if (response.ok) {
if (data.scenario_id === 'demo_rejection') {
// Запрос отклонён — показать дружественную подсказку
showRejection(data.answer);
} else if (data.scenario_id === 'error') {
showError(data.answer);
} else {
showAnswer(data.answer);
}
} else if (response.status === 429) {
showRateLimit();
}
Безопасность¶
- Без аутентификации — конечная точка публично доступна
- Ограничение запросов — 30 запросов/минуту на IP предотвращает злоупотребление
- Ограничение сценария — доступен только
onboarding(без анализа безопасности, редактирования файлов и т.д.) - Валидация запросов — 3-этапный конвейер блокирует вредоносные и нерелевантные запросы
- Только чтение — операции записи невозможны через демонстрационную конечную точку
- Ключевые слова домена — загружаются из доменного плагина, не зашиты в код
Связанная документация¶
Модуль: src/api/routers/demo.py
Конфигурация: src/config/demo.yaml, config.yaml → api.demo
Последнее обновление: март 2026