Skip to content

Commit 14dce2f

Browse files
abossardCopilot
andcommitted
Move task picker from NB02 to NB03, expand domain tuning
NB02: removed 'Beliebige Aufgabe' section (belongs in NB03) NB03: 6-step arc with task catalog + domain tuning: 1. See all 20 tasks 2. Pick any task and optimize it 3. Load real ticket data 4. Run generic prompt → mediocre 5. Tune with domain data → much better 6. Takeaway: your data is your moat Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent f049276 commit 14dce2f

2 files changed

Lines changed: 108 additions & 123 deletions

File tree

notebooks/02_optimization.ipynb

Lines changed: 15 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -365,9 +365,12 @@
365365
" print(f\" Schwierigkeit: [{bar}] {normalized:.0%}\")\n",
366366
" print(f\" Verteilung: {dict(counter.most_common(5))}\")\n",
367367
"\n",
368+
"n_single = sum(1 for c in groups.values() if c == 1)\n",
369+
"n_groups = len(groups)\n",
370+
"top_cat = cats.most_common(1)[0]\n",
368371
"print(f\"\\n💡 Entropy misst die 'Zufälligkeit' der Labels.\")\n",
369-
"print(f\" Category hat nur 3 Werte, 66% davon 'Service Request' → leicht zu raten.\")\n",
370-
"print(f\" Assigned Group hat 21 Werte, 12 davon mit nur 1 Beispiel → fast unmöglich.\")\n",
372+
"print(f\" Category hat nur 3 Werte, {top_cat[1]}/{sum(cats.values())} davon '{top_cat[0]}' → relativ leicht.\")\n",
373+
"print(f\" Assigned Group hat {n_groups} Werte, {n_single} davon mit nur 1 Beispiel → Schwierigkeit {entropy(groups)/max_entropy(n_groups):.0%}.\")\n",
371374
"\n",
372375
"# ━━━ 3. Self-Consistency: Wie stabil sind die Vorhersagen? ━━━\n",
373376
"print(f\"\\n{'━'*60}\")\n",
@@ -421,10 +424,13 @@
421424
"\n",
422425
"| Beobachtung | Was es bedeutet |\n",
423426
"|---|---|\n",
424-
"| **12 von 21 Gruppen haben nur 1 Trainings-Beispiel** | Das Modell kann keine Zuordnungsregel lernen — es hat kein zweites Beispiel zum Vergleichen |\n",
425-
"| **Category-Entropy ist niedrig** (66% = Service Request) | Einfach zu raten, aber nicht informativ |\n",
426-
"| **Group-Entropy ist fast maximal** | 21 Klassen, extrem ungleich verteilt — das härteste Feld |\n",
427-
"| **Self-Consistency ist niedrig bei schweren Tickets** | Das Modell gibt bei mehrfachem Fragen verschiedene Antworten — es \"rät\" |\n",
427+
"| **Die Daten sind relativ gut verteilt** (18 von 20 Gruppen haben 4 Beispiele) | Kein 1-Beispiel-Problem — aber trotzdem nicht genug für 20 Klassen |\n",
428+
"| **Category-Entropy bei 76%** (58% = Service Request, 36% = Failure) | Ungleich verteilt, aber nicht trivial — \"Event NO Customer Impact\" hat nur 4 Beispiele |\n",
429+
"| **Priority-Entropy bei 71%** (69% = Standard) | \"Standard\" dominiert — \"High\" hat nur 5 von 77 Beispielen |\n",
430+
"| **Group-Entropy bei 98%** — fast maximal | 20 Klassen, fast gleichverteilt — das härteste Feld, weil das Modell zwischen 20 ähnlichen Optionen wählen muss |\n",
431+
"| **Self-Consistency ist hoch** (3/3 Übereinstimmung) | Das Modell ist sich **sicher** — aber sicher **falsch**: es erfindet eigene Kategorien wie \"Access\" oder \"Hardware\" statt die gültigen Werte zu nutzen |\n",
432+
"\n",
433+
"**Das zentrale Problem:** Das Modell antwortet konsistent, aber mit **eigenen Kategorien** (\"Access\", \"Hardware\", \"Workplace Support / ...\") statt den vorgegebenen Werten (\"Service Request\", \"Failure\", \"SDE - Service Desk\", ...). Es hat eine klare interne Logik — aber die stimmt nicht mit den Domain-spezifischen Labels überein.\n",
428434
"\n",
429435
"**Techniken aus der Forschung:**\n",
430436
"\n",
@@ -433,87 +439,7 @@
433439
"3. **Calibration** — Messen ob ein Modell das 90% Confidence zuweist auch wirklich zu 90% richtig liegt. LLMs sind typischerweise *overconfident*.\n",
434440
"4. **SelfCheckGPT** — Mehrere Antworten generieren und prüfen ob sie sich widersprechen. Widersprüche = Halluzination/Unsicherheit.\n",
435441
"\n",
436-
"> **Die Kernfrage ist immer:** Wann kann ich dem Modell vertrauen? Die Antwort: Wenn es bei mehrfachem Fragen die gleiche Antwort gibt, und wenn es genug Trainingsbeispiele für diese Klasse gibt."
437-
]
438-
},
439-
{
440-
"cell_type": "markdown",
441-
"metadata": {},
442-
"source": [
443-
"## 🎯 Beliebige Aufgabe optimieren\n",
444-
"\n",
445-
"Wähl eine Aufgabe aus der Liste unten und sieh dir an, was der Optimizer daraus macht. Jede Aufgabe hat eine andere Schwierigkeit — und der Optimizer zeigt dir genau, was er am Prompt ändert.\n",
446-
"\n",
447-
"**So geht's:** Ändere den `TASK`-String und führe die Zelle aus. Der Prompt-Diff zeigt dir den Unterschied."
448-
]
449-
},
450-
{
451-
"cell_type": "code",
452-
"execution_count": null,
453-
"metadata": {},
454-
"outputs": [],
455-
"source": [
456-
"# ━━━ Verfügbare Aufgaben ━━━\n",
457-
"from dspy_tasks.tasks import list_tasks\n",
458-
"from dspy_tasks.actions import run_with_prompt\n",
459-
"\n",
460-
"print(\"Alle verfügbaren Aufgaben:\\n\")\n",
461-
"for tier in [1, 2, 3, 4]:\n",
462-
" tasks_in_tier = [t for t in list_tasks() if t.tier == tier]\n",
463-
" tier_labels = {1: \"Basics\", 2: \"Reasoning\", 3: \"Composition\", 4: \"Agentic\"}\n",
464-
" print(f\" Tier {tier} — {tier_labels[tier]}:\")\n",
465-
" for t in tasks_in_tier:\n",
466-
" examples = t.load_examples()[:1]\n",
467-
" sample_input = \"\"\n",
468-
" if examples:\n",
469-
" sample_input = \" | \".join(f\"{k}={str(v)[:40]}\" for k, v in examples[0].inputs().items())\n",
470-
" print(f\" • {t.id:25s} {t.name}\")\n",
471-
" print(f\" {t.description[:80]}\")\n",
472-
" if sample_input:\n",
473-
" print(f\" Beispiel: {sample_input[:100]}\")\n",
474-
" print()\n",
475-
"\n",
476-
"# ━━━ Aufgabe 1: Sentiment (BootstrapFewShot) ━━━\n",
477-
"SENT_PROMPT = \"Bestimme ob diese Bewertung positiv, negativ oder neutral ist.\"\n",
478-
"print(\"\" * 60)\n",
479-
"print(\"📋 Sentiment Classification — BootstrapFewShot\\n\")\n",
480-
"\n",
481-
"manual_sent = run_with_prompt(\"sentiment\", SENT_PROMPT, max_eval=8)\n",
482-
"display_score(\"Sentiment: Manueller Prompt\", manual_sent.score)\n",
483-
"display_results_table(manual_sent.individual_scores)\n",
484-
"\n",
485-
"print(\"\\n⏳ Optimiere mit BootstrapFewShot (basierend auf manuellem Prompt)...\")\n",
486-
"r_sent = run_optimization(\"sentiment\", \"BootstrapFewShot\", max_eval=8, instructions=SENT_PROMPT)\n",
487-
"display_score(\"Sentiment: Nach BootstrapFewShot\", r_sent.optimized_score)\n",
488-
"if r_sent.optimized_individual_scores:\n",
489-
" display_results_table(r_sent.optimized_individual_scores)\n",
490-
"display_improvement(manual_sent.score, r_sent.optimized_score)\n",
491-
"\n",
492-
"# ━━━ Aufgabe 2: Multi-Hop QA (BootstrapFewShot → MIPROv2) ━━━\n",
493-
"MHQA_PROMPT = \"Beantworte die Frage basierend auf dem Kontext. Denke Schritt für Schritt.\"\n",
494-
"print(f\"\\n{'━'*60}\")\n",
495-
"print(\"📋 Multi-Hop QA — BootstrapFewShot → MIPROv2\\n\")\n",
496-
"\n",
497-
"manual_mhqa = run_with_prompt(\"multihop_qa\", MHQA_PROMPT, max_eval=8)\n",
498-
"display_score(\"Multi-Hop QA: Manueller Prompt\", manual_mhqa.score)\n",
499-
"display_results_table(manual_mhqa.individual_scores)\n",
500-
"\n",
501-
"# Erst BootstrapFewShot\n",
502-
"print(\"\\n⏳ Schritt 1: BootstrapFewShot...\")\n",
503-
"r_mhqa_bs = run_optimization(\"multihop_qa\", \"BootstrapFewShot\", max_eval=8, instructions=MHQA_PROMPT)\n",
504-
"display_score(\"Multi-Hop QA: Nach BootstrapFewShot\", r_mhqa_bs.optimized_score)\n",
505-
"\n",
506-
"# Dann MIPROv2 auf dem besseren Ergebnis\n",
507-
"best_mhqa_score = max(manual_mhqa.score, r_mhqa_bs.optimized_score)\n",
508-
"best_mhqa_prompt = MHQA_PROMPT if manual_mhqa.score >= r_mhqa_bs.optimized_score else r_mhqa_bs.prompt_after\n",
509-
"best_mhqa_label = \"Manuell\" if manual_mhqa.score >= r_mhqa_bs.optimized_score else \"BootstrapFewShot\"\n",
510-
"print(f\"\\n⏳ Schritt 2: MIPROv2 (basierend auf {best_mhqa_label}: {best_mhqa_score:.0%})...\")\n",
511-
"r_mhqa = run_optimization(\"multihop_qa\", \"MIPROv2\", max_eval=8, instructions=best_mhqa_prompt)\n",
512-
"display_score(\"Multi-Hop QA: Nach MIPROv2\", r_mhqa.optimized_score)\n",
513-
"if r_mhqa.optimized_individual_scores:\n",
514-
" display_results_table(r_mhqa.optimized_individual_scores)\n",
515-
"\n",
516-
"print(f\"\\n📊 Manuell {manual_mhqa.score:.0%} → BootstrapFewShot {r_mhqa_bs.optimized_score:.0%} → MIPROv2 {r_mhqa.optimized_score:.0%}\")"
442+
"> **Die Kernfrage ist immer:** Wann kann ich dem Modell vertrauen? Die Antwort: Hohe Self-Consistency allein reicht nicht — das Modell kann konsistent falsch liegen. Es braucht **Trainingsdaten mit den richtigen Labels**, damit der Optimizer dem Modell beibringen kann, welche Domain-spezifischen Werte erwartet werden."
517443
]
518444
},
519445
{
@@ -541,9 +467,9 @@
541467
"source": [
542468
"## ⏭️ Weiter geht's!\n",
543469
"\n",
544-
"Der Optimizer hat den Prompt verbessert — automatisch, messbar, reproduzierbar. Aber was passiert, wenn du **deine eigenen, echten Daten** benutzt?\n",
470+
"Du hast gesehen: der Optimizer findet bessere Prompts als du — automatisch, messbar, reproduzierbar.\n",
545471
"\n",
546-
"Im nächsten Notebook nehmen wir die echten Ticket-Daten aus dem Projekt und zeigen: **Deine Daten sind dein Burggraben.** Ein generisches Modell + deine Domain-Daten + Tuning = etwas, das kein Konkurrent kopieren kann.\n",
472+
"Im nächsten Notebook testen wir das auf **verschiedenen Aufgaben** und mit **deinen echten Daten**. Dort zeigt sich der wahre Wert: **Domain-Tuning = Burggraben.**\n",
547473
"\n",
548474
"👉 **[Weiter zu Notebook: Domain-Tuning →](03_domain_tuning.ipynb)**\n"
549475
]

notebooks/03_domain_tuning.ipynb

Lines changed: 93 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
"\n",
1010
"GPT-4o, Claude, Gemini — jeder kann diese Modelle nutzen. **Das Modell ist gemietet.** Was dich von der Konkurrenz unterscheidet, sind **deine Daten**.\n",
1111
"\n",
12-
"In diesem Notebook siehst du den Unterschied:\n",
13-
"1. Echte Ticket-Daten aus dem Projekt laden\n",
14-
"2. Einen generischen Prompt darauf loslassen → mässiges Ergebnis\n",
15-
"3. Mit deinen Daten tunen → deutlich besseres Ergebnis\n",
12+
"In diesem Notebook:\n",
13+
"1. Optimierung auf **verschiedenen Aufgaben** ausprobieren\n",
14+
"2. Echte Ticket-Daten laden → generischer Prompt → mässiges Ergebnis\n",
15+
"3. Mit deinen Daten tunen → deutlich besser\n",
1616
"4. Verstehen: **Domain-Wissen = Wettbewerbsvorteil**\n"
1717
]
1818
},
@@ -30,7 +30,7 @@
3030
"import sys; sys.path.insert(0, \".\")\n",
3131
"import dspy, pandas as pd\n",
3232
"from dspy_tasks.config import configure_dspy\n",
33-
"from dspy_tasks.tasks import get_task\n",
33+
"from dspy_tasks.tasks import get_task, list_tasks\n",
3434
"from dspy_tasks.actions import run_baseline, run_optimization\n",
3535
"from dspy_tasks.visualize import *\n",
3636
"\n",
@@ -43,21 +43,83 @@
4343
"id": "2",
4444
"metadata": {},
4545
"source": [
46-
"## 1. Deine echten Daten\n",
46+
"## 1. Welche Aufgaben gibt es?\n",
47+
"\n",
48+
"Wir haben 20 verschiedene Aufgaben in 4 Schwierigkeitsstufen. Jede hat ein eigenes Dataset und eine Metrik. Hier eine Übersicht:\n"
49+
]
50+
},
51+
{
52+
"cell_type": "code",
53+
"execution_count": null,
54+
"id": "3",
55+
"metadata": {},
56+
"outputs": [],
57+
"source": [
58+
"tier_labels = {1: \"Basics\", 2: \"Reasoning\", 3: \"Composition\", 4: \"Agentic\"}\n",
59+
"\n",
60+
"for tier in [1, 2, 3, 4]:\n",
61+
" tasks_in_tier = [t for t in list_tasks() if t.tier == tier]\n",
62+
" print(f\"{'★' * tier}{'☆' * (4-tier)} Tier {tier} — {tier_labels[tier]}\")\n",
63+
" for t in tasks_in_tier:\n",
64+
" print(f\" • {t.id:25s} {t.name}\")\n",
65+
" print()\n"
66+
]
67+
},
68+
{
69+
"cell_type": "markdown",
70+
"id": "4",
71+
"metadata": {},
72+
"source": [
73+
"## 2. Optimierung auf einer beliebigen Aufgabe\n",
74+
"\n",
75+
"Lass uns eine Aufgabe wählen und schauen, was der Optimizer daraus macht. Ändere den `TASK`-String und führe die Zelle erneut aus, um andere Aufgaben zu testen.\n"
76+
]
77+
},
78+
{
79+
"cell_type": "code",
80+
"execution_count": null,
81+
"id": "5",
82+
"metadata": {},
83+
"outputs": [],
84+
"source": [
85+
"# Ändere den Task hier (z.B. \"sentiment\", \"math_word\", \"fact_verification\")\n",
86+
"TASK = \"sentiment\"\n",
87+
"\n",
88+
"task = get_task(TASK)\n",
89+
"print(f\"📋 {task.name}\")\n",
90+
"print(f\" {task.description}\")\n",
91+
"print(f\" Schwierigkeit: {task.difficulty}\")\n",
92+
"print()\n",
93+
"\n",
94+
"print(f\"⏳ Optimiere {task.name}...\\n\")\n",
95+
"result = run_optimization(TASK, \"BootstrapFewShot\", max_eval=8)\n",
96+
"\n",
97+
"display_improvement(result.baseline_score, result.optimized_score)\n",
98+
"display_prompt_diff(result.prompt_before, result.prompt_after)\n"
99+
]
100+
},
101+
{
102+
"cell_type": "markdown",
103+
"id": "6",
104+
"metadata": {},
105+
"source": [
106+
"## 3. Jetzt mit deinen echten Daten\n",
107+
"\n",
108+
"Generische Aufgaben wie Sentiment oder Mathe sind nett — aber der **echte Wert** zeigt sich bei **deinen eigenen Daten**.\n",
47109
"\n",
48110
"Wir laden echte Support-Tickets aus dem Projekt. Jedes Ticket hat:\n",
49-
"- Eine **Zusammenfassung** (was ist das Problem?)\n",
50-
"- Eine **Kategorie** (Network, Software, Hardware, ...)\n",
51-
"- Eine **Priorität** (Critical, High, Medium, Low)\n",
52-
"- Ein **zuständiges Team** (welches Team kümmert sich?)\n",
111+
"- **Zusammenfassung** was ist das Problem?\n",
112+
"- **Kategorie** Network, Software, Hardware, ...\n",
113+
"- **Priorität** Critical, High, Medium, Low\n",
114+
"- **Team** welches Team kümmert sich?\n",
53115
"\n",
54-
"Das Ziel: das Modell soll neue Tickets automatisch in die richtige Kategorie einsortieren, die Priorität setzen und das richtige Team zuweisen.\n"
116+
"Das Modell soll neue Tickets automatisch klassifizieren.\n"
55117
]
56118
},
57119
{
58120
"cell_type": "code",
59121
"execution_count": null,
60-
"id": "3",
122+
"id": "7",
61123
"metadata": {},
62124
"outputs": [],
63125
"source": [
@@ -73,53 +135,50 @@
73135
},
74136
{
75137
"cell_type": "markdown",
76-
"id": "4",
138+
"id": "8",
77139
"metadata": {},
78140
"source": [
79-
"## 2. Generischer Prompt → wie gut ist er?\n",
141+
"## 4. Generischer Prompt → wie gut ist er?\n",
80142
"\n",
81-
"Jetzt lassen wir das Modell diese Tickets klassifizieren — **ohne** Beispiele, **ohne** Domain-Wissen. Nur ein generischer Prompt: \"Klassifiziere dieses Ticket.\"\n",
143+
"Erst testen wir ohne Tuning — nur ein generischer Prompt: \"Klassifiziere dieses Ticket.\"\n",
82144
"\n",
83145
"Die Metrik bewertet drei Felder gleichzeitig:\n",
84-
"- Priorität korrekt? → 40% Gewicht\n",
85-
"- Kategorie korrekt? → 35% Gewicht \n",
86-
"- Richtiges Team? → 25% Gewicht\n"
146+
"- Priorität korrekt? → **40%** Gewicht (am wichtigsten!)\n",
147+
"- Kategorie korrekt? → **35%** Gewicht\n",
148+
"- Richtiges Team? → **25%** Gewicht\n"
87149
]
88150
},
89151
{
90152
"cell_type": "code",
91153
"execution_count": null,
92-
"id": "5",
154+
"id": "9",
93155
"metadata": {},
94156
"outputs": [],
95157
"source": [
96158
"print(f\"⏳ Teste generischen Prompt auf {MODEL}...\\n\")\n",
97159
"\n",
98-
"result = run_baseline(\"ticket_routing\", max_eval=10)\n",
160+
"baseline = run_baseline(\"ticket_routing\", max_eval=10)\n",
99161
"\n",
100-
"display_score(\"Generischer Prompt (Baseline)\", result.score)\n",
101-
"display_results_table(result.individual_scores[:5])\n",
162+
"display_score(\"Generischer Prompt (Baseline)\", baseline.score)\n",
163+
"display_results_table(baseline.individual_scores[:5])\n",
102164
"\n",
103-
"print(f\"\\n👆 {result.score:.0%} — nicht schlecht, aber auch nicht gut genug für Produktion.\")\n",
104-
"print(f\" Schau dir die Fehler an: das Modell kennt eure Teams und Kategorien nicht!\")\n"
165+
"print(f\"\\n👆 {baseline.score:.0%} — das Modell kennt eure Teams und Kategorien nicht!\")\n"
105166
]
106167
},
107168
{
108169
"cell_type": "markdown",
109-
"id": "6",
170+
"id": "10",
110171
"metadata": {},
111172
"source": [
112-
"## 3. Domain-Tuning → der Unterschied\n",
113-
"\n",
114-
"Jetzt lassen wir den Optimizer mit **deinen echten Ticket-Beispielen** trainieren. Er sucht automatisch die besten Few-Shot-Beispiele aus deinen Daten und baut einen besseren Prompt.\n",
173+
"## 5. Domain-Tuning → der Unterschied\n",
115174
"\n",
116-
"Gleiches Modell, gleiche Aufgabe — aber mit Domain-Wissen.\n"
175+
"Jetzt lassen wir den Optimizer mit **deinen echten Ticket-Beispielen** trainieren. Gleiches Modell, gleiche Aufgabe — aber mit Domain-Wissen.\n"
117176
]
118177
},
119178
{
120179
"cell_type": "code",
121180
"execution_count": null,
122-
"id": "7",
181+
"id": "11",
123182
"metadata": {},
124183
"outputs": [],
125184
"source": [
@@ -133,10 +192,10 @@
133192
},
134193
{
135194
"cell_type": "markdown",
136-
"id": "8",
195+
"id": "12",
137196
"metadata": {},
138197
"source": [
139-
"## 4. Was du gerade gesehen hast\n",
198+
"## 6. Was du gerade gesehen hast\n",
140199
"\n",
141200
"| | Generisch | Domain-getuned |\n",
142201
"|---|---|---|\n",
@@ -155,7 +214,7 @@
155214
{
156215
"cell_type": "code",
157216
"execution_count": null,
158-
"id": "9",
217+
"id": "13",
159218
"metadata": {},
160219
"outputs": [],
161220
"source": [
@@ -167,7 +226,7 @@
167226
},
168227
{
169228
"cell_type": "markdown",
170-
"id": "10",
229+
"id": "14",
171230
"metadata": {},
172231
"source": [
173232
"## ⏭️ Weiter geht's!\n",

0 commit comments

Comments
 (0)