Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
2c86632
docs(plan): design high-accuracy document slide alignment
fUS1ONd Jul 19, 2026
cad0909
Merge remote-tracking branch 'origin/dev' into docs/document-slide-al…
fUS1ONd Jul 24, 2026
85e8733
feat(slides): add evidence-first document alignment
fUS1ONd Jul 24, 2026
24ee1ab
feat(evaluation): add automated lecture quality evaluator
fUS1ONd Jul 25, 2026
86d1c10
feat(evaluation): add independent lecture judge skill
fUS1ONd Jul 25, 2026
0521142
chore: exclude local lecture benchmark data
fUS1ONd Jul 25, 2026
01a08f8
refactor(evaluation): use independent judge skill only
fUS1ONd Jul 25, 2026
f58bce7
test(slides): record skill-judged alignment baseline
fUS1ONd Jul 25, 2026
4f24452
docs(slides): add matcher generalization roadmap
fUS1ONd Jul 26, 2026
cc7e535
feat(slides): ground matcher confidence in evidence
fUS1ONd Jul 26, 2026
4e14e69
feat(slides): rank concise transcript evidence
fUS1ONd Jul 26, 2026
2dd107e
fix(slides): prioritize distinctive recovery evidence
fUS1ONd Jul 26, 2026
f97df10
fix(slides): stabilize structured matcher responses
fUS1ONd Jul 26, 2026
37518b9
style: format slide alignment implementation
fUS1ONd Jul 26, 2026
e595b5f
fix(slides): устранить деградацию каталога слайдов
fUS1ONd Jul 27, 2026
34a0cfe
feat(llm): настраиваемый потолок ответа вместо зашитых 4096
fUS1ONd Jul 27, 2026
50b9445
feat(slides): отдельный effort для матчера слайдов
fUS1ONd Jul 27, 2026
544c5f4
feat(slides): строгие схемы ответов вместо json_object
fUS1ONd Jul 27, 2026
a2456eb
docs(slides): зафиксировать эксперименты с моделями и отчёты судей
fUS1ONd Jul 27, 2026
14f46ec
docs(slides): долг по упавшим задачам — TTL-sweeper и resume
fUS1ONd Jul 27, 2026
11f33f5
docs(slides): отчёт судьи по прогону D (strict-схемы + effort матчера)
fUS1ONd Jul 28, 2026
e24333c
fix(llm): не принимать оборванный ответ апстрима за валидный
fUS1ONd Jul 28, 2026
54e3159
feat(slides): свой список моделей для матчера слайдов
fUS1ONd Jul 28, 2026
b122da5
feat(slides): каталог просит пересказ вместо дословного текста слайда
fUS1ONd Jul 28, 2026
f95d02a
feat(slides): якорить probable-слайды и опускать галерею под текст
fUS1ONd Jul 28, 2026
6fea927
docs(slides): прогон F и правило размещения слайдов
fUS1ONd Jul 28, 2026
ccb8414
feat(slides): подмешивать написания со слайдов в рендер секции
fUS1ONd Jul 28, 2026
7ccaa24
fix(slides): справочник написаний строить по всей колоде
fUS1ONd Jul 28, 2026
c9f6859
feat(slides): справочник написаний только из имён собственных
fUS1ONd Jul 29, 2026
162754b
docs(slides): отчёт судьи по прогону I (справочник написаний)
fUS1ONd Jul 29, 2026
cce7367
fix(llm): не падать на ответе апстрима без choices
fUS1ONd Jul 29, 2026
02cab04
docs(slides): отчёт судьи по лекции 2026-02-26 (прогон J)
fUS1ONd Jul 29, 2026
472c39c
fix(structurize): чинить секции с end <= start по соседям
fUS1ONd Jul 29, 2026
b88b2f8
docs(slides): отчёт судьи по лекции 2026-03-12 (прогон K, валидация)
fUS1ONd Jul 29, 2026
4839ae5
docs(slides): прогоны G–K, две новые лекции и два прод-фикса
fUS1ONd Jul 29, 2026
e577268
docs(slides): отчёт судьи по лекции 2026-05-07 (прогон L, новый лектор)
fUS1ONd Jul 29, 2026
2aa8690
docs: спека единого размещения слайдов для видео и документного режимов
fUS1ONd Jul 29, 2026
29bff3a
docs: план реализации единого размещения слайдов
fUS1ONd Jul 29, 2026
9fc1f2b
docs: уточнить перенос тестов сегментации в плане слияния
fUS1ONd Jul 29, 2026
799e69e
fix(slides): посторонняя колода не даёт написаний рендеру
fUS1ONd Jul 29, 2026
ad17776
docs: добавить в план задачи по краевым случаям выравнивания
fUS1ONd Jul 29, 2026
5c21c3c
feat(slides): пустая запись каталога получает роль blank
fUS1ONd Jul 29, 2026
54b1b58
fix(slides): понижать пару несвязанных слайдов на одном доказательстве
fUS1ONd Jul 29, 2026
97c1ba9
fix(slides): не ставить маркер внутрь нумерованного списка
fUS1ONd Jul 29, 2026
efa9972
fix(slides): фильтр колонтитулов не обнуляет страницу целиком
fUS1ONd Jul 29, 2026
af65683
fix(slides): вердикт-массив тоже уходит на независимую проверку
fUS1ONd Jul 29, 2026
ac6230d
fix(slides): повышенный вердикт судьи подтверждает раздел
fUS1ONd Jul 29, 2026
1337c1b
docs: задачи 15-19 по регрессиям от охоты на краевые случаи
fUS1ONd Jul 29, 2026
a9069fe
fix(slides): коллизия доказательств считается по парам связей
fUS1ONd Jul 29, 2026
6afe046
fix(slides): подтверждением считается только тот же раздел
fUS1ONd Jul 29, 2026
db68f65
fix(slides): лексический путь не выдаёт explicit
fUS1ONd Jul 29, 2026
d4e2fab
fix(slides): blank только когда пусты все поля записи
fUS1ONd Jul 29, 2026
f5ce3c3
fix(slides): колонтитул не становится доказательством привязки
fUS1ONd Jul 29, 2026
a15afbc
docs: хендофф по слиянию режимов размещения слайдов
fUS1ONd Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.env
.env.*
!.env.example
.git
.worktrees
test-data

21 changes: 20 additions & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,33 @@ VIDEO_TARGET_RESOLUTION=720
# Модели LLM через OpenRouter (приоритетный список, fallback при 429)
LLM_MODELS_SPLIT=google/gemini-3.6-flash,google/gemini-3.5-flash,google/gemini-3.5-flash-lite
LLM_MODELS_SUBSPLIT=google/gemini-3.6-flash,google/gemini-3.5-flash,google/gemini-3.5-flash-lite
LLM_MODELS_RENDER=google/gemini-3.5-flash-lite,google/gemini-3.6-flash,google/gemini-3.5-flash
LLM_MODELS_RENDER=google/gemini-3.5-flash-lite,google/gemini-3.5-flash,google/gemini-3.6-flash

# Параллельность и reasoning effort по этапам структуризации
# Потолок ответа модели, включая reasoning-токены. Обрезанный ответ рвал
# JSON каталога слайдов, поэтому по умолчанию — предел моделей Gemini.
LLM_MAX_TOKENS=65536
LLM_CONCURRENCY_SUBSPLIT=2
LLM_CONCURRENCY_RENDER=5
LLM_EFFORT_SPLIT=medium
LLM_EFFORT_SUBSPLIT=medium
LLM_EFFORT_RENDER=medium
# Матчер слайдов отвечает строгими схемами: reasoning мешает их соблюдать,
# поэтому его effort отделён от контентных стадий.
LLM_EFFORT_SLIDE_MATCH=low
# Свой список моделей для матчера слайдов. Пусто (или переменная не задана) —
# берутся модели LLM_MODELS_SUBSPLIT, поведение как раньше.
# Зачем отделять: на каталогизации страниц, дословно цитирующих официальные
# документы, Gemini обрывает генерацию по фильтру цитирования Google
# (RECITATION), и каталог всего батча деградирует в нативный текст PDF.
# У модели с открытыми весами такого обрыва мы не наблюдали, при этом она
# доступна через того же BYOK-провайдера google-ai-studio — её удобно ставить
# последним звеном ротации, чтобы батч добирала она, а не запасной текст.
# LLM_MODELS_SLIDE_MATCH=google/gemini-3.6-flash,google/gemini-3.5-flash-lite,google/gemma-4-31b-it:free


# Привязка приложенного PDF/PPTX: legacy, shadow или evidence-first v2.
DOCUMENT_SLIDE_ALIGNMENT_MODE=legacy

# Стадия отбора кадров из видео (video_slides): отдельно от документных слайдов,
# запускается только для видео без приложенного slides-документа и без no_slides
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ __pycache__/
.venv/
.env
data/
test-data/
docs/plans/
.pytest_cache/
*.egg-info/
Expand Down
10 changes: 10 additions & 0 deletions OPEN-QUESTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Открытые вопросы

Очередь продуктовых развилок, которые нельзя решить из кода: правильное поведение
здесь не выводится ни из контракта, ни из инварианта — его выбирает человек.

---

Записи отсюда **удаляются**, а не накапливаются: пользователь отвечает на вопрос →
ответ немедленно превращается в тест → строка убирается из файла. Файл должен
стремиться к нулю; это очередь долга, а не база знаний.
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ HTTP-сервис обработки лекций: на вход — лекци
3. **Слайды** — три источника: из приложенного PDF/PPTX (pymupdf + LibreOffice),
автоматически из видеоряда через Gemini Vision, либо без слайдов (флаг `no_slides`).
4. **Структуризация** транскрипта на темы и подтемы через Gemini, с привязкой слайдов.
Секция с некорректной шкалой (конец раньше начала) не роняет задачу: конец берётся
из начала следующей секции, у последней — из конца транскрипта.
5. **Кадры из видео** (стадия `video_slides`, только для видео-лекций без
приложенного документа слайдов) — из видеоряда автоматически отбираются
осмысленные стопкадры (слайд / доска в момент «дописал» / финальный код
Expand All @@ -30,6 +32,10 @@ HTTP-сервис обработки лекций: на вход — лекци
задачи автоматически помечаются как `interrupted`. Несколько лекций обрабатываются параллельно
(лимит — `MAX_CONCURRENT_TASKS`), остальные ждут в очереди.

Регрессионное качество готового ZIP проверяется независимым сабагентом по
репозиторному skill `skills/lecture-quality-judge`: он сопоставляет конспект,
транскрипт, слайды и alignment diagnostics с проверяемым evidence.

### Режимы слайдов

- **видео без слайдов-документа** → слайды извлекаются автоматически из видеоряда (Gemini Vision);
Expand All @@ -38,6 +44,12 @@ HTTP-сервис обработки лекций: на вход — лекци

Для аудио слайды есть только при приложенном документе.

Для документных слайдов доступны `legacy`, `shadow` и `v2` через
`DOCUMENT_SLIDE_ALIGNMENT_MODE`. В `v2` страница попадает inline только при
проверяемом свидетельстве в SRT и безопасном Markdown-anchor; менее уверенные
страницы остаются галереей раздела, а неупомянутые выводятся в отдельном
приложении. `shadow` считает диагностику, но сохраняет legacy-результат.

### Кадры из видео (стадия `video_slides`)

Отдельно от «Режимов слайдов» выше: для видео-лекций **без** приложенного документа
Expand Down Expand Up @@ -353,6 +365,8 @@ python scripts/submit_task.py --base http://my-host:8000/api/v1 status <task_id>
| `LLM_CONCURRENCY_*` | Параллельность вызовов LLM по этапам. |
| `LLM_EFFORT_*` | Reasoning effort по этапам структуризации (по умолчанию `low`). |
| `FRAMES_ENABLED` | Вкл./выкл. стадии отбора кадров из видео (`video_slides`). По умолчанию `true`. |
| `DOCUMENT_SLIDE_ALIGNMENT_MODE` | Привязка PDF/PPTX: `legacy`, `shadow` или `v2`; по умолчанию `legacy`. |
| `LLM_MODELS_SLIDE_MATCH` | Свой приоритетный список моделей для матчера слайдов. Пусто (по умолчанию) → берутся модели `LLM_MODELS_SUBSPLIT`. |
| `LLM_MODELS_VIDEO_SLIDES` | Приоритетный список VLM-моделей для QC кадров (fallback при 429). |
| `LLM_EFFORT_VIDEO_SLIDES` | Reasoning effort для QC кадров (по умолчанию `low`). |
| `LLM_MODELS_FRAMES_CLASSIFY` | Приоритетный список VLM-моделей для классификации режимов видео. |
Expand Down Expand Up @@ -411,6 +425,18 @@ OpenRouter вызывается в режиме BYOK с провайдером `
конкретная модель временно ставится на cooldown, после чего клиент пробует следующую
модель из списка.

На cooldown (60 секунд) модель уходит и в двух других случаях:

- `5xx` от апстрима (например, «This model is currently experiencing high demand») —
задача не падает, попытка повторяется с нарастающей паузой на другой модели;
- апстрим оборвал генерацию — OpenRouter отдаёт `200` с частичным контентом,
нулевым usage и `finish_reason="error"` (фильтр цитирования Gemini `RECITATION`,
`provider_overloaded`). Такой обрывок не считается валидным ответом и не попадает
в статистику использования;
- ответ пришёл без `choices` — при отказе провайдера OpenRouter отдаёт `200` с телом
из одной ошибки. Это тоже трактуется как отказ модели, а не как ошибка формата, и
задача не падает.

Текущий набор моделей:

| Модель | Роль |
Expand All @@ -427,6 +453,20 @@ OpenRouter вызывается в режиме BYOK с провайдером `
количеству, но критичных решений классификатора и `LLM_MODELS_VIDEO_SLIDES` для QC.
- Несколько Google AI Studio ключей сейчас не ротируются в core; для увеличения RPD
нужно выпускать отдельный OpenRouter key/конфигурацию на окружение.
- У матчера слайдов свой список моделей (`LLM_MODELS_SLIDE_MATCH`) и свой effort
(`LLM_EFFORT_SLIDE_MATCH`). Пустой список означает наследование от
`LLM_MODELS_SUBSPLIT`.

Зачем матчеру отдельный список: на каталогизации слайды уходят в LLM батчами по 6
изображений, и на страницах, дословно цитирующих официальные документы, Gemini
обрывает генерацию по фильтру цитирования Google (`RECITATION`) — каталог всего
батча деградирует в запасной нативный текст PDF, а слайды теряют верификацию.
Модель с открытыми весами этим фильтром не ограничена и доступна через того же
BYOK-провайдера `google-ai-studio`: в наших прогонах `google/gemma-4-31b-it:free`
проходила батч, который рвал Gemini, с качеством на уровне
`gemini-3.5-flash-lite`. Наблюдение сделано на небольшом числе прогонов и гарантией
не является, но как последнее звено ротации такая модель полезна — при обрыве батч
добирает она, а не запасной текст.

## Тесты

Expand Down
Loading