Terminal / agents /critic.py
Baida-A
Initial clean deploy (Reverse Proxy removed)
28a08e7
Raw
History Blame
3.52 kB
"""
critic.py — Critic Model
Secondo passaggio: verifica output, trova errori, suggerisce miglioramenti.
Usa AIClient generico (non OllamaClient) per compatibilità HF Space.
"""
import json
import re
from typing import Any
import logging
_logger = logging.getLogger("agents.critic")
CRITIC_SYSTEM = """Sei un critico AI. Valuta l'output dato e rispondi SOLO con JSON valido:
{
"quality": 0-10,
"issues": ["lista problemi trovati — solo se GRAVI, non dettagli stilistici"],
"suggestions": ["lista miglioramenti concreti"],
"is_complete": true/false,
"needs_retry": true/false,
"confidence": 0.0-1.0
}
Criteri:
- quality 8-10: risposta corretta, completa, con codice/calcoli se richiesti
- quality 5-7: risposta parziale ma utile, mancano dettagli non essenziali
- quality 0-4: risposta sbagliata, vuota, o fuori tema → needs_retry: true
- needs_retry: true SOLO se quality <= 3 (non per risposte corrette ma incomplete)
- Se la risposta ha codice funzionante, calcoli corretti o dati reali → quality >= 7
Nessun testo prima o dopo il JSON."""
def _extract_json_balanced(raw: str) -> str | None:
"""P16-B3: depth-counting bilanciato — sostituisce regex greedy r'{[\s\S]+}'.
Gestisce oggetti JSON annidati correttamente (es. patch con sub-oggetti).
"""
depth = 0
start = -1
for i, ch in enumerate(raw):
if ch == '{':
if depth == 0:
start = i
depth += 1
elif ch == '}':
depth -= 1
if depth == 0 and start != -1:
return raw[start:i + 1]
return None
class Critic:
def __init__(self, llm_client: Any):
self.llm = llm_client
async def evaluate(self, task: str, output: str, model: str | None = None) -> dict:
messages = [
{"role": "system", "content": CRITIC_SYSTEM},
{
"role": "user",
"content": (
f"Task originale: {task}\n\n"
f"Output da valutare:\n{output[:2000]}"
),
},
]
try:
raw = await self.llm.chat(messages, temperature=0.2, max_tokens=512)
json_match = _extract_json_balanced(raw)
if json_match:
result = json.loads(json_match)
result["_evaluated"] = True
return result
except Exception as _exc:
_logger.debug("[critic] silenced %s", type(_exc).__name__) # noqa: BLE001
# Fallback euristico (nessuna chiamata LLM)
quality = 5
issues: list[str] = []
if len(output) < 50:
quality -= 3
issues.append("Output troppo breve")
if "errore" in output.lower() or "error" in output.lower():
quality -= 2
issues.append("Potenziali errori nell'output")
if len(output) > 100:
quality += 2
# Penalizza description leak dei tool
if "usa il tool" in output.lower() or "esegui il comando" in output.lower():
quality -= 3
issues.append("Agente descrive tool invece di usarli")
return {
"quality": max(0, min(10, quality)),
"issues": issues,
"suggestions": ["Verifica la completezza della risposta"],
"is_complete": len(output) > 100,
"needs_retry": quality < 3, # S192: alzato soglia 4→3 — meno falsi negativi
"confidence": 0.5,
"_fallback": True,
}