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
| capa | dónde | por qué |
|---|---|---|
| Producción (chat del paciente) | Cloudflare Worker + gemma-4 | $0, ya desplegado y verificado (24/24) |
| Harness + RAG completo (lab/eval) | Space HF ZeroGPU | corre modelos abiertos arbitrarios; gratis marginalmente |
| Demo por cliente | un Space Gradio por cliente | hasta 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:RUNNINGconzero-a10g. El bloqueador viejo (“quedó encpu-basic”) está resuelto. - Endpoints probados en vivo:
| endpoint | modelo | prueba | resultado |
|---|---|---|---|
embed | Qwen3-Embedding-4B | 2 textos | ✅ vectores reales |
rerank | Qwen3-Reranker-0.6B | 1 query, 3 docs | ✅ acierta (8.5 vs −12.4 / −9.6) |
chat | Qwen3-14B | ”cuánto cuesta la limpieza” | ✅ “850 pesos” |
- API (Gradio 6):
POST /gradio_api/call/<api_name>devuelveevent_id; el resultado se lee por SSE conGET /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 (
durationes techo). Resetea 24 h después del primer uso, no a medianoche. - Cuota restante = prioridad de cola. Estrategia:
durationajustado (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:
| PRO | Team | |
|---|---|---|
| cuota por persona | 40 min/día | igual (hereda PRO) |
| créditos | $2/mes | $2 por asiento, compartidos |
| storage privado | 1 TB | 1 TB por asiento |
| storage público | 10 TB | 12 TB base + 1 TB/asiento |
| Spaces ZeroGPU | 10 | 50 |
| 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 HF | local 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.623 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):
| nivel | qué | cuándo | ejemplos |
|---|---|---|---|
| 0 | tu máquina | dev, demos, datos que caben | System76 + Ollama / llama.cpp |
| 1 | serverless por token | volumen bajo, esporádico, sin ops | Cloudflare Workers AI + Vectorize, OpenRouter, Fireworks/Together, Groq/Cerebras, DeepInfra/Nebius/Novita |
| 2 | GPU rentada/dedicada | hay contrato o volumen medido; necesitas tus pesos/LoRA | RunPod, Modal, Baseten, Vast.ai, Lambda (vLLM) |
| 3 | on-prem del cliente | el contrato exige que los datos NO salgan | app entregable + docs, no SaaS |
Eje de decisión:
| pregunta | si… | entonces |
|---|---|---|
| ¿los datos pueden salir del cliente? | No | nivel 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: truedel 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). Verificarps aux | grep opencodeantes de pushear a un repo compartido.
10. Espejo del código
[usuario]/clinica-chaten 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 mainfalla 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/logsda 404).502= la app está viva pero no bindeó (cargando) o crasheó.404sin token +200con token = Space privado, no es bug. - Frontmatter, las 3 causas reales: falta el
---de apertura (el cierre solo no basta), faltasdk_version, oshort_description> 60 chars (rompevalidate-yaml). - ZeroGPU:
import spacesantes de torch. En módulo scopedevice = "cuda" if torch.cuda.is_available() else "cpu"+.to(device); nuncadevice_map=(bypasea el hijack de ZeroGPU y exigeaccelerate). No pinnearspacesenrequirements.txt(la plataforma fija el suyo → el build falla). - Arranque rápido:
preload_from_hubbaja 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-Instructno existe; el correcto esQwen/Qwen3-14B). - Durations: declarar el mínimo real → mejor prioridad de cola.
- Cliente:
gradio_clientusatoken=(nohf_token=);Client("owner/name", token=HF_TOKEN)para Spaces privados. - Medido al cierre (2026-09-26):
/embed5.4 s (2560 dim),/rerank4.4 s,/chat7.6 s; pack ZeroGPU ~38.8 GB, todos los modelos en cuda a nivel módulo. - Dev Mode (trampa, 2026-09-27): con
devMode: trueel Space ignora los commits pusheados (seguía 2 commits atrás). Hay quehf spaces restart <id>(o--factory-reboot). Y elruntime.shadel 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 caminoopenaiprobado.
12. Pendiente
Apagar— hecho (2026-09-27).enable_thinkingen elapp.pydel SpaceCorrer el harness/pipeline sobre el Space— hecho: E1 n=20 completo (§14).- Encender
rerank(está enoff) para subircontext_precision(0.645). - Mover el harness dentro de
app.py(hoy el Space expone primitivos y el harness vive enproyecto_1_dentistas; funciona, pero implica dos piezas). - Decidir si es un Space por cliente o uno multi-tenant.
gradio_clientno estaba enpyproject.toml(el providerspacelo 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étrica | valor | umbral | |
|---|---|---|---|
| retrieval_hit_rate | 1.0 | — | ✅ |
| citation_accuracy | 0.938 | 0.90 | ✅ |
| context_recall | 0.80 | 0.75 | ✅ |
| faithfulness | 0.77 | 0.85 | ❌ |
| context_precision | 0.645 | 0.75 | ❌ |
| escalate_accuracy | 0.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:
— hecho (corrida 20260927-193113): context_precision 0.645 con el rerank en off → encenderlorerank=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".