Volver a la wiki

Chat Tutor multi-turn — hilo conversacional en el Help Widget

Chat Tutor multi-turn — hilo conversacional en el Help Widget

Resumen

El modo IT Tutor del Help Widget pasa de ser un sistema de pregunta-respuesta única a un chat con historial conversacional (s55). El usuario puede encadenar preguntas de seguimiento y el modelo mantiene el contexto entre mensajes, comportándose como un tutor de IT interactivo.

La funcionalidad convive con el modo Help existente (búsqueda contextual en la wiki de la app), que no recibe historial y mantiene su flujo de respuesta única.


Decisiones de diseño (consensuadas con Edu)

DecisiónValor
Persistencia del hiloEfímera — se limpia al cerrar el panel o al cambiar de modo
Límite de historialÚltimos 10 turnos enviados al modelo (recorte en cliente)
StreamingSin streaming (MVP) — respuesta completa de golpe
Arranque en modo HelpSin cambios — historial no aplica
“New chat”Botón visible solo cuando hasChat === true

Arquitectura

Frontend — alpine-components.js (helpWidget)

chatMessages[]            ← array de turnos { role, content, htmlContent }
ask()                     ← bifurca lógica tutor vs help
newChat()                 ← limpia hilo (botón "New chat")
_renderChat()             ← innerHTML directo en #help-chat-list (patrón CSP)
_stripHtmlTags(html)      ← extrae texto plano del HTML para enviar al modelo
hasChat (getter)          ← tutorMode && chatMessages.length > 0

Patrón CSP: Alpine CSP build no soporta x-html. El renderizado de burbujas se hace mediante innerHTML directo en #help-chat-list desde _renderChat(), igual que openArticle() y el bloque help-answer-body del modo Help.

Flujo tutor ask():

  1. Push del turno user al array chatMessages antes del fetch → aparece en la UI inmediatamente.
  2. POST a /api/help/ask con { mode: 'tutor', question, history: chatMessages.slice(-10) }.
  3. Push de la respuesta assistant al array.
  4. _renderChat() actualiza el DOM.
  5. El mensaje de “Thinking…” se muestra durante loading y desaparece en el re-render final.

Template — base.html

<!-- Bloque exclusivo tutorMode -->
<template x-if="tutorMode">
  <div class="help-chat">
    <div class="help-chat-bar">
      <span class="help-chat-mode-badge">IT Tutor chat</span>
      <button x-show="hasChat" @click.stop="newChat()">New chat</button>
    </div>
    <div class="help-chat-list" id="help-chat-list"></div>
  </div>
</template>

<!-- Bloques help pasan a !tutorMode -->
<template x-if="!tutorMode && loading"> … </template>
<template x-if="!tutorMode && hasAnswer"> … </template>
<template x-if="!tutorMode && hasError"> … </template>

El badge help-answer-mode-badge (s53) queda eliminado — dead code reemplazado por el badge help-chat-mode-badge dentro del hilo.

Backend — core/api_help.py

El proxy /api/help/ask extrae history del payload solo cuando mode == 'tutor' y lo reenvía al MCP workspace:

history = payload.get("history") if mode == "tutor" else None
# history se pasa a _mcp_call("bib_ask", {..., "history": history})

El recorte a 10 turnos se aplica en cliente (chatMessages.slice(-10)). El backend pasa el array sin modificar al workspace.

El workspace (commit 7406e35) introduce TutorMessage y modifica buildTutorPrompts / synthesizeAnswer / handleAsk para consumir el historial.

Estilos — help.css

Nuevas clases introducidas:

ClasePropósito
.help-chatContenedor columna del hilo (gap 8px)
.help-chat-barBarra superior: badge + botón “New chat”
.help-chat-mode-badgeBadge “IT Tutor chat” (pill naranja, --warning)
.help-chat-listLista scrollable (max-height: 420px, overflow-y: auto)
.help-chat-msgBurbuja base (padding, border-radius, font-size)
.help-chat-msg-userBurbuja usuario: alineada derecha, fondo accent translúcido
.help-chat-msg-assistantBurbuja asistente: alineada izquierda, card-bg
.help-chat-msg-loadingEstado “Thinking…”: opacity: 0.7, cursiva
.help-chat-msg-contentWrapper interno con estilos para <p>, <code>, <pre>

Eliminada: .help-answer-mode-badge (s53 — obsoleta).


Lifecycle del hilo

toggleTutor()  ──→  chatMessages = []   (cambio de modo)
close(true)    ──→  chatMessages = []   (cierre del panel)
newChat()      ──→  chatMessages = []   (acción explícita del usuario)

El flag localStorage.helpTutorMode persiste entre páginas, pero el hilo nunca persiste — es efímero por diseño.


Getters eliminados / renombrados

s53s55
isTutorAnswer → !!(answer && answer.mode==='tutor')Eliminado (dead code)
—hasChat → tutorMode && chatMessages.length > 0

Limitaciones conocidas (MVP)


Véase también

Subir