Aurora SIGER — Fase 5: NCAS, a Colônia Ganha Memória

A colônia que operou no tempo e se mapeou no espaço agora se lembra. Persistência dupla (JSON e log append-only), cinco regras de álgebra booleana provadas por tabela-verdade exaustiva e um alerta que deixa de ser digitado para ser derivado da simulação da Fase 3. Projeto acadêmico FIAP 2026.

Python · Pytest · Álgebra booleana · Persistência em arquivo · Engenharia de prompts

← Voltar para Aurora SIGER

O cenário

As quatro primeiras fases do Aurora SIGER viveram inteiramente em RAM. A telemetria da decolagem, a fila de pouso, os sete sóis de operação energética, o grafo de dependências: tudo era calculado, exibido e descartado. Cada execução começava do zero e não deixava rastro. Um sistema assim consegue reagir, consegue prever e consegue mapear, mas não consegue responder à pergunta mais banal de qualquer operação real: o que aconteceu ontem, e como eu confiro?

A Fase 5 responde a essa pergunta dando à colônia a primeira coisa que ela nunca teve: memória em disco. O sistema que materializa isso é o NCAS, Núcleo Cognitivo da Aurora Siger, uma aplicação de menu com 13 opções, construída só com a biblioteca padrão do Python, que registra, organiza, consulta e interpreta as informações operacionais da colônia.

É o quinto ato do arco. Decolagem, pouso, operação, topologia, e agora memória. E o salto conceitual da fase não está no que o NCAS grava, e sim no que ele decidiu não gravar.

Como funciona

Dois formatos, um critério

O sistema escreve em dois formatos, e o critério é a natureza do dado.

DadoFormatoPor quê
Módulos, alertas, interaçõesJSONvários campos chave/valor, lidos, filtrados e atualizados individualmente
Registro cronológico da colôniaTXT append-onlylinhas sequenciais, só acrescentadas ao final e lidas em ordem

A justificativa fica clara quando se imagina a troca. Um log em JSON exigiria, a cada linha registrada, ler o arquivo inteiro, inserir um item e reescrever tudo: custo linear na história acumulada para anotar um único evento. Um cadastro em texto puro exigiria varrer e reparsear linhas toda vez que alguém perguntasse "quais alertas são críticos?". Cada formato é ótimo para um padrão de acesso e péssimo para o outro.

As três aberturas de arquivo aparecem com papéis distintos: 'r' para leitura, 'w' para escrita truncando (o cadastro é reescrito por inteiro), 'a' para acréscimo ao final (o log nunca reescreve o passado). Todas passam por with open(...), de modo que o descritor fecha mesmo quando a escrita levanta exceção. Sem o gerenciador de contexto, um erro no meio de um write deixaria o buffer sem descarregar, e o dado pareceria gravado sem estar.

O diretório de dados é parâmetro do construtor, nunca constante de módulo. Isso tem três consequências práticas: duas colônias podem ser registradas em paralelo, os testes escrevem em diretório temporário e jamais na raiz do repositório, e com um relógio injetado o log fica comparável byte a byte, o que estende à camada de persistência o determinismo que a Fase 3 já tinha na simulação.

As cinco regras, e a diferença entre provar e ilustrar

O coração lógico da fase são cinco regras de álgebra booleana. Cada uma é uma função pura que devolve as duas formas da expressão, a original e a simplificada, já avaliadas, junto com o teorema que autoriza a simplificação:

#RegraForma originalForma simplificadaTeorema
1Alerta crítico(FALHA ∧ CRÍTICO) ∨ (FALHA ∧ ¬CRÍTICO)FALHAAbsorção: A·B + A·B' = A
2Alerta geral(FALHA ∧ CRÍTICO) ∨ CONSUMO_ELEVADOjá mínimasem literal comum
3Liberar consultaAUTORIZADO ∧ MÓDULO_ATIVOjá mínimacomplemento da regra 4
4Bloquear operação¬(AUTORIZADO ∧ MÓDULO_ATIVO)¬AUTORIZADO ∨ ¬MÓDULO_ATIVODe Morgan: (A·B)' = A' + B'
5Priorizar atendimento(URGENTE ∧ ESSENCIAL) ∨ (URGENTE ∧ DISPONÍVEL)URGENTE ∧ (ESSENCIAL ∨ DISPONÍVEL)Fatoração: A·B + A·C = A·(B+C)

A redundância da forma original é o ponto didático. Ela existe para ser comparada, não para ser executada. E manter duas regras irredutíveis é deliberado: simplificar é um resultado possível, não uma obrigação, e reconhecer que uma expressão já está na forma mínima faz parte do exercício.

A regra 1 é o exemplo mais limpo da absorção. Com FALHA falso, os dois termos morrem. Com FALHA verdadeiro, sobra CRÍTICO ∨ ¬CRÍTICO, que vale sempre. O valor de CRÍTICO não altera a saída em nenhuma linha, logo ele não pertence à expressão mínima:

FALHACRÍTICO(F∧C) ∨ (F∧¬C)Fequivalente
FFFF✓
FVFF✓
VFVV✓
VVVV✓

O detalhe que muda a natureza dessa tabela é onde ela é verificada. A verificação acontece em teste automatizado, sobre todo o domínio de entrada, via itertools.product:

@pytest.mark.parametrize("falha,critico", itertools.product([False, True], repeat=2))
def test_alerta_critico_preserva_tabela_verdade(falha, critico):
    check = rules.alerta_critico(falha, critico)
    assert check.original == check.simplificada

Quatro combinações para as regras de duas variáveis, oito para as de três, 28 linhas ao todo, e nenhuma delas escapa. Conferir a equivalência sobre um único par de valores mostra que os dois lados coincidem naquele ponto. O teorema afirma que coincidem em todos, e uma amostra de tamanho um não distingue identidade algébrica de coincidência numérica.

Vale registrar que a purificação da regra e a prova do teorema foram o mesmo movimento. Enquanto a função imprimia por dentro, testá-la exigiria capturar stdout. Devolvendo um objeto com as duas formas avaliadas, a tabela-verdade exaustiva saiu de graça.

O alerta que se tornou verdadeiro

Aqui está a decisão que define a fase.

Na versão original, as regras booleanas recebiam três valores digitados pelo operador (falha, crítico, consumo_elevado), e esses mesmos três ficavam gravados dentro de cada alerta no arquivo JSON. Só que as fases 3 e 4 já calculam essas grandezas a partir da física da colônia. O estado de falha de um módulo é uma propriedade da simulação. A criticidade sai da árvore de tiers. O consumo instantâneo é calculado hora a hora, com termo térmico e fator de estrangulamento de potência.

Persistir um booleano derivável cria uma segunda fonte de verdade que envelhece em silêncio. Se o módulo for reparado na hora 60, o "falha": true gravado no arquivo vira mentira e nada no sistema reclama. O dado não fica corrompido, fica desatualizado, o que é pior, porque continua parecendo válido.

Então o JSON passou a guardar apenas o que o sistema não sabe calcular: que ocorrência houve, quando, e com que prioridade a tripulação a classificou. O resto é derivado no momento da análise. Nome do módulo e criticidade saíram pelo mesmo raciocínio. É o mesmo movimento que a Fase 4 fez ao derivar a prioridade dos nós da árvore de criticidade em vez de manter uma tabela paralela.

Linha do tempo dos predicados derivados ao longo de 264 horas da simulação da Fase 3 com seed 42: cada um dos 13 módulos ocupa uma faixa, as bandas douradas marcam as horas de consumo elevado e os blocos vermelhos as horas em falha; uma linha tracejada na hora 239 marca o instante em que a Produção de Alimentos quebra e o alerta 14 se torna verdadeiro, e o painel inferior mostra a saída da regra booleana ALERTA = FALHA acompanhando a série
Linha do tempo dos predicados derivados ao longo de 264 horas da simulação da Fase 3 com seed 42: cada um dos 13 módulos ocupa uma faixa, as bandas douradas marcam as horas de consumo elevado e os blocos vermelhos as horas em falha; uma linha tracejada na hora 239 marca o instante em que a Produção de Alimentos quebra e o alerta 14 se torna verdadeiro, e o painel inferior mostra a saída da regra booleana ALERTA = FALHA acompanhando a série

A figura acima não é ilustrativa: é a saída de cognition.predicates rodando hora a hora sobre a simulação canônica. O NCAS abre avançando o relógio até o primeiro instante em que um módulo com alerta registrado está em falha. Com seed=42, isso acontece na hora 239, sol 9: a Produção de Alimentos quebra, e o alerta 14, que existia como linha de dados inerte desde a semeadura, passa a satisfazer ALERTA = FALHA. A expressão deixou de ser exercício de digitar s no terminal.

Duas observações que a figura entrega de graça. A primeira é que os dois módulos com mais alertas registrados, Suporte de Vida (7) e Habitação (4), atravessam os onze sóis sem quebrar nem consumir demais: as faixas deles estão vazias. O registro da tripulação e a física da colônia são camadas distintas, e o sistema mostra exatamente onde as duas se encontram. A segunda é que o consumo elevado não é evento raro. Produção de Alimentos, ISRU e Laboratório passam a maior parte do tempo acima do próprio modo adequado, porque entram em modo surplus quando há energia sobrando. Um predicado que fosse digitado uma vez e gravado nunca capturaria isso.

O limiar que separa as duas populações foi medido, não escolhido. Na simulação canônica, um módulo em modo adequate fica exatamente em 1,00× do seu nominal, enquanto os que entram em surplus saltam para algo entre 1,8× e 2,5×. Qualquer fator entre 1,05 e 1,7 separaria os dois grupos, e CONSUMO_ELEVADO_FATOR = 1.2 fica no meio da folga, longe das duas bordas. O limiar é por módulo, comparando o consumo instantâneo contra o próprio modo adequado, e não contra a bateria da colônia inteira, o que trocaria a escala do predicado no meio do caminho.

Um detalhe de engenharia que vale mencionar: a abertura do sistema procura o evento em vez de avançar um número fixo de horas. Fixar 239 funcionaria hoje e apodreceria em silêncio se qualquer constante da Fase 3 mudasse, porque o número está calibrado para o gerador de números aleatórios atual. Procurar a primeira falha relevante sobrevive à mudança.

O assistente que não chama modelo nenhum

A fase inclui quatro técnicas de engenharia de prompts, cada uma escolhida por adequação à tarefa e não por variedade: zero-shot para resumo de alerta (tarefa autoexplicativa), few-shot para classificação em conjunto fechado (três exemplos rotulados ensinam o formato), saída estruturada em JSON para resposta consumida por outro programa, e tradução para linguagem simples para ampliar quem consegue auditar o sistema.

Os quatro templates são código, não configuração. Um template é lógica de aplicação: versiona junto com quem o consome, e uma mudança nele aparece no diff. A renderização levanta erro se faltar um campo, em vez de produzir um prompt com um buraco {...} no meio, porque prompt truncado é bug silencioso: o modelo responde alguma coisa, e ninguém percebe que a pergunta estava incompleta.

O assistente que responde a esses prompts não chama modelo de linguagem algum. As respostas vêm de casamento de palavra-chave sobre tabelas declaradas no topo do módulo, e cada resposta carrega um campo gatilho: a palavra exata que decidiu a classificação, ou vazio quando caiu no caso padrão. Para saber por que um alerta foi classificado como risco alto, lê-se o campo, não o código-fonte.

A escolha custa capacidade. Um modelo real resumiria alertas melhor que qualquer tabela de palavras-chave, e com folga. O que se ganha é que toda resposta permanece explicável em princípio, e é isso que sustenta a seção de ética da fase. Uma página que alega auditabilidade ao lado de uma chamada a modelo opaco estaria descrevendo um sistema que não existe.

Por que importa

Os limites da fase são específicos e vale nomeá-los.

O assistente simulado é uma simulação de comportamento, não inteligência artificial, e a distância entre as duas coisas é exatamente o que ele não consegue fazer: lidar com uma frase que não contenha nenhuma das palavras-chave previstas. O log cresce sem rotação, o que é adequado para uma colônia de treze módulos e insuficiente para qualquer coisa maior. E derivar predicados tem um custo real de acoplamento: para analisar um alerta, o NCAS precisa da simulação da Fase 3 carregada, enquanto ler três booleanos de um arquivo não precisaria de nada. A troca foi consciente. Correção contra independência, e correção ganhou.

Uma colônia que não se lembra pode reagir e até prever. Só uma que se lembra pode ser auditada.

Há uma continuidade de tema com a Fase 1, que abriu o arco perguntando quem decide quando a máquina decide. O NCAS calcula, apresenta e registra. Isolar um módulo habitado ou bloquear uma operação continua sendo assinatura da tripulação, e automação de recomendação e automação de decisão têm perfis de risco diferentes. O que a persistência acrescenta a essa posição é a possibilidade de conferir depois: sem registro, a auditabilidade é uma alegação sobre o presente. A síntese narrativa completa está no ensaio Da topologia à memória.

Qualidade e rigor

A Fase 5 leva o pacote aurora_siger à versão 0.5.0, mantendo o monolito incremental: a camada cognitiva vive em aurora_siger.cognition (journal, store, rules, predicates, prompts, assistant, analysis, seeds, essays, cli) e o entrypoint ncas é um wrapper fino sobre ela. Zero dependências externas, como nas fases anteriores. Os dois ensaios avaliados (ética e memória) viajam como Markdown dentro do pacote e são lidos por importlib.resources, de modo que existe uma cópia só e ela funciona também a partir de um wheel instalado.

MétricaValor
Módulos do subpacote cognition10
Coleções JSON persistidas3 (módulos, alertas, interações)
Alertas semeados sobre os 13 módulos canônicos15
Regras booleanas5 (3 simplificáveis, 2 já mínimas)
Linhas de tabela-verdade verificadas em teste28
Técnicas de engenharia de prompts4
Chamadas a API de modelo de linguagem0
Opções do menu13
Testes da Fase 5164
Testes automatizados (acumulado)478

Os testes cobrem as tabelas-verdade exaustivas das cinco regras, o CRUD em JSON incluindo arquivo ausente e arquivo corrompido, o comportamento do log append-only (readlines, fatiamento de lista, writelines), a derivação dos predicados a partir do estado da simulação, o encadeamento completo de análise de alerta e o menu. Nenhum teste escreve fora do diretório temporário, e a raiz do repositório permanece limpa. O pacote foi verificado também a partir de um wheel instalado em ambiente virtual vazio, o que prova que o caminho inteiro da fase depende só da biblioteca padrão.


Projeto desenvolvido coletivamente pela equipe de Ciência da Computação (online) da FIAP em 2026, com a consolidação ao monorepo curada por Iúri Leão de Almeida. Explore o código-fonte no repositório GitHub, leia o relatório técnico ou rode o sistema localmente com pip install -e . && ncas.