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
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
Comments (0)