Guia · para iniciantes

Usar a API e testar a Tuiuiú

Duas coisas nesta página: como chamar a Tuiuiú de dentro do seu próprio programa (sem cadastro, sem chave) e como saber se ela está respondendo certo. Você não precisa saber programar para a segunda parte.

Usar a API, começando

Uma API é um jeito de um programa conversar com outro. A Tuiuiú tem uma: você manda uma pergunta em texto, ela devolve a resposta em texto, palavra por palavra, igual ao que acontece na tela de conversa. Tudo o que o site faz, o seu código também pode fazer.

Endereço-base: https://ia-ollama.victorhugocampos.cloud/api
Todos os caminhos abaixo são relativos a ele. Não existe login nem chave, é só chamar.

Só existem duas rotas que importam para começar:

RotaPara quê
GET/healthSaber se a API está no ar e o que está funcionando.
POST/chatMandar uma pergunta e receber a resposta em streaming (palavra por palavra).

Está no ar?, GET /health

Cole isto no terminal (Mac e Linux já têm o curl; no Windows use o PowerShell):

curl https://ia-ollama.victorhugocampos.cloud/api/health

Resposta:

{"status": "ok", "banco": "ok", "ollama": "ok"}

Se status vier "stub", a API está no modo demonstração (respostas de exemplo, poucos municípios). Se vier "erro", o campo que estiver diferente de "ok" diz o que caiu.

Conversar, POST /chat

Você envia um JSON com a sua pergunta. Os campos:

CampoTipoObrigatórioO que é
mensagemtextosimA pergunta, do seu jeito. Ex.: "Cuiabá é Cerrado ou Pantanal?"
historicolistanãoAs últimas mensagens da conversa, para ela lembrar o contexto. Cada item: {"papel": "user" | "assistant", "texto": "..."}. Mande no máximo 6.
modotextonão"conversa" (padrão) ou "quiz".

A resposta não é um JSON único, é um fluxo de eventos (Server-Sent Events, ou SSE). Em vez de esperar a frase inteira, você recebe pedacinhos conforme ela escreve. É o que faz o chat parecer vivo.

curl -N https://ia-ollama.victorhugocampos.cloud/api/chat \
  -H "Content-Type: application/json" \
  -d '{"mensagem": "Cuiabá é Cerrado ou Pantanal?"}'

O -N manda o curl mostrar cada pedaço assim que chega. Você verá algo assim:

event: entidade
data: {"id": 5103403, "nome": "Cuiabá", "tipo": "municipio"}

event: token
data: {"texto": "Essa "}

event: token
data: {"texto": "é boa, "}

... (um evento por pedaço de texto) ...

event: citacao
data: {"fonte": "IBGE Biomas 2019", "url": null}

event: fim
data: {}

Os eventos, um a um

EventoDadosO que fazer com ele
token{"texto": "..."}Um pedaço da resposta. Vá concatenando na ordem em que chegam.
entidade{"id", "nome", "tipo"}Um lugar que a resposta cita. O id é o código IBGE, no site, é o que acende o mapa.
citacao{"fonte", "url"}De onde veio o dado. Mostre ao usuário, é a garantia de que não foi inventado.
fim{}Acabou. Pode fechar a conexão.
erro{"mensagem": "..."}Algo deu errado no meio. Mostre a mensagem e ofereça tentar de novo.
Linhas começando com : são batimentos (heartbeat) que a API manda a cada ~15 s enquanto pensa, para a conexão não cair. Ignore.

Exemplos de código

JavaScript (navegador ou Node 18+)

EventSource só faz GET, e a Tuiuiú precisa de POST, então usamos fetch e lemos o fluxo na mão. É menos assustador do que parece:

async function perguntar(mensagem, aoReceber) {
  const resp = await fetch("https://ia-ollama.victorhugocampos.cloud/api/chat", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ mensagem }),
  });
  const leitor = resp.body.getReader(), dec = new TextDecoder();
  let buf = "";
  while (true) {
    const { value, done } = await leitor.read();
    if (done) break;
    buf += dec.decode(value, { stream: true });
    let i;
    while ((i = buf.indexOf("\n\n")) >= 0) {          // cada evento termina com linha em branco
      const bloco = buf.slice(0, i); buf = buf.slice(i + 2);
      const ev   = (bloco.match(/^event: (.+)$/m) || [])[1] || "message";
      const data = (bloco.match(/^data: (.+)$/m)  || [])[1];
      if (data) aoReceber(ev, JSON.parse(data));
    }
  }
}

// uso:
let texto = "";
perguntar("Qual a capital do Acre?", (ev, d) => {
  if (ev === "token") { texto += d.texto; console.log(texto); }
  if (ev === "citacao") console.log("fonte:", d.fonte);
});

Python (3.10+, com httpx)

# pip install httpx
import httpx, json

def perguntar(mensagem: str):
    with httpx.stream("POST", "https://ia-ollama.victorhugocampos.cloud/api/chat",
                      json={"mensagem": mensagem}, timeout=120) as r:
        ev = "message"
        for linha in r.iter_lines():
            if linha.startswith("event:"): ev = linha[6:].strip()
            elif linha.startswith("data:"):  yield ev, json.loads(linha[5:])

texto = ""
for ev, d in perguntar("Quais estados fazem divisa com Goiás?"):
    if ev == "token":   texto += d["texto"]; print(d["texto"], end="", flush=True)
    if ev == "citacao": print(f"\n[fonte: {d['fonte']}]")

Mantendo o contexto

Para ela lembrar do que vocês falaram, mande as últimas mensagens em historico:

{
  "mensagem": "E quantas pessoas moram lá?",
  "historico": [
    { "papel": "user",      "texto": "Cuiabá é Cerrado ou Pantanal?" },
    { "papel": "assistant", "texto": "Essa é boa. Cuiabá fica na divisa: ..." }
  ]
}

Limites e erros

CódigoSignificaO que fazer
200Deu certo; o fluxo de eventos vem no corpo.Ler até o fim.
422Seu JSON está errado (falta mensagem, tipo trocado).Confira os campos da tabela acima.
429Muitas perguntas em pouco tempo. O limite em /chat é 10 por minuto por endereço IP, com rajada de 5.Espere o tempo do cabeçalho x-retry-in e tente de novo. Só /chat tem limite; /health e a página não.
502/503A API está fora ou reiniciando.Confira /health; tente em alguns segundos.
Por que o limite? A Tuiuiú roda num servidor pequeno (2 núcleos) e o modelo de linguagem gera uma resposta por vez. Perguntas simples (“capital de X”) nem chegam nele e voltam em milissegundos; perguntas abertas levam de 5 a 20 segundos. O limite é o que mantém o serviço de pé para todo mundo. Enquanto uma resposta longa é gerada, você pode ver : heartbeat chegando, é normal.

Em breve

Rotas que já estão desenhadas e entram nas próximas versões, os nomes não vão mudar:

GET/geo/municipio/{id}Contorno do município em GeoJSON, para desenhar no mapa.
POST/quiz/perguntaSorteia uma pergunta de múltipla escolha com dificuldade calibrada.
POST/quiz/respostaCorrige, dá dica antes da resposta e explica.

Testar a IA, o que é “certo”

A Tuiuiú tem uma promessa incomum para uma IA: ela não pode inventar fato. Testar a Tuiuiú é, principalmente, tentar fazê-la quebrar essa promessa. Uma resposta boa tem estas quatro marcas:

Uma resposta ruim costuma ser convincente. É por isso que o teste precisa de gabarito, não de impressão.

Roteiro de 6 testes (10 minutos)

Abra a conversa e faça estas perguntas na ordem. Ao lado, o que deve acontecer.

#PergunteEsperadoEstá testando
1Qual a capital do Acre?“Rio Branco”, com fonte IBGE. Resposta quase instantânea.Fato simples (Camada 0)
2Qual a população de Salvador?Um número com “Censo 2022” na fonte. Confira no IBGE, tem que bater.Número real, não inventado
3Quais estados fazem divisa com Goiás?MG, BA, TO, MT, MS e DF, os seis, nem mais nem menos.Consulta espacial
4Cuiabá é Cerrado ou Pantanal?Os dois, com percentual aproximado. Se ela responder só um, é erro.Município em mais de um bioma
5Me fala de Bom JesusEla pergunta qual, existem vários (PI, RS, RN, PB, SC). Não pode chutar um.Ambiguidade
6Qual a população de Lisboa?Recusa com bom humor: a especialidade dela é o Brasil. Nenhum número.Fora de escopo
Passou nos 6? Então tente quebrar: peça um número que ela não tem (“quantas árvores tem em Manaus?”), peça para arredondar, insista. A resposta certa continua sendo “não tenho esse dado”.

Como pegar uma alucinação

“Alucinação” é quando a IA afirma algo falso com total confiança. Os três sinais mais comuns aqui:

  1. Número sem fonte. Toda resposta com número traz um 📎 com a origem. Número sem 📎 é suspeito.
  2. Especificidade demais. “Exatamente 2.347 espécies” para uma pergunta aberta sobre um bioma. Dado real de bioma costuma ser aproximado e vir da Wikipédia, e a fonte diz isso.
  3. Bicho errado no lugar errado. Crocodilo no Pantanal (são jacarés), pinguim no Nordeste. Modelos pequenos misturam. A arquitetura existe para impedir; se passou, é bug e queremos saber.
Teste rápido de honestidade: pergunte a mesma coisa duas vezes com palavras diferentes. Fato consultado dá o mesmo número as duas vezes. Fato inventado varia.

Reportar um erro

Achou uma resposta errada? Isso vale ouro. Abra uma issue no GitHub com:

Não precisa saber programar. Uma pergunta bem descrita vira um teste automático que impede o erro de voltar.

Para quem programa: testar de dentro

Se você clonou o repositório:

make chat        # conversa pelo terminal, sem navegador
make eval        # roda as 50 perguntas com gabarito e mede acerto, alucinação e recusa
make test        # testes unitários

O make eval é o coração: 50 perguntas com resposta esperada, nas mesmas 6 categorias do roteiro acima. Ele imprime três números, acerto factual (meta ≥ 90%), alucinação numérica (meta 0%) e recusa correta (meta 100%), e roda em todo pull request. Um PR que faz a Tuiuiú voltar a inventar fica vermelho antes de chegar em produção. Detalhes em docs/05-AVALIACAO.md.