Все 40 резервных копий лежали на том же диске, что и оригинал, а фотографии
не дублировались нигде: смерть диска уносила и 59 записей, и 69 снимков.
Что появилось:
- «📦 Скачать архив» — zip с базой и всеми фотографиями (13 МБ на текущих
данных). Работает и с телефона, годится для переноса на другой компьютер.
- «📥 Загрузить архив» — восстановление. Сначала показывает, что внутри
(сколько вещей в архиве против текущих), и только после подтверждения
заменяет данные. Прежнее состояние откладывается в data/backups —
и база, и папка со старыми фотографиями.
- HLAMINGO_BACKUP_DIR — путь, куда сама уходит копия базы и новых фотографий.
Не чаще раза в час и только если путь доступен: сетевой диск отваливается
регулярно, а терять запись о вещи из-за этого недопустимо. Состояние
и кнопка «Скопировать сейчас» — на вкладке настроек.
Имена файлов из архива как пути не используются: только базовое имя
и расширение из белого списка. Иначе архив со строкой «../../» писал бы
куда угодно — тест это проверяет отдельно.
Попутно исправлены два дефекта:
- предел загрузки был 8 МБ, и архив в 13 МБ не прошёл бы. Разделён на общий
предел запроса и отдельный предел для одного снимка;
- настройки нельзя было менять по одной: непереданные поля приходили как null
и валились на проверке. Теперь null означает «не трогать», а пустая строка —
осознанное «очистить».
Про защиту копий на сервере. Задумывался .htaccess, но сайты обслуживает
nginx, который его игнорирует, — файл создавал бы ложное чувство защиты.
Настоящая защита в расположении: папка лежит рядом с корнем сайта, а не внутри,
и nginx за пределы своего root не выпускает. Проверено запросами извне:
и сама папка, и db.json, и фотографии дают 404, включая попытку выйти
через «/../». .htaccess положен рядом как подстраховка на случай переезда
на Apache, о чём честно написано внутри самого файла.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Фото выбрано в форме — приложение показывает его модели и предлагает название
с прочитанными надписями. «Подставить» переносит в поля, «Не надо» прячет.
Само не подставляется, и это принципиально: модели домысливают марки
и артикулы, когда надписей не видно. В замере клипсу для кабеля модель назвала
«конструктором LEGO Technic, 8376, 401 шт.». Запрет на выдумывание в промпте
помог: та же вещь стала «красной щёткой для посуды».
Смотрит gemma3:4b. Выбрана замером на настоящих снимках из базы:
gemma3:4b 3.5 с, отвечает всегда, надписи читает верно
(«Лампа светодиодная C35, Нейтральный белый, 9 Вт LED, E14»);
qwen3-vl:8b 11-32 с и часто пустой ответ — это ризонер, ему нужен бюджет
в 2000+ токенов, и уменьшение картинки не спасает.
Проверялась и гипотеза, что дело в размере снимка: у gemma3 разницы между
1280 и 512 px почти нет, у qwen3-vl время падает с 62 до 41 с, но ответ
всё равно пуст. Картинка всё же уменьшается до 768 px — незачем гонять лишнее.
Генерация тегов чинилась по ходу: на «Стабилизатор 7805 (TO-220)» модель
отвечала массивом объектов с пояснениями, не укладывалась в 256 токенов,
и JSON обрывался на полуслове — теги выходили пустыми. Бюджет поднят до 500,
промпт требует плоский массив строк, а из битого ответа строки теперь
вытаскиваются регуляркой: лучше несколько живых тегов, чем ничего.
Pillow добавлен в зависимости — для уменьшения картинки перед распознаванием.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Замер на наших задачах (разбор 8 фраз + теги для 6 предметов):
qwen2.5:7b разбор 8/8, но теги «электротool Makita», «вoltage meter»
(первая буква кириллическая), «многофункциональный мера» —
модель думает по-английски и переводит на ходу;
qwen3:8b разбор 8/8, теги на нормальном русском.
Плата — примерно на треть медленнее на тегах. Теги важнее: по ним вещь
и находится.
Поддержка моделей-ризонеров: ollama.chat получил think=False. Без него
qwen3 тратит весь бюджет ответа на внутренний монолог и возвращает пустой
content — на первом прогоне модель выглядела нерабочей.
Разбор ответа с тегами укреплён: qwen3 иногда присылает объект
{"name":..., "synonyms":[...]} вместо списка строк, и он попадал в теги
целиком как строка. Теперь достаётся вложенный список, отсеиваются дубликаты
в разном регистре, вложенные объекты, bool и слишком длинные строки.
По замечаниям Алекса в правке предмета:
- кнопка «Отмена» — раньше открытую форму можно было только сохранить или
удалить вещь целиком;
- кнопка «🔄 Пересоздать теги» — после смены модели теги остаются старыми,
и это способ обновить их, не перезаводя вещь. Новый список показывается,
но не сохраняется молча: его ещё можно поправить руками.
В README добавлен раздел «Какую модель выбрать» с замерами и требованиями
к своей модели для тех, кто ставит приложение себе.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nomic-embed-text на русском не работал: в замере «велосипедная камера»
набирала мультиметру 0.704 — больше, чем верный ответ на «паяльник» (0.691).
Зазор −0.013, то есть мусор систематически обгонял правильное и порогом это
не лечилось.
bge-m3 на том же наборе: верные не ниже 0.537, мусор не выше 0.460,
зазор +0.077. Префиксы задачи ему не нужны, поэтому обнулены.
Порог откалиброван на настоящей базе, а не на придуманном наборе, и картина
там честнее: абстрактное «чем посветить» набирает лампам 0.411, а посторонняя
«детская коляска» — 0.535. Полностью не разделяются, поэтому SEMANTIC_MIN
выставлен так, чтобы отсечь весь мусор ценой слабых догадок — уверенно
показанное не то хуже честного «не нашёл». Точных запросов это не касается:
они находятся по словам и тегам.
Добавлен tools/reindex.py: смена эмбеддера требует полного пересчёта векторов,
иначе смешанная база тихо портит поиск. Скрипт делает копию перед работой.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Веб-помощник для домашнего склада: помнит, в какой коробке и в каком месте
лежит вещь, и находит её по названию, тегам и маркировке. Открывается
с телефона, интеллект — локальная Ollama.
Основное:
- структурный ввод «место → ёмкость → предмет» и кнопки
Положить / Забрать / Переместить / Удалить;
- иерархия мест (Гараж → Стеллаж А → Полка 2), ёмкости с кодом «буква+цифра»,
состояние «на руках» для взятых вещей;
- поиск по словам с учётом русских окончаний, теги от модели расширяют
словарь предмета, смысловой поиск — вспомогательный (замеры в search.py);
- фото к вещам и коробкам: съёмка и выбор из галереи;
- правка и удаление всех сущностей, удаление коробки не теряет её содержимое;
- работает и без Ollama: отключаются только разбор фраз, теги и поиск
по смыслу.
Данные (data/, static/photos/) и локальный конфиг (.env) в репозиторий
не попадают. Тесты работают только в песочнице: путь к данным берётся
из окружения, tests/guard.py отказывается запускаться на живых данных.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>