Download docs/PLAN_CORRECCIONES.md from CamiloS1111/ragsolver: direct link, hf CLI and curl.
- Browser
- Download file 45.2 kB
-
https://huggingface.co/CamiloS1111/ragsolver/resolve/main/docs/PLAN_CORRECCIONES.md
- Command line
-
hf download hf://CamiloS1111/ragsolver/docs/PLAN_CORRECCIONES.md
-
curl -L -o PLAN_CORRECCIONES.md https://huggingface.co/CamiloS1111/ragsolver/resolve/main/docs/PLAN_CORRECCIONES.md
Plan de corrección — hallazgos de la auditoría del 2026-09-23
Documento de ejecución para el agente implementador. Fuente de los hallazgos (IDs SEC-, VER-, BUG-, …):
AUDIT_2026-09-23.md. Fecha: 2026-09-23. Idioma de trabajo: español; código, prompts y comentarios de código siguen en el idioma que ya usa cada archivo.
0. Cómo usar este documento
- Plan por fases. Cada fase trae objetivo, hallazgos que cierra, tareas numeradas, archivos, criterios de aceptación medibles, plan B, esfuerzo y costo en US$. No se pasa a la siguiente fase sin cumplir los criterios de aceptación, salvo que la sección 7 marque que dos fases son independientes.
- Reglas de trabajo (valen para todas las fases):
- Corrección mínima en el lugar correcto. Rige la misma regla de
PLAN_RAG.md §0(sin sobreingeniería, sin abstracciones de una sola implementación). Un arreglo va en la función por la que pasan todos los llamadores, no en cada llamador. - Todo bug empieza con un test de regresión que falla; después viene el arreglo.
- Toda decisión o desvío se registra en
docs/DECISIONS.md, con el formato existente y numeración a partir de #69. - Si cambia un contrato Pydantic, se regeneran los JSON Schema con
python scripts/export_schemas.pyy se comprueba que siguen sincronizados. - Los resultados ya reportados de
eval-v1no se reescriben en su sitio. Si un arreglo cambia una cifra publicada, se recalcula desde los datos guardados (0 llamadas LLM), se escribe en un archivo nuevo y se actualiza el informe con una nota del cambio. - Ninguna llamada de pago sin aprobación explícita del usuario. La key tiene un tope de US$2,00 y quedan unos US$0,42.
- Operación en esta máquina (7,5 GB de RAM, repo en
/mnt/c):- Un solo proceso con modelos cargados a la vez.
- Corridas largas con
setsid nohup … < /dev/null &y log persistente endata/eval/results/. - Nunca
uv addniuv synccon una corrida en marcha. Siempreuv sync --extra dev --extra ingest(más--extra demodesde la Fase 3). - El REPL de Lean se usa desde
~/ragsolver_cache/lean_project.
- Después de cambiar código, ejecutar
graphify update .(lo pideCLAUDE.md). - No hacer commit ni push salvo que el usuario lo pida explícitamente.
- Cierre de cada fase: la suite pasa (
uv run pytest -q --deselect tests/test_retriever_integration.py, más la integración cuando aplique) y la documentación afectada queda actualizada en la misma fase.
- Corrección mínima en el lugar correcto. Rige la misma regla de
1. Decisiones que necesita el usuario
| # | Decisión | Se necesita en | Opción recomendada |
|---|---|---|---|
| D1 | Licencia del repositorio | Fase 9 (antes del commit) | Apache-2.0, coherente con CITATION.cff, Mathlib, miniF2F-v2 y Qwen3 |
| D2 | Commit y push de la rama (habilita el CI) | Fase 6.4 y Fase 9 | Tras cerrar las Fases 1–3 |
| D3 | Qué hacer con blobs/ (caché de MinerU con el texto del PDF) |
Fase 2 | Ignorarlo en git; borrarlo sólo si el usuario lo confirma |
| D4 | Alcance de la ingesta informal | Fase 5 | Guard de un solo documento con metadatos correctos; multidocumento sólo si hay un segundo PDF real |
| D5 | Replan | Fase 7 | Pasar el feedback del Crítico al Planificador (0 llamadas extra); alternativa: max_replans: 0 |
| D6 | Gasto | Fases 4.8, 7.1 y 9.4 | Smoke de 5 problemas triviales (≈ US$0,001); eval-v1-full (≈ US$0,30) |
| D7 | HF_TOKEN y nombres de los repos |
Fase 9.3 | — |
| D8 | Sandbox a nivel de sistema operativo | Fase 10 | Obligatorio antes de exponer la demo fuera de localhost |
2. Estado del arte consultado y decisiones
| Tema | Qué hacen los proyectos de referencia | Decisión para RagSolver | Por qué |
|---|---|---|---|
| Validar pruebas no confiables | La guía oficial de Lean (Validating a Lean Proof) define niveles: (1) el kernel acepta; (2) #print axioms con sólo propext, Classical.choice y Quot.sound (avisa de que Lean.trustCompiler delata evaluación nativa); (3) replay con lean4checker; (4) comparator en sandbox. Advierte que "las tácticas y el metacódigo pueden realizar acciones arbitrarias". |
Ahora, nivel 2: lista blanca de axiomas y rechazo de comandos y metaprogramación en las tácticas. Nivel 3/4 opcional, sólo para las pruebas que se publiquen (Fase 10). | Cierra SEC-1 y VER-1 con pocas líneas y sin infraestructura nueva. |
| Replay del entorno | SafeVerify: Environment.replay más la lista blanca de los 3 axiomas; lo usa el leaderboard de PutnamBench, y Mistral mantiene un fork para su evaluación (LeanstralSafeVerify). Rechaza native_decide. |
La misma lista blanca. Replay con SafeVerify sólo como verificación opcional de las pruebas publicadas. | Reutiliza una herramienta estándar en lugar de escribir un replay propio. |
| Juez de referencia | leanprover/comparator (Lean FRO, 2026): compila en un sandbox landrun, exporta con lean4export y hace replay con el kernel oficial y con kernels independientes. |
No adoptarlo por ahora. | Necesita Landlock, que no está activo en este kernel de WSL2 (verificado: landlock no aparece en /sys/kernel/security/lsm), y un .olean por problema. |
| Precedente | En los artefactos miniF2F publicados de Kimina-Prover, 15/225 soluciones dependían de Lean.ofReduceBool. |
VER-1 se trata como un riesgo real, no teórico. | Un prover publicado cayó exactamente en este fallo. |
| Sandbox en WSL2 | bubblewrap funciona en WSL2 porque usa namespaces de usuario, y Landlock es sólo una capa adicional (Codex linux-sandbox, lince #356). | bubblewrap mediante un script envoltorio pasado como lake_path de lean-interact (Fase 10). |
Verificado aquí: /usr/bin/bwrap está instalado, max_user_namespaces = 29869, y LeanREPLConfig(lake_path=…) es configurable, así que no hay que parchear la librería. |
| Reintentos de LLM | Lección propia (DECISIONS #59): los reintentos invisibles gastaron unos US$0,9. | No reintentar timeouts de lectura (pueden haberse cobrado) y contar cada intento. | Mismo tipo de fallo que LLM-1. |
| Reranker con poca RAM (2026-09-23, a pedido del usuario) | llama.cpp sirve Qwen3-Reranker de forma nativa por /v1/rerank (--reranking --pooling rank --embedding), con conversión GGUF oficial ya publicada (ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF; confirmado funcionando en el hilo de la propia model card de Qwen, discusión #22). |
Reranker de los agentes por GPU vía Vulkan (Fase 7.7), mismo patrón que el embedder (#48); no se espera a una máquina con más RAM. | tools/llama-vulkan/llama-server.exe ya instalado es un binario genérico de llama.cpp (sirve cualquier GGUF); /dev/dxg confirma el passthrough de GPU activo en esta máquina; el reranker (~1,8 GB) cabe junto al embedder en los 16 GB de VRAM de la RX 9060 XT sin tocar los 7,3 GB de RAM de WSL. |
3. Mapa hallazgo → fase
| Hallazgo | Severidad | Fase | Acción |
|---|---|---|---|
| SEC-1 | HIGH | 1 (y 10) | Validación de tácticas y del enunciado de la demo; sandbox de sistema operativo con disparador |
| VER-1 | HIGH | 1 | Auditoría de axiomas con lista blanca |
| SEC-2 | HIGH | 2 | .gitignore y publicación saneada |
| ING-1 | HIGH | 5 | Guard, metadatos reales, section_path real |
| BUG-1 | MEDIUM | 4 | IDs de cita estables |
| BUG-2 | MEDIUM | 1 | Una sola fuente Lean para el REPL y el reporte |
| BUG-3 | MEDIUM | 5 | CLI con argparse |
| BUG-4 | MEDIUM | 5 | Rutas ancladas a la raíz del repo |
| LLM-1 | MEDIUM | 4 | Política de reintentos |
| DES-1 | MEDIUM | 7 | Replan con feedback (D5) |
| TRZ-1 | MEDIUM | 4 | run_id en RetrievalContext |
| MET-1 | MEDIUM | 3 (doc), 4 (pin), 7 (tabla por defecto) | Documentar, registrar la tabla servida, mathlib_full por defecto |
| DATA-1 | MEDIUM | 3 (doc), 7 (medición), 10 (corpus) | Limitación documentada; disparador medido |
| DEP-1 | MEDIUM | 3 | pyproject.toml |
| TEST-1…4, CI-1 | MEDIUM/LOW | 1, 4, 5, 6 | Tests por fase y CI en funcionamiento |
| ACC-1, BUG-5, BUG-6 | LOW | 4 | Contabilidad completa |
| MET-2 | LOW | 7 | Redefinir la métrica |
| ARQ-2 | LOW | 5 | Versiones sólo desde versions.yaml |
| ARQ-3 | LOW | 8 | channels como parámetro del constructor |
| ARQ-4 | LOW | — | No se corrige: una reconstrucción atómica de mathlib_full (1,1 GB de vectores) no cabe cómodamente en 7,5 GB. Se documenta el riesgo; la tabla se puede regenerar desde el JSONL embebido |
| ARQ-5 y §9 (Ponytail) | INFO | 8 | Limpieza |
| ARQ-1 | LOW | 10 | Con disparador |
| SEC-3 | LOW | 3 (doc), 10 | Documentar el llama-server en 0.0.0.0 y cerrarlo con el sandbox |
| DES-2 | INFO | 7 | Sólo documentar |
| DES-3 | INFO | — | Ya reconocido en final_report.md §7; sin acción |
| DOC-1…12 | — | 3 y la fase que corresponda | Ver cada fase |
| Tareas del PLAN sin hacer (§3.A de la auditoría) | — | 1.5, 5, 7.3, 10 | Test índice ↔ Lean, etiqueta de cita, taxonomía del retriever; el resto con disparador |
4. Fases
Fase 1 — Verificador seguro y sólido
Cierra: SEC-1, VER-1, BUG-2, TEST-1 y el test de arranque índice ↔ Lean de PLAN_RAG §12. Esfuerzo: M. Costo: US$0.
Objetivo. Que "verificado" signifique: el kernel aceptó la prueba usando sólo propext, Classical.choice y Quot.sound. Que el texto generado por el LLM no pueda ejecutar comandos. Y que el lean_code reportado sea exactamente el código que se verificó.
Tareas
Una sola fuente Lean por intento (
lean/verify.py). Una función arma, a partir del problema y las tácticas, el texto que se envía al REPL: la cabeceraopen …sin imports,set_option maxHeartbeats N iny el enunciado con las tácticas empalmadas. Esa misma función la usan:LeanChecker.check;- el
lean_codedelSolutionReport, tanto en el grafo (graph.py:143) como en las líneas base (run.py:59); - el backend
lake, mientras exista.
lean_codepasa a ser los imports de configuración seguidos de esa fuente: un archivo autocontenido que reproduce el entorno del REPL. SobreProblem.importsadicionales: comprobar que ningún conjunto de problemas actual los usa y rechazar con un error explícito los problemas que los traigan, en lugar de ignorarlos en silencio.Validación previa de las tácticas. Una función pura que reemplaza a
_FORBIDDEN:- Quita antes los comentarios (
--y/- … -/). - Compara tokens completos, con límites de palabra.
- Rechaza:
- comandos que empiezan por
#(#eval,#print,#exit,#check, …); - metaprogramación:
run_tac,run_cmd,run_elab,elab,elab_rules,macro,macro_rules,syntax,notation,infix,infixl,infixr,prefix,postfix; - atributos y código no confiable:
attribute,@[,unsafe,implemented_by,extern; - cambios de entorno:
set_option(el presupuesto de heartbeats lo fija el sistema),import; - declaraciones:
theorem,lemma,def,abbrev,instance,example,axiom,opaque,structure,inductive,class, ynamespace/section/endal principio de una línea; - pruebas no verificadas por el kernel:
native_decide,+native,ofReduceBool,trustCompiler,sorry,admit; IO..
- comandos que empiezan por
- Se permite
open … in, que es una táctica legítima. - El rechazo devuelve un
Verdictcon un error legible, para que el Crítico pueda corregirlo en el siguiente intento.
- Quita antes los comentarios (
Auditoría de axiomas.
- Cuando el veredicto sale sin errores, el sistema (no el LLM) ejecuta
#print axioms <nombre del teorema>sobre el entorno que devolvió el REPL. - Se acepta sólo si los axiomas son un subconjunto de {
propext,Classical.choice,Quot.sound}. Cualquier otro (sorryAx,Lean.ofReduceBool,Lean.trustCompiler, un axioma nuevo) daok=falsecon el error "non-standard axioms: …". - El nombre del teorema se saca de
formal_statement. Si el enunciado es unexamplesin nombre, al empalmar se declara comotheoremcon un nombre fijo; eso no debilita el enunciado. Verdictgana el campoaxioms: list[str]: regenerar los esquemas y registrar el cambio en DECISIONS.
- Cuando el veredicto sale sin errores, el sistema (no el LLM) ejecuta
Demo (
demo/app.py).- El enunciado formal que escribe el usuario pasa por la misma validación y debe ser exactamente un
theoremolemmaque termina en:= by sorry. - La pestaña "Resolver" se niega a ejecutar si el servidor escucha en una dirección que no es de loopback.
- El enunciado formal que escribe el usuario pasa por la misma validación y debe ser exactamente un
Consistencia índice ↔ Lean (
PLAN_RAG §12).- En el precalentamiento del runner:
#checkde 20decl_nameelegidos al azar (semilla fija) de la tabla servida, excluyendo nombres privados y auxiliares. - Se escribe en el log cuántos existen. Si son menos de 19/20, se muestra un aviso, sin abortar.
- Cuesta segundos, porque el REPL ya está caliente.
- En el precalentamiento del runner:
Tests sin Lean (
tests/test_verify.py, archivo nuevo).- Deben rechazarse: la inyección exacta de la auditoría (
simpseguido de#eval IO.println …),decide +native,exact Lean.ofReduceBool _ _ rfl,run_tac. - Deben aceptarse: un comentario que contiene la palabra "axiom"; tácticas legítimas como
nlinarith [sq_nonneg (a - b)],norm_num [Nat.Prime] at h ⊢,decide,omega,interval_cases x <;> simp_all. - Parseo de la salida de
#print axioms, con fixtures de mensajes reales capturados una vez del REPL. lean_codedel reporte == texto recibido por un checker falso que registra lo que le llega.
- Deben rechazarse: la inyección exacta de la auditoría (
Test lento con Lean real.
- Marcador
slow, registrado enpytest.ini; se salta si no existe la caché de Lean. - Comprueba que la inyección nunca llega al REPL y que
decide +nativesobre2 ^ 10 = 1024queda rechazado por la auditoría de axiomas. - Se ejecuta una vez en local y el resultado se anota en DECISIONS.
- Marcador
Re-verificación offline de
eval-v1(0 llamadas LLM, sólo el REPL: unos 5 minutos de import más unos segundos).- Extraer las tácticas de los
lean_codede las 27 filasdoney verificarlas de nuevo con el verificador nuevo. - Pasar la validación de tácticas sobre los
lean_codefinales de las 300 filas, para medir falsos positivos. - Salida en un archivo nuevo,
data/eval/runs/eval-v1/reverify.json;results.jsonlno se modifica.
- Extraer las tácticas de los
Documentación.
- Nueva sección "Modelo de amenazas del verificador" en
architecture.md: qué se valida y qué no (no hay sandbox de sistema operativo hasta la Fase 10). - Entrada #69 en DECISIONS.
- Nueva sección "Modelo de amenazas del verificador" en
Aceptación
- Los tests nuevos pasan. El test lento se ejecutó una vez en local: la inyección fue rechazada y
+nativetambién. - Re-verificación: 27/27 siguen aceptadas y sólo usan los 3 axiomas estándar. Si alguna no pasa, la discrepancia queda en DECISIONS y en
final_report.md §6.3/§8con las cifras corregidas. - 0 falsos positivos de la validación sobre las 27 pruebas aceptadas.
- Esquemas sincronizados y
graphify update .ejecutado.
Plan B
- Si la salida de
#print axiomsno se puede leer de forma fiable del REPL, el sistema emite un#evalpropio conLean.collectAxiomssobre la constante. Es código del arnés, no del LLM, así que no contradice la tarea 2. - Si la lista de rechazo bloquea tácticas legítimas en más del 1 % de los intentos de desarrollo, se afina la lista y se registra el cambio.
Fase 2 — Licencias y datos, antes de cualquier commit
Cierra: SEC-2. Esfuerzo: S. Costo: US$0. Depende de: D3.
Tareas
.gitignore. Añadir:problemas resueltos olimpiadas.pdf;blobs/;lean_project/scratch/ylean_project.lock;.claude/*.graphify-bak;data/eval/runs/: las corridas se publican ya saneadas en HF (tarea 3) y contienen 354 fragmentos del PDF.
Preguntar al usuario si
Propuesta_Proyecto_Final_IA_UPTC.pdf, que es suya, debe versionarse.Comprobación por contenido.
- Tomar los primeros 60 caracteres del
content_mdde cada una de las 153 filas deinformal.jsonl. - Buscarlos en todos los archivos que se añadirían al repo: la lista de
git add --dry-run .y los archivos dedata/eval/results/ydata/eval/problems/. - Resultado esperado: 0 coincidencias.
- Tomar los primeros 60 caracteres del
scripts/publish_hf.py.- Cuando algún documento tenga
redistributable=false, las carpetas derunsse suben desde una copia temporal saneada:snippetvacío en las citas del corpus informal, y nada del PDF en las trazas. - Los originales nunca se modifican.
- Aplicar la misma comprobación de la tarea 2 sobre la copia saneada.
- Cuando algún documento tenga
Documentación. DECISIONS #70 y una nota en
user_guide.md("Publicación").
Aceptación
git status --porcelain --ignoredmuestra esos archivos como ignorados.- La comprobación por contenido da 0 coincidencias, tanto en el repo como en la copia saneada que produce
publish_hf.py --dry-run.
Plan B. Si el usuario quiere las corridas en git, se versiona una copia saneada generada por la misma función (por ejemplo data/eval/runs_public/).
Fase 3 — Dependencias y documentación de estado
Cierra: DEP-1, DOC-1…3, DOC-5, DOC-7…10, DOC-12, la parte documental de MET-1 y DATA-1, y SEC-3 (documentación). Esfuerzo: M. Costo: US$0.
Tareas
pyproject.toml.- Declarar
httpxytenacity. - Nuevo extra
democongradio, con cota inferior a la versión mayor instalada (6.x). - Pasar
ranxadev. - Quitar
pandasyacceleratesólo si, sin ellos, cargan el embedder y el reranker (bf16 en CPU) y la suite pasa. Si falla algo, se restauran y se anota. uv lockyuv sync --extra dev --extra ingest --extra demo, siempre sin corridas en marcha.
- Declarar
Documentación inmediata. Todo esto no depende de cambios de código:
README.md:uv synccon los extras.install.md: GPU vía Vulkan (#48), número real de tests, y tabla única con las 9 variables de entorno (RAGSOLVER_MATHLIB_TABLE,RAGSOLVER_EMBED_BACKEND,RAGSOLVER_EMBED_URL,RAGSOLVER_EMBED_BF16,RAGSOLVER_LEAN_PROJECT,RAGSOLVER_DB,RAGSOLVER_HF_DATASET,GRADIO_SERVER_NAME,HF_TOKEN).PROGRESS.md: reescritura. Una sección "Estado actual" al principio, sin contradicciones, y el material antiguo movido a "Historial". Se quita la tabla invalidada de la Fase 1, la sección "Sin GPU" y "Qué esperar cuando vuelvas".interfaces.md: rango real desubgoals.- DECISIONS: tachar la #34 indicando que la reemplaza la #40.
- Docstring de
evaluate.py. versions.yaml: el comentario deprovider_routing.
Limitaciones en
final_report.md§8 (y §10 donde aplique).- La tabla de 3.903 filas está sembrada con las premisas gold de miniF2F, lo que favorece al RAG (MET-1).
- Hueco de cobertura: ninguna declaración
*.eq_*ni del núcleo de Lean en el corpus (DATA-1). used_in_sketchno es fiable en los 34 reportes con IDs repetidos (BUG-1, hasta la Fase 4).- RF-3 y RF-6 se aplican por problema, no por subobjetivo.
- La frase "we release" pasa a indicar que la publicación está preparada pero pendiente.
DECISIONS #71. Una sola tabla con los desvíos del plan que no estaban registrados (§3.A de la auditoría): recuperación informal sólo en PLAN, sin boost por
math_area,_fts_texten lugar dename_tokens, embeddings float32 en lugar de float16,re_retrieveimplícito, sin tabladocuments.scripts/run_chain.sh. El resumen automático se escribe endata/eval/results/chain_summary.mden lugar de agregarse aPROGRESS.md(cambio de una línea).llama-serveren0.0.0.0(SEC-3). Eninstall.mdy en la cabecera deembed_full_mathlib.sh, indicar que se prefiera ligarlo a la IP de WSL, o apagarlo al terminar.
Aceptación
uv run python -c "import httpx, tenacity, gradio"funciona en el entorno sincronizado con los extras; la suite pasa.- Checklist de la auditoría §11 cerrado, cada punto contrastado con el código (con
grepographify explain). - Nada en
README,install.md,PROGRESS.md,architecture.mdniinterfaces.mdcontradice el código.
Fase 4 — Citas, trazas y contabilidad
Cierra: BUG-1, TRZ-1, LLM-1, ACC-1, BUG-5, BUG-6 y el pin de tabla de MET-1. Esfuerzo: M. Costo: US$0, más un smoke opcional (D6).
Tareas
IDs de cita estables.
- El estado del grafo guarda un mapa
chunk_id → citation_id. - Los nodos
planyretrievesólo asignan número a los chunks nuevos (el siguiente libre de cada prefijo,MoI) y reutilizan los existentes. Los prompts muestran esos IDs. - Test de regresión: un replan y una re-recuperación que devuelven chunks repetidos y nuevos no producen IDs duplicados, y
used_in_sketchmarca exactamente el chunk citado. - Los datos de
eval-v1no se recalculan: no es reconstruible porque las trazas del retriever no se pueden atribuir a una configuración. Queda como limitación (Fase 3.3).
- El estado del grafo guarda un mapa
Trazas del retriever por corrida.
RetrievalContextganarun_id. El orquestador le pasa el identificador del par.- El
Retrieverescribe en el archivo de ese par cuandorun_idestá presente; si no, se mantiene el comportamiento actual (CLI, demo, evaluación). - El tratamiento de trazas interrumpidas del runner pasa a cubrir también las búsquedas.
- Para las corridas nuevas, cambiar
:por__en los nombres de archivo de traza, porque/mnt/ces NTFS. Registrarlo en DECISIONS. - Test: tras una corrida falsa, hay un archivo por par con los eventos de agentes y de búsquedas, y ningún archivo
<problema>:<uuid>.
Pin de tabla.
VersionsPinganamathlib_table, con la tabla que realmente sirve el corpusmathlib. Regenerar esquemas.Política de reintentos (
llm.py).- Número de intentos configurable, por defecto 4.
- Se reintenta ante error de conexión, timeout de conexión, 429 y 5xx. No se reintenta ante un timeout de lectura, porque el proveedor puede haber cobrado.
timeout_sde la secciónllmbaja a 300 s: 4.000 tokens de salida al proveedor más lento medido (19 tok/s, #62) son unos 210 s.- Cada intento fallido se registra con su código, y la respuesta lleva el número de intentos (
attempts), que también va a la traza. - Tests con
httpx.MockTransport, que viene conhttpx(sin dependencias nuevas): 503, 503, 200 da 3 intentos registrados; un timeout de lectura no se reintenta.
Contabilidad sin pérdidas.
_guardcaptura cualquier excepción, salvoDailyQuotaExhausted, y la convierte enstatus: errordentro del grafo, conservando lo acumulado. Hoysolve()descarta el estado (graph.py:222).- El evento
invalid_jsonregistrallm_calls, tokens ycost_usd. - Las filas de error de las líneas base llevan el modelo.
- Test: una excepción a mitad de corrida conserva los contadores (análogo al test existente de JSON inválido).
Self-consistency. Sumar
cost_usdy el número real de llamadas de cada muestra (BUG-5).Caché de consultas. Las escrituras y el desalojo de
_QUERY_CACHEvan bajo un lock (BUG-6).Smoke opcional (D6). Los 5 problemas triviales con
full, código nuevo, LLM real y REPL. Costo ≈ US$0,001.- Esperado: 5/5 resueltos;
axiomsinformado; una traza por par con las búsquedas;mathlib_tableen el pin.
- Esperado: 5/5 resueltos;
Documentación.
architecture.md(trazas),reproduce.mdyuser_guide.md(estructura detraces/), DECISIONS.
Aceptación
- Tests nuevos en verde.
- En una corrida falsa de 3 pares con replan: 0 IDs duplicados, 1 archivo de traza por par, y los contadores de llamadas, tokens y costo del reporte coinciden con la suma de los eventos de la traza.
Fase 5 — Ingesta, CLI y configuración
Cierra: ING-1, BUG-3, BUG-4, ARQ-2 y la etiqueta de cita de PLAN §7.5. Esfuerzo: M (L si D4 = multidocumento). Costo: US$0, porque la caché de enriquecimiento evita llamadas.
Tareas
Rutas ancladas a la raíz. Una función que resuelve la raíz del repo a partir de
__file__(válido con la instalación editable que haceuv). La usan todas las lecturas deconfigs/,.envy los valores por defecto dedata/:llm.py,agents/agents.py,rag/retrieve.py,rag/enrich.py,rag/ingest_*.py,rag/embed_corpus.py,orchestrator/run.py,logging/jsonl_writer.py,demo/app.py.- Si algún día se distribuye como wheel no editable, añadir una variable de entorno para la raíz; hoy no hace falta.
- Test: con otro directorio de trabajo, se cargan la configuración de
LLM(con una clave falsa en el entorno) y la delRetriever.
Versiones desde
versions.yaml.index_versiony la versión del parser dejan de estar escritas eningest_informal.py(4 sitios).publish_space.pycuenta las filas de la tabla, igual quepublish_hf.py. El título de la demo toma la tag deversions.yaml.CLI.
ragsolver ingestdelega en elargparsedeingest_informal, a través de unmain(argv). Test: las flags se interpretan igual en cualquier orden (--skip-llm --pdf Xy--pdf X --skip-llm).Ingesta correcta (D4 = guard).
- Si
data/corpus/informal.jsonlexiste con otrodoc_id, la ingesta aborta con un mensaje claro, salvo que se pase--replace. - Los metadatos del documento se construyen con el modelo
Document, que así deja de estar muerto y además valida. Salen depymupdf(metadata: title, author, creationDate) y los sobrescriben las flags--title,--authors,--year,--licensey--redistributable. reproduce.md §2pasa las flags del PDF de ejemplo, de modo que sudocument.jsonquede idéntico al actual.section_pathtoma el título de sección que realmente detectóparse_structure, no el literal "Problemario".
- Si
Etiqueta de cita (
PLAN §7.5).- Cada
InformalChunkganasource_label(por ejemplo "Moreno Núñez & Tellechea Armenta (2002)"), escrito en la ingesta. _informal_citation_labello usa si existe; si no, eldoc_id.- Hay que reingerir (0 llamadas por la caché), reembeber (≈ 215 s en CPU) y reconstruir las tablas
informalyfigures.
- Cada
Multidocumento, sólo si D4 = multi. Archivos por documento (
data/corpus/informal/<doc_id>.jsonly sus figuras),ragsolver ingest <dir>que recorre los PDFs, y el CLI deindexconcatenando el directorio.Tests. Guard de otro
doc_id, metadatos desde flags, parseo del CLI, rutas desde otro directorio. La prueba de ingesta de extremo a extremo va en la Fase 6.Documentación.
user_guide.md §4(qué soporta y qué no),reproduce.md §2, DECISIONS.
Aceptación
- Reingerir el PDF de ejemplo da 153 chunks con los mismos
content_sha,document.jsonsin cambios y 0 llamadas LLM. ragsolver search … --corpus informal --mode exemplarmuestra la etiqueta con autor y año.ragsolver ingestdesde otro directorio funciona.- Ingerir un segundo PDF sin
--replaceaborta sin tocar el corpus.
Plan B. Si pymupdf no trae metadatos útiles para un PDF, las flags son obligatorias para ese documento (error explícito si faltan).
Fase 6 — Tests de las zonas sin cobertura y CI
Cierra: TEST-2 (resto), TEST-3, TEST-4 y CI-1. Esfuerzo: M. Costo: US$0. Depende de: Fases 1, 4 y 5; D2 para el CI.
Tareas
Fixture sintético. Sin contenido con licencia:
- un PDF de 2 páginas generado dentro del test con
pymupdf, con una sección "Problemario" (2 problemas), otra "Soluciones" (2 soluciones) y un dibujo vectorial; - un JSON de bloques estilo MinerU mínimo en
tests/fixtures/.
Test:
ingest_informal.run(skip_llm=True)de extremo a extremo, con directorios temporales. Esperado: 2 cards emparejadas, 1 figura recortada, archivos de corpus escritos.- un PDF de 2 páginas generado dentro del test con
Tests con modelos falsos (reutilizando el embedder falso y la LanceDB temporal de
test_retriever.py):_clide búsqueda;evaluate_variantssobre una tabla temporal;embed_corpus.runconembed_documentssustituido.
Integración con Lean real (marcador
slow; no se ejecuta en CI). Los 5 problemas triviales pasan por elOrchestratorcon un LLM falso que devuelve tácticas correctas conocidas y elLeanCheckerreal. Esperado: 5/5donecon axiomas estándar. Tarda unos 5 minutos, casi todos del import.CI (tras D2).
- Se ejecuta en cada push con
-m "not slow"y deseleccionando la integración con el índice. - Caché de
uvensetup-uv. - Medir la duración. Sólo si supera 15 minutos, usar en CI el índice de ruedas de torch para CPU.
- Se ejecuta en cada push con
Documentación. En
install.md, qué tests se saltan sin datos locales (15, según la auditoría) y cómo ejecutar losslow.
Aceptación
- Cobertura:
ingest_informal.py≥ 50 %,cli.py≥ 80 %,evaluate.pyyembed_corpus.py> 0 %,lean/verify.py≥ 70 %, total ≥ 78 %. - El CI termina en verde una vez en GitHub.
Fase 7 — Diseño y métricas
Cierra: DES-1, MET-2, la taxonomía de errores del retriever (PLAN F5 t5), la medición de DATA-1, la tabla por defecto de MET-1, la documentación de DES-2 y el reranker para los agentes (restricción de capacidad del §8 de la auditoría). Esfuerzo: S–M. Costo: US$0, salvo la medición opcional.
Orden: si se aprueba
eval-v1-full(D6), debe ejecutarse antes de la tarea 7.1, conprompt_version: "1", para no mezclar el cambio de prompt con el cambio de índice (ver Fase 9.4).
Tareas
Replan con feedback (D5).
- El prompt del Planificador recibe
{feedback}: la razón del Crítico y la estrategia anterior. - El grafo deja de vaciar
feedbackantes de replanificar; lo vacía después. prompt_version: "2".- Test con el LLM falso: el segundo prompt del planner contiene el feedback.
eval-v1no se repite; sus trazas registranprompt_version: "1".- Medición opcional (con aprobación de gasto) sobre el split dev, sin afirmar una mejora: con n = 24, la variación entre corridas es del tamaño del efecto (informe §7).
- El prompt del Planificador recibe
Métrica de re-recuperación (MET-2). Se reemplaza
re_retrieval_successporre_retrieval_used: la fracción de re-recuperaciones en modoerrortras las cuales el siguiente intento usa (segúnidentifiers_used) al menos una declaración que devolvió esa búsqueda, o se resuelve. Actualizareval_report.pyyfill_final_report.py.eval-v1sólo tiene 1 re-recuperación: se recalcula desde los datos si es trivial y, si no, se anota.Taxonomía de errores del retriever. Se calcula desde los datos guardados, sin traza nueva y a US$0:
- "Lema necesario no recuperado": premisas gold (
premises_gold.jsonl) que no están entre las citas consultadas del par (results.jsonl). - "Nombre alucinado": identificadores de
unknown identifieren los errores del verificador que no existen en el corpus. - "Ejemplar irrelevante": revisión manual de 20 listas de ejemplares, o se omite con una nota si no hay revisor.
- "Figura mal descrita": no aplica a miniF2F.
- Nueva sección en
eval_report.md.
- "Lema necesario no recuperado": premisas gold (
Disparador de DATA-1. A partir de la tarea 3, contar los identificadores desconocidos, y los
missing_identifiersdel Crítico, cuyo último componente empieza poreq_o que existen en Lean (se comprueba con#checken el REPL) pero no en el corpus. Si superan el 5 % de los fallos por lema faltante, se activa la tarea de corpus de la Fase 10.Tabla por defecto (MET-1). Si la tabla
mathlib_fullexiste yRAGSOLVER_MATHLIB_TABLEno está definida, el corpusmathlibse sirve desdemathlib_full(el pin de la Fase 4.3 lo registra).run_chain.shyreproduce.mdfijanRAGSOLVER_MATHLIB_TABLE=mathlibde forma explícita para reproducireval-v1.DES-2. Documentar en
architecture.mdy enfinal_report.mdque la crítica del último intento sólo alimenta la taxonomía (40 llamadas, 3,6 %). Sin cambio de código.Reranker para los agentes por GPU (revisado 2026-09-23 — ya no depende de más RAM).
- Hallazgo: el reranker no necesita más RAM del sistema, necesita correr en la GPU, igual que ya se hizo con el embedder (DECISIONS #48).
tools/llama-vulkan/llama-server.exees un binario genérico de llama.cpp: sirve cualquier GGUF, no sólo el embedder. llama.cpp soporta Qwen3-Reranker de forma nativa por el endpoint/v1/rerank(--reranking --pooling rank --embedding), con conversión GGUF oficial ya publicada y verificada (ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF). La RX 9060 XT (16 GB VRAM) tiene sitio de sobra junto al embedder (~1,8 GB); el tráfico va por Vulkan, sin tocar los 7,3 GB de RAM de WSL. Verificado en esta máquina:/dev/dxgpresente (passthrough de GPU activo),tools/llama-vulkan/llama-server.exeya instalado. - Tareas del lado del usuario (Windows, fuera de este agente): descargar
Qwen3-Reranker-0.6B-Q8_0.gguf(o el f16 si se prefiere) atools/llama-vulkan/models/; arrancar un segundollama-server.exeen un puerto distinto al del embedder (por ejemplo 8082) con--reranking --pooling rank --embedding -ngl 99 --host 0.0.0.0 --port 8082. - Tareas de código (este agente, en la Fase 7):
rag/retrieve.py::Retriever._reranker()gana un caminoRAGSOLVER_RERANK_BACKEND=llamaque llama aPOST {RAGSOLVER_RERANK_URL}/v1/rerankcon{query, documents}en vez deCrossEncoder.predict(), replicando el patrón deembed.py::_llama_embed(mismoThreadPoolExecutor, mismo manejo de reintentos,RAGSOLVER_RERANK_URLcon el mismo fallback de gateway que_llama_url()).configs/agents.yaml: retrieval.rerank: true.- Nuevo script
scripts/rerank_llama.sh(mismo formato queembed_full_mathlib.sh) con el comando exacto del servidor, para no depender de memoria. - Test con un servidor HTTP falso (
httpx.MockTransport, sin dependencias nuevas) que verifica el payload y el parseo de la respuesta.
- Medición (con el servidor arriba, 0 costo de LLM): repetir la Fase 5 de
PLAN_RAG.md(evaluación sobre MathlibQR, variantehibrido_rerank) sobre el índice completo y comparar con los 0,45 de recall@10 ya medidos sin reranker de agentes (#56). Si la ganancia es ≥ 3 puntos, se documenta como confirmada yretrieval.rerank: truequeda como valor por defecto también en un smoke de los 5 triviales.
- Hallazgo: el reranker no necesita más RAM del sistema, necesita correr en la GPU, igual que ya se hizo con el embedder (DECISIONS #48).
Aceptación
- Tests del prompt y de la métrica en verde.
- Sección de taxonomía publicada en
eval_report.md, con el cálculo reproducible con un comando. - Resultado del disparador de DATA-1 registrado en DECISIONS.
- Con el servidor de reranking arriba:
RAGSOLVER_RERANK_BACKEND=llama ragsolver search "gcd is commutative" --corpus mathlibdevuelve resultados reordenados y el camposcores.rerankpoblado, sin cargar elCrossEncoderde torch (verificable por RSS o por log).
Plan B (tarea 7). Si el usuario no tiene la máquina con la GPU disponible en el momento de esta fase, la tarea de código se implementa y se prueba con el servidor falso igual, y la medición y el rerank: true por defecto quedan pendientes de que el servidor esté arriba — no bloquean el resto de la fase.
Fase 8 — Limpieza (Ponytail)
Cierra: la auditoría §9, ARQ-3, ARQ-5 y el código muerto de §3.D. Esfuerzo: S–M. Costo: US$0. Depende de: Fase 6 (red de tests) y D3.
Tareas
Borrar código sin uso:
render_pages,read_events(sólo si ningún script lo necesita),LLMResponse.json,Enricher.modelsyEnricher.model,QUERY_INSTRUCTION,VectorFigure.n_pathsy los tests que sólo prueban eso.Borrar scripts reemplazados:
scripts/run_night.shyscripts/run_eval_v1.sh(los reemplazarun_chain.sh), y actualizar sus menciones en la documentación.Quitar el backend
lake(verify(),run_lean(),parse_messages()). El sandbox de la Fase 10 envuelve el REPL mediantelake_pathy no necesita el camino por proceso. La función de fuente única de la Fase 1 sustituye abuild_file.Deduplicar:
- despacho de verificación:
graph.verifyyrun_baselinellaman a una sola función de chequeo; - un helper para construir
VersionsPin.
- despacho de verificación:
Estado del
Retriever(ARQ-3):channelspasa a ser parámetro del constructor.evaluate.pyconstruye unRetrieverpor variante; el embedder ya está cacheado a nivel de módulo, así que es barato. La caché de tablas deja de serlru_cachesobre un método y pasa a un dict por instancia.Artefactos (según D3):
lean_project/scratch/,lean_project.locky.claude/settings.json.graphify-bak;blobs/sólo si el usuario lo confirma.graphify update .y comprobar congraphify explainque no quedan nodos huérfanos de lo borrado.
Aceptación
- La suite pasa.
- Balance de líneas registrado en DECISIONS; la auditoría estimaba unas −150.
- Ningún
importroto:python -m compileall src scripts demo.
Fase 9 — Entregables (decisiones del usuario)
Esfuerzo: S. Costo: ≈ US$0,30 si se ejecuta eval-v1-full. Depende de: D1, D2, D6 y D7. Obligatorio antes: Fases 1 y 2.
Tareas
Licencia (D1). Archivo
LICENSEyCITATION.cffcon la URL real del repositorio.Commit (D2).
- Revisar la lista de archivos antes; la comprobación de la Fase 2.2 debe dar 0.
- El commit y el push los ordena el usuario. El push dispara el CI (Fase 6.4).
Hugging Face (D7).
publish_hf.py --dry-runy revisión de la copia saneada; luego publicar el dataset y el Space.- Comprobar que el Space arranca y responde a una búsqueda, y que "Resolver" muestra el aviso de uso sólo local.
eval-v1-full(D6).- Se ejecuta con
run_chain.shdespués de las Fases 1 y 4 y antes de la Fase 7.1, conprompt_version: "1"y la misma configuración de agentes queeval-v1. - Los cambios de las Fases 1 y 4 no alteran los resultados, salvo lo que muestre la re-verificación (1.8): así, la única diferencia con
eval-v1es el índice. Hay que declararlo así en el informe.
- Se ejecuta con
Cierre. Actualizar
final_report.md(enlaces publicados, §6.3 coneval-v1-fullsi se ejecutó) yPROGRESS.md.
Aceptación
- Repo y dataset públicos sin texto del PDF (comprobación por contenido = 0).
- CI en verde.
- Informe con los enlaces reales.
Fase 10 — Trabajo con disparador (sólo si se cumple la condición)
| Extensión | Disparador | Qué implica | Aceptación |
|---|---|---|---|
| Sandbox de sistema operativo para Lean (SEC-1 residual, SEC-3) | Antes de exponer la demo fuera de localhost, de procesar problemas o corpus de terceros, o de cambiar a un proveedor LLM no confiable | Script envoltorio pasado como lake_path de lean-interact: bwrap sin red (--unshare-net), bind de sólo lectura del proyecto Lean y del toolchain, tmpfs para lo temporal, sin acceso a $HOME ni al repo |
Un #eval IO.FS.readFile sobre el .env, forzado con la validación desactivada en un test aislado, falla por permisos; el import de Mathlib tarda lo mismo (±10 %) |
| Replay de las pruebas publicadas (nivel 3) | Antes de presentar las pruebas como definitivas en HF o en el informe final | SafeVerify sobre las pruebas resueltas. comparator queda descartado mientras Landlock no esté activo |
Todas pasan, o las discrepancias quedan documentadas |
| RF-1, RF-3 y RF-6 por subobjetivo | Si el curso exige el cumplimiento literal | Subobjetivos como lemas have o lemma verificados por separado, con reintentos por subobjetivo |
Diseño propio (L) antes de implementar |
| Gold de OCR de 10 páginas | Cuando haya un anotador humano | Transcripción manual; métricas de PLAN F2 t10 |
Fórmulas ≥ 95 %, texto ≤ 3 % de distancia de edición |
| Completar el corpus (DATA-1) | Si el disparador de la Fase 7.4 supera el 5 % | Extraer las declaraciones que faltan (eq_* y núcleo de Lean) con el pipeline de LeanSearch v2 sin informalización (PLAN F1, plan B) y reindexar |
El recall de premisas gold sube y se documenta |
| Multidocumento (si D4 = guard) | Un segundo documento real para ingerir | Fase 5.6 | Dos documentos conviven con citas correctas |
| Contratos transversales (ARQ-1) | El próximo cambio que toque contratos de dos paquetes | Mover TraceEvent, VersionsPin, Citation y Verdict a un único módulo de contratos |
Sin dependencias lean → agents ni logging ↔ rag (verificado con Graphify) |
| Optimizaciones (auditoría §8) | Corridas en las que la recuperación supere el 10 % del tiempo de pared | Embeber las consultas de todos los subobjetivos en un lote; figuras en una sola consulta; embeddings en Arrow en lugar de JSONL | Latencia medida antes y después |
| PutnamBench | Trabajo futuro (#67) | Portar los enunciados al pin v4.28.0-rc1 |
— |
5. Esfuerzo y costo estimados
| Fase | Esfuerzo | Costo | Bloquea a |
|---|---|---|---|
| 1 Verificador | M (≈ 1 día) | US$0 | 3 (parcial), 4, 6, 9 |
| 2 Licencias | S | US$0 | 9 |
| 3 Dependencias y documentación | M | US$0 | 6 |
| 4 Citas, trazas y contabilidad | M | US$0 (+ US$0,001 opcional) | 6, 7, 9.4 |
| 5 Ingesta, CLI y configuración | M (L si hay multidocumento) | US$0 | 6 |
| 6 Tests y CI | M | US$0 | 8 |
| 7 Diseño y métricas | S–M | US$0 (+ medición opcional) | — |
| 8 Limpieza | S–M | US$0 | — |
| 9 Entregables | S | ≈ US$0,30 si hay eval-v1-full |
— |
Total estimado del agente: 6 a 8 días de trabajo, sin contar la Fase 10.
6. Definición de terminado (global)
- Los 4 HIGH (SEC-1, VER-1, SEC-2, ING-1) están cerrados, con tests.
- Cada MEDIUM está cerrado, o aplazado con su motivo registrado en DECISIONS.
eval-v1re-verificado con el verificador nuevo y el resultado documentado.- La documentación de estado es consistente con el código (checklist de la auditoría §11 cerrado).
- La suite pasa (incluidos los tests nuevos); el test
slowse ejecutó al menos una vez; el CI está en verde. graphify update .ejecutado tras la última fase de código.
7. Orden de ejecución
- Fase 1 y Fase 2 (independientes entre sí; ambas antes de cualquier commit).
- Fase 3 (depende de la Fase 1 sólo para la sección de amenazas; el resto puede ir en paralelo).
- Fase 4. Si se aprueba, aquí va el smoke de D6.
- Fase 9.1–9.2: licencia y commit, si el usuario lo decide. Habilita el CI.
- Fase 5, luego Fase 6.
- Fase 9.4 (
eval-v1-full, si se aprueba), antes de la Fase 7.1. - Fase 7, Fase 8, Fase 9.3 (publicación en HF).
- Fase 10, según los disparadores.
8. Referencias consultadas
- Lean FRO, Validating a Lean Proof (manual de referencia): https://lean-lang.org/doc/reference/latest/ValidatingProofs/
- leanprover/comparator (juez en sandbox para pruebas generadas por IA): https://github.com/leanprover/comparator
- GasStationManager/SafeVerify (replay más lista blanca de axiomas; leaderboard de PutnamBench): https://github.com/GasStationManager/SafeVerify · fork de Mistral: https://github.com/mistralai/LeanstralSafeVerify
- Kimina-Prover-Preview, issue #11 (15/225 soluciones de miniF2F dependían de
Lean.ofReduceBool): https://github.com/MoonshotAI/Kimina-Prover-Preview/issues/11 - Kimina Lean Server: https://arxiv.org/abs/2504.21230
- OpenAI Codex,
linux-sandbox(Landlock y bubblewrap): https://github.com/openai/codex/blob/main/codex-rs/linux-sandbox/README.md - Sandbox en WSL (bubblewrap y namespaces de usuario): https://github.com/RisorseArtificiali/lince/issues/356
- Comprobaciones locales (2026-09-23):
/usr/bin/bwrappresente;max_user_namespaces = 29869; Landlock ausente de/sys/kernel/security/lsm;lean-interact0.11.5 admiteLeanREPLConfig(lake_path=…). - llama.cpp, soporte de Qwen3-Reranker vía
/v1/rerank: https://huggingface.co/ggml-org/Qwen3-Reranker-0.6B-Q8_0-GGUF · https://huggingface.co/Qwen/Qwen3-Reranker-0.6B/discussions/22 · issue de calidad del endpoint (usar la conversión oficial): https://github.com/ggml-org/llama.cpp/issues/16407 - Comprobación local (2026-09-23):
/dev/dxgpresente (passthrough de GPU activo en WSL2);tools/llama-vulkan/llama-server.exeya instalado (mismo binario usado para el embedder, #48).