Fuentes de Datos para Sofía

Sofía puede responder preguntas usando contenido real de los tres programas de FIA. El sistema usa routing semántico en dos pasos: primero encuentra la fuente más relevante, luego busca el contenido dentro de esa fuente.

Arquitectura de dos pasos

Pregunta del usuario
      ↓
match_data_sources(embedding)  →  ¿cuál fuente tiene la respuesta?
      ↓
match_capsule_chunks(embedding, p_path_id)
  o  match_knowledge_base(embedding)     →  chunks de contenido real
      ↓
Contexto para el prompt de Sofía

Por qué dos pasos

Con un solo paso (buscar en todos los chunks directamente) Sofía podría traer contenido de cualquier programa sin saber de cuál habla el alumno. Con dos pasos:

  • El paso 1 es liviano: compara con 5 filas de descripción
  • El paso 2 busca solo en la fuente correcta: miles de chunks, pero filtrados
  • Agregar un track nuevo = 1 INSERT + 1 embedding, sin redeployar el engine

Tabla sofia_data_sources

Cada fila describe una fuente de conocimiento en lenguaje natural. El engine la fetchea para saber qué puede buscar y cuándo.

Campo Tipo Para qué sirve
source_key TEXT (único) Identificador interno: fia_ventas_transcripts
name TEXT Nombre legible: "FIA Ventas — Clases y Cápsulas"
description TEXT Qué hay aquí y cuándo buscar — lo que Sofía "lee"
query_method TEXT match_capsule_chunks o match_knowledge_base
rpc_name TEXT Nombre exacto del RPC en Supabase
default_params JSONB Parámetros por defecto: threshold, count, p_path_id
covers_topics TEXT[] Temas que cubre
example_questions TEXT[] Preguntas de ejemplo (también usadas para el embedding)
embedding vector(1536) Embedding sobre name+description+topics+questions

Fuentes activas

source_key Programa Contenido
fia_agentica_transcripts FIA Agéntica 16 cápsulas: clases 1-4 + Primeros Pasos
fia_ventas_transcripts FIA Ventas 22 cápsulas: clases 1-10 + bonus
fia_empresas_transcripts FIA Empresas 17 cápsulas del programa
knowledge_base_frameworks KB Frameworks: RoLoCoDePre, WorkIA, RICA, Método FIA
knowledge_base_casos KB Casos de éxito con números reales

RPCs disponibles

match_data_sources — Paso 1: routing

Dada la pregunta embebida, devuelve las fuentes más relevantes.

SELECT * FROM match_data_sources(
  query_embedding := '<vector>',
  match_threshold := 0.4,  -- default
  match_count     := 3     -- default
);
-- Devuelve: source_key, name, description, query_method, rpc_name, default_params, similarity

match_capsule_chunks — Paso 2a: contenido de clases

SELECT * FROM match_capsule_chunks(
  query_embedding := '<vector>',
  match_threshold := 0.4,
  match_count     := 5,
  p_path_id       := 'b2c3d4e5-f6a7-8901-bcde-f12345678901'  -- FIA Ventas
);
-- Devuelve: capsule_slug, capsule_title, chunk_index, content, similarity

p_path_id = null busca en todos los tracks.

match_knowledge_base — Paso 2b: frameworks y casos

SELECT * FROM match_knowledge_base(
  query_embedding := '<vector>',
  match_threshold := 0.5,
  match_count     := 4,
  p_category      := 'caso'  -- opcional
);
-- Devuelve: slug, category, title, summary, content, voice_notes, similarity

Helpers TypeScript

En src/lib/sofia-data-sources.ts:

import { matchDataSources, getSofiaDataSources, matchCapsules, formatDataSourcesForPrompt } from '@/lib/sofia-data-sources'

// Paso 1: routing semántico
const fuentes = await matchDataSources("¿cómo uso ChatGPT para vender?")
// → [{ source_key: 'fia_ventas_transcripts', similarity: 0.62, default_params: { p_path_id: '...' } }]

// Paso 2: contenido de la fuente ganadora
const chunks = await matchCapsules("¿cómo uso ChatGPT para vender?", {
  pathId: 'b2c3d4e5-f6a7-8901-bcde-f12345678901'
})
// → [{ capsule_title: 'Clase 1 FV', content: '...', similarity: 0.71 }]

// Fallback: todas las fuentes como texto para el system prompt
const todas = await getSofiaDataSources()
const texto = formatDataSourcesForPrompt(todas)

Path IDs de los tracks

// src/lib/agentica.ts
FIA_AGENTICA_PATH_ID = 'd4e5f6a7-b8c9-0123-defa-234567890123'

// src/lib/static-ventas.ts
FIA_VENTAS_PATH_ID = 'b2c3d4e5-f6a7-8901-bcde-f12345678901'

// src/lib/static-empresas.ts
FIA_EMPRESAS_PATH_ID = 'c3d4e5f6-a7b8-9012-cdef-123456789012'

Agregar una fuente nueva

  1. INSERT en sofia_data_sources con embedding = null
  2. Generar el embedding sobre: name + '\n' + description + '\n' + covers_topics.join(', ') + '\n' + example_questions.join(' | ')
  3. PATCH con el embedding → la fuente es inmediatamente visible para el engine
  4. No hace falta redeployar nada