AI-Stack kostenlos: Detaillierte Erklärung mit Beispielen

Zu diesem Artikel gibt es ein kurzes Video:YouTubeTikTok

1. Übersicht

Viele Menschen denken, dass der Bau eines AI-Systems — Agent, RAG, interner Chatbot — Tausende Dollar pro Monat kostet, weil man APIs von OpenAI oder Anthropic bezahlen muss. Das ist nicht wahr: Es gibt einen Stack aus 8 Schichten, jede mit einer kostenlosen oder self-hosted Version, ausreichend um ein vollständiges AI-System mit 0€ zu bauen und zu betreiben. Dieser Artikel geht durch jede Schicht, erklärt ihre Rolle und zeigt dir konkrete Code-Beispiele, damit du dir vorstellen kannst, wie man sie zusammenfügt. Nach dem Lesen wirst du wissen, welche Tools du für jede Schicht wählst und wo die echten Grenzen der “kostenlosen” Variante wirklich liegen.

2. Warum “0€” nicht für immer kostenlos bedeutet

Bevor wir ins Detail gehen, muss das klar sein: “0€” hier bedeutet Software-/Lizenzkosten, nicht unbegrenzte Infrastrukturkosten. Vercel Free Tier hat Bandbreitenlimits, Supabase Free Tier hat Storage-Limits, lokal Ollama zu laufen kostet CPU/RAM deiner Maschine. Dieser Stack eignet sich zum Prototyping, Lernen, Demo für kleine Kunden, nicht zum Skalieren auf Millionen Nutzer.

Hinweis: Wenn dein echter Traffic die Free Tier überschreitet, musst du trotzdem zahlen — nur zahlst du für Infrastruktur (Compute, Storage) statt für LLM-Tokens beim Vendor. Das ist ein wichtiger Unterschied, den du vor dem Commit zu diesem Stack für echte Produktion verstehen solltest.

3. Frontend-Schicht: Next.js oder Streamlit

Die Interface-Schicht muss nicht komplex sein. Wenn du eine schöne UI willst, nutze Next.js auf Vercel Free Tier. Wenn du nur schnell eine AI-Pipeline demonstrieren möchtest, ist Streamlit deutlich schneller, weil du kein separates Frontend bauen brauchst.

# app.py
import streamlit as st
import requests

st.title("Assistent für interne Dokumentfragen")

question = st.text_input("Gib deine Frage ein")

if st.button("Fragen") and question:
    response = requests.post(
        "http://localhost:8000/ask",
        json={"question": question},
        timeout=30,
    )
    st.write(response.json()["answer"])

Dieser Code erstellt ein Eingabefeld und einen Button. Wenn du den Button klickst, ruft er das Backend-API (lokal laufend) auf und zeigt die Antwort an. Mit streamlit run app.py starten, Streamlit hostet es automatisch auf localhost:8501. Keine React zu schreiben, keine Build-Pipeline — perfekt für die Validierungsphase, bevor du in Next.js investierst.

4. Agent-Orchestrierungs-Schicht: LangGraph

Wenn dein System nicht nur “LLM einmal aufrufen dann Ergebnis zurückgeben” ist, sondern mehrere Schritte braucht (suchen → zusammenfassen → prüfen → antworten), brauchst du einen Orchestrator. LangGraph modelliert den Agent als einen Graph mit State, erlaubt Verzweigung und Schleife.

<!-- pom.xml nicht anwendbar hier, da LangGraph eine Python-Bibliothek ist -->
pip install langgraph langchain-ollama
from langgraph.graph import StateGraph, END
from typing import TypedDict

class AgentState(TypedDict):
    question: str
    answer: str

def retrieve_step(state: AgentState) -> AgentState:
    state["answer"] = f"Suche nach Dokumenten für: {state['question']}"
    return state

def generate_step(state: AgentState) -> AgentState:
    state["answer"] += " -> Antwort zusammengefasst"
    return state

graph = StateGraph(AgentState)
graph.add_node("retrieve", retrieve_step)
graph.add_node("generate", generate_step)
graph.set_entry_point("retrieve")
graph.add_edge("retrieve", "generate")
graph.add_edge("generate", END)

app = graph.compile()
result = app.invoke({"question": "Was ist die Garantierichtlinie?", "answer": ""})
print(result["answer"])
Suche nach Dokumenten für: Was ist die Garantierichtlinie? -> Antwort zusammengefasst

StateGraph definiert den Verarbeitungsablauf mit zwei Nodes: retrieve und generate, jeder Node nimmt State und gibt neuen State zurück. set_entry_point setzt den Startnode, add_edge verbindet die Schritte. Wenn du app.invoke(...) aufrufst, läuft LangGraph sequenziell durch den Graph und gibt den finalen State zurück. Alles läuft lokal, kein API-Call mit Kosten.

5. RAG-Pipeline-Schicht: Chroma und Qdrant

RAG (Retrieval-Augmented Generation) braucht einen Vector Store um Embeddings zu speichern und nach Ähnlichkeit zu suchen. Chroma läuft embedded, braucht keinen separaten Server — perfekt für Prototypen.

pip install chromadb
import chromadb

client = chromadb.PersistentClient(path="./chroma_data")
collection = client.get_or_create_collection(name="policies")

collection.add(
    documents=["Produktgarantie 12 Monate ab Kaufdatum.",
               "Rückgabe innerhalb 7 Tage wenn Verpackung intakt."],
    ids=["doc1", "doc2"],
)

results = collection.query(query_texts=["Garantierichtlinie"], n_results=1)
print(results["documents"])
[['Produktgarantie 12 Monate ab Kaufdatum.']]

PersistentClient speichert Daten auf der Festplatte statt nur im RAM, so bleiben Daten über Container-Neustart erhalten. collection.add berechnet automatisch Embeddings für die Dokumente (Chroma hat eine Standard-Embedding-Funktion). collection.query findet das ähnlichste Dokument zum Vektor der Frage. Mit größeren Datenmengen wechselst du zu Qdrant, lokal über Docker — ähnliche API, aber optimiert für Skalierung.

6. LLM-Schicht: Ollama mit lokalem Modell

Das ist die Kernschicht, um den API-Rechnungen zu entkommen. Ollama lässt dich Llama, Mistral, Gemma direkt auf der Maschine laufen, ohne Request-Limit.

ollama pull llama3.1
ollama run llama3.1 "Fasse die Garantierichtlinie in einem Satz zusammen."
Das Produkt wird 12 Monate ab Kaufdatum garantiert.

ollama pull lädt das Modell herunter (Modell ist quantisiert um auf Standard-CPUs/GPUs zu laufen). ollama run startet das Modell und sendet den Prompt, gibt das Ergebnis direkt im Terminal zurück. Ollama öffnet auch eine REST-API auf localhost:11434, sodass du es von Python oder Java aus aufrufen kannst.

Wenn dein Backend Spring Boot ist, Ollama über RestClient aufrufen ist sehr einfach:

import org.springframework.web.client.RestClient;

public class OllamaClient {

    private final RestClient restClient = RestClient.create("http://localhost:11434");

    public String generate(String model, String prompt) {
        var response = restClient.post()
                .uri("/api/generate")
                .body(new OllamaRequest(model, prompt, false))
                .retrieve()
                .body(OllamaResponse.class);
        return response.response();
    }

    record OllamaRequest(String model, String prompt, boolean stream) {}
    record OllamaResponse(String response) {}
}

OllamaClient kapselt den HTTP-Call zu Ollama. RestClient aus Spring 6 ersetzt das alte RestTemplate, schreibt sich kürzer. OllamaRequest setzt stream = false um die komplette Response auf einmal zu bekommen statt Token für Token — einfacher für Demo-Code.

7. Tool Use über MCP (Model Context Protocol)

Ein Agent der nur mit dem LLM spricht kann nichts Praktisches tun. MCP ist ein Standard, der erlaubt, dass Agent externe Tools aufruft — Dateien lesen, Database abfragen, GitHub-API aufrufen — über eine einheitliche Schnittstelle ohne Custom-Integration für jedes Tool.

{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-filesystem", "/data/docs"]
    },
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"]
    }
  }
}

Diese Config deklariert zwei MCP Server: einen für Filesystem (lesen/schreiben in /data/docs), einen für GitHub (Issues erstellen, PRs lesen). Dein Agent (LangGraph, Claude Code, oder jeder MCP-fähige Client) verbindet sich zu diesen Servern und ruft Tools über das Standard-Schema auf — keine Custom-API-Wrapper nötig für jeden Service.

Hinweis: MCP Server lokal über npx zu laufen kostet keine Lizenz, aber manche Server fordern Access Tokens (z.B. GitHub Token) — diesen Token musst du trotzdem vom Original-Service besorgen.

8. Code-Agent: Aider

Wenn du möchtest, dass der Agent selbst Code in deinem Repo schreibt und korrigiert, ist Aider ein Open-Source CLI-Tool, integriert sich gut mit lokalem Model über Ollama.

pip install aider-chat
aider --model ollama/llama3.1 --file src/main/java/OrderService.java

Dieser Command öffnet Aider, sagt ihm llama3.1 über Ollama zu nutzen (nicht API mit Kosten), und lädt OrderService.java in den Context. Von hier tippst du Anforderungen (“validiere negative Mengen”), Aider generiert das Diff und committed es wenn du OK gibst.

9. Daten-Schicht: SQLite, DuckDB, Supabase

Nicht jedes AI-System braucht einen Postgres-Cluster. SQLite reicht für kleine strukturierte Daten, DuckDB ist mächtig um Dateien zu analysieren (CSV, Parquet) ohne separaten Server.

import duckdb

con = duckdb.connect()
result = con.execute("""
    SELECT category, COUNT(*) AS total
    FROM read_csv_auto('orders.csv')
    GROUP BY category
    ORDER BY total DESC
""").fetchall()
print(result)
[('electronics', 120), ('books', 45), ('clothing', 30)]

DuckDB liest direkt CSV-Dateien mit read_csv_auto, braucht keine ETL oder Import in separate DB. SQL-Queries laufen schnell mit Hunderttausenden Zeilen — reicht für die meiste interne Analyse. Wenn du ein Backend mit REST-API und Auth brauchst, gibt Supabase Free Tier managed Postgres mit Realtime und Storage.

10. Deployment: Docker, Cloudflare Workers, Hugging Face

Mit Docker containerisierst du alles, um konsistent zwischen Dev-Maschine und Host zu deployen.

# docker-compose.yml
services:
  ollama:
    image: ollama/ollama
    ports:
      - "11434:11434"
    volumes:
      - ollama_data:/root/.ollama

  app:
    build: .
    ports:
      - "8000:8000"
    depends_on:
      - ollama

volumes:
  ollama_data:

Diese Config definiert zwei Services: ollama läuft das LLM-Modell, app ist dein Backend, abhängig von ollama um Inference aufzurufen. docker compose up startet beide gleichzeitig, Volume ollama_data behält die gezogenen Modelle über Container-Neustarts. Für öffentliche Demos kannst du das Frontend auf Cloudflare Workers (Edge, großzügiger Free Tier) pushen und kleine Modelle auf Hugging Face Spaces damit andere testen ohne ihre Maschine zu konfigurieren.

11. Den kompletten Stack zusammenfügen

Der echte Ablauf: Nutzer tippt Frage in Streamlit → LangGraph empfängt Request, ruft Chroma auf um relevanten Context zu holen → Context in Prompt einbauen, an Ollama senden → wenn Tools nötig sind (Datei lesen, DB abfragen), ruft Agent über MCP auf → Ergebnis zurück zur UI. Alles auf einer Maschine oder Container, kein Request geht raus zu API mit Kosten.

def answer_question(question: str) -> str:
    docs = collection.query(query_texts=[question], n_results=2)
    context = "\n".join(docs["documents"][0])
    prompt = f"Basierend auf folgenden Informationen:\n{context}\n\nBeantworte: {question}"
    import ollama
    response = ollama.generate(model="llama3.1", prompt=prompt)
    return response["response"]

Diese Funktion ist das “Herz” von RAG: Context von Chroma holen, in den Prompt einbauen, Ollama aufrufen um die finale Antwort zu generieren. Das ist das einfachste retrieve-then-generate Pattern, keine komplexen Frameworks wenn dein Use-Case einfach ist.

12. Tests für die Datenverarbeitung schreiben

Der leichteste Teil einer RAG-Pipeline zu testen ist meist der Text-Chunking-Schritt (kleine Textstücke vor Embedding). Wenn dein Java-Backend diesen Schritt macht, hilft ein einfacher Unit-Test off-by-one Fehler zu vermeiden.

import java.util.List;
import java.util.ArrayList;

public class DocumentChunker {

    public List<String> chunk(String text, int maxLength) {
        List<String> chunks = new ArrayList<>();
        for (int i = 0; i < text.length(); i += maxLength) {
            chunks.add(text.substring(i, Math.min(i + maxLength, text.length())));
        }
        return chunks;
    }
}
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;

class DocumentChunkerTest {

    @Test
    void shouldSplitTextIntoFixedSizeChunks() {
        DocumentChunker chunker = new DocumentChunker();

        List<String> chunks = chunker.chunk("Produktgarantie 12 Monate für alle Artikel", 10);

        assertThat(chunks).hasSizeGreaterThan(1);
        assertThat(chunks.get(0)).hasSize(10);
        assertThat(String.join("", chunks)).isEqualTo("Produktgarantie 12 Monate für alle Artikel");
    }
}

Dieser Test stellt drei Dinge sicher: langer Text wird in mehrere Chunks aufgeteilt, erstes Chunk hat richtige Länge, und Zusammenfügen aller Chunks gibt genau den Original-Text — kein Zeichen verloren. Das ist eine Art Bug die leicht passiert wenn du Chunking selbst schreibst ohne Tests.

13. Häufige Fehler und wann man diesen Stack nicht nutzen sollte

  • Lokale Modelle sind viel langsamer als Cloud-APIs ohne GPU. Mit Standard-CPU kann ein llama3.1 8B Request 5-15 Sekunden dauern — akzeptabel für Demo, nicht für Produkt mit direkter Nutzer-Erwartung.
  • Free Tiers haben versteckte Limits. Vercel, Supabase, Cloudflare limitieren Requests/Monat; über dem Limit wird die App blockiert wenn du nicht das Dashboard überwachst.
  • Chroma embedded passt nicht zu Multi-Instance. Wenn du dein Backend auf mehrere Pods skalierst, hat jeder Pod seine eigene Chroma-Datei → Inkonsistenz. Dann wechselst du zu Qdrant Server oder managed Vector Store.
  • Lokale MCP Server vergrößern die Angriffsfläche. Ein Filesystem MCP Server erlaubt dem Agent Dateien zu lesen/schreiben — wenn versehentlich ins Internet exposed, ist das ein großes Sicherheitsrisiko.

Hinweis: Wenn du ein echtes Produkt mit SLA baust, stabilen niedriger Latenz brauchst oder großes Traffic von Anfang an, nutze diesen 0€ Stack nur in der Prototyp-Phase. Bei echtem Launch zahle für managed LLM-APIs oder private GPU-Instanzen um Service-Qualität zu garantieren.

14. Fazit

Der 0€ Stack ist kein Marketing-Trick — es ist eine echte Kombination aus Open-Source-Tools und Free Tiers, ausreichend um ein vollständiges AI-System mit RAG, Agent, Tool Use und Deployment zu bauen ohne Kreditkarte. Die echte Barriere liegt nicht beim Geld, sondern ob du jede Schicht richtig zusammenfügen kannst, die Free-Tier-Grenzen verstehst und weißt, wann du auf bezahlte Infrastruktur upgraden solltest um Qualität zu sichern. Fang mit diesem Stack an um zu lernen und Ideen zu validieren, dann upgrade jede Schicht wenn dein Produkt echten Scale braucht.

llmragollamalanggraphmcp