13 KiB
GFIBER-708 (Spike A) — Propuesta técnica: replicar Triple T dentro de Upsell 2.0
Contexto
GFIBER-708 ("Spike A - Create experiment to replicate the Triple T functionality in Upsell 2.0") es un ticket tipo Spike sin descripción, asignado a Alejandro, estado In Development. Existe un hermano GFIBER-709 ("Spike B", mismo título, asignado a Christian Barillas, estado Blocked) — son dos experimentos/propuestas paralelas que el equipo comparará antes de decidir el camino. GFIBER-609 ("Take down the old GCP deployments for Triple T and Upsell", estado Analysis) confirma que el destino final es decomisionar el stack GCP legacy una vez migrado.
Triple T hoy es una herramienta de troubleshooting asistido por IA sobre GCP (FastAPI + LangGraph + Firestore), basada en un grafo de nodos: el agente de call center sigue una guía paso a paso (ej. "¿el cliente no tiene wifi? → revisar conexión del router → revisar luz del router → …"), con los nodos agrupados en clusters ("initial triage", "diagnóstico de layer físico") por un LLM de clustering. Corre en un proyecto GCP separado del stack Next.js/Supabase de este repo (ver página Docmost Triple_T_—_Arquitectura_(GCP-Backend)).
El usuario (asignado al Spike A) quiere que esta propuesta gire alrededor de un chat agéntico (no un árbol de clicks) que ayude al agente en lenguaje natural, pero que siga fielmente el procedimiento definido — sin inventar ni saltarse pasos. Discutimos y descartamos explícitamente:
- LoRA fine-tuning: no iterable (cualquier cambio al procedimiento exige re-entrenar), no garantiza orden estricto, fuera de escala para 2 ingenieros/3 meses.
- RAG puro (similitud semántica sobre texto libre): bueno para Q&A abierto, pero no lleva estado de "qué paso ya se hizo" ni garantiza no saltarse pasos del procedimiento.
- Árbol de decisión puro (el approach legacy): garantiza el orden pero es rígido y no conversacional.
Landing en: chat agéntico con tool-calling sobre un árbol de troubleshooting versionado como JSON (sin RAG como columna vertebral). Confirmado con el usuario. Ya existe en este repo el 80% de la infraestructura necesaria:
app/(protected)/upsell-evaluator/api/copilot/route.ts— loop agéntico acotado (MAX_TOOL_ROUNDS) contra Fuelix (gateway Claude de la empresa), con tools de solo-lectura ejecutadas server-side.utils/ai/copilot.ts—buildSystemPrompt(base + "gem" persona),COPILOT_TOOLS(catálogo de tools como data), parsing de resultado estructurado.utils/ai/copilot-tools.ts— ejecución real de las tools contra Supabase.utils/ai/fuelix.ts— cliente Anthropic apuntado a Fuelix.- Tabla
gems(migración20260606120000_create_gems.sql) — ya permite inyectar instrucciones adicionales por sesión/persona.
Supabase no soporta un motor de grafos nativo (Apache AGE no está entre las extensiones habilitadas, y tiene problemas de compatibilidad de versión) — pero no lo necesitamos, y tampoco conviene modelarlo como tablas relacionales (nodes+edges): Triple T hoy es un solo árbol ("Internet Troubleshooting"), generado como unidad desde un Google Doc, y su escalabilidad futura pensada es "un árbol por guía adicional" (varios documentos pequeños, no un grafo gigante interconectado). Eso es exactamente el caso donde modelar el árbol completo como JSON versionado gana: hay precedente directo en el propio ecosistema — la tabla configuration de Apsel (BigQuery) versiona configuraciones completas del clasificador con status active/inactive, subiendo una versión nueva y despromoviendo la anterior sin tocar código. El mismo patrón acá: una tabla troubleshooting_guides (Postgres/Supabase) con columna tree jsonb + version + status active/inactive. El agente carga el árbol activo completo (o el subárbol relevante) y lo navega con funciones puras en TypeScript — no SQL, no recursive CTEs. Relacional (nodos/edges) seguiría siendo mejor si más adelante se necesitara edición de nodos individuales vía UI de admin o analytics por nodo vía SQL, pero eso no es parte del alcance de esta spike.
Adenda — persistencia de datos capturados durante el chat de troubleshooting: el usuario notó que el plan original no contemplaba que Upsell Evaluator ya captura datos del cliente/ticket, y preguntó si convendría construir un cliente y servidor MCP para que el chat guarde datos mencionados en la conversación. Investigación + exploración del código (agente Explore) concluyeron que no — MCP resuelve el problema de exponer un catálogo de tools a múltiples clientes de IA distintos o de aislar ejecución por gobernanza/auditoría; aquí el copilot vive en el mismo runtime Next.js que ejecutaría la tool, así que MCP solo agregaría latencia, overhead de tokens y un proceso más que mantener, sin ningún beneficio real (ver fuentes: supabase/supabase#679, comparativa MCP vs tool-calling nativo 2026). Se confirmó además que las tools actuales (utils/ai/copilot-tools.ts) son estrictamente de solo lectura (get_client_by_gaid, search_clients, recent_tickets, sales_summary — solo select, nunca mutan) — este sería el primer caso de escritura del catálogo.
El usuario precisó el alcance: los datos a persistir son del ticket (upsell_tickets), no del perfil del cliente (clients) — específicamente datos de troubleshooting (ej. qué se revisó, qué encontró el agente), separados de la razón de la llamada que ya se captura hoy vía la columna existente upsell_tickets.issue_type (enum, incluye internet_wifi). Y quiere que el guardado no sea autónomo: el agente de IA propone, el humano confirma con un click antes de persistir — mismo criterio de seguridad que ya aplican a otros campos sensibles del ticket (initiation_type, objection_handled_correctly, sales_coach_notes — todos con Server Actions dedicadas y restricción de rol).
Dado ese "confirmar con click", no hace falta una tool con efecto de escritura para el LLM — alcanza con extender el mismo contrato de salida estructurada que ya usa el copilot (CopilotResult en utils/ai/copilot.ts, hoy { mood, replies, objection?, next_action? }) con un campo nuevo proposed_troubleshooting_data?: { label, value }[], renderizarlo como chips/botones de confirmación en la UI de chat, y persistir el click con un nuevo Server Action (mismo patrón que saveClientSurvey/updateTicket en app/(protected)/upsell-evaluator/actions.ts) que hace un append al nuevo campo jsonb del ticket.
Entregable
Documento de propuesta técnica (no código de producto) en Docmost, siguiendo el patrón ya usado por otros planes del space GFiber (ej. Upsell_State_Override_—_Plan_Historial_de_Cambios, GFIBER-673_—_Propuesta_Tutorial_in-app).
Ya creado en esta sesión (turno anterior):
- Página padre:
GFIBER-708_—_Propuesta_Troubleshooting_Agéntico_en_Upsell_2.0(id019fa9bc-84e5-76d9-b8d1-ec8bba0f7cff), hija deGFiber_Pilot_Extension. - Subpágina
Plan_(English)(id019fa9bd-d66c-7462-9b1a-c0855ade6f37). - Subpágina
Plan_(Español)(id019fa9bd-4959-76ee-8a92-9f2861e87e2e).
Pendiente en esta iteración: actualizar (mcp__docmost__update_page) ambas subpáginas para agregar la nueva sección 6 (persistencia de datos de troubleshooting en el ticket) descrita abajo — no se recrean las páginas, se extienden.
Ambas versiones deben cubrir el mismo contenido (no es un resumen vs. detalle — son traducciones equivalentes, como en los planes previos del space).
Contenido del documento
- Resumen ejecutivo — qué es Triple T hoy, por qué se está evaluando reemplazarlo/replicarlo dentro de Upsell 2.0, y que este documento es la propuesta "Spike A" (mencionar la existencia de "Spike B" en paralelo, sin asumir su contenido).
- Comparación de 4 enfoques con tradeoffs concretos (árbol de decisión, RAG puro, fine-tuning/LoRA, agéntico+árbol-versionado) — la tabla de razones ya desarrollada en la conversación.
- Arquitectura recomendada:
- Modelo de datos: tabla
troubleshooting_guidesen Supabase Postgres con columnatree jsonb(el árbol completo: nodos, contenido en markdown, símbolos de síntomas/triggers, referencias a nodos siguientes, agrupación por cluster) +version+status active/inactive— mismo patrón de versionado queconfigurationde Apsel. Reemplaza el grafo de Firestore. Sin extensión de grafos, sin recursive CTEs: el árbol activo se carga completo y se navega con funciones puras en TypeScript. - Nuevas tools de agente (mismo esquema que
COPILOT_TOOLS): p. ej.get_next_candidate_steps(symptoms | current_node_id),get_node(id),search_nodes_by_symptom(text)— operan sobre el JSON ya cargado en memoria, no hacen queries SQL por nodo. Todas de solo lectura, ejecutadas server-side como las existentes encopilot-tools.ts. - System prompt / "gem" de troubleshooting: instrucción explícita de nunca inventar ni saltarse un paso — siempre grounded en el resultado de una tool call antes de sugerir la siguiente acción al agente/cliente.
- Reuso del loop agéntico ya existente en
copilot/route.ts(bounded tool rounds, Fuelix) — nueva ruta o extensión de la existente, a definir en el documento como pregunta abierta de implementación (no se decide en esta spike). - Migración de contenido: el árbol actual "Internet Troubleshooting" (hoy en Firestore, generado desde un Google Doc) se migraría a un único registro
tree jsonbentroubleshooting_guides— mencionar como trabajo de datos, no de código, fuera del alcance de esta spike.
- Modelo de datos: tabla
- Qué NO cubre esta spike — no incluye el pipeline de generación automática desde Google Doc ni el LLM-clustering de Triple T original (se asume carga estática/manual de nodos por ahora); no incluye migración de datos real ni decommission de GCP (eso es GFIBER-609, posterior).
- Próximos pasos — validación con Morossini/equipo, comparación con Spike B (GFIBER-709) cuando se desbloquee, y qué se necesitaría para pasar de spike a implementación real.
- Persistencia de datos de troubleshooting en el ticket (sección nueva):
- ¿Por qué no MCP? — un párrafo explicando que MCP resuelve exposición de tools a múltiples clientes de IA o aislamiento por gobernanza, ninguno de los cuales aplica aquí (copilot y tool corren en el mismo proceso Next.js); agregar MCP sumaría latencia/overhead de tokens sin beneficio. Se reutiliza el mismo tool-calling nativo (Anthropic) ya implementado.
- Modelo de datos: nueva columna en
upsell_tickets(migración nueva, ej.troubleshooting_log jsonb not null default '[]'::jsonb) — un array de entradas confirmadas{ node_id, label, value, confirmed_at }, por-ticket (no por-cliente): cada llamada/sesión de troubleshooting es su propio registro, coherente con queupsell_ticketsya es el log append-only por interacción. Distinto declients.discovery_answers(perfil estable del hogar) y deupsell_tickets.issue_type(la razón de la llamada, que ya existe como enum — ej.internet_wifi— y no requiere cambios). - Sin tool de escritura para el LLM: dado que el usuario quiere confirmación humana con un click (no autoguardado silencioso), basta con extender el contrato de salida estructurada que ya devuelve el copilot (
CopilotResultenutils/ai/copilot.ts) con un campo opcionalproposed_troubleshooting_data?: { label: string; value: string }[]. La UI de chat renderiza cada propuesta como un chip/botón "Guardar"; al hacer click se llama un Server Action nuevo (mismo patrón quesaveClientSurvey/updateTicketenapp/(protected)/upsell-evaluator/actions.ts) que haceappendal arraytroubleshooting_logdel ticket activo. - GAID + Case ID obligatorios al inicio de la conversación: el chat de troubleshooting debe pedir el GAID y el Case ID como primer paso, antes de cualquier diagnóstico — son identificadores obligatorios y ya compartidos entre sistemas (GAID identifica al cliente/
clients.gaid, Case ID cruza con el sistema de case-management víaupsell_tickets.case_id, ambos ya existentes en el esquema). Esto no es un dato más a proponer-y-confirmar como el resto: es el paso de identificación que resuelve a qué ticket pertenece la sesión — reutilizalookupClient(gaidInput)(ya existente enactions.ts) para resolver/crear elclient_id, y el mismo ticket en curso (identificado porcase_id) es al que se le haceappendentroubleshooting_log. Sin estos dos datos, no hay ticket al cual asociar ninguna propuesta de guardado — por eso van primero, no como una propuesta más entre otras. - Fuera de alcance de esta spike: la implementación real de la migración, el Server Action, y el cambio de UI — esta sección documenta el diseño para que el equipo lo evalúe junto con el resto de la propuesta, no lo construye.
Verificación
- Ambas subpáginas (
Plan_(English),Plan_(Español)) actualizadas con la sección 6 nueva, víamcp__docmost__update_page, verificado conmcp__docmost__get_pagetras la escritura. - Contenido revisado por el usuario antes de considerar la spike cerrada — no se toca el estado del ticket Jira GFIBER-708 a menos que el usuario lo pida explícitamente, y no se ejecuta ninguna migración de Supabase real (esto es solo el documento de propuesta).