#!/usr/bin/env python3 """Descarga articulos de Wikipedia en espanol sobre los temas de JARVIS y los deja como .txt en la carpeta de documentos del RAG. Luego, construir_indice.py los vectoriza. Corre en el host (solo stdlib + red), sin sonido ni GPU. python3 config/wiki_ingesta.py # descarga # despues, dentro del flatpak: flatpak run --command=python3 io.github.qwersyk.Newelle//master \ ~/COFRE/CODERS/JARVIS/config/construir_indice.py Es aditivo: agrega ficheros a la carpeta, no borra lo que ya haya. """ import os, json, time, urllib.parse, urllib.request, urllib.error API = "https://es.wikipedia.org/w/api.php" UA = "JARVIS-local-RAG/1.0 (asistente de voz local; contacto: local)" DOCS = os.path.expanduser( "~/.var/app/io.github.qwersyk.Newelle/config/models/documents") # Por tema: semillas seguras + una busqueda que amplia con articulos afines. TEMAS = { "tecnologia": { "buscar": ["tecnología", "informática", "hardware", "red informática", "inteligencia artificial", "sistema operativo"], "semillas": ["Tecnología", "Informática", "Internet", "Protocolo de red", "Microprocesador", "Computación en la nube", "Base de datos"], }, "criptografia": { "buscar": ["criptografía", "cifrado", "criptografía asimétrica"], "semillas": ["Criptografía", "Cifrado", "RSA", "Advanced Encryption Standard", "Función hash criptográfica", "Firma digital", "Diffie-Hellman", "Pretty Good Privacy", "Transport Layer Security", "Criptografía de curva elíptica", "Cadena de bloques", "Bitcoin", "Criptomoneda"], }, "software_libre": { "buscar": ["software libre", "código abierto", "historia de Linux"], "semillas": ["Software libre", "GNU", "Richard Stallman", "GNU/Linux", "Linus Torvalds", "Free Software Foundation", "Licencia pública general de GNU", "Código abierto", "Debian", "Unix", "Kernel Linux", "Movimiento del software libre"], }, "hacking": { "buscar": ["seguridad informática", "hacker", "ciberseguridad"], "semillas": ["Hacker", "Seguridad informática", "Hacker (seguridad informática)", "Malware", "Exploit", "Ingeniería social (seguridad informática)", "Phreaking", "Kevin Mitnick", "Anonymous (colectivo)", "Prueba de penetración", "Vulnerabilidad (informática)", "Cortafuegos (informática)", "Ataque de denegación de servicio"], }, "clima": { "buscar": ["cambio climático", "calentamiento global", "clima"], "semillas": ["Cambio climático", "Calentamiento global", "Efecto invernadero", "Gas de efecto invernadero", "Protocolo de Kioto", "Acuerdo de París", "IPCC", "Clima", "Dióxido de carbono", "El Niño", "Nivel del mar", "Energía renovable"], }, "conflictos_espana": { "buscar": ["guerra en España", "conflicto España historia"], "semillas": ["Guerra civil española", "Transición española", "Guerra de la Independencia Española", "Guerras carlistas", "Reconquista", "Dictadura de Francisco Franco", "Segunda República Española", "Movimiento 15-M", "Conflicto vasco", "Guerra de Sucesión Española"], }, } BUSCAR_POR_CONSULTA = 15 # articulos extra por cada busqueda PAUSA = 0.8 # cortesia con la API (subida tras el 429) def _get(params, reintentos=5): params = {**params, "format": "json"} url = API + "?" + urllib.parse.urlencode(params) req = urllib.request.Request(url, headers={"User-Agent": UA}) espera = 2.0 for intento in range(reintentos): try: with urllib.request.urlopen(req, timeout=30) as r: return json.load(r) except urllib.error.HTTPError as e: if e.code == 429 and intento < reintentos - 1: # Wikipedia nos frena: esperar cada vez mas (backoff) time.sleep(espera) espera *= 2 continue raise raise RuntimeError("agotados los reintentos") def titulos_de_busqueda(consulta, limite): try: d = _get({"action": "query", "list": "search", "srsearch": consulta, "srlimit": limite}) return [h["title"] for h in d.get("query", {}).get("search", [])] except Exception as e: print(f" busqueda '{consulta}' fallo: {e}") return [] def extracto(titulo): try: d = _get({"action": "query", "prop": "extracts", "explaintext": 1, "redirects": 1, "titles": titulo}) paginas = d.get("query", {}).get("pages", {}) for _, pg in paginas.items(): txt = pg.get("extract", "") if txt and len(txt) > 300: return pg.get("title", titulo), txt except Exception as e: print(f" extracto '{titulo}' fallo: {e}") return None, None def slug(s): s = "".join(c if c.isalnum() else "_" for c in s.lower()) return s.strip("_")[:80] def main(): os.makedirs(DOCS, exist_ok=True) total = 0 for tema, cfg in TEMAS.items(): print(f"\n=== {tema} ===") titulos = list(cfg["semillas"]) for consulta in cfg["buscar"]: titulos += titulos_de_busqueda(consulta, BUSCAR_POR_CONSULTA) time.sleep(PAUSA) # dedup conservando orden vistos, unicos = set(), [] for t in titulos: if t.lower() not in vistos: vistos.add(t.lower()); unicos.append(t) for t in unicos: dst = os.path.join(DOCS, f"wiki_{tema}_{slug(t)}.txt") if os.path.exists(dst): continue titulo_real, txt = extracto(t) time.sleep(PAUSA) if not txt: continue with open(dst, "w", encoding="utf-8") as f: f.write(f"# {titulo_real}\n(Wikipedia, tema: {tema})\n\n{txt}\n") total += 1 print(f" [{total}] {titulo_real} ({len(txt)//1024} KB)") print(f"\nlisto: {total} articulos nuevos en {DOCS}") if __name__ == "__main__": main()