7.3 KiB
Классификация застряла на 68/84: диагностика 2026-08-27
Наблюдение
На экране классификация остаётся на 68/84, хотя контейнер продолжает работать.
Проверка через ВМ:
GET /api/batch-progress?batch=acbdffe5-e3ec-43f4-ab61-84861cac35bd
{"counts":{"classified":68,"garbage":16},"ok":true,"total":84}
Повторный запрос вернул то же значение. Kubernetes показывает pod
pythonk8s-574b5cf9b4-z4wxz в состоянии Running, Ready 1/1, RESTARTS 0.
Событий Kubernetes нет. На ВМ contracts.service активен, convert_server.py
запущен в одном экземпляре.
Причина в коде
site/routes/api_bp.py возвращает раздельные счётчики classified, failed и
garbage, а total равен числу всех документов батча.
site/static/app.js в runClassify() считает завершённые документы так:
var done = (d.counts.classified || 0) + (d.counts.failed || 0);
garbage в эту сумму не включён. Для текущего батча:
done = 68 classified + 0 failed = 68
total = 84
garbage = 16
фактически завершено = 68 + 0 + 16 = 84
Поэтому условие done >= total никогда не выполняется, polling не
останавливается, а кнопка остаётся на 68/84. Это ошибка фронтендового
подсчёта прогресса, а не зависание Kubernetes или LLM на 68-м файле.
Исправление
В site/static/app.js к числу завершённых документов добавлен конечный статус
garbage:
var done = (d.counts.classified || 0)
+ (d.counts.failed || 0)
+ (d.counts.garbage || 0);
Версия приложения повышена с 2.0.17 до 2.0.18; cache-buster статических
скриптов обновлён в site/templates/index.html.
Kubernetes проверялся только командами чтения через ВМ:
kubectl get, kubectl describe, kubectl top, kubectl logs.
Дополнительная проверка загрузки ZIP
При проверке загрузки разными способами обнаружен отдельный дефект связи
файлов с архивом. expandZipClient() создаёт для каждого распакованного файла
поле zip_source, а uploadFile(file, onProgress, zipSource) умеет передавать
его в /api/upload_refs. Однако вызов uploadFile() в цикле onFilesSelected()
передавал только два аргумента. Поэтому backend получал zip_source=null, и
связь с исходным ZIP терялась.
Проверены архивы:
testgen/out/zips/duplicates_inside.zip— целый ZIP, два файла с одинаковым именем;testgen/out/zips/mixed_with_error.zip— целый ZIP, корректный и повреждённый DOCX.
Исправление применено в site/static/files.js: третьим аргументом передаётся
entry.zip_source. Для обычных файлов значение null, поэтому их поведение не
изменяется. Версия приложения повышена с 2.0.18 до 2.0.19, cache-buster
обновлён в site/templates/index.html.
Проверки исправления
node --check site/static/files.js— успешно;- frontend upload-тесты — успешно;
- layer 2 upload-тесты — успешно;
- архивы
duplicates_inside.zipиmixed_with_error.zipпрошлиunzip -t.
Реальный end-to-end тест сверки
27.08.2026 выполнен тест через deployed API v2.0.19 на batch
0dd0d3a7-3e52-4888-95eb-1b6d3e37f40e.
Загружены через VM-поток два реальных DOCX:
договор-XXX001-03700.docx— распарсен, 20 элементов;допник-1-XXX001-03700.docx— распарсен, 16 элементов.
Классификация завершилась 2/2, но результат классификации неожиданен:
- допсоглашение попало в группу
03700; - базовый договор попал в
__unresolved__со статусомgarbageи причиной отсутствия matching-договора.
Это функциональный дефект/проблема классификации тестовой пары: без базового договора группа не проверяет корректное сравнение договора с приложением.
Тем не менее реальный pipeline сверки для сформированной группы проверен:
/api/apply-groups—200, создан contract IDb859e813-bac9-4eb9-82b5-6b5666f2ba4a;/process-v2— SSE завершён событиемcomplete;- LLM вернул
6операций в режимеpartial; - применено:
added=6,updated=0,deleted=0; - ошибок соединения и SSE не было;
- время pipeline:
9.4с.
Следующий обязательный тест для проверки matching — загрузить базовый договор с точным именем/содержимым, которое классификатор ожидает как базовый, и повторить ту же пару. Код после этого прогона не изменялся.
Исправление ложного garbage для базового договора
При повторной проверке реального файла договор-XXX001-03700.docx установлена
точная причина ошибочной классификации: второй garbage-фильтр искал подстроку
УПД в первых 2000 символах. В договоре эта аббревиатура встречается в
обычном условии оплаты: «на основании УПД и счета». Из-за этого договор
получал garbage=header_keywords, хотя его заголовок начинается со слова
«Договор».
Исправление в site/services/classify.py:
- удалено общее substring-срабатывание для
УПД; - добавлено распознавание
УПДи полного названия только с начала строки, когда это заголовок самого документа.
Добавлены регрессионные тесты: упоминание УПД внутри договора не является
garbage, а заголовок документа УПД № ... является garbage.
Версия приложения повышена до 2.0.20, cache-buster обновлён в
site/templates/index.html.