iachat/progresso/2026-02-24_faq_guardrail_e_orquestracao.md

75 lines
3.8 KiB
Markdown

# Ajuste de Orquestração + Guardrail de FAQ (Captain/Jasmine)
**Data:** 24/02/2026
**Autor:** Codex (AI Developer)
## Objetivo
Garantir que o agente **não responda “não sei / não tenho acesso” sem antes consultar FAQ**, e evitar que a conversa fique presa em um cenário que ignora FAQ.
## Problema Observado
- Em algumas conversas, o agente respondia corretamente usando FAQ (ex.: preços), mas em outras respostas dizia que “não tinha acesso” (ex.: senha do Wi-Fi).
- Isso gerava comportamento inconsistente e perda de confiança.
## Causa-Raiz
1. **Sticky de cenário no histórico**
O runner podia iniciar o turno no último `agent_name` do histórico (ex.: cenário de disponibilidade), em vez de iniciar no orquestrador principal.
2. **Guardrail com detecção ampla demais de `faq_lookup`**
A checagem considerava `faq_lookup` em `result.messages` inteiro.
Se houve `faq_lookup` em turno anterior, isso podia bloquear o fallback no turno atual.
## O que foi implementado
### 1) Início de turno sempre pelo orquestrador
- O contexto da execução foi padronizado para iniciar no agente principal (`current_agent` do assistente).
- O `conversation_history` enviado ao runner zera `agent_name` das mensagens para evitar lock indevido em cenário.
### 2) Guardrail de FAQ para respostas de incerteza
- Foi criado um guardrail em `AgentRunnerService`:
- Detecta respostas de incerteza (`não sei`, `não tenho`, etc.).
- Se **não houve `faq_lookup` no turno atual**, dispara fallback de FAQ via busca semântica (`@assistant.responses.approved.search(query)`).
- Se encontrar resposta no FAQ, substitui a resposta final.
- Se não encontrar, responde explicitamente:
- “Consultei o FAQ e não encontrei essa informação cadastrada ainda...”
- Evita resposta genérica “não sei” sem contexto.
### 3) Correção de escopo do `faq_lookup`
- A verificação agora considera apenas mensagens **após a última pergunta do usuário do turno atual**.
- Isso evita falso positivo de `faq_lookup` antigo.
## Arquivos Alterados
- `/Users/user/Dev/Produtos/Chatwoot-fazer-ai/fazer-ai-kanban/chatwoot/enterprise/app/services/captain/assistant/agent_runner_service.rb`
- `/Users/user/Dev/Produtos/Chatwoot-fazer-ai/fazer-ai-kanban/chatwoot/spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb`
## Testes e Validação
- Executado:
- `bundle exec rspec spec/enterprise/services/captain/assistant/agent_runner_service_spec.rb`
- Resultado:
- **29 examples, 0 failures**
Casos cobertos em teste:
- fallback aplicado quando resposta incerta vem sem `faq_lookup`;
- não aplica fallback quando `faq_lookup` ocorreu no turno atual;
- aplica fallback quando `faq_lookup` ocorreu apenas em turno anterior;
- retorna mensagem explícita de “FAQ não encontrado” quando busca não acha nada.
## Observabilidade (logs)
Foram mantidos logs para rastrear guardrail:
- Trigger do guardrail com query;
- Quantidade de resultados encontrados na busca FAQ;
- Falha de fallback (se houver exception).
## Impacto Prático
- O agente deixa de “desistir” sem consultar conhecimento.
- Reduz respostas incoerentes entre perguntas parecidas.
- Mantém handoff para humano apenas quando realmente necessário.
## Limitações Atuais
- Se o FAQ estiver mal cadastrado ou com baixa qualidade semântica, o fallback ainda pode não encontrar resposta.
- O guardrail atua em respostas de incerteza; não corrige respostas erradas factualmente quando o modelo responde com confiança indevida.
## Próximos Passos Recomendados
1. Revisar/normalizar entradas de FAQ críticas (Wi-Fi, horários, políticas, preços).
2. Adicionar métrica de “fallback guardrail acionado” por inbox/assistente.
3. Opcional: forçar política “FAQ-first” para intents informativas antes de responder livremente.