Yap:面向资源受限教育环境的本地 AI 助手(基于 Llama 3.2)
ChincoLinux/Yap
Yap 是一个基于 Llama 3.2 3B/1B Instruct(GGUF Q4_K_M)的本地对话式 AI 助手,通过 llama.cpp 在 CPU-only 上运行,面向 Debian 13 与 3-8 GB RAM 的教育硬件。
Yap: Asistente de IA Local para Entornos Educativos con Recursos Limitados
Version: 1.0.1 (ver VERSION · CHANGELOG.md) |
Sistema de agente conversacional basado en Llama 3.2, ejecucion CPU-only, disenado para Debian 13 con soporte de 3 a 8 GB de RAM mediante ramas de configuracion graduales.
| Modelo | Llama 3.2 3B / 1B Instruct (GGUF Q4_K_M) |
| Runtime | llama.cpp, CPU-only, enlace estatico |
| Dependencias de Python | ninguna (solo stdlib) |
| Dependencias de tests | pytest |
| Idioma | Espanol |
| Licencia | MIT |
Resumen
Yap es un asistente de inteligencia artificial que opera integramente en local
sobre CPU, sin dependencia de conexion a Internet para su funcionamiento base.
Emplea el modelo Llama 3.2 Instruct (GGUF Q4_K_M) ejecutado mediante
llama.cpp con enlace estatico, e implementa un sistema de seguridad basado
en listas blancas (whitelist) de aplicaciones y dominios. El proyecto se
distribuye en tres ramas (main, lowmem, ultra-lowmem) que escalan el
consumo de RAM desde ~3.5 GB hasta ~1.8 GB, adaptandose a hardware educativo de
distintas capacidades. La clasificacion de intenciones se realiza mediante el
propio LLM, eliminando la necesidad de patrones rigidos y proporcionando
tolerancia a errores ortograficos y variaciones sintacticas.
No es solo un chat: es un entorno de practica para el aula. Sobre el mismo
agente hay un sistema de cursos con RAs y EAs, catalogo de ejercicios PSeInt
evaluados automaticamente, tutor paso a paso, perfil del estudiante, historial
persistente entre sesiones, dificultad adaptativa, RAG local offline, feedback
pedagogico con nota chilena, telemetria anonima en disco y modo accesible para
lector de pantalla. Todas esas capacidades viven en un unico archivo,
yap.py, y operan 100% offline.
| Documento | Para que |
|---|---|
| USAGE.md | Referencia rapida de comandos y solucion de problemas |
| docs/DEPLOY.md | Despliegue individual y masivo en red escolar |
| docs/PACKAGING.md | Empaquetado .deb y repositorio apt |
| docs/SUPER-YAP.md | Cliente Gradio en Cloud Run (opt-in) |
| docs/RAG.md | RAG local con BM25, sin dependencias |
| docs/SECURITY-AUDIT.md | Auditoria de seguridad post-talleres |
| docs/ROADMAP.md | Roadmap y prioridades |
| tests/README.md | Suite de pruebas: 30 archivos, 1079 tests |
| AGENTS.md | Guia para agentes IA que colaboran en el repo |
| CLAUDE.md | Arquitectura tecnica resumida |
| CONTRIBUTING.md | Como contribuir |
Estructura del repositorio
Yap/
├── yap.py # El agente completo (~6200 lineas, stdlib-only)
├── setup.sh # Instalador de desarrollo: compila llama.cpp, baja el modelo
├── build-deb.sh # Genera yap_*.deb y yap-models-*.deb (#31)
├── deploy-yap.sh # Despliegue masivo por SSH en un lab (#31)
├── VERSION # Version, leida por setup.sh y auto-release
├── whitelist/
│ ├── apps.conf # Aplicaciones permitidas (Nombre:bin1,bin2)
│ ├── web.conf # Dominios permitidos para webfetch
│ └── pseint/
│ ├── ejercicios.conf # Catalogo v1 y v2
│ └── guia_ejercicios.pdf # Guia de solucion, la abre el tutorial
├── cursos/ # Cursos en JSON (FPY1101.json)
├── packaging/ # Plantillas DEBIAN: yap, yap-models-1b, yap-models-3b
├── apparmor/ # Perfiles AppArmor del agente (#14)
├── tests/ # 30 archivos de pruebas, 1079 tests + run_tests.py
├── docs/ # DEPLOY, PACKAGING, RAG, ROADMAP, SECURITY-AUDIT, SUPER-YAP, TRUNK-BASED
├── scripts/ci/ # Utilidades de CI: trazabilidad PR, labels, preflight
├── agent-platform/ # Plantillas de despliegue de Super Yap
├── .githooks/post-checkout# Aviso de cambio de modelo al cambiar de rama
├── .github/
│ ├── workflows/ # 11 workflows (CI, revision A-Dev, releases, quality gates)
│ ├── adev/ # Politicas Hardness, agente reviewer, RAG, skills
│ └── ISSUE_TEMPLATE/
├── AGENTS.md CLAUDE.md ADEV.md GOVERNANCE.md CONTRIBUTING.md
├── README.md USAGE.md CHANGELOG.md SECURITY.md SUPPORT.md CODE_OF_CONDUCT.md
└── LICENSE MIT
Tabla de contenidos
- 1. Objetivos
- 2. Especificaciones tecnicas
- 3. Arquitectura del sistema
- 4. Componentes del stack tecnologico
- 5. Seguridad
- 6. Instalacion
- 7. Ramas de configuracion
- 8. Uso
- 9. Limitaciones
- 10. Trabajo futuro
- 11. Licencia
- 12. Pruebas y verificacion
- 13. Contribuir
1. Objetivos
Construir un sistema Debian estable ultraligero con un agente IA local (CPU-only) capaz de:
- Responder en espanol con enfoque educativo.
- Ejecutar acciones seguras del sistema mediante tooling controlado por whitelist.
- Abrir aplicaciones desde una lista blanca configurable.
- Recuperar informacion de sitios web aprobados.
- Aplicar restricciones para acciones sensibles.
- Emitir alertas graficas mediante
notify-send. - Practicar y evaluar con un sistema de cursos y ejercicios, sin salir del aula.
- Functionar sin conexion: todo el material pedagogico vive en disco y se indexa con un RAG local.
2. Especificaciones tecnicas
| Componente | Detalle |
|---|---|
| Modelo | Llama 3.2 3B Instruct (GGUF Q4_K_M) / 1B Instruct |
| Runtime | llama.cpp (CPU-only, enlace estatico) |
| RAM minima | ~3.5 GB (main), ~3.1 GB (lowmem), ~1.8 GB (ultra-lowmem) |
| Contexto | 4096 tokens (main) / 2048 tokens (lowmem, ultra-lowmem) |
| Historial | MAX_HISTORY = 6 turnos en memoria; MAX_HISTORY_SESSIONS = 20 sesiones en disco |
| Latencia estimada | 5-10 s primeras tokens en CPU (2 nucleos), ~40 tok/s |
| Idioma | Español (default), English, Mapudungun (yap perfil idioma) |
| SO destino | Debian 13 (64-bit) |
| Dependencias Python | ninguna — yap.py es stdlib-only por politica del proyecto |
2.1 Constantes de configuracion
Todas en el bloque superior de yap.py, sobreescribibles por variable
de entorno cuando el caso lo pide:
| Constante | Default | Entorno | Para que |
|---|---|---|---|
MODEL_PATH |
/opt/yap/models/Llama-3.2-3B-Instruct-Q4_K_M.gguf |
YAP_MODEL_PATH |
Techo 3B: un path con 8B se ignora y cae al 3B |
MAX_CTX |
2048 |
— | Tokens de contexto de llama-cli |
MAX_HISTORY |
6 |
— | Turnos de conversacion en memoria |
LLAMA_THREADS |
2 |
YAP_LLAMA_THREADS |
Hilos de inferencia |
LLAMA_TEMP_QUERY |
0.7 |
YAP_LLAMA_TEMP_QUERY |
Temperatura de respuesta |
LLAMA_TEMP_PSEINT |
0.5 |
YAP_LLAMA_TEMP_PSEINT |
Temperatura del tutor |
LLAMA_TEMP_CLASSIFY |
0.1 |
YAP_LLAMA_TEMP_CLASSIFY |
Baja, para que el clasificador sea estable |
LLAMA_TEMP_EVAL |
0.2 |
YAP_LLAMA_TEMP_EVAL |
Baja, para que el evaluador devuelva JSON |
MAX_INTENTOS_EJERCICIO |
3 |
YAP_MAX_INTENTOS |
Intentos por actividad o ejercicio |
PUNTAJE_APROBACION |
60 |
YAP_PUNTAJE_APROBACION |
Puntaje que se considera aprobado |
MAX_OPEN_SESSIONS |
3 |
YAP_MAX_SESSIONS |
Sesiones abiertas simultaneas |
| RAG | RAG_ENABLED=1, RAG_TOP_K=5, RAG_MAX_CONTEXT_TOKENS=512 |
YAP_RAG_ENABLED, YAP_RAG_TOP_K, YAP_RAG_MAX_TOKENS |
Recuperacion contextual local sobre cursos/, docs/, whitelist/, USAGE.md y AGENTS.md (#118) |
| Super Yap | opt-in | YAP_SUPER_ENABLED, YAP_SUPER_ENDPOINT, YAP_SUPER_TOKEN, YAP_SUPER_TOKEN_FILE, YAP_SUPER_TIMEOUT, YAP_SUPER_HOSTS, YAP_SUPER_INTERNET |
Cliente Gradio en Cloud Run (#91) |
| Diagnostico | apagado | YAP_DEBUG |
Trazas internas |
3. Arquitectura del sistema
3.1 Diagrama de capas
+----------+ +---------+ +------------+
| Usuario | --> | CLI yap | --> | interpret()|
+----------+ +---------+ +------------+
|
+-------------------------+-------------------------+
| | |
v v v
+------------------+ +------------------+ +------------------+
| Rutas por teclado| | classify_intent()| | Menu numerado |
| (sin LLM) | | (LLM, temp 0.1) | | (TUI) |
+------------------+ +------------------+ +------------------+
| | |
+-------------------------+-------------------------+
|
v
+--------------------+
| handle_action() |
+--------------------+
|
+-----------+-----------+-----------+-----------+-----------+
| | | | | |
v v v v v v
+----------+ +---------+ +--------+ +---------+ +--------+ +--------+
| Whitelist| | Whitelist| | LLM | | RAG | |Evaluac.| |Sesiones|
| apps | | web | | local | | BM25 | |LLM | |perfil |
+----------+ +---------+ +--------+ +---------+ +--------+ +--------+
| | | | | |
v v v v v v
+----------+ +---------+ +--------+ +---------+ +--------+ +--------+
| Lanzar | | Webfetch| |Respues-| |Contexto | |Puntaje | |Historial|
| app | | + 3000 | |ta | |recuperado| |nota 1-7| | en disco|
| + alerta | | chars | | | | | | | |
+----------+ +---------+ +--------+ +---------+ +--------+ +--------+
3.2 Flujo de una consulta
- El usuario escribe un comando o pregunta en la terminal, o elige una opcion del menu numerado.
interpret()(yap.py:5825) decide sin gastar LLM cuando puede: rutas por teclado (abre,busca,pseint,curso,sesion,telemetria,rag,super,perfil,menu...) y opciones del menu por numero. Con el modelo 1B el clasificador acierta poco, asi que todo lo que el menu anuncia tiene una ruta directa.- Si nada coincide,
classify_intent()(yap.py:5757) consulta al LLM con un prompt de clasificacion y recibeACCION|PARAMETRO. handle_action()(yap.py:6059) ejecuta la accion, actualiza el historial y los contadores de telemetria.- El resultado se muestra en pantalla; si procede, se lanza el RAG y se evaluan actividades cuando la sesion esta dentro de un curso.
3.3 Componentes del sistema
| Componente | Funcion |
|---|---|
| TUI | Interfaz de linea de comandos: curses nativo, ANSI directo, 0 dependencias |
| Menu numerado | 15 opciones que cubren lo mas usado sin memorizar comandos |
Router interpret() |
Resuelve ordenes conocidas sin pasar por el LLM |
| Clasificador | classify_intent() decide la intencion con el propio LLM |
| Whitelist de apps | Lista de aplicaciones permitidas con soporte multi-binario |
| Whitelist de dominios | Lista de dominios permitidos para webfetch |
| LLM local | Llama 3.2 via llama-cli, CPU-only, con historial |
| Super Yap | Cliente Gradio en Cloud Run, opt-in, con reenvio de HISTORY |
| RAG local | Recuperacion contextual con Okapi BM25, 100% stdlib (docs/RAG.md) |
| Cursos | JSON en cursos/, con RAs, EAs, actividades y evaluaciones tipadas |
| Ejercicios PSeInt | Catalogo v1/v2 con 4 tipos de evaluacion y pistas progresivas |
| Evaluador | Puntaje + feedback por LLM, con fallback textual sin LLM |
| Perfil | Nombre, nivel e idioma, inyectados al system prompt (#24) |
| Sesiones | Pausa, retoma, archiva; el contexto sobrevive al cierre (#21) |
| Historial | Ultimas 20 sesiones en disco, reanudables (#13) |
| Dificultad adaptativa | Sube o baja el nivel segun el rendimiento (#30) |
| Accesibilidad | Alto contraste, fuentes 2x, deteccion de Orca, teclado (#37) |
| Telemetria | Contadores locales anonimos, exportables y desactivables (#38) |
| Notificador | Alertas graficas mediante notify-send |
4. Componentes del stack tecnologico
4.1 Bash — Script de instalacion (setup.sh)
El instalador automatiza la configuracion del entorno. Conceptos clave:
| Concepto | Explicacion |
|---|---|
set -euo pipefail |
Modo estricto: -e aborta en error; -u variables no definidas como error; -o pipefail propaga errores en tuberias |
SCRIPT_DIR |
Obtiene la ruta absoluta del directorio del script mediante ${BASH_SOURCE[0]} antes de leer VERSION |
git clone --depth 1 |
Clonado superficial (un solo commit) para minimizar ancho de banda |
cmake + cmake --build |
Configuracion y compilacion con -DBUILD_SHARED_LIBS=OFF para enlace estatico |
| Descarga del modelo | Lee MODEL_PATH de yap.py y descarga el .gguf correspondiente (3B o 1B) |
ln -sf |
Enlace simbolico a yap.py en el repositorio; git pull actualiza sin reinstalar |
core.hooksPath |
Instala .githooks/post-checkout, que informa del cambio de modelo al cambiar de rama |
4.2 Python — Agente principal (yap.py)
Un solo archivo, ~6200 lineas, stdlib-only. Los imports son a proposito
pocos: subprocess, sys, os, time, shutil, textwrap, json, glob,
urllib.*, http.cookiejar, re, atexit, unicodedata, math, hashlib.
Prohibidos por politica: socket, ctypes, pickle, base64, requests.
Mapa por bloques (los numeros de linea son de la version en main):
| Lineas | Bloque | Que hace |
|---|---|---|
| 1–29 | Constantes de rutas | /etc/yap/{whitelist,pseint,cursos} |
| 31–127 | TUI ChincoLinux | ASCII art, cajas, menu numerado, paleta ANSI |
| 130–158 | Modelo y constantes | MODEL_PATH, MAX_CTX, temperaturas, tipos de ejercicio |
| 160–217 | Super Yap (#91) | Ajustes de cliente Gradio, whitelist de hosts |
| 218–308 | Confirmacion humana (#12) | Acciones sensibles, niveles de confianza, persistencia |
| 309–332 | Whitelists | load_whitelist(), load_domain_whitelist() |
| 333–588 | Ejercicios y cursos | Parser v1/v2, catalogo, carga y validacion de cursos |
| 590–687 | Validacion de cursos | Esquema de JSON, actividades evaluables, listado |
| 688–932 | Perfil (#24) | Config, normalizacion, escritura atomica, system prompt |
| 933–1324 | Accesibilidad (#37) | Opciones, sanitizado ANSI, deteccion de Orca, alto contraste, fuentes grandes |
| 1325–1457 | Teclado (#37) | Lectura de teclas, menu interactivo navegable |
| 1458–1577 | Progreso e historial (#13) | progress.json, ultimas 20 sesiones, cmd_historial() |
| 1578–1961 | Sesiones (#21) | CRUD, pausa, retoma, archivo, prompt con contexto |
| 1962–2187 | Telemetria (#38) | Contadores, acciones sin usar, exportacion, borrado |
| 2188–2245 | Onboarding y progreso | Tutorial de bienvenida, carga/guardado |
| 2246–2661 | RAG local (#118) | Tokenizador, chunking, RagBM25Index, persistencia, cmd_rag() |
| 2662–3270 | Evaluacion (#23) | Schema, parser de JSON del LLM, fallback, nota chilena |
| 3271–3984 | Ejercicios (#27) | Evaluacion por tipo, pistas, intentos, cmd_ejercicios() |
| 3985–4165 | Adaptativo (#30) | AdaptiveEngine: reglas de subida, bajada y mantenimiento |
| 4166–4673 | Cursos | cmd_curso(), iniciar_ea(), cmd_mostrar_progreso() |
| 4674–4780 | Notificacion y AppArmor (#14) | notify(), apparmor_status(), cmd_apparmor_status() |
| 4781–4858 | Acciones base | cmd_open_app() con shutil.which(), cmd_webfetch() con 3000 chars |
| 4859–5481 | Super Yap cliente | Delegacion, hosts permitidos, SSE, cmd_query_super() |
| 5482–5756 | Consultas y PSeInt | cmd_query(), cmd_pseint(), cmd_intro_pseint() |
| 5757–5824 | classify_intent() |
Clasificacion con LLM, temp 0.1, fallback a query |
| 5825–5953 | interpret() |
Rutas por teclado, menu por numero, delegacion al clasificador |
| 5954–6221 | main() y handle_action() |
Arranque, TUI curses, REPL de reserva, despacho de acciones |
4.3 llama.cpp y GGUF
| Componente | Rol |
|---|---|
| llama.cpp | Runtime de inferencia en C/C++ para modelos Llama en CPU. Compilado desde fuente con enlace estatico (-DBUILD_SHARED_LIBS=OFF) |
| GGUF | Formato de archivo para modelos cuantizados. Q4_K_M: cuantizacion de 4 bits con mezcla K-quant, balance calidad-rendimiento |
| llama-cli | Parametros: -m (modelo), -p (prompt), -n 384 (tokens), --temp (por tipo de consulta), --ctx-size 2048, --cache-type-k/v q8_0 (KV cache cuantizada), --threads 2, -no-cnv, --no-display-prompt |
Las nueve llamadas a subprocess pasan stdin=subprocess.DEVNULL y todas
llevan timeout=. Sin eso, llama-cli heredaba la terminal, apagaba el eco con
tcsetattr y las consultas del estudiante se volvian invisibles a partir de la
segunda pregunta (#99).
4.4 CMake — Sistema de compilacion
| Parametro | Explicacion |
|---|---|
-DCMAKE_BUILD_TYPE=Release |
Optimiza el binario para velocidad |
-DBUILD_SHARED_LIBS=OFF |
Enlaza todo estaticamente; sin dependencia de libllama.so |
-DLLAMA_CURL=OFF |
Deshabilita soporte CURL (se usa wget para descargas) |
-DLLAMA_CUDA=OFF |
Deshabilita soporte GPU NVIDIA |
-DLLAMA_METAL=OFF |
Deshabilita soporte GPU Apple |
4.5 VirtualBox — Entorno de desarrollo
| Comando | Funcion |
|---|---|
VBoxManage createvm |
Crea y registra una maquina virtual |
VBoxManage modifyvm |
Configura RAM, CPU, VRAM y red NAT |
VBoxManage createmedium disk |
Crea disco virtual de 50 GB dinamico |
VBoxManage storagectl |
Agrega controlador SATA |
VBoxManage storageattach |
Monta ISO de instalacion |
VBoxManage startvm |
Inicia la maquina virtual |
4.6 Debian Linux — Paquetes del sistema base
| Paquete | Proposito |
|---|---|
| build-essential | Compilador gcc y herramientas base para llama.cpp |
| cmake | Generador de archivos de compilacion |
| libcurl4-openssl-dev | Headers de libcurl (requerido por llama.cpp) |
| python3-pip | Instalador de paquetes Python |
| libnotify-bin | Cliente notify-send para alertas graficas |
| libreoffice, evince, firefox-esr, micro, htop | Aplicaciones incluidas en la whitelist |
5. Seguridad
La seguridad de Yap no depende de un solo mecanismo: son capas independientes, y todas estan cubiertas por pruebas.
5.1 Capas de defensa
| Capa | Que impide |
|---|---|
| Whitelist de binarios | Ejecutar cualquier comando del sistema |
| Whitelist de dominios | Salir a la red hacia donde no sea |
| Validacion estricta de dominio | Que notwikipedia.org pase por wikipedia.org |
shell=False siempre |
Inyeccion de comandos via metacaracteres |
timeout= en toda llamada |
Que el agente se cuelgue esperando un subproceso |
stdin=DEVNULL |
Que un hijo robe la terminal y apague el eco (#99) |
| Limite de 3000 chars | Que una pagina entera se coma el contexto |
| Confirmacion humana (#12) | Acciones sensibles sin una segunda confirmacion |
| AppArmor (#14) | Que el proceso se escape de su perfil declarado |
| Sin escritura fuera de rutas | Que el agente modifique archivos del sistema |
| Sin imports peligrosos | socket, ctypes, pickle, base64 estan prohibidos |
5.2 Whitelist de aplicaciones (/etc/yap/whitelist/apps.conf)
- Mapea nombres visibles a comandos del sistema:
Nombre:bin1,bin2. - Soporta multiples binarios alternativos separados por coma (ej.
firefox-esr,firefox). - El agente prueba cada binario en orden hasta encontrar uno disponible mediante
shutil.which(), y valida conshutil.which()antes de cualquierPopen. - Si la app no esta en la lista, el bloqueo es graceful: muestra
[ERROR]y la lista de apps permitidas, sin ejecutarla.
5.3 Whitelist de dominios (/etc/yap/whitelist/web.conf)
- Lista de dominios permitidos para webfetch.
- Validacion estricta: coincidencia exacta o subdominio directo
(
domain == d or domain.endswith("." + d)). - Contenido limitado a 3000 caracteres (
text[:3000]encmd_webfetch()). - Se rechazan esquemas no HTTP y destinos locales:
file:///etc/passwd,127.0.0.1,[::1],javascript:.
Correccion de seguridad (commit
348e9b0): la implementacion original usabadomain.endswith(d), lo que permitia quenotwikipedia.orgcoincidiera conwikipedia.org. El testtest_notwikipedia_no_coincidefija el comportamiento.
5.4 Confirmacion humana para acciones sensibles (#12)
confirm_action() pide una segunda confirmacion antes de ejecutar acciones
marcadas como sensibles. Las respuestas quedan registradas y una accion
confirmada se marca como confiable para no volver a preguntar
(_record_confirmation, _is_trusted). La persistencia sobrevive entre
ejecuciones.
5.5 AppArmor (#14)
apparmor_status() informa si el perfil del agente esta cargado y en modo
enforce o complain. Los perfiles viven en apparmor/ y se
instalan con el paquete .deb. Comandos: yap --apparmor-status.
5.6 Datos que el agente puede tocar
Lectura (configuracion del sistema, nunca se escribe):
| Ruta | Proposito |
|---|---|
/etc/yap/whitelist/ |
Whitelists de apps y dominios |
/etc/yap/cursos/ |
Cursos JSON |
/etc/yap/pseint/ |
Catalogo de ejercicios y guia en PDF |
cursos/*.json, docs/*.md, whitelist/*.conf, USAGE.md, AGENTS.md |
Corpus del RAG local |
Escritura (solo bajo ~/.config/yap/, que respeta $XDG_CONFIG_HOME):
| Ruta | Proposito |
|---|---|
~/.config/yap/profile.json |
Perfil: nombre, nivel, idioma, accesibilidad, estadisticas |
~/.config/yap/progress.json |
Progreso del estudiante en cursos y ejercicios |
~/.config/yap/history.json |
Ultimas 20 sesiones, reanudables |
~/.config/yap/sessions.json |
Sesiones abiertas, pausadas y archivadas |
~/.config/yap/telemetry.json |
Contadores locales anonimos de uso |
~/.config/yap/index/bm25_index.json |
Indice RAG, reconstruido cuando cambia el hash del corpus |
os.remove y shutil.rmtree no aparecen en el codigo fuente. Las escrituras
son atomicas (os.replace sobre un .tmp), asi que un corte de energia no
deja un JSON truncado. La telemetria es anonima, local y desactivable:
telemetria off y telemetria borrar.
5.7 Acciones bloqueadas por diseno
- Ejecucion de comandos arbitrarios del sistema.
- Operaciones de red fuera de la whitelist.
- Instalacion o eliminacion de software.
- Modificacion de archivos del sistema.
- Lectura arbitraria del disco.
5.8 Auditoria
Los hallazgos de la revision de seguridad estan en
docs/SECURITY-AUDIT.md, y el fuzzing de entrada
(path traversal, esquemas, unicode, JSON corrupto, inputs grandes) vive en
tests/test_yap_security_audit.py (27 pruebas).
6. Instalacion
6.1 Requisitos del sistema
- SO: Debian 13 (o derivada) 64-bit.
- RAM: 8 GB (main), 6 GB (
lowmem), 3-4 GB (ultra-lowmem). - Disco: 5 GB libres, de los cuales el modelo ocupa 0.81 GB (1B) o 2.0 GB (3B).
- Red: solo durante la instalacion, para clonar y descargar el modelo. Despues, Yap funciona 100% offline.
- Para compilar:
build-essentialycmake(no hacen falta si instalas el.deb, que traellama-cliprecompilado).
6.2 Paquete .deb (recomendado en aulas)
sudo apt install ./yap_1.0.1_amd64.deb sudo apt install ./yap-models-1b_1.0.1_all.deb # ~2 GB RAM # sudo apt install ./yap-models-3b_1.0.1_all.deb # ~3.5 GB RAM
Los tres paquetes:
| Paquete | Contenido |
|---|---|
yap |
El agente, llama-cli precompilado, whitelists, AppArmor, symlink |
yap-models-1b |
Llama 3.2 1B Q4_K_M (0.81 GB) — para equipos de 3-4 GB |
yap-models-3b |
Llama 3.2 3B Q4_K_M (2.0 GB) — para equipos de 8 GB |
No requiere setup.sh ni build-essential. Como construir los paquetes, montar
un repositorio apt o generar una ISO offline: docs/PACKAGING.md.
6.3 Procedimiento de desarrollo (setup.sh)
git clone https://github.com/ChincoLinux/Yap.git cd Yap # Opcional: seleccionar rama antes de instalar # git checkout lowmem # 6 GB RAM # git checkout ultra-lowmem # 3-4 GB RAM bash setup.sh
Nota sobre ramas: Al clonar, git descarga todas las ramas remotas pero solo hace checkout de
main. Para cambiar alowmemoultra-lowmem, simplementegit checkout lowmem— git creará la rama local automáticamente. Si usastegit clone --depth 1(clon superficial), primero ejecutagit fetch --allpara descargar las demás ramas antes de hacer checkout.
El instalador realiza automaticamente:
- Instalacion de dependencias del sistema (
build-essential,cmake,python3,libnotify,libcurl). - Compilacion de llama.cpp desde fuente con enlace estatico.
- Descarga del modelo — Lee
MODEL_PATHdeyap.pyy descarga el.ggufcorrespondiente (3B o 1B). - Instalacion del agente Yap y whitelists en
/etc/yap/. - Instalacion de la configuracion de PSeInt y de los cursos en
/etc/yap/. - Instalacion de aplicaciones sugeridas (LibreOffice, Firefox, Evince, Micro, Htop).
- Enlace simbolico
/usr/local/bin/yapy activacion de.githooks/post-checkout. - Verificacion de componentes.
6.4 Despliegue en un laboratorio
./deploy-yap.sh --user alumno --parallel 4 # por SSH a varios equipos ./deploy-yap.sh --user alumno --dry-run # ver que haria, sin ejecutar ./deploy-yap.sh --mirror /srv/mirror/Yap --branch main # desde un mirror NFS/HTTP
Opciones: --mirror, --branch, --whitelist, --user, --dry-run,
--parallel. Guia completa, incluidos los permisos sudoers del administrador y
el checklist de despliegue: docs/DEPLOY.md.
6.5 Actualizacion
cd ~/Yap git pull # El enlace simbolico en /usr/local/bin/yap apunta al repositorio; # no es necesario reinstalar.
Cambiar de modelo (por ejemplo, de ultra-lowmem a main) si requiere
descargar el .gguf que falta. La tabla de cuando hace falta re-ejecutar
setup.sh esta en la seccion 7.3.
7. Ramas de configuracion
El proyecto mantiene tres ramas con distintos perfiles de consumo de RAM y calidad de respuesta:
| Rama | Modelo | Contexto | KV Cache | RAM total | Ideal para |
|---|---|---|---|---|---|
| main | 3B Q4_K_M (2.0 GB) | 4096 | FP16 | ~3.5 GB | 8 GB+ RAM |
| lowmem | 3B Q4_K_M (2.0 GB) | 2048 | Q8_0 | ~3.1 GB | 6 GB RAM |
| ultra-lowmem | 1B Q4_K_M (0.81 GB) | 2048 | Q8_0 | ~1.8 GB | 3-4 GB RAM |
La tabla describe el perfil objetivo de cada rama. El valor efectivo es el
MAX_CTXdelyap.pyque tienes en el disco, y el CI lo verifica conbranch-check. Enmainen este momentoMAX_CTX = 2048.
Techo del modelo: 3B. _modelo_local_path() descarta cualquier
YAP_MODEL_PATH que apunte a un 8B y cae al 3B; el 1B sigue permitido para
ultra-lowmem.
Super Yap (opt-in, issue #91) consulta Gradio /chat en Cloud Run si el Yap local (1B/3B) tarda 3 minutos o se pasa de tokens, sin perder el historial. No hay modelo 8B local. super on / super off cambian el motor. Ver docs/SUPER-YAP.md.
7.1 Cambio entre ramas
cd ~/Yap git checkout main # maxima calidad git checkout lowmem # balanceado git checkout ultra-lowmem # minima RAM
El enlace simbolico en /usr/local/bin/yap apunta al repositorio: el cambio es inmediato.
7.2 Hook post-checkout — informacion automatica al cambiar de rama
Al hacer git checkout <rama>, un hook de git (.githooks/post-checkout) se ejecuta automaticamente y muestra:
- Rama anterior y modelo que usaba.
- Rama actual y modelo que usara.
- Estado del modelo: si ya existe en disco o si falta descargar.
- Modelos inactivos: se listan pero no se eliminan — disponibles para cuando vuelvas a esa rama.
- Siguiente paso: si ejecutar
setup.sho si ya esta listo para usar.
El hook se activa al instalar con setup.sh (configura git config core.hooksPath .githooks).
7.3 Gestion de modelos
setup.sh se ejecuta una sola vez al instalar. Detecta automaticamente la rama actual y descarga el modelo que corresponda:
| Cambio | Requiere re-ejecutar setup.sh |
Accion |
|---|---|---|
| main ↔ lowmem | No | Solo git checkout (mismo modelo 3B) |
| ultra-lowmem → main/lowmem | Solo para descargar modelo 3B | git checkout + sudo wget <URL del 3B> o re-ejecutar setup.sh |
| main/lowmem → ultra-lowmem | Solo para descargar modelo 1B | git checkout + sudo wget <URL del 1B> o re-ejecutar setup.sh |
Re-ejecutar setup.sh despues de cambiar de rama es seguro: detecta lo que ya existe y solo descarga lo faltante.
7.4 Optimizaciones aplicadas por rama
| Optimizacion | main | lowmem | ultra-lowmem |
|---|---|---|---|
Contexto (--ctx-size) |
4096 | 2048 | 2048 |
KV cache (--cache-type-k/v) |
FP16 | Q8_0 | Q8_0 |
Flash Attention (--flash-attn) |
No | Si | Si |
Hilos (--threads) |
4 | 2 | 2 |
| Ahorro RAM | — | ~400 MB | ~1.7 GB |
Para una referencia rapida de todos los comandos, ejemplos y solucion de problemas, ver USAGE.md.
8. Uso
8.1 Modo interactivo (TUI)
yap
Abre la TUI interactiva nativa (curses, 0 dependencias externas): pantalla dividida con salida arriba, entrada abajo, prompt Chinco > , historial con flechas, scroll con RePág/AvPág y Tab para completar comandos.
Si la terminal no soporta curses, cae en el REPL clasico de texto.
Chinco > Abre LibreOffice
Chinco > busca variable en programacion
Chinco > salir
La primera vez tras instalar se muestra el onboarding: que es Yap, como
pedir una app, como llegar a los cursos, y se pide el nombre del estudiante.
Se repite con yap --tutorial. En cualquier momento, escribir un numero (1-15)
muestra el menu de opciones.
8.2 Modo comando directo
yap Abre LibreOffice
yap Busca https://es.wikipedia.org/wiki/Linux
yap Busca que es una particion de disco
yap Que es Debian?8.3 Acciones soportadas
| Accion | Ejemplo | Descripcion |
|---|---|---|
| Abrir aplicacion | yap Abre LibreOffice |
Abre la app si esta en whitelist (soporta multi-binario) |
| Webfetch + resumen | yap Busca https://es.wikipedia.org/wiki/Linux |
Obtiene contenido del sitio, lo limpia de HTML y lo envia al LLM para resumir |
| Busqueda Wikipedia | yap Busca que es Linux |
Consulta la API REST de Wikipedia, extrae contenido y resume con el LLM; muestra la fuente |
| Consulta LLM | yap Que es Debian? |
Responde con el modelo local. Mas rapido pero sin fuente verificable |
| Menu | yap menu |
Vuelve a mostrar las 15 opciones numeradas |
| Ayuda | yap ayuda |
Lista de comandos con descripcion |
| Guia rapida | yap guia |
Tutorial interactivo de 7 pasos por todas las funciones |
| Tutor PSeInt | yap pseint como hago un ciclo mientras |
Guias paso a paso, sin historial, contexto reducido para ahorrar RAM |
| Tutorial PSeInt | yap aprender pseint |
Tutorial interactivo: abre el PDF, lanza PSeInt, guia paso a paso |
| Ejercicios | yap ejercicios / yap ejercicios lista |
Practica evaluada: 4 tipos, pistas progresivas, validacion exacta o LLM |
| Ver curso | yap curso FPY1101 |
Plan completo: RAs, EAs, horas, ponderaciones |
| Iniciar EA | yap iniciar EA1 |
Sesion guiada por una experiencia de aprendizaje |
| Progreso | yap progreso / yap mi progreso |
Avance, promedio, reprobadas y nota final |
| Perfil | yap perfil |
Nombre, nivel e idioma. perfil nivel avanzado, perfil nombre Ana |
| Accesibilidad | yap accesibilidad |
Estado. accesibilidad alto_contraste on, accesibilidad fuentes on, accesibilidad lector on, accesibilidad teclado on |
| Sesion | yap sesion |
Estado. sesion nueva, sesion pausar, sesion retomar 3, sesion cerrar, sesion listar |
| Historial | yap historial |
Sesiones anteriores. historial --ultimo (o retomar) reanuda la ultima |
| Telemetria | yap telemetria |
Resumen local de uso. telemetria exportar, telemetria off, telemetria borrar |
| RAG | yap rag |
Estado del indice. rag rebuild, rag buscar particiones |
| Super Yap | yap super |
Estado de la nube. super on, super off, super <pregunta> |
| AppArmor | yap --apparmor-status |
Si el perfil esta cargado y en enforce o complain |
Lista de contactos rapidos y ejemplos: USAGE.md.
8.4 Ordenes que no pasan por el LLM
interpret() resuelve por teclado todo lo que el menu anuncia, sin consultar al
clasificador. Con el modelo 1B el clasificador acierta poco, y estas son
justamente las ordenes que el estudiante mas usa:
| Escribe | Resuelve |
|---|---|
abre firefox / abrir firefox |
Abre una app de la whitelist |
busca que es un algoritmo / buscar ... |
Wikipedia + resumen |
pseint como hago un ciclo / tutor pseint ... |
Tutor paso a paso |
aprender pseint |
Tutorial interactivo |
curso FPY1101 |
Plan de estudio |
iniciar EA1 |
Sesion de la EA |
ejercicios [lista | <id>] |
Catalogo de ejercicios |
1 … 15 |
Opcion del menu; si necesita datos, muestra un ejemplo de uso |
guia, ayuda, menu, salir |
Navegacion |
El texto posterior a la orden se respeta tal cual, con sus mayusculas:
busca Linus Torvalds busca exactamente eso. Si escribes solo abre sin nada
mas, Yap se lo pasa al modelo para que interprete la intencion.
8.5 Clasificacion de intenciones
Cuando ninguna ruta directa aplica, classify_intent() consulta al LLM con
temperatura 0.1 y recibe ACCION|PARAMETRO. Eso aporta:
- Tolerancia a errores ortograficos: "Abre", "abre" y "abrir" se clasifican
como
open_app. - Flexibilidad sintactica: "busca sobre Linux" y "que es Linux" se distinguen.
- Modo tutor PSeInt: preguntas de programacion responden con guias paso a paso, sin historial de conversacion.
- Historial de conversacion: hasta 6 turnos (
MAX_HISTORY) en modo interactivo. - Fallback: si el LLM no responde o expira, la consulta se degrada a
queryen vez de fallar.
Las acciones del clasificador son open_app, search, webfetch, query y
pseint.
8.6 Flujo del tutorial PSeInt
Al ejecutar yap quiero aprender pseint o seleccionar "Introduccion PSeInt" desde el menu de ayuda, el sistema ejecuta el siguiente flujo:
- Carga de ejercicios:
cargar_ejercicios()lee/etc/yap/pseint/ejercicios.conf(bloques v2[id]y lineas v1Titulo:Descripcion|GuiaSolucion). - Apertura del PDF: El sistema abre el archivo
guia_ejercicios.pdf(pre-generado, instalado porsetup.shen/etc/yap/pseint/) que contiene los ejercicios y sus guias de resolucion paso a paso con formato profesional. - Apertura de PSeInt:
cmd_open_app("pseint")lanza el entorno PSeInt desde la whitelist. - Presentacion paso a paso: El tutorial muestra cada paso de la guia uno por uno. El estudiante presiona Enter para avanzar.
- Bucle de asistencia: En cualquier momento, el estudiante puede escribir una pregunta.
cmd_pseint()recibe el contexto completo: titulo del ejercicio, descripcion, guia de resolucion completa (todos los pasos) y el paso actual. Asi la IA responde con precision sobre exactamente donde esta atascado el estudiante. - Comandos:
ayuda(pista),siguiente(siguiente ejercicio),salir(terminar).
8.6.1 Practica evaluada (yap ejercicios)
Modo distinto al tutorial: el estudiante escribe la respuesta y Yap la evalua. La solucion permanece oculta.
yap ejercicios lista # catalogo con estado yap ejercicios # pendientes, con validacion yap ejercicios hola_mundo # un ejercicio Chinco > ejercicios
Cada ejercicio v2 en /etc/yap/pseint/ejercicios.conf declara:
| Campo | Uso |
|---|---|
tipo |
codigo_pseint, respuesta_libre (alias respuesta_texto), opcion_multiple, completar |
enunciado |
Consigna visible |
pista1 pista2 pista3 |
Conceptual, parcial, casi solucion |
solucion / criterio |
Referencia oculta y criterios |
validacion |
exacta (sin LLM) o llm |
opcion_multipleycompletar(consolucion) se comparan de forma exacta, sin LLM.codigo_pseintyrespuesta_libreusan el evaluador LLM (evaluar_actividad/evaluar_ejercicio).- Si la respuesta es incorrecta se ofrece
pista(3 niveles). Hasta 3 intentos (YAP_MAX_INTENTOS). - El progreso se guarda en
progress.jsonbajoejercicios.{id}. - Una actividad de EA puede apuntar al catalogo con
"ejercicio_id": "hola_mundo".
Las lineas v1 Titulo:Descripcion|GuiaSolucion siguen cargando para el tutorial; no son evaluables en yap ejercicios.
8.7 Sistema de Cursos
Yap incluye un sistema de cursos configurable. El primer curso implementado es FPY1101 Fundamentos de Programacion (126 horas, 18 semanas), basado en el PIA y PDA institucional.
8.7.1 Iniciar un curso
yap curso FPY1101 # Plan completo con RAs, EAs y evaluaciones Chinco > curso FPY1101 # Desde el modo interactivo
Aparece una pantalla con:
- Datos del curso: horas, semanas, herramientas
- 4 Resultados de Aprendizaje (RAs): RA1 (algoritmos), RA2 (programacion Python), RA3 (estructuras de datos), RA4 (funciones)
- 3 Experiencias de Aprendizaje (EAs): EA1 (algoritmos con PSeInt, 35h, 35%), EA2 (Python, 49h, 60%), EA3 (colecciones y funciones, 35h, 25%)
- Evaluacion Final Transversal (EFT): 7 horas, 40% de la nota final
8.7.2 Iniciar una Experiencia de Aprendizaje
yap iniciar EA1 # Sesion guiada de Fundamentos de Algoritmos yap iniciar EA2 # Sesion guiada de Programacion con Python yap iniciar EA3 # Sesion guiada de Colecciones y Funciones
Cada EA muestra:
- Actividades numeradas con descripcion detallada (12 en total, del PDA oficial)
- Estado de progreso: ✓ completado, · pendiente
- Herramientas sugeridas para cada actividad
- Evaluaciones formativas y parciales con sus ponderaciones
8.7.3 Flujo de una sesion EA
- Inicias con
yap iniciar EA1 - El sistema muestra la actividad actual con consignas, criterios y (si aplica) opciones
- Si la actividad tiene
tipode evaluacion, escribes tu respuesta. Yap la evalua:respuesta_libre/completar/codigo_pseint→ el LLM local devuelve JSON con puntaje y feedbackopcion_multiple→ comparacion exacta, sin LLM
- Hasta 3 intentos por actividad. Si repruebas puedes
saltar.pregunta ...consulta al tutor sin gastar intento - abrir [app] → lanzar herramienta (PSeInt, VS Code, Terminal, Navegador)
- salir → guardar progreso y salir
- Al completar la EA se calcula promedio 0-100 y nota final 1.0-7.0 (escala chilena, 60% = 4.0)
- Actividades sin
tipoconservan el flujo anterior: Enter marca como hecha
La evaluacion ocurre dentro de la sesion activa: el contexto del curso, la EA y la conversacion reciente se envian al LLM para un feedback mas contextual.
8.7.4 Progreso y persistencia
El progreso se guarda automaticamente en ~/.config/yap/progress.json. Cada intento guarda puntaje, intentos y fecha_aprobacion. Si el sistema se apaga, al volver retomas donde quedaste.
yap mi progreso # % completado, promedio, reprobadas y nota8.7.5 Agregar mas cursos
Los cursos se definen en archivos JSON en cursos/. Para agregar uno nuevo:
- Crea
cursos/MAT1101.jsoncon la estructura de PIA/PDA setup.shlo instala automaticamente en/etc/yap/cursos/yap curso MAT1101ya funciona sin modificar codigo
Estructura minima del JSON:
{
"codigo": "MAT1101",
"nombre": "Matematicas",
"horas": 90,
"semanas": 18,
"ras": [{"id": "RA1", "descripcion": "...", "indicadores": ["IL1.1"]}],
"eas": [{"id": "EA1", "nombre": "...", "descripcion": "...", "horas": 30,
"actividades": [{"orden": 1, "nombre": "Act1", "descripcion": "...",
"tipo": "respuesta_libre", "criterios_evaluacion": ["..."]}],
"evaluaciones": []}],
"evaluaciones": []
}8.8 Guia rapida integrada
Escribe yap guia para un tutorial interactivo de 7 pasos que recorre todas
las funciones. yap --tutorial repite el onboarding de bienvenida.
Chinco > guia
┌─ PASO 1: Bienvenida a ChincoLinux ─────────────────────┐
│ Escribe 'yap' para entrar al modo interactivo. │
│ El prompt 'Chinco > ' con colores te indica que estas │
│ dentro. Todos los comandos funcionan igual aqui. │
└─────────────────────────────────────────────────────────┘
┌─ PASO 2: Abrir herramientas ───────────────────────────┐
│ 'Abre Firefox' — lanza apps de la whitelist. │
│ 'abrir pseint' — desde una sesion EA abre herramientas │
└─────────────────────────────────────────────────────────┘
... (Enter para continuar, Ctrl+C o 'salir' para terminar)
8.9 Perfil del estudiante (#24)
yap perfil # ver yap perfil nombre Ana # cambia el nombre yap perfil nivel avanzado # basico | intermedio | avanzado yap perfil idioma es # es | en
El perfil vive en ~/.config/yap/profile.json y se inyecta al system prompt, de
modo que el agente conoce el nombre y el nivel del estudiante. Tambien guarda
preferencias (idioma, tema, feedback detallado, notificaciones, opciones de
accesibilidad), el flag de onboarding completado y estadisticas de uso. Las
escrituras son atomicas.
El nivel inicial de la dificultad adaptativa se deriva del perfil.
8.10 Accesibilidad (#37)
yap accesibilidad # estado de las cuatro opciones yap accesibilidad alto_contraste on # paleta de alto contraste yap accesibilidad fuentes on # escala de texto 2x yap accesibilidad lector on # sanitiza ANSI para lector de pantalla yap accesibilidad teclado on # navegacion completa por teclado
- Alto contraste: replaces la paleta ANSI por una version legible.
- Fuentes grandes: duplica el ancho de celda al dibujar cajas y menus.
- Lector de pantalla: detecta Orca por variables de entorno y filtra las
secuencias ANSI con un
_FiltroSalidasobrestdout, para que el estudiante oiga texto limpio en vez de secuencias de escape. - Teclado:
menu_interactivo()permite recorrer opciones con flechas,Home/EndyEnter, sin raton.
8.11 Sesiones e historial (#21, #13)
yap sesion # estado de la sesion activa yap sesion nueva # abrir una sesion nueva yap sesion listar # sesiones abiertas y pausadas yap sesion pausar # guardar el contexto y seguir despues yap sesion retomar 3 # retomar la sesion 3 yap sesion cerrar # archivar y limpiar yap sesion asociar FPY1101 EA1 # asociar a un curso y una EA yap historial # ultimas 20 sesiones yap historial --ultimo # (o `retomar`) reanuda la ultima
Cada sesion guarda contexto, curso, EA, historial de turnos y timestamps. Al
cerrar se archiva en history.json, y al salir de la TUI la sesion abierta se
pausa sola. Se pueden tener YAP_MAX_SESSIONS (3 por defecto) sesiones abiertas
a la vez.
8.12 Telemetria local (#38)
yap telemetria # resumen: que usas y que no yap telemetria exportar # vuelca el JSON a la terminal yap telemetria off # deja de recolectar yap telemetria on yap telemetria borrar # elimina el archivo local
Contadores anonimos de uso en ~/.config/yap/telemetry.json: cuantas veces se
llamo a cada accion, y cuales comandos nunca se han usado. Sin identificador
de usuario, sin red, sin reloj de pared: el archivo se puede leer y borrar por
el estudiante en cualquier momento.
8.13 RAG local (#118)
Recuperacion augmentada offline con Okapi BM25, 100% stdlib. Antes de responder, Yap recupera los fragmentos mas relevantes del corpus y los injecta como contexto.
yap rag # activo, ruta del indice, nº de fragmentos y archivos yap rag rebuild # reconstruir el indice y medir la latencia yap rag buscar particiones # busqueda directa, muestra los puntajes BM25
| Corpus | cursos/*.json, docs/*.md, whitelist/*.conf, USAGE.md, AGENTS.md |
| Chunking | por encabezado markdown o doble salto de linea, 300 palabras por fragmento |
| Indice | ~/.config/yap/index/bm25_index.json, revalidado por hash del corpus |
RAG_TOP_K |
5 fragmentos recuperados (default) |
RAG_MAX_CONTEXT_TOKENS |
512 tokens de contexto inyectado (default) |
| Degradacion | si el indice falla, Yap responde igual sin contexto RAG |
Detalle de diseno y tuneles: docs/RAG.md.
8.14 Super Yap — Gradio en Cloud Run (#91)
Cliente Gradio 5 en Cloud Run escrito con urllib y http.cookiejar (sin
requests, sin socket). Opt-in: el estudiante sigue trabajando con el modelo
local de 1B/3B, y la nube entra solo cuando el local no da la talla.
yap super # estado: motor actual, endpoint, si responde yap super on # forzar la nube en todas las consultas yap super off # volver al local yap super <pregunta> # forzar una consulta puntual
- Delegacion automatica: si
llama-clitarda 3 minutos o el prompt excede los tokens del contexto local, Yap consulta Gradio reenviando el historial vivo (HISTORY), asi que no se pierde el hilo. - Fallback: si la nube no responde, se vuelve al LLM local.
- Whitelist de hosts: solo loopback, LAN privada o el host pin de Cloud Run. Se rechaza cualquier otro destino.
- Plantillas de despliegue en
agent-platform/.
Ver docs/SUPER-YAP.md.
8.15 Dificultad adaptativa (#30)
AdaptiveEngine observa el rendimiento (puntajes, intentos, pistas usadas) y
propone subir o bajar el nivel del estudiante. Las reglas de subida exigen
varias respuestas correctas consecutivas; las de bajada, varios fallos; entre
ambas hay mantenimiento, para que el nivel no oscile. El mapeo entre nivel del
perfil y nivel de dificultad, y las variantes por tipo de actividad, se fijan
en tests/test_yap_adaptive.py (45 pruebas).
9. Limitaciones
- Contexto limitado:
MAX_CTX = 2048tokens (~1500 palabras). El RAG recupera hasta 512 tokens extra, pero el modelo no razona sobre mas. - Persistencia por usuario, no por equipo: el progreso, el perfil, las
sesiones y la telemetria viven en
~/.config/yap/del usuario actual. No hay sincronizacion entre maquinas. - Latencia: en CPU con 2 nucleos la primera respuesta puede tardar hasta 60 s. El LLM local es el cuello de botella de todo lo que pasa por el.
- Alucinaciones: Llama 3.2 3B puede generar informacion incorrecta. Para
datos factuales conviene
webfetchobusca(Wikipedia), que muestran fuente. - El evaluador tambien alucina: la nota de
respuesta_libreycodigo_pseintla pone el LLM. Por esoopcion_multipleycompletarse comparan de forma exacta, sin LLM, y por eso existe la nota seca cuando el LLM no responde. - Clasificador debil en 1B: con el modelo de
ultra-lowmemel clasificador de intenciones acierta poco. De ahi las rutas por teclado de la seccion 8.4. - Idioma: interfaz y LLM siguen el perfil (
yap perfil idioma es|en|arn); el clasificador interno sigue en español. es inconsistente. - Sin GPU ni aceleracion hardware: CPU-only por diseno, para que quepa en el hardware del aula.
- Super Yap es una excepcion de red: aunque es opt-in, si se activa sale a Internet. El resto de Yap es 100% offline.
10. Trabajo futuro
Fase 1 — MVP (completada)
- LLM local (Llama 3.2 Instruct).
- CLI interactiva (TUI curses) y por comando directo.
- Tooling de sistema con whitelist.
- Alertas graficas (
notify-send). - Whitelist configurable de apps y dominios.
- Busqueda en Wikipedia y webfetch con limite de 3000 chars.
- Onboarding de bienvenida.
Fase 2 — Optimizacion y seguridad (completada)
- Compilacion estatica de llama.cpp (sin dependencia de
libllama.so). - Correccion de seguridad en whitelist de dominios (
notwikipedia.org). - Soporte multi-binario en whitelist de apps (fallback
firefox-esr→firefox). - Desactivacion de modo conversacion en llama-cli (
-no-cnv,--no-display-prompt). - Aislamiento de la terminal:
stdin=DEVNULLen toda llamada asubprocess(#99). - Confirmacion humana para acciones sensibles (#12).
- Integracion con AppArmor (#14).
- Fuzzing de entrada: traversal, esquemas, unicode, JSON corrupto, inputs grandes.
- Instalador
.debcon repositorio apt opcional (#31). - Despliegue masivo en red escolar por SSH.
Fase 3 — Entorno pedagogico (completada)
- Sistema de cursos con RAs, EAs y actividades tipadas (
cursos/*.json). - Evaluacion automatica de actividades con feedback del LLM (#23).
- Catalogo de ejercicios PSeInt con 4 tipos y pistas progresivas (#27).
- Tutor y tutorial interactivo de PSeInt.
- Feedback pedagogico con nota chilena 1.0-7.0 (#29).
- Perfil del estudiante inyectado al system prompt (#24).
- Historial persistente entre sesiones (#13).
- Sesiones pausables, retomables y archivables (#21).
- Dificultad adaptativa segun rendimiento (#30).
- Accesibilidad: alto contraste, fuentes grandes, lector de pantalla, teclado (#37).
- Telemetria local anonima, exportable y desactivable (#38).
- RAG local con BM25, sin dependencias (#118).
- Rutas por teclado para las ordenes del menu (#59).
- Super Yap en Cloud Run con reenvio de historial (#91).
Fase 4 — En desarrollo
- Mas fuentes en la whitelist educativa.
- Interfaz de configuracion grafica.
- Exportar el progreso del estudiante para el docente.
- Catalogo de ejercicios mas amplio, authored por el curso.
Vision a largo plazo
- Plugins de tooling extensibles.
- Integracion con gestores de coursework externos.
- Cuantizacion mas agresiva para equipos de 2 GB.
- Evaluacion entre pares: que un estudiante evalue la respuesta de otro.
El detalle de fases, dependencias y metricas esta en docs/ROADMAP.md.
11. Licencia
Este proyecto se distribuye bajo licencia MIT (ver LICENSE). El modelo Llama 3.2 esta sujeto a los terminos de la Licencia Llama 3.2 de Meta.
12. Pruebas y verificacion
La suite tiene 30 archivos y 1079 pruebas. La unica dependencia externa es
pytest: ni el modelo, ni la GPU, ni Internet. Detalle por archivo y por
requisito en tests/README.md.
12.1 Como ejecutar
python3 -m pip install pytest # unica dependencia python3 -m pytest tests/ -v # 1079 pruebas (desarrollo y CI) python3 -m pytest tests/test_yap_rag.py -v # un area python3 -m pytest tests/ -v -k whitelist # por palabra clave python3 tests/run_tests.py # runner: etapas + chequeos + requisitos python3 tests/run_tests.py --report # runner + tests/report/report_<fecha>.txt
12.2 Los dos ejecutores, y cuando usar cada uno
pytest tests/ |
python3 tests/run_tests.py |
|
|---|---|---|
| Archivos | los 30 | los 6 de ARCHIVOS_PYTEST |
| Pruebas | 1075 | 307 |
| Chequeos de infraestructura | no | si (5) |
Chequeo estatico de yap.py |
no | si (5) |
| Mapeo de 26 requisitos | no | si |
| Reporte TXT | no | con --report |
| Para que | desarrollo, CI | auditoria de entrega |
Los numeros no cuadran al decimal porque el parser del runner cuenta lineas con
PASSED y FAILED: un test SKIPPED no suma. Hay 24 skips en los seis
archivos del runner (283 contados, 307 recogidos) y 24 en la suite completa
(1055 contados, 1079 recogidos).
run_tests.py ejecuta 5 etapas en orden: pytest sobre los archivos de
ARCHIVOS_PYTEST, verificaciones de infraestructura, verificacion estatica del
codigo fuente, mapeo de requisitos y resumen.
Requisitos de run_tests.py
- Python 3.12+, stdlib-only. La unica dependencia externa es
pytest. - No necesita el modelo GGUF,
llama-cli, GPU ni Internet: la suite mockeasubprocess,urllib.requestyshutil.which. - No necesita haber instalado Yap. Las 5 verificaciones de
infraestructura —symlink
/usr/local/bin/yap,llama-clien el PATH, el modelo enMODEL_PATH,/etc/yap/whitelist/apps.confyweb.conf— miden la instalacion, no el codigo, asi que fallan por diseño en CI o en un portatil donde no se instalo nada. - Timeout de 120 s por archivo de pytest: el techo de la primera etapa son 10 minutos. El resto es instantaneo.
--vmes cosmetico: cambia la etiqueta del encabezado a "VM (con LLM)". Las verificaciones de infraestructura se ejecutan siempre, con o sin la opcion;--vmno las filtra.- Codigo de salida:
0si no falla ninguna prueba ni chequeo estatico,1si falla alguna. El mapeo de requisitos no altera el codigo de salida: en un host sin Yap instaladoCFG-01..03quedan en rojo sin que eso invalide un PR de codigo. - Aislamiento de la nube:
tests/conftest.pyfijaYAP_SUPER_ENABLED=0yYAP_SUPER_INTERNET=0en una fixtureautouse, salvo que la variable ya este definida en el entorno. Ningun test habla con Cloud Run.
Los 26 requisitos catalogados
REQUISITOS en tests/run_tests.py es la fuente unica de verdad: ahi esta la
descripcion de cada ID, y main() solo aporta el estado. Si un ID queda en un
lado y no en el otro, el runner lo avisa.
| Familia | IDs | Significado |
|---|---|---|
| SEG | 01-08 | Whitelist, anti-inyeccion, limites de recursos |
| FUN | 01-12 | Capacidades del agente |
| SEC | 01 | Catalogo de ejercicios PSeInt |
| CFG | 01-03 | Instalacion en el sistema |
| PKG | 01-02 | Empaquetado .deb (#31) |
La tabla completa ID ↔ archivo ↔ clase de prueba esta en tests/README.md.
12.3 Integracion continua (GitHub Actions)
Once workflows en .github/workflows/. El principal, test.yml, corre en cada
push y pull request a main, lowmem o ultra-lowmem:
| Job | Que hace |
|---|---|
| unit-tests | 1079 pruebas en Python 3.12 (seguridad, funcional, i18n, empaquetado, desktop, post-install) + verificacion estatica (shell=True, eval(), os.system()) + validacion de whitelist |
| branch-check | Verifica que MODEL_PATH en cada rama apunte al modelo correcto |
| results | Resumen del pipeline |
Los demas workflows cubren el ciclo de vida del PR y del repo:
| Workflow | Que hace |
|---|---|
pr-review.yml |
Agente yap-reviewer (A-Dev) evalua las politicas Hardness y deja un comentario asesor. Nunca aprueba ni rechaza |
pr-validation.yml |
Validacion de la estructura del PR |
pr-state-labeler.yml |
Etiqueta el estado del PR (pr:needs-review, pr:changes-requested, ...) |
pr-traceability-check.yml |
Exige que el PR referencie un issue; si no, comenta y etiqueta needs-human |
pr-quality-suite.yml |
Suite de calidad extendida |
quality-gates.yml |
Puertas de calidad antes del merge |
build-deb.yml |
Construye los paquetes .deb |
auto-release.yml |
Version bump y CHANGELOG desde un PR a main, nunca push directo |
weekly-sprint-assignment.yml |
Asignacion semanal de issues |
auto-add-to-project.yml |
Anade issues al project del roadmap |
Reglas de rama que el CI y la doctrina A-Dev hacen cumplir:
mainesta protegida:enforce_admins: true, sin push directo, sin force push.- Cada cambio va en una rama fresca con nombre convencional
(
feat/issue-NNN-...,fix/issue-NNN-...). - Commits con Conventional Commits.
- Todo PR referencia su issue con
Closes #NNN(una linea por issue). - La suite debe estar verde antes de pedir revision.
Pipeline definido en .github/workflows/test.yml. La revisión de calidad de cada
PR (ruff, pyflakes) vive en .github/workflows/pr-quality-suite.yml y el
checklist completo en docs/REVIEW.md.
12.4 Pruebas de seguridad (test_yap_security.py, 25)
| Clase | Prueba | Verifica |
|---|---|---|
| TestAppWhitelist | test_app_permitida_devuelve_ok |
App en whitelist se carga correctamente |
test_app_bloqueada_muestra_alternativas |
App bloqueada → [ERROR] + lista de apps permitidas |
|
test_app_bloqueada_no_ejecuta_comando |
App bloqueada → subprocess.Popen NO se llama |
|
test_multiples_binarios_fallback |
firefox-esr,firefox → lista de 2 binarios |
|
| TestDomainWhitelist | test_dominio_permitido_exacto |
wikipedia.org en whitelist → pasa |
test_subdominio_permitido |
es.wikipedia.org → pasa (subdominio directo) |
|
test_dominio_bloqueado_muestra_alternativas |
malware.com → [ERROR] + dominios permitidos |
|
test_notwikipedia_no_coincide |
notwikipedia.org no hace match con wikipedia.org |
|
| TestCommandSecurity | test_no_shell_true_en_subprocess |
Escanea codigo: shell=True NO aparece |
test_no_eval |
Escanea codigo: eval() NO aparece |
|
test_no_os_system |
Escanea codigo: os.system() NO aparece |
|
test_command_injection_app_name |
"; rm -rf /", $(whoami), `id`, && shutdown → bloqueados |
|
test_url_injection |
file:///etc/passwd, 127.0.0.1, [::1], javascript: → bloqueados |
|
| TestConfigLoading | test_whitelist_ignora_comentarios |
Lineas con # se ignoran |
test_whitelist_ignora_lineas_vacias |
Lineas vacias se ignoran | |
test_formato_invalido_ignorado |
Lineas sin : se ignoran |
|
| TestSecurityLimits | test_contenido_limitado_3000_chars |
text[:3000] existe en cmd_webfetch |
test_timeout_en_subprocess |
Toda llamada a subprocess.run() tiene timeout= |
|
| TestFileSystemSecurity | test_no_escritura_fuera_de_whitelist |
Sin open(w), os.remove, shutil.rmtree en codigo |
| TestRealConfig | test_apps_conf_existe |
whitelist/apps.conf existe en el repo |
test_web_conf_existe |
whitelist/web.conf existe en el repo |
|
test_apps_conf_tiene_contenido |
apps.conf tiene entradas validas | |
test_web_conf_tiene_contenido |
web.conf tiene dominios validos | |
| TestCodeQuality | test_no_shebang_incorrecto |
#!/usr/bin/env python3 correcto |
test_imports_minimos |
Sin imports peligrosos (socket, ctypes, pickle, base64) |
El resto de areas con su archivo y sus clases:
| Archivo | Pruebas | Clases principales |
|---|---|---|
test_yap_security_audit.py |
27 | TestPathTraversal, TestSchemeValidation, TestAppInjectionFuzz, TestLargeInputFuzz, TestUnicodeFuzz, TestCorruptJsonFuzz |
test_yap_functional.py |
59 | TestOpenApp, TestWebfetch, TestIntentClassification, TestQuery, TestHistory, TestNotifications, TestPSeIntConfig, TestIntroduccionPSeInt, TestPSeIntTutor, TestArchitecture, TestChincoTUI, TestCourseSystem, TestProgreso, TestCursoCommand |
test_yap_evaluacion.py |
63 | TestSchemaEvaluacion, TestParserEvaluacion, TestEvaluarActividad, TestProgresoEvaluacion, TestNotaChilena, TestFlujoEvaluacion, TestCmdProgresoNotas, TestIntegracionFPY1101, TestComandosActividad |
test_yap_super.py |
58 | TestConsultaParaSuper, TestHostSuperPermitido, TestDelegacion, TestCmdQuerySuper, TestInterpretSuper, TestGradioNube, TestFallbackLocalASuper |
test_yap_deb.py |
34 | TestYapDebianTemplates, TestModelPackages, TestBuildDebScript, TestBuildDebSmoke, TestPostinstCopySemantics |
test_yap_ejercicios.py |
35 | TestParserEjercicios, TestEvaluarEjercicio, TestPistas, TestFlujoEjercicio, TestCliEjercicios, TestHookEA |
test_yap_accesibilidad.py |
90 | TestSanitizarSalida, TestDetectarOrca, TestAltoContraste, TestFuentesGrandes, TestNavegacionTeclado |
test_yap_sessions.py |
75 | TestSessionStore, TestSessionCRUD, TestSessionLimit, TestCmdSesion, TestSessionExit |
test_yap_rag.py |
61 | TestRagBM25Index, TestRagPersistence, TestRagRetrieve, TestRagPathTraversal, TestRagGoldenSet |
test_yap_perfil.py |
61 | TestNormalizacion, TestEscrituraAtomica, TestCmdPerfil, TestSystemPromptInjection |
test_yap_adaptive.py |
45 | TestReglaSubida, TestReglaBajada, TestMantenimiento, TestMapeoPerfil |
test_yap_telemetry.py |
41 | TestTelemetryStore, TestRegistrarUso, TestAccionesSinUsar, TestPrivacidad |
test_yap_feedback.py |
32 | TestTipoFeedback, TestPautasDelPrompt, TestFeedbackSumativo |
test_yap_rutas_teclado.py |
32 | TestRutasDeTeclado, TestPistas, TestMenu |
test_yap_config_escolar.py |
21 | TestAppsEducativas, TestDominiosEducativos, TestAppArmorEnforce |
test_yap_confirmation.py |
20 | TestSensitiveActions, TestConfirmAction, TestTrustedLevel |
test_yap_history.py |
17 | TestHistorySave, TestHistoryLoad, TestMaxSessions, TestCmdHistorial |
test_yap_apparmor.py |
17 | TestAppArmorStatus, TestProfileExists, TestSetupShIntegration |
test_yap_terminal.py |
9 | TestNingunProcesoHeredaLaTerminal, TestRutasDeLlamaCli |
test_yap_onboarding.py |
3 | Onboarding de bienvenida |
12.5 Mecanismo de pruebas
Todas las pruebas se ejecutan sin el modelo LLM y sin red, mediante mocking:
subprocess.run → mock (devuelve stdout/stderr predefinidos)
urllib.request → mock (devuelve HTML de prueba)
shutil.which → mock (devuelve rutas falsas)
Hay ademas pruebas que escanean el codigo fuente de yap.py en busca de
patrones prohibidos. Ojo con los falsos positivos: si un comentario o una
cadena de texto contiene shell=True, el patron aparece igual. El bot
yap-reviewer busca sobre el diff completo y por eso marca things que en
realidad son texto; verifica siempre que el match este en codigo ejecutable.
Los agregados del runner: en un host donde Yap esta instalado, las 5
verificaciones de infraestructura y los 5 chequeos estaticos de codigo pasan
todos, y el mapeo queda en 26/26. En CI, donde no hay nada instalado, las 5 de
infraestructura caen y el mapeo queda en 23/26 con CFG-01..03 en rojo: eso
esperado, y el codigo de salida sigue siendo el de las pruebas.
| Clase | Prueba | Verifica |
|---|---|---|
| TestAppWhitelist | test_app_permitida_devuelve_ok |
App en whitelist se carga correctamente |
test_app_bloqueada_muestra_alternativas |
App bloqueada → [ERROR] + lista de apps permitidas |
|
test_app_bloqueada_no_ejecuta_comando |
App bloqueada → subprocess.Popen NO se llama |
|
test_multiples_binarios_fallback |
firefox-esr,firefox → lista de 2 binarios |
|
| TestDomainWhitelist | test_dominio_permitido_exacto |
wikipedia.org en whitelist → pasa |
test_subdominio_permitido |
es.wikipedia.org → pasa (subdominio directo) |
|
test_dominio_bloqueado_muestra_alternativas |
malware.com → [ERROR] + dominios permitidos |
|
test_notwikipedia_no_coincide |
notwikipedia.org no hace match con wikipedia.org |
|
| TestCommandSecurity | test_no_shell_true_en_subprocess |
Escanea codigo: shell=True NO aparece |
test_no_eval |
Escanea codigo: eval() NO aparece |
|
test_no_os_system |
Escanea codigo: os.system() NO aparece |
|
test_command_injection_app_name |
"; rm -rf /", $(whoami), `id`, && shutdown → bloqueados |
|
test_url_injection |
file:///etc/passwd, 127.0.0.1, [::1], javascript: → bloqueados |
|
| TestConfigLoading | test_whitelist_ignora_comentarios |
Lineas con # se ignoran |
test_whitelist_ignora_lineas_vacias |
Lineas vacias se ignoran | |
test_formato_invalido_ignorado |
Lineas sin : se ignoran |
|
| TestSecurityLimits | test_contenido_limitado_3000_chars |
text[:3000] existe en cmd_webfetch |
test_timeout_en_subprocess |
Toda llamada a subprocess.run() tiene timeout= |
|
| TestFileSystemSecurity | test_no_escritura_fuera_de_whitelist |
Sin open(w), os.remove, shutil.rmtree en codigo |
| TestRealConfig | test_apps_conf_existe |
whitelist/apps.conf existe en el repo |
test_web_conf_existe |
whitelist/web.conf existe en el repo |
|
test_apps_conf_tiene_contenido |
apps.conf tiene entradas validas | |
test_web_conf_tiene_contenido |
web.conf tiene dominios validos | |
| TestCodeQuality | test_no_shebang_incorrecto |
#!/usr/bin/env python3 correcto |
test_imports_minimos |
Sin imports peligrosos (socket, ctypes, pickle, base64) |
12.6 Resultados
| Categoria | Resultado |
|---|---|
Suite completa (python3 -m pytest tests/ -q) |
1051 passed, 24 skipped (0 failed, 2026-10-11) |
Formato y lint (ruff format --check + ruff check) |
✓ limpio |
Estático (pyflakes yap.py tests/) |
✓ sin hallazgos |
CI en main |
✓ verde (Yap CI + Quality Gates) |
Los 24 tests omitidos son los de Super Yap (nube suspendida, cloud-suspended)
y los de infraestructura que sólo corren en VM (--vm).
13. Contribuir
Lee CONTRIBUTING.md y, antes de tocar nada, AGENTS.md. El flujo es trunk-based:
- Rama fresca desde
main:git checkout -b feat/issue-NNN-descripcion. Nunca reutilices una rama ya commiteada ni mergeada. - Commits atomicos con Conventional Commits:
feat:,fix:,docs:,test:,chore:. - Pruebas locales verdes antes de pedir revision:
python3 -m pytest tests/ -v. - PR que referencia su issue (
Closes #NNN, una linea por issue) y completa el checklist de PULL_REQUEST_TEMPLATE.md. - Revision: el bot
yap-reviewerevalua las politicas A-Dev y deja un comentario asesor; la decision siempre es de una persona. Ningun agente se aprueba a si mismo. - Merge por squash con auto-merge, y borrado de la rama.
Invariantes que el CI y la doctrina A-Dev hacen cumplir:
| Regla | Como se comprueba |
|---|---|
Sin push directo a main |
Branch protection con enforce_admins: true |
| Sin force push | allow_force_pushes: false |
| Cada cambio en su rama | pr-validation.yml + revision A-Dev |
| PR ligado a un issue | pr-traceability-check.yml |
| Suite verde | test.yml |
| Sin patrones peligrosos | test_yap_security.py + paso estatico del CI |
| Politicas de seguridad | .github/adev/policies/HD-YAP-*.json |
Convenciones de codigo: espanol en los strings de usuario, espanol o ingles en
los comentarios tecnicos, prefijo ponytail: en workarounds sin dependencias
externas, ANSI directo para la TUI (sin Rich ni Textual),
shutil.which() antes de todo Popen, y timeout= en toda llamada a
subprocess.
Referencias: llama.cpp | Llama 3.2 | GGUF format | Debian | VirtualBox | A-Dev | Trunk-Based Development | Conventional Commits
来源:GitHub · github.com