66ee943da4
El enumerador de URLs no se había commiteado y se perdió. Recuperado reproduciendo la composición de la corrida previa (menús×5 idiomas + categorías con contenido + contenido no-ES). Se añade también classify_gaps.py (clasifica huecos: traducir vs descargar) y run_all.sh. Outputs y .venv quedan en .gitignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
2.6 KiB
Python
49 lines
2.6 KiB
Python
#!/usr/bin/env python3
|
|
# Capa 3 (opcional): review semántica con Gemma LOCAL, SOLO sobre páginas marcadas por la Capa 1.
|
|
# Coste 0 (LLM local). Si Gemma no responde, sale limpio y deja constancia.
|
|
import json, os, urllib.request
|
|
DIR=os.path.dirname(os.path.abspath(__file__))
|
|
GEMMA="http://172.19.128.1:1234/v1/chat/completions"; MODEL="google/gemma-4-e4b"
|
|
MAX=int(os.environ.get('MAX','40'))
|
|
LNAME={'en':'inglés','fr':'francés','it':'italiano','pt':'portugués','es':'español'}
|
|
|
|
# texto visible está en report.jsonl (no en report.json)
|
|
texts={}
|
|
for line in open(os.path.join(DIR,'report.jsonl'),encoding='utf-8'):
|
|
line=line.strip()
|
|
if not line: continue
|
|
r=json.loads(line); texts[(r['lang'],r['url'])]=r.get('text','')
|
|
|
|
rep=json.load(open(os.path.join(DIR,'report.json'),encoding='utf-8'))
|
|
flagged=[r for r in rep if any(f in r.get('flags',[]) for f in ('SPANISH_LEAK','LANG_MISMATCH'))][:MAX]
|
|
print(f"páginas marcadas para review LLM: {len(flagged)} (cap {MAX})")
|
|
|
|
def ask(lang,text):
|
|
prompt=(f"Eres revisor de QA de un sitio web. Esta página DEBERÍA estar en {LNAME.get(lang,lang)}. "
|
|
f"Evalúa el texto visible. Responde SOLO un JSON válido: "
|
|
f'{{"idioma_ok": true/false, "idioma_predominante": "es/en/fr/it/pt", "problemas": ["lista breve de mistraducciones, trozos sin traducir o sin sentido"]}}.\n\nTEXTO:\n'+text[:1500])
|
|
body=json.dumps({"model":MODEL,"messages":[{"role":"user","content":prompt}],"temperature":0.1,"max_tokens":2200}).encode()
|
|
req=urllib.request.Request(GEMMA,data=body,headers={"Content-Type":"application/json"})
|
|
r=json.load(urllib.request.urlopen(req,timeout=120))
|
|
out=r["choices"][0]["message"]["content"].strip()
|
|
s=out.find('{'); e=out.rfind('}')
|
|
return json.loads(out[s:e+1]) if s>=0 else {"raw":out[:200]}
|
|
|
|
results=[]
|
|
try:
|
|
urllib.request.urlopen("http://172.19.128.1:1234/v1/models",timeout=6)
|
|
except Exception as e:
|
|
print("Gemma no responde, salto Capa 3:",e);
|
|
json.dump({"skipped":True,"reason":str(e)},open(os.path.join(DIR,'llm_review.json'),'w')); raise SystemExit(0)
|
|
|
|
for i,r in enumerate(flagged):
|
|
t=texts.get((r['lang'],r['url']),'')
|
|
if len(t)<60: continue
|
|
try:
|
|
v=ask(r['lang'],t); v.update({'url':r['url'],'lang':r['lang'],'flags':r['flags']}); results.append(v)
|
|
print(f" [{i+1}/{len(flagged)}] {r['lang']} ok={v.get('idioma_ok')} pred={v.get('idioma_predominante')}")
|
|
except Exception as e:
|
|
print(f" [{i+1}] error {e}")
|
|
json.dump(results,open(os.path.join(DIR,'llm_review.json'),'w',encoding='utf-8'),ensure_ascii=False,indent=1)
|
|
print(f"review LLM guardada: {len(results)} páginas")
|