chitta
Himbersor
Bueno he creado un par de script en Python para aprovechar al máximo la poca capacidad de un modelo pequeño como phi4 mini abliterated de 3b para el móvil.
Herramientas:
Termux descargado de su web, para tener una consola de comandos buena en el móvil.
Instalar Python, y librerías (pdf2image, tqdm, pytesseract. Pregunten a una IA si tenéis problemas para instalarlo.
El primer script, te pide que le digas el nombre de un pdf como primeros auxilios.pdf y coge un prompt desde un archivo de texto, por ejemplo, busca en el documento como parar una hemorragia arterial.
Da igual que solo sea un 3b, buscará en el documento PDF aunque no esté en OCX el PDF, el script lo pasará a OCX para que la IA pueda leerlo.
Python:import sys from pdf2image import convert_from_path import pytesseract from tqdm import tqdm import requests import os # ================== CONFIGURACIÓN ================== MODEL_NAME = "huihui_ai/phi4-mini-abliterated:3.8b" pytesseract.pytesseract.tesseract_cmd = '/data/data/com.termux/files/usr/bin/tesseract' RUTA_PROMPT_DEFECTO = "/storage/emulated/0/Documents/prompt.txt" def extraer_texto_con_ocr(ruta_pdf: str, max_paginas: int = None): print(" Convirtiendo PDF a imágenes para OCR...") try: # Convertir PDF a imágenes images = convert_from_path(ruta_pdf, dpi=300, thread_count=2) if max_paginas: images = images[:max_paginas] texto_completo = [] print(f" Procesando {len(images)} páginas con OCR...") for i, image in enumerate(tqdm(images)): print(f" Página {i+1}...", end=" ") texto = pytesseract.image_to_string(image, lang='spa') # 'spa' = español texto_completo.append(texto) print(f" {len(texto)} caracteres") return "\n\n".join(texto_completo) except Exception as e: print(f"❌ Error: {e}") return "" def resumir_con_phi4(texto: str, ruta_prompt: str): # Intentar leer el prompt personalizado desde el archivo de texto if os.path.exists(ruta_prompt): print(f" Cargando instrucciones personalizadas desde: {ruta_prompt}") with open(ruta_prompt, 'r', encoding='utf-8') as f: prompt_base = f.read().strip() else: # Prompt de emergencia por si el archivo no existe o no se encuentra print("⚠️ Archivo de instrucciones no encontrado. Usando prompt de resumen por defecto.") prompt_base = "Haz un resumen completo y bien estructurado del siguiente documento en español." payload = { "model": MODEL_NAME, "messages": [ {"role": "system", "content": "Eres un asistente científico y técnico experto en análisis de documentos. Sigue estrictamente las instrucciones del usuario."}, {"role": "user", "content": f"{prompt_base}\n\nDocumento:\n{texto[:100000]}"} ], "stream": False, "options": {"temperature": 0.3, "num_ctx": 5000} } try: print(f"\n Procesando con {MODEL_NAME}...\n") response = requests.post("http://127.0.0.1:11434/api/chat", json=payload, timeout=600) if response.status_code == 200: return response.json()['message']['content'] else: return f"Error {response.status_code}" except Exception as e: return f"❌ Error conectando con Ollama: {e}" if __name__ == "__main__": # 1. Gestión del archivo PDF if len(sys.argv) > 1: ruta_pdf = sys.argv[1] else: nombre = input("Nombre del archivo PDF (en carpeta Documents): ").strip() ruta_pdf = f"/storage/emulated/0/Documents/{nombre}" if not nombre.lower().endswith(".pdf"): ruta_pdf += ".pdf" # 2. Gestión del archivo de instrucciones (Prompt) print(f"\n Por defecto buscaré las instrucciones en: {RUTA_PROMPT_DEFECTO}") cambiar_prompt = input("¿Quieres usar otro archivo .txt para las instrucciones? (S/N): ").strip().lower() if cambiar_prompt == 's': nombre_prompt = input("Nombre del archivo TXT (en carpeta Documents): ").strip() ruta_prompt = f"/storage/emulated/0/Documents/{nombre_prompt}" if not ruta_prompt.lower().endswith(".txt"): ruta_prompt += ".txt" else: ruta_prompt = RUTA_PROMPT_DEFECTO print(f"\n Procesando PDF: {ruta_pdf}") texto = extraer_texto_con_ocr(ruta_pdf) if len(texto.strip()) < 200: print("⚠️ Muy poco texto extraído. Revisa que el PDF tenga texto legible.") else: resultado = resumir_con_phi4(texto, ruta_prompt) print("="*70) print("RESULTADO DEL PROCESAMIENTO (PHI-4 ABLITERATED)") print("="*70) print(resultado) print("="*70)
El segundo script, te pregunta que es lo que quieres saber, y después el número de iteraciones. Lo que hara será masticar su respuesta para afinarla más y mejor, razonar sobre su respuesta y dar más calidad en la salida y conclusión final. Dado que un 3b es un poco simple.
Python:import requests import sys import time # ================== CONFIGURACIÓN ================== MODEL_NAME = "huihui_ai/phi4-mini-abliterated:3.8b" OLLAMA_URL = "http://127.0.0.1:11434/api/chat" # =================================================== def ejecutar_bucle_razonamiento(problema_inicial: str, max_iteraciones: int): # Inicializamos el historial con el rol del sistema y el problema del usuario historial_mensajes = [ { "role": "system", "content": ( "Eres un motor de razonamiento avanzado y autocrítico. Tu objetivo es resolver " "problemas complejos dividiendo tu pensamiento en pasos técnicos, analizando " "tus propios errores del pasado y refinando la solución en cada turno." ) }, { "role": "user", "content": f"Este es el problema a resolver. Da tu primer enfoque o hipótesis inicial:\n\n{problema_inicial}" } ] print("\n" + "="*60) print(" INICIANDO BUCLE DE RAZONAMIENTO ITERATIVO") print("="*60) tiempo_inicio = time.time() for i in range(1, max_iteraciones + 1): print(f"\n [ITERACIÓN {i} de {max_iteraciones}] El modelo está pensando...") payload = { "model": MODEL_NAME, "messages": historial_mensajes, "stream": False, "options": { "temperature": 0.5, # Temperatura media para permitir enfoques alternativos "num_ctx": 8192 # Contexto expandido para recordar las iteraciones previas } } try: response = requests.post(OLLAMA_URL, json=payload, timeout=300) if response.status_code != 200: print(f"❌ Error en Ollama: {response.status_code}") break # Extraemos la respuesta del pensamiento actual respuesta_modelo = response.json()['message']['content'] print(f"\n--- SALIDA DEL RAZONAMIENTO (PASO {i}) ---") print(respuesta_modelo) print("-" * 40) # Guardamos lo que pensó el modelo en el historial historial_mensajes.append({"role": "assistant", "content": respuesta_modelo}) # Si es la última iteración, no le pedimos que vuelva a pensar, rompemos el ciclo if i == max_iteraciones: break # Inyectamos el prompt de auto-corrección para la siguiente iteración prompt_autocritica = ( f"Analiza de forma crítica tu razonamiento del paso {i}. " "Encuentra posibles fallos lógicos, sesgos, asunciones incorrectas o datos omitidos. " "A raíz de tu autocrítica, genera un nuevo razonamiento superador que corrija esos errores " "y te acerque más a la solución matemática o técnica exacta." ) historial_mensajes.append({"role": "user", "content": prompt_autocritica}) except Exception as e: print(f"❌ Error de conexión: {e}") break # ================= FASE FINAL: RESPUESTA DEFINITIVA ================= print("\n [FASE FINAL] Generando la solución depurada definitiva...") prompt_final = ( "Tomando en cuenta todo tu proceso de pensamiento, tus autocríticas y las correcciones " "anteriores, escribe la solución final, limpia, estructurada y definitiva al problema original." ) historial_mensajes.append({"role": "user", "content": prompt_final}) try: payload["messages"] = historial_mensajes payload["options"]["temperature"] = 0.1 # Temperatura baja para máxima precisión al cerrar response = requests.post(OLLAMA_URL, json=payload, timeout=300) solucion_final = response.json()['message']['content'] tiempo_total = time.time() - tiempo_inicio print("\n" + "="*70) print(f"✨ SOLUCIÓN FINAL CONVERGENTE (Tiempo total: {tiempo_total:.2f}s)") print("="*70) print(solucion_final) print("="*70) except Exception as e: print(f"❌ Error al generar la conclusión final: {e}") if __name__ == "__main__": print("--- MOTOR DE PENSAMIENTO AUTÓNOMO LOCAL ---") problema = input("Introduce el problema, código o pregunta compleja:\n> ").strip() try: iteraciones = int(input("\n¿Cuántas iteraciones de pensamiento deseas (ej. 3 o 4)?:\n> ")) except ValueError: print("Entrada no válida. Usando 3 iteraciones por defecto.") iteraciones = 3 if problema: ejecutar_bucle_razonamiento(problema, iteraciones) else: print("No introdujiste ningún problema.")
De nada
Esto tiene bastante sentido como herramienta de campo, pero yo separaría tres cosas:
1. OCR y extracción: ahí el modelo no pinta nada. Cuanto mejor dejes el texto antes de pasarlo al LLM, menos alucina. Tesseract + limpieza + troceado por secciones ya te da media batalla ganada.
2. Recuperación: en vez de meterle 100.000 caracteres a pelo, lo ideal es buscar primero los trozos relevantes y pasarle solo esos. Aunque sea un RAG tacaño con embeddings pequeños o incluso BM25, para móvil mejora mucho.
3. Razonamiento iterativo: cuidado con eso. Un 3B puede refinar una respuesta, sí, pero también puede refinar su propio error hasta dejarlo muy convincente. Yo añadiría una fase de "busca evidencia textual exacta" antes de la conclusión final.
Para móvil lo veo más como bibliotecario offline que como sabio. Si le das documentos buenos, encuentra, resume y ordena. Si le pides juicio experto desde cero, empiezan los fuegos artificiales.