Теги учитывают надписи с фотографии
Алекс пересоздавал теги для вещи «Светодиодная лампа LED (навигатор)»
и получил «лампа для навигатора», «лампа для GPS-навигатора», «навигационная
лампа». Модель прочитала «навигатор» как назначение, а не как марку Navigator,
и по таким тегам вещь уже не найти.
Правкой промпта это лечилось лишь наполовину: правила и запреты убирали часть
мусора, но «лампа для навигатора» держалась. Написание марки латиницей
помогало, но переименовывать все вещи — не решение.
Настоящее решение подсказал сам Алекс: у вещи есть фотография, и на упаковке
написана настоящая маркировка. Теперь пересоздание тегов сначала читает
надписи с фото и отдаёт их модели как достоверные данные.
Результат на той же вещи:
было: лампа для навигатора, лампа для GPS-навигатора
стало: лампа Navigator, встроенный светодиодный светильник, лампа DOWNLIGHT,
лампа 10 Вт, лампа с холодным белым светом
С коробки прочиталось: Navigator, DOWNLIGHT, 10Вт, IP44, 90°, холодный белый
свет — заодно выяснилось, что это не лампочка, а встраиваемый светильник.
В промпт добавлены два разобранных примера: марка в названии — это марка,
а не назначение вещи; и не приписывать артикулы, которых никто не давал
(на «шуруповёрт Makita» модель выдумывала «Makita XA020, 18 В»).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -60,6 +60,21 @@ qwen3: изолента → «изоляционная лента»,
|
||||
После смены у записанных вещей теги остаются прежними; обновить их можно
|
||||
кнопкой **«🔄 Пересоздать теги»** в карточке.
|
||||
|
||||
**Пересоздание учитывает фотографию.** Если у вещи есть снимок, приложение
|
||||
сначала читает с него надписи и отдаёт их модели как достоверную маркировку.
|
||||
Разница на живом примере — вещь «Светодиодная лампа LED (навигатор)»:
|
||||
|
||||
```
|
||||
без фото: «лампа для навигатора», «лампа для GPS-навигатора» ← модель
|
||||
решила, что это лампа для навигатора
|
||||
с фото: «лампа Navigator», «встроенный светодиодный светильник»,
|
||||
«лампа DOWNLIGHT», «лампа 10 Вт»
|
||||
```
|
||||
|
||||
С коробки прочиталось `Navigator, DOWNLIGHT, 10Вт, IP44, 90°, холодный белый
|
||||
свет` — и выяснилось, что это вообще не лампочка, а встраиваемый светильник.
|
||||
На упаковке написана настоящая маркировка, гадать по названию не нужно.
|
||||
|
||||
## Установка
|
||||
|
||||
Положите `install.cmd` в пустую папку и запустите — он скачает проект, создаст
|
||||
|
||||
+19
-7
@@ -495,12 +495,22 @@ def api_retag_item(item_id):
|
||||
return jsonify({"ok": False,
|
||||
"reply": "Модель недоступна — теги придумывать некому."}), 503
|
||||
|
||||
# Вторая попытка не роскошь: если только что распознавали фото, в памяти
|
||||
# Ollama лежит vision-модель, и первый запрос уходит на её выгрузку
|
||||
# и загрузку текстовой — он может вернуться пустым.
|
||||
fresh = brain.aliases(item.get("name"), item.get("note"))
|
||||
# Если у вещи есть фотография — сначала читаем надписи с неё. На упаковке
|
||||
# настоящая маркировка, и модели не приходится гадать по названию.
|
||||
seen = None
|
||||
if item.get("photo") and config.VISION_MODEL:
|
||||
path = PHOTOS_DIR / item["photo"]
|
||||
if path.exists():
|
||||
guess = brain.describe_photo(path.read_bytes())
|
||||
if guess and guess.get("text"):
|
||||
seen = guess["text"]
|
||||
|
||||
# Вторая попытка не роскошь: после обращения к vision-модели в памяти
|
||||
# Ollama лежит она, и первый запрос уходит на её выгрузку и загрузку
|
||||
# текстовой — он может вернуться пустым.
|
||||
fresh = brain.aliases(item.get("name"), item.get("note"), seen=seen)
|
||||
if not fresh:
|
||||
fresh = brain.aliases(item.get("name"), item.get("note"))
|
||||
fresh = brain.aliases(item.get("name"), item.get("note"), seen=seen)
|
||||
if not fresh:
|
||||
return jsonify({"ok": False, "reply": "Модель не вернула ни одного тега."})
|
||||
|
||||
@@ -517,8 +527,10 @@ def api_retag_item(item_id):
|
||||
item["emb"] = None # словарь предмета изменился
|
||||
search.ensure_embeddings(db)
|
||||
store.save(db)
|
||||
return jsonify({"ok": True, "item": _card(db, item),
|
||||
"reply": "Теги пересозданы: %s" % ", ".join(item["aliases"])})
|
||||
reply = "Теги пересозданы: %s" % ", ".join(item["aliases"])
|
||||
if seen:
|
||||
reply += "\n(прочитано с фото: %s)" % seen
|
||||
return jsonify({"ok": True, "item": _card(db, item), "seen": seen, "reply": reply})
|
||||
|
||||
|
||||
@app.route("/api/containers/<code>", methods=["PATCH"])
|
||||
|
||||
+22
-2
@@ -79,6 +79,16 @@ ALIAS_SYSTEM = """Ты придумываешь альтернативные н
|
||||
|
||||
«Стабилизатор 7805» → ["LM7805","стабилизатор 5 вольт","линейный регулятор напряжения","КР142ЕН5А","стабилизатор питания"]
|
||||
«изолента» → ["изоляционная лента","синяя лента","ПВХ-лента","электроизоляционная лента"]
|
||||
|
||||
Марка в названии — это МАРКА, а не назначение вещи:
|
||||
«Светодиодная лампа LED (навигатор)» → ["лампа Navigator","светодиодная лампа","LED лампа","лампочка светодиодная","лампа навигатор"]
|
||||
Здесь «навигатор» — марка Navigator. Вещь остаётся ЛАМПОЙ, поэтому «лампа для
|
||||
навигатора» и «навигационная лампа» — НЕВЕРНО: это описания другой вещи.
|
||||
Каждый тег обязан обозначать ту же самую вещь, только другими словами.
|
||||
|
||||
Не приписывай вещи артикулы и характеристики, которых нет в её названии:
|
||||
«шуруповёрт Makita» → ["шуруповерт Makita","шуруповерт Макита","аккумуляторный шуруповерт","электрошуруповерт"]
|
||||
а не «Makita XA020» и не «Makita 18 В» — этих данных тебе никто не давал.
|
||||
"""
|
||||
|
||||
_THINK_RE = re.compile(r"<think(?:ing)?>.*?</think(?:ing)?>", re.IGNORECASE | re.DOTALL)
|
||||
@@ -338,13 +348,23 @@ def describe_photo(image_bytes):
|
||||
# ============================================================
|
||||
# Синонимы
|
||||
# ============================================================
|
||||
def aliases(name, note=None, model=None):
|
||||
"""3-7 альтернативных названий. Пустой список, если модель недоступна."""
|
||||
def aliases(name, note=None, model=None, seen=None):
|
||||
"""3-7 альтернативных названий. Пустой список, если модель недоступна.
|
||||
|
||||
seen — надписи, прочитанные с фотографии вещи. Это лучший источник тегов:
|
||||
на упаковке написана настоящая маркировка, и модели не приходится гадать.
|
||||
Без неё «Светодиодная лампа LED (навигатор)» превращалась в «лампу для
|
||||
GPS-навигатора», хотя на коробке ясно написано Navigator.
|
||||
"""
|
||||
name = (name or "").strip()
|
||||
if not name:
|
||||
return []
|
||||
model = model or config.CHAT_MODEL
|
||||
|
||||
prompt = name + (" (" + note + ")" if note else "")
|
||||
if seen:
|
||||
prompt += ("\nНа фотографии этой вещи видны надписи: " + seen
|
||||
+ "\nИспользуй их: это настоящая маркировка, а не догадка.")
|
||||
# 500, а не 256: модель может ответить многословно (объектами вместо строк),
|
||||
# и тогда на 256 токенах JSON обрывается на полуслове — разбирать нечего.
|
||||
content = ollama.chat(model, [
|
||||
|
||||
Reference in New Issue
Block a user