Von Dr. Cassian Holt, Senior Architect bei Java Fleet Systems Consulting

📚 Was bisher geschah – Teil 1 Recap
In Teil 1 haben wir die Grundlagen gelegt:
- ✅ Use Cases: Code-Review, FAQ-Bots, Dokumentations-Suche, Log-Analyse
- ✅ DSGVO-Compliance: Warum lokale LLMs rechtlich die bessere Wahl sind
- ✅ Quantisierung erklärt: Von FP32 zu INT4 (87% kleiner!)
- ✅ Ollama als Modell-Manager: Der „Docker“ für LLMs
- ✅ Modell-Familien: Llama, Mistral, Phi, Code Llama, DeepSeek
- ✅ Kosten-Analyse: ROI nach 9 Tagen bei 1000 Anfragen/Tag🚀
Die Theorie sitzt. Heute wird gebaut!
⚡ Kurze Zusammenfassung – Das Wichtigste in 30 Sekunden
In Teil 1 hast du gelernt, WARUM lokale LLMs die Zukunft sind. Jetzt wird’s praktisch: Wir bauen ein produktionsreifes Spring Boot Multi-Model-System mit Ollama. Du lernst das Maven-Setup, die intelligente Multi-Model-Orchestrierung, Caching-Strategien und Production-Deployment. Das vollständige Projekt steht auf GitHub zum Download bereit. Von Zero zu Production in 30 Minuten!
Moin zurück, Entwickler-Community! 🔬
Cassian hier – und heute wird’s hands-on. Während Liam gerade versucht, meinen Laptop als „KI-Experimentier-Station“ zu übernehmen („Papa, kann ich dem Computer beibringen, Lego zu bauen?“), und Emma philosophisch fragt, ob der Computer auch müde wird, zeige ich dir, wie du lokale LLMs in deine Spring Boot Anwendung integrierst.
Kein theoretisches Gerede mehr – heute schreiben wir Code!
Sarah (meine Frau) meinte heute Morgen: „Du weißt schon, dass normale Menschen einfach ChatGPT nutzen, oder?“ – „Ja,“ sagte ich, „aber normale Menschen verstehen auch nicht, warum ihre Daten in US-Rechenzentren liegen.“ Sie seufzte. Die Zwillinge klatschten. Ich interpretiere das als Zustimmung. 😊
Also, schnapp dir deinen Kaffee (oder bei mir: Tee, weil Sarah sagt, Kaffee um 22 Uhr ist keine gute Idee), und lass uns ein Production-ready LLM-System bauen!
🚀 Warum Spring Boot + Ollama? Die perfekte Kombination
In Teil 1 hast du die Theorie gelernt. Jetzt kommt die Praxis – und ehrlich gesagt: Hier trennt sich die Spreu vom Weizen.
Die Herausforderung: Du kannst die beste Hardware haben, die cleversten Modelle pullen, Ollama perfekt konfigurieren – aber wenn deine Anwendung nicht produktionsreif ist, bleibt es ein Spielzeug. Und Java Fleet baut keine Spielzeuge.
Was „produktionsreif“ wirklich bedeutet:
- Nicht nur „es funktioniert“ – sondern es funktioniert auch unter Last
- Nicht nur „schnell“ – sondern konsistent schnell (P95 < 2s)
- Nicht nur „sicher“ – sondern defense-in-depth (Input-Validation, Rate Limiting, Monitoring)
- Nicht nur „billig“ – sondern nachhaltig skalierbar
Warum Spring Boot die richtige Wahl ist:
- ✅ Enterprise-proven: Millionen von Production-Apps weltweit
- ✅ Batteries included: Actuator (Monitoring), Security, Caching out-of-the-box
- ✅ Langchain4j-Integration: Native Support für LLM-Orchestrierung
- ✅ Cloud-ready: Läuft überall (Bare Metal, Docker, Kubernetes, Cloud)
Warum Ollama die richtige Wahl ist:
- ✅ Einfachstes Setup:
ollama pull model– fertig - ✅ REST API: Standard HTTP, kein proprietäres Protokoll
- ✅ Model-Management: Versions, Updates, Rollbacks
- ✅ Resource-Optimierung: Automatisches GPU-Sharing
Die Kombination:
Spring Boot (Application Layer)
↕ HTTP REST API
Ollama (Model Layer)
↕ llama.cpp
Hardware (GPU/CPU)
Simple, robust, wartbar. Genau wie gute Architektur sein sollte.
Zeit, das in die Realität umzusetzen! 🔧
🎯 Was wir heute bauen: Das Multi-Model-LLM-System
Unser Ziel ist ein intelligentes System, das:
- ✅ 3 verschiedene Modelle parallel verwaltet (Fast/Balanced/Specialized)
- ✅ Automatisch das beste Modell für die Aufgabe wählt
- ✅ REST API bereitstellt (Spring Boot)
- ✅ Caching nutzt (40-60% Request-Reduktion)
- ✅ Rate Limiting hat (Schutz vor Überlastung)
- ✅ Monitoring eingebaut (Metriken, Logs)
- ✅ Production-ready ist (Docker, Health Checks)
Das Endergebnis:
curl http://localhost:8080/api/ai/faq -d "Was ist Spring Boot?"
→ Nutzt Llama 3.2 3B (schnell, 0.5s)
curl http://localhost:8080/api/ai/code-review -d "public void test() {...}"
→ Nutzt DeepSeek Coder 6.7B (spezialisiert, 1.5s)
Warum 3 Modelle statt eins?
Die Mathematik ist gnadenlos:
Szenario: 1000 Anfragen/Tag - 600 FAQ (einfach) - 300 Analysen (komplex) - 100 Code-Reviews (spezialisiert) Nur großes Modell (8B): 1000 × 2s = 2000s = 33 Min CPU-Zeit Multi-Model-Strategie: 600 × 0.5s + 300 × 2s + 100 × 1.5s = 1050s = 17.5 Min → 47% schneller! 47% weniger Kosten!
📦 Code-Highlight #1: Die pom.xml – Foundation is Key
Das Herzstück jeder Maven-Anwendung. Hier definieren wir alle Dependencies, die unser LLM-System braucht.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<groupId>com.javafleet</groupId>
<artifactId>local-llm-demo</artifactId>
<version>1.0.0</version>
<name>Local LLM Demo</name>
<properties>
<java.version>21</java.version>
<langchain4j.version>0.34.0</langchain4j.version>
</properties>
<dependencies>
<!-- Spring Boot Web (REST API) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Actuator (Health Checks, Metrics) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- Langchain4j Core -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Langchain4j Ollama Integration (KRITISCH!) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-ollama</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Micrometer für Prometheus Metrics -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
<!-- Caffeine Cache (für Response-Caching) -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<!-- Resilience4j (Rate Limiting, Circuit Breaker) -->
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Was macht jede Dependency?
| Dependency | Zweck | Warum wichtig? |
|---|---|---|
spring-boot-starter-web | REST API, Embedded Tomcat | Basis für HTTP-Endpoints |
spring-boot-starter-actuator | Health Checks, Metrics | Production-Monitoring |
langchain4j | Core Interfaces | Abstraktion über LLM-APIs |
langchain4j-ollama | Ollama-Integration | Die Brücke zu Ollama! |
micrometer-registry-prometheus | Metrics Export | Monitoring mit Grafana |
caffeine | High-Performance Cache | 40-60% Request-Reduktion |
resilience4j | Rate Limiting | Schutz vor Überlastung |
Ohne langchain4j-ollama funktioniert NICHTS! Das ist die Dependency, die die Verbindung zwischen Java und Ollama herstellt.
🐳 Code-Highlight #2: Ollama Setup – Der Motor
Bevor Spring Boot starten kann, muss Ollama laufen und die Modelle gepullt haben.
Installation (Wähle eine Variante):
macOS/Linux:
curl -fsSL https://ollama.com/install.sh | sh
Windows:
winget install Ollama.Ollama
Docker (Production-empfohlen):
docker run -d \ --name ollama \ -p 11434:11434 \ -v ollama_data:/root/.ollama \ --gpus all \ ollama/ollama
Modelle pullen – Die drei Säulen:
# 1. Schnelles Modell für FAQ (2 GB) ollama pull llama3.2:3b # 2. Ausgewogenes Modell für allgemeine Tasks (5 GB) ollama pull llama3.1:8b # 3. Code-spezialisiertes Modell (4 GB) ollama pull deepseek-coder:6.7b
Überprüfen:
ollama list # Output sollte zeigen: NAME ID SIZE MODIFIED llama3.2:3b a80c4f17acd5 2.0 GB 2 minutes ago llama3.1:8b 42182419e950 4.7 GB 3 minutes ago deepseek-coder:6.7b 140e4a97f7dc 3.8 GB 5 minutes ago
Test-Anfrage:
curl http://localhost:11434/api/generate -d '{
"model": "llama3.2:3b",
"prompt": "Erkläre Spring Boot in einem Satz",
"stream": false
}'
Wenn das funktioniert → Ollama ist ready! ✅
🏗️ Code-Highlight #3: Projekt-Struktur
local-llm-demo/
├── pom.xml ← Dependencies
├── src/main/java/com/javafleet/llmdemo/
│ ├── LocalLlmDemoApplication.java ← Main Class
│ ├── config/
│ │ ├── MultiModelConfig.java ← ⭐ HERZSTÜCK
│ │ ├── CacheConfig.java ← Caching Setup
│ │ └── MetricsConfig.java ← Monitoring
│ ├── model/
│ │ └── TaskType.java ← Enum (SIMPLE_QA, GENERAL, CODE)
│ ├── service/
│ │ ├── SmartLLMService.java ← ⭐ Orchestrierung
│ │ └── CachedLLMService.java ← ⭐ Caching Layer
│ ├── controller/
│ │ └── AIController.java ← REST Endpoints
│ ├── security/
│ │ ├── PromptValidator.java ← Input-Validation
│ │ └── OutputValidator.java ← Output-Validation
│ └── dto/
│ ├── AIRequest.java ← Request DTO
│ └── AIResponse.java ← Response DTO
└── src/main/resources/
├── application.properties ← ⭐ Configuration
└── logback-spring.xml ← Logging Config
Die Architektur folgt Clean Architecture Prinzipien:
- Controller (Presentation Layer) → REST Endpoints
- Service (Business Logic) → LLM-Orchestrierung, Caching
- Config (Infrastructure) → Model-Setup, Caching, Monitoring
- Security (Cross-Cutting) → Validation, Rate Limiting
💎 Code-Highlight #4: MultiModelConfig – Das Herzstück!
Das ist der Kern unseres Systems. Hier werden die 3 Modelle als Spring Beans konfiguriert.
package com.javafleet.llmdemo.config;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
/**
* Multi-Model-Konfiguration: Das Herzstück unseres LLM-Systems.
*
* Design-Prinzip: "Right tool for the right job"
* - Fast Model: Für einfache, schnelle Anfragen
* - Balanced Model: Für allgemeine Tasks
* - Code Model: Für Code-spezifische Aufgaben
*
* @author Dr. Cassian Holt
*/
@Configuration
public class MultiModelConfig {
@Value("${ollama.base-url:http://localhost:11434}")
private String ollamaBaseUrl;
/**
* Schnelles Modell für einfache FAQ-Anfragen.
*
* Llama 3.2 3B:
* - 2 GB VRAM
* - ~0.5s Response-Zeit
* - Perfekt für: FAQ, Kategorisierung, einfache Chats
*/
@Bean("fastModel")
public ChatLanguageModel fastModel() {
return OllamaChatModel.builder()
.baseUrl(ollamaBaseUrl)
.modelName("llama3.2:3b")
.temperature(0.7) // Etwas Kreativität
.timeout(Duration.ofSeconds(60))
.build();
}
/**
* Ausgewogenes Modell für allgemeine Anfragen.
*
* Llama 3.1 8B:
* - 5 GB VRAM
* - ~2s Response-Zeit
* - Perfekt für: Analysen, Zusammenfassungen, komplexe Prompts
*/
@Bean("balancedModel")
public ChatLanguageModel balancedModel() {
return OllamaChatModel.builder()
.baseUrl(ollamaBaseUrl)
.modelName("llama3.1:8b")
.temperature(0.5) // Ausgewogen
.timeout(Duration.ofSeconds(120))
.build();
}
/**
* Code-spezialisiertes Modell für technische Tasks.
*
* DeepSeek Coder 6.7B:
* - 4 GB VRAM
* - ~1.5s Response-Zeit
* - Perfekt für: Code-Review, Code-Generierung, Refactoring
*/
@Bean("codeModel")
public ChatLanguageModel codeModel() {
return OllamaChatModel.builder()
.baseUrl(ollamaBaseUrl)
.modelName("deepseek-coder:6.7b")
.temperature(0.2) // Sehr deterministisch für Code
.timeout(Duration.ofSeconds(90))
.build();
}
}
Was passiert beim build()?
Intern macht OllamaChatModel.builder().build():
- HTTP-Client erstellen (für Kommunikation mit Ollama)
- Verbindung testen (
GET http://localhost:11434/api/tags) - Modell-Verfügbarkeit prüfen (ist „llama3.2:3b“ gepullt?)
- ChatLanguageModel-Instanz zurückgeben (ready to use!)
Warum 3 Beans statt eine Factory?
Spring injiziert die Beans beim Start → Zero Runtime-Overhead. Factory hätte bei jeder Anfrage Entscheidungslogik.
🎯 Code-Highlight #5: SmartLLMService – Die intelligente Orchestrierung
Hier geschieht die Magie: Welches Modell für welche Aufgabe?
package com.javafleet.llmdemo.service;
import com.javafleet.llmdemo.model.TaskType;
import dev.langchain4j.model.chat.ChatLanguageModel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* Intelligente LLM-Orchestrierung mit automatischer Modell-Auswahl.
*
* @author Dr. Cassian Holt
*/
@Slf4j
@Service
public class SmartLLMService {
private final ChatLanguageModel fastModel;
private final ChatLanguageModel balancedModel;
private final ChatLanguageModel codeModel;
/**
* Constructor Injection mit @Qualifier für Bean-Auswahl.
* Spring injiziert automatisch die 3 konfigurierten Modelle.
*/
public SmartLLMService(
@Qualifier("fastModel") ChatLanguageModel fastModel,
@Qualifier("balancedModel") ChatLanguageModel balancedModel,
@Qualifier("codeModel") ChatLanguageModel codeModel) {
this.fastModel = fastModel;
this.balancedModel = balancedModel;
this.codeModel = codeModel;
log.info("SmartLLMService initialized with 3 models");
}
/**
* Generiert LLM-Antwort mit dem passenden Modell für den Task-Type.
*/
public String generate(String prompt, TaskType type) {
log.info("Generating response for TaskType: {}", type);
long startTime = System.currentTimeMillis();
String response = switch (type) {
case SIMPLE_QA -> {
log.debug("Using fastModel (Llama 3.2 3B)");
yield fastModel.generate(prompt);
}
case GENERAL -> {
log.debug("Using balancedModel (Llama 3.1 8B)");
yield balancedModel.generate(prompt);
}
case CODE -> {
log.debug("Using codeModel (DeepSeek Coder)");
yield codeModel.generate(prompt);
}
};
long duration = System.currentTimeMillis() - startTime;
log.info("Response generated in {}ms", duration);
return response;
}
/**
* Automatische Task-Type-Erkennung basierend auf Prompt-Inhalt.
*
* Heuristiken:
* - Enthält Code-Snippets? → CODE
* - Kurzer Text (<100 Zeichen)? → SIMPLE_QA
* - Sonst: GENERAL
*/
public TaskType detectTaskType(String prompt) {
// Code-Indikatoren
if (prompt.contains("```") ||
prompt.contains("public class") ||
prompt.contains("function ") ||
prompt.matches(".*\\b(code|review|refactor|bug)\\b.*")) {
return TaskType.CODE;
}
// Kurze FAQ
if (prompt.length() < 100) {
return TaskType.SIMPLE_QA;
}
// Standard
return TaskType.GENERAL;
}
}
Die Intelligenz liegt in der Einfachheit:
- Switch-Expression (Java 14+) → Klar, lesbar
- Automatische Erkennung → Keine manuelle User-Eingabe nötig
- Logging → Nachvollziehbarkeit in Production
⚡ Code-Highlight #6: Caching – Der Performance-Boost
Warum Caching so wichtig ist:
Ohne Cache: 1000 FAQ-Anfragen/Tag × 2s = 2000s CPU-Zeit Kosten: 100% (Baseline) Mit 50% Cache Hit Rate: 500 Anfragen × 2s + 500 Cache-Hits × 0.001s = 1000.5s → 50% Einsparung! Zusätzlich: Latenz für User < 1ms bei Cache-Hit
Implementation:
package com.javafleet.llmdemo.service;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.javafleet.llmdemo.model.TaskType;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
@Slf4j
@Service
public class CachedLLMService {
private final SmartLLMService llmService;
private final Cache<String, String> responseCache;
public CachedLLMService(SmartLLMService llmService) {
this.llmService = llmService;
// Caffeine Cache-Konfiguration
this.responseCache = Caffeine.newBuilder()
.maximumSize(10_000) // Max 10k Einträge
.expireAfterWrite(Duration.ofHours(1)) // TTL: 1 Stunde
.recordStats() // Für Monitoring
.build();
log.info("CachedLLMService initialized with cache size: 10,000");
}
public String generateCached(String prompt, TaskType type) {
String cacheKey = createCacheKey(prompt, type);
return responseCache.get(cacheKey, key -> {
log.info("Cache MISS for key: {}", key.substring(0, 8));
return llmService.generate(prompt, type);
});
}
private String createCacheKey(String prompt, TaskType type) {
try {
String input = prompt + "|" + type.name();
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return HexFormat.of().formatHex(hash);
} catch (Exception e) {
return String.valueOf((prompt + type).hashCode());
}
}
}
Warum Caffeine und nicht Spring Cache?
- Bessere Performance: Spezialisiert auf High-Throughput
- Granulare Kontrolle: TTL, Eviction-Policies, Stats
- Non-Blocking: Keine Locks bei gleichzeitigen Anfragen
⚙️ Code-Highlight #7: application.properties
# Application Name spring.application.name=local-llm-demo # Server Configuration server.port=8080 # Ollama Configuration ollama.base-url=http://localhost:11434 # Actuator (Monitoring) management.endpoints.web.exposure.include=health,metrics,prometheus management.metrics.export.prometheus.enabled=true # Rate Limiting (Resilience4j) resilience4j.ratelimiter.instances.llmApi.limitForPeriod=10 resilience4j.ratelimiter.instances.llmApi.limitRefreshPeriod=1s resilience4j.ratelimiter.instances.llmApi.timeoutDuration=0 # Logging logging.level.root=INFO logging.level.com.javafleet.llmdemo=DEBUG logging.level.dev.langchain4j=DEBUG
Warum .properties statt .yml?
- Einfacher für Ops: Keine Einrückungs-Fehler
- Environment-Variables:
${OLLAMA_BASE_URL:http://localhost:11434} - Spring Boot Standard: Historisch bewährt
🎬 Das vollständige System: Wie es zusammenspielt
Unser System besteht aus mehreren Layern, die elegant zusammenarbeiten:
1. REST Controller (Presentation Layer)
Der AIController bietet 3 Haupt-Endpoints:
/api/ai/generate– Universeller Endpoint mit allen Features/api/ai/faq– Optimiert für FAQ (nutzt fastModel)/api/ai/code-review– Optimiert für Code (nutzt codeModel)
Features:
- Rate Limiting (10 req/sec via Resilience4j)
- Input Validation (PromptValidator)
- Response-Time-Tracking
- Automatic Model Selection
2. Service Layer (Business Logic)
SmartLLMService:
- Orchestriert die 3 Modelle
- Automatische Task-Type-Erkennung
- Performance-Logging
CachedLLMService:
- Wrapper um SmartLLMService
- 40-60% Cache-Hit-Rate in Production
- SHA-256-basierte Cache-Keys
3. Security Layer (Cross-Cutting)
PromptValidator:
- Prüft auf Prompt-Injection-Patterns
- Max-Length-Validation (4000 chars)
- Sanitization gefährlicher Phrasen
OutputValidator:
- Prüft Outputs auf sensible Daten
- Maskiert E-Mails, IPs
- Verhindert Information Leakage
4. Configuration Layer (Infrastructure)
MultiModelConfig:
- Erstellt 3 ChatLanguageModel-Beans
- Verbindet zu Ollama
- Konfiguriert Timeouts, Temperature
CacheConfig & MetricsConfig:
- Caching-Setup
- Prometheus-Metrics-Export
🚀 Starten und Testen
Schritt 1: Ollama starten
ollama serve
Schritt 2: Spring Boot starten
mvn clean install mvn spring-boot:run
Schritt 3: API testen
# Test 1: Einfache FAQ
curl -X POST http://localhost:8080/api/ai/faq \
-H "Content-Type: text/plain" \
-d "Was ist Spring Boot?"
# Test 2: Code-Review
curl -X POST http://localhost:8080/api/ai/code-review \
-H "Content-Type: text/plain" \
-d 'public void test() {
String sql = "SELECT * FROM users WHERE id=" + userId;
db.execute(sql);
}'
# Test 3: Health Check
curl http://localhost:8080/actuator/health
# Test 4: Metrics
curl http://localhost:8080/actuator/prometheus | grep llm
Erwartete Response-Zeiten:
- FAQ: 500-800ms (Cache-Miss), <1ms (Cache-Hit)
- Code-Review: 1.5-2s (Cache-Miss), <1ms (Cache-Hit)
- Health Check: <10ms
📊 Production-Readiness: Was fehlt noch?
Unser System ist funktional – aber ist es production-ready? Schauen wir uns die Checkliste an:
✅ Was wir haben:
- [x] Multi-Model-Orchestrierung
- [x] Caching (40-60% Hit-Rate)
- [x] Rate Limiting (10 req/sec)
- [x] Input/Output-Validation
- [x] Monitoring (Prometheus)
- [x] Health Checks (Actuator)
📦 Was noch kommt (im GitHub-Projekt):
Das vollständige Projekt auf GitHub enthält zusätzlich:
1. Docker-Deployment:
docker-compose.ymlfür Ollama + Spring Boot- Multi-Stage Dockerfile (Build + Runtime)
- Prometheus + Grafana Setup
- Automatisches Model-Pulling beim Start
2. Advanced Features:
- RAG (Retrieval-Augmented Generation) mit lokalen Embeddings
- Streaming-Responses für bessere UX
- Hybrid-Architecture (Local + Cloud Fallback)
- Circuit Breaker Pattern
3. Testing:
- Unit Tests mit JUnit 5
- Integration Tests mit Testcontainers
- Load Tests mit Gatling
- Contract Tests für API
4. Security:
- HTTPS/TLS-Support
- API-Key-Authentication
- CORS-Configuration
- Security Headers
5. Observability:
- Structured Logging (JSON)
- Distributed Tracing (OpenTelemetry)
- Custom Dashboards (Grafana)
- Alerting (Prometheus AlertManager)
💡 Lessons Learned: Cassians Production-Erfahrungen
Nach 6 Monaten Production-Betrieb bei Java Fleet hier meine wichtigsten Erkenntnisse:
1. Caching ist KRITISCH
Ohne Cache: Server-Last zu hoch, Response-Zeiten inkonsistent Mit Cache: 50% weniger Load, 90% schnellere Responses
Tipp: Start mit konservativem TTL (1h), dann basierend auf Metrics anpassen.
2. Model-Größe ≠ Qualität für deinen Use Case
Wir starteten mit Llama 3.1 70B für alles. Fehler!
- 70B war overkill für FAQ
- Hardware-Kosten explodierten
- Latenz war inakzeptabel
Lösung: Multi-Model-Strategie. Ergebnis: 60% Kosten gespart, 3× schneller.
3. Input-Validation ist NON-NEGOTIABLE
In Woche 2: User fand Prompt-Injection-Lücke. System lief 3h wild.
Lesson: Validiere ALLES. Trust no input.
4. Monitoring before Problems
Ohne Metrics fliegst du blind. Wir merkten Performance-Degradation erst nach User-Complaints.
Jetzt: Prometheus + Grafana. Alerts bei P95 > 3s. Viel besser.
5. DSGVO-Compliance ist kein Afterthought
Rechtsabteilung prüfte unser System. Fazit: Lokale LLMs = Compliance-Gold.
Keine AVVs, keine Drittland-Übermittlung, keine Datenschutz-Folgenabschätzung für Cloud.
Dokumentationsaufwand: 80% weniger als mit OpenAI.
❓ FAQ – Teil 2: Praktische Implementierung
Frage 7: Was macht ihr bei persönlichen Herausforderungen zwischen den technischen Projekten?
Antwort: Das ist… kompliziert. Zwischen dem Debuggen von Multi-Model-Configs, Emma’s Fragen („Papa, warum braucht der Computer so lange zum Denken?“) und Liam’s Versuchen, meine Docker-Container als „Raumschiffe“ zu commandieren – manchmal gibt es Momente, wo nicht der Code debuggt werden muss, sondern das Leben. Sarah sagt immer: „Du kannst nicht alles cachen wie deine LLM-Responses, Cassian.“ Hat sie recht. Manche Geschichten gehören nicht in Tech-Blogs, sondern in private logs. Die Momente zwischen den Commits, wo das Herz schwerer wiegt als die Codebase. Aber das ist ein anderes Kapitel unserer Geschichte. 🔒
Frage 8: Wie migriere ich von OpenAI zu lokalen LLMs?
Antwort: Die Migration ist simpler als du denkst:
// Vorher: OpenAI
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4")
.build();
// Nachher: Ollama (nur 2 Zeilen ändern!)
ChatLanguageModel model = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama3.1:8b")
.build();
Aber: Prompts brauchen oft Anpassungen (20-30% Refactoring). Lokale Modelle brauchen explizitere Instruktionen.
Frage 9: Wie skaliere ich bei steigender Last?
Antwort: 3 Skalierungs-Strategien:
- Vertical Scaling (bis 10k req/day):
- Bessere GPU (RTX 4090 → A100)
- Mehr RAM
- Horizontal Scaling (10k-100k req/day):
- Multiple Ollama-Instanzen
- Load Balancer (Nginx/HAProxy)
- Kubernetes mit HPA
- Hybrid (100k+ req/day):
- Lokale LLMs für sensible Daten
- Cloud-LLMs (via API) für unkritische Peaks
- Smart Routing basierend auf Data-Sensitivity
Frage 10: Was kostet der Betrieb wirklich?
Antwort: Realistische Monatskosten (1000 req/day):
Hardware-Amortisation: 150€ (Server über 3 Jahre) Strom (200W Dauerlast): 15€ Wartung (4h/Monat): 200€ (wenn intern) Total: ~365€/Monat vs. OpenAI: 15.000€/Monat Einsparung: 97.5%
Frage 11: Wie handle ich Model-Updates?
Antwort: Blue/Green-Deployment:
# Neue Ollama-Instanz (Port 11435)
docker run -d -p 11435:11434 --name ollama-green ollama/ollama
docker exec ollama-green ollama pull llama3.1:8b-v2
# Testen
curl http://localhost:11435/api/generate -d '{...}'
# application.properties anpassen
ollama.base-url=http://localhost:11435
# App neu starten, alte Ollama stoppen
Zero-Downtime garantiert.
Frage 12: Ist der Aufwand wirklich gerechtfertigt?
Antwort: Für wen lohnt es sich?
✅ JA, wenn:
- Du >500 Anfragen/Tag hast
- Sensible Daten verarbeitest
- DSGVO-Compliance brauchst
- Langfristige Kostenkontrolle willst
- Ein Tech-Team für Wartung hast
❌ NEIN, wenn:
- Du <200 Anfragen/Tag hast
- Nur kreative Texte generierst
- Kein Tech-Team hast
- Schnelles Prototyping brauchst
Für 70% europäischer Unternehmen: Lokale LLMs sind die bessere Wahl.
🎓 Dein Projekt: Download & Next Steps
📦 Vollständiges GitHub-Repository
Das komplette Projekt mit allem Code ist verfügbar:
🔗 GitHub:Cassians GitHub
Was du bekommst:
- ✅ Vollständiger Source-Code (alle Klassen)
- ✅
docker-compose.yml(Ollama + Spring Boot + Prometheus + Grafana) - ✅ Tests (Unit, Integration, Load)
- ✅ Dokumentation (Setup, Deployment, Troubleshooting)
- ✅ Beispiel-Prompts & Use Cases
- ✅ Grafana-Dashboards (Import-Ready)
Quick Start:
git clone https://github.com/java-fleet/local-llm-demo cd local-llm-demo docker-compose up -d
In 5 Minuten läuft dein Production-ready LLM-System!
🚀 Dein 4-Wochen Action Plan
Woche 1: Setup & Basics
- [ ] Repository clonen
- [ ] Ollama installieren & Modelle pullen
- [ ] Spring Boot lokal starten
- [ ] Erste API-Calls testen
Woche 2: Verstehen & Anpassen
- [ ] Code durchgehen (alle Klassen verstehen)
- [ ] Eigene Prompts testen
- [ ] Cache-Hit-Rate analysieren
- [ ] Erste Anpassungen (z.B. eigene TaskTypes)
Woche 3: Production-Ready
- [ ] Docker-Setup lokal testen
- [ ] Monitoring-Dashboard einrichten
- [ ] Load-Tests durchführen
- [ ] Security-Audit (Input-Validation)
Woche 4: Deployment
- [ ] Production-Hardware-Plan
- [ ] Deployment auf Server
- [ ] Monitoring live schalten
- [ ] Go-Live! 🎉
🔬 Next Steps: Machine Learning Grundlagen Serie
Du weißt jetzt, WIE man LLMs nutzt. Willst du verstehen, WIE sie funktionieren?
Machine Learning Grundlagen (6 Teile, ab 21.10.2025)
Die Serie startet in 3 Tagen!
- Teil 1 : Was ist Machine Learning eigentlich? – Von Regression zu Neural Networks
- Teil 2 : Neural Networks entmystifiziert – Backpropagation von Hand
- Teil 3 : Training Deep Learning Modelle – Loss Functions & Optimizers
- Teil 4 : Transformer-Architektur – Wie LLMs wirklich funktionieren
- Teil 5 : Training vs. Inference – Production ML mit Java
- Teil 6 : Fine-Tuning & Transfer Learning – Dein eigenes Modell
Von den Tools zur Theorie. Verstehe die Mathematik hinter der Magie!
🏁 Fazit: Von der Theorie zur Production
Glückwunsch! In dieser 2-teiligen Serie hast du gelernt:
Teil 1 (Theorie):
- ✅ Warum lokale LLMs (DSGVO, Kosten, Kontrolle)
- ✅ Wie sie funktionieren (Quantisierung, Ollama, Modelle)
- ✅ Welche Modelle für welchen Zweck
Teil 2 (Praxis):
- ✅ Spring Boot + Langchain4j Setup
- ✅ Multi-Model-Orchestrierung
- ✅ Production-Features (Caching, Monitoring, Security)
- ✅ Deployment-Ready Code
Du hast jetzt ein vollständiges, produktionsreifes LLM-System.
Als Wissenschaftler sage ich: Die Architektur ist solid. Als Praktiker sage ich: Es läuft in Production. Als Vater sage ich: Emma und Liam können in einer Welt aufwachsen, in der KI nicht nur in US-Rechenzentren existiert.
Zeit für den nächsten Schritt! 🚀
Und wenn dich mal nicht nur Code, sondern auch das Leben herausfordert – zwischen nächtlichen Debugging-Sessions, Emma’s philosophischen Fragen und Liam’s „Papa, bau mir einen KI-Roboter!“-Anfragen – manchmal sind die interessantesten Geschichten die, die nicht in Tech-Blogs stehen. Um private logs sozusagen. Für die wirklich Neugierigen. 🔍
Keep coding, keep learning – und vergiss nicht: Privacy ist kein Feature, sondern ein Grundrecht! 🚀🔒
– Dr. Cassian Holt
P.S.: Sarah hat gerade gefragt, ob ich jetzt endlich mit den Kindern ins Bett gehen kann. „Der Blog ist fertig, Cassian.“ – „Einen Moment noch, Schatz, ich muss noch die FAQs…“ – Sie seufzt. Die Zwillinge lachen. Das Leben eines Tech-Dads. 😊
📖 Lokale LLMs Serie – Alle Teile im Überblick
✅ Bereits veröffentlicht:
- Teil 1 (16.10.2025): Lokale LLMs: Privacy-First AI für deine Java-Anwendungen – Theorie, Motivation, Quantisierung, Modell-Familien, DSGVO-Compliance
- Teil 2 (18.10.2025): Lokale LLMs in Production: Spring Boot Integration & Deployment – Maven-Setup, Multi-Model-System, Caching, Production-Deployment
📅 Nächste Serie:
- Machine Learning Grundlagen (ab 21.10.2025): 6-teilige Serie über die Theorie hinter LLMs
Alle Teile findest du auf: java-developer.online
📚 Weiterführende Ressourcen
Tools & Frameworks:
- Ollama: https://ollama.com
- Langchain4j: https://github.com/langchain4j/langchain4j
- Langchain4j Docs: https://docs.langchain4j.dev
- Spring Boot: https://spring.io/projects/spring-boot
- Caffeine Cache: https://github.com/ben-manes/caffeine
Modelle (HuggingFace):
- Llama 3.2: https://huggingface.co/meta-llama/Llama-3.2-3B
- Llama 3.1: https://huggingface.co/meta-llama/Llama-3.1-8B
- DeepSeek Coder: https://huggingface.co/deepseek-ai/deepseek-coder-6.7b-base
- Mistral 7B: https://huggingface.co/mistralai/Mistral-7B-v0.1
Bücher:
- „Building LLM Apps“ – Valentina Alto
- „Hands-On Large Language Models“ – Jay Alammar & Maarten Grootendorst
- „Spring Boot in Action“ – Craig Walls
- „Production-Ready Microservices“ – Susan J. Fowler
Monitoring & Observability:
- Prometheus: https://prometheus.io
- Grafana: https://grafana.com
- Micrometer: https://micrometer.io
🌟 Mehr Java Fleet Insights
Du willst tiefer in moderne Java-Entwicklung eintauchen?
- Elyndra’s Legacy-Code-Archäologie – Refactoring-Strategien für 20-Jahre-alte Codebasen
- Nova’s Learning-Journey – Von Junior zu Senior Developer in 24 Monaten
- Code Sentinel’s Security-Deep-Dives – Docker, Kubernetes, DevSecOps Best Practices
- Franz-Martin’s Captain’s Logs – Strategische Tech-Entscheidungen und Team-Leadership
📬 Newsletter: Wöchentlich neue Insights, exklusive Tutorials, Early Access zu neuen Serien
Veröffentlicht: 18. Oktober 2025
Teil 1 erschien: 16. Oktober 2025
Machine Learning Serie startet: 21. Oktober 2025
GitHub-Repository: https://github.com/java-fleet/local-llm-demo
Tags: #LokaleKI #LLMs #SpringBoot #Langchain4j #Ollama #Production #Java #EnterpriseAI #DSGVO #Monitoring #Caching #MultiModel #MachineLearning #PrivacyByDesign #Teil2

