Diego Souza · AI Engineer

Todos os projetos

SupportOps

Uma plataforma de suporte em que a IA pode rascunhar um reembolso, mas só uma segunda pessoa pode aprová-lo.

verificado offline, smoke live pendenteÚltima verificação: 2026-10-31 · Commit 6e51483

Resumo

Problema
Automação de suporte cruza dados de tenant, ferramentas, trabalho em segundo plano e dinheiro. Um modelo que aprova o próprio reembolso é um risco, então o sistema precisa tornar isso impossível por construção, não por prompt.
Meu papel
Projetei e construí a API, a busca, o fluxo de aprovação, o caminho live contabilizado e o console, com agentes de código sob um harness que começa pela spec.
Stack
Python, FastAPI, PostgreSQL, pgvector, Redis, MCP, Keycloak, React, TypeScript, Fly.io, Cloudflare Pages
Resultado
179 tests passed na última execução registrada da suíte, e 17 passed para o ledger live e o failover de chaves, rodados contra PostgreSQL 16. O demo live em si ainda não foi medido.

Tickets chegam, um time de agentes faz a triagem sobre conhecimento isolado por tenant, e cada resposta cita suas fontes. Dinheiro é a linha que o modelo não cruza: ele propõe, um gate determinístico confere e uma pessoa decide. Quem pediu o reembolso não pode aprová-lo. Abra o console e tente.

Veja rodando

O demo ao vivo roda este sistema contra um modelo real e mostra cada passo.

Demo ao vivo

Como funciona

Um reembolso que precisa de uma segunda pessoaFluxo: Ticket, Tenant do token, Busca por tenant, Quatro agentes, Ledger de orçamento, Gate de reembolso, Segunda pessoa, Reembolso simuladoTicketTenant do tokenBusca por tenantQuatro agentesLedger de orçamentoGate de reembolsoSegunda pessoaReembolso simuladoUm reembolso que precisa de uma segunda pessoaFluxo: Ticket, Tenant do token, Busca por tenant, Quatro agentes, Ledger de orçamento, Gate de reembolso, Segunda pessoa, Reembolso simuladoTicketTenant do tokenBusca por tenantQuatro agentesLedger de orçamentoGate de reembolsoSegunda pessoaReembolso simulado

O fluxo inteiro. Aperte tocar ou próximo para percorrer.

Versão em texto deste diagrama
  1. Ticket P06. O console envia o ticket com o token do usuário.
    • depois Tenant do token
  2. Tenant do token P05. Tenant e papel são lidos do token; a requisição não escolhe.
    • depois Busca por tenant
  3. Busca por tenant P05. A busca só enxerga o tenant do ticket, e as respostas citam fontes.
    • depois Quatro agentes
  4. Quatro agentes P01. Classificador, pesquisador, billing e responder, com limite de passos.
    • depois Ledger de orçamento
  5. Ledger de orçamento P05. O custo é reservado no Postgres antes da chamada e falha fechado no teto.
    • depois Gate de reembolso (proposta)
  6. Gate de reembolso P05. A proposta vira um pedido de aprovação por uma ferramenta determinística.
    • depois Segunda pessoa
  7. Segunda pessoa P06. Um aprovador diferente de quem pediu decide.
    • depois Reembolso simulado (aprovado)
  8. Reembolso simulado P05. Um pedido aprovado cria um reembolso simulado. Nada real se move.

Decisões

Tenant e papel vêm do token

A API lê tenant e papel do token assinado. Um token de navegador pode carregar escopos que o papel do usuário não pode usar, então os escopos são reduzidos por papel no servidor.

Código: supportops_platform/api/app.py

Descartado: Confiar em um tenant id ou lista de escopos enviados pelo cliente.

O modelo propõe; um gate e uma pessoa decidem

Um pedido de reembolso passa por uma ferramenta determinística e vira uma aprovação que outro principal precisa decidir. O valor vem dessa ferramenta, não do modelo.

Código: supportops_platform/service_runtime.py

Descartado: Deixar a saída do modelo definir o valor ou aprovar.

Reservar o custo antes da chamada

Cada chamada ao modelo reserva o custo do pior caso em um ledger no Postgres, com uma única instrução atômica que também confere o teto. Uma resposta de limite de taxa passa para a próxima chave e não cobra nada; uma chamada incerta mantém a reserva.

Código: supportops_platform/agents/demo_live.py

Descartado: Somar o gasto depois do fato.

Resultados

Medido

Medido
MétricaValorModoFonte
Suíte de testes do P05 na fase 033179 tests passedfake: lógica de controle.planning/phases/033-console-blockers/VERIFY.mdCommit d859a50 · 2026-10-31 · Exit code 0
Testes do ledger e do failover do demo-live-v1 contra PostgreSQL 16 Transporte de provedor falso. Nenhum modelo foi chamado.17 passedserviços, modelo fakeartifacts/evidence/demo-live-v1/pytest-demo-live-postgres.txtCommit ffcdb81 · 2026-10-31 · Exit code 0
Quem pediu não aprova o próprio reembolso; um tenant não lê o ticket de outro2 passedfake: lógica de controleartifacts/evidence/portfolio-w7/dual-control-tests.txtCommit 6e51483 · 2026-10-31 · Exit code 0

Não medido, então não afirmado

  • Qualidade semântica das respostas. Fixtures de busca provam fluxo de controle, não significado.
  • Custo e latência do demo live. Ainda não existe execução live pública.
  • Vazão sob carga.

Galeria de falhas

O que aconteceu
O primeiro caminho contabilizado parava todo trabalho seguinte depois de qualquer tentativa que falhasse.
Causa
Foi feito para uma operação local revisada, em que uma falha deve bloquear até alguém olhar. Um serviço público não funciona assim.
O que mudou
O caminho público libera a reserva quando o provedor respondeu com erro, e só a mantém quando o resultado é desconhecido.
O que aconteceu
Um clone limpo não conseguia instalar o projeto.
Causa
Um arquivo de ignore excluía todas as wheels geradas localmente, inclusive as que o lock aponta.
O que mudou
As cinco wheels que o lock cita estão versionadas e seus hashes conferidos contra o lock.
O que aconteceu
A triagem falhava no console com uma falha genérica do worker.
Causa
O worker roda com os escopos de quem chama, e o console não pedia os escopos que as ferramentas precisam.
O que mudou
Os escopos do console agora seguem os do agente, com um aviso para atualizar os dois ao adicionar uma ferramenta.

Valide você mesmo

git clone https://github.com/DiegoVSouza/supportops-platform.git
cd supportops-platform
uv sync --locked && uv run --locked pytest

Esperado: A suíte passa. O gate de serviços (`scripts/check_full.py`) precisa de Docker.

O que eu faria diferente

O valor do reembolso é um valor fixo da ferramenta. Eu deixaria a proposta carregar um valor limitado lido do ticket, para o trabalho do modelo aparecer na decisão sobre dinheiro.

Próximo projeto

Foundation e evals live