Base de datos
PostgreSQL con la extensión pgvector. ORM y migraciones con Drizzle. Los schemas
viven junto a cada módulo (src/**/*.schema.ts) y se re-exportan desde el barrel
src/database/db.schema.ts.
Tablas
memory_embeddings — correcciones (flywheel)
Lecciones validadas por supervisores; se recuperan por RAG en llamadas futuras.
| Columna | Tipo | Notas |
|---|---|---|
id | TEXT PK | SHA-256 del complaint (lowercase, trim) → idempotente |
embedding | vector(1536) | HNSW cosine |
customer_complaint | TEXT | Queja original |
wrong_answer_bot | TEXT | Respuesta incorrecta del bot |
human_correction | TEXT | Corrección del supervisor |
supervisor_id | TEXT | Quién corrigió |
category | TEXT | feedback_wrong | feedback_approved | general |
call_id | TEXT | Llamada de origen |
created_at | TIMESTAMPTZ | — |
knowledge_base — manuales técnicos
Chunks de los manuales de equipos. Ver RAG → Ingesta.
| Columna | Tipo | Notas |
|---|---|---|
id | TEXT PK | SHA-256 de source::section::rowKey → idempotente |
source | TEXT | Ruta del .md dentro de documentation/manuals/ |
category | TEXT | Carpeta de primer nivel (ej. concentradores electricos, cap) |
title | TEXT | H1 del manual |
section | TEXT | H2 de la sección |
content | TEXT | Texto del chunk |
embedding | vector(1536) | HNSW cosine |
created_at | TIMESTAMPTZ | — |
call_sessions — sesiones persistidas
Se escribe al terminar la llamada. Fuente del historial y del teléfono en los logs.
| Columna | Tipo | Notas |
|---|---|---|
id | TEXT PK | call_id de Retell o FreeSWITCH |
caller_id | TEXT | Número o nombre del llamante |
agent_id | TEXT | Agente que atendió |
transcript | JSONB | { speaker, text }[] |
suggestions | JSONB | Sugerencias generadas |
source | TEXT | retell | audio_fork |
started_at / ended_at | TIMESTAMPTZ | — |
feedback — acciones del supervisor
Una fila por marca sobre una sugerencia. Alimenta /feedback/stats y el flywheel.
| Columna | Tipo | Notas |
|---|---|---|
id | BIGSERIAL PK | — |
call_id / agent_id / suggestion_id | TEXT | suggestion_id = {call_id}_{turn} |
action | TEXT | used | correct | wrong | flagged_wrong | discarded | edited |
suggestion_text / edited_text | TEXT | Original / corrección |
flag_mode / flag_reason | TEXT | Opcionales |
created_at | TIMESTAMPTZ | — |
event_log — logging estructurado + auditoría
Store append-only de todos los eventos operativos y de usuario. Ver Observabilidad.
Migraciones (Drizzle)
Regla de oro: nunca editar a mano los .sql ni drizzle/meta/_journal.json. Siempre
generar con db:generate, que fija el when con el timestamp actual.
# 1. Modificar el schema en src/**/*.schema.ts
# 2. Generar la migración (crea el .sql y actualiza _journal.json)
pnpm db:generate
# 3. Revisar el SQL en drizzle/XXXX_nombre.sql
# 4. Aplicar en local
pnpm db:migrate
# 5. Commitear drizzle/ junto con el cambio de schema
| Comando | Qué hace |
|---|---|
pnpm db:generate | Genera SQL de migración desde cambios del schema (no toca la DB) |
pnpm db:migrate | Aplica migraciones pendientes en la DB local (lee .env) |
pnpm db:migrate:prod | Igual pero contra producción (lee .env.prod) |
pnpm db:push | Push directo del schema sin migración (solo dev) |
pnpm db:studio | Drizzle Studio (explorador visual) |
ALTER TYPE ... ADD VALUEAgregar un valor a un enum de Postgres (ej. un nuevo tipo de evento) va en su propia
migración y no puede usarse en la misma transacción en que se crea. Drizzle lo separa
con breakpoints; si db:migrate se queja de "unsafe use of new value", dividir el
ADD VALUE en una migración aparte de las columnas que lo usen.
Drizzle solo corre migraciones cuyo when sea mayor al último created_at en
__drizzle_migrations. Si una migración se saltea en prod, verificar su when en
_journal.json contra SELECT MAX(created_at) FROM __drizzle_migrations.
En producción (DigitalOcean) las migraciones corren como job pre-deploy automáticamente; ver Operaciones → Despliegue.