[{"data":1,"prerenderedAt":666},["ShallowReactive",2],{"search-de":3,"content-de-developer\u002Fdecisions\u002F009-model-routing":4,"surround-de-\u002Fdeveloper\u002Fdecisions\u002F009-model-routing":657},[],{"id":5,"title":6,"body":7,"description":650,"extension":651,"meta":652,"navigation":323,"path":653,"seo":654,"stem":655,"__hash__":656},"content_de\u002F3.developer\u002Fdecisions\u002F10.009-model-routing.md","ADR-009: Aufgabenbasiertes Modell-Routing",{"type":8,"value":9,"toc":645},"minimark",[10,27,32,36,139,201,204,207,228,232,241,251,258,483,503,518,552,596,600,605,622,627,641],[11,12,13,21],"ul",{},[14,15,16,20],"li",{},[17,18,19],"strong",{},"Status:"," Angenommen",[14,22,23,26],{},[17,24,25],{},"Datum:"," 2026-03-24",[28,29,31],"h2",{"id":30},"kontext","Kontext",[33,34,35],"p",{},"Die Agent-Pipeline, der Knowledge Graph und das Dateisystem führen allesamt LLM-Aufrufe durch — allerdings mit unterschiedlichen Anforderungen:",[37,38,39,55],"table",{},[40,41,42],"thead",{},[43,44,45,49,52],"tr",{},[46,47,48],"th",{},"Aufgabe",[46,50,51],{},"Anforderungen",[46,53,54],{},"Volumen",[56,57,58,70,80,91,102,117,128],"tbody",{},[43,59,60,64,67],{},[61,62,63],"td",{},"Klassifikation",[61,65,66],{},"Geschwindigkeit, strukturierte Ausgabe",[61,68,69],{},"Jede eingehende Nachricht",[43,71,72,75,78],{},[61,73,74],{},"Sicherheitsfilter",[61,76,77],{},"Geschwindigkeit, geringe Latenz",[61,79,69],{},[43,81,82,85,88],{},[61,83,84],{},"Wissensextraktion",[61,86,87],{},"Strukturierte Ausgabe, Kosteneffizienz",[61,89,90],{},"Jede verarbeitete Nachricht",[43,92,93,96,99],{},[61,94,95],{},"Datei-Tagging",[61,97,98],{},"Zusammenfassung, Kosteneffizienz",[61,100,101],{},"Jede hochgeladene Datei",[43,103,104,111,114],{},[61,105,106,107],{},"Handlungsplanung ",[108,109,110],"em",{},"(geplant)",[61,112,113],{},"Reasoning, Tool-Nutzung",[61,115,116],{},"Pro Nachricht (nach der Klassifikation)",[43,118,119,122,125],{},[61,120,121],{},"Entwurfserstellung",[61,123,124],{},"Schreibqualität, Tonalitätstreue",[61,126,127],{},"Pro Nachricht (wenn ein Entwurf benötigt wird)",[43,129,130,133,136],{},[61,131,132],{},"Kontextverdichtung",[61,134,135],{},"Zusammenfassung",[61,137,138],{},"Wenn der Kontext das Budget überschreitet",[140,141,144],"callout",{"title":142,"type":143},"Verdrahtete vs. reservierte Tiers","info",[33,145,146,147,151,152,155,156,159,160,163,164,163,167,163,170,173,174,177,178,180,181,184,185,163,188,163,191,173,194,197,198,200],{},"Die Aufgabe ",[148,149,150],"code",{},"plan"," (Handlungsplanung) ist im Typ definiert und in ",[148,153,154],{},"taskTier()"," einem Tier zugeordnet, hat aber ",[17,157,158],{},"noch keine Aufrufstellen",". Verdrahtet sind die Aufgaben ",[148,161,162],{},"classify",", ",[148,165,166],{},"draft",[148,168,169],{},"extract",[148,171,172],{},"summarize"," und ",[148,175,176],{},"guard"," — ",[148,179,176],{}," läuft produktiv in ",[148,182,183],{},"apps\u002Fapi\u002Fconvex\u002Fagent\u002Fsteps\u002Fsecurity_scan\u002Findex.ts:88",", die übrigen werden in ",[148,186,187],{},"apps\u002Fapi\u002Fconvex\u002Fagent\u002Fsteps\u002F",[148,189,190],{},"knowledge\u002Fextraction.ts",[148,192,193],{},"semanticFileProcessing.ts",[148,195,196],{},"translate.ts"," aufgerufen. Die Zeile ",[148,199,150],{}," beschreibt die beabsichtigte Nutzung, nicht das aktuelle Verhalten.",[33,202,203],{},"Alles auf dem leistungsfähigsten Modell laufen zu lassen (GPT-4o, Claude Sonnet) verschwendet Ressourcen — die Klassifikation liefert ein einfaches Enum und braucht nicht die Reasoning-Kapazität eines Frontier-Modells. Alles auf dem günstigsten Modell laufen zu lassen (GPT-4o-mini, Llama 3) spart Geld, erzeugt aber Entwürfe von schlechter Qualität.",[33,205,206],{},"Die betrachteten Optionen:",[208,209,210,216,222],"ol",{},[14,211,212,215],{},[17,213,214],{},"Ein einziges Modell"," — die einfachste Konfiguration, erzwingt aber eine Wahl zwischen Qualität und Kosten",[14,217,218,221],{},[17,219,220],{},"Modellkonfiguration pro Schritt"," — fünf separate Modell-Umgebungsvariablen, aufwendig zu konfigurieren",[14,223,224,227],{},[17,225,226],{},"Zweistufiges Routing"," — schnelles Modell für strukturierte Aufgaben mit hohem Volumen, leistungsfähiges Modell für Reasoning und Textproduktion",[28,229,231],{"id":230},"entscheidung","Entscheidung",[33,233,234,235,240],{},"Den ",[236,237,239],"a",{"href":238},"\u002Fdeveloper\u002Fdecisions\u002F007-pluggable-llm","austauschbaren LLM-Provider"," um ein zweistufiges Modell-Routing erweitern:",[242,243,248],"pre",{"className":244,"code":246,"language":247},[245],"language-text","LLM_MODEL=gpt-4o              # Fallback for all tasks\nLLM_MODEL_CAPABLE=gpt-4o      # Drafting, planning, reasoning\nLLM_MODEL_FAST=gpt-4o-mini    # Classification, extraction, summarization\n","text",[148,249,246],{"__ignoreMap":250},"",[33,252,253,254,257],{},"Aufrufer fordern ein Modell über ",[148,255,256],{},"task"," an, und die Routing-Regel ordnet jede Aufgabe dem Fast- oder dem Capable-Tier zu:",[242,259,263],{"className":260,"code":261,"language":262,"meta":250,"style":250},"language-typescript shiki shiki-themes github-light github-dark-dimmed","\u002F\u002F apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts (illustrative — actual tier rule)\ntype LLMTask = 'classify' | 'extract' | 'guard' | 'summarize' | 'draft' | 'plan'\n\nfunction taskTier(task: LLMTask): 'fast' | 'capable' {\n  \u002F\u002F classify \u002F extract \u002F guard \u002F summarize → fast\n  \u002F\u002F draft \u002F plan → capable\n}\n\n\u002F\u002F OpenAI-compatible provider resolves the model ID for the tier\n\u002F\u002F (env reads go through the typed `getOptional()` wrapper, not raw process.env):\nconst modelId = tier === 'fast'\n  ? getOptional('LLM_MODEL_FAST') ?? getOptional('LLM_MODEL') ?? 'gpt-4o-mini'\n  : getOptional('LLM_MODEL_CAPABLE') ?? getOptional('LLM_MODEL') ?? 'gpt-4o'\n","typescript",[148,264,265,274,318,325,363,369,375,381,386,392,398,419,453],{"__ignoreMap":250},[266,267,270],"span",{"class":268,"line":269},"line",1,[266,271,273],{"class":272},"sDN9O","\u002F\u002F apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts (illustrative — actual tier rule)\n",[266,275,277,281,285,288,292,295,298,300,303,305,308,310,313,315],{"class":268,"line":276},2,[266,278,280],{"class":279},"s7YZ4","type",[266,282,284],{"class":283},"sOLd2"," LLMTask",[266,286,287],{"class":279}," =",[266,289,291],{"class":290},"s-HuK"," 'classify'",[266,293,294],{"class":279}," |",[266,296,297],{"class":290}," 'extract'",[266,299,294],{"class":279},[266,301,302],{"class":290}," 'guard'",[266,304,294],{"class":279},[266,306,307],{"class":290}," 'summarize'",[266,309,294],{"class":279},[266,311,312],{"class":290}," 'draft'",[266,314,294],{"class":279},[266,316,317],{"class":290}," 'plan'\n",[266,319,321],{"class":268,"line":320},3,[266,322,324],{"emptyLinePlaceholder":323},true,"\n",[266,326,328,331,335,339,342,345,347,350,352,355,357,360],{"class":268,"line":327},4,[266,329,330],{"class":279},"function",[266,332,334],{"class":333},"sPO5f"," taskTier",[266,336,338],{"class":337},"sYgZi","(",[266,340,256],{"class":341},"stnAF",[266,343,344],{"class":279},":",[266,346,284],{"class":283},[266,348,349],{"class":337},")",[266,351,344],{"class":279},[266,353,354],{"class":290}," 'fast'",[266,356,294],{"class":279},[266,358,359],{"class":290}," 'capable'",[266,361,362],{"class":337}," {\n",[266,364,366],{"class":268,"line":365},5,[266,367,368],{"class":272},"  \u002F\u002F classify \u002F extract \u002F guard \u002F summarize → fast\n",[266,370,372],{"class":268,"line":371},6,[266,373,374],{"class":272},"  \u002F\u002F draft \u002F plan → capable\n",[266,376,378],{"class":268,"line":377},7,[266,379,380],{"class":337},"}\n",[266,382,384],{"class":268,"line":383},8,[266,385,324],{"emptyLinePlaceholder":323},[266,387,389],{"class":268,"line":388},9,[266,390,391],{"class":272},"\u002F\u002F OpenAI-compatible provider resolves the model ID for the tier\n",[266,393,395],{"class":268,"line":394},10,[266,396,397],{"class":272},"\u002F\u002F (env reads go through the typed `getOptional()` wrapper, not raw process.env):\n",[266,399,401,404,408,410,413,416],{"class":268,"line":400},11,[266,402,403],{"class":279},"const",[266,405,407],{"class":406},"sviXB"," modelId",[266,409,287],{"class":279},[266,411,412],{"class":337}," tier ",[266,414,415],{"class":279},"===",[266,417,418],{"class":290}," 'fast'\n",[266,420,422,425,428,430,433,436,439,441,443,446,448,450],{"class":268,"line":421},12,[266,423,424],{"class":279},"  ?",[266,426,427],{"class":333}," getOptional",[266,429,338],{"class":337},[266,431,432],{"class":290},"'LLM_MODEL_FAST'",[266,434,435],{"class":337},") ",[266,437,438],{"class":279},"??",[266,440,427],{"class":333},[266,442,338],{"class":337},[266,444,445],{"class":290},"'LLM_MODEL'",[266,447,435],{"class":337},[266,449,438],{"class":279},[266,451,452],{"class":290}," 'gpt-4o-mini'\n",[266,454,456,459,461,463,466,468,470,472,474,476,478,480],{"class":268,"line":455},13,[266,457,458],{"class":279},"  :",[266,460,427],{"class":333},[266,462,338],{"class":337},[266,464,465],{"class":290},"'LLM_MODEL_CAPABLE'",[266,467,435],{"class":337},[266,469,438],{"class":279},[266,471,427],{"class":333},[266,473,338],{"class":337},[266,475,445],{"class":290},[266,477,435],{"class":337},[266,479,438],{"class":279},[266,481,482],{"class":290}," 'gpt-4o'\n",[33,484,485,488,489,492,493,495,496,499,500,502],{},[148,486,487],{},"getLLMProvider(task)"," in ",[148,490,491],{},"apps\u002Fapi\u002Fconvex\u002Flib\u002FllmProvider.ts"," ist die Implementierung, die Aufrufer verwenden; die Fast\u002FCapable-Entscheidung liegt in der privaten Funktion ",[148,494,154],{}," in derselben Datei. Die Fallback-Semantik der Umgebungsvariablen steckt in ",[148,497,498],{},"modelIdForTier()",", ebenfalls in ",[148,501,491],{},".",[33,504,505,506,509,510,513,514,517],{},"Sowohl ",[148,507,508],{},"LLM_MODEL_CAPABLE"," als auch ",[148,511,512],{},"LLM_MODEL_FAST"," fallen auf ",[148,515,516],{},"LLM_MODEL"," zurück, was bedeutet:",[11,519,520,529,542],{},[14,521,522,525,526,528],{},[17,523,524],{},"Minimale Konfiguration:"," Setzen Sie nur ",[148,527,516],{},", und alles verwendet dieses Modell (dasselbe Verhalten wie in ADR-007)",[14,530,531,534,535,173,538,541],{},[17,532,533],{},"Kostenoptimierung:"," Setzen Sie ",[148,536,537],{},"LLM_MODEL_FAST=gpt-4o-mini",[148,539,540],{},"LLM_MODEL_CAPABLE=gpt-4o",", um nach Aufgabe zu trennen",[14,543,544,547,548,551],{},[17,545,546],{},"Einfachheit beim Self-Hosting:"," Self-Hoster, die ein einzelnes Ollama-Modell betreiben, setzen ",[148,549,550],{},"LLM_MODEL=llama3",", und beide Tiers verwenden es",[33,553,554,555,558,559,562,563,566,567,163,570,573,574,577,578,580,581,583,584,587,588,558,590,592,593,502],{},"Die oben genannten Modell-IDs (",[148,556,557],{},"gpt-4o-mini"," \u002F ",[148,560,561],{},"gpt-4o",") sind die Standardwerte des OpenAI-kompatiblen Clients. ",[148,564,565],{},"LLM_PROVIDER"," (",[148,568,569],{},"openai",[148,571,572],{},"openrouter"," oder ",[148,575,576],{},"ollama",") wählt die Basis-URL; jeder Client ist OpenAI-kompatibel. Um Claude zu verwenden, belassen Sie ",[148,579,565],{}," auf dem Standardwert ",[148,582,569],{},", richten ",[148,585,586],{},"LLM_BASE_URL"," auf einen OpenAI-kompatiblen Anthropic-Proxy und setzen anschließend ",[148,589,512],{},[148,591,508],{}," auf die gewünschten Claude-Modell-IDs. Zur Provider-Schicht siehe ",[236,594,595],{"href":238},"ADR-007",[28,597,599],{"id":598},"konsequenzen","Konsequenzen",[33,601,602],{},[17,603,604],{},"Ermöglicht:",[11,606,607,610,613,616,619],{},[14,608,609],{},"Kostensenkung um 60–80 % bei Aufgaben mit hohem Volumen (Klassifikation, Extraktion) durch den Einsatz kleinerer Modelle",[14,611,612],{},"Qualitätserhalt bei Aufgaben, die ihn brauchen (Entwurfserstellung, Planung), durch den Einsatz leistungsfähiger Modelle",[14,614,615],{},"Einfacher Upgrade-Pfad — mit einem Modell starten und später bei wachsendem Volumen aufteilen",[14,617,618],{},"Self-Hoster können ein einzelnes Modell ohne jegliche Routing-Konfiguration betreiben",[14,620,621],{},"Kostenverfolgung pro Schritt wird aussagekräftig (Aufrufe des schnellen Modells sind günstig, Aufrufe des leistungsfähigen Modells sind der eigentliche Kostentreiber)",[33,623,624],{},[17,625,626],{},"Trade-offs:",[11,628,629,632,635,638],{},[14,630,631],{},"Zwei zusätzliche Umgebungsvariablen, die dokumentiert und unterstützt werden müssen",[14,633,634],{},"Annahmen zur Modellkompatibilität — das Verhalten bei strukturierter Ausgabe unterscheidet sich je Modell und erfordert unter Umständen modellspezifische Prompt-Anpassungen",[14,636,637],{},"Self-Hoster, die ein einzelnes kleines Modell verwenden, werden bei Entwurfsaufgaben Qualitätseinbußen sehen (derselbe Trade-off wie bereits in ADR-007)",[14,639,640],{},"Noch keine Modellüberschreibung pro Organisation — das Routing gilt systemweit und ist nicht mandantenspezifisch konfigurierbar",[642,643,644],"style",{},"html pre.shiki code .sDN9O, html code.shiki .sDN9O{--shiki-default:#6A737D;--shiki-dark:#768390}html pre.shiki code .s7YZ4, html code.shiki .s7YZ4{--shiki-default:#D73A49;--shiki-dark:#F47067}html pre.shiki code .sOLd2, html code.shiki .sOLd2{--shiki-default:#6F42C1;--shiki-dark:#F69D50}html pre.shiki code .s-HuK, html code.shiki .s-HuK{--shiki-default:#032F62;--shiki-dark:#96D0FF}html pre.shiki code .sPO5f, html code.shiki .sPO5f{--shiki-default:#6F42C1;--shiki-dark:#DCBDFB}html pre.shiki code .sYgZi, html code.shiki .sYgZi{--shiki-default:#24292E;--shiki-dark:#ADBAC7}html pre.shiki code .stnAF, html code.shiki .stnAF{--shiki-default:#E36209;--shiki-dark:#F69D50}html pre.shiki code .sviXB, html code.shiki .sviXB{--shiki-default:#005CC5;--shiki-dark:#6CB6FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":250,"searchDepth":276,"depth":276,"links":646},[647,648,649],{"id":30,"depth":276,"text":31},{"id":230,"depth":276,"text":231},{"id":598,"depth":276,"text":599},"Warum Owlat eine LLM-Modellauswahl pro Aufgabe unterstützt, statt ein einziges Modell für alle Pipeline-Schritte zu verwenden.","md",{},"\u002Fdeveloper\u002Fdecisions\u002F009-model-routing",{"title":6,"description":650},"3.developer\u002Fdecisions\u002F10.009-model-routing","Z7fpZ_3gxrO4ywdeflxCCT1T1ceX2dQmrhu03JRSN1I",[658,662],{"title":659,"path":660,"stem":661,"children":-1},"Architecture Decision Records","\u002Fdeveloper\u002Fdecisions","3.developer\u002Fdecisions\u002F1.index",{"title":663,"path":664,"stem":665,"children":-1},"ADR-010: Listing-Engine","\u002Fdeveloper\u002Fdecisions\u002F010-listing-engine","3.developer\u002Fdecisions\u002F11.010-listing-engine",1786915110866]