chore(verify): recuperar enum_urls.php y commitear tooling de verificación #121
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>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
#!/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")
|
||||
Reference in New Issue
Block a user