AiAnonymize_v3 / web /services /verify_engine.py
Alessandro Tomassini
deploy(hf): overlay README/Dockerfile da huggingface/, senza docs/binari/model
c42a5a1
Raw
History Blame Contribute Delete
3.57 kB
"""Costruzione del verificatore LLM (giudice a valle).
Estratta dal `ServiceRegistry` perché è una factory autonoma: dipende solo dalle
`Settings` e da un hook `on_before` (fornito dal registry per liberare la VRAM
prima di caricare l'LLM). Ritorna `None`, con degradazione controllata, quando
l'ML è disabilitato o il modello-giudice non è disponibile.
"""
from __future__ import annotations
from collections.abc import Callable
from config.runtime.settings import Settings
from core.support.logging_utils import get_logger
logger = get_logger("registry")
def build_verifier(
settings: Settings,
on_before: Callable[[], None],
approver: str | None = None,
simulate: bool = False,
):
"""Costruisce il verificatore LLM (giudice a valle) o None se indisponibile.
Il modello-giudice è scelto tramite la chiave `approver` (default da
Settings, fallback al predefinito del registry): un plugin in
`layers/llm/llm_approver/` fornisce client (llama.cpp locale) e prompt.
Richiede ENABLE_ML come il layer LLM. `on_before` è l'hook (eseguito una sola
volta prima della prima chiamata al modello) che libera la VRAM dei modelli di
detection prima di caricare l'LLM.
`simulate=True` (debug): il verificatore costruisce i prompt per ogni span ma
NON chiama l'LLM (nessun load del modello, nessuna valutazione). Serve a
ispezionare in debug i prompt che verrebbero inviati.
"""
if not settings.enable_ml:
logger.info("Verifica LLM richiesta ma ENABLE_ML=False: ignorata.")
return None
try:
from layers.llm.backends.base import LlmGenerationParams
from layers.llm.llm_approver import provider as approver_provider
from layers.llm.verifier import LlmSpanVerifier
key = approver or settings.approver
plugin = approver_provider.resolve_approver(key)
logger.info(
"[giudice] modello selezionato: '%s' (%s)",
plugin.spec.key, plugin.spec.repo,
)
# Batch moderato: dare al modello il contesto di una lista lo fa
# "ingaggiare" meglio (a entità singola tende a rispondere []), ma
# piccolo per limitare troncamenti/omissioni dei modelli più deboli.
verifier_batch = 8
# Token di output PROPORZIONATI al batch: ogni oggetto JSON pesa ~32
# token, quindi bastano ~32/entità + margine. Tetto basso = niente
# generazioni runaway = molto più veloce (prima era 768, lentissimo).
# La repetition_penalty effettiva è quella dello spec del giudice
# (tarata per modello), applicata in BaseApprover.build_client.
params = LlmGenerationParams(
max_new_tokens=48 * verifier_batch, # ~384 per 8 entità
temperature=settings.llm_temperature,
timeout=settings.llm_timeout,
repetition_penalty=settings.verifier_repetition_penalty,
)
client = plugin.build_client(settings, params)
prompt = plugin.prompt()
except Exception as exc: # noqa: BLE001 - degradazione controllata
logger.warning("Verificatore LLM non disponibile: %s", exc)
return None
# Hook eseguito UNA volta prima della prima chiamata al giudice: libera
# dalla VRAM TUTTI gli altri modelli (sempre, non solo su cuda: su CPU è
# un no-op innocuo che comunque libera RAM e cache engine).
return LlmSpanVerifier(
client, weight=settings.verifier_weight,
batch_size=verifier_batch, on_before=on_before,
prompt=prompt, simulate=simulate,
)