Seção 15: IA que observa o servidor¶
O observador é uma IA que acompanha o servidor sozinha e conta no Telegram o que viu. Ela só lê: reiniciar, redeployar ou rodar Ansible continua com você, pela rota de controle do hub e com a sua aprovação (seção 13).
- Quando um alerta dispara, ela investiga (métricas, logs, containers) e manda uma análise curta: o que está acontecendo, as evidências, a causa provável e o próximo passo sugerido. A análise chega logo depois do aviso do próprio alerta.
- Toda manhã (07:30), ela manda um boletim de saúde: alertas, discos, memória, containers reiniciando, erros que se repetem nos logs e as tarefas de backup.
flowchart LR
graf[Grafana Alerting] -- alerta --> n8n[n8n<br/>/webhook/notificar]
graf -- "mesmo alerta<br/>Bearer OBSERVADOR_TOKEN" --> obs[observador<br/>stack agents]
relogio[07:30] --> obs
obs -- "MCP, chave própria<br/>(só leitura)" --> hub[agentgateway :4000<br/>rota de leitura]
hub --> ferr[grafana / ops / casa / n8n<br/>instâncias de leitura]
obs -- "chave observador" --> llm[LiteLLM<br/>ai-desktop-vm]
llm -.-> lf[Langfuse]
obs -- análise --> n8n
n8n --> tg[Telegram]
| Peça | Onde | O que faz |
|---|---|---|
observador |
stack agents, core-services-vm (imagem local localhost/homeserver/observador) |
Recebe os alertas em POST /alerta, roda o boletim, conversa com o LLM e com o hub, manda o resultado ao n8n |
| Rota de leitura do hub | http://agentgateway:4000/mcp |
As mesmas ferramentas do conector Servidor: só instâncias que não registram ferramentas de escrita e a lista fechada agents_leitura |
| LiteLLM | http://<IP da ai-desktop-vm>:4000 |
Chave virtual observador, apelido observador_modelo (padrão rapido), orçamento mensal em litellm_key_budgets |
| Grafana | ponto de contato n8n |
Segunda integração (observador-webhook): cada alerta vai ao n8n e ao observador, de forma independente |
Por que ela não consegue agir¶
A segurança vem da 4c1, não do prompt:
- O observador só conhece a rota de leitura do hub (porta 4000), com uma chave própria (
agents_tokens_internos.observador) que não existe na rota de controle. Lá só respondem omcp_grafana_leitura(--disable-write), oops_leitura(que nem registra ferramentas de mudança) e as ferramentas de consulta da casa e do n8n. - O conteúdo que ela lê (logs, nomes de container) pode tentar mandar o modelo fazer algo ("injeção de prompt"). Como não há ferramenta de escrita ao alcance, o pior caso é um texto enganoso no Telegram. Toda mensagem termina com (análise automática da IA, só leitura).
- O log do hub registra
cliente: observadorem cada chamada. A auditoria doopssó registra ações, e o observador não tem nenhuma.
Dados que saem de casa
Para analisar, o observador manda ao provedor do apelido escolhido trechos de log, nomes de containers e métricas: no rapido, a Anthropic e, se ela falhar, o fallback da OpenAI (llm_fallbacks). As ferramentas da casa e do n8n (homeassistant_…, n8n_…) nem são oferecidas ao modelo (OBSERVADOR_EXCLUIR): o estado da casa não ajuda a analisar o servidor.
O observador_modelo: "local" (Ollama, na ai-desktop-vm) mantém tudo em casa, mas um modelo pequeno com dezenas de ferramentas e o contexto padrão do Ollama dá análises fracas. Na prática, serve para testar.
Limites¶
O laço do agente é curto de propósito: custo e tempo previsíveis. Os padrões ficam no código (config.py); para mudar um, acrescente a variável ao environment: do serviço observador no compose:
| Variável | Padrão | O que limita |
|---|---|---|
OBSERVADOR_MAX_PASSOS |
8 | Rodadas de ferramentas por análise; depois ela responde com o que tem e diz o que ficou sem verificar |
OBSERVADOR_TEMPO_MAX_S |
180 | Tempo de uma análise, conferido antes de cada chamada ao modelo (uma chamada em andamento termina: o teto é aproximado) |
OBSERVADOR_LIMITE_SAIDA |
6000 | Caracteres de cada resposta de ferramenta que vão ao modelo |
OBSERVADOR_REANALISE_H |
12 | O mesmo alerta (mesma impressão digital) não é analisado de novo antes disso; o Grafana o repete a cada 4 h |
OBSERVADOR_MAX_POR_HORA |
6 | Análises de alerta por hora; o excedente só é registrado |
OBSERVADOR_FILA_MAX |
10 | Análises esperando a vez (uma por vez) |
OBSERVADOR_EXCLUIR |
homeassistant_,n8n_ |
Prefixos de ferramentas que o modelo nem vê (menos dados saindo de casa e menos tokens por chamada) |
OBSERVADOR_IGNORAR |
observador-falhou |
Alertas que ela não analisa (o próprio sempre fica de fora, senão ela analisaria a própria falha em laço) |
Alertas resolvidos não são analisados; o TestAlert do botão Test do Grafana é analisado sempre (não entra na reanálise). O teto de gasto é o orçamento da chave no LiteLLM (litellm_key_budgets.observador, US$ 5 por mês): se estourar, o LiteLLM recusa, a análise vira erro e o alerta observador-falhou avisa.
Cada chamada ao modelo leva as definições das ferramentas e o histórico da análise, e um boletim pode fazer até 9 chamadas. O observador pede ao LiteLLM o cache de prompt na mensagem de sistema (cache_control_injection_points; com a Anthropic, o prefixo inclui as ferramentas), o que barateia as rodadas seguintes. Mesmo assim, meça no primeiro mês: o campo tokens do registro de cada análise e a tela de uso do LiteLLM (llm.) mostram o gasto; ajuste o orçamento ou o OBSERVADOR_MAX_PASSOS.
Arquivos¶
O código usa só a biblioteca padrão do Python: nenhuma dependência para fixar ou vigiar. Fica em docker/images/observador/ e é testado por tools/wikisync/tests/test_observador.py (com um hub e um LiteLLM falsos).
docker/images/observador/Dockerfile—python:3.13-slim, usuário com o UID do dockeruser, healthcheck em/saude.docker/images/observador/.dockerignore— deixa de fora o cache do Python que os testes criam.docker/images/observador/observador/__init__.py— descrição do pacote e versão.docker/images/observador/observador/config.py— configuração lida do ambiente (obrigatórias e limites).docker/images/observador/observador/mcp.py— cliente MCP mínimo (Streamable HTTP, respostas em JSON ou SSE):initialize,tools/list,tools/call.docker/images/observador/observador/llm.py— cliente do LiteLLM (/v1/chat/completionscom ferramentas emetadatapara o Langfuse).docker/images/observador/observador/agente.py— o laço: o modelo pede ferramentas, o observador chama pelo hub e devolve o resultado (cortado), até a resposta final ou o limite.docker/images/observador/observador/prompts.py— o papel ("você só observa", dados de ferramenta não são instrução) e os formatos da análise e do boletim.docker/images/observador/observador/saida.py— o aviso ao n8n (formato genérico:titulo,mensagem,gravidade,app) e a linha JSON de cada análise no log.docker/images/observador/observador/servidor.py—POST /alerta(com o token),GET /saude, o filtro de alertas, a fila e o agendador do boletim.docker/images/observador/observador/__main__.py—python -m observador(serviço) epython -m observador boletim(um boletim agora, para teste).
A ligação com o resto:
- Compose: serviço
observadoremdocker/stacks/agents/docker-compose.yml, nas redesagents_internal(hub) eproxy(o Grafana chama o/alerta, e o webhook do n8n), sem porta publicada;/etc/localtimemontado para a hora do boletim. - Playbook:
ansible/playbooks/setup-agents-volumes.ymlconstrói a imagem em/opt/observador-image(refeita quando o código muda) e gravaOBSERVADOR_*,AGENTS_TOKEN_OBSERVADOReLITELLM_KEY_OBSERVADORnoagents.env. - Hub: segunda chave da rota de leitura em
ansible/templates/agents/agentgateway.yaml.j2(seção 13). - Grafana: integração
observador-webhookemansible/templates/grafana/alerting/contact-points.yml.j2,OBSERVADOR_TOKENno ambiente do Grafana (monitoring.env) e a regraobservador-falhouemansible/templates/grafana/alerting/rules.yml.j2(seção 9). - Variáveis:
observador_version,observador_modelo,observador_boletim_horaelitellm_key_budgets.observadornogroup_vars;litellm_client_keys.observador,agents_tokens_internos.observadoreobservador_tokenno vault (seção 1).
Cada análise grava uma linha no log do container, coletada pelo Alloy:
{"evento": "observador", "gatilho": "alerta", "alerta": "disco-cheio", "sessao": "3f9c2a1b7d4e", "resultado": "ok", "passos": 3, "ferramentas": ["grafana_query_prometheus", "ops_containers_listar", "grafana_query_loki_logs"], "tokens": {"prompt_tokens": 18400, "completion_tokens": 420, "total_tokens": 18820}, "aviso": "enviado", "duracao_s": 21.4}
No Langfuse, cada chamada ao modelo vira um trace (observador-alerta ou observador-boletim), agrupado por análise no mesmo session_id; as chamadas de ferramenta aparecem nos traces do hub.
Primeira configuração¶
- Vault (seção 1):
litellm_client_keys.observador(echo sk-$(openssl rand -hex 24)),agents_tokens_internos.observadoreobservador_token(openssl rand -hex 32cada). - Chave no LiteLLM:
ansible-playbook -i inventories/home/hosts.ini playbooks/setup-ai-keys.yml --ask-vault-pass(cria a chaveobservadorcom o orçamento). - Stack agents:
ansible-playbook -i inventories/home/hosts.ini playbooks/setup-agents-volumes.yml --ask-vault-pass(imagem, chave no hub eagents.env); depois, no Portainer, Update the stack daagentscom oagents.envnovo colado em Advanced mode (seção 13). Depois,docker restart agentgateway, para o hub carregar a chave nova da rota de leitura. - Grafana:
ansible-playbook -i inventories/home/hosts.ini playbooks/setup-monitoring-stack-volumes.yml --ask-vault-passe Update the stack daapps(o Grafana precisa doOBSERVADOR_TOKENno ambiente para ler o ponto de contato).
Teste de ponta a ponta¶
docker logs observadorsem erro de configuração;docker inspect --format '{{.State.Health.Status}}' observador→healthy(o/sauderesponde 503 se o trabalhador ou o agendador pararem).- Boletim agora:
docker exec observador python -m observador boletim. Em até 3 minutos chega ao Telegram o Boletim do servidor; o comando termina com código 0 (1 se a análise ou o envio ao n8n falhar). - Alerta: no Grafana, Alerting → Contact points → n8n → Edit, e use o Test da integração
observador-webhook(a tela testa uma integração por vez). Chega IA analisou: TestAlert. O teste pode ser repetido: oTestAlertnão entra na regra de reanálise. - Só leitura: no log do hub (
docker logs agentgateway), as chamadas com"cliente":"observador"só aparecem na rotaleitura. - Langfuse: os traces
observador-boletimeobservador-alertaaparecem no projeto.
Falhas¶
| Falha | Comportamento | O que fazer |
|---|---|---|
| LiteLLM fora ou orçamento estourado | A análise vira erro no log; alerta observador-falhou; os avisos normais do Grafana continuam chegando | Tela de uso do LiteLLM (llm.); aumentar litellm_key_budgets.observador e rodar o setup-ai-keys.yml |
| Hub fora | Mesmo comportamento (sem ferramentas, sem análise) | Portainer → stack agents |
| Observador fora | O Grafana registra falha só na integração dele; o n8n recebe o alerta normalmente | docker logs observador; Portainer → stack agents |
| Análise sem sentido | O modelo errou ou leu pouco | Ver o trace no Langfuse; trocar observador_modelo para principal (mais caro) |
| Muitas análises | Limites por hora e por alerta seguram o volume | Ajustar OBSERVADOR_MAX_POR_HORA ou incluir o alerta em OBSERVADOR_IGNORAR |