10. Guia técnico
Esta seção é para quem instala, configura e mantém a AtalaIA. Usa termos técnicos livremente — as outras nove seções não.
Arquitetura, em resumo
Sistema multiagente orientado a eventos, com Redis Streams como barramento (grupos de consumidores, retenção, reprocessamento) e PostgreSQL como fonte da verdade, seguindo o padrão outbox (o evento só é publicado depois que a transação no banco confirma). Todo evento é JSON validado contra um esquema versionado em events/schemas/<tipo>.v1.json, com envelope comum (id, tipo, versao, ocorrido_em, municipio, orgao, origem, correlacao, payload, fontes[]). Evento é imutável; correção é um novo evento referenciando o anterior. Detalhe completo em pacote/02_ARQUITETURA.md.
Pilha: Python 3.12, FastAPI, SQLAlchemy 2, Alembic, Pydantic v2 (backend) · Redis 7 Streams (barramento) · PostgreSQL 16 com pgvector (memória semântica) e pg_trgm (busca textual) · Next.js (App Router, TypeScript) para o frontend · python-telegram-bot v21 para os bots (previsto) · Caddy como proxy reverso · Docker Compose para empacotamento · OpenTelemetry/Grafana/Prometheus/Loki para observabilidade (previsto) · pytest (backend) e vitest (frontend) para testes automatizados hoje; Playwright com checagem automática de acessibilidade (frontend/e2e/) testa as páginas do site em tamanho de computador e de celular, contra uma API falsa com dados de exemplo; capturas de tela do manual com Playwright são previstas.
Estado real em setembro de 2026: o backend (API FastAPI, modelos SQLAlchemy, migrações Alembic) e a camada de dados/memória estão implementados e testados, em main. Uma primeira estrutura de frontend (frontend/, Next.js) também já existe: páginas que consomem GET /organograma e GET /mapa/obras de verdade e uma página que lê e mostra o conteúdo de docs/manual/, todas sem acabamento visual (a identidade visual definitiva é o pacote do Claude Design, já versionado em design/; a aplicação dele ao site está em andamento). O portal do cidadão e o mapa geográfico aparecem nessa estrutura só como avisos de "previsto". O guardião de custo é diferente e não está disponível hoje: o código foi aprovado pela revisão adversarial (na quarta rodada, depois de três reprovações corrigidas) e pela revisão de segurança, mas não é usado por nenhum outro código do produto e os tetos dele estão vazios. Não existem ainda: bots (bots/ só tem um .gitkeep), agentes do produto (agents/registry/ vazio de propósito, workers/ só tem .gitkeep) e docker-compose.prod.yml (só existe docker-compose.homolog.yml, de homologação, que hoje não inclui o frontend).
Instalação (ambiente de homologação, hoje disponível)
- Requisitos: Docker e Docker Compose na máquina.
- Copiar
.env.examplepara.enve preencher (ver "Configuração" abaixo). Nunca commitar.envcom valores reais. - Subir os serviços:
docker compose -f docker-compose.homolog.yml up -d. Isso levanta:db— PostgreSQL 16 com a imagempgvector/pgvector:pg16, porta do host55432(5432 já está em uso por outro produto na mesma VPS de construção), inicializado porinfra/postgres/init.sql.redis— Redis 7, porta do host56379.api— a API FastAPI, construída a partir debackend/Dockerfile, porta do host58000.caddy— proxy reverso na frente da API, porta do host58080, configurado porinfra/caddy/Caddyfile.
- Migrações de banco: só por Alembic (
backend/alembic), nunca alteração manual de esquema. Rodaralembic upgrade headdentro do ambiente do backend antes do primeiro uso. - Testes do backend:
pytestdentro debackend/(usa um ambiente virtual próprio do projeto,backend/requirements.txt). - O frontend (
frontend/) roda separado, hoje fora dodocker-compose.homolog.yml:npm installenpm run build/npm run devdentro defrontend/, apontandoATALAIA_API_URLpara o endereço da API.
Não existe, ainda, um docker-compose.prod.yml — a topologia de produção (VPS dedicada, backup automatizado) é trabalho futuro, listado abaixo.
Configuração
.env(raiz do projeto, a partir de.env.example):AMBIENTE,DATABASE_URL,REDIS_URL,MUNICIPIO_PADRAO(hojecascavel-pr),ANTHROPIC_API_KEY(chave de IA — ver nota abaixo). Hoje o arquivo de exemplo só declara dois valores de Telegram:TELEGRAM_TOKEN_FABRICAeATALAIA_DONO_CHAT_ID, usados pela fábrica de construção (fabrica/notificar.py) — um token de bot por gabinete do produto (item 3) ainda é previsto, ainda não tem variável própria.backend/app/config.py: lê essas variáveis via Pydantic Settings; qualquer novo parâmetro do backend deve entrar aqui, nunca hardcoded em outro arquivo.config/modelos.yaml: roteamento de modelo de IA por tipo de tarefa (triagem/classificação em modelo mais leve; redação/resumo em modelo intermediário; jurídico/sala de ensaio/projeto de lei no modelo mais robusto), mais a seçãoguardiao_custo(tetos diários por município e por assistente — item 5, M16; hoje vazios, então nada é checado). Nenhum nome de modelo deve aparecer fora deste arquivo. Hojemodo_provedor: claude_code_headless— os agentes do produto, quando existirem, vão rodar pelo mesmo mecanismo de plano usado para construir o próprio sistema, não por uma chave de API paga por token; o modoanthropic_apiexiste só como estrutura (backend/app/provedor_ia.py), desligado, para quando houver decisão comercial (verDECISOES.md).- Segredos nunca em
.envversionado: tokens reais de bot e chaves ficam em/etc/atalaia/.env, fora do repositório, com permissão restrita.
Integrações
Cada produto externo tem um conector em integrations/<produto>/, com um CONTRATO.md descrevendo o mapeamento de campos, a regra de confiança dos dados e o comportamento quando o produto está indisponível (a AtalaIA nunca trava por dependência externa fora do ar; ela segue servindo o último dado confirmado).
- DETEPOL (
/opt/detepol, somente leitura) — pacote de obras (CO_EXCHANGE/CASCAVEL/works/) para o mapa do município (M10); e, mais adiante, sinais de risco de contrato para o Escudo do gestor (M8, previsto). Implementado hoje:backend/app/integracoes/detepol_obras.pyeintegrations/detepol/CONTRATO.md. A ingestão só ocorre semanifest.jsonexistir e os hashes de cada arquivo conferirem; qualquer divergência levanta um erro explícito (DetepolIndisponivel) sem gravação parcial. - RefFrota / Preços de frota (
/opt/precos-frota) — referência de preços para o módulo M9 (previsto). Ainda não integrado. - Fonte-agente (
/opt/fonte-agente) — conectores prontos para diário oficial (Atende), PNCP, TSE e sanções, reaproveitados como biblioteca somente leitura em vez de duplicados. Foi usado na coleta dos dados que hoje estão emdata/cascavel/organograma.json(busca de portarias e atos oficiais no diário oficial de Cascavel); novas rodadas de coleta dependem de acesso liberado a essa pasta pela sessão que estiver rodando. - MCP (Model Context Protocol) — a AtalaIA está desenhada para expor um servidor MCP com ferramentas de leitura segura (organograma, obras, preços de referência, prazos) para outros sistemas e para o próprio dono consultarem, e para consumir os produtos da Schadler Tech pelo mesmo protocolo quando eles expuserem MCP. Ainda não implementado.
Backup e restauração
É previsto um backup diário do PostgreSQL criptografado, guardado fora da VPS onde o sistema roda, com restauração testada semanalmente por um teste automatizado que restauraria o backup num banco temporário e conferiria as contagens de tabelas principais — isso é só desenho, nada disso está implementado. Nenhum script de backup/restauração existe no repositório ainda — é um item pendente antes de qualquer dado real de produção.
Como implantar a AtalaIA em outro município
O produto foi desenhado desde o início para servir mais de um município, repetindo a mesma estrutura de dados com fonte própria. Nenhum destes passos foi executado ainda para um segundo município — este é o roteiro para quando isso acontecer, não um relato do que já foi feito:
- Semente de organograma própria — montar o equivalente ao que existe hoje para Cascavel (
data/cascavel/organograma.json, gerado a partir depacote/07_CASCAVEL_ESTRUTURA.md): a estrutura de secretarias, autarquias e responsáveis do novo município, com cada campo já nascendo com o rótuloA_CONFERIRe só sendo promovido aFATOdepois de confirmado por documento oficial no diário oficial daquele município (mesma disciplina do item 6 deste manual). Cada município tem o seu próprio identificador (municipio, ex.:cascavel-pr) em todos os dados e eventos. - Fonte do diário oficial daquele município — verificar se
/opt/fonte-agentejá tem um conector para a plataforma de diário oficial usada por aquele município; se não tiver, é preciso um conector novo antes de confirmar qualquer nome. - Pacote de obras equivalente — se o município tiver um contrato com o DETEPOL (ou produto equivalente) para o pacote de obras, repetir a integração de
integrations/detepol/CONTRATO.mdapontando para oCO_EXCHANGEdaquele município. - Legislação municipal própria — ingerir a Lei Orgânica e as leis municipais relevantes daquele município na memória da prefeitura (módulo M15), do mesmo jeito que foi feito para Cascavel.
- Bots e caixas de e-mail próprios — cada município vai precisar dos seus próprios tokens de bot do Telegram (um por gabinete) e das suas próprias caixas de e-mail; nenhum dado de um município deve aparecer para outro (o isolamento por
municipioé o mesmo princípio do isolamento por gabinete, item 7). - Contrato de tratamento de dados — antes de qualquer dado real de servidor ou cidadão daquele município entrar no sistema, é preciso o contrato formal entre a Schadler Tech (operadora) e a nova prefeitura (controladora), nos mesmos termos já previstos para Cascavel.