Code AI

Fill-in-the-Middle (FIM) Code-Vervollständigung: Benchmarks & Architektur

Schnelle Antwort: Im Jahr 2026 ermöglicht Fill-in-the-Middle (FIM) die Echtzeit-Ghost-Text-Codevervollständigung in IDEs, indem Prompts mit Präfix-, Suffix- und Middle-Tokens strukturiert werden (z. B. <|fim_prefix|>, <|fim_suffix|>, <|fim_middle|>). Bei Sub-100ms-Inferenz führt Qwen 2.5 Coder 1.5B bei einzeiliger Genauigkeit (84,6% SantaCoder FIM) mit 42ms TTFT, während Qwen 2.5 Coder 7B mehrzeilige Synthesen (76,8%) bei 84ms dominiert.


1. Einführung: Latenz- und Präzisionsanforderungen an Ghost-Text-KI-Autovervollständigung

Echtzeit-ai code completion (KI-Code-Vervollständigung) und ai autocomplete stellen die latenzkritischsten Workloads in der angewandten künstlichen Intelligenz dar. Während dialogbasierte Programmier-Agenten (wie Claude Code, Aider oder OpenCode) sich 1,5 bis 5,0 Sekunden Bedenkzeit für Multi-File-Refactorings nehmen können, müssen Inline-Vorschläge im Editor („Ghost-Text“) in unter 100 Millisekunden erscheinen, um den kognitiven Entwickler-Flow nicht zu unterbrechen.

+-----------------------------------------------------------------------------------------------+
|                       Latenzbudget für Ghost-Text IDE-Code-Vervollständigung                  |
+-----------------------------------------------------------------------------------------------+
| Tastendruck-Debounce  : 30ms - 50ms                                                           |
| Kontextzusammenstellung: 10ms - 15ms  (Tree-sitter AST, Präfix-/Suffix-Fensterung)            |
| Netzwerk / IPC        : 5ms  - 20ms  (Lokales vLLM / llama.cpp oder Edge WebSocket)           |
| Time-To-First-Token   : 35ms - 55ms  (Harte Sub-100ms-Grenze für das erste Zeichen)           |
| Token-Streaming       : 15ms - 25ms  (15-40 Tokens bei 120+ Tokens/Sek. für Zeilenabschluss)   |
+-----------------------------------------------------------------------------------------------+
| GESAMTES BUDGET       : 95ms - 145ms (Wahrnehmungsschwelle für sofortige Textvervollständigung)|
+-----------------------------------------------------------------------------------------------+

Tippt ein Softwareentwickler in VS Code, JetBrains, Neovim oder Xcode, löst jeder Tastenanschlag ein Dokument-Änderungsevent aus. Benötigt der Vervollständigungsvorschlag länger als 150ms, hat der Entwickler bereits weitere Zeichen getippt, was zu Flackern, verworfenen Inferenzzyklen und Frustration führt.

Traditionelle autoregressive Sprachmodelle werden ausschließlich auf die Vorhersage des nächsten Tokens von links nach rechts trainiert:

$$P(W) = \prod_{i=1}^{n} P(w_i \mid w_1, w_2, \dots, w_{i-1})$$

In einem aktiven Code-Editor schreibt man jedoch selten rein von oben nach unten. Häufig befinden sich nach dem Cursor bereits Klassen, Methoden oder schließende Klammern. Sieht ein Modell nur den Präfix vor dem Cursor, generiert es redundante Klammern oder Variablen, die mit nachfolgendem Code kollidieren.

Hier setzt Fill-in-the-Middle (FIM) Code-Generierung an: Ein Paradigma, das sowohl den Präfix (Code vor dem Cursor) als auch das Suffix (Code nach dem Cursor) als Bedingung nutzt, um exakt das fehlende syntaktische Mittelstück (Middle) einzusetzen.


2. Kernarchitektur: Wie Fill-in-the-Middle (FIM) funktioniert

Das FIM-Verfahren, von OpenAI-Forschern (Bavarian et al.) theoretisiert und in Open-Source-Modellen wie StarCoder, DeepSeek Coder und Qwen Coder skaliert, verwandelt autoregressive Transformer in bidirektional kontextbewusste Engines, ohne die Kausalmaske der Attention-Matrix zu verändern.

+-----------------------------------------------------------------------------------------------+
|                            Fill-in-the-Middle (FIM) Transformation                            |
+-----------------------------------------------------------------------------------------------+
| Ursprüngliches Quelltextdokument:                                                             |
| [ KONTEXT VOR DEM CURSOR (Prefix) ] [ CURSOR-LÜCKE (Middle) ] [ KONTEXT NACH DEM CURSOR (Suffix)]|
|                                                                                               |
| FIM-Transformation (PSM-Modus):                                                               |
| <PRE> [ Präfix-Tokens ] <SUF> [ Suffix-Tokens ] <MID> ===> Modell prognostiziert [ Middle ]   |
|                                                                                               |
| FIM-Transformation (SPM-Modus):                                                               |
| <SUF> [ Suffix-Tokens ] <PRE> [ Präfix-Tokens ] <MID> ===> Modell prognostiziert [ Middle ]   |
+-----------------------------------------------------------------------------------------------+

PSM-Modus vs. SPM-Modus

Beim Pretraining werden Dokumente zufällig in Präfix ($C_p$), Middle ($C_m$) und Suffix ($C_s$) zerlegt. Das Modell lernt anhand zweier Formate:

  1. PSM-Modus (Prefix-Suffix-Middle):
  1. SPM-Modus (Suffix-Prefix-Middle):

Indem 50% der Trainings-Tokens im FIM-Format vorliegen, behält das Modell seine regulären Programmierfähigkeiten und lernt gleichzeitig, Zwischenräume perfekt an zukünftigen Code anzupassen.

+-----------------------------------------------------------------------------------------------+
|                         FIM-Aufmerksamkeitskonditionierung im Transformer                     |
+-----------------------------------------------------------------------------------------------+
|    Kausale untere Dreiecksmaske (Causal Mask)                                                 |
|    Tokens:   <PRE>  pref_1  pref_2  <SUF>  suff_1  suff_2  <MID>  mid_1  mid_2                |
|    PRE         x                                                                              |
|    pref_1      x      x                                                                       |
|    pref_2      x      x       x                                                               |
|    SUF         x      x       x       x                                                       |
|    suff_1      x      x       x       x      x                                                |
|    suff_2      x      x       x       x      x       x                                        |
|    MID         x      x       x       x      x       x       x                                |
|    mid_1       x      x       x       x      x       x       x      x                         |
|    mid_2       x      x       x       x      x       x       x      x      x                  |
|                                                                                               |
|    Ergebnis: Bei der Vorhersage von `mid_1` bezieht sich die Self-Attention                   |
|    gleichzeitig auf das GESAMTE Präfix UND das GESAMTE Suffix!                                |
+-----------------------------------------------------------------------------------------------+

3. Spezielle FIM-Tokens: Qwen, DeepSeek, StarCoder und Codestral

Ein häufiger Fehler bei der Implementierung von IDE-Erweiterungen ist der Token-Mismatch. Verschiedene Modellfamilien nutzen unterschiedliche Sonderzeichen. Werden falsche Token-Strings gesendet, sinkt die Genauigkeit drastisch oder Sonderzeichen landen im Benutzercode.

+-------------------------------------------------------------------------------------------------------------+
|                                  FIM-Spezialtokens im Überblick (2026)                                      |
+--------------------+--------------------------+--------------------------+--------------------------+-------+
| Modellfamilie      | Präfix-Token             | Suffix-Token             | Middle-Token             | Modus |
+--------------------+--------------------------+--------------------------+--------------------------+-------+
| Qwen 2.5 Coder     | <|fim_prefix|>           | <|fim_suffix|>           | <|fim_middle|>           | PSM   |
| DeepSeek Coder V1/2| <|fim begin|>          | <|fim hole|>           | <|fim end|>            | SPM   |
| StarCoder / SC2    | <fim_prefix>             | <fim_suffix>             | <fim_middle>             | PSM   |
| Mistral Codestral  | [PREFIX]                 | [SUFFIX]                 | [MIDDLE]                 | PSM   |
| CodeLlama          | <PRE>                    | <SUF>                    | <MID>                    | PSM   |
+--------------------+--------------------------+--------------------------+--------------------------+-------+

Konkrete Prompt-Beispiele

#### 1. Qwen 2.5 Coder (1.5B / 7B / 32B) Nutzt ein BPE-Vokabular mit 152.064 Tokens und die Standard-PSM-Reihenfolge:

# FIM-Prompt für Qwen 2.5 Coder:
prompt = f"<|fim_prefix|>{prefix_code}<|fim_suffix|>{suffix_code}<|fim_middle|>"

#### 2. DeepSeek Coder (1.3B / 6.7B / V2.5) Nutzt vollbreite Trennzeichen und bevorzugt im Repo-FIM die SPM-Reihenfolge:

# FIM-Prompt für DeepSeek Coder (SPM-Modus):
prompt = f"<|fim begin|>{suffix_code}<|fim hole|>{prefix_code}<|fim end|>"

#### 3. StarCoder2 (3B / 7B / 15B) Standard-Hugging-Face-Format:

# FIM-Prompt für StarCoder2:
prompt = f"<fim_prefix>{prefix_code}<fim_suffix>{suffix_code}<fim_middle>"

#### 4. Mistral Codestral 2501 (22B) Verwendet eckige Markdown-Klammern:

# FIM-Prompt für Codestral:
prompt = f"[PREFIX]{prefix_code}[SUFFIX]{suffix_code}[MIDDLE]"

4. Benchmark-Vergleich: Sub-100ms Ghost-Text-Modelle

Für den Praxistest kompakter Autovervollständigungsmodelle im Jahr 2026 haben wir vier führende Architekturen verglichen:

  1. Qwen 2.5 Coder 1.5B: 1,54 Mrd. Parameter, 32k Kontext, GQA.
  2. Qwen 2.5 Coder 7B: 7,61 Mrd. Parameter, 128k Kontextfenster.
  3. DeepSeek Coder 1.3B: Extrem leichtgewichtiges 1,3B-Modell, trainiert auf 2T Tokens.
  4. StarCoder2 3B: 3-Mrd.-Parameter-Modell für 600+ Sprachen.

Testumgebung

  • Hardware: 1x NVIDIA RTX 4090 (24GB VRAM) und Apple M4 Max (128GB Unified Memory für On-Device-Tests).
  • Inferenz-Engine: vLLM v0.7.3 mit FlashAttention-3 und Chunked Prefill.
  • Datensätze: SantaCoder FIM Benchmark (Einzeilig in Python, JS, Java) und HumanEval-Infill (Mehrzeilig).
  • Parallelität: 10 gleichzeitige IDE-Anfragen.
+---------------------------------------------------------------------------------------------------------------+
|                              BENCHMARK-VERGLEICH DER SUB-100MS GHOST-TEXT-MODELLE                             |
+-----------------------+--------------------+-------------------+------------------+-------------+-------------+
| Modellkandidat        | Einzeilige FIM-    | Mehrzeilige FIM-  | Time-To-First-   | Generierungs| VRAM-Bedarf |
|                       | Genauigkeit(Pass@1)| Genauigkeit(Pass@1| Token (p50 TTFT) | geschw.(tps)| (FP16)      |
+-----------------------+--------------------+-------------------+------------------+-------------+-------------+
| Qwen 2.5 Coder 1.5B   | 84,6%              | 64,2%             | 42 ms            | 188 tok/s   | 3,2 GB      |
| Qwen 2.5 Coder 7B     | 89,2%              | 76,8%             | 84 ms            | 112 tok/s   | 15,2 GB     |
| DeepSeek Coder 1.3B   | 78,4%              | 56,1%             | 39 ms            | 196 tok/s   | 2,8 GB      |
| StarCoder2 3B         | 81,1%              | 60,5%             | 58 ms            | 144 tok/s   | 6,4 GB      |
+-----------------------+--------------------+-------------------+------------------+-------------+-------------+

Wichtigste Erkenntnisse

  • Sieger Einzeilige Vorschläge: Qwen 2.5 Coder 1.5B erreicht hervorragende 84,6% Genauigkeit bei nur 42ms TTFT. Perfekt für Entwickler-Laptops (MacBooks oder RTX-Karten).
  • Sieger Mehrzeilige Blöcke: Bei komplexeren Methodenrümpfen dominiert Qwen 2.5 Coder 7B mit 76,8% Genauigkeit und bleibt mit 84ms sicher unter der 100ms-Grenze.
  • Ressourcensparend: DeepSeek Coder 1.3B liefert die geringste Latenz (39ms) und belegt nur 2,8 GB VRAM.

5. IDE-Erweiterungsentwicklung: Kontextaufbereitung in der Praxis

Ein häufiger Fehler ist das unbedachte Senden der gesamten Datei. Bei 10.000 Zeilen Code überschreitet allein die Tokenisierung das 100ms-Latenzbudget deutlich.

Das asymmetrische Schiebefenster

Produktionserweiterungen (wie Continue.dev) nutzen ein asymmetrisches Fenster:

  • Präfix-Budget: 60% bis 70% des aktiven Kontexts (1.500 bis 3.000 Tokens vor dem Cursor).
  • Suffix-Budget: 30% bis 40% des aktiven Kontexts (500 bis 1.500 Tokens nach dem Cursor).
  • Dateienübergreifende Symbole: Relevante Typen und Imports benachbarter Tabs via Tree-sitter AST (ca. 300 Tokens im Header).

6. Python-Implementierung: Schneller asynchroner FIM-Server

Hier ist ein produktionsbereiter Server auf Basis von FastAPI und vLLM:

import os
import time
from typing import Optional, List
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import httpx

app = FastAPI(title="FIM Ghost-Text Engine", version="2026.1")

BACKEND_URL = os.getenv("INFERENCE_BACKEND_URL", "http://127.0.0.1:8000/v1/completions")
MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-Coder-1.5B")

class FIMRequest(BaseModel):
    prefix: str
    suffix: str
    max_tokens: int = 48
    temperature: float = 0.1
    stop: Optional[List[str]] = None

def format_fim_prompt(model: str, prefix: str, suffix: str) -> tuple[str, list[str]]:
    if "qwen" in model.lower():
        prompt = f"<|fim_prefix|>{prefix}<|fim_suffix|>{suffix}<|fim_middle|>"
        stop = ["<|fim_prefix|>", "<|fim_suffix|>", "<|fim_middle|>", "<|endoftext|>"]
    elif "deepseek" in model.lower():
        prompt = f"<|fim begin|>{suffix}<|fim hole|>{prefix}<|fim end|>"
        stop = ["<|fim begin|>", "<|fim hole|>", "<|fim end|>", "<|end of sentence|>"]
    elif "starcoder" in model.lower():
        prompt = f"<fim_prefix>{prefix}<fim_suffix>{suffix}<fim_middle>"
        stop = ["<fim_prefix>", "<fim_suffix>", "<fim_middle>", "<|endoftext|>"]
    else:
        prompt = f"<fim_prefix>{prefix}<fim_suffix>{suffix}<fim_middle>"
        stop = ["<fim_prefix>", "<fim_suffix>", "<fim_middle>"]
    
    stop.extend(["\n\n", "```"])
    return prompt, stop

@app.post("/v1/autocomplete")
async def autocomplete(req: FIMRequest):
    start_time = time.perf_counter()
    prompt, default_stops = format_fim_prompt(MODEL_NAME, req.prefix, req.suffix)
    active_stops = list(set(default_stops + (req.stop or [])))
    
    payload = {
        "model": MODEL_NAME,
        "prompt": prompt,
        "max_tokens": req.max_tokens,
        "temperature": req.temperature,
        "top_p": 0.95,
        "stop": active_stops,
        "stream": False
    }
    
    async with httpx.AsyncClient(timeout=1.5) as client:
        try:
            resp = await client.post(BACKEND_URL, json=payload)
            resp.raise_for_status()
            data = resp.json()
        except Exception as exc:
            raise HTTPException(status_code=502, detail=f"Inferenz-Fehler: {str(exc)}")
            
    latency_ms = (time.perf_counter() - start_time) * 1000
    completion_text = data["choices"][0]["text"]
    
    return {
        "completion": completion_text,
        "latency_ms": round(latency_ms, 2),
        "model": MODEL_NAME
    }

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8080)

7. Stop-Sequenzen und Vermeidung von Code-Duplikaten

Beim Ghost-Text ist das Anhalten der Generierung essenziell. Fehlen saubere Stop-Sequenzen, generiert das Modell über die Cursor-Grenze hinaus und wiederholt Suffix-Code.

Checkliste:

  1. FIM-Tokens im Stop-Array: Verhindert unkontrolliertes Weiter-Prompting.
  2. Doppelter Zeilenumbruch (\n\n): Stoppt einzeilige Vervollständigungen zuverlässig.
  3. Overlap-Filter: Clientseitiges Entfernen doppelter schließender Klammern } oder ).

8. Kostenanalyse (TCO) & Bereitstellung

Kostenrechnung für ein Entwicklerteam von 100 Ingenieuren:

  • ~1.200 Anfragen/Entwickler/Tag nach Debounce.
  • 100 Entwickler: 120.000 Anfragen/Tag (~2,64 Mio. Anfragen/Monat).
  • Durchschnitt: 800 Input-Tokens + 25 Output-Tokens.
  • Monatliches Volumen: 2,11 Mrd. Input-Tokens und 66 Mio. Output-Tokens.
+---------------------------------------------------------------------------------------------------------------+
|                                  MONATLICHE KOSTEN (100 ENTWICKLER, 2,64M ANFRAGEN)                           |
+-----------------------+-----------------------+-----------------------+---------------------------------------+
| Lösung                | Infrastruktur / Tarif | Monatliche Kosten     | Hinweise                              |
+-----------------------+-----------------------+-----------------------+---------------------------------------+
| Kommerzieller Copilot | $19 / Nutzer / Monat  | $1.900 / Monat        | Feste Modelle, Telemetrie-Risiken.    |
+-----------------------+-----------------------+-----------------------+---------------------------------------+
| Serverless API        | $0,05 / 1M Input      | $115,40 / Monat       | Sehr günstig, abhängig von Latenz.    |
| (DeepInfra / Together)| $0,15 / 1M Output     |                       |                                       |
+-----------------------+-----------------------+-----------------------+---------------------------------------+
| Dedizierte Cloud-GPU  | 1x NVIDIA A10G        | $730,00 / Monat       | Sub-70ms, 100% Datenschutz, fix.      |
| (AWS g5.xlarge / vLLM)| Stündliche Miete      |                       |                                       |
+-----------------------+-----------------------+-----------------------+---------------------------------------+
| Lokaler Rechner       | Apple M4 Mac /        | $0 / Monat            | Null Latenz, maximale Privatsphäre,   |
| (Mac / RTX 4090)      | RTX 4090 GPU          | (Hardwarekosten)      | keine Serverlast.                     |
+-----------------------+-----------------------+-----------------------+---------------------------------------+

9. Fazit und Praxistipps

  1. Für lokale Laptops: Qwen 2.5 Coder 1.5B mit llama.cpp – 42ms Latenz und höchste Privatsphäre.
  2. Für Team-Server: Qwen 2.5 Coder 7B auf einer RTX 4090 – 76,8% Mehrzeilen-Präzision für bis zu 30 parallele Entwickler.
  3. Kontext-Fenster begrenzen: Maximal 2.000 Tokens Präfix und 800 Tokens Suffix.
  4. Modellspezifische Tokens verwenden: Immer native Tags wie <|fim_prefix|> statt reiner Textkonkatenation nutzen.
← Alle Artikel
0 / 4