File size: 35,725 Bytes
526c809
 
 
 
 
256d64b
526c809
 
 
 
10aee01
526c809
 
 
 
256d64b
526c809
256d64b
10aee01
526c809
 
 
 
 
 
256d64b
 
526c809
 
256d64b
 
 
 
 
 
 
 
 
 
 
 
526c809
 
 
 
 
 
 
256d64b
10aee01
 
 
256d64b
 
 
 
 
 
10aee01
256d64b
10aee01
 
256d64b
 
 
 
 
526c809
 
256d64b
526c809
 
256d64b
 
 
 
 
 
526c809
 
 
 
 
256d64b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8cc6b43
 
 
 
256d64b
 
526c809
 
 
 
256d64b
526c809
 
 
256d64b
 
 
 
 
10aee01
526c809
 
 
256d64b
10aee01
 
526c809
 
 
256d64b
10aee01
526c809
 
 
 
 
10aee01
526c809
10aee01
526c809
 
 
 
10aee01
 
 
526c809
 
 
10aee01
 
526c809
 
10aee01
 
 
526c809
 
 
 
 
 
 
256d64b
10aee01
 
 
 
256d64b
526c809
 
10aee01
 
 
256d64b
10aee01
 
526c809
10aee01
 
 
 
 
 
 
526c809
10aee01
 
 
 
526c809
10aee01
 
526c809
 
10aee01
 
 
526c809
10aee01
256d64b
 
526c809
10aee01
526c809
10aee01
526c809
 
 
 
 
 
 
 
 
 
10aee01
 
 
 
 
 
 
 
526c809
10aee01
526c809
10aee01
 
 
 
 
 
256d64b
10aee01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256d64b
10aee01
 
 
 
 
 
 
 
 
 
256d64b
10aee01
 
256d64b
10aee01
 
 
256d64b
10aee01
 
 
 
 
 
 
 
 
 
 
 
 
 
526c809
10aee01
526c809
10aee01
 
 
 
 
 
 
256d64b
 
 
10aee01
256d64b
 
10aee01
256d64b
 
10aee01
256d64b
 
10aee01
 
256d64b
 
 
10aee01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256d64b
10aee01
256d64b
10aee01
 
8cc6b43
10aee01
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256d64b
 
 
 
 
 
 
 
 
10aee01
256d64b
 
 
10aee01
256d64b
 
 
 
 
526c809
256d64b
 
 
10aee01
256d64b
 
 
526c809
8cc6b43
256d64b
 
 
 
 
 
526c809
256d64b
526c809
10aee01
256d64b
 
 
526c809
256d64b
 
 
526c809
256d64b
8cc6b43
256d64b
 
10aee01
256d64b
526c809
8cc6b43
526c809
8cc6b43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526c809
 
8cc6b43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
526c809
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10aee01
526c809
 
 
 
256d64b
526c809
256d64b
 
 
8cc6b43
 
 
 
 
 
256d64b
10aee01
8cc6b43
10aee01
 
8cc6b43
 
 
10aee01
256d64b
526c809
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
256d64b
 
526c809
 
 
256d64b
526c809
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
<!DOCTYPE html>
<html lang="de" class="scroll-smooth">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Konzept eines MAS mit Haystack 2.0</title>
    
    <!-- Tailwind CSS -->
    <script src="https://cdn.tailwindcss.com"></script>
    
    <!-- Highlight.js for Code Formatting -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/python.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/xml.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/bash.min.js"></script>
    
    <!-- Google Fonts (Inter & Source Code Pro for HF feel) -->
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Source+Code+Pro:wght@400;500;600&display=swap" rel="stylesheet">
    
    <script>
        tailwind.config = {
            theme: {
                extend: {
                    fontFamily: {
                        sans: ['Inter', 'ui-sans-serif', 'system-ui', 'sans-serif'],
                        mono: ['Source Code Pro', 'ui-monospace', 'SFMono-Regular', 'monospace'],
                    },
                    colors: {
                        hfYellow: {
                            DEFAULT: '#FFD21E',
                            hover: '#F5C518',
                            light: '#FFF5CC'
                        },
                        hfGray: {
                            50: '#F9FAFB',
                            100: '#F3F4F6',
                            200: '#E5E7EB',
                            800: '#1F2937',
                            900: '#111827'
                        }
                    }
                }
            }
        }
    </script>
    
    <style>
        body { font-family: 'Inter', sans-serif; background-color: #ffffff; color: #111827; }
        pre code { font-family: 'Source Code Pro', monospace; border-radius: 0.75rem; font-size: 0.875rem; padding: 1.5rem 1.25rem 1.25rem 1.25rem; }
        .prose h2 { margin-top: 3.5rem; margin-bottom: 1.25rem; font-weight: 700; font-size: 1.75rem; color: #111827; border-bottom: 1px solid #E5E7EB; padding-bottom: 0.5rem; display: flex; align-items: center; gap: 0.5rem; }
        .prose h3 { margin-top: 2.5rem; margin-bottom: 1rem; font-weight: 600; font-size: 1.25rem; color: #374151; }
        .prose p { margin-bottom: 1.25rem; line-height: 1.8; color: #4B5563; }
        .prose table { width: 100%; border-collapse: collapse; margin-bottom: 2rem; border-radius: 0.5rem; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
        .prose th, .prose td { border: 1px solid #E5E7EB; padding: 1rem; text-align: left; font-size: 0.95rem; }
        .prose th { background-color: #F9FAFB; font-weight: 600; color: #111827; }
        
        /* Tooltip / Copy Button */
        .copy-btn { position: absolute; top: 0.75rem; right: 0.75rem; background: rgba(255,255,255,0.1); color: #D1D5DB; border: 1px solid rgba(255,255,255,0.2); padding: 0.25rem 0.75rem; border-radius: 0.375rem; cursor: pointer; font-size: 0.75rem; transition: all 0.2s; z-index: 20; }
        .copy-btn:hover { background: rgba(255,255,255,0.2); color: #fff; }
        .code-wrapper { position: relative; margin-top: 1.5rem; margin-bottom: 2rem; border-radius: 0.75rem; background: #282c34; }
        .file-label { position: absolute; top: -14px; left: 16px; background: #FFD21E; color: #111827; padding: 0.15rem 0.75rem; border-radius: 9999px; font-size: 0.75rem; font-weight: 700; z-index: 10; font-family: 'Source Code Pro', monospace; border: 2px solid #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
        
        /* Custom Scrollbar for Sidebar */
        #sidebar::-webkit-scrollbar { width: 6px; }
        #sidebar::-webkit-scrollbar-track { background: transparent; }
        #sidebar::-webkit-scrollbar-thumb { background-color: #E5E7EB; border-radius: 20px; }
    </style>
</head>
<body class="flex flex-col md:flex-row min-h-screen selection:bg-hfYellow selection:text-black">

    <!-- Mobile Header -->
    <div class="md:hidden bg-white border-b border-gray-200 p-4 flex justify-between items-center sticky top-0 z-50">
        <div class="flex items-center gap-2">
            <span class="text-2xl">🤗</span>
            <h1 class="font-bold text-lg text-gray-900">MAS Konzept</h1>
        </div>
        <button id="mobile-menu-btn" class="text-gray-600 focus:outline-none hover:bg-gray-100 p-2 rounded-lg">
            <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
        </button>
    </div>

    <!-- Sidebar Navigation -->
    <nav id="sidebar" class="hidden md:flex flex-col w-full md:w-72 bg-gray-50 border-r border-gray-200 h-screen sticky top-0 overflow-y-auto shrink-0 transition-all duration-300 z-40">
        <div class="p-6 hidden md:flex flex-col gap-2">
            <div class="flex items-center gap-3">
                <span class="text-3xl">🤗</span>
                <h1 class="text-xl font-bold text-gray-900 tracking-tight leading-tight">Konzept eines MAS<br><span class="text-sm font-normal text-gray-500">mit Haystack 2.0</span></h1>
            </div>
        </div>
        
        <div class="px-4 pb-4">
            <div class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2 ml-3 mt-4">Architektur</div>
            <ul class="space-y-1 text-sm font-medium">
                <li><a href="#intro" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">1. Das Konzept & Framework</a></li>
                <li><a href="#hf-infra" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">2. Hugging Face Inference</a></li>
            </ul>

            <div class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2 ml-3 mt-6">Die Agenten (Pipelines)</div>
            <ul class="space-y-1 text-sm font-medium">
                <li><a href="#agent1" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">3. Agent 1: Forschung & Suche</a></li>
                <li><a href="#agent2" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">4. Agent 2: Struktur & Pydantic</a></li>
                <li><a href="#agent3" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">5. Agent 3: Visionär (Bildgen.)</a></li>
                <li><a href="#agent4" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">6. Agent 4: Meta-Aggregator</a></li>
            </ul>

            <div class="text-xs font-semibold text-gray-400 uppercase tracking-wider mb-2 ml-3 mt-6">Orchestrierung & Code</div>
            <ul class="space-y-1 text-sm font-medium">
                <li><a href="#orchestration" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">7. Routing & Tool-Calling</a></li>
                <li><a href="#repo" class="block py-2 px-3 text-gray-600 rounded-lg hover:bg-gray-200 hover:text-gray-900 transition">8. Repositorystruktur</a></li>
                <li><a href="#outlook" class="flex items-center gap-2 mt-2 py-2 px-3 bg-gray-900 text-white rounded-lg hover:bg-gray-800 transition">
                    <svg class="w-4 h-4 text-hfYellow" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
                    9. Fazit: Die KI-Küchenbrigade
                </a></li>
            </ul>
        </div>
    </nav>

    <!-- Main Content -->
    <main class="flex-1 w-full max-w-4xl mx-auto p-6 md:p-10 lg:p-14">
        <div class="prose max-w-none">
            
            <!-- Hero -->
            <div class="bg-hfYellow/10 border border-hfYellow rounded-2xl p-8 mb-12 shadow-sm">
                <div class="flex items-center gap-3 mb-4">
                    <span class="text-4xl">🥘</span>
                    <h1 class="text-3xl font-bold text-gray-900 m-0">Architektur eines Kulinarischen MAS</h1>
                </div>
                <p class="text-gray-700 text-lg leading-relaxed m-0">Ein Deep-Dive in die Orchestrierung von Multi-Agenten-Systemen (MAS) für <strong>"The Infinite Cookbook"</strong>. Wir zerlegen komplexe Aufgaben (Recherche, Strukturierung, Bildgenerierung, Layout) in spezialisierte Haystack 2.0 Pipelines und verbinden sie über Hugging Face Open-Source LLMs zu einem autonomen System.</p>
            </div>

            <section id="intro">
                <h2><span>🏗️</span> 1. Das Konzept: Haystack 2.0</h2>
                <p>Die Evolution der KI verlangt nach neuen Architekturen. Der Übergang von Haystack 1.x zu 2.0 markiert einen Paradigmenwechsel: Frühere Versionen waren auf lineare Ketten ausgelegt, Haystack 2.0 führt <strong>Komponenten und Pipelines als dynamische Rechengraphen</strong> ein.</p>
                <p>Diese Graphen unterstützen Kontrollflüsse, Schleifen und präzises Routing. Ein Agent in diesem Ökosystem ist weit mehr als ein Chatbot; er ist ein System, das ein LLM als Steuerungsorgan nutzt, um Aufgaben zu planen und an spezialisierte Sub-Pipelines (andere Agenten) zu delegieren. Typprüfung an den Ein- und Ausgabesockets sichert dabei die Stabilität vor der Laufzeit.</p>
            </section>

            <section id="hf-infra">
                <h2><span>🤗</span> 2. Hugging Face Inference Provider</h2>
                <p>Hugging Face ist der zentrale Marktplatz für Open-Source-Intelligenz. Im Juli 2025 stellte die Inference API auf <code>chat_completion</code>-Endpunkte um. Haystack bindet diese Modelle über die <code>HuggingFaceAPIChatGenerator</code>-Komponente ein. Durch dynamische Suffixe wie <code>:fastest</code> oder Provider-Wahl können wir Lasten optimieren:</p>
                
                <div class="overflow-x-auto">
                    <table>
                        <thead>
                            <tr>
                                <th>Infrastruktur-Option</th>
                                <th>Charakteristika</th>
                                <th>Anwendungsfall im MAS</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr>
                                <td><strong>Serverless Inference API</strong></td>
                                <td>Kostenlos/Rate-limited, schnell</td>
                                <td>Prototyping und einfache Zusammenfassungen (z.B. Qwen 72B).</td>
                            </tr>
                            <tr>
                                <td><strong>Inference Endpoints</strong></td>
                                <td>Dedizierte Instanzen, /h Abrechnung</td>
                                <td>Produktion, garantierte Latenz, Bildgenerierung (SDXL / FLUX).</td>
                            </tr>
                            <tr>
                                <td><strong>Text Generation Inference (TGI)</strong></td>
                                <td>Optimiertes Serving, JSON-Guiding</td>
                                <td>Komplexe Datenextraktion mit strikter Schema-Einhaltung.</td>
                            </tr>
                        </tbody>
                    </table>
                </div>
            </section>

            <section id="agent1">
                <h2><span>🔍</span> 3. Agent 1: Forschung & Suche</h2>
                <p>Die erste Phase ist die <em>Deep Research Phase</em>. Dieser Agent durchsucht das Web iterativ nach authentischen Rezepten, regionalen Varianten und historischen Hintergründen. Er nutzt Tools, um Links zu besuchen, HTML zu bereinigen und die Ergebnisse nach Relevanz (Context Optimization) zu ranken, bevor sie ans LLM gehen.</p>
                
                <div class="code-wrapper">
                    <div class="file-label">agents/researcher.py</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-python">from haystack import Pipeline
from haystack.components.websearch import SerperDevWebSearch
from haystack.components.fetchers import LinkContentFetcher
from haystack.components.converters import HTMLToDocument
from haystack.components.rankers import TransformersSimilarityRanker

def build_research_pipeline() -> Pipeline:
    pipe = Pipeline()

    # Komponenten initialisieren
    pipe.add_component("search", SerperDevWebSearch(api_key="DEIN_API_KEY"))
    pipe.add_component("fetcher", LinkContentFetcher())
    pipe.add_component("html_converter", HTMLToDocument())
    
    # Der Ranker sortiert Dokumente nach Relevanz zur Suchanfrage aus
    pipe.add_component("ranker", TransformersSimilarityRanker(model="cross-encoder/ms-marco-MiniLM-L-6-v2"))

    # Den gerichteten Graphen aufbauen
    pipe.connect("search.links", "fetcher.urls")
    pipe.connect("fetcher.streams", "html_converter.sources")
    pipe.connect("html_converter.documents", "ranker.documents")

    return pipe</code></pre>
                </div>
            </section>

            <section id="agent2">
                <h2><span>📋</span> 4. Agent 2: Struktur & Pydantic</h2>
                <p>Der Zusammenfassungs-Agent wandelt die unstrukturierten Forschungsnotizen in ein präzises, maschinenlesbares Rezept um. Das ist kritisch, da Inkonsistenzen das HTML-Layout gefährden. Haystack 2.0 erzwingt strukturierte Ausgaben über <strong>Pydantic-Modelle</strong> (besonders mächtig in Kombination mit Hugging Face TGI-Instanzen).</p>
                
                <div class="code-wrapper">
                    <div class="file-label">schemas/recipe.py</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-python">from pydantic import BaseModel
from typing import List, Dict, Literal

# Strikte Typisierung eliminiert Halluzinationen in der Struktur
class Ingredient(BaseModel):
    name: str
    amount: float
    unit: str

class RecipeStructure(BaseModel):
    recipe_title: str
    servings: int
    ingredients: List[Ingredient]
    instructions: List[str]
    nutrients: Dict[str, float]
    difficulty: Literal["Einfach", "Mittel", "Schwer"]

# Nutzungshinweis für die Pipeline:
# generator = HuggingFaceAPIChatGenerator(
#     api_type="text_generation_inference",
#     generation_kwargs={"response_format": RecipeStructure.schema_json()}
# )</code></pre>
                </div>
            </section>

            <section id="agent3">
                <h2><span>🎨</span> 5. Agent 3: Visionär (Bildgenerierung)</h2>
                <p>Ein Kochbuch braucht Ästhetik. Da Sprachmodelle keinen Text in Bilder konvertieren, implementieren wir eine <strong>benutzerdefinierte Haystack-Komponente</strong>. Diese nimmt den Rezepttitel auf, optimiert den Prompt ("Professionelle Food-Fotografie, Makro...") und steuert via <code>diffusers</code> ein Modell wie Stable Diffusion XL oder FLUX an.</p>
                
                <div class="code-wrapper">
                    <div class="file-label">components/image_generator.py</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-python">from haystack import component
from haystack.dataclasses import ImageContent
# import diffusers ...

@component
class StableDiffusionGenerator:
    def __init__(self, model_id="stabilityai/stable-diffusion-xl-base-1.0"):
        self.model_id = model_id
        # Initialisierung z.B. Pipeline Laden inkl. FP16 Optimierung

    # Definition des Output-Sockets für die Validierung vor der Laufzeit
    @component.output_types(image=ImageContent)
    def run(self, prompt: str):
        # image = self.pipe(prompt).images[0]
        # base64_str = convert_to_base64(image)
        
        # Rückgabe als Haystack 2.0 Multimodal Objekt
        return {"image": ImageContent.from_base64(base64_str)}</code></pre>
                </div>
            </section>

            <section id="agent4">
                <h2><span>💻</span> 6. Agent 4: Meta-Aggregator (HTML)</h2>
                <p>Der letzte Agent orchestriert die Präsentation. Er empfängt das strukturierte Rezept (als Dictionary/JSON) und das generierte Bild (als Base64) und verpackt beides über <strong>Jinja2-Templating</strong>.</p>
                
                <div class="code-wrapper">
                    <div class="file-label">templates/recipe_card.html</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-xml">&lt;!-- Das Template wird vom HTML-Agenten mit Daten befüllt --&gt;
&lt;div class="recipe-card"&gt;
    &lt;h1&gt;{{ recipe_title }}&lt;/h1&gt;
    
    &lt;!-- Base64 Einbettung erspart externe Bild-Hostings --&gt;
    &lt;img src="data:image/png;base64,{{ image_base64 }}" alt="{{ recipe_title }}"&gt;
    
    &lt;div class="meta"&gt;
        &lt;span&gt;Schwierigkeit: {{ difficulty }}&lt;/span&gt;
    &lt;/div&gt;
    
    &lt;h3&gt;Zutaten&lt;/h3&gt;
    &lt;ul&gt;
        {% for ingredient in ingredients %}
            &lt;li&gt;{{ ingredient.amount }} {{ ingredient.unit }} {{ ingredient.name}}&lt;/li&gt;
        {% endfor %}
    &lt;/ul&gt;
    
    &lt;h3&gt;Zubereitung&lt;/h3&gt;
    &lt;ol&gt;
        {% for step in instructions %}
            &lt;li&gt;{{ step }}&lt;/li&gt;
        {% endfor %}
    &lt;/ol&gt;
&lt;/div&gt;</code></pre>
                </div>
            </section>

            <section id="orchestration">
                <h2><span>🚦</span> 7. Orchestrierung & Fehlerbehandlung</h2>
                <p>Um diese Agenten zu vereinen, nutzen wir den <strong>"Agent-as-a-Tool"</strong>-Ansatz. Ein Koordinator-Agent kapselt die Pipelines in Werkzeugen. So kann das Haupt-LLM dynamisch entscheiden, wann es recherchiert und wann es Bilder generiert.</p>
                
                <p>Für Robustheit sorgt der <code>ConditionalRouter</code>. Liefert ein Tool einen Fehler (z.B. keine Suchergebnisse), leitet der Router den Datenfluss um, sodass das LLM seinen Such-Prompt anpassen kann, anstatt abzustürzen.</p>

                <div class="code-wrapper">
                    <div class="file-label">tools/agent_tools.py</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-python">from haystack.tools import Tool
from haystack.components.routers import ConditionalRouter
from agents.researcher import build_research_pipeline

# Pipeline laden
research_pipe = build_research_pipeline()

# Pipeline als aufrufbares Werkzeug für das LLM verpacken
research_tool = Tool(
    name="research_tool",
    description="Durchsucht das Web nach authentischen Rezepten.",
    parameters={"query": {"type": "string"}},
    function=research_pipe.run
)

# Fehlerbehandlung: Ein Router, der den Pfad basierend auf Tool-Erfolg wählt
routes = [
    {
        "condition": "{{ 'error' in tool_result.text | lower }}",
        "output": "{{ tool_result }}",
        "output_name": "error_route",
        "output_type": str
    },
    {
        "condition": "{{ 'error' not in tool_result.text | lower }}",
        "output": "{{ tool_result }}",
        "output_name": "success_route",
        "output_type": str
    }
]
router = ConditionalRouter(routes)</code></pre>
                </div>
            </section>

            <!-- KAPITEL 8: REPOSITORY UND CODE -->
            <section id="repo">
                <h2><span>📁</span> 8. Repository-Struktur: Der Master-Plan</h2>
                <p>Alle Code-Schnipsel aus Kapitel 3 bis 7 fügen sich nun zusammen. Ein komplexes MAS sollte <strong>niemals ein Monolith</strong> sein (alles in einem Skript). Eine monolithische Architektur würde das Testen einzelner Pipelines fast unmöglich machen und zu Spaghetti-Code beim Routing führen.</p>
                
                <p>Dank der modularen Architektur von Haystack 2.0 spiegelt sich die fachliche Trennung direkt im Repository wider:</p>
                
                <div class="bg-gray-50 border border-gray-200 rounded-lg p-5 mb-8 font-mono text-sm text-gray-700 leading-relaxed shadow-sm">
infinite-cookbook/<br>
├── <span class="font-bold text-gray-900">schemas/</span><br>
│   └── <span class="text-blue-600">recipe.py</span> <span class="text-gray-400 italic"># (Kapitel 4) Pydantic Modelle</span><br>
├── <span class="font-bold text-gray-900">components/</span><br>
│   └── <span class="text-blue-600">image_generator.py</span> <span class="text-gray-400 italic"># (Kapitel 5) Custom @component</span><br>
├── <span class="font-bold text-gray-900">templates/</span><br>
│   └── <span class="text-blue-600">recipe_card.html</span> <span class="text-gray-400 italic"># (Kapitel 6) Jinja2 Template</span><br>
├── <span class="font-bold text-gray-900">agents/</span><br>
│   ├── <span class="text-blue-600">researcher.py</span> <span class="text-gray-400 italic"># (Kapitel 3) Web-Search Pipeline</span><br>
│   └── <span class="text-gray-500">structurer.py ...</span><br>
├── <span class="font-bold text-gray-900">tools/</span><br>
│   └── <span class="text-blue-600">agent_tools.py</span> <span class="text-gray-400 italic"># (Kapitel 7) Pipeline -> Tool Wrapper</span><br>
└── <span class="text-blue-600 font-bold">main.py</span> <span class="text-gray-400 italic"># Orchestrierungs-Loop (siehe unten)</span>
                </div>

                <h3>Das Herzstück: <code>main.py</code></h3>
                <p>Weil wir alle Komplexität in die Unterordner ausgelagert haben, ist die <code>main.py</code> unglaublich elegant. Sie initialisiert das Haupt-LLM, importiert die fertige Werkzeugkiste und startet die Interaktions-Schleife.</p>
                
                <div class="code-wrapper mt-4">
                    <div class="file-label">main.py</div>
                    <button class="copy-btn" onclick="copyCode(this)">Copy</button>
                    <pre><code class="language-python">import os
from haystack import Pipeline
from haystack.components.generators.chat import HuggingFaceAPIChatGenerator
from haystack.components.tools import ToolInvoker
from haystack.dataclasses import ChatMessage

# Der saubere Import aus unserer Architektur (Kapitel 7)
from tools.agent_tools import cookbook_tools

def main():
    # 1. Der Koordinator: Ein starkes Modell (z.B. Qwen), das Tool-Calling beherrscht
    llm = HuggingFaceAPIChatGenerator(
        api_type="serverless_inference_api",
        model="Qwen/Qwen2.5-72B-Instruct",
        tools=cookbook_tools
    )
    
    # 2. Der Invoker: Führt die vom LLM gewählten Tools aus
    invoker = ToolInvoker(tools=cookbook_tools)

    # 3. Der Orchestrierungs-Graph aufbauen
    coordinator = Pipeline()
    coordinator.add_component("llm", llm)
    coordinator.add_component("invoker", invoker)
    
    # Zyklische Verbindung für iteratives Arbeiten
    coordinator.connect("llm.replies", "invoker.messages")
    coordinator.connect("invoker.tool_messages", "llm.messages")

    # 4. System Start
    user_prompt = "Erstelle ein Rezept für ein thailändisches grünes Curry samt HTML und Bild."
    messages = [ChatMessage.from_user(user_prompt)]
    
    print(f"🤖 Koordinator startet Aufgabe: {user_prompt}")
    
    # Run Loop
    result = coordinator.run({"llm": {"messages": messages}})
    
    print("Fertiges Ergebnis:", result["llm"]["replies"][0].text)

if __name__ == "__main__":
    main()</code></pre>
                </div>
                
                <div class="mt-8 p-5 bg-blue-50/50 rounded-xl text-sm text-gray-700 border border-blue-100 flex items-start gap-4 shadow-sm">
                    <span class="text-2xl mt-1">💡</span>
                    <div>
                        <strong class="text-gray-900 block mb-1">Die Architektur-Philosophie:</strong> 
                        Diese Aufteilung erlaubt es dir, das Modell in <code>main.py</code> auszutauschen, die Such-Logik in <code>researcher.py</code> komplett neu zu schreiben oder an der HTML-Karte zu basteln, ohne dass die anderen Teile des Multi-Agenten-Systems zerbrechen. Das ist die Stärke von Haystack 2.0!
                    </div>
                </div>
            </section>

            <!-- NEUES GRAND FINALE (KERNPRINZIP MAS & KÜCHENMETAPHER) -->
            <section id="outlook" class="mt-20 pt-8 border-t border-gray-200">
                <div class="bg-gray-900 text-white rounded-3xl p-8 md:p-12 shadow-2xl relative overflow-hidden">
                    <!-- Dekoration -->
                    <div class="absolute -top-24 -right-24 w-64 h-64 bg-hfYellow rounded-full mix-blend-multiply filter blur-3xl opacity-20"></div>
                    <div class="absolute -bottom-24 -left-24 w-64 h-64 bg-blue-500 rounded-full mix-blend-multiply filter blur-3xl opacity-20"></div>
                    
                    <div class="relative z-10">
                        <div class="flex items-center gap-3 mb-6">
                            <span class="text-3xl">👨‍🍳</span>
                            <h2 class="text-3xl font-bold m-0 border-none !text-white">Fazit: Die KI-Küchenbrigade</h2>
                        </div>
                        
                        <p class="text-gray-300 text-lg leading-relaxed max-w-2xl mb-6">
                            Man sagt oft: <em>»Viele Köche verderben den Brei.«</em> In der Welt der Künstlichen Intelligenz gilt jedoch genau das Gegenteil – vorausgesetzt, man hat die richtige Architektur.
                        </p>

                        <p class="text-gray-300 text-lg leading-relaxed max-w-2xl mb-8">
                            Das "Infinite Cookbook" illustriert das <strong>Grundprinzip eines Multi-Agenten-Systems (MAS)</strong> perfekt. Anstatt ein einziges, riesiges "Gott-Modell" mit einem gigantischen Prompt zu überlasten (und zu hoffen, dass es gleichzeitig fehlerfrei recherchiert, programmiert, layoutet und malt), etablieren wir eine hochspezialisierte, arbeitsteilige Küchenbrigade:
                        </p>
                        
                        <div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-10">
                            <div class="bg-white/5 border border-white/10 rounded-xl p-5 hover:bg-white/10 transition">
                                <h3 class="!text-white text-base font-semibold mt-0 mb-2 flex items-center gap-2">
                                    <span>👔</span> Der Küchenchef (Koordinator)
                                </h3>
                                <p class="text-gray-400 text-sm m-0">
                                    Er kocht nicht selbst. Er nimmt die Bestellung des Gastes entgegen, plant die Schritte und delegiert die Aufgaben an sein Team (Tool Calling).
                                </p>
                            </div>
                            <div class="bg-white/5 border border-white/10 rounded-xl p-5 hover:bg-white/10 transition">
                                <h3 class="!text-white text-base font-semibold mt-0 mb-2 flex items-center gap-2">
                                    <span>🛒</span> Der Einkäufer (Forschung)
                                </h3>
                                <p class="text-gray-400 text-sm m-0">
                                    Er weiß, auf welchen Märkten (Web) es die besten und authentischsten Zutaten (Daten) gibt. Er sortiert den Müll aus und bringt nur das Beste in die Küche.
                                </p>
                            </div>
                            <div class="bg-white/5 border border-white/10 rounded-xl p-5 hover:bg-white/10 transition">
                                <h3 class="!text-white text-base font-semibold mt-0 mb-2 flex items-center gap-2">
                                    <span>🔪</span> Der Postenchef (Struktur)
                                </h3>
                                <p class="text-gray-400 text-sm m-0">
                                    Er ist besessen von Präzision. Er nimmt die losen Zutaten und formatiert sie exakt nach der strikten Pydantic-Rezeptvorlage. Keine Halluzinationen, nur Fakten.
                                </p>
                            </div>
                            <div class="bg-white/5 border border-white/10 rounded-xl p-5 hover:bg-white/10 transition">
                                <h3 class="!text-white text-base font-semibold mt-0 mb-2 flex items-center gap-2">
                                    <span>📸</span> Der Food-Stylist (Visionär)
                                </h3>
                                <p class="text-gray-400 text-sm m-0">
                                    Er interessiert sich nicht für Grammatik oder HTML. Er malt mit Diffusionsmodellen atemberaubende Bilder, die das Gericht perfekt in Szene setzen.
                                </p>
                            </div>
                        </div>

                        <p class="text-gray-300 text-lg leading-relaxed max-w-2xl mb-8 font-medium border-l-4 border-hfYellow pl-4">
                            Die wahre Stärke eines MAS ist die <strong>Isolation von Komplexität</strong>. Wenn das HTML bricht, reparieren wir den Meta-Agenten. Wenn wir ein besseres Open-Source-Modell für JSON finden, tauschen wir den Postenchef aus, ohne dass der Rest der Küche stehenbleibt. <span class="text-hfYellow">Haystack 2.0 liefert uns dafür die Küche – und die Infrastruktur von Hugging Face das Personal.</span>
                        </p>

                        <div class="flex flex-wrap items-center gap-4">
                            <a href="https://haystack.deepset.ai/" target="_blank" class="px-6 py-3 bg-hfYellow text-gray-900 font-bold rounded-lg hover:bg-hfYellow-hover transition-colors flex items-center gap-2 no-underline">
                                Erforsche Haystack 2.0
                                <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14 5l7 7m0 0l-7 7m7-7H3"></path></svg>
                            </a>
                            <a href="https://huggingface.co/models" target="_blank" class="px-6 py-3 bg-gray-800 text-white border border-gray-700 font-semibold rounded-lg hover:bg-gray-700 transition-colors flex items-center gap-2 no-underline">
                                🤗 HF Modelle entdecken
                            </a>
                        </div>
                    </div>
                </div>
            </section>
            
            <!-- ÜBERARBEITETER FOOTER -->
            <footer class="mt-16 py-8 border-t border-gray-200 flex flex-col md:flex-row items-center justify-between gap-6">
                <div class="flex items-center gap-3">
                    <span class="text-2xl opacity-80">🤗</span>
                    <p class="text-sm text-gray-500 m-0 leading-tight">
                        <strong>The Infinite Cookbook Konzept</strong><br>
                        Ein Architektur-Beispiel für Multi-Agenten-Systeme.
                    </p>
                </div>
                
                <div class="flex items-center gap-6 text-sm font-medium text-gray-500">
                    <a href="#" class="hover:text-gray-900 transition-colors no-underline">Source PDF</a>
                    <a href="https://github.com/deepset-ai/haystack" target="_blank" class="hover:text-gray-900 transition-colors no-underline">Haystack GitHub</a>
                    <a href="https://huggingface.co/docs/inference-providers/index" target="_blank" class="hover:text-gray-900 transition-colors no-underline">Inference Docs</a>
                </div>
            </footer>
        </div>
    </main>

    <script>
        // Syntax Highlighting initialisieren
        document.addEventListener('DOMContentLoaded', (event) => {
            hljs.highlightAll();
        });

        // Mobile Menu Toggle
        const btn = document.getElementById('mobile-menu-btn');
        const sidebar = document.getElementById('sidebar');

        btn.addEventListener('click', () => {
            sidebar.classList.toggle('hidden');
        });

        // Smooth scroll adjustment for fixed header
        document.querySelectorAll('a[href^="#"]').forEach(anchor => {
            anchor.addEventListener('click', function (e) {
                e.preventDefault();
                if(window.innerWidth < 768) {
                    sidebar.classList.add('hidden');
                }
                
                // Active State Styling Update
                document.querySelectorAll('#sidebar a').forEach(a => {
                    a.classList.remove('bg-hfYellow/20', 'text-yellow-800', 'font-semibold', 'bg-gray-900', 'text-white', 'hover:bg-gray-800');
                    if (a.getAttribute('href') === '#outlook') {
                         a.classList.add('bg-gray-900', 'text-white', 'hover:bg-gray-800');
                    } else {
                         a.classList.add('text-gray-600');
                    }
                });
                
                if(this.getAttribute('href') !== '#outlook' && this.getAttribute('href') !== '#repo') {
                   this.classList.remove('text-gray-600');
                   this.classList.add('bg-hfYellow/20', 'text-yellow-800', 'font-semibold');
                } else if (this.getAttribute('href') === '#repo') {
                   this.classList.remove('text-gray-600');
                   this.classList.add('bg-hfYellow/20', 'text-yellow-900', 'font-semibold');
                }

                document.querySelector(this.getAttribute('href')).scrollIntoView({
                    behavior: 'smooth'
                });
            });
        });

        // Copy to clipboard function
        function copyCode(button) {
            const codeBlock = button.nextElementSibling.querySelector('code');
            const textToCopy = codeBlock.innerText;
            
            const textArea = document.createElement("textarea");
            textArea.value = textToCopy;
            document.body.appendChild(textArea);
            textArea.select();
            
            try {
                document.execCommand('copy');
                const originalText = button.innerText;
                button.innerText = 'Copied!';
                button.classList.add('bg-green-600', 'text-white', 'border-green-600');
                
                setTimeout(() => {
                    button.innerText = originalText;
                    button.classList.remove('bg-green-600', 'text-white', 'border-green-600');
                }, 2000);
            } catch (err) {
                console.error('Kopieren fehlgeschlagen', err);
            }
            
            document.body.removeChild(textArea);
        }
    </script>
</body>
</html>