top of page

Além do bot: A anatomia de um agente operacional para emergências

  • Foto do escritor: João Ataide
    João Ataide
  • há 15 horas
  • 10 min de leitura

Quando se fala em automação em geoprocessamento, a imagem mais comum é a de um script rodando em loop ou de um bot que responde a comandos pré-definidos. Em cenários críticos, como acidentes de trânsito, essa rigidez pode virar risco: o sistema precisa lidar com incerteza, falhas e mudanças de contexto. Por isso, mais do que um executor de tarefas, o que faz diferença é um Agent.


A distinção é simples, mas é bastante importante. Enquanto um bot percorre um fluxo linear, um Agent interpreta o contexto, escolhe quais tools acionar, em que ordem, e ajusta o plano conforme a informação disponível, para esse caso específico ele não apenas reage, ele coordena o processo.


Neste artigo, vou destrinchar a construção de um operational GIS Agent, desenvolvido para situações de emergência. A ideia é explicar o raciocínio por trás do sistema, porque cada parte existe e como decisões de projeto transformam um pipeline de análise espacial em uma aplicação interativa.


O resultado esperado é simples:

  • Localizar a ocorrência usando pontos de referência;

  • Encontrar o hospital mais próximo;

  • Traçar uma rota no mapa e gerar uma mensagem modelo de comunicação.


Preparando o terreno: dependências e constantes


Antes de falar em autonomia, o ambiente precisa estar pronto para executar ações “de verdade”. Isso exige três coisas básica, encontrar uma base espacial confiável, integrar os serviços externos e uma interface que sustente uso interativo.


Na camada espacial, GeoPandas e Shapely permitem manipular geometrias e fazer operações como interseção e recorte e outros processos de geoprocessamento. Mesmo quando o usuário só vê um ponto no mapa, por trás existe um conjunto de cálculos que precisa ser realizado, então é aí que entra nossos amigos NumPy e pandas entram como suporte para dados numéricos e estruturas tabulares, especialmente quando o resultado precisa de agregações e conversões. Já para integração com dados externos, usamos a biblioteca requests que faz a conexão nas APIs públicas onde iremos adquirir os dados.


Na interface, a combinação de ipywidgets com ipyleaflet para transforma um notebook em uma aplicação simples, mas interativa. Esse tipo de abordagem funciona bem em prova de conceito e permite a gente explorar o fluxo sem precisar construir um front-end completo, o que foi ideal para mim, já que não tenho muita familiaridade com desenvolvimento de interface.



Também existem parâmetros fixos importantes. O OVERPASS_URL aponta para a Overpass API, usada para consultar dados do OpenStreetMap, que vão nos retornar informações no entorno do das ocorrências (vias, hospitais e referências). Já o OSRM_URL define o serviço de roteamento (OSRM), que vamos usar para calcular a melhor rota até o hospital mais próximo, incluindo distância, duração e a própria geometria da rota.


Outras definições importantes é o DEFAULT_BUFFER_M define o raio padrão de análise, quanto de dados vamos trazer da API para analisar, o qual temos que tentar equilibrar a definição do contexto, mas se alertando ao desempenho, para evitar baixar e analisar a cidade toda. Em seguida precisamos definir DEFAULT_ROAD_TAGS delimita quais classes de vias são relevantes para a resposta, neste caso para evitar trazer contexto desnecessário para a aplicação que não irá impactar na ocorrência. Por fim, entram as configurações da API de IA, neste caso, a da OpenAI.



Projeção local: buffer em metros e consistência de medida


Um erro comum em análises espaciais rápidas é tratar latitude/longitude como unidade métrica, isso é muito comum para quem nunca trabalhou com Geo. Para visualização, podemos utilizar tranquilamente, mas para medida, não é o ideal. Quando calculamos distância, comprimento de via e buffer precisam ser calculados em CRS projetado, caso contrário, o resultado varia com a latitude e pode distorcer toda a análise.


Para evitar isso vamos definir uma função auxiliar calcula automaticamente um CRS UTM local, assim, podemos garantir a portabilidade das feições e realizar a projeção sem depender de uma zona fixa. No final o fluxo é bem simples, a coordenada entra em WGS84, é transformada para UTM para aplicar o buffer em metros, e depois retorna para WGS84 para manter compatibilidade.



Overpass com mirrors e tolerância a falhas


Outro ponto importante para ser alertado é que em um pipeline acadêmico, falha de API é inconveniente, já em um pipeline operacional, é esperado. Por isso, é de extrema importância tratar a instabilidade como parte do cenário.


A estratégia para resolver esse problema é combina três decisões: uso de múltiplos mirrors (reduzindo dependência de um único endpoint), tentativas com backoff exponencial (evitando insistência agressiva) e tratamento explícito de rate limit, especialmente quando a API devolve 429. Esse conjunto reduz quedas e aumenta a chance de resposta útil em condições reais.



Do OSM para o GIS: organizar antes de analisar


A resposta da Overpass mistura o que vem da API, informações como nodes, ways e relations no mesmo conjunto. Para um Agente que estamos contruindo se trabalharmos com tudo isso torna, pode tornar o pipeline frágil e difícil de manter, pelo menos foi isso que observei em outros testes. O primeiro passo, então, é organizar os dados em estruturas previsíveis.


A função de parsing separa exatamente isso: cria um dicionário de nodes por ID para acesso rápido e listas dedicadas para ways e relations, gerando o dado final menos complexo e as etapas seguintes ficam mais estáveis.



Ainda sobre os dados OpenStreetMap, é importante também tratar que uma via (way) não chega como uma linha pronta, ela vem como uma lista de IDs de nós. A conversão para geometria exige resolver esses IDs em coordenadas e, só então, montar uma LineString, que possibilita recortar, medir e visualizar as vias com maior facilidade.



Entretanto emergência, mais dados não significa melhor resposta, então, o excesso de informação pode confundir o modelo e até induzir uma resposta diferente do esperado, então para facilitar nosso trabalho precisamos filtrar somente as vias relevantes para deslocamento e contexto operacional, não de todas as trilhas e tags marginais que possam aparecer. Por isso, criamos uma função auxiliar que filtra ways pela tag highway e, quando há uma lista de classes, mantém apenas categorias definidas como úteis em DEFAULT_ROAD_TAGS.



Sabemos que os dados do OpenStreetMap (OSM) são bastante ricos, mas nem sempre consistentes, já que são levantados pela própria comunidade. Por exemplo, um hospital pode ter o campo name, pode ter apenas operator, pode não ter nenhum desses atributos ou até existir apenas como um endereço.


Para o agente, isso é um ponto crítico, pois a utilidade do resultado final depende da capacidade de nomear destinos e referências de forma clara e compreensível. Imagine se todas as referências repassadas à equipe do SAMU estivessem sem as informações necessárias.



Continuando sobre a política de nomeação, essa define uma ordem de prioridade e aplica fallback ("Hopital sem nome cadastrado") para endereço quando necessário. Isso evita respostas genéricas e melhora a clareza.

A partir daí entram duas funções auxiliares:

  • get_poi_name, que percorre os campos definidos em POI_NAME_FIELDS e retorna o primeiro valor disponível, caso contrário retorna “Ponto de Referência”

  • get_poi_kind, que complementa a identificação classificando o tipo do ponto de referência



Outro ponto que precisa ser considerado é que os hospitais podem aparecer como node, way ou relation. Por isso, é essencial contemplar essas possibilidades, reduzindo falsos negativos, como casos em que o sistema indica “hospital não encontrado” apenas por causa da forma como o dado foi modelado.


Além disso, ao padronizar os nomes, o agente tende a produzir um texto final mais útil e legível.



Mesmo com uma coordenada precisa, como comentei no artigo sobre H3, o par lat/lon, por si só, não ajuda muito a IA a entender o contexto. Para esse tipo de caso, é bem mais efetivo trabalhar com pontos de referência.


Por isso, criei uma função que extrai e busca elementos com tags de interesse (serviços, comércio, turismo e lazer), remove itens que podem confundir o relato, como hospitais, que já foram extraídos anteriormente, e retorna dois candidatos, priorizando a proximidade. Assim, fica mais fácil para a IA, ou até mesmo para o SAMU, entender que o acidente ocorreu perto da Loja X e do Shopping Y. Se quiser deixar isso ainda mais completo, poderiamos até incluir o endereço usando o reverse geocoding via https://nominatim.openstreetmap.org/reverse.



Análise de impacto no entorno do acidente


Com o buffer, a consulta ao Overpass e a conversão para geometrias, o Agent passa a ter contexto do entorno do ocorrido usando a função compute_impact. Ela começa montando uma única consulta, que busca roads, hospitals e points of interest dentro do mesmo envelope espacial (região de busca), e recorta as vias para considerar apenas o que de fato cai dentro do buffer.


Em seguida, o resultado é reprojetado para um sistema em unidades métricas. A partir daí, calcula-se o comprimento das vias e faz-se a agregação por classe, o que dá uma noção de densidade e do tipo de infraestrutura no entorno. Em paralelo, a distância até o hospital mais próximo também é calculada.


Tudo isso volta como contexto do acidente em um JSON, com informações como exemplo: accident_location, radius, road_count, total_road_length, road_length_by_class, nearest_hospital e landmarks.


Rota até o hospital mais próximo


Depois que o Agent identifica o nearest hospital, o resultado deixa de ser apenas contexto e passa a orientar uma ação. A etapa que acontece em paralelo é a de roteirização, que transforma um destino em um caminho navegável, incluindo distância, duração e geometria da rota.


Essa roteirização é feito com uma chamada à API pública do OSRM em https://router.project-osrm.org/route/v1/driving, informando origem e destino no formato lon,lat. A resposta traz uma ou mais rotas e, aqui, usamos a primeira, extraindo:

  • distance_m, distância total em metros

  • duration_s, duração estimada em segundos

  • geometry, o traçado completo da rota, decodificado de polyline para uma lista de coordenadas


Comunicação: gerando uma mensagem modelo de chamado


Aqui é um ponto interessante, em um ambiente de produção, a função draft_samu_message ficaria aberta para evolução como um componente dedicado e isolado, já que comunicação é uma etapa sensível e costuma exigir padronização, rastreabilidade e auditoria. A ideia seria que ela não apenas montasse a mensagem, mas estruturasse o chamado para envio aos órgãos competentes, no formato e no canal definidos pela operação, por exemplo, integração via API, fila de mensagens ou sistema interno.


Aqui, como o projeto é exemplificado, então essa etapa permanece integrada ao tool executor para demonstrar o fluxo completo de ponta a ponta sem aumentar a complexidade estrutural.


Tools e System Prompt: quando vira comportamento (e não só função)


Até agora, o que existe são funções. Para virar um Agent, é preciso empacotar capacidades como tools. Uma tool é um contrato: a descrição não serve apenas para documentar, ela orienta o modelo sobre quando e como usar cada recurso, quais entradas são esperadas e que tipo de saída deve ser produzida.


Quando você explicita dependências, por exemplo, que get_route e draft_samu_message devem ser chamadas depois de geo_analyze_impact, você reduz erros clássicos do fluxo, como rota sem destino definido, mensagem sem contexto e respostas que soam bem, mas não estão ancoradas nos dados.



Tools prontas, o proximo passo é definir o System Prompt que estabelece o comportamento em fluxos que espelham cenários reais na interface, no nosso caso:

  • Testemunha, com triagem guiada;

  • Emergência, com execução imediata (sem perguntas);

  • Pedido simples de rota.


As regras gerais, como não responder vazio, encerrar com aviso, não inventar dados e reforçar que é uma simulação, trazem "disciplina" ao sistema e ajudam a manter consistência ao longo das interações.



O loop do agente: pensar, agir, observar e concluir


O comportamento do Agent depende de um loop que permite múltiplas etapas dentro de uma mesma solicitação. O modelo recebe o histórico e a lista de tools, decide quais chamadas fazer, você executa, devolve os resultados e ele continua iterando até produzir a resposta final.


Esse desenho traz previsibilidade, porque você controla o limite de rodadas e evita que um chat de alertas acabe virando um bate-papo sem fim. Ao mesmo tempo, mantém a flexibilidade necessária para encadear ações, já que uma etapa pode depender diretamente do resultado da anterior. Além disso, manter conversa e resultados como histórico ajuda a resposta a permanecer coerente entre interações, reduzindo contradições e evitando que o modelo perca o contexto no meio do fluxo.



Também vale tratar um ponto prático: é preciso extrair o texto com segurança e evitar a sensação de travamento quando o SDK muda o formato de resposta, o que acontece na prática. Ao cobrir diferentes estruturas possíveis (texto direto, blocos, mensagens e recusas), a aplicação fica mais resiliente a variações do retorno da API.



Memória de curto prazo: estado compartilhado para continuidade


Um Agent sem memória de curto prazo vira um sistema cansativo, porque o usuário acaba tendo de repetir o contexto o tempo todo. Por isso, faz sentido criar uma função que armazene estado e resultados recentes, permitindo chamadas dependentes sem exigir entradas “perfeitas” a cada etapa.


Além disso, o tool executor pode normalizar parâmetros e reaproveitar o que já foi descoberto no fluxo. Se a chamada de rota não trouxer um destino explícito, por exemplo, ele recupera o hospital salvo no estado. Se a mensagem precisar de contexto, ele combina o que o usuário enviou com o último resultado geográfico disponível. Esse reaproveitamento deixa o fluxo mais fluido e aproxima a experiência do que se espera em uma aplicação real, em que o sistema mantém continuidade e reduz atrito.



Interface: a aplicação como camada final de operação


Na camada de interface, a prioridade não foi refinar engenharia de front-end, e sim entregar funcionalidade. Usei o ChatGPT como apoio para montar a UI, com mapa em camadas, histórico em formato de chat e botões de ação bem destacados para acionar os modos "Presenciei acidente”, "Tive um acidente" e "Ver rota".


O ponto central é que a UI cumpre o papel de painel operacional da simulação, serve exatamente para guiar a triagem, dispara execução imediata quando necessário e exibe resultados verificáveis no mapa. Isso ajuda a validar o que o agente inferiu e o que de fato foi calculado pelas tools.



Casos de uso

Para demonstrar o comportamento operacional do sistema, a simulação foi organizada em dois casos de uso que representam situações comuns em emergências. O objetivo não é apenas “mostrar o mapa”, mas evidenciar como o Agent muda sua postura conforme o contexto:

  • Caso de uso 1: Testemunha com triagem guiada;

  • Caso de uso 2: Vítima com execução imediata e rota no mapa

Eu sei que a tirinha ta exagerada, mas ilustra bem a ideia.


Caso de Uso 1: Testemunha com triagem guiada


O usuário entra como testemunha, responde perguntas de triagem e recebe um resumo com orientações. No mapa aparecem hospital e pontos de referência, tornando a localização mais compreensível. A mensagem modelo mostra como o resultado geográfico vira comunicação objetiva.



Caso de Uso 2:  vítima com execução imediata e rota no mapa


O usuário aciona o modo emergência, por exemplo via controle de voz, e o Agent executa a sequência completa: análise do entorno, rota e mensagem modelo. A diferença é a ausência de perguntas, ele pegaria as informações do usuário direto do aparelho. O sistema age, desenha a rota e entrega um resumo com destino, distância, duração estimada e referências.



Conclusão


O valor deste projeto não está apenas em aplicar geoprocessamento, nem apenas em usar um modelo de linguagem. Ele está em transformar funções isoladas em um sistema com comportamento operacional. As tools definem o que o Agent consegue fazer, o prompt define como ele deve agir, o loop permite encadear decisões, o estado garante continuidade e a interface torna tudo utilizável e verificável no mapa.


Mesmo sendo uma simulação educacional, a proposta já indica um caminho claro para evoluir para uma aplicação mais madura. Ela pode se transformar em um produto real ao manter o mesmo núcleo, isto é, respostas baseadas em dados estruturados, comunicação objetiva, visualização espacial e mecanismos de resiliência para lidar com falhas de APIs e variações do mundo real. A partir daí, basta acrescentar camadas operacionais, como observabilidade, auditoria, testes, governança de mensagens e integrações.

Comentários


bottom of page