qwen3:8b вместо qwen2.5, отмена правки и пересоздание тегов

Замер на наших задачах (разбор 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>
This commit is contained in:
2026-07-30 18:51:13 +03:00
co-authored by Claude Opus 5
parent 39c807d4de
commit 01d7f17f89
8 changed files with 197 additions and 16 deletions
+25
View File
@@ -116,6 +116,31 @@ r.check("пустое имя — 400",
r.check("занятое имя — 400",
c.open("/api/item/" + item, method="PATCH", json={"name": "лопата"}).status_code == 400)
print("\n== Пересоздание тегов ==")
if HAS_AI:
j("PATCH", "/api/item/" + item, aliases=["мусорный тег"])
d = c.post("/api/item/" + item + "/retag").get_json()
r.check("теги пересозданы", d.get("ok") and d["item"]["aliases"], d)
r.check("мусорный тег заменён", "мусорный тег" not in d["item"]["aliases"],
d["item"]["aliases"])
d = c.post("/api/item/" + item + "/retag?keep=1").get_json()
r.check("режим дополнения работает", d.get("ok"), d)
r.check("retag для несуществующей — 404",
c.post("/api/item/нет-такой/retag").status_code == 404)
# Разбор кривых ответов модели: qwen3 присылала объект вместо списка строк,
# и он попадал в теги целиком, а ещё дублировала одно значение семь раз.
from hlamingo.brain import _clean_tags # noqa: E402
r.check("объект вместо списка разбирается",
_clean_tags({"name": "x", "synonyms": ["тестер", "дрель"]}, "x")
== ["тестер", "дрель"])
r.check("дубликаты схлопываются",
_clean_tags(["дрель", "ДРЕЛЬ", "бур"], "x") == ["дрель", "бур"])
r.check("вложенные объекты разбираются",
_clean_tags([{"name": "тестер"}, "мультиметр"], "x") == ["тестер", "мультиметр"])
r.check("мусор отсеивается", _clean_tags([None, True, {}, "годный"], "x") == ["годный"])
r.check("не список — пусто", _clean_tags("строка", "x") == [])
print("\n== Фото ==")
d = upload("item", item)
fname = d["photo"]