Uma POC real, construída em poucas horas, com TypeSafe AI, fallback e arquitetura vertical slice

Um ticket diz que o checkout parou, há clientes esperando e o assunto veio marcado como “dúvida”. Seu backend precisa decidir três coisas: isso é urgente, qual time assume e qual o nível de prioridade.
Pedir a um LLM para “analisar o ticket e responder em JSON” funciona na demo. Em produção, a pergunta importante vem depois: quem garante que o departamento existe, que a probabilidade está no intervalo certo e que uma indisponibilidade do modelo não impede o ticket de ser criado?
Foi esse problema que eu usei para testar o JEV, modelo da TypeSafe AI voltado a decisões estruturadas, por meio do Vercel AI Gateway. A implementação entrou em uma fatia vertical FastAPI real: router, service, repository, contrato de avaliação e testes de degradação. Foi uma POC funcional feita durante algumas horas de trabalho, não um benchmark de produção nem uma promessa de acurácia.
Minha conclusão é específica: JEV é interessante quando seu software precisa de um julgamento estreito e probabilístico, mas a aplicação deve continuar dona do contrato, da política e do fallback. Tipar a resposta reduz uma classe de erros. Não transforma uma inferência em verdade.
state e perguntas tipadas; devolve respostas que o código consegue consumir sem extrair texto livre.choice, score e noul. Noul representa a probabilidade de uma resposta “sim”.typesafe-ai/jev no endpoint /v1/evaluate.A TypeSafe chama JEV de seu primeiro modelo “System One”: um modelo para decisões rápidas e estruturadas. Em vez de pedir prosa e tentar recuperar estrutura depois, você envia o estado do problema e perguntas com tipos conhecidos.
Os primitivos são:
| Tipo | Pergunta que resolve | Resposta útil ao código |
|---|---|---|
choice | Qual opção definida se aplica? | opção escolhida, probabilidades por opção e confiança |
score | Em qual nível ordenado o caso cai? | score, probabilidades por nível e confiança |
noul | Qual a probabilidade de “sim”? | número entre 0 e 1 |
O nome incomum noul importa porque copiar um contrato como boolean quebraria a integração. No Gateway, a página do modelo descreve capacidades como Choice, Score e Boolean; na API de avaliação e na documentação TypeSafe, o tipo usado no payload é noul. O contrato da API vence o rótulo de marketing.
JEV não é:
if, regex, enum ou consulta ao banco resolve o caso de forma determinística.Use código comum para fatos. Considere um modelo de decisão quando o input é ambíguo e a saída possível já é conhecida.
Este é um exemplo didático, adaptado da minha implementação. Requer Python 3.11+, httpx e uma chave do Vercel AI Gateway.
python -m venv .venv
source .venv/bin/activate
pip install "httpx>=0.27,<1"
export AI_GATEWAY_API_KEY="sua-chave"
Crie jev_demo.py:
import os
from typing import Any
import httpx
GATEWAY_URL = "https://ai-gateway.vercel.sh"
MODEL = "typesafe-ai/jev"
QUESTIONS = {
"is_urgent": {
"type": "noul",
"instructions": "Does this ticket require immediate attention?",
},
"department": {
"type": "choice",
"instructions": "Which team should handle this ticket?",
"criteria": {
"billing": "Payments, invoices, refunds, or charges",
"technical": "Bugs, outages, integrations, or product errors",
"shipping": "Delivery, tracking, or logistics",
"general": "Anything outside the other departments",
},
},
"urgency": {
"type": "score",
"instructions": "How urgent is this ticket?",
"criteria": ["low", "medium", "high"],
},
}
def evaluate_ticket(subject: str, message: str) -> dict[str, Any]:
api_key = os.environ["AI_GATEWAY_API_KEY"]
payload = {
"model": MODEL,
"state": {"subject": subject, "message": message},
"questions": QUESTIONS,
}
with httpx.Client(timeout=10.0) as client:
response = client.post(
f"{GATEWAY_URL}/v1/evaluate",
headers={"Authorization": f"Bearer {api_key}"},
json=payload,
)
response.raise_for_status()
return response.json()["answers"]
if __name__ == "__main__":
answers = evaluate_ticket(
subject="Checkout fora do ar",
message="Pagamentos falham há 20 minutos; 42 pedidos estão bloqueados.",
)
print(answers)
Execute:
python jev_demo.py
O formato exato inclui metadados definidos pelo serviço, mas o consumo relevante segue este desenho:
{
"is_urgent": {"noul": 0.97},
"department": {
"choice": "technical",
"probabilities": {
"billing": 0.01,
"technical": 0.96,
"shipping": 0.0,
"general": 0.03
}
},
"urgency": {"score": 2.8}
}
Valores acima são ilustrativos, não output prometido. O ganho estrutural está em conhecer os campos possíveis antes da requisição. Seu código ainda deve validar a resposta real.
Pense no JEV como um sensor. Um sensor mede e carrega incerteza; ele não decide sozinho se uma esteira industrial deve parar. O controlador lê a medição, verifica limites, considera outras condições e escolhe a ação segura.
Aqui, o modelo produz julgamento:
ticket → {urgente: 0.97, departamento: technical, score: 2.8}
A aplicação produz política:
if result.is_urgent >= 0.90 and result.confidence >= 0.80:
page_on_call()
elif result.is_urgent >= 0.60:
enqueue_human_review()
else:
follow_normal_queue()
Os cortes pertencem ao seu domínio. Eles não devem ficar escondidos em prompt, resposta do modelo ou regra do provedor.
Na PR privada do POC FastAPI, implementei um CRUD de tickets no mesmo padrão vertical já usado pelo projeto. O link registra a origem do estudo, mas o conteúdo não é acessível sem permissão. Por isso, os trechos úteis e sanitizados estão reproduzidos aqui; nenhuma credencial ou configuração privada foi copiada.
O JevClient é um adapter fino. Ele conhece URL, autenticação, timeout e envelope HTTP. O service conhece o caso de uso e decide o que fazer quando o adapter falha.
from typing import Any
import httpx
class JevEvaluationError(RuntimeError):
pass
class JevClient:
def __init__(
self,
*,
api_key: str,
model: str = "typesafe-ai/jev",
base_url: str = "https://ai-gateway.vercel.sh",
timeout: float = 10.0,
) -> None:
self.api_key = api_key
self.model = model
self.base_url = base_url.rstrip("/")
self.timeout = timeout
def evaluate(self, state: Any, questions: dict[str, Any]) -> dict[str, Any]:
try:
with httpx.Client(timeout=self.timeout) as client:
response = client.post(
f"{self.base_url}/v1/evaluate",
headers={"Authorization": f"Bearer {self.api_key}"},
json={
"model": self.model,
"state": state,
"questions": questions,
},
)
response.raise_for_status()
body = response.json()
except (httpx.HTTPError, ValueError, KeyError) as error:
raise JevEvaluationError("evaluation unavailable") from error
answers = body.get("answers")
if not isinstance(answers, dict):
raise JevEvaluationError("invalid evaluation contract")
return answers
O fluxo do POST /tickets preserva a função principal mesmo sem IA:
Esse detalhe mudou o risco do experimento. JEV enriquece o ticket; não possui a criação. Em falha, o dado principal continua entrando no sistema e pode ser triado manualmente.
Uma integração confiável precisa responder “quem é dono?” antes de responder “qual modelo?”.
| Responsabilidade | Dono | Evidência | Reversão |
|---|---|---|---|
| esquema das perguntas | time do domínio | contrato versionado e revisão de código | voltar à versão anterior |
| chamada ao Gateway | adapter de infraestrutura | métricas de latência/status e testes HTTP fake | desligar provider via flag |
| validação da resposta | boundary da aplicação | erros de schema e fixtures canônicas | encaminhar para fallback |
| cortes e ação | service/policy | decisão registrada com versão da policy | modo shadow ou advisory |
| segredo do Gateway | plataforma/secret manager | auditoria de acesso e rotação | revogar chave |
| qualidade do modelo | owner do produto | eval por segmento e revisão de erros | regra determinística/manual |
O modelo nunca deve receber permissão implícita para executar uma ação sensível. Ele fornece evidência para uma policy que o código controla.
O script mínimo ensina endpoint e payload. Ele omite pontos que importam sob carga ou incidente:
[0, 1], soma das probabilidades e faixa do score. Pydantic ajuda, mas tipos válidos não provam decisão correta.httpx.AsyncClient com lifecycle controlado pelo FastAPI.pending/evaluated/failed ou outbox se reprocessamento for necessário.typesafe-ai/jev é o ID do Gateway; a TypeSafe documentava jev-latest como alias de jev-1.13.0 em 19 de setembro de 2026. Aliases facilitam upgrades, mas reduzem reprodutibilidade.Timeouts, 429, 5xx e falhas de rede acontecem. Se triagem é enriquecimento, salve o ticket com evaluation: null, emita métrica e reprocese depois. Se a decisão protege uma ação irreversível, falhe fechado ou exija humano.
department="sales" não deve entrar se o enum só aceita quatro times. Uma probabilidade 1.2 também não. Valide antes de persistir ou agir. Resposta inválida é falha de integração, não “melhor esforço”.
Confiança não substitui acurácia. Meça falsos negativos de urgência e erros por idioma, produto e tipo de cliente. Um corte global pode esconder desempenho ruim num segmento pequeno.
Texto do ticket é dado não confiável. A superfície é menor do que em geração com tools, mas prompt injection ainda pode influenciar classificação. Não inclua segredos no estado; mantenha ações fora do modelo; teste entradas adversariais.
Em 19 de setembro de 2026, a página de pricing do Gateway documentava US$ 5 mensais para times no free tier, enquanto superfícies do catálogo mostravam JEV como “Free” e o detalhe do modelo exibia US$ 0,04 por milhão. Essa divergência é motivo para tratar preço como snapshot, medir uso real e configurar alertas. Comprar créditos remove o crédito mensal gratuito daquele time, segundo a documentação vigente.
Atualização de modelo pode alterar distribuição das respostas sem quebrar schema. Rode eval antes de promover nova versão. Guarde versão efetiva quando o provedor a expuser e mantenha rollback para modelo ou policy anterior.
Automação não precisa nascer com permissão de agir.
| Modo | Comportamento | Critério para avançar |
|---|---|---|
offline | roda eval em dataset histórico | baseline e taxonomia aceitos |
shadow | avalia tráfego real, sem afetar rota | cobertura, latência e erros conhecidos |
advisory | mostra sugestão ao operador | concordância e override medidos |
enforce | policy executa ações limitadas | SLO, cortes e rollback aprovados |
Eu comecei com dependency injection. Nos testes FastAPI, get_jev_client é substituído por um fake determinístico e por outro que lança erro. Isso cobre caminho feliz e degradação sem consumir API ou depender de credencial real.
A mesma arquitetura apareceu em duas PRs públicas do meu hub de skills:
heuristic, shadow, advisory e enforce, além de um envelope tipado para decisões entre estágios. Merge em 19 de setembro de 2026.O ponto não é colocar JEV em todo lugar. É separar julgamento, contrato e autoridade de execução para poder trocar o provider sem redesenhar o domínio.
Depois da POC FastAPI, levei a ideia para o harness que uso no dia a dia. O caso está documentado em duas PRs abertas, não em um exemplo inventado:
typesafe-ai/jev pelo Vercel AI Gateway e mantém uma réplica local determinística, Builtin One, para fallback.route.jev de forma aditiva no RPC e mostra fonte, modo, provider/modelo, probabilidade, confiança e fallback na interface.O PR do core inclui typecheck, 849 testes, 2.879 assertions, 50 testes focados e smoke live com cinco casos. O benchmark local mediu média de 0,070 ms antes e 72,177 ms depois; o smoke remoto, uma execução por caso, mediu média de 536,641 ms. São números da POC, datados de 20 de setembro de 2026, não SLO nem benchmark de produção. O smoke observou respostas tipadas e fallbacks; isso prova integração exercitada, não qualidade estatística suficiente para automatizar decisões críticas.
O paradigma muda quando a incerteza vira parte do protocolo. O modelo não recebe autoridade para “escolher o próximo agente”; ele propõe uma decisão entre candidatos que o código já conhece. A policy local aplica dois gates no modo padrão do PR: confidence >= 0.65 e probability >= 0.55. Resposta inválida, baixa confiança, timeout ou erro remoto caem para Builtin One.
type JevGate = {
mode: "shadow" | "advisory" | "enforce";
confidenceFloor: number;
probabilityFloor: number;
};
const defaultGate: JevGate = {
mode: "advisory",
confidenceFloor: 0.65,
probabilityFloor: 0.55,
};
shadow mede sem alterar rota. advisory mostra sugestão para inspeção. enforce permite aplicar policy somente depois de evals e rollback aprovados. Cancelamento do caller aborta a avaliação sem inventar fallback; o erro remoto chega ao restante do sistema como classificação estável, não como stack trace de provider.
Essa separação preserva controle humano em três pontos: o conjunto de candidatos, os limites de confiança e a ação final. O ciclo real é JEV → sugestão → operador vê proveniência → aceita ou faz override → decisão fica auditável. A PR do App implementa visibilidade de proveniência, não um workflow completo de aprovação humana. A UI macOS recebe route.jev opcional; payloads legados continuam decodificando quando o core ainda não envia esse campo. Credencial, prompt bruto e decisão de provider permanecem no core. A interface apenas torna a proveniência auditável; o readiness router continua soberano.
O ganho não é “tirar humano do loop”. É tirar julgamento implícito do loop: quando o sistema está confiante e a policy permite, ele avança; quando não está, entrega evidência suficiente para uma pessoa revisar.
Monte um conjunto de tickets rotulados por pessoas do domínio. Preserve casos comuns, raros, ambíguos, multilíngues e adversariais. Separe desenvolvimento e teste para não ajustar cortes olhando o resultado final.
Para cada pergunta:
choice: accuracy, matriz de confusão, erro por classe e cobertura após abstention;noul: precision/recall no corte operacional, Brier score ou curva de calibração;score: erro absoluto por nível e custo dos erros assimétricos;Defina custo de erro com o negócio. Perder um ticket urgente costuma custar mais do que revisar um falso positivo. Isso muda o corte.
def route_urgency(probability: float, confidence: float) -> str:
if not 0.0 <= probability <= 1.0:
return "invalid"
if confidence < 0.70:
return "human_review"
if probability >= 0.90:
return "page_on_call"
if probability >= 0.60:
return "priority_queue"
return "normal_queue"
Esses números são exemplos, não recomendação. O resultado do eval define os seus.
O free tier ajuda a provar integração, não elimina governança.
state; remova PII que não melhora a decisão.Se não é possível desligar a avaliação sem derrubar o POST /tickets, o boundary ainda está errado.
O caso de uso ficou dentro de ticket: models, repository, service e router evoluem juntos. O adapter do JEV ficou em shared/services porque encapsula infraestrutura reutilizável. Essa divisão evita um “AI service” central que conhece todos os domínios e vira gargalo.
Para escalar horizontalmente, a regra é simples: request handlers stateless; dependências externas explícitas; trabalho assíncrono em fila quando couber; idempotência e locks no storage compartilhado; telemetria correlacionada por request/decision ID.
Se você quer partir de uma base FastAPI que já organiza o backend por vertical slices e foi desenhada para crescer sem acoplar todos os casos de uso, meu boilerplate FastAPI com Clean Architecture encurta essa fundação. Ele não “resolve IA”. Entrega a estrutura em que adapter, policy, testes e fallback conseguem ter donos claros.
null, abstention e revisão humana existem.pending, um worker avalia e atualiza por chave idempotente.401, 429, timeout e JSON inválido. Confirme que nenhum segredo aparece no log e que o ticket segue o fallback definido.Você aprendeu o padrão quando consegue trocar JEV por um fake ou outro provider sem alterar a entidade Ticket, e quando consegue desligar a automação mantendo o fluxo principal vivo.
JEV oferece uma interface útil para problemas que ficam entre regra determinística e geração aberta: decisões estreitas, respostas conhecidas e incerteza que o código pode inspecionar.
Minha integração com FastAPI e Vercel AI Gateway funcionou porque o modelo recebeu uma responsabilidade pequena. O service continuou dono do caso de uso; o adapter, da rede; a policy, dos cortes; os testes, do comportamento sob falha.
Esse é o padrão que vale levar para produção: modelo propõe um julgamento tipado, aplicação valida, policy decide, observabilidade prova e rollback limita o dano.
Claims de modelo, versão, disponibilidade e preço verificadas em 20 de setembro de 2026. Confirme as páginas oficiais antes de usar em produção.
Produtos gratuitos e pagos para transformar ideias em uma base que você consegue executar.
13 produtos disponíveisContinue explorando tópicos similares
Checklist de 47 pontos para encontrar bugs, riscos de segurança e problemas de performance antes do lançamento.
Templates testados em produção, usados por desenvolvedores. Economize semanas de setup no seu próximo projeto.