PLAN HF — documento unificado (2026-09-27)

Único doc del plan HF. Reemplaza a los fragmentos listados al final. Registro canónico: ~/nef/decisions/; copia de contexto: clinica-chat/decisions/.

1. Objetivo

Servicio de RAG/harnessing para clínicas y PyMEs, multi-cliente. HF corre el pipeline completo: embed → retrieval → rerank → generación → respuesta, en una sola app Gradio. No se limita a los tres primitivos sueltos.

2. Decisión vigente

capadóndepor qué
Producción (chat del paciente)Cloudflare Worker + gemma-4$0, ya desplegado y verificado (24/24)
Harness + RAG completo (lab/eval)Space HF ZeroGPUcorre modelos abiertos arbitrarios; gratis marginalmente
Demo por clienteun Space Gradio por clientehasta 10 Spaces; artefacto demostrable

Invierte el “HF fuera de la ecuación” anterior: HF no es parte del runtime del paciente, pero sí es donde vive y corre el pipeline de RAG completo.

3. Estado verificado (evidencia, no memoria)

  • Cuenta [usuario]: PRO activo, isPro: true. Renueva 2026-10-01.
  • Space [usuario]/sonrisa-inference: RUNNING con zero-a10g. El bloqueador viejo (“quedó en cpu-basic”) está resuelto.
  • Endpoints probados en vivo:
endpointmodelopruebaresultado
embedQwen3-Embedding-4B2 textos✅ vectores reales
rerankQwen3-Reranker-0.6B1 query, 3 docs✅ acierta (8.5 vs −12.4 / −9.6)
chatQwen3-14B”cuánto cuesta la limpieza”✅ “850 pesos”
  • API (Gradio 6): POST /gradio_api/call/<api_name> devuelve event_id; el resultado se lee por SSE con GET /gradio_api/call/<api_name>/<event_id>. El path viejo /call/<api_name> da 405.

4. Trampa del modelo (crítica, misma clase que gemma)

Qwen3-14B razona por defecto. Con max_tokens chico el <think> consume el presupuesto y la respuesta sale cortada. Arreglo probado: /no_think en el mensaje → respuesta correcta. Arreglo robusto: apply_chat_template(..., enable_thinking=False) en app.py.

Regla general: todo modelo “thinking” hay que apagarlo o el tope de tokens se agota en razonamiento y la respuesta sale vacía o cortada. (Bug idéntico en gemma-4 en el Worker.)

5. Límites (medidos / documentados)

  • 40 min GPU/día de cuota ZeroGPU (PRO). Se cobra por duración efectiva, no por lo reservado (duration es techo). Resetea 24 h después del primer uso, no a medianoche.
  • Cuota restante = prioridad de cola. Estrategia: duration ajustado (embed/rerank 15 s, chat 60 s) → misma cuota, mejor prioridad.
  • Cold start y cola: una GPU a la vez.
  • Hasta 10 Spaces ZeroGPU (uno por cliente).
  • Excedente: $1 por 10 min de GPU vía créditos. Colchón, no plan.
  • Estrategia de horario: el demo es atendido → confinarlo a horario de clínica y prenderlo con un Cron Trigger de Cloudflare. La producción (Cloudflare) es always-on y no se programa.

6. Costo: PRO (20/usuario)

Team = PRO para cada miembro + gobernanza. Diferencias reales:

PROTeam
cuota por persona40 min/díaigual (hereda PRO)
créditos$2/mes$2 por asiento, compartidos
storage privado1 TB1 TB por asiento
storage público10 TB12 TB base + 1 TB/asiento
Spaces ZeroGPU1050
extra—SSO (SAML/OIDC), Storage Regions, Audit Logs, Resource Groups, Analytics, control de tokens

Decisión: quedarse en PRO $9. No hay org ni equipo; las funciones de Team son de gobernanza/compliance que hoy no se aplican. Revisar Team solo si: (a) se contrata a alguien, (b) un cliente exige residencia de datos/audit, (c) se pasa de 10 Spaces o 1 TB.

7. Sustituto OSS (evaluado, no adoptado)

El reemplazo OSS de HF ya está instalado: Ollama (esta máquina: M1 Pro, 16 GB).

de HFlocal ya disponible
embed (Qwen3-Embedding-4B)nomic-embed-text
chat (Qwen3-14B)qwen3.5:9b, gemma4
rerank (Qwen3-Reranker-0.6B)falta (agregar GGUF o TEI)
  • Techo práctico ~9B (16 GB unificada; qwen3.6 23 GB no cabe).
  • Stack 100 % OSS auto-hospedable: TEI (embed/rerank) + vLLM/TGI (chat) + MinIO.
  • No se migra: HF da GPU gratis y demo público; lo único que Ollama no puede dar es un GPU público corriendo modelos abiertos arbitrarios.

8. Marco estratégico (de dónde correr open-weights)

  • El modelo es commodity; el valor y el lock-in están en el harness + las evals + el cliente. El modelo debe ser variable de configuración, no decisión arquitectónica.

Los 4 niveles (el Space de HF ≈ nivel 2, gratis):

nivelquécuándoejemplos
0tu máquinadev, demos, datos que cabenSystem76 + Ollama / llama.cpp
1serverless por tokenvolumen bajo, esporádico, sin opsCloudflare Workers AI + Vectorize, OpenRouter, Fireworks/Together, Groq/Cerebras, DeepInfra/Nebius/Novita
2GPU rentada/dedicadahay contrato o volumen medido; necesitas tus pesos/LoRARunPod, Modal, Baseten, Vast.ai, Lambda (vLLM)
3on-prem del clienteel contrato exige que los datos NO salganapp entregable + docs, no SaaS

Eje de decisión:

preguntasi…entonces
¿los datos pueden salir del cliente?Nonivel 2/3
¿volumen bajo y esporádico?Sínivel 1
¿latencia interactiva por request?SíGroq/Cerebras o GPU local
¿necesitas LoRA / pesos / fine-tune?SíFireworks/Together o nivel 2
¿vendes a SMB sin equipo de ops?SíCloudflare (nivel 1 edge)

Recomendación: LiteLLM como proxy delante de todo (cambiar de proveedor = una línea; evita lock-in desde el día 1). Harness + evals local con Ollama. Ruta “empresa” con Cloudflare. Subir a nivel 2 solo cuando un cliente firme o el volumen lo justifique; rentar GPU antes es quemar dinero en un problema que no tienes.

  • Anti-rabbit-hole: comparar 15 proveedores es la trampa (como el CRM). Elegir uno y ship.

9. Credenciales y permisos (verificado)

  • Token fine-grained de zshrc: crea repos y push por git ✅; NO sube archivos por API (403), NO cambia hardware (403), NO reinicia Spaces (403).
  • OAuth del MCP: read-repos, contribute-repos, inference-api, jobs, read-mcp.
  • Space: el hardware se asigna solo desde la web (Settings → Hardware); el campo zero_gpu: true del README no asigna GPU.
  • Sandboxes/Jobs de HF dieron 402 (billing).
  • Por qué importa el cómputo de HF: el free tier de Groq topa en 200k tokens/día por modelo (TPD); la eval completa con RAGAS necesitaba ~1.2M tokens de juez → no cabía en un día. HF (ZeroGPU) quita ese techo.
  • Riesgo operativo: había 3 sesiones opencode a la vez y una escribió en el mismo repo (commit ajeno a sonrisa-inference). Verificar ps aux | grep opencode antes de pushear a un repo compartido.

10. Espejo del código

  • [usuario]/clinica-chat en HF = dataset privado, espejo de GitHub [usuario]/clinica-chat.
  • Card de dataset: requiere front matter YAML en README.md (agregado). GitHub lo renderiza como tablita de metadata.
  • Sincronización: git push hf main falla por el commit inicial que HF auto-crea (.gitattributes); usar merge en rama temporal (sin force) o --force-with-lease.

11. Operación del Space (lo esencial del runbook)

Detalle completo y vigente: 2026-09-26-hf-space-zerogpu-runbook.md. Lo que cuesta tiempo si se olvida:

  • Diagnóstico: hf spaces info <id> --json → runtime.stage / requested_hardware. CONFIG_ERROR = frontmatter YAML. Logs de runtime: hf spaces logs <id> (el endpoint REST /logs da 404). 502 = la app está viva pero no bindeó (cargando) o crasheó. 404 sin token + 200 con token = Space privado, no es bug.
  • Frontmatter, las 3 causas reales: falta el --- de apertura (el cierre solo no basta), falta sdk_version, o short_description > 60 chars (rompe validate-yaml).
  • ZeroGPU: import spaces antes de torch. En módulo scope device = "cuda" if torch.cuda.is_available() else "cpu" + .to(device); nunca device_map= (bypasea el hijack de ZeroGPU y exige accelerate). No pinnear spaces en requirements.txt (la plataforma fija el suyo → el build falla).
  • Arranque rápido: preload_from_hub baja los pesos en build. python 3.12.12 o 3.10.13; torch 2.8.0+.
  • Fallo típico de preload: RepositoryNotFoundError = model ID inexistente (Qwen/Qwen3-14B-Instruct no existe; el correcto es Qwen/Qwen3-14B).
  • Durations: declarar el mínimo real → mejor prioridad de cola.
  • Cliente: gradio_client usa token= (no hf_token=); Client("owner/name", token=HF_TOKEN) para Spaces privados.
  • Medido al cierre (2026-09-26): /embed 5.4 s (2560 dim), /rerank 4.4 s, /chat 7.6 s; pack ZeroGPU ~38.8 GB, todos los modelos en cuda a nivel módulo.
  • Dev Mode (trampa, 2026-09-27): con devMode: true el Space ignora los commits pusheados (seguía 2 commits atrás). Hay que hf spaces restart <id> (o --factory-reboot). Y el runtime.sha del API va stale: la verdad es el comportamiento, no el campo.
  • Juez RAGAS (2026-09-27): RAGAS habla OpenAI y el Space habla Gradio. Se resuelve con un shim OpenAI-compatible local (proyecto_1_dentistas/src/dentistas/space_server.py) que traduce /v1/chat/completions → Space; así RAGAS usa su camino openai probado.

12. Pendiente

  • Apagar enable_thinking en el app.py del Space — hecho (2026-09-27).
  • Correr el harness/pipeline sobre el Space — hecho: E1 n=20 completo (§14).
  • Encender rerank (está en off) para subir context_precision (0.645).
  • Mover el harness dentro de app.py (hoy el Space expone primitivos y el harness vive en proyecto_1_dentistas; funciona, pero implica dos piezas).
  • Decidir si es un Space por cliente o uno multi-tenant.
  • gradio_client no estaba en pyproject.toml (el provider space lo necesita) — reponer.

13. Docs que este documento unifica/supera

  • [CREDENCIAL].md — superado (el bloqueador de ZeroGPU ya no aplica; el objetivo sigue, la ruta cambió).
  • [CREDENCIAL].md — superado (contenido aquí).
  • [CREDENCIAL].md — superado (contenido aquí).
  • 2026-09-27-hf-plataforma-lecciones.md — lo útil traído a §9; el original se conserva por el detalle (scopes del token, RAGAS, Groq TPD).
  • [CREDENCIAL].md — lo útil traído a §8 (tabla de niveles, eje de decisión, LiteLLM); el original se conserva por el análisis completo y las fuentes.
  • 2026-09-26-hf-space-zerogpu-runbook.md — lo útil traído a §11; el original se conserva como runbook operativo completo.

Todos ellos se conservan: son importantes. Este documento los resume; ellos son el detalle.

14. Resultados de pruebas de uso (E1, n=20, todo en el Space, 2026-09-27)

Primer uso real: el pipeline completo (embed + retrieval + rerank + respuesta + juez) corrió sobre el Space, costo $0, sin Groq ni Inference Providers. Corrida: proyecto_1_dentistas/artifacts/runs/20260927-182716/.

métricavalorumbral
retrieval_hit_rate1.0—✅
citation_accuracy0.9380.90✅
context_recall0.800.75✅
faithfulness0.770.85❌
context_precision0.6450.75❌
escalate_accuracy0.5—❌
costo generación$0.0—✅
  • Índice 77 chunks (dim 2560) · generación 72.6 s · juez 220 llamadas / 1120 s.
  • Wall clock total: 20 min 39 s.

Hallazgo: escalate_accuracy = 0.5. Fuera de alcance (q17–q20), el RAG pelado inventa negativas (“no atendemos a domicilio”, “no aceptamos criptomonedas”) en vez de negarse. Las mismas 4 en clinica-chat (gemma + reglas) pasan 4/4. Medido: para “no inventar”, el prompt pesa más que el modelo. El retrieval (1.0) y las citas (0.938) no son el problema; la política del respondedor sí.

Palanca siguiente: context_precision 0.645 con el rerank en off → encenderlo — hecho (corrida 20260927-193113): rerank=llm subió context_precision a 0.853 (✅ pasa el umbral) y escalate 0.5→0.75; fe de ello con citation 0.875 (−0.063, por debajo de 0.9: un caso). Ninguna corrida domina a la otra: con rerank pasan precision+recall; sin él, citation. faithfulness ≈0.76 en ambas → el problema de invención vive en el respondedor, no en el retrieval. Queda como default rerank="llm".