E1 con el Space de HF como único backend (20 preguntas, 2026-09-27)
Primer uso real del Space ZeroGPU: todo el pipeline (embed + retrieval + rerank +
respuesta + juez) corrió sobre [usuario]/sonrisa-inference, costo $0 y sin tocar Groq
ni Inference Providers. Produjo números de evaluación.
Setup
# en proyecto_1_dentistas
export DENTISTAS_LLM_PROVIDER=space
export DENTISTAS_EMB_PROVIDER=space
export DENTISTAS_JUDGE_PROVIDER=space
export DENTISTAS_SPACE_OPENAI=http://[IP]:8123/v1
PYTHONPATH=src .venv/bin/python -m dentistas.space_server 8123 & # shim
PYTHONPATH=src .venv/bin/python -m dentistas.cli index # 77 chunks, dim 2560
PYTHONPATH=src .venv/bin/python -m dentistas.cli eval --limit 20Resultado (n=20, golden set v0)
| métrica | valor | umbral | ¿pasa? |
|---|---|---|---|
| retrieval_hit_rate | 1.0 | — | ✅ |
| citation_accuracy | 0.938 | 0.90 | ✅ |
| escalate_accuracy | 0.5 | — | ❌ |
| faithfulness | 0.77 | 0.85 | ❌ |
| context_precision | 0.645 | 0.75 | ❌ |
| context_recall | 0.80 | 0.75 | ✅ |
| 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.
El hallazgo que importa: el prompt, no el modelo
escalate_accuracy = 0.5. Las 4 preguntas fuera de alcance (q17–q20) fueron contestadas
de forma asertiva en vez de negarse:
- q17 “¿atienden a domicilio?” → “No, no atendemos a domicilio” (el corpus no dice eso).
- q19 “¿criptomonedas?” → “No aceptamos criptomonedas” (inferido de la lista de pagos).
- q20 “¿convenio con IMSS?” → “No tenemos convenio con el IMSS” (inferido).
O sea: el RAG pelado inventa respuestas negativas. Es exactamente lo que las reglas
de clinica-chat prohíben — y ahí, con gemma + el prompt estricto, las mismas 4 preguntas
pasan 4/4. Conclusión: para “no inventar”, las reglas del prompt pesan más que el
modelo. El retrieval (1.0) y las citas (0.938) no son el problema; la política del
respondedor sí.
context_precision = 0.645 (bajo): recupera bien pero trae contexto de más. Candidato a
mejorar con el rerank (está en off en config) o el chunking.
Arreglo aplicado al Space (y su trampa)
app.py:apply_chat_template(..., enable_thinking=False)— Qwen3 razona por defecto y el<think>truncaba/vaciaba la respuesta. Antes:'<think>Okay, the user is asking...'en 13.9 s. Después:"La limpieza dental completa cuesta 850 pesos."en 7.2 s.- Trampa: el Space estaba en Dev Mode, así que el push del commit no se aplicó
(seguía corriendo 2 commits atrás). Hay que reiniciar:
hf spaces restart <id>(o--factory-reboot). Además elruntime.shadel API va stale: la verdad es el comportamiento, no el campo.
Bugs de infra que salieron
gradio_clientno estaba enpyproject.toml(el providerspacelo necesita).- El juez
spaceestaba comoNotImplementedError; se cableó con un shim OpenAI-compatible (src/dentistas/space_server.py) que traduce/v1/chat/completions→ Space Gradio, para reusar el caminoopenaide RAGAS. - El resumen imprime
juez = space/deepseek-ai/DeepSeek-V4-Pro: es solo la etiqueta de config, el juez real fue Qwen3-14B. No leerlo mal.
Repetición con rerank=llm (corrida 20260927-193113, mismo día)
| métrica | rerank off | rerank llm | umbral |
|---|---|---|---|
| retrieval_hit_rate | 1.0 | 1.0 | — |
| citation_accuracy | 0.938 | 0.875 | 0.90 |
| escalate_accuracy | 0.5 | 0.75 | — |
| faithfulness | 0.77 | 0.755 | 0.85 |
| context_precision | 0.645 | 0.853 | 0.75 |
| context_recall | 0.80 | 0.80 | 0.75 |
- Objetivo cumplido: context_precision cruza el umbral (0.645→0.853).
- Cambio de perfil, no dominancia: sin rerank pasa citation; con rerank pasan precision y escalate. Ninguna corrida pasa las 4 umbrales.
- faithfulness ≈0.76 en ambas: la invención vive en el respondedor (política del prompt), no en el retrieval. Confirmado dos veces.
- Costes: $0 en ambas; 20 llamadas extra de rerank (judge 1198 s vs 1120 s).
- Queda
rerank = "llm"como default en config. - Bugs destapados al encenderlo:
_llm_rerankasumía dict y recibía lista (AttributeError en el fallback, nunca ejercido con rerank=off); yproviders.chatmodo space ignorabajson_mode→ el rerank habría caído en silencio al orden RRF. Ambos arreglados.
220 llamadas de chat + embeddings en un día. La cuota PRO es 40 min de GPU/día; se
consumió tiempo efectivo (no el duration reservado). Caber, cupo — pero un E1 completo
es una corrida de ~20 min de reloj, no algo para correr en bucle.