EstebanBarac's picture
Add panel parental, fix app.py docstring, model card, and open trace
0cd186e
|
Raw
History Blame Contribute Delete
3.55 kB
---
license: apache-2.0
language:
- es
tags:
- agent-trace
- conversational
- education
- spanish
- build-small-hackathon
pretty_name: Sofía Agent Trace
size_categories:
- n<1K
---
# Open Trace — Sofía (mérito 5, "Sharing is Caring")
**TL;DR (EN):** 12 end-to-end turns of the real Sofía pipeline (intent router →
curated content from `content/` → safety guard → small fine-tuned LLM), captured
as JSONL with the exact prompt sent to the model at each step. Shows the LLM is
a small, replaceable "conversational glue" component, not the source of facts —
including a turn where the model refuses to invent a story and offers curated
content instead, and a turn blocked entirely before reaching the LLM. Part of the
[`sofia-educational-companion`](https://huggingface.co/spaces/build-small-hackathon/sofia-educational-companion)
project (Build Small Hackathon, track *Backyard AI*).
`agent_trace.jsonl` son 12 turnos representativos corridos contra el **pipeline
real** (`safety.guard` + `llm.engine.LumiEngine` + `content.loader.ContentLibrary`),
generados con `export_trace.py`. Cada línea es un turno completo, de punta a punta.
Objetivo: que se vea, con datos reales, lo que dice el "Principio inviolable" de
[`CLAUDE.md`](../CLAUDE.md): el LLM es **solo el pegamento conversacional** dentro
de un pipeline mayormente determinista (router de intención → contenido curado de
`content/` → guard de seguridad). El modelo nunca es la fuente de hechos, cuentos
ni números.
## Formato (JSONL, un turno por línea)
| campo | qué es |
|---|---|
| `turn` | número de turno (1-12) |
| `child_age` | edad simulada (3) |
| `user_message` | lo que "dice" la niña |
| `safety_input_blocked` | `true` si `safety.guard.blocks_input` frenó el mensaje antes de llegar al motor |
| `intent` | intención detectada por el router (`llm/engine.py:_intent`); `null` si se bloqueó en la entrada |
| `prompt_messages` | los `messages` **exactos** que recibe el LLM: `system` (persona de Sofía) + historial + bloques `<contenido>`/`<contexto>`/`<nota>` armados por `LumiEngine.respond`; `null` si se bloqueó en la entrada |
| `activity` | la actividad curada elegida de `content/` (si aplica), con su `id`, `title` y `script` |
| `reply` | la respuesta final que recibiría la niña |
| `safety_output_blocked` | `true` si `safety.guard.blocks_output` reemplazó la respuesta por el fallback |
## Turnos destacados
- **Turno 10** (`"¿Qué es una pistola?"`): `safety_input_blocked: true`,
`prompt_messages: null`. El mensaje **ni llega al LLM**: lo frena
`safety.guard.blocks_input` y responde `gentle_redirect()`.
- **Turno 11** (`"Contame un cuento de un dragón que come autos"`): no existe ese
cuento en `content/stories.json`. El modelo **no inventa**`LumiEngine`
elige otro cuento curado existente ("La tortuguita paciente") y el LLM solo
lo presenta, con una `<nota>` que le prohíbe inventar contenido.
- **Turnos 3-9**: cada `<contenido>` es texto literal de `content/` (conteo,
formas, colores, animales, canción, emoción, cambio de color); el LLM
reformula/celebra pero los datos vienen del JSON curado.
## Cómo se generó
```bash
LUMI_LLM_BACKEND=ollama python trace/export_trace.py
```
Corre contra Ollama (`qwen2.5:7b`) en local, usando el mismo `SYSTEM_PROMPT` y
los mismos bloques `<contenido>`/`<contexto>`/`<nota>` que arma `llm/engine.py`
en producción (Qwen2.5-7B-Instruct fine-tuneado vía ZeroGPU). Usa un SQLite
temporal para el historial conversacional — no toca `parental/lumi.db`.