15 KiB
15 KiB
SWITCHER Feature - Response Format Modifiers
Overview
Add a switcher interface that allows users to toggle response modifiers that influence how the AI generates responses. Unlike suggestions (which are one-time actions), switchers are persistent toggles that remain active until deactivated.
Location
botui/ui/suite/chat/ - alongside existing suggestion buttons
Syntax
Standard Switcher (predefined prompt)
ADD SWITCHER "tables" AS "Tabelas"
Custom Switcher (with custom prompt)
ADD SWITCHER "sempre mostrar 10 perguntas" AS "Mostrar Perguntas"
What Switcher Does
The switcher:
- Injects the prompt into every LLM request
- The prompt can be:
- Standard: References a predefined prompt by ID (
"tables","cards", etc.) - Custom: Any custom instruction string (
"sempre mostrar 10 perguntas")
- Standard: References a predefined prompt by ID (
- Influences the AI response format
- Persists until toggled OFF
Available Standard Switchers
| ID | Label | Color | Description |
|---|---|---|---|
| tables | Tabelas | #4CAF50 | Format responses as tables |
| infographic | Infográfico | #2196F3 | Visual, graphical representations |
| cards | Cards | #FF9800 | Card-based layout |
| list | Lista | #9C27B0 | Bulleted lists |
| comparison | Comparação | #E91E63 | Side-by-side comparisons |
| timeline | Timeline | #00BCD4 | Chronological ordering |
| markdown | Markdown | #607D8B | Standard markdown |
| chart | Gráfico | #F44336 | Charts and diagrams |
Predefined Prompts (Backend)
Each standard ID maps to a predefined prompt in the backend:
ID: tables
Prompt: "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML usando <table>, <thead>, <tbody>, <tr>, <th>, <td>. Cada dado deve ser uma célula. Use cabeçalhos claros na primeira linha. Se houver dados numéricos, alinhe à direita. Se houver texto, alinhe à esquerda. Use cores sutis em linhas alternadas (nth-child). NÃO use markdown tables, use HTML puro."
ID: infographic
Prompt: "REGRAS DE FORMATO: Crie representações visuais HTML usando SVG, progress bars, stat cards, e elementos gráficos. Use elementos como: <svg> para gráficos, <div style="width:X%;background:color"> para barras de progresso, ícones emoji, badges coloridos. Organize informações visualmente com grids, flexbox, e espaçamento. Inclua legendas e rótulos visuais claros."
ID: cards
Prompt: "REGRAS DE FORMATO: Retorne informações em formato de cards HTML. Cada card deve ter: <div class="card" style="border:1px solid #ddd;border-radius:8px;padding:16px;margin:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1)">. Dentro do card use: título em <h3> ou <strong>, subtítulo em <p> style="color:#666", ícone emoji ou ícone SVG no topo, badges de status. Organize cards em grid usando display:grid ou flex-wrap."
ID: list
Prompt: "REGRAS DE FORMATO: Use apenas listas HTML: <ul> para bullets e <ol> para números numerados. Cada item em <li>. Use sublistas aninhadas quando apropriado. NÃO use parágrafos de texto, converta tudo em itens de lista. Adicione ícones emoji no início de cada <li> quando possível. Use classes CSS para estilização: .list-item, .sub-list."
ID: comparison
Prompt: "REGRAS DE FORMATO: Crie comparações lado a lado em HTML. Use grid de 2 colunas: <div style="display:grid;grid-template-columns:1fr 1fr;gap:20px">. Cada lado em uma <div class="comparison-side"> com borda colorida distinta. Use headers claros para cada lado. Adicione seção de "Diferenças Chave" com bullet points. Use cores contrastantes para cada lado (ex: azul vs laranja). Inclua tabela de comparação resumida no final."
ID: timeline
Prompt: "REGRAS DE FORMATO: Organize eventos cronologicamente em formato de timeline HTML. Use <div class="timeline"> com border-left vertical. Cada evento em <div class="timeline-item"> com: data em <span class="timeline-date" style="font-weight:bold;color:#666">, título em <h3>, descrição em <p>. Adicione círculo indicador na timeline line. Ordene do mais antigo para o mais recente. Use espaçamento claro entre eventos."
ID: markdown
Prompt: "REGRAS DE FORMATO: Use exclusivamente formato Markdown padrão. Sintaxe permitida: **negrito**, *itálico*, `inline code`, ```bloco de código```, # cabeçalhos, - bullets, 1. números, [links](url), , | tabela | markdown |. NÃO use HTML tags exceto para blocos de código. Siga estritamente a sintaxe CommonMark."
ID: chart
Prompt: "REGRAS DE FORMATO: Crie gráficos e diagramas em HTML SVG. Use elementos SVG: <svg width="X" height="Y">, <line> para gráficos de linha, <rect> para gráficos de barra, <circle> para gráficos de pizza, <path> para gráficos de área. Inclua eixos com labels, grid lines, legendas. Use cores distintas para cada série de dados (ex: vermelho, azul, verde). Adicione tooltips com valores ao hover. Se o usuário pedir gráfico de pizza com "pizza vermelha", use fill="#FF0000" no SVG."
UI Design
HTML Structure
<div class="switchers-container" id="switchers">
<div class="switchers-label">Formato:</div>
<div class="switchers-chips" id="switchersChips">
<!-- Switcher chips will be rendered here -->
</div>
</div>
Placement
Position the switchers container above the suggestions container:
<footer>
<div class="switchers-container" id="switchers"></div>
<div class="suggestions-container" id="suggestions"></div>
<!-- ... existing form ... -->
</footer>
CSS Styling
Container
.switchers-container {
display: flex;
align-items: center;
gap: 12px;
padding: 8px 16px;
flex-wrap: wrap;
background: rgba(0, 0, 0, 0.02);
border-top: 1px solid rgba(0, 0, 0, 0.05);
}
.switchers-label {
font-size: 13px;
font-weight: 600;
color: #666;
text-transform: uppercase;
letter-spacing: 0.5px;
}
Switcher Chips (Toggle Buttons)
.switchers-chips {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.switcher-chip {
display: flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
border-radius: 20px;
border: 2px solid transparent;
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
background: rgba(0, 0, 0, 0.05);
color: #666;
user-select: none;
}
.switcher-chip:hover {
background: rgba(0, 0, 0, 0.08);
transform: translateY(-1px);
}
.switcher-chip.active {
border-color: currentColor;
background: currentColor;
color: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.switcher-chip-icon {
font-size: 14px;
}
JavaScript Implementation
State Management
// Track active switchers
var activeSwitchers = new Set();
// Switcher definitions (from ADD SWITCHER commands in start.bas)
var switcherDefinitions = [
{
id: 'tables',
label: 'Tabelas',
icon: '📊',
color: '#4CAF50'
},
{
id: 'infographic',
label: 'Infográfico',
icon: '📈',
color: '#2196F3'
},
{
id: 'cards',
label: 'Cards',
icon: '🃏',
color: '#FF9800'
},
{
id: 'list',
label: 'Lista',
icon: '📋',
color: '#9C27B0'
},
{
id: 'comparison',
label: 'Comparação',
icon: '⚖️',
color: '#E91E63'
},
{
id: 'timeline',
label: 'Timeline',
icon: '📅',
color: '#00BCD4'
},
{
id: 'markdown',
label: 'Markdown',
icon: '📝',
color: '#607D8B'
},
{
id: 'chart',
label: 'Gráfico',
icon: '📉',
color: '#F44336'
}
];
Render Switchers
function renderSwitchers() {
var container = document.getElementById("switcherChips");
if (!container) return;
container.innerHTML = switcherDefinitions.map(function(sw) {
var isActive = activeSwitchers.has(sw.id);
return (
'<div class="switcher-chip' + (isActive ? ' active' : '') + '" ' +
'data-switch-id="' + sw.id + '" ' +
'style="--switcher-color: ' + sw.color + '; ' +
(isActive ? 'color: ' + sw.color + ' background: ' + sw.color + '; ' : '') +
'">' +
'<span class="switcher-chip-icon">' + sw.icon + '</span>' +
'<span>' + sw.label + '</span>' +
'</div>'
);
}).join('');
// Add click handlers
container.querySelectorAll('.switcher-chip').forEach(function(chip) {
chip.addEventListener('click', function() {
toggleSwitcher(this.getAttribute('data-switch-id'));
});
});
}
Toggle Handler
function toggleSwitcher(switcherId) {
if (activeSwitchers.has(switcherId)) {
activeSwitchers.delete(switcherId);
} else {
activeSwitchers.add(switcherId);
}
renderSwitchers();
}
Message Enhancement
When sending a user message, prepend active switcher prompts:
function sendMessage(messageContent) {
// ... existing code ...
var content = messageContent || input.value.trim();
if (!content) return;
// Prepend active switcher prompts
var enhancedContent = content;
if (activeSwitchers.size > 0) {
// Get prompts for active switchers from backend
var activePrompts = [];
activeSwitchers.forEach(function(id) {
// Backend has predefined prompts for each ID
activePrompts.push(getSwitcherPrompt(id));
});
// Inject prompts before user message
if (activePrompts.length > 0) {
enhancedContent = activePrompts.join('\n\n') + '\n\n---\n\n' + content;
}
}
// Send enhanced content
addMessage("user", content);
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({
bot_id: currentBotId,
user_id: currentUserId,
session_id: currentSessionId,
channel: "web",
content: enhancedContent,
message_type: MessageType.USER,
timestamp: new Date().toISOString(),
}));
}
}
function getSwitcherPrompt(switcherId) {
// Get predefined prompt from backend or API
// For example, tables ID maps to:
// "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML..."
var switcher = switcherDefinitions.find(function(s) { return s.id === switcherId; });
if (!switcher) return "";
// This could be fetched from backend or stored locally
return SWITCHER_PROMPTS[switcherId] || "";
}
Bot Integration (start.bas)
The bot receives the switcher prompt injected into the user message and simply passes it to the LLM.
Example in start.bas
REM Switcher prompts are automatically injected by frontend
REM Just pass user_input to LLM - no parsing needed!
REM If user types: "mostra os cursos"
REM And "Tabelas" switcher is active
REM Frontend sends: "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML... --- mostra os cursos"
REM Bot passes directly to LLM:
response$ = CALL_LLM(user_input)
REM The LLM will follow the REGRAS DE FORMATO instructions
Multiple Active Switchers
When multiple switchers are active, all prompts are injected:
REM Frontend injects multiple REGRAS DE FORMATO blocks
REM Example with "Tabelas" and "Gráfico" active:
REM
REM "REGRAS DE FORMATO: SEMPRE retorne suas respostas em formato de tabela HTML...
REM REGRAS DE FORMATO: Crie gráficos e diagramas em HTML SVG...
REM ---
REM mostra os dados de vendas"
REM Bot passes to LLM:
response$ = CALL_LLM(user_input)
Implementation Steps
- ✅ Create prompts/switcher.md (this file)
- ⬜ Define predefined prompts in backend (map IDs to prompt strings)
- ⬜ Add HTML structure to chat.html (switchers container)
- ⬜ Add CSS styles to chat.css (switcher chip styles)
- ⬜ Add switcher definitions to chat.js
- ⬜ Implement renderSwitchers() function
- ⬜ Implement toggleSwitcher() function
- ⬜ Modify sendMessage() to prepend switcher prompts
- ⬜ Update salesianos bot start.bas to use ADD SWITCHER commands
- ⬜ Test locally with all switcher options
- ⬜ Verify multiple switchers can be active simultaneously
- ⬜ Test persistence across page refreshes (optional - localStorage)
Testing Checklist
- Switchers appear above suggestions
- Switchers are colorful and match their defined colors
- Clicking a switcher toggles it on/off
- Multiple switchers can be active simultaneously
- Active switchers have distinct visual state (border, background, shadow)
- Formatted responses match the selected format
- Toggling off removes the format modifier
- Works with empty active switchers (normal response)
- Works in combination with suggestions
- Responsive design on mobile devices
Files to Modify
botui/ui/suite/chat/chat.html- Add switcher container HTMLbotui/ui/suite/chat/chat.css- Add switcher stylesbotui/ui/suite/chat/chat.js- Add switcher logicbotserver/bots/salesianos/start.bas- Add ADD SWITCHER commands
Example start.bas
USE_WEBSITE("https://salesianos.br", "30d")
USE KB "carta"
USE KB "proc"
USE TOOL "inscricao"
USE TOOL "consultar_inscricao"
USE TOOL "agendamento_visita"
USE TOOL "informacoes_curso"
USE TOOL "documentos_necessarios"
USE TOOL "contato_secretaria"
USE TOOL "calendario_letivo"
ADD_SUGGESTION_TOOL "inscricao" AS "Fazer Inscrição"
ADD_SUGGESTION_TOOL "consultar_inscricao" AS "Consultar Inscrição"
ADD_SUGGESTION_TOOL "agendamento_visita" AS "Agendar Visita"
ADD_SUGGESTION_TOOL "informacoes_curso" AS "Informações de Cursos"
ADD_SUGGESTION_TOOL "documentos_necessarios" AS "Documentos Necessários"
ADD_SUGGESTION_TOOL "contato_secretaria" AS "Falar com Secretaria"
ADD_SUGGESTION_TOOL "segunda_via" AS "Segunda Via de Boleto"
ADD_SUGGESTION_TOOL "calendario_letivo" AS "Calendário Letivo"
ADD_SUGGESTION_TOOL "outros" AS "Outros"
ADD SWITCHER "tables" AS "Tabelas"
ADD SWITCHER "infographic" AS "Infográfico"
ADD SWITCHER "cards" AS "Cards"
ADD SWITCHER "list" AS "Lista"
ADD SWITCHER "comparison" AS "Comparação"
ADD SWITCHER "timeline" AS "Timeline"
ADD SWITCHER "markdown" AS "Markdown"
ADD SWITCHER "chart" AS "Gráfico"
TALK "Olá! Sou o assistente virtual da Escola Salesiana. Como posso ajudá-lo hoje com inscrições, visitas, informações sobre cursos, documentos ou calendário letivo? Você pode também escolher formatos de resposta acima da caixa de mensagem."
Notes
- Switchers are persistent until deactivated
- Multiple switchers can be active at once
- Switcher prompts are prepended to user messages with "---" separator
- The backend (LLM) should follow these format instructions
- UI should provide clear visual feedback for active switchers
- Color coding helps users quickly identify active formats
- Standard switchers use predefined prompts in backend
- Custom switchers allow any prompt string to be injected