top of page

Além da Latitude e Longitude: Criando Embeddings Espaciais para RAG

  • Foto do escritor: João Ataide
    João Ataide
  • há 2 horas
  • 6 min de leitura

Quando comecei a misturar modelos de linguagem (LLMs) com dados geográficos, a primeira tentação foi tratar latitude e longitude como “apenas mais duas features” e seguir adiante. Logo percebi o problema, coordenadas são ótimas, mas não capturam contexto. Latitude e longitude não dizem nada sobre vizinhança. Elas não capturam bem a noção de vizinhança, não funcionam como identidades discretas e não oferecem uma estrutura hierárquica clara, como bairro, rua e quarteirão, dificultando então fazer essa conexão.


Neste artigo, compartilho um caminho simples e prático para transformar localização em algo que se comporte como um embedding, isto é, uma representação vetorial que pode ser indexada, comparada e utilizada em retrieval. O melhor é que esse processo é totalmente determinístico, não exige treinamento, tem baixo custo e escala bem. Para isso, vamos usar o H3, biblioteca de indexação espacial criada pela Uber, muito usada quando fazemos operações e análise de camadas vetoriais.


Definindo o recorte: o Recife como exemplo


Para tornar a explicação mais concreta, escolhi trabalhar com um recorte do Recife e pontos de interesse do OpenStreetMap (OSM). A partir desse conjunto, construí um retrieval geográfico básico e, em seguida, dei o passo para combinar geografia e texto e criar uma base consistente de RAG geográfico, algo que uma LLM consegue utilizar de forma eficaz.


Antes de buscar os dados, foi necessário definir o universo do experimento. Em geoprocessamento, isso é mais relevante do que parece, pois o recorte influencia a densidade, os tipos de lugares e até o comportamento do retrieval. No meu caso, defini um bounding box aproximado do Recife.



Com o recorte estabelecido, consultei os pontos do OSM por meio da Overpass API. Selecionei categorias comuns em ambientes urbanos, como hospital, escola, farmácia, polícia e abrigo. Essas classes são fáceis de validar e representam bem a dinâmica da cidade. Assim, obtive um dataset realista e, ao mesmo tempo, interpretável.


1250

Estruturando os dados para indexação


Com os dados coletados, organizei tudo em um formato simples de indexar e depurar, gerando um DataFrame com colunas essenciais, como id, nome, categoria, latitude e longitude. Parece bobagem, mas esse passo facilita muito depois. Principalmente quando você quer evoluir o pipeline, por exemplo, ao trocar node por way ou relation e utilizar center no lugar das coordenadas diretas.



Ao analisar a distribuição das classes, observei que as escolas predominam no conjunto. Isso é positivo, pois cria uma situação próxima da realidade. Ao buscar uma classe como hospital, não quero receber escolas próximas apenas porque são maioria. Em outras palavras, o retrieval precisa compreender intenção, e não apenas densidade.






Até aqui, eu tinha apenas um conjunto de pontos com latitude e longitude. A partir deste ponto, vem a mudança central, no qual iremos tratar as coordenadas como números contínuos e convertê-las em tokens espaciais com o H3.


Convertendo coordenadas em tokens espaciais com H3


A ideia principal do H3 é dividir o mapa em hexágonos e atribuir um identificador único a cada célula, como na imagem ao lado. Para a LLM, esse identificador funciona como um token espacial.


Para utilizar esse índice, é possível escolher diferentes resoluções, representando o espaço em múltiplas escalas. Utilizei as resoluções 7, 9 e 11 para capturar desde regiões mais amplas até detalhes locais.





A partir daqui cada local deixa de ser apenas um ponto somente e passa a ser representado por um conjunto de identificadores discretos. É nesse momento que o embedding começa a tomar forma, sem necessidade de treinar uma rede.


Lidando com o problema das bordas


No mundo real, bordas importam. Dois pontos podem estar muito próximos, mas cair em hexágonos diferentes. Se esses hexágonos forem tratados como completamente distintos, o retrieval tende a falhar justamente onde deveria acertar.


Para lidar com isso, adotei uma estratégia simples, além do token da célula principal, incluí também os tokens das células vizinhas. No H3, esse conjunto é chamado de k-ring, ou grid disk. Utilizei a resolução 9 como base urbana e k = 1 para incluir os vizinhos imediatos.


Primeiro, gerei uma assinatura multiescala. Em seguida, expandi essa assinatura com as células vizinhas. Assim, o conceito de “perto” deixa de se restringir à distância e passa a incorporar a adjacência topológica, o que melhora o desempenho em áreas densas.











sig

0 [878183980ffffff, 89818398053ffff, 8b818398051...

1 [878183980ffffff, 89818398057ffff, 8b818398057...

2 [878183981ffffff, 8981839810fffff, 8b81839810e...

3 [878183981ffffff, 89818398107ffff, 8b818398105...

4 [878183981ffffff, 898183981cfffff, 8b8183981cf...


E depois expande com vizinhos:

sig_k1

0 878183980ffffff<br>89818398053ffff<br>8b818398...

1 878183980ffffff<br>89818398057ffff<br>8b818398...

2 878183981ffffff<br>8981839810fffff<br>8b818398...

3 878183981ffffff<br>89818398107ffff<br>8b818398...

4 878183981ffffff<br>898183981cfffff<br>8b818398...


Convertendo tudo em vetores (hashing trick)


Com os tokens definidos, o passo seguinte foi convertê-los em vetores numéricos de dimensão fixa. Isso permite utilizar similaridade por cosseno, índices vetoriais como FAISS ou HNSW e, consequentemente, aplicar RAG.


Para essa conversão, utilizei o hashing trick, onde cada token é mapeado para uma posição do vetor por meio de uma função hash, no caso MD5, acumulando contagens nessa posição. Ao final, normalizo o vetor. O resultado foi um conjunto de 1.250 locais do Recife convertidos em vetores prontos para retrieval.


(1250, 1024)

Visualizando o retrieval no mapa


Após transformar os pontos em vetores e estruturar o índice, busquei observar o comportamento espacial do sistema. Para isso, defini um ponto de consulta e visualizei no mapa quais locais o mecanismo de retrieval considerava mais próximos.


Utilizei uma função em Python com matplotlib:


Retrieval “perto de mim” similaridade vetorial


Retrieval é o processo de recuperar informações relevantes a partir de um conjunto de dados indexado. Em sistemas vetoriais, isso significa identificar os itens mais semelhantes a uma consulta com base em uma métrica, como similaridade por cosseno ou distância euclidiana.


Primeiro vamos criar o retrieve geográfico, para iniciar os testes de recuperação por proximidade, por meio do cálculo a similaridade em relação ao conjunto dos dados.

Essa abordagem não busca medir distâncias em metros com precisão absoluta (o que seria lento caso muitos pontos), o objetivo é oferecer uma noção prática de proximidade que seja rápida, passível de indexação e menos sensível a efeitos de borda. Em muitos contextos urbanos, esse nível de aproximação é suficiente, em especial casos de uso como "Qual hospital mais próximos dessa coordenada?".


Obviamente, isso não é perfeito, veja que ela ainda selecionou algumas classes que não são hospitais. Além disso, caso um usuário fizesse a busca bem na divisa entre dois bairros, poderia rolar uma confusão. Mas na prática, esse é um processo que pode funcionar bem o suficiente para maioria dos casos do dia a dia.


Combinando texto e geografia: o passo que cria o RAG geográfico


Até aqui, eu respondia à pergunta “o que está perto desta coordenada?”. No entanto, a dúvida real do usuário costuma ser outra “quais hospitais estão perto de mim?”.


Para incorporar a intenção semântica à busca, gerei embeddings de texto a partir do nome e da categoria utilizando o modelo SentenceTransformer. Optei pelo modelo all-MiniLM-L6-v2, que para mim ofereceu equilíbrio entre desempenho e custo computacional, bem leve e funcionou bem na minha máquina. Em seguida, concatenei os embeddings o textual e o geográfico.



Quando analisei o resultado o ganho foi imediato. O sistema deixa de ser apenas geográfico e passa a considerar simultaneamente localização e intenção. Em vez de retornar qualquer ponto próximo, ele passa a priorizar o que está próximo e semanticamente alinhado à consulta.


Agora sim temos um RAG geográfico de verdade. O retriever seleciona o contexto mais adequado, e a LLM assume a etapa linguística, explicando, comparando ou recomendando com base nos resultados recuperados.



Conclusão


Aqui eu parti de uma limitação clara das coordenadas. Quando usadas isoladamente, latitude e longitude não representam adequadamente o contexto. Ao converter o espaço em tokens H3 multiescala, incorporar a adjacência com k-ring e transformar essas assinaturas em vetores por meio do hashing trick, construí um embedding geográfico simples, determinístico e eficiente. Esse processo dispensa treinamento, tem baixo custo computacional e escala de forma previsível.


O ponto mais relevante, porém, não é apenas técnico. Ao transformar a localização em uma representação vetorial compatível com mecanismos modernos de retrieval, passamos a tratar o espaço como parte da linguagem do sistema. A geografia deixa de ser um filtro externo e passa a integrar o próprio raciocínio do modelo.


Quando esse embedding é combinado a representações textuais geradas pelo SentenceTransformer, surge um retriever capaz de compreender simultaneamente o que se busca e onde se busca. Nesse estágio, o RAG geográfico deixa de ser uma adaptação pontual e passa a operar como uma arquitetura integrada, unindo localização e intenção semântica de forma prática e aplicável a contextos reais, da busca contextual urbana a sistemas inteligentes de apoio à decisão.


No fim, isso vai além de indexação espacial, é uma forma de fazer LLMs entenderem geografia de verdade, não só descreverem coordenadas, o que é um passo essencial para que LLMs deixem de apenas descrever o espaço e passem, de fato, a compreendê-lo.

bottom of page