434 lines
15 KiB
Markdown
434 lines
15 KiB
Markdown
# 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:
|
|
1. **Injects the prompt** into every LLM request
|
|
2. **The prompt** can be:
|
|
- **Standard**: References a predefined prompt by ID (`"tables"`, `"cards"`, etc.)
|
|
- **Custom**: Any custom instruction string (`"sempre mostrar 10 perguntas"`)
|
|
3. **Influences** the AI response format
|
|
4. **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
|
|
```html
|
|
<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:
|
|
```html
|
|
<footer>
|
|
<div class="switchers-container" id="switchers"></div>
|
|
<div class="suggestions-container" id="suggestions"></div>
|
|
<!-- ... existing form ... -->
|
|
</footer>
|
|
```
|
|
|
|
### CSS Styling
|
|
|
|
#### Container
|
|
```css
|
|
.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)
|
|
```css
|
|
.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
|
|
```javascript
|
|
// 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
|
|
```javascript
|
|
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
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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
|
|
|
|
```basic
|
|
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:
|
|
|
|
```basic
|
|
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
|
|
|
|
1. ✅ Create prompts/switcher.md (this file)
|
|
2. ⬜ Define predefined prompts in backend (map IDs to prompt strings)
|
|
3. ⬜ Add HTML structure to chat.html (switchers container)
|
|
4. ⬜ Add CSS styles to chat.css (switcher chip styles)
|
|
5. ⬜ Add switcher definitions to chat.js
|
|
6. ⬜ Implement renderSwitchers() function
|
|
7. ⬜ Implement toggleSwitcher() function
|
|
8. ⬜ Modify sendMessage() to prepend switcher prompts
|
|
9. ⬜ Update salesianos bot start.bas to use ADD SWITCHER commands
|
|
10. ⬜ Test locally with all switcher options
|
|
11. ⬜ Verify multiple switchers can be active simultaneously
|
|
12. ⬜ 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
|
|
|
|
1. `botui/ui/suite/chat/chat.html` - Add switcher container HTML
|
|
2. `botui/ui/suite/chat/chat.css` - Add switcher styles
|
|
3. `botui/ui/suite/chat/chat.js` - Add switcher logic
|
|
4. `botserver/bots/salesianos/start.bas` - Add ADD SWITCHER commands
|
|
|
|
## Example start.bas
|
|
|
|
```basic
|
|
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
|