Architect és robot szabályok frissítése - Fixes #1
This commit is contained in:
@@ -15,6 +15,9 @@ Te vagy a rendszer őre. Feladatod a forráskód (Primary Truth) és a MasterBoo
|
|||||||
3. **Kanban Menedzsment:** 3A szintű granulártság. Minden technikai részfeladatot (pl. "Alembic migration for vehicle_types") rögzíts a Focalboardon.
|
3. **Kanban Menedzsment:** 3A szintű granulártság. Minden technikai részfeladatot (pl. "Alembic migration for vehicle_types") rögzíts a Focalboardon.
|
||||||
4. **Jóváhagyási Pont:** A tervezés végén ÁLLJ MEG. Várj a felhasználó kifejezett jóváhagyására a `logic_spec` kapcsán.
|
4. **Jóváhagyási Pont:** A tervezés végén ÁLLJ MEG. Várj a felhasználó kifejezett jóváhagyására a `logic_spec` kapcsán.
|
||||||
|
|
||||||
|
5. **Focalboard Automatizálás:** Ha a logokban azt látod, hogy egy robot (pl. Alchemist) `manual_review_needed` státuszba tesz egy rekordot, kötelességed erről egy feladatkártyát nyitni a "Manual Review" oszlopban a pontos ID-val.
|
||||||
|
6. **Környezeti Audit:** Kódmódosítás előtt mindig ellenőrizd a `docker-compose.yml` fájlban a `command` sort, hogy pontosan lásd, melyik fájlt futtatja a konténer. Így elkerülhető a rossz fájl szerkesztése.
|
||||||
|
|
||||||
## ⚠️ Korlátozások
|
## ⚠️ Korlátozások
|
||||||
- Meglévő, hiba nélkül futó kódhoz TILOS hozzányúlni jóváhagyás nélkül.
|
- Meglévő, hiba nélkül futó kódhoz TILOS hozzányúlni jóváhagyás nélkül.
|
||||||
- Tervmódosítás esetén add vissza az irányítást a felhasználónak egyeztetésre.
|
- Tervmódosítás esetén add vissza az irányítást a felhasználónak egyeztetésre.
|
||||||
|
|||||||
@@ -5,3 +5,8 @@ AI & OCR: Hibrid AI Gateway (Helyi Ollama: 14B Qwen szövegre, Llama Vision kép
|
|||||||
Identity & Auth: "Dual Entity" modell (Person = hús-vér ember, User = technikai fiók). Triple Wallet gazdasági motor.
|
Identity & Auth: "Dual Entity" modell (Person = hús-vér ember, User = technikai fiók). Triple Wallet gazdasági motor.
|
||||||
|
|
||||||
Deduplikáció (MDM): Csak akkor van merge, ha a make, a technical_code és a hengerűrtartalom egyezik. N/A és UNKNOWN fallback kódok generálása az SQL kényszerek miatt.
|
Deduplikáció (MDM): Csak akkor van merge, ha a make, a technical_code és a hengerűrtartalom egyezik. N/A és UNKNOWN fallback kódok generálása az SQL kényszerek miatt.
|
||||||
|
|
||||||
|
## 5. SQL és Adatbázis Hibakezelés (Error Handling)
|
||||||
|
- **Unique Constraint hibák:** Ha a PostgreSQL `InvalidColumnReferenceError` vagy `UniqueViolation` hibát dob az `ON CONFLICT` miatt, TILOS találgatni a mezőket!
|
||||||
|
- **A kötelező megoldás:** Használd az `ON CONFLICT ON CONSTRAINT [korlát_neve] DO NOTHING` vagy `DO UPDATE` szintaxist.
|
||||||
|
- A pontos korlát (constraint) nevét mindig a pgAdmin-ból vagy a `\d+ táblanév` lekérdezéssel kell kideríteni módosítás előtt.
|
||||||
93
backend/app/workers/vehicle/vehicle_robot_3_alchemist_pro.py
Executable file → Normal file
93
backend/app/workers/vehicle/vehicle_robot_3_alchemist_pro.py
Executable file → Normal file
@@ -4,6 +4,7 @@ import datetime
|
|||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
from sqlalchemy import text, func, update, case
|
from sqlalchemy import text, func, update, case
|
||||||
from app.database import AsyncSessionLocal
|
from app.database import AsyncSessionLocal
|
||||||
from app.models.vehicle_definitions import VehicleModelDefinition
|
from app.models.vehicle_definitions import VehicleModelDefinition
|
||||||
@@ -15,9 +16,9 @@ logger = logging.getLogger("Vehicle-Robot-3-Alchemist-Pro")
|
|||||||
|
|
||||||
class TechEnricher:
|
class TechEnricher:
|
||||||
"""
|
"""
|
||||||
Vehicle Robot 3: Alchemist Pro (Atomi Zárolás Patch)
|
Vehicle Robot 3: Alchemist Pro (Atomi Zárolás + Kézi Moderáció Patch)
|
||||||
Tiszta GPU fókusz: Csak az AI elemzésre és adategyesítésre koncentrál.
|
Tiszta GPU fókusz: Csak az AI elemzésre és adategyesítésre koncentrál.
|
||||||
Nincs felesleges webkeresés. Szigorú Sane-Check.
|
Nincs felesleges webkeresés. Szigorú, de intelligens Sane-Check.
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.max_attempts = 5
|
self.max_attempts = 5
|
||||||
@@ -31,34 +32,38 @@ class TechEnricher:
|
|||||||
self.last_reset_date = datetime.date.today()
|
self.last_reset_date = datetime.date.today()
|
||||||
return self.ai_calls_today < self.daily_ai_limit
|
return self.ai_calls_today < self.daily_ai_limit
|
||||||
|
|
||||||
def is_data_sane(self, data: dict, base_info: dict) -> bool:
|
def validate_merged_data(self, merged_kw: int, merged_ccm: int, v_class: str, fuel: str, current_attempts: int) -> tuple[bool, str]:
|
||||||
""" Szigorított AI Hallucináció szűrő """
|
""" Intelligens validáció a MERGE után. Visszaadja a státuszt és a hiba okát. """
|
||||||
if not data: return False
|
if merged_ccm > 18000:
|
||||||
|
return False, f"Irreális CCM érték ({merged_ccm})"
|
||||||
|
if merged_kw > 1500 and v_class != "truck":
|
||||||
|
return False, f"Irreális KW érték ({merged_kw})"
|
||||||
|
|
||||||
try:
|
# Ha hiányzik a KW
|
||||||
ccm = int(data.get("ccm", 0) or 0)
|
if merged_kw == 0:
|
||||||
kw = int(data.get("kw", 0) or 0)
|
if current_attempts < 3:
|
||||||
v_class = base_info.get("v_type", "car")
|
return False, "Hiányzó KW adat. Újrakutatás javasolt."
|
||||||
|
else:
|
||||||
|
logger.warning("Sane-check: Többszöri próbálkozás után sincs KW, de átengedjük részlegesként.")
|
||||||
|
|
||||||
# 1. Alapvető fizikai korlátok
|
# Ha hiányzik a CCM (és belsőégésű)
|
||||||
if ccm > 18000 or (kw > 1500 and v_class != "truck"):
|
if merged_ccm == 0 and "electric" not in fuel and "elektric" not in fuel and v_class != "trailer":
|
||||||
return False
|
if current_attempts < 3:
|
||||||
|
return False, "Hiányzó CCM (belsőégésű motornál). Újrakutatás javasolt."
|
||||||
|
else:
|
||||||
|
logger.warning("Sane-check: Többszöri próbálkozás után sincs CCM, átengedjük részlegesként.")
|
||||||
|
|
||||||
# 2. Üres adatok kizárása (Kivéve elektromos autók, ahol ccm = 0)
|
return True, "OK"
|
||||||
fuel = data.get("fuel_type", base_info.get("rdw_fuel", "")).lower()
|
|
||||||
if kw == 0:
|
|
||||||
return False
|
|
||||||
if ccm == 0 and "electric" not in fuel and "elektric" not in fuel and v_class != "trailer":
|
|
||||||
return False
|
|
||||||
|
|
||||||
return True
|
|
||||||
except Exception as e:
|
|
||||||
logger.debug(f"Sane check hiba: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
async def process_single_record(self, db, record_id: int, base_info: dict, current_attempts: int):
|
async def process_single_record(self, db, record_id: int, base_info: dict, current_attempts: int):
|
||||||
|
# Pontos azonosító a logokhoz (Márka, Modell, ID, RDW adatok)
|
||||||
|
v_ident = f"{base_info['make'].upper()} {base_info['m_name']} (ID: {record_id}, RDW: {base_info['rdw_ccm']}ccm, KW: {base_info['rdw_kw']})"
|
||||||
|
attempt_str = f"[Próba: {current_attempts + 1}/{self.max_attempts}]"
|
||||||
|
|
||||||
|
ai_data = {} # Üres dict, ha az AI hívás elszállna
|
||||||
|
|
||||||
try:
|
try:
|
||||||
logger.info(f"🧠 AI dúsítás indul: {base_info['make']} {base_info['m_name']}")
|
logger.info(f"🧠 AI dúsítás indul: {v_ident} {attempt_str}")
|
||||||
|
|
||||||
# 1. LÉPÉS: AI Hívás (Rábízzuk az adatokat a modellre)
|
# 1. LÉPÉS: AI Hívás (Rábízzuk az adatokat a modellre)
|
||||||
ai_data = await AIService.get_clean_vehicle_data(
|
ai_data = await AIService.get_clean_vehicle_data(
|
||||||
@@ -67,13 +72,13 @@ class TechEnricher:
|
|||||||
base_info
|
base_info
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. LÉPÉS: Validáció (Ha az AI rossz adatot ad, NEM megyünk ki a webre, hanem dobjuk az aktát!)
|
if not ai_data:
|
||||||
if not ai_data or not self.is_data_sane(ai_data, base_info):
|
raise ValueError("Teljesen üres AI válasz (API hiba vagy extrém hallucináció).")
|
||||||
raise ValueError("Az AI hiányos adatot adott vissza vagy hallucinált.")
|
|
||||||
|
|
||||||
# 3. LÉPÉS: HIBRID MERGE (Az RDW adatok felülbírálják az AI-t a hatósági paramétereknél)
|
# 2. LÉPÉS: HIBRID MERGE (Még a validáció előtt!)
|
||||||
final_kw = base_info['rdw_kw'] if base_info['rdw_kw'] > 0 else (ai_data.get("kw") or 0)
|
# Az RDW adatok felülbírálják az AI-t a hatósági paramétereknél
|
||||||
final_ccm = base_info['rdw_ccm'] if base_info['rdw_ccm'] > 0 else (ai_data.get("ccm") or 0)
|
final_kw = base_info['rdw_kw'] if base_info['rdw_kw'] > 0 else int(ai_data.get("kw", 0) or 0)
|
||||||
|
final_ccm = base_info['rdw_ccm'] if base_info['rdw_ccm'] > 0 else int(ai_data.get("ccm", 0) or 0)
|
||||||
|
|
||||||
# Üzemanyag tisztítása
|
# Üzemanyag tisztítása
|
||||||
fuel_rdw = base_info.get('rdw_fuel', '')
|
fuel_rdw = base_info.get('rdw_fuel', '')
|
||||||
@@ -83,6 +88,11 @@ class TechEnricher:
|
|||||||
final_euro = base_info['rdw_euro'] or ai_data.get("euro_classification")
|
final_euro = base_info['rdw_euro'] or ai_data.get("euro_classification")
|
||||||
final_cylinders = base_info['rdw_cylinders'] or ai_data.get("cylinders")
|
final_cylinders = base_info['rdw_cylinders'] or ai_data.get("cylinders")
|
||||||
|
|
||||||
|
# 3. LÉPÉS: Intelligens Validáció
|
||||||
|
is_valid, error_msg = self.validate_merged_data(final_kw, final_ccm, base_info['v_type'], final_fuel.lower(), current_attempts)
|
||||||
|
if not is_valid:
|
||||||
|
raise ValueError(f"Validációs hiba: {error_msg}")
|
||||||
|
|
||||||
# 4. LÉPÉS: Mentés az Arany Katalógusba
|
# 4. LÉPÉS: Mentés az Arany Katalógusba
|
||||||
clean_model = str(ai_data.get("marketing_name", base_info['m_name']))[:50].upper()
|
clean_model = str(ai_data.get("marketing_name", base_info['m_name']))[:50].upper()
|
||||||
|
|
||||||
@@ -90,7 +100,7 @@ class TechEnricher:
|
|||||||
INSERT INTO data.vehicle_catalog
|
INSERT INTO data.vehicle_catalog
|
||||||
(master_definition_id, make, model, power_kw, engine_capacity, fuel_type, factory_data)
|
(master_definition_id, make, model, power_kw, engine_capacity, fuel_type, factory_data)
|
||||||
VALUES (:m_id, :make, :model, :kw, :ccm, :fuel, :factory)
|
VALUES (:m_id, :make, :model, :kw, :ccm, :fuel, :factory)
|
||||||
ON CONFLICT (make, model, year_from, fuel_type) DO NOTHING
|
ON CONFLICT ON CONSTRAINT uix_vehicle_catalog_full DO NOTHING
|
||||||
RETURNING id;
|
RETURNING id;
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -121,15 +131,18 @@ class TechEnricher:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
logger.info(f"✨ ARANY REKORD KÉSZ: {base_info['make'].upper()} {clean_model}")
|
logger.info(f"✨ ARANY REKORD KÉSZ: {v_ident}")
|
||||||
self.ai_calls_today += 1
|
self.ai_calls_today += 1
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await db.rollback()
|
await db.rollback()
|
||||||
logger.warning(f"⚠️ Alkimista hiba ({base_info['make']} {base_info['m_name']}): {e}")
|
logger.warning(f"⚠️ Alkimista hiba - {v_ident}: {e}")
|
||||||
|
|
||||||
# Visszaküldés a sorba vagy felfüggesztés
|
# Ha elértük a limitet, KÉZI MODERÁCIÓRA küldjük, egyébként vissza a Kutatónak
|
||||||
new_status = 'suspended' if current_attempts + 1 >= self.max_attempts else 'unverified'
|
new_status = 'manual_review_needed' if current_attempts + 1 >= self.max_attempts else 'unverified'
|
||||||
|
|
||||||
|
# Elmentjük az AI részleges válaszát (vagy a hibát), hogy az admin lássa, mit rontott el a gép
|
||||||
|
review_data = ai_data if ai_data else {"error": "Nincs értékelhető JSON adat az AI-tól", "raw_context": base_info['web_context']}
|
||||||
|
|
||||||
await db.execute(
|
await db.execute(
|
||||||
update(VehicleModelDefinition)
|
update(VehicleModelDefinition)
|
||||||
@@ -138,15 +151,19 @@ class TechEnricher:
|
|||||||
attempts=current_attempts + 1,
|
attempts=current_attempts + 1,
|
||||||
last_error=str(e)[:200],
|
last_error=str(e)[:200],
|
||||||
status=new_status,
|
status=new_status,
|
||||||
|
specifications=review_data, # Kézi ellenőrzéshez beírjuk a törött adatot!
|
||||||
updated_at=func.now()
|
updated_at=func.now()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
|
|
||||||
if new_status == 'unverified':
|
if new_status == 'unverified':
|
||||||
logger.info("♻️ Akta visszaküldve a Robot-2-nek (Kutató).")
|
logger.info(f"♻️ Akta visszaküldve a Robot-2-nek (Kutató). {attempt_str}")
|
||||||
|
else:
|
||||||
|
logger.error(f"🛑 Max próbálkozás elérve! Kézi moderációra küldve: {v_ident}")
|
||||||
|
|
||||||
async def run(self):
|
async def run(self):
|
||||||
logger.info(f"🚀 Alchemist Pro HIBRID ONLINE (Atomi Zárolás Patch)")
|
logger.info(f"🚀 Alchemist Pro HIBRID ONLINE (Atomi Zárolás + Moderáció Patch)")
|
||||||
while True:
|
while True:
|
||||||
if not self.check_budget():
|
if not self.check_budget():
|
||||||
logger.warning("💸 Napi AI limit kimerítve! Pihenés...")
|
logger.warning("💸 Napi AI limit kimerítve! Pihenés...")
|
||||||
@@ -155,7 +172,6 @@ class TechEnricher:
|
|||||||
try:
|
try:
|
||||||
async with AsyncSessionLocal() as db:
|
async with AsyncSessionLocal() as db:
|
||||||
# ATOMI ZÁROLÁS (A "Szent Grál" a race condition ellen)
|
# ATOMI ZÁROLÁS (A "Szent Grál" a race condition ellen)
|
||||||
# A Robot-1 (ACTIVE) és a Robot-2 (awaiting_ai_synthesis) aktáit is felveszi!
|
|
||||||
query = text("""
|
query = text("""
|
||||||
UPDATE data.vehicle_model_definitions
|
UPDATE data.vehicle_model_definitions
|
||||||
SET status = 'ai_synthesis_in_progress'
|
SET status = 'ai_synthesis_in_progress'
|
||||||
@@ -204,5 +220,4 @@ class TechEnricher:
|
|||||||
await asyncio.sleep(10)
|
await asyncio.sleep(10)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import os # Import az AI limit környezeti változóhoz
|
|
||||||
asyncio.run(TechEnricher().run())
|
asyncio.run(TechEnricher().run())
|
||||||
Reference in New Issue
Block a user