Seu Primeiro RAG com Spring AI: Crie um Agente de IA Local que Realmente Funciona (Sem Nuvem Necessária)

Crie seu primeiro sistema RAG com Spring AI, Ollama e Kotlin — processe documentos, responda perguntas e mantenha seus dados privados.


O problema que começou tudo


Imagine o seguinte: você é dono de um negócio ou líder técnico que precisa processar documentos, responder perguntas de clientes e extrair insights dos seus dados. Mas toda solução de IA que você encontra:

  • Custa uma fortuna em chamadas de API,
  • Envia dados sensíveis para a nuvem, ou
  • Parece um stack totalmente diferente da experiência do seu time.

Soa familiar?

Há três meses, enfrentei o mesmo desafio. Como engenheiro de software com mais de 10 anos de experiência no ecossistema JVM (Java, Kotlin), percebi algo: embora o Python domine as discussões sobre IA, milhões de desenvolvedores trabalham diariamente com tecnologias JVM — e eles não deveriam precisar mudar de stack para aproveitar o poder da inteligência artificial.

Foi assim que construímos um sistema RAG local (Retrieval-Augmented Generation) usando Spring AI + Kotlin + Ollama: privado, econômico e nativo para as ferramentas que os desenvolvedores JVM já dominam.

“O projeto foi criado com a crença de que a próxima onda de aplicações de IA Generativa não será apenas para desenvolvedores Python, mas será ubíqua em muitas linguagens de programação.” – Equipe de engenharia do Spring AI




O que você vai aprender


Ao final deste artigo, você entenderá:

  • Por que a IA local não é apenas uma questão de privacidade — é uma verdadeira revolução.
  • Como escolher o modelo de linguagem (LLM) ideal para o seu caso de uso.
  • A arquitetura por trás de um sistema RAG.
  • Um exemplo de código usando PDF e Markdown como fonte de conhecimento.
  • Os desafios que enfrentei e como evitá-los.



Entendendo a base: Conceitos essenciais para IA local

Antes de mergulhar na implementação, vamos revisar os conceitos fundamentais que tornam esta demonstração possível.

Pense nisso como um curso rápido de vocabulário em IA: compreender esses seis elementos ajudará você a acompanhar e tomar decisões mais assertivas na sua própria implementação.

Modelos de Linguagem de Grande Escala (LLMs) são poderosas redes neurais treinadas em imensos conjuntos de texto.

Eles podem gerar, resumir, traduzir e responder perguntas usando Processamento de Linguagem Natural, atuando como reconhecedores de padrões avançados que entendem contexto e intenção.

Agentes de IA vão além.

São LLMs que raciocinam, agem e utilizam ferramentas como APIs, bancos de dados ou sistemas de arquivos. Não apenas conversam — eles executam tarefas reais.

Retrieval-Augmented Generation (RAG) conecta os LLMs aos seus próprios dados — PDFs, CSVs ou documentos internos.

Em vez de “adivinhar”, o modelo primeiro recupera trechos relevantes e depois gera respostas baseadas nesse contexto. Isso transforma um LLM genérico em um especialista no seu domínio.

Vector Stores e Embeddings tornam isso possível.

Ao converter texto em vetores semânticos, o RAG permite uma busca baseada em similaridade, compreendendo o significado, não apenas as palavras.

GGUF (GPT-Generated Unified Format) viabiliza a IA local.

Esses modelos otimizados e quantizados podem rodar de forma eficiente em notebooks ou dispositivos de borda, democratizando o acesso à IA avançada.

Por fim, Spring AI e Ollama unem tudo:

O Spring AI integra facilmente LLMs em aplicações JVM, enquanto o Ollama executa modelos quantizados localmente via CLI ou REST — dando aos desenvolvedores o poder de construir sistemas de IA privados e voltados para o local.




Por que ir para o local? (Spoiler: não é só sobre privacidade)

Executar LLMs localmente não é apenas um “truque de desenvolvedor” — é, muitas vezes, a maneira mais segura, econômica e flexível de implantar soluções de IA generativa em ambientes de produção.

O argumento a favor dos modelos de IA locais nunca foi tão forte.

Como destaca Rod Johnson (criador do Spring):

“Os modelos locais são o futuro do desenvolvimento de IA.” (Johnson, 2025)

Eles permitem integração mais profunda, comportamento transparente e personalização completa, sem dependências externas.

Na prática, os LLMs com abordagem local oferecem várias vantagens fundamentais:

  • Privacidade em primeiro lugar: seus documentos sensíveis permanecem seguros no seu próprio ambiente, garantindo conformidade com o GDPR.
  • Desenvolvimento econômico: modelos locais eliminam as taxas contínuas de API, permitindo iterações ilimitadas no hardware existente.
  • Ecológico: ajuste o tamanho do modelo — uma IA menor significa uma pegada de carbono reduzida.
  • Prototipagem mais rápida: desfrute de um desenvolvimento ágil, iterativo e sem atrasos de infraestrutura em nuvem.
  • Conformidade regulatória: modelos locais proporcionam controle total de dados e atendimento a requisitos geográficos rigorosos.



Arquitetura: O que você vai construir

Local RAG implementation by Alejandro Mantilla inspired by Bijit Ghosh


O fluxo é direto:

  • Ingestão de documentos: envie arquivos PDF ou Markdown.
  • Divisão e embeddings: separe os documentos em partes e gere seus embeddings.
  • Armazenamento vetorial: armazene os embeddings no PGVector.
  • Processamento de consultas: o usuário faz uma pergunta.
  • Recuperação: encontre os trechos relevantes por meio de similaridade vetorial.
  • Geração: o LLM gera uma resposta com base no contexto recuperado.



Visão geral do stack tecnológico


Aqui está o que usei e o motivo:

  • Spring AI + Kotlin: porque nem tudo precisa ser Python. O Spring AI 1.0 GA provou que é possível criar uma camada de abstração modular e poderosa sobre LLMs e bancos vetoriais.
  • Ollama: o “Docker dos modelos de IA”. Torna os LLMs portáteis, acessíveis e fáceis de implantar. Roda na porta 11434 com uma API REST pronta para receber requisições.
  • PGVector: PostgreSQL com extensões vetoriais. Banco de dados conhecido, agora com capacidades semânticas.
  • LLMs locais: Gemma3, Mistral, Phi-2 e outros modelos otimizados para rodar eficientemente em hardware de consumo.



Codificação — Fluxo de Demonstração ao Vivo


A seguir, está a implementação principal do RAG em Kotlin:


Bloco de código 1: Configuração do banco de dados vetorial

Esta é a base para armazenar e recuperar os embeddings, parte essencial da funcionalidade RAG (Retrieval-Augmented Generation).

Toda a configuração é feita de forma programática usando o padrão Builder.

@Configuration
@EnableJpaRepositories
class VectorDatabaseConfig {
    
    @Bean
    @Primary
    fun dataSource(): DataSource {
        val config = HikariConfig()
        config.jdbcUrl = "jdbc:postgresql://localhost:5432/ssc_agent_db"
        config.username = "postgres"
        config.password = "password"
        config.driverClassName = "org.postgresql.Driver"
        
        // Enable pgvector extension support
        config.addDataSourceProperty("stringtype", "unspecified")
        
        return HikariDataSource(config)
    }
    
    @Bean
    fun vectorStore(
        dataSource: DataSource,
        embeddingModel: EmbeddingModel
    ): PgVectorStore {
        return PgVectorStore.builder(dataSource, embeddingModel)
            .withSchemaName("public")
            .withTableName("document_embeddings")
            // Configure vector dimensions - must match embedding model output
            .withDimensions(1024)
            // Set index type to HNSW for fast similarity search
            .withIndexType(PgVectorStore.PgIndexType.HNSW)
            // Use cosine distance for semantic similarity measurement
            .withDistanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)
            // Configure batch processing for better performance
            .withMaxDocumentBatchSize(10000)
            // Enable automatic schema initialization
            .withInitializeSchema(true)
            // Enable vector table validations for data integrity
            .withVectorTableValidationsEnabled(true)
            // Optional: Configure HNSW-specific parameters for performance tuning
            .withHnswEfConstruction(200)  // Higher = better recall, slower build
            .withHnswM(16)                // Higher = better recall, more memory
            .build()
    }
}



Bloco de código 2: Processamento de documentos

Esta é a lógica central de negócios que transforma documentos empresariais brutos em conhecimento estruturado e pesquisável.

Ela lida com tarefas essenciais como ingestão de documentos, divisão inteligente de texto, geração de embeddings e armazenamento eficiente em bancos de dados vetoriais.

Esses processos formam a base da base de conhecimento que permite ao agente de IA raciocinar sobre o contexto específico da sua empresa.

Nesta configuração, modelos como mxbai-embed-large e nomic-embed-text são inicializados para gerar embeddings de alta qualidade a partir de diversos formatos, enquanto gemma3 é usado para geração de linguagem controlada e econômica, com temperatura ajustada em 0.4 (valores baixos tornam as respostas mais focadas e previsíveis, enquanto valores altos aumentam a aleatoriedade e variabilidade).

Todo o fluxo é orquestrado localmente usando o Ollama, garantindo privacidade e baixa latência.

Isso permite que o agente de IA forneça respostas inteligentes, rápidas e contextualizadas, baseadas nos dados internos do seu negócio.

spring.ai.ollama.base-url= http://localhost:11434
spring.ai.ollama.init.embedding.additional-models= mxbai-embed-large, nomic-embed-text
spring.ai.ollama.chat.options.temperature = 0.4
spring.ai.ollama.chat.options.model = gemma3



Bloco de código 3: Implementação RAG no IngestionService

Este serviço demonstra o pipeline RAG (Retrieval-Augmented Generation) em sua forma mais simples e eficaz.

O método queryRAGKnowledge executa uma busca por similaridade vetorial para encontrar os documentos mais relevantes, combina o conteúdo em uma string de contexto e cria um prompt de sistema que instrui a IA a usar apenas as informações recuperadas ou responder “IDK :(” quando não tiver certeza.

Usando o Ollama como LLM local, o sistema gera respostas fundamentadas nos próprios dados da empresa.

O serviço também gerencia a ingestão de documentos em vários formatos (PDF, Markdown, Imagens), com divisão de tokens consistente e armazenamento vetorial uniforme, tornando-se uma solução completa de gestão do conhecimento.

@Service
class IngestionService(
    private val vectorStore: VectorStore,
    private val pdfDocumentReader: PdfDocumentReader,
    private val markdownReader: MarkdownReader,
    private val imageReader: ImageReader,
    private val ollamaChatModel: OllamaChatModel
) {
    private val logger = LoggerFactory.getLogger(IngestionService::class.java)

    fun ingest(type: IngestionType) {
        when (type) {
            IngestionType.PDF -> ingestPdf()
            IngestionType.MARKDOWN -> ingestMarkdown()
            IngestionType.IMG -> ingestImage()
        }
    }

    private fun ingestPdf() {
        logger.info("Ingesting PDF using PdfDocumentReader component")
        pdfDocumentReader.getDocsFromPdfWithCatalog()
            .let { TokenTextSplitter().apply(it) }
            .let { vectorStore.add(it) }
        logger.info("PDF loaded into vector store")
    }

    // ...
    // ...
    // ...

    /**
     * Main RAG query method - retrieves similar documents and generates response
     * This is the core of the local AI agent's intelligence
     */
    fun queryRAGKnowledge(query: String): ResponseEntity<String> {
        // Step 1: Find similar documents from vector store
        val information = vectorStore.similaritySearch(query)
            ?.joinToString(System.lineSeparator()) { it.getFormattedContent() }
            .orEmpty()

        // Step 2: Create system prompt with retrieved information
        val systemPromptTemplate = SystemPromptTemplate(
            """
        You are a helpful assistant.
        Use only the following information to answer the question.
        Do not use any other information. If you do not know, simply answer: IDK :(

        {information}
        """.trimIndent()
        )

        // Step 3: Build prompt with context and user query
        val systemMessage = systemPromptTemplate.createMessage(mapOf("information" to information))
        val userMessage = PromptTemplate("{query}").createMessage(mapOf("query" to query))
        val prompt = Prompt(listOf(systemMessage, userMessage))

        // Step 4: Generate response using Ollama chat model
        return ollamaChatModel.call(prompt)
            .result
            .output
            .text
            .let { ResponseEntity.ok(it) }
    }
}

// Supporting Data Classes and Enums
data class ChatRequest(val message: String)
data class ChatResponse(
    val message: String,
    val sources: List<String>,
    val timestamp: LocalDateTime
)

enum class IngestionType {
    PDF, MARKDOWN, IMG
}



Lições aprendidas / Dicas de desenvolvimento

💡 A IA não é mais o futuro — é o seu localhost

A diferença de desempenho entre modelos locais e em nuvem está diminuindo rapidamente.

Para muitos casos de uso, os modelos locais já são “bons o suficiente” e trazem grandes vantagens.

🔐 Privacidade + Controle = Superpoderes

Garantir a privacidade dos dados abre portas com clientes que antes nem consideravam soluções baseadas em IA.

🧩 Spring AI + Kotlin = Experiência de desenvolvimento limpa

A maturidade do ecossistema Spring, combinada com a expressividade do Kotlin, cria uma experiência de desenvolvimento que rivaliza com o Python para aplicações de IA.

🧠 Comece simples

Não tente construir um GPT-5 no primeiro dia.

Comece com um RAG básico, faça funcionar e depois adicione complexidade.




Recursos e próximos passos

Todo o código, arquivos de configuração e documentos de exemplo estão prontos para você no meu repositório do GitHub.

➡️ Repositório oficial: https://github.com/AlejoJamC/ssc-local-agent

🔧 O que você precisa para começar:

# 1. Instale o Ollama
curl -fsSL https://ollama.ai/install.sh | sh

# 2. Baixe um modelo
ollama pull gemma3

# 3. Clone o repositório
git clone https://github.com/AlejoJamC/ssc-local-agent.git

# 4. Execute a aplicação
./gradlew bootRun


E é isso. Sem configuração em nuvem, sem chaves de API, sem cartão de crédito.

🎥 Assista à demonstração ao vivo

SSC Meetup Talk: “Your First RAG with Spring AI”

Veja a implementação completa em ação, com sessão de perguntas e respostas em tempo real.

📚 Documentação oficial e exemplos

  • Spring AI 1.0 GA Release — anúncio oficial com os principais recursos.
  • Awesome Spring AI Community Samples — coleção curada de implementações Spring AI.

🔍 Ferramentas para seleção de modelos

  • Artificial Analysis — compare desempenho, custo e velocidade entre provedores.
  • LMArena Leaderboard — ranking e comparações conduzidas pela comunidade.
  • 🤗 Open LLM Leaderboard — avaliação abrangente de modelos no Hugging Face.

👉 O que vem a seguir?

Tente construir seu próprio sistema RAG.

Comece pelo repositório do GitHub, experimente diferentes modelos e descubra qual funciona melhor para o seu caso de uso.

A barreira de entrada nunca foi tão baixa.

Tem dúvidas sobre a implementação?

Quer discutir estratégias de seleção de modelos?

Deixe um comentário abaixo ou conecte-se comigo no LinkedIn.

Lembre-se: a melhor IA é aquela que você realmente usa.

E, às vezes, isso significa mantê-la local.


Referências

Liu, F., Kang, Z. and Han, X. (2024) ‘Optimizing RAG Techniques for Automotive Industry PDF Chatbots: A Case Study with Locally Deployed Ollama ModelsOptimizing RAG Techniques Based on Locally Deployed Ollama ModelsA Case Study with Locally Deployed Ollama Models’, in Proceedings of 2024 3rd International Conference on Artificial Intelligence and Intelligent Information Processing, AIIIP 2024. New York, NY, USA: ACM, pp. 152–159. Available at: https://doi.org/10.1145/3707292.3707358.

Johnson, Rod (2025) Why you should use local models. Medium. 30 May. Available at: https://medium.com/@springrod/why-you-should-use-local-models-a3fce1124c94 (Accessed: 6 July 2025).

Spring.io (2025) ‘Spring AI 1.0 GA Released’ [online image] Available at: https://spring.io/blog/2025/05/20/spring-ai-1-0-GA-released [Accessed 20 May 2025].

Mantilla Celis, J.A. (2025) ‘Build a Local AI Agent for Small Businesses [Local RAG implementation diagram], inspired by the work of Bijit, B. (2024) ‘Advanced RAG for LLMs & SLMs’, Medium, 21 April. Available at: https://medium.com/@bijit211987/advanced-rag-for-llms-slms-5bcc6fbba411 (Accessed: 18 May 2025).

#AI #RAG #SpringAI #Kotlin #LocalAI #MachineLearning #Privacy #OpenSource #Ollama #Gemma3

Tu Primer RAG con Spring AI: Crea un Agente de IA Local que Realmente Funciona (Sin Necesidad de la Nube)

Crea tu primer sistema RAG con Spring AI, Ollama y Kotlin — procesa documentos, responde preguntas y mantén tus datos privados.

El problema que lo empezó todo

Imagina esto: eres dueño de un negocio o líder técnico que necesita procesar documentos, responder preguntas de clientes y extraer información útil de tus datos. Pero cada solución de IA que encuentras:

  • Cuesta una fortuna en llamadas a la API,
  • Envía datos sensibles a la nube, o
  • Se siente como un stack completamente ajeno al conocimiento de tu equipo.


¿Te suena familiar?

Hace tres meses enfrenté el mismo desafío. Como ingeniero de software con más de 10 años de experiencia en el ecosistema JVM (Java, Kotlin), me di cuenta de algo: aunque Python domina las conversaciones sobre IA, millones de desarrolladores trabajan a diario con tecnologías JVM, y no deberían tener que cambiar de stack para aprovechar el poder de la inteligencia artificial.

Así fue como construimos un sistema RAG local (Retrieval-Augmented Generation) usando Spring AI + Kotlin + Ollama: privado, rentable y nativo para las herramientas que los desarrolladores JVM ya conocen.

“El proyecto nació con la creencia de que la próxima ola de aplicaciones de IA Generativa no será solo para desarrolladores de Python, sino que será ubicua en muchos lenguajes de programación.” – Equipo de ingeniería de Spring AI




Lo que vas a aprender

  • Por qué la IA local no es solo una moda por privacidad, sino un cambio de juego real.
  • Cómo elegir el modelo de lenguaje (LLM) adecuado para tu caso de uso.
  • La arquitectura detrás de un sistema RAG.
  • Un ejemplo de código usando PDF y Markdown como fuente de conocimiento.
  • Los desafíos que enfrenté y cómo evitarlos.



Entendiendo la base: Conceptos clave para la IA local

Antes de entrar en la implementación, establezcamos los conceptos fundamentales que hacen posible esta demo.

Piénsalo como un curso exprés de vocabulario en IA: comprender estos seis elementos te ayudará a seguir el proceso y tomar decisiones informadas sobre tu propia implementación.

Modelos de Lenguaje de Gran Escala (LLMs) son potentes redes neuronales entrenadas con enormes corpus de texto.

Pueden generar, resumir, traducir y responder preguntas utilizando procesamiento de lenguaje natural, actuando como reconocedores avanzados de patrones que comprenden contexto e intención.

Agentes de IA van un paso más allá.

Son LLMs capaces de razonar, actuar y utilizar herramientas como APIs, bases de datos o sistemas de archivos. No solo conversan; resuelven tareas reales.

Retrieval-Augmented Generation (RAG) conecta los LLMs con tus propios datos —PDFs, CSVs o documentos internos.

En lugar de “adivinar”, el modelo primero recupera fragmentos relevantes y luego genera respuestas basadas en ese contexto. Esto convierte a un LLM genérico en un experto en tu dominio.

Vector Stores y Embeddings hacen esto posible.

Al convertir texto en vectores semánticos, RAG permite una recuperación basada en similitud, entendiendo el significado, no solo las palabras.

GGUF (GPT-Generated Unified Format) hace viable la IA local.

Estos modelos optimizados y cuantizados pueden ejecutarse eficientemente en portátiles o dispositivos de borde, democratizando el acceso a la IA avanzada.

Finalmente, Spring AI y Ollama integran todo:

Spring AI permite conectar fácilmente LLMs dentro de aplicaciones JVM, mientras que Ollama ejecuta modelos cuantizados localmente mediante CLI o REST —dando a los desarrolladores el poder de construir sistemas de IA privados y con enfoque local.


¿Por qué hacerlo local? (Spoiler: no se trata solo de la privacidad)

Ejecutar modelos LLM de forma local no es solo una muestra de habilidad técnica — suele ser la forma más segura, rentable y flexible de desplegar soluciones de IA generativa en entornos de producción.

El argumento a favor de los modelos de IA locales nunca ha sido tan sólido.

Como señala Rod Johnson (creador de Spring):

“Los modelos locales son el futuro del desarrollo de IA” (Johnson, 2025).

Permiten una integración más estrecha, un comportamiento transparente y una personalización completa sin depender de servicios externos.

En la práctica, los LLM con enfoque local desbloquean varias ventajas clave:

  • Privacidad ante todo: tus documentos sensibles permanecen seguros dentro de tu propio entorno, garantizando el cumplimiento de normas como el GDPR.
  • Desarrollo rentable: los modelos locales eliminan los costos recurrentes por uso de API, permitiendo iterar sin límites en el hardware que ya tienes.
  • Ecológico: ajusta el tamaño de tu modelo; una IA más pequeña implica una menor huella de carbono.
  • Prototipado más rápido: disfruta de un desarrollo ágil, iterativo y sin demoras por infraestructura en la nube.
  • Cumplimiento normativo: los modelos locales facilitan el control de datos y el cumplimiento de regulaciones geográficas estrictas.

Arquitectura: Lo que vas a construir

Local RAG implementation by Alejandro Mantilla inspired by Bijit Ghosh

Local RAG implementation by Alejandro Mantilla inspired by Bijit Ghosh


El flujo es sencillo:

  • Ingesta de documentos: carga archivos PDF o Markdown.
  • Fragmentación y embeddings: divide los documentos en fragmentos y genera sus embeddings.
  • Almacenamiento vectorial: guarda los embeddings en PGVector.
  • Procesamiento de consultas: el usuario formula una pregunta.
  • Recuperación: se buscan los fragmentos relevantes mediante similitud vectorial.
  • Generación: el LLM genera una respuesta basada en el contexto recuperado.

Resumen del stack tecnológico

Esto fue lo que utilicé y por qué:

  • Spring AI + Kotlin: porque no todo tiene que ser Python. La versión 1.0 GA de Spring AI demostró que es posible crear una capa de abstracción modular y potente sobre los LLMs y los almacenes vectoriales.
  • Ollama: el “Docker de los modelos de IA”. Hace que los LLMs sean portables, accesibles y fáciles de desplegar. Se ejecuta en el puerto 11434 con una API REST lista para recibir solicitudes.
  • PGVector: PostgreSQL con extensiones vectoriales. Una base de datos familiar, ahora con capacidades semánticas.
  • LLMs locales: Gemma3, Mistral, Phi-2 y otros modelos optimizados para correr eficientemente en hardware de consumo.

Codificación — Flujo de Demo en Vivo

A continuación, se muestra la implementación principal de RAG en Kotlin:

Bloque de código 1: Configuración de la base de datos vectorial

Esta es la base para almacenar y recuperar los embeddings, elemento esencial para la funcionalidad RAG (Retrieval-Augmented Generation).

Toda la configuración se realiza de forma programática utilizando el patrón Builder.

@Configuration
@EnableJpaRepositories
class VectorDatabaseConfig {
    
    @Bean
    @Primary
    fun dataSource(): DataSource {
        val config = HikariConfig()
        config.jdbcUrl = "jdbc:postgresql://localhost:5432/ssc_agent_db"
        config.username = "postgres"
        config.password = "password"
        config.driverClassName = "org.postgresql.Driver"
        
        // Enable pgvector extension support
        config.addDataSourceProperty("stringtype", "unspecified")
        
        return HikariDataSource(config)
    }
    
    @Bean
    fun vectorStore(
        dataSource: DataSource,
        embeddingModel: EmbeddingModel
    ): PgVectorStore {
        return PgVectorStore.builder(dataSource, embeddingModel)
            .withSchemaName("public")
            .withTableName("document_embeddings")
            // Configure vector dimensions - must match embedding model output
            .withDimensions(1024)
            // Set index type to HNSW for fast similarity search
            .withIndexType(PgVectorStore.PgIndexType.HNSW)
            // Use cosine distance for semantic similarity measurement
            .withDistanceType(PgVectorStore.PgDistanceType.COSINE_DISTANCE)
            // Configure batch processing for better performance
            .withMaxDocumentBatchSize(10000)
            // Enable automatic schema initialization
            .withInitializeSchema(true)
            // Enable vector table validations for data integrity
            .withVectorTableValidationsEnabled(true)
            // Optional: Configure HNSW-specific parameters for performance tuning
            .withHnswEfConstruction(200)  // Higher = better recall, slower build
            .withHnswM(16)                // Higher = better recall, more memory
            .build()
    }
}

Bloque de código 2: Procesamiento de documentos

Esta es la lógica de negocio principal que transforma documentos empresariales en bruto en conocimiento estructurado y consultable.

Se encarga de tareas clave como la ingesta de documentos, la fragmentación inteligente del texto, la generación de embeddings, y el almacenamiento eficiente en bases de datos vectoriales.

Estos procesos forman la base de la base de conocimiento que permite al agente de IA razonar sobre el contexto específico de tu negocio.

En esta configuración, modelos como mxbai-embed-large y nomic-embed-text se inicializan para generar embeddings de alta calidad desde diversos formatos, mientras que gemma3 se utiliza para una generación de lenguaje controlada y rentable, con una temperatura ajustada en 0.4 (valores bajos hacen las respuestas más enfocadas y predecibles, mientras que valores altos aumentan la aleatoriedad y variabilidad).

Todo el flujo se orquesta localmente usando Ollama, garantizando privacidad y baja latencia.

Esto permite que el agente de IA ofrezca respuestas inteligentes, rápidas y contextualizadas, basadas en los datos internos de tu organización.

spring.ai.ollama.base-url= http://localhost:11434
spring.ai.ollama.init.embedding.additional-models= mxbai-embed-large, nomic-embed-text
spring.ai.ollama.chat.options.temperature = 0.4
spring.ai.ollama.chat.options.model = gemma3

Bloque de código 3: Implementación RAG en IngestionService

Este servicio demuestra el pipeline RAG (Retrieval-Augmented Generation) en su forma más simple y efectiva.

El método queryRAGKnowledge realiza una búsqueda por similitud vectorial para encontrar los documentos más relevantes, combina su contenido en una cadena de contexto y crea un prompt del sistema que instruye a la IA a usar solo la información recuperada o responder “IDK :(” cuando no está segura.

Usando Ollama como LLM local, el sistema genera respuestas fundamentadas en los propios datos del negocio.

El servicio también gestiona la ingesta de documentos en múltiples formatos (PDF, Markdown, Imágenes) con una división de tokens coherente y almacenamiento vectorial uniforme, convirtiéndose así en una solución completa de gestión del conocimiento.

@Service
class IngestionService(
    private val vectorStore: VectorStore,
    private val pdfDocumentReader: PdfDocumentReader,
    private val markdownReader: MarkdownReader,
    private val imageReader: ImageReader,
    private val ollamaChatModel: OllamaChatModel
) {
    private val logger = LoggerFactory.getLogger(IngestionService::class.java)

    fun ingest(type: IngestionType) {
        when (type) {
            IngestionType.PDF -> ingestPdf()
            IngestionType.MARKDOWN -> ingestMarkdown()
            IngestionType.IMG -> ingestImage()
        }
    }

    private fun ingestPdf() {
        logger.info("Ingesting PDF using PdfDocumentReader component")
        pdfDocumentReader.getDocsFromPdfWithCatalog()
            .let { TokenTextSplitter().apply(it) }
            .let { vectorStore.add(it) }
        logger.info("PDF loaded into vector store")
    }

    // ...
    // ...
    // ...

    /**
     * Main RAG query method - retrieves similar documents and generates response
     * This is the core of the local AI agent's intelligence
     */
    fun queryRAGKnowledge(query: String): ResponseEntity<String> {
        // Step 1: Find similar documents from vector store
        val information = vectorStore.similaritySearch(query)
            ?.joinToString(System.lineSeparator()) { it.getFormattedContent() }
            .orEmpty()

        // Step 2: Create system prompt with retrieved information
        val systemPromptTemplate = SystemPromptTemplate(
            """
        You are a helpful assistant.
        Use only the following information to answer the question.
        Do not use any other information. If you do not know, simply answer: IDK :(

        {information}
        """.trimIndent()
        )

        // Step 3: Build prompt with context and user query
        val systemMessage = systemPromptTemplate.createMessage(mapOf("information" to information))
        val userMessage = PromptTemplate("{query}").createMessage(mapOf("query" to query))
        val prompt = Prompt(listOf(systemMessage, userMessage))

        // Step 4: Generate response using Ollama chat model
        return ollamaChatModel.call(prompt)
            .result
            .output
            .text
            .let { ResponseEntity.ok(it) }
    }
}

// Supporting Data Classes and Enums
data class ChatRequest(val message: String)
data class ChatResponse(
    val message: String,
    val sources: List<String>,
    val timestamp: LocalDateTime
)

enum class IngestionType {
    PDF, MARKDOWN, IMG
}

Lecciones aprendidas / Consejos para desarrolladores

💡 La IA ya no es el futuro — es tu localhost

La brecha de rendimiento entre modelos locales y en la nube se está cerrando rápidamente.

Para muchos casos de uso, los modelos locales son “suficientemente buenos” y ofrecen ventajas significativas.

🔐 Privacidad + Control = Superpoderes

Garantizar la privacidad de los datos abre puertas con clientes que antes ni consideraban soluciones basadas en IA.

🧩 Spring AI + Kotlin = Experiencia de desarrollo limpia

La madurez del ecosistema Spring, combinada con la expresividad de Kotlin, crea una experiencia de desarrollo que rivaliza con Python para aplicaciones de IA.

🧠 Empieza simple

No intentes construir un GPT-5 el primer día.

Empieza con un RAG básico, haz que funcione, y luego agrega complejidad.


Recursos y próximos pasos

Todo el código, archivos de configuración y documentos de ejemplo están listos para ti en mi repositorio de GitHub.

➡️ Repositorio oficial: https://github.com/AlejoJamC/ssc-local-agent

🔧 Lo que necesitas para comenzar:

# 1. Instala Ollama
curl -fsSL https://ollama.ai/install.sh | sh

# 2. Descarga un modelo
ollama pull gemma3

# 3. Clona el repositorio
git clone https://github.com/AlejoJamC/ssc-local-agent.git

# 4. Ejecuta la aplicación
./gradlew bootRun


Y eso es todo. Sin configuración en la nube, sin llaves de API, sin tarjeta de crédito.

🎥 Mira la demo en vivo

SSC Meetup Talk: “Your First RAG with Spring AI”

Mira la implementación completa en acción, con sesión de preguntas y respuestas en tiempo real.

📚 Documentación oficial y ejemplos

  • Spring AI 1.0 GA Release — anuncio oficial con las principales funcionalidades.
  • Awesome Spring AI Community Samples — colección curada de implementaciones de Spring AI.

🔍 Herramientas para selección de modelos

  • Artificial Analysis — compara rendimiento, costo y velocidad entre proveedores.
  • LMArena Leaderboard — ranking y comparaciones impulsadas por la comunidad.
  • 🤗 Open LLM Leaderboard — evaluación integral de modelos en Hugging Face.

👉 ¿Qué sigue?

Intenta construir tu propio sistema RAG.

Empieza con el repositorio de GitHub, experimenta con diferentes modelos y descubre cuál se adapta mejor a tu caso de uso.

La barrera de entrada nunca ha sido tan baja.

¿Tienes preguntas sobre la implementación?

¿Quieres hablar sobre estrategias de selección de modelos?

Déjame un comentario abajo o conéctate conmigo en LinkedIn.

Recuerda: la mejor IA es la que realmente usas.

Y, a veces, eso significa mantenerla local.


Referencias

Liu, F., Kang, Z. and Han, X. (2024) ‘Optimizing RAG Techniques for Automotive Industry PDF Chatbots: A Case Study with Locally Deployed Ollama ModelsOptimizing RAG Techniques Based on Locally Deployed Ollama ModelsA Case Study with Locally Deployed Ollama Models’, in Proceedings of 2024 3rd International Conference on Artificial Intelligence and Intelligent Information Processing, AIIIP 2024. New York, NY, USA: ACM, pp. 152–159. Available at: https://doi.org/10.1145/3707292.3707358.

Johnson, Rod (2025) Why you should use local models. Medium. 30 May. Available at: https://medium.com/@springrod/why-you-should-use-local-models-a3fce1124c94 (Accessed: 6 July 2025).

Spring.io (2025) ‘Spring AI 1.0 GA Released’ [online image] Available at: https://spring.io/blog/2025/05/20/spring-ai-1-0-GA-released [Accessed 20 May 2025].

Mantilla Celis, J.A. (2025) ‘Build a Local AI Agent for Small Businesses [Local RAG implementation diagram], inspired by the work of Bijit, B. (2024) ‘Advanced RAG for LLMs & SLMs’, Medium, 21 April. Available at: https://medium.com/@bijit211987/advanced-rag-for-llms-slms-5bcc6fbba411 (Accessed: 18 May 2025).

#AI #RAG #SpringAI #Kotlin #LocalAI #MachineLearning #Privacy #OpenSource #Ollama #Gemma3

Hello World! 👋

¿Qué vas a encontrar aquí?

Este es mi espacio personal para compartir todo lo que voy aprendiendo en mi camino como ingeniero de software. Con más de 10 años de experiencia, principalmente en el mundo del backend, he trabajado con diversas tecnologías y arquitecturas. Actualmente estoy muy metido en Azure, pero tengo muchas ganas de explorar AWS y compartir ese viaje de aprendizaje contigo.

Además, estoy cursando una maestría en Inteligencia Artificial en la Universidad de Liverpool, y este blog va a ser el lugar donde documente todo ese conocimiento—desde los conceptos más teóricos hasta las implementaciones prácticas que vaya desarrollando.

Más que solo código

Aquí también voy a compartir:

  • Mis proyectos personales (algunos geniales, otros… experimentos interesantes)
  • Aprendizajes del día a día trabajando con sistemas reales
  • Tutoriales y guías técnicas
  • Y básicamente, este blog es mi sandbox personal para probar ideas y explorar nuevas tecnologías

Un blog multilingüe

Algo importante: este blog va a tener contenido en español, inglés y portugués brasileño. ¿Por qué? Porque creo firmemente que el conocimiento técnico no debería estar limitado solo al inglés. Hay muchísima información valiosa que se produce únicamente en inglés, y quiero ayudar a que ese conocimiento llegue a más personas en sus idiomas nativos.

Let’s build something

Así que si te interesa el backend, la inteligencia artificial, cloud computing, o simplemente ver cómo alguien aprende en público (con todos sus éxitos y fracasos), estás en el lugar correcto.


🇨🇴 Bienvenidos a mi espacio personal para compartir proyectos, ideas y aprendizajes sobre ingeniería, IA y tecnología.

🇺🇸 Welcome to my personal space to share projects, ideas, and learnings about engineering, AI, and technology.

🇧🇷 Bem-vindos ao meu espaço pessoal para compartilhar projetos, ideias e aprendizados sobre engenharia, IA e tecnologia.

Happy coding! 🚀