Especificação do Agente
Spec Driven Development — HealthQuery v0.3
Visão Geral
Agente de consulta a dataset de saúde via linguagem natural. O usuário faz uma pergunta em português, espanhol ou inglês — o agente interpreta, gera SQL SELECT, executa no banco conectado e devolve a resposta em texto com o raciocínio exposto.
Opera sobre 5 tabelas: pacientes, consultas, diagnosticos, medicamentos, exames. Limite de 30 queries por sessão. Nenhum dado é modificado.
Capacidades
O que o agente faz
- Aceita perguntas em linguagem natural em PT, ES e EN
- Classifica a intenção via modelo Haiku antes de qualquer outra chamada
- Gera SQL SELECT compatível com o banco conectado (SQLite em prep, Teradata no evento)
- Executa a query e trata o resultado — linhas retornadas, resultado vazio, erro de SQL
- Devolve resposta em 3 partes: resposta direta + SQL gerado (sempre exposto) + raciocínio
- Suporta perguntas multi-step: decompõe em sub-queries, informa quantas vai usar antes de executar
- Gerencia contador de queries: avisa quando restam 5, encerra quando chega a 30
- Loga cada interação com intenção classificada, SQL, tempo de execução, tokens
- Memória conversacional com sliding window de 30 mensagens — suporta refinamentos anafóricos
- Sugestões de próxima pergunta pós-resposta, no idioma do usuário
- Modo relatório — sintetiza histórico da sessão sem incrementar o contador
O que o agente não faz
- Nenhuma escrita — sem INSERT, UPDATE, DELETE, DROP, CREATE, ALTER (bloqueio programático)
- Não responde perguntas fora do domínio do dataset
- Não formula diagnósticos médicos nem dá conselhos clínicos
- Não executa SQL passado diretamente pelo usuário
- Não referencia tabelas fora das 5 definidas no schema (bloqueio programático)
- Não continua após o limite de 30 queries
- Não inventa dados — se não encontrou no banco, diz que não encontrou
7 Ferramentas
| Ferramenta | Função |
|---|---|
execute_sql |
NL→SQL principal com guard rails multicamada |
detect_anomalies |
Compara resultado_valor com referencia_min/max, calcula desvio %, classifica alterado/crítico |
compare_periods |
2 queries de agregação, diff absoluto + variação percentual por coluna |
rank_patients |
CTE com 4 fatores ponderados, top N com score e justificativa |
patient_profile |
4 queries internas, síntese em prosa clínica pelo Sonnet |
trend_analysis |
Divide registros em 2 metades, compara médias, interpreta relativo à faixa de referência |
calculate |
Evaluador AST seguro para crescimento composto, exponencial, fórmulas multi-passo |
Guard Rails
Guard rails são validações programáticas no código, não no prompt. O modelo não decide o que é permitido — o código decide antes.
| Guard Rail | Tipo | Trigger | Ação |
|---|---|---|---|
| Escrita bloqueada | Programático | Haiku classifica como write_attempt |
Bloqueia, responde sem chamar banco |
| Query limit | Programático | Contador ≥ 30 | Encerra sessão |
| Schema enforcement | Programático | SQL referencia tabela fora das 5 definidas | Rejeita antes de executar |
| PII + diagnóstico sensível | Programático | Query retornaria nome + condição sem agrupamento | Força agregação ou recusa |
| check_pii_result | Programático | Resultado real contém coluna nome + colunas clínicas | Bloqueia — captura SELECT * e split-queries |
Comportamento por Situação
Pergunta clara, query direta
Classifica como data_query, gera SELECT, executa, devolve resultado com SQL e raciocínio.
Pergunta ambígua
Faz 1 pergunta de clarificação por turno. Se ainda ambígua, escolhe a interpretação mais comum e informa qual escolheu.
Pergunta fora do domínio
Classifica como out_of_scope e informa que opera exclusivamente sobre o dataset de saúde.
Tentativa de escrita ou modificação
Haiku classifica como write_attempt → bloqueio programático antes de chegar no modelo principal ou no banco.
Query retorna vazio
Não inventa dados, não especula. Informa que nenhum resultado foi encontrado para a condição consultada.
Erro de SQL
Captura o erro, tenta auto-corrigir uma vez (re-envia erro + SQL ao modelo). Se falhar de novo, informa o usuário. A tentativa conta no limite de 30.
Pergunta multi-step
Decompõe explicitamente antes de executar: informa quantas queries vai usar e o que cada uma resolve.
Chegando no limite
Avisa ao chegar em 5 restantes. Encerra com mensagem clara ao atingir 30. Orienta a iniciar nova sessão.
PII + condição sensível
Bloqueia retorno de identificadores combinados com diagnóstico específico. Oferece contagem ou agregação como alternativa. Padrão production-safe mesmo com dados fake.
Input não é pergunta (saudação)
Classifica como greeting, responde brevemente. Não decrementa o contador de queries.
Schema — 5 tabelas
pacientes
| Coluna | Tipo | Descrição |
|---|---|---|
| id | INTEGER PK | Identificador |
| nome | TEXT | Nome completo |
| data_nascimento | DATE | Para calcular idade |
| sexo | TEXT | 'M' ou 'F' |
| cidade | TEXT | Cidade de residência |
| estado | TEXT | UF |
| plano_saude | TEXT | Ex: "Unimed", "SUS", "Bradesco" |
consultas
| Coluna | Tipo | Descrição |
|---|---|---|
| id | INTEGER PK | Identificador |
| paciente_id | INTEGER FK | → pacientes |
| data_consulta | DATE | Data da consulta |
| medico | TEXT | Nome do médico |
| especialidade | TEXT | Ex: "Cardiologia" |
| tipo | TEXT | 'eletiva', 'emergencia', 'retorno' |
| duracao_min | INTEGER | Duração em minutos |
diagnosticos
| Coluna | Tipo | Descrição |
|---|---|---|
| id | INTEGER PK | Identificador |
| paciente_id | INTEGER FK | → pacientes |
| consulta_id | INTEGER FK | → consultas |
| cid | TEXT | Código CID (ex: E11.9, I10) |
| descricao | TEXT | Ex: "Diabetes tipo 2" |
| data_diagnostico | DATE | Data do diagnóstico |
| status | TEXT | 'ativo', 'cronico', 'resolvido' |
medicamentos
| Coluna | Tipo | Descrição |
|---|---|---|
| id | INTEGER PK | Identificador |
| paciente_id | INTEGER FK | → pacientes |
| consulta_id | INTEGER FK | → consultas (nullable) |
| nome_comercial | TEXT | Ex: "Losartana" |
| principio_ativo | TEXT | Substância ativa |
| dosagem | TEXT | Ex: "500mg" |
| frequencia | TEXT | Ex: "2x ao dia" |
| data_inicio | DATE | Início do tratamento |
| data_fim | DATE | Fim (NULL = em uso) |
| status | TEXT | 'ativo', 'suspenso', 'concluido' |
exames
| Coluna | Tipo | Descrição |
|---|---|---|
| id | INTEGER PK | Identificador |
| paciente_id | INTEGER FK | → pacientes |
| consulta_id | INTEGER FK | → consultas (nullable) |
| tipo_exame | TEXT | 'laboratorial', 'imagem', 'funcional' |
| nome_exame | TEXT | Ex: "Glicemia em jejum" |
| resultado_valor | REAL | Para resultados numéricos (nullable) |
| resultado_texto | TEXT | Para resultados textuais |
| unidade | TEXT | Ex: "mg/dL" |
| referencia_min | REAL | Valor mínimo normal |
| referencia_max | REAL | Valor máximo normal |
| data_coleta | DATE | Data da coleta |
| status | TEXT | 'normal', 'alterado', 'critico' |
Decisões de Design
| Decisão | Escolha | Razão |
|---|---|---|
| Classificador de intenção | Haiku (modelo) | Mais robusto que regex para edge cases; custo desprezível |
| Guard rails | Programático, não via prompt | Prompt pode ser contornado; código não |
| Auto-correção de SQL | Máximo 1 retry | Loops são caros; 2 falhas sinalizam ambiguidade no input |
| Contagem de queries | Inclui tentativas com erro | Alinha com limite de 30 do hackathon |
| SQL sempre exposto | Sim | Observabilidade e credibilidade |
| SELECT * | Nunca permitido | Buraco de PII via wildcard |
| EXISTS para múltiplas condições | EXISTS separado, não AND na mesma row | Um registro de diagnóstico só tem um CID; AND retornaria vazio |
| tool_choice="none" na final_response | Forçado | Evita BadRequestError 400 quando modelo tenta gerar segunda ferramenta |
| Idioma | PT, ES e EN | Patrick trabalha em PT; hackathon é global |
Observabilidade — Estrutura do Log
Cada interação é logada em logs/session_<id>.jsonl. Um registro por linha.
{
"session_id": "uuid",
"query_number": 3,
"timestamp": "ISO8601",
"user_input": "...",
"intent_classified": "data_query",
"sql_generated": "SELECT ...",
"execution_time_ms": 120,
"rows_returned": 47,
"error": null,
"model_used": "claude-sonnet-4-6",
"input_tokens": 340,
"output_tokens": 85
}
Changelog
- 7 ferramentas: +detect_anomalies, +compare_periods, +rank_patients, +patient_profile, +trend_analysis, +calculate
- 6 intents: +greeting, +report_request
- Memória conversacional com sliding window de 30 mensagens
- Sugestões de próxima pergunta pós-resposta
- Modo relatório sem incrementar query_count
- Multilíngue PT/ES/EN — classifier detecta idioma
- 12 regras no SYSTEM_PROMPT (vs. 5 originais) — 7 novas surgidas de bugs em testes
- check_pii_result: analisa colunas do resultado real, não só o SQL
- Suporte a múltiplos tool_use blocks em paralelo
- tool_choice="none" na final_response — corrige BadRequestError 400
- Calibração do classifier Haiku: exemplos de clarification_needed vs. data_query
- Spec inicial: visão geral, capacidades, 4 guard rails, schema 5 tabelas
- 10 situações de comportamento documentadas
- Estrutura do log JSONL definida
HealthQuery — AI Agent Hackathon Pilot — Teradata Global Services — Junho 2026