Bij systeemintegraties vertrouwen we vaak op logbestanden om operationele activiteiten te monitoren en problemen op te lossen. Maar het vinden van de juiste logs en deze interpreteren kost vaak veel tijd en is repetitief werk. In deze blog leer je hoe je een AI-gedreven Ops Assistant bouwt met Apache Camel, Langchain4j, Ollama en Qdrant om dit proces te stroomlijnen.
Door deze tools te combineren in een werkende Retrieval-Augmented Generation (RAG)-pijplijn, kunnen we ruwe logs omzetten in doorzoekbare inzichten. Dit maakt snellere diagnoses mogelijk, vermindert handmatig werk en creëert een slimmere operations workflow.
- Apache Camel: een open-source integratieframework dat helpt bij het routeren, transformeren en verwerken van data tussen verschillende systemen via diverse protocollen en formaten.
- Langchain4j: een Java-framework dat het integreren van Large Language Models (LLMs) in applicaties vereenvoudigt.
- Ollama: een tool waarmee je LLM’s lokaal op je machine kunt draaien en beheren.
- Qdrant: een open-source vectorzoekmachine geoptimaliseerd voor het opslaan en doorzoeken van high-dimensional vector data.
- LLM (Gemma 3): een lichtgewicht, open-source LLM van Google, gebaseerd op Gemini-technologie.
Omdat we een oplossing bouwen om loggegevens te analyseren, hanteren we een Retrieval-Augmented Generation (RAG)-benadering. Apache Camel en LangChain4j werken samen om de data in te lezen, op te halen en met AI te verwerken.
Om de toepassing van Apache Camel te demonstreren, bouwen we twee routes:
- Data Ingestion Route: leest logbestanden en slaat de geëxtraheerde gegevens op in een vector database (in dit geval Qdrant).
- Data Retrieval Route: voert een gelijkeniszoekopdracht uit op Qdrant, stuurt de gevonden data naar Ollama en gebruikt het geconfigureerde LLM om een samenvatting en verfijnd antwoord te genereren.
1. Data Ingestion Route
a. High Level Flow Diagram

Figuur 01: Hoog-niveau procesdiagram van de Data Ingestion Route
b. Qdrant installeren
Voor een proof-of-concept (POC) of testdoeleinden kun je Qdrant eenvoudig lokaal uitvoeren door de officiële Docker-images van Qdrant te gebruiken.docker pull qdrant/qdrant
Dit commando haalt de nieuwste officiële Qdrant vector database-image op vanaf Docker Hub.
docker run -p 6333:6333 -p 6334:6334 \ -v "$(pwd)/qdrant_storage:/qdrant/storage:z" \ qdrant/qdrant.
Dit commando start een Qdrant-container met Docker met de volgende configuratie:
- -p 6333:6333: Maps port 6333 of the container (Qdrant’s REST API) to port 6333 on your host.
- -p 6334:6334: Maps port 6334 of the container (Qdrant’s gRPC API) to port 6334 on your host.
- -v “$(pwd)/qdrant_storage:/qdrant/storage:z”: Mounts the local directory qdrant_storage (in your current working directory) to the container’s internal data storage path.
- qdrant/qdrant: Specifies the Docker image to run (latest Qdrant image from Docker Hub).
Zodra de Qdrant Docker-instantie is gestart en draait, voer je de volgende commando’s uit om een collectie aan te maken waarin de tekstsegmenten worden opgeslagen.
curl -X PUT http://localhost:6333/collections/log-data -H "Content-Type: application/json" -d '{ "vectors": { "size": 768, "distance": "Cosine" } }'

Figuur 02: Qdrant Console
a. Ollama installeren
Download en installeer Ollama op basis van je besturingssysteem. In dit artikel laten we de installatie zien op macOS. Vervolgens installeren we twee modellen in Ollama:
- Gemma 3: Voer het commando ollama run gemma3 uit om het LLM-model te installeren dat we gaan gebruiken.
- nomic-embed-text: Dit is een lichtgewicht model dat specifiek is ontworpen voor het genereren van tekst-embeddings. Installeer het met het commando ollama pull nomic-embed-text.
ajanthan@YENLO-R7RDCJ626R ~ % ollama list
NAME ID SIZE MODIFIED
nomic-embed-text:latest 0a109f422b47 274 MB 10 days ago
gemma3:latest a2af6cc3eb7f 3.3 GB 3 weeks ago
ajanthan@YENLO-R7RDCJ626R ~ %
b. Voorbereiding van het Apache Camel-project
In dit artikel bouwen we de oplossing met behulp van Spring Boot in combinatie met Apache Camel. Hieronder zie je de mappenstructuur die we voor dit project hanteren.
├── OpsAssistant
│ ├── HELP.md
│ ├── README.md
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ ├── src
│ │ └── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── ai
│ │ │ └── agent
│ │ │ └── OpsAssistant
│ │ │ ├── OpsAssistantApplication.java
│ │ │ ├── config
│ │ │ │ └── BeanConfig.java
│ │ │ ├── routes
│ │ │ │ ├── DataIngestionRoute.java
│ │ │ │ └── DataRetreivalRoute.java
│ │ │ ├── service
│ │ │ │ └── DataIngestionService.java
│ │ │ └── utils
│ │ │ ├── CamelConstants.java
│ │ │ └── TextSegmenter.java
│ │ └── resources
│ │ └── application.yml
i. Het begrijpen van ‘the pom.xml’
Benodigde afhankelijkheden
| Nr. | Dependency | Beschrijving |
| 1 | camel-spring-boot-starter | Maakt het mogelijk om Camel-routes te integreren binnen Spring Boot-applicaties. |
| 2 | camel-platform-http-starter | Ondersteunt het configureren van Camel-routes als RESTful services. |
| 3 | lombok | Vereenvoudigt de ontwikkeling door gebruik van annotaties voor onder andere slf4j-logging en getters/setters. |
| 4 | camel-langchain4j-chat-starter | Hiermee kun je integreren met elk Large Language Model (LLM) dat ondersteund wordt door LangChain4j. |
| 5 | camel-langchain4j-embeddings-starter | Biedt ondersteuning voor het berekenen van embeddings via LangChain4j. |
| 6 | langchain4j-ollama | Een LangChain4j-integratie waarmee je eenvoudig verbinding maakt met lokale LLMs beheerd door Ollama, voor snelle en private AI-operaties vanuit Java. |
| 7 | spring-boot-starter-web | Bevat alle benodigde dependencies voor het bouwen van webapplicaties en RESTful APIs met Spring MVC, inclusief een ingebouwde Tomcat-server. |
| 8 | langchain4j-qdrant | Een LangChain4j-integratie voor het opslaan en ophalen van vector-embeddings uit een Qdrant-vector database. Ondersteunt onder andere semantisch zoeken en RAG in Java. |
| 9 | camel-stream-starter | Maakt het mogelijk om tekstinvoer en -uitvoer via standaard streams (zoals console) te integreren in Camel-routes. |
| 10 | camel-jackson-starter | Zorgt voor eenvoudige integratie met de Jackson-bibliotheek voor automatische JSON-serialisatie en -deserialisatie in Camel-routes. |
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ai.agent</groupId>
<artifactId>OpsAssistant</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>OpsAssistant</name>
<description>This is an Operations Assistant AI Agent</description>
<url/>
<licenses>
<license/>
</licenses>
<developers>
<developer/>
</developers>
<scm>
<connection/>
<developerConnection/>
<tag/>
<url/>
</scm>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-langchain4j-chat-starter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-langchain4j-embeddings-starter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>0.36.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-qdrant</artifactId>
<version>0.36.2</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-platform-http-starter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-stream-starter</artifactId>
<version>4.10.2</version>
</dependency>
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-jackson-starter</artifactId>
<version>4.10.2</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
ii. Implementatie van de Spring Boot Main Application Class
package com.ai.agent.OpsAssistant;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class OpsAssistantApplication {
public static void main(String[] args) {
SpringApplication.run(OpsAssistantApplication.class, args);
}
}
iii. Implementatie van de BeanConfig-klasse
Deze klasse configureert de benodigde componenten voor het gebruik van Ollama voor chat en embeddings, het opslaan van data in Qdrant en het beheren van conversatiegeheugen — allemaal automatisch ingericht via Spring Boot.
Let op: Raadpleeg de comments in de code voor een beter begrip van elke bean.
package com.ai.agent.OpsAssistant.config;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.model.ollama.OllamaEmbeddingModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.qdrant.QdrantEmbeddingStore;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Value("${ollama.url}")
private String ollamaUrl;
@Value("${ollama.model}")
private String ollamaModel;
@Value("${ollama.embed.model}")
private String ollamaEmbedModel;
@Value("${ollama.temperature}")
private double temperature;
@Value("${qdrant.host}")
private String qdrantHost;
@Value("${qdrant.port}")
private int qdrantPort;
/*
* Creates a ChatLanguageModel bean using Ollama for LLM-based chat, configured with base URL, model name, and temperature.
* baseUrl -> Where to talk to Ollama.
* modelName -> Which model to use for chats.
* temperature -> How creative the model should be. A low value makes answers more predictable and a higher value makes answers more random and creative. Range 0-1.
*/
@Bean(name = "ollama")
public ChatLanguageModel chatLanguageModel(ChatMemoryProvider memoryProvider) {
return OllamaChatModel.builder()
.baseUrl(ollamaUrl)
.modelName(ollamaModel)
.temperature(temperature)
.build();
}
/*
* This bean creates an EmbeddingModel that connects to the Ollama server to generate vector embeddings for text.
* baseUrl -> Where the embedding model API is running.
* modelName -> Which embedding model to use to turn text into vectors.
*/
@Bean
public EmbeddingModel embeddingModel() {
return OllamaEmbeddingModel.builder()
.baseUrl(ollamaUrl)
.modelName(ollamaEmbedModel)
.build();
}
/*
* This bean sets up an EmbeddingStore that connects to a Qdrant vector database. It allows to store, search, and retrieve
* vector embeddings which is useful for similarity search or Retrieval-Augmented Generation (RAG).
* host -> Where Qdrant is running.
* port -> How to reach Qdrant.
* collectionName → Where the vectors will be saved inside Qdrant.
*/
@Bean
public EmbeddingStore<TextSegment> embeddingStore() {
return QdrantEmbeddingStore.builder()
.host(qdrantHost)
.port(qdrantPort)
.collectionName("log-data")
.build();
}
/*
* This bean creates an EmbeddingStoreContentRetriever, which searches the EmbeddingStore, here Qdrant to find text segments that are semantically similar to a given input text.
* It uses the EmbeddingModel to turn input text into a vector, then finds the best matching stored vectors from the database.
* embeddingModel -> Convert your input text into a vector.
* embeddingStore -> Where the vectors are stored and searched.
* maxResults -> Limit the number of results returned.
* minScore -> Filter out weak matches. Only results with a similarity score above this value are returned, ensuring that the matches are relevant and strong.
*/
@Bean
public EmbeddingStoreContentRetriever retriever(EmbeddingModel embeddingModel,
EmbeddingStore<TextSegment> embeddingStore) {
return EmbeddingStoreContentRetriever.builder()
.embeddingModel(embeddingModel)
.embeddingStore(embeddingStore)
.maxResults(30)
.minScore(0.8)
.build();
}
/*
* Creates a simple in-memory store to hold the chat memory. It doesn't persist across application restarts, making it suitable for temporary use during a session.
*/
@Bean
public ChatMemoryStore chatMemoryStore() {
return new InMemoryChatMemoryStore();
}
/*
* This bean creates a ChatMemoryProvider, which is responsible for providing access to the chat history of a conversation.
* memoryId -> Identifies a specific conversation.
* maxMessages -> Here for example Stores up to 10 messages per conversation.
* chatMemoryStore -> The place where the messages are stored, here in-memory.
*/
@Bean
public ChatMemoryProvider memoryProvider(ChatMemoryStore store) {
return memoryId -> MessageWindowChatMemory.builder()
.id(memoryId)
.maxMessages(10)
.chatMemoryStore(store)
.build();
}
}
iv. Implementatie van de TextSegmenter-klasse
package com.ai.agent.OpsAssistant.utils;
import dev.langchain4j.data.segment.TextSegment;
import java.util.ArrayList;
import java.util.List;
/*
* This takes an input string containing multiple lines of text and splits it into a list of TextSegment objects, which represent individual lines.
* Empty lines are ignored, and each non-empty line is converted into a TextSegment.
*/
public class TextSegmenter {
public static List<TextSegment> segment(String input) {
List<TextSegment> segments = new ArrayList<>();
String[] lines = input.split("\\r?\\n");
for (String line : lines) {
if (!line.trim().isEmpty()) {
segments.add(TextSegment.from(line.trim()));
}
}
return segments;
}
}
v. Implementatie van de DataIngestionService-klasse
package com.ai.agent.OpsAssistant.service;
import com.ai.agent.OpsAssistant.utils.TextSegmenter;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class DataIngestionService {
@Autowired
private EmbeddingStore<TextSegment> vectorStore;
@Autowired
private EmbeddingModel embeddingModel;
/*
* The method ingestLogData is responsible for processing a log text, splitting it into segments, generating embeddings for each segment, and storing the embeddings in a vector store.
*/
public void ingestLogData(String logText) {
List<TextSegment> segments = TextSegmenter.segment(logText);
for (TextSegment segment : segments) {
Embedding embedding = embeddingModel.embed(segment.text()).content();
vectorStore.add(embedding, segment);
}
}
}
vi. Implementatie van de DataIngestionRoute
In de logica voor data-ingestie lezen we het bestand server.log en voegen we de regels als tekstsegmenten toe aan Qdrant. In dit artikel laden we, ter demonstratie, voornamelijk de regels met een tijdstempel. Deze aanpak kan eenvoudig worden uitgebreid met meer geavanceerde verwerkingen en verbeteringen.
package com.ai.agent.OpsAssistant.routes;
import com.ai.agent.OpsAssistant.service.DataIngestionService;
import org.apache.camel.builder.RouteBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.io.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Component
public class DataIngestionRoute extends RouteBuilder {
@Autowired
private DataIngestionService dataIngestionService;
@Value("${custom.data.ingestion.log.file}")
private String logFilePath;
@Value("${custom.data.ingestion.log.tracker}")
private String lastTimestampFile;
private volatile LocalDateTime lastTimestamp = LocalDateTime.MIN;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss,SSS");
@Override
public void configure() throws Exception {
/* Retrieve the last time stamp data file, which helps to identify which line has been read last */
File tsFile = new File(lastTimestampFile);
if (tsFile.exists()) {
try (BufferedReader reader = new BufferedReader(new FileReader(tsFile))) {
String ts = reader.readLine();
if (ts != null) {
lastTimestamp = LocalDateTime.parse(ts, formatter);
}
}
}
/* Apache Camel Route for read the lines and push to Vector Store */
from("stream:file?fileName=" + logFilePath + "&scanStream=true&scanStreamDelay=1000")
.routeId("DataIngestionRoute")
.process(exchange -> {
String line = exchange.getIn().getBody(String.class);
Pattern timestampPattern = Pattern.compile("^\\[(\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3})\\]");
Matcher matcher = timestampPattern.matcher(line);
if (matcher.find()) {
String timestampStr = matcher.group(1);
try {
LocalDateTime logTime = LocalDateTime.parse(timestampStr, formatter);
if (logTime.isAfter(lastTimestamp)) {
lastTimestamp = logTime;
try (BufferedWriter writer = new BufferedWriter(new FileWriter(lastTimestampFile))) {
writer.write(logTime.format(formatter));
}
log.info("Processing as the timestamp is not processed earlier.");
dataIngestionService.ingestLogData(line);
}
} catch (DateTimeParseException e) {
log.warn("Skipping line due to invalid timestamp: {}", timestampStr);
}
} else {
log.warn("Skipping line without valid timestamp: {}", line);
}
})
.log("Processed: ${body}");
}
}
2. ‘Data Retrieval Route’
a. High Level Proces Diagram

Figuur 03: Procesdiagram van de Data Retrieval Route
b. Implementatie van de CamelConstants-klasse
package com.ai.agent.OpsAssistant.utils;
import lombok.experimental.UtilityClass;
@UtilityClass
public class CamelConstants {
public static final String MEMORY_ID = "memoryId";
}
c. Implementatie van Data Retrieval Route
package com.ai.agent.OpsAssistant.routes;
import com.ai.agent.OpsAssistant.utils.CamelConstants;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.query.Query;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.langchain4j.chat.LangChain4jChat;
import org.apache.camel.model.dataformat.JsonLibrary;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.*;
@Component
public class DataRetreivalRoute extends RouteBuilder {
@Autowired
private EmbeddingStoreContentRetriever retriever;
@Autowired
private ProducerTemplate producerTemplate;
@Autowired
private ChatMemoryProvider memoryProvider;
@Override
public void configure() throws Exception {
/*
* Use the platform-http component, Camel's built-in lightweight HTTP server.
* */
restConfiguration()
.component("platform-http")
.port(8080);
/* API Configuration */
from("rest:post:chat:/assistant/{userId}")
.routeId("DataRetreivalRouteApi")
.to("direct:initiate-rag");
/*
* Apache Camel route to fetch the needed information userId and the question from the API request and
* initiate the call to LLM through langchain4j
*/
from("direct:initiate-rag")
.routeId("DataRetreivalRouteRag")
.unmarshal()
.json(JsonLibrary.Jackson)
.setHeader(CamelConstants.MEMORY_ID, header("userId"))
.setBody(simple("${body[message]}"))
.process(exchange -> {
String question = exchange.getIn().getBody(String.class);
String memoryId = exchange.getIn().getHeader(CamelConstants.MEMORY_ID, String.class);
/* Retrieve previous chat memory messages for this particular userId */
ChatMemory chatMemory = memoryProvider.get(memoryId);
/* Add SystemMessage only if it's a new conversation */
if (chatMemory.messages().isEmpty()) {
chatMemory.add(SystemMessage.from("You are a log analyzer assistant. "
+ "Your job is to carefully analyze logs retrieved from Qdrant embeddings and answer based on that. "
+ "If the user asks a common, casual questions like greetings, respond politely without using log data."));
}
/* Add the new UserMessage */
chatMemory.add(UserMessage.from(question));
Map<String, Object> headers = new HashMap<>();
/* Load the contents from Qdrant */
List<Content> contents = retriever.retrieve(Query.from(question));
log.info("Contents retrieved: {}", contents.size());
headers.put(LangChain4jChat.Headers.AUGMENTED_DATA, contents);
/* Set memoryId for conversation continuity */
headers.put(CamelConstants.MEMORY_ID, memoryId);
/* Send to LangChain4j */
String result = producerTemplate.requestBodyAndHeaders("direct:send-langchain4j", chatMemory.messages(), headers, String.class);
exchange.getIn().setBody(result);
});
from("direct:send-langchain4j")
.routeId("DataRetreivalLangchainRoute")
.to("langchain4j-chat:ollama?chatModel=#ollama&chatOperation=CHAT_MULTIPLE_MESSAGES")
.process(exchange -> {
String memoryId = exchange.getIn().getHeader("memoryId", String.class);
ChatMemory chatMemory = memoryProvider.get(memoryId);
String response = exchange.getMessage().getBody(String.class);
/* Set the response from LLM to memory */
chatMemory.add(new UserMessage(response));
/* Setting a proper json back to API call initiator */
Map<String, Object> responseJson = new HashMap<>();
responseJson.put("response", response);
exchange.getIn().setBody(responseJson);
})
.marshal()
.json(JsonLibrary.Jackson);
}
}
3. Testen en Verifiëren van de Implementatie
- Configuratie van application.yml
spring:
application:
name: OpsAssistant
ollama:
url: 'http://localhost:11434'
model: gemma3
embed:
model: nomic-embed-text
temperature: 0.2
qdrant:
host: localhost
port: 6334
camel:
component:
platform-http:
enabled: true
custom:
data:
ingestion:
log:
file: "https://2ae95bce.delivery.rocketcdn.me/Users/ajanthan/yenlo/blogs/2025/logs/server.log"
tracker: "https://2ae95bce.delivery.rocketcdn.me/Users/ajanthan/yenlo/blogs/2025/logs/last-log-timestamp.txt"
- Start de service met onderstaande commando’s:
- mvn clean install
- java -jar target/OpsAssistant-0.0.1-SNAPSHOT.jar
- Zodra het proces is gestart, wordt het server.log-bestand automatisch gelezen. De logregels worden gesegmenteerd en als tekstsegmenten ingevoegd in de Qdrant-vector database.

Figuur 04: Qdrant Collection
- Initieer de Chat voor de Data Retrieval

Figuur 05: Voorbeeld van initiële chatoutput met Gemma 3 en opgehaalde logsegmenten
Controleer of geheugenpersistentie correct functioneert.

Figuur 06: Test Uitslag – Geheugen Check

Figuur 07: Test Uitslag – Geheugen Check
- Stel vragen over de logs

Figuur 08: Test Uitslag – Log Analysis
Deze blog liet zien hoe je een basis Ops Assistant bouwt die logbestanden kan lezen en interpreteren met behulp van open-source tools en RAG-principes. Door Apache Camel te combineren voor routing, Langchain4j voor orkestratie, Ollama voor het draaien van lokale LLM’s en Qdrant voor vectoropslag, leg je de basis voor intelligente loganalyse.
In een volgende blog gaan we in op het verhogen van de precisie door gestructureerde logdata te integreren en de promptverwerking te optimaliseren.
Blijf ons volgen, slimmere en snellere Ops-workflows beginnen hier.
Neem contact op met onze experts als je meer vragen hebt n.a.v. dit blog.
