Explicacao de Decisao
Visao geral
Seção intitulada “Visao geral”Cada decisao de roteamento na Floopy ja carrega um decision_trace estruturado: candidatos, pesos, motivos de filtragem, vencedor, confianca e (a partir do v2) evidence. Esse e o registro legivel por maquina. A feature de Explicacao de Decisao renderiza o mesmo registro em um paragrafo curto de prosa simples, na lingua do cliente, sob demanda.
Intencionalmente nao e uma explicacao gerada por LLM. Ha uma taxonomia fechada de templates, um schema fechado de parametros tipados e uma funcao de render deterministica. A mesma decisao renderizada duas vezes produz a mesma prosa byte por byte.
O que voce recebe
Seção intitulada “O que voce recebe”Quando voce le uma decisao via GET /v1/decisions/{request_id} ou POST /v1/routing/explain, a resposta agora inclui:
{ "explanation": { "text": "A Floopy roteou esta requisicao para openai/gpt-5.4-mini com base em 412 amostras historicas e uma confianca moderada de 0,78. O proximo candidato pontuou dentro de 0,07 pontos e a variancia dos resultados tem se mantido estavel. Uma regressao foi registrada nos ultimos 7 dias.", "template_id": "feedback_driven_moderate_confidence" }}E a resposta carrega um cabecalho HTTP:
Content-Language: ptA locale e ecoada como cabecalho em vez de campo JSON para que o formato do wire fique compacto e a locale seja comunicada pelo mecanismo HTTP padrao.
Taxonomia de template-id
Seção intitulada “Taxonomia de template-id”As explicacoes sao compostas a partir de um conjunto fechado de 15 templates. Cada decisao escolhe exatamente um template com base na sua estrategia e resultado:
| Template id | Quando dispara |
|---|---|
cache_hit | A resposta veio do cache; nenhum router foi invocado. |
fallback_only | Todos os candidatos foram filtrados; a requisicao caiu para o modelo default. |
no_router_invoked | Um caminho de short-circuit assumiu (modelo legado, regra com candidato unico). |
feedback_driven_high_confidence | O router Feedback-Driven selecionou com confidence >= 0,8. |
feedback_driven_moderate_confidence | Selecionou com confidence em [0,5; 0,8). |
feedback_driven_low_confidence | Selecionou com confidence < 0,5. |
smart_cost_selected | O router Smart Cost escolheu o candidato mais barato que atendeu a barra de qualidade. |
constraint_rejected_max_cost_increase | O vencedor pretendido foi filtrado por max_cost_increase. |
constraint_rejected_max_regression | Filtrado por max_regression. |
constraint_rejected_min_samples | Filtrado por min_samples_before_promotion. |
constraint_rejected_cost_drop_requires_validation | Filtrado por max_cost_drop_without_validation. |
constraint_rejected_high_variance | Filtrado por max_outcome_variance. |
constraint_rejected_shadow_required | Filtrado por require_shadow_before_live. |
firewall_blocked | O firewall LLM bloqueou a requisicao antes do roteamento concluir. |
fallback | Um caminho generico de fallback assumiu (erro de provedor, circuit breaker, rate limit). |
O enum template_id e fechado e estavel. Adicionar um novo template e uma mudanca deliberada e versionada. Uma decisao cuja estrategia ainda nao esta coberta gera erro de compilacao no gateway, nao um fallback em runtime para “nao sei”.
Como a locale e resolvida
Seção intitulada “Como a locale e resolvida”A locale usada no texto renderizado e resolvida em tempo de leitura, em cada requisicao, na seguinte ordem:
- O cabecalho HTTP
Accept-Languageda requisicao. - Se ausente, malformado ou de outra forma nao parseavel:
en.
Nao ha locale default por organizacao nem preferencia por usuario embutida na resposta. A decisao depende exclusivamente do cabecalho da requisicao. A locale resolvida e entao ecoada pelo cabecalho de resposta Content-Language para que clientes possam verificar qual locale receberam.
O parser de Accept-Language e endurecido contra abuso:
- O cabecalho e limitado a 256 bytes.
- Apenas ASCII — bytes nao-ASCII disparam fallback para
en. - Uma allowlist regex estrita rejeita fatores de qualidade malformados e locales desconhecidas.
- As locales suportadas sao
enept; qualquer outra cai paraen.
O contrato de i18n
Seção intitulada “O contrato de i18n”A funcao de render e um match exaustivo sobre (template_id, locale). Todo template suportado tem uma string para toda locale suportada, garantido em tempo de compilacao. Adicionar uma traducao pt faltante ou desatualizada quebra o build, nao gera fallback em runtime.
O conjunto atual de locales e {en, pt}. Adicionar uma nova locale e um release explicito do gateway, nao um toggle de configuracao.
O que o texto nunca contem
Seção intitulada “O que o texto nunca contem”Este e o contrato mais importante: o texto da explicacao nunca referencia o conteudo do prompt. A funcao de render recebe um par (template_id, params) onde params e um sub-schema fechado de escalares tipados — contagens de amostras, valores de confianca, gap para o segundo colocado, contagem bucketizada de regressoes e identificadores de provider/model. Nenhum desses campos carrega texto controlado pelo chamador.
Strings de provider e model passam por um sanitizador antes de serem interpoladas no template:
- Qualquer coisa fora de
[a-zA-Z0-9._/-]e descartada. - O resultado e limitado para que um nome de modelo hostil nao exploda a prosa.
A saida tambem e defensivamente truncada em 600 caracteres na borda do renderer, testada com fuzz contra caracteres de controle e bloqueada contra caracteres de controle de Markdown. O dashboard renderiza o texto dentro de um <p> como texto plano — sem HTML, sem Markdown.
Por que renderizar em tempo de leitura
Seção intitulada “Por que renderizar em tempo de leitura”Duas razoes.
Primeiro, seguranca e armazenamento: se a prosa fosse persistida em decision_trace, cada byte de cada decisao carregaria uma superficie de ataque para injecao de texto armazenado para sempre. Mantendo os artefatos persistidos estritamente tipados (explanation_template_id mais explanation_params), a linha de auditoria fica pequena, amigavel a maquina e livre de texto livre.
Segundo, internacionalizacao: renderizar em tempo de leitura significa que um cliente que le a mesma decisao em en hoje e em pt amanha recebe dois paragrafos diferentes a partir de uma unica decisao armazenada. Nao ha passo de migracao quando lancamos uma nova locale ou refinamos um template — toda decisao existente passa a refletir a nova prosa imediatamente.
Onde voce nao vera texto de explicacao
Seção intitulada “Onde voce nao vera texto de explicacao”O campo de explicacao nao e adicionado a:
GET /v1/decisions(endpoint de listagem) — muito verboso para uma lista paginada.GET /v1/export/decisions— exportacoes sao consumidas por maquina; otemplate_idpersistido fica intencionalmente fora do allowlist da exportacao para que o formato do wire seja estavel entre clientes independentemente de suporte a locale.
Se voce quer a explicacao, pegue a decisao individual: GET /v1/decisions/{request_id}.
Defaults do shadow no onboarding
Seção intitulada “Defaults do shadow no onboarding”O passo “shadow setup” do onboarding da Floopy usa o catalogo interno de modelos da Floopy para escolher uma alternativa mais barata sensata para o modelo que o cliente acabou de conectar. Quando o experimento desse passo grava as primeiras decisoes, o template de explicacao tipicamente sera um dos ramos de Smart Cost ou Feedback-Driven, dependendo se o cliente ja esta usando roteamento Feedback-Driven.
Veja tambem
Seção intitulada “Veja tambem”- GET /v1/decisions/{request_id} — decisao individual com explicacao.
- POST /v1/routing/explain — dry-run com explicacao.
- Metodologia de Confianca — o que os numeros de confianca na prosa significam.
- Feature de Constraints — os motivos de constraint que disparam os templates
constraint_rejected.