Weiter zum Inhalt

DeepSeek V4 API Tutorial: Eine Arena für Denkmodi bauen

Lerne, wie die drei Denkmodi von DeepSeek V4 funktionieren, und baue mit Streamlit eine Vergleichs-App, die zeigt, wann welcher Modus bei Qualität, Geschwindigkeit und Kosten gewinnt.
Aktualisiert 12. Mai 2026

Große Sprachmodelle werden immer meinungsstärker, was ihre Art zu denken angeht. Mit den drei expliziten Denkmodi von DeepSeek V4, Non-think, Think High und Think Max, kannst du die Tiefe des Denkens jetzt direkt auf API-Ebene steuern – und die richtige Wahl hängt vollkommen von der Aufgabe ab.

In diesem Tutorial zeige ich dir, wie du mit Streamlit eine DeepSeek V4 Think Mode Arena baust. Die App schickt denselben Prompt parallel an alle drei Denkmodi und präsentiert einen Side-by-Side-Vergleich der Antworten, Latenz, Token-Nutzung, Kosten und der Tiefe der Reasoning-Traces. Am Ende hast du ein funktionsfähiges Tool, das Folgendes kann:

  • Alle drei DeepSeek V4 Denkmodi gleichzeitig mit parallelen API-Calls ausführen

  • Einklappbare Denk-Traces aus <think>...</think>-Blöcken parsen und anzeigen

  • Latenz, Ausgabetokens, Durchsatz und geschätzte Kosten pro Modus tracken

  • Einen Gewinner-Überblick über vier Dimensionen zeigen: schnellste, günstigste, effizienteste und von Menschen am besten bewertete Antwort

Der vollständige Code zu diesem Tutorial ist in meinem GitHub-Repository verfügbar.

Was ist DeepSeek V4?

DeepSeek V4 ist die neueste Generation der Mixture-of-Experts (MoE) Sprachmodelle von DeepSeek AI. Die Serie umfasst zwei Varianten:

  • DeepSeek-V4-Pro: 1,6 T Gesamtparameter (49 B aktiviert) und ein Kontextfenster von 1 Mio. Tokens.
  • DeepSeek-V4-Flash: Die kleinere Modellvariante mit 284 B Gesamtparametern (13 B aktiviert), ebenfalls mit einem 1M-Token-Kontextfenster.

Mehrere architektonische Verbesserungen unterscheiden V4 von seinen Vorgängern:

DeepSeek V4 Series Architecture

Abbildung: Architektur der DeepSeek V4 Serie (DeepSeek Technical report)

  • Hybride Attention-Architektur (CSA / HCA): Der rechte Pfad (in der obigen Abbildung) in jedem Block beginnt mit Compressed Sparse Attention (CSA) und Heavily Compressed Attention (HCA). Anstatt in jeder Schicht auf jedes Token zu achten, komprimieren diese Mechanismen die Attention-Berechnung selektiv. In einem 1M-Token-Kontext benötigt DeepSeek-V4-Pro dadurch nur 27 % der Single-Token-Inference-FLOPs und 10 % des KV-Caches im Vergleich zu DeepSeek-V3.2. Die Attention-Ausgabe wird durch Pre-Block- und Post-Block-Mixing geleitet, bevor sie über die Residual-Verbindung wieder zum Haupt-Hidden-State addiert wird.
  • DeepSeekMoE: Nach der Attention fließt derselbe Pfad in die DeepSeekMoE-Schicht, in der die Mixture-of-Experts-Komponente pro Token nur einen Teil der Parameter aktiviert. Dadurch verhalten sich die 284 B Gesamtparameter von V4-Flash zur Inferenzzeit wie ein 13B-Modell und die 1,6 T von V4-Pro wie ein 49B-Modell.
  • Manifold-Constrained Hyper-Connections (mHC): Der linke Pfad (in der Abbildung) zeigt gestapelte Hidden-State-Repräsentationen, die unten und oben in jedem Block durch Residual Mixing fließen. Das sind die mHC-Schichten, bei denen ein Ersatz für Standard-Residual-Verbindungen Updates auf einer gelernten Mannigfaltigkeit einschränkt und so die Stabilität des Gradientsignals über tiefe Stacks verbessert, ohne an Ausdrucksstärke zu verlieren.
  • Muon Optimizer: Muon (nicht in der Architektur-Grafik sichtbar) ersetzt AdamW für die Mehrheit der Parameter mit einer an zweite Ordnung angelehnten Update-Regel, die bei den V4-Trainingsskalen schneller konvergiert und stabilere Loss-Kurven liefert.

Beide Modelle unterstützen drei unterschiedliche Aufwandsmodi für das Denken: Non-think, Think High und Think Max, die wir in diesem Tutorial erkunden und vergleichen.

DeepSeek V4 Benchmark-Ergebnisse

Das DeepSeek-Team hat die Leistung des V4-Modells mit seinen Gegenstücken verglichen. Das kam dabei heraus:

Benchmark performance of DeepSeek V4 series

Abbildung: Benchmark-Leistung der DeepSeek V4 Serie (DeepSeek Technical report)

Das Balkendiagramm links zeigt, wo V4-Pro-Max im Vergleich zu führenden Closed-Source-Modellen bei Wissens-, Reasoning- und Agentenaufgaben landet. Die beiden Diagramme rechts zeigen, warum die Effizienzgewinne zählen – und sie sind die überraschenderen Ergebnisse. DeepSeek-V4-Pro-Max (der Think Max Denkmodus) erzielt wettbewerbsfähige Ergebnisse gegenüber führenden Closed-Source-Modellen:

Benchmark

DeepSeek-V4-Pro Max

Claude Opus 4.6 Max

Gemini-3.1-Pro High

LiveCodeBench (Pass@1)

93,5

88,8

91,7

Codeforces Rating

3206

3052

GPQA Diamond (Pass@1)

90,1

91,3

94,3

SWE Verified (Resolved)

80,6

80,8

80,6

Das Codeforces-Rating von 3206 platziert V4-Pro-Max auf Legendary-Grandmaster-Niveau – die Programmieraufgaben in unserer Arena sind also ein wirklich anspruchsvoller Stresstest.

Die drei Denkmodi verstehen

Bevor wir die App bauen, lohnt es sich zu verstehen, was die Modi konkret tun:

  • Non-think: Das Modell antwortet direkt, also ohne interne Gedankenkette. Ideal, wenn Geschwindigkeit wichtiger ist als Tiefe – etwa für Formatkonvertierung, einfache Nachschlagen-Aufgaben und Ein-Satz-Zusammenfassungen.
  • Think High: Das Modell denkt schrittweise nach, bevor es sich auf eine Antwort festlegt. Ein guter Kompromiss aus Tiefe und Kosten für mäßig komplexe Aufgaben wie Debugging, Systemdesign und Planung.
  • Think Max: Das Modell treibt das Reasoning aufs Maximum, erkundet mehrere Ansätze, prüft die Logik gründlich und legt sich erst fest, wenn es sicher ist. Think Max eignet sich daher am besten für harte mathematische Beweise, komplexe Refactorings über mehrere Dateien oder Aufgaben, bei denen eine falsche, aber selbstbewusste Antwort schlimmer ist als eine langsamere, verifizierte.

Die zentrale Erkenntnis dieses Tutorials: Think Max gewinnt nicht immer. Bei trivialen Aufgaben verschwendet es Rechenzeit bei gleicher Qualität. Ziel der Arena ist es, basierend auf realer Latenz, Token-Zahlen, Kosten und Nutzerbewertungen sichtbar zu machen, welcher Modus sich pro Aufgabentyp wirklich lohnt.

Wenn du wissen willst, wie sich das Modell gegenüber anderen State-of-the-Art-LLMs schlägt, lies unsere Artikel Claude Opus 4.7 vs DeepSeek V4 und GPT-5.5 vs DeepSeek V4.

Tutorial: Eine Think Mode Arena mit DeepSeek V4 bauen

In diesem Tutorial bauen wir eine Streamlit-Anwendung, die Folgendes kann:

  • Einen Prompt und eine Aufgabenkategorie vom Nutzer entgegennehmen

  • Drei parallele API-Calls auslösen, je einen pro Denkmodus, mithilfe von Pythons ThreadPoolExecutor

  • Denk-Traces aus <think>...</think>-Blöcken in der Modellausgabe parsen

  • Latenz, Token-Zahlen und geschätzte Kosten pro Modus erfassen

  • Einen Side-by-Side-Vergleich mit Metriken, Bewertungssystem und Gewinner-Überblick präsentieren

Die komplette App besteht aus einer einzigen app.py-Datei – ohne externe Datenbanken oder Hintergrunddienste.

Der vollständige Code zu diesem Tutorial ist in meinem GitHub-Repository verfügbar.

Schritt 1: Projekt-Setup und Abhängigkeiten

Erstelle zuerst einen Projektordner und installiere die zwei benötigten Pakete:

mkdir deepseek-arena && cd deepseek-arena
pip install streamlit>=1.32.0 openai>=1.12.0

Die Bibliothek openai wird hier verwendet, weil DeepSeeks API OpenAI-kompatibel ist. Das heißt, wir können den OpenAI-Client einfach auf DeepSeeks Basis-URL zeigen lassen – ohne weitere Änderungen. Ein DeepSeek-spezifisches SDK ist nicht nötig.

Hole dir einen API-Schlüssel von platform.deepseek.com und setze ihn als Umgebungsvariable:

export DEEPSEEK_API_KEY="sk-..."

Sobald die Umgebung steht, geht es an die Konfiguration von Modellen und Modi.

Schritt 2: Konfiguration — Modelle, Modi und Preise

Bevor wir Logik schreiben, definieren wir Imports, Modell-IDs, Konfigurationen für die Denkmodi und Preis-Konstanten. Diese Dictionaries steuern das Verhalten der gesamten App – vom Modelldropdown über API-Parameter, die jeden Denkmodus steuern, bis zum UI-Farbschema und den Kostenschätzungen im Vergleichspanel.

import os
import time
import concurrent.futures
from dataclasses import dataclass
from typing import Optional, Dict
import streamlit as st
from openai import OpenAI
DEEPSEEK_BASE_URL = "https://api.deepseek.com/"
DEEPSEEK_API_KEY_ENV = "DEEPSEEK_API_KEY"
MODELS = {
    "DeepSeek-V4-Flash (default)": "deepseek-v4-flash",
    "DeepSeek-V4-Pro": "deepseek-v4-pro",
}
MODES: Dict[str, dict] = {
    "Non-think": {
        "icon": "",
        "color": "#10b981",
        "badge": "green",
        "desc": "Fast, direct answers — no internal reasoning",
        "thinking_type": "disabled",
        "reasoning_effort": None,
    },
    "Think High": {
        "icon": "",
        "color": "#3b82f6",
        "badge": "blue",
        "desc": "Careful step-by-step reasoning before responding",
        "thinking_type": "enabled",
        "reasoning_effort": "high",
    },
    "Think Max": {
        "icon": "",
        "color": "#ef4444",
        "badge": "red",
        "desc": "Exhaustive reasoning — push analysis to the limit",
        "thinking_type": "enabled",
        "reasoning_effort": "max",
    },
}
PRICING = {
    "deepseek-v4-flash": {
        "input_cache_hit":  0.0028,
        "input_cache_miss": 0.14,
        "output":           0.28,
    },
    "deepseek-v4-pro": {
        "input_cache_hit":  0.003625,
        "input_cache_miss": 0.435,
        "output":           0.87,
    },
}

Ein paar Designentscheidungen lohnen sich hier zu erklären:

  • Parameter für den Denkmodus: Jeder Eintrag in MODES führt zwei API-Felder statt eines Systemprompts mit: thinking_type und reasoning_effort (”high”, "max" oder None). thinking_type wird über extra_body={"thinking": {"type": ...}} übergeben, reasoning_effort ist ein Top-Level-Request-Parameter. Non-think setzt thinking_type auf "disabled" und lässt reasoning_effort auf None, sodass überhaupt kein Thinking-Body gesendet wird.

  • Flash als Standardmodell: Flash aktiviert 13 B Parameter pro Token gegenüber 49 B bei Pro – dadurch sind drei parallele Aufrufe günstig genug für wiederholte Experimente. Pro ist über das Dropdown verfügbar, wenn du Spitzenleistung brauchst. Think Max auf Pro verbraucht jedoch schnell Budget, daher ist es bewusst als Opt-in statt Standard gesetzt.

Hinweis: Die im Code genutzten Werte spiegeln V4-Pro zu seinem aktuellen Promo-Rabatt wider. Prüfe die aktuellen Preise immer unter api-docs.deepseek.com/quick_start/pricing, bevor du Kostenvergleiche veröffentlichst, da sich Basispreise und Rabatte nach einem neuen Modell-Release häufig ändern.

Schritt 3: Aufgabenvorlagen

Das Aufgabendesign ist der am meisten unterschätzte Teil beim Bau eines Reasoning-Vergleichs. Wenn alle Aufgaben schwer und offen sind, gewinnt Think Max immer – und die Demo lehrt nichts. Der Aufgabensatz muss die gesamte Schwierigkeitsbreite abdecken, damit jeder Modus mindestens eine Kategorie hat, in der er gewinnt.

TASKS = {
    "Trivial / Lookup": {
        "prompt": (
            "Complete both tasks below:\n\n"
            "Task A — Convert this JSON to YAML:\n"
            '{"name": "Alice", "age": 30, "skills": ["Python", "ML", "LLMs"],\n'
            ' "address": {"city": "San Francisco", "zip": "94105"}}\n\n'
            "Task B — Summarize this paragraph in exactly one sentence:\n"
            '"Large language models have rapidly transformed natural language processing '
            "by demonstrating unprecedented capabilities across translation, summarization, "
            "reasoning, and code generation, driven by scale and alignment techniques like RLHF "
            'that bring model outputs closer to human intent."'
        ),
        "expected_winner": "Non-think",
        "tip": "Non-think should dominate here. No reasoning is required.",
    },
    "Coding / Debugging": {
        "prompt": (
            "Find every bug in the Python code below, explain each bug clearly, "
            "and provide a fully corrected version:\n\n"
            "```python\n"
            "def binary_search(arr, target):\n"
            "    left, right = 0, len(arr)\n"
            "    while left < right:\n"
            "        mid = (left + right) // 2\n"
            "        if arr[mid] == target:\n"
            "            return mid\n"
            "        elif arr[mid] < target:\n"
            "            left = mid\n"
            "        else:\n"
            "            right = mid - 1\n"
            "    return -1\n\n"
            "print(binary_search([1, 3, 5, 7, 9], 7))\n"
            "```"
        ),
        "expected_winner": "Think High",
        "tip": "Think High usually finds the bugs and explains them well.",
    },
    "System Design": {
        "prompt": (
            "Design a scalable vector search system for 100 million documents.\n\n"
            "Address each of the following:\n"
            "1. Indexing strategy and pipeline\n"
            "2. ANN algorithm selection (HNSW vs IVF-PQ vs ScaNN — justify your choice)\n"
            "3. Sharding and replication strategy\n"
            "4. p99 query latency target (< 50 ms) — how do you hit it?\n"
            "5. Real-time document update handling\n"
            "6. Top 3 failure modes and their mitigations"
        ),
        "expected_winner": "Think High",
        "tip": "Think High often gives the best quality-per-dollar design answer.",
    },
    "Planning": {
        "prompt": (
            "Create a detailed 6-month roadmap for deploying an enterprise RAG system.\n\n"
            "Include:\n"
            "- Month-by-month phases with concrete, measurable milestones\n"
            "- Top 5 risks and mitigation strategies\n"
            "- Team roles and headcount required per phase\n"
            "- Evaluation metrics for each phase (how do you know it's working?)\n"
            "- Go / no-go production checklist"
        ),
        "expected_winner": "Think High",
        "tip": "Think High should produce a more structured, complete roadmap.",
    },
    "Math (IMO-style)": {
        "prompt": (
            "Solve this problem completely and verify your answer:\n\n"
            "Find all positive integers n such that n² + 1 is divisible by n + 1.\n\n"
            "Your answer must include:\n"
            "1. A complete proof with clear logical steps\n"
            "2. Verification with at least 3 concrete numerical examples\n"
            "3. A rigorous argument for why your solution set is complete "
            "(i.e., there are no other solutions)"
        ),
        "expected_winner": "Think Max",
        "tip": "Think Max earns its cost when thorough verification matters.",
    },
}

Die fünf Aufgaben lassen sich klar den drei erwarteten Gewinnern zuordnen:

Aufgabe

Erwarteter Gewinner

Begründung

Trivial / Lookup

Non-think

Kein Denk-Overhead nötig; Denkzeit ist hier reine Verschwendung

Coding / Debugging

Think High

Eine methodische Runde Logik findet alle Bugs; Max bringt nur geringen Zusatznutzen

System Design

Think High

Mehr Tiefe erhöht Qualität; totale Ausschöpfung liefert keine bessere Architektur

Planning

Think High

Struktur schlägt reine Rechenleistung; ein Lernplan ist kein Beweis

Math (IMO-style)

Think Max

Verifikation ist tragend; ein falscher Beweis ist schlimmer als ein langsamer, korrekter

Jede Aufgabe führt außerdem ein Feld expected_winner. Die App zeigt es nach Eintreffen der Ergebnisse an, damit Nutzer prüfen können, ob der erwartete Gewinner bestätigt oder geschlagen wurde.

Schritt 4: Datenklasse

Mit den Aufgabenvorlagen brauchen wir nun eine Struktur, die die Ergebnisse jedes API-Calls hält. Statt rohe Response-Objekte durch die App zu reichen, definieren wir eine RunResult-Dataclass, die alle Metriken kapselt, die das Vergleichspanel benötigt.

@dataclass
class RunResult:
    mode: str
    answer: str = ""
    thinking: str = ""
    latency: float = 0.0
    input_tokens: int = 0
    output_tokens: int = 0
    cost_usd: float = 0.0
    error: Optional[str] = None
    @property
    def tokens_per_second(self) -> Optional[float]:
        if self.latency > 0 and self.output_tokens > 0:
            return self.output_tokens / self.latency
        return None
    @property
    def thinking_word_count(self) -> int:
        return len(self.thinking.split()) if self.thinking else 0

Das Feld thinking ist getrennt von answer, weil die DeepSeek V4 API die Chain-of-Thought im separaten Feld reasoning_content zurückgibt – auf derselben Ebene wie content. Durch die Trennung kann die UI einen einklappbaren Reasoning-Trace neben der sauberen Finalantwort rendern. So werden die Modusunterschiede unmittelbar greifbar, besonders bei Mathe, wo der Think Max-Trace tausende Wörter umfassen kann.

Die Property tokens_per_second misst den Generierungsdurchsatz unabhängig von der Promptlänge. Das ist hilfreich, wenn sich die Ausgabetoken stark unterscheiden. Beachte, dass Think Max naturgemäß mehr Tokens produziert – reine Latenzvergleiche können daher in die Irre führen, wenn man nicht auch die Generierungsrate sieht.

Schritt 5: Kostenhelfer

Bevor wir die API-Logik schreiben, definieren wir zwei kleine Helfer zwischen der Rohantwort des SDKs und RunResult. Sie sind leicht zu übersehen, aber relevant für korrekte Kostenschätzungen.

def get_cached_prompt_tokens(usage) -> int:
    prompt_details = getattr(usage, "prompt_tokens_details", None)
    if prompt_details is None:
        return 0
    cached_tokens = getattr(prompt_details, "cached_tokens", None)
    if cached_tokens is not None:
        return cached_tokens or 0
    if isinstance(prompt_details, dict):
        return prompt_details.get("cached_tokens", 0) or 0
    return 0
def estimate_cost_usd(
    model: str,
    prompt_tokens: int,
    completion_tokens: int,
    cached_prompt_tokens: int,
) -> float:
    pricing = PRICING.get(model, PRICING["deepseek-v4-flash"])
    cached_tokens = min(cached_prompt_tokens, prompt_tokens)
    uncached_tokens = max(prompt_tokens - cached_tokens, 0)
    return (
        cached_tokens   / 1_000_000 * pricing["input_cache_hit"]
        + uncached_tokens / 1_000_000 * pricing["input_cache_miss"]
        + completion_tokens / 1_000_000 * pricing["output"]
    )

Die Funktion get_cached_prompt_tokens() greift nicht direkt auf ein Feld zu, weil die Struktur prompt_tokens_details zwischen SDK-Versionen variieren kann. Sie prüft zuerst auf ein typisiertes Attribut, fällt dann auf Dict-Zugriff zurück und gibt ansonsten null zurück, statt eine Exception zu werfen. Das ist wichtig im ThreadPoolExecutor, wo ein stiller Fehler in einem Thread zu zu niedrigen Kostenschätzungen führen könnte, ohne sichtbar zu werden.

Der Helfer estimate_cost_usd() trennt die Prompttokens in Cache-Hit- und Cache-Miss-Anteile, bevor Preise angewendet werden. Da DeepSeek die Cache-Hit-Preise auf ein Zehntel der Miss-Rate gesenkt hat, ist der Unterschied zwischen Cold- und Warm-Prompt erheblich. Bei wiederholten Läufen kann Non-think dadurch fast kostenlos erscheinen – nicht, weil es weniger Tokens ausgibt, sondern weil sein kürzerer Prompt sehr wahrscheinlich warm im Cache ist.

Schritt 6: Parallele API-Ausführung

Das ist der architektonische Kern der Arena. Alle drei Modi starten gleichzeitig über einen ThreadPoolExecutor, sodass die gesamte Wandzeit der langsamsten Einzelantwort entspricht – nicht der Summe aller drei.

def call_mode(client: OpenAI, model: str, mode_name: str, user_prompt: str) -> RunResult:
    result = RunResult(mode=mode_name)
    mode_cfg = MODES[mode_name]
    start = time.perf_counter()
    try:
        request_kwargs = {
            "model": model,
            "messages": [{"role": "user", "content": user_prompt}],
            "max_tokens": 4096,
            "extra_body": {"thinking": {"type": mode_cfg["thinking_type"]}},
        }
        if mode_cfg["reasoning_effort"]:
            request_kwargs["reasoning_effort"] = mode_cfg["reasoning_effort"]
        response = client.chat.completions.create(**request_kwargs)
        result.latency = time.perf_counter() - start
        message = response.choices[0].message
        result.thinking = (getattr(message, "reasoning_content", None) or "").strip()
        result.answer   = (message.content or "").strip()
        usage = response.usage
        result.input_tokens  = getattr(usage, "prompt_tokens", 0) or 0
        result.output_tokens = getattr(usage, "completion_tokens", 0) or 0
        result.cost_usd = estimate_cost_usd(
            model=model,
            prompt_tokens=result.input_tokens,
            completion_tokens=result.output_tokens,
            cached_prompt_tokens=get_cached_prompt_tokens(usage),
        )
    except Exception as exc:
        result.latency = time.perf_counter() - start
        result.error = str(exc)
    return result
def run_parallel(client: OpenAI, model: str, prompt: str) -> Dict[str, RunResult]:
    results: Dict[str, RunResult] = {}
    with concurrent.futures.ThreadPoolExecutor(max_workers=3) as pool:
        futures = {
            pool.submit(call_mode, client, model, mode_name, prompt): mode_name
            for mode_name in MODES
        }
        for fut in concurrent.futures.as_completed(futures):
            results[futures[fut]] = fut.result()
    return results

Einige Konstruktionsdetails sind hier wichtig:

  • request_kwargs bedingt aufbauen: Der Non-think-Modus sendet thinking_type: "disabled" und keinen Schlüssel reasoning_effort. Think High und Think Max senden thinking_type: "enabled" sowie ihre jeweilige Effort-Stufe. Die Abfrage if mode_cfg["reasoning_effort"] stellt sicher, dass der Parameter bei Non-think komplett weggelassen wird, statt als None gesendet zu werden – das könnte sonst zu API-Validierungsfehlern führen.

  • reasoning_content direkt lesen: Die Chain-of-Thought wird aus message.reasoning_content per getattr() mit Fallback auf einen leeren String gelesen. Das ist robuster, als <think>...</think>-Tags aus dem Content-String zu parsen. Es nutzt das offizielle Feld, handhabt Fälle ohne Trace (Non-think) und bricht nicht, falls das Wort "think" aus anderen Gründen im Output steht.

  • Defensives getattr() bei Usage-Feldern: Usage-Felder werden mit getattr(..., 0) or 0 gelesen statt direkt. Fehlt ein Feld in älteren SDKs oder in ungewöhnlichen Antworten, fällt der Wert auf null zurück, anstatt eine AttributeError auszulösen, die den gesamten Lauf scheitern ließe.

  • Error-Handling: Wenn ein Call fehlschlägt, speichert die RunResult-Dataclass den Fehlerstring statt zu werfen. Die UI prüft result.error und rendert eine informative Karte, ohne den gesamten Drei-Modi-Lauf zum Absturz zu bringen.

Schritt 7: Streamlit UI und CSS

Dieser Schritt deckt die Präsentation ab: Seitenlayout, CSS-Designsystem, die Ergebnis-Spalte pro Modus und die Tab-Struktur, die den Vergleich organisiert, ohne auf den ersten Blick zu überfrachten.

Die App nutzt st.set_page_config mit layout="wide", damit der Drei-Spalten-Vergleich genug horizontalen Platz hat:

def main():
    st.set_page_config(
        page_title="DeepSeek V4 Think Mode Arena",
        layout="wide",
        initial_sidebar_state="expanded",
    )
    inject_css()

Die Funktion inject_css() injiziert ein vollständiges Designsystem per st.markdown. Es nutzt einen warmen Pergamenthintergrund, Space Mono für Monospace-Überschriften und Metrik-Chips sowie DM Sans für Fließtext.

def render_mode_column(result: RunResult, mode_name: str):
    cfg = MODES[mode_name]
    badge_cls = f"mode-{cfg['badge']}"  
    st.markdown(
        f'<div class="mode-header {badge_cls}">{mode_name}</div>',
        unsafe_allow_html=True,
    )
    st.markdown(f'<div class="subtle-copy">{cfg["desc"]}</div>', unsafe_allow_html=True)
    if result.error:
        st.error(f"**API Error:** {result.error}")
        return
    chips = [
        ("Latency", f"{result.latency:.1f}s"),
        ("Output tokens", f"{result.output_tokens:,}"),
        ("Cost", f"${result.cost_usd:.5f}"),
    ]
    if result.tokens_per_second:
        chips.append(("Tok/s", f"{result.tokens_per_second:.0f}"))
    chip_html = "".join(
        f'<span class="metric-chip">{label} <span>{val}</span></span>'
        for label, val in chips
    )
    st.markdown(chip_html, unsafe_allow_html=True)
    if result.thinking:
        with st.expander(f" Thinking trace — {result.thinking_word_count:,} words"):
            preview = result.thinking[:5000]
            if len(result.thinking) > 5000:
                preview += "\n\n[… truncated for display …]"
            st.text(preview)
    elif mode_name != "Non-think":
        st.caption("_No thinking trace emitted_")
    st.markdown('<div class="answer-label">Final answer</div>', unsafe_allow_html=True)
    st.markdown(result.answer if result.answer else "_No answer returned._")

Der Thinking-Trace-Expander macht den Modusunterschied besonders greifbar: Think Max kann bei harten Matheaufgaben tausende Wörter an Selbstkorrektur und Verifikation ausgeben, während Non-think oft gar keinen Trace hat.

Das Hauptlayout nutzt Streamlit-Tabs, um Inhalte zu trennen, statt alles in einen endlosen Scroll zu pressen:

overview_tab, answers_tab, ratings_tab = st.tabs(
    ["Overview", "Full Responses", "Ratings"]
)

Der Tab „Overview“ zeigt die Gewinnerzusammenfassung und die Metriktabelle. „Full Responses“ zeigt den Drei-Spalten-Vergleich mit Thinking-Traces. „Ratings“ enthält Slider für Nutzerbewertungen (1–5) pro Modus.

Schritt 8: Metriken und Bewertungen

Zum Schluss bauen wir zwei Komponenten für den Overview-Tab: eine flache Metriktabelle mit allen Dimensionen nebeneinander und eine Gewinnerzusammenfassung mit vier klaren Kategorien.

def render_metrics_table(results: Dict[str, RunResult], ratings: Dict[str, int]):
    rows = []
    for mode_name, res in results.items():
        ok = not res.error
        rows.append({
            "Mode":            mode_name,
            "Latency (s)":     f"{res.latency:.2f}" if ok else "—",
            "Input Tokens":    f"{res.input_tokens:,}" if ok else "—",
            "Output Tokens":   f"{res.output_tokens:,}" if ok else "—",
            "Tok/s":           f"{res.tokens_per_second:.0f}" if (ok and res.tokens_per_second) else "—",
            "Est. Cost (USD)": f"${res.cost_usd:.5f}" if ok else "—",
            "Thinking Words":  f"{res.thinking_word_count:,}" if ok else "—",
            "User Rating":     f"{ratings.get(mode_name)}/5" if ratings.get(mode_name) else "—",
        })
    st.table(rows)
def render_winner_summary(results: Dict[str, RunResult], ratings: Dict[str, int], expected: str):
    valid = {k: v for k, v in results.items() if not v.error}
    fastest        = min(valid, key=lambda k: valid[k].latency)
    cheapest       = min(valid, key=lambda k: valid[k].cost_usd)
    most_efficient = max(
        valid,
        key=lambda k: valid[k].output_tokens / max(valid[k].cost_usd, 1e-9),
    )
    top_rated = max(ratings, key=ratings.get) if ratings else None

Kurz, was diese Funktionen leisten:

  • render_metrics_table() iteriert über alle RunResult-Objekte und baut je Modus eine Zeile. Die acht Spalten decken Zeit (Latenz, Tok/s), Umfang (Input- und Output-Tokens, Thinking-Wörter), Geld (geschätzte Kosten) und menschliches Urteil (Nutzerbewertung) ab.

  • render_winner_summary() filtert zunächst fehlgeschlagene Läufe heraus, damit ein einzelner API-Fehler die Ergebnisse nicht verzerrt. Dann ermittelt es den Sieger in vier unabhängigen Dimensionen: Wandzeit, Rohkosten, Ausgabe-Effizienz und Nutzerwertung.

Die vier Kategorien bleiben bewusst getrennt statt zu einem Score gewichtet – die Frage, ob Latenz wichtiger ist als Kosten, ist eine Produktentscheidung, keine Framework-Entscheidung.

Außerdem zeigt die App den erwarteten Gewinner pro Aufgabe an und fordert zur Bestätigung auf:

st.markdown(
    f"> **Expected winner for this task type:** {expected} — "
    "does your result match? Rate answers to confirm.",
)

Schritt 9: App starten

Die gesamte App steckt in einer einzigen app.py mit zwei Abhängigkeiten – der Start erfolgt in zwei Befehlen:

# Set your API key
export DEEPSEEK_API_KEY="sk-..."
# Run
streamlit run app.py

Die App öffnet sich im Browser unter localhost:8501. Wähle eine Aufgabenvorlage im Dropdown, bearbeite optional den Prompt und klicke auf Run Arena. Ein Fortschrittsbalken aktualisiert sich, sobald ein Modus fertig ist. Ergebnisse liegen in st.session_state, damit du Tabs wechseln und bewerten kannst, ohne einen Re-Run auszulösen.

Demo

Hinweis zu Preisen: DeepSeek hat die V4 API-Preise seit dem Launch deutlich angepasst. V4-Pro ist aktuell im Promo-Rabatt, und die Cache-Hit-Preise wurden im April 2026 auf ein Zehntel der Miss-Rate gesenkt. Die in der App gezeigten Kosten spiegeln diese aktuellen Sätze wider, können sich aber ändern. Prüfe immer unter api-docs.deepseek.com/quick_start/pricing, bevor du ein Modell produktiv nutzt.

Fazit

In diesem Tutorial haben wir eine Streamlit-App gebaut, die denselben Prompt parallel über die drei Denkmodi von DeepSeek V4 laufen lässt und die Ergebnisse nach Latenz, Kosten, Tokennutzung, Tiefe der Thinking-Traces und menschlicher Bewertung vergleicht. Die wichtigsten Architekturentscheidungen waren:

  • ThreadPoolExecutor für echte Parallelität – die gesamte Wandzeit entspricht der langsamsten Antwort statt der Summe aller drei

  • Die API-Parameter thinking und reasoning_effort sorgen für saubere, explizite Modussteuerung im Einklang mit DeepSeeks API – statt fragilem Systemprompt-Steering

  • Cache-bewusste Kostenschätzung teilt Eingabetokens in Cache-Hit und -Miss auf und liefert deutlich genauere Kosten, besonders bei wiederholten Läufen, wo Warm-Cache-Rabatte Non-think nahezu kostenlos erscheinen lassen können

Wenn du dieses Projekt erweitern willst, könntest du eine LLM-as-judge-Schicht hinzufügen (ein separates Modell wie Claude oder GPT-4 bewertet Antworten automatisch), Antworten per Prompthash cachen, um identische Anfragen nicht neu zu rechnen, oder eine Cross-Model-Achse einführen, die Flash Think Max gegen Pro Think High stellt – eine wirklich spannende Kostenparitätsfrage, die das V4-Paper aufwirft, aber nicht vollständig beantwortet.

DeepSeek V4 API Denkmodus-Arena: FAQs

Warum sollten wir OpenAI statt eines DeepSeek SDKs verwenden?

Die API von DeepSeek ist OpenAI-kompatibel. Wir können den OpenAI-Client daher mit einem DeepSeek API-Schlüssel auf https://api.deepseek.com zeigen. Das bedeutet auch: Die App funktioniert bei jedem OpenAI-kompatiblen Provider, der DeepSeek V4 hostet – es müssen nur Basis-URL und Modellstring angepasst werden.

Warum wird reasoning_effort beim Non-think-Modell weggelassen statt auf None gesetzt?

Die Abfrage if mode_cfg["reasoning_effort"] in call_mode() lässt den Parameter ganz weg, wenn er None ist, statt reasoning_effort=None zu senden. Manche API-Validatoren lehnen unbekannte Parameter mit expliziten Nullwerten ab. Das vollständige Weglassen ist daher das robustere Muster bei frisch eingeführten Endpunkten.

Warum erscheint der Thinking-Trace bei Think High manchmal nicht?

Der Trace wird aus message.reasoning_content gelesen. Gibt das Modell für einen Lauf ein leeres oder null Feld zurück – was bei kürzeren, weniger komplexen Prompts passieren kann, wenn Think High schnell konvergiert –, ist result.thinking ein leerer String.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Ich bin Google Developers Expertin für ML (Gen AI), dreifache Kaggle-Expertin und Women-Techmakers-Botschafterin mit über drei Jahren Erfahrung in der Tech-Branche. 2020 habe ich ein Health-Tech-Startup mitgegründet und absolviere derzeit einen Master in Informatik an der Georgia Tech mit Schwerpunkt Machine Learning.

Themen

Top-Kurse zu KI

Lernpfad

KI-Agent-Grundlagen

6 Std.
Entdecke, wie KI-Agenten deine Arbeitsweise verändern und Mehrwert für dein Unternehmen schaffen können!
Details anzeigenRight Arrow
Kurs starten
Mehr anzeigenRight Arrow
Verwandt

Blog

Arten von KI-Agenten: Ihre Rollen, Strukturen und Anwendungen verstehen

Lerne die wichtigsten Arten von KI-Agenten kennen, wie sie mit ihrer Umgebung interagieren und wie sie in verschiedenen Branchen eingesetzt werden. Verstehe einfache reflexive, modellbasierte, zielbasierte, nutzenbasierte, lernende Agenten und mehr.

Blog

Die 36 wichtigsten Fragen und Antworten zum Thema generative KI für 2026

Dieser Blog hat eine ganze Reihe von Fragen und Antworten zu generativer KI, von den Grundlagen bis hin zu fortgeschrittenen Themen.
Hesam Sheikh Hassani's photo

Hesam Sheikh Hassani

15 Min.

Tutorial

Python Switch Case Statement: Ein Leitfaden für Anfänger

Erforsche Pythons match-case: eine Anleitung zu seiner Syntax, Anwendungen in Data Science und ML sowie eine vergleichende Analyse mit dem traditionellen switch-case.
Matt Crabtree's photo

Matt Crabtree

Tutorial

Python Datenstrukturen Tutorial

Mach dich mit Python-Datenstrukturen vertraut: Lerne mehr über Datentypen und primitive sowie nicht-primitive Datenstrukturen wie Strings, Listen, Stapel usw.
Sejal Jaiswal's photo

Sejal Jaiswal

Tutorial

Wie man Listen in Python aufteilt: Einfache Beispiele und fortgeschrittene Methoden

Lerne, wie du Python-Listen mit Techniken wie Slicing, List Comprehensions und itertools aufteilen kannst. Finde heraus, wann du welche Methode für die beste Datenverarbeitung nutzen solltest.
Allan Ouko's photo

Allan Ouko

Tutorial

Loop-Schleifen in Python-Tutorial

Lerne, wie du For-Schleifen in Python umsetzt, um eine Sequenz oder die Zeilen und Spalten eines Pandas-DataFrame zu durchlaufen.
Aditya Sharma's photo

Aditya Sharma

Mehr anzeigenMehr anzeigen