final lessons
This commit is contained in:
@@ -1,442 +0,0 @@
|
||||
# Les 13 — Agents
|
||||
## Slide Overzicht (Klas A — 3 uur fysiek, demo-driven)
|
||||
|
||||
**Lesvorm:** Tim demonstreert klassikaal. Studenten kijken. Zelf bouwen = huiswerk.
|
||||
**Demo-app:** Nieuwe research-agent (from scratch, los van Polderfest)
|
||||
**Vervolg op:** Les 12 — Tool Calling
|
||||
**Aansluit op:** Les 14 — RAG + embeddings
|
||||
|
||||
---
|
||||
|
||||
## Slide 1: Title
|
||||
### Les 13 — Agents
|
||||
|
||||
**Visual:**
|
||||
- Background: CREAM
|
||||
- "Les 13" in BLUE
|
||||
- "Agents" in BLACK
|
||||
- Subtitle: "Een LLM in een loop met tools — autonoom tot het klaar is"
|
||||
|
||||
---
|
||||
|
||||
## Slide 2: Terugblik
|
||||
### Waar staan we?
|
||||
|
||||
**Vorige les (Les 12):**
|
||||
- Tool Calling — AI roept zelf functies aan
|
||||
- `tool({ description, inputSchema, execute })`
|
||||
- `stopWhen: stepCountIs(5)` — multi-step
|
||||
- Polderfest chat met 6 tools (search, stats, schedule, favorites)
|
||||
|
||||
**Wat een tool-call bot nog NIET kan:**
|
||||
- Lange ketens (30+ stappen)
|
||||
- Eigen plan opstellen + bijstellen
|
||||
- Tools die andere tools triggeren
|
||||
- Dynamisch het model wisselen per stap
|
||||
|
||||
**Vandaag: agents. Eén stap verder.**
|
||||
|
||||
**Visual:** Pijltje van "5-step tool calling" naar "20-50 step agent loop"
|
||||
|
||||
---
|
||||
|
||||
## Slide 3: Planning
|
||||
### Vandaag — 180 minuten
|
||||
|
||||
| Onderwerp | Duur |
|
||||
|-----------|------|
|
||||
| Welkom + Terugblik | 10 min |
|
||||
| Theorie: wat is een agent? | 30 min |
|
||||
| **Live Demo 1** — Setup nieuwe research-agent | 25 min |
|
||||
| **Live Demo 2** — ToolLoopAgent + stopWhen | 25 min |
|
||||
| **Pauze** | 15 min |
|
||||
| **Live Demo 3** — prepareStep + dynamic control | 25 min |
|
||||
| **Live Demo 4** — Planning patterns + done-tool | 20 min |
|
||||
| Wanneer agent vs tool-call vs workflow? | 10 min |
|
||||
| Lesopdracht + Huiswerk uitleg | 15 min |
|
||||
| Vragen + Afsluiting | 5 min |
|
||||
|
||||
**Format:** Demo-driven. Code-fragmenten in slides + live in editor.
|
||||
|
||||
---
|
||||
|
||||
## Slide 4: Wat is een agent?
|
||||
### LLM + Tools + Loop
|
||||
|
||||
**De definitie van Vercel AI SDK:**
|
||||
> Agents are LLMs that use tools in a loop to accomplish tasks.
|
||||
|
||||
**Drie componenten:**
|
||||
|
||||
1. **LLM** — beslist wat de volgende actie is
|
||||
2. **Tools** — uitbreiding (search, read, write, anything)
|
||||
3. **Loop** — runtime die deze twee orchestreert
|
||||
- *Context management:* wat ziet model in elke stap
|
||||
- *Stopping condition:* wanneer is de taak klaar
|
||||
|
||||
**Het verschil met Les 12:**
|
||||
- Les 12: tot 5 stappen, korte taken
|
||||
- Les 13: 20-50+ stappen, langere autonome workflows
|
||||
|
||||
**Visual:** Loop-diagram — LLM → kies tool → execute → resultaat → LLM → ... → klaar
|
||||
|
||||
---
|
||||
|
||||
## Slide 5: De anatomie van de loop
|
||||
### Wat gebeurt er per stap?
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ Loop iteratie (1 step): │
|
||||
│ │
|
||||
│ 1. Stuur messages naar model │
|
||||
│ 2. Model genereert response: │
|
||||
│ - text? → loop stopt │
|
||||
│ - tool-call? → ga door │
|
||||
│ 3. Voer tool execute() uit │
|
||||
│ 4. Voeg tool-result toe aan msgs │
|
||||
│ 5. Check stop-conditie │
|
||||
│ 6. Volgende iteratie │
|
||||
└─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**De loop stopt wanneer:**
|
||||
- Model geeft text in plaats van tool-call (natural finish), OF
|
||||
- `stopWhen`-conditie is voldaan, OF
|
||||
- Tool zonder execute wordt aangeroepen (done-pattern), OF
|
||||
- Tool-call needs approval
|
||||
|
||||
**Default in v6:** `stepCountIs(20)` — veiligheidsgrens.
|
||||
|
||||
---
|
||||
|
||||
## Slide 6: ToolLoopAgent
|
||||
### De nieuwe agent-abstractie in v6
|
||||
|
||||
**In plaats van zelf streamText loopen, gebruik je ToolLoopAgent:**
|
||||
|
||||
```typescript
|
||||
import { ToolLoopAgent, tool, stepCountIs } from "ai";
|
||||
|
||||
const researchAgent = new ToolLoopAgent({
|
||||
model: "openai/gpt-4o",
|
||||
system: "You are a research assistant...",
|
||||
tools: {
|
||||
webSearch,
|
||||
readPage,
|
||||
saveReport,
|
||||
},
|
||||
stopWhen: stepCountIs(30),
|
||||
});
|
||||
|
||||
const result = await researchAgent.generate({
|
||||
prompt: "Onderzoek de impact van AI op de Nederlandse bouwsector",
|
||||
});
|
||||
|
||||
console.log(result.text); // antwoord
|
||||
console.log(result.steps); // alle stappen
|
||||
```
|
||||
|
||||
**Wat krijg je:**
|
||||
- Minder boilerplate (geen eigen loop schrijven)
|
||||
- Herbruikbaar (definieer 1x, gebruik overal)
|
||||
- Built-in loop, context, stopping
|
||||
|
||||
**Visual:** Side-by-side: handmatige while-loop vs ToolLoopAgent.
|
||||
|
||||
---
|
||||
|
||||
## Slide 7: Stop-condities
|
||||
### Wanneer is een agent klaar?
|
||||
|
||||
**Built-in condities:**
|
||||
|
||||
| Conditie | Wanneer | Wanneer gebruiken |
|
||||
|----------|---------|-------------------|
|
||||
| `stepCountIs(N)` | Na N stappen | Default veiligheid |
|
||||
| `hasToolCall("done")` | Specifieke tool gebruikt | Done-pattern |
|
||||
| `isLoopFinished()` | Nooit — model bepaalt | Lange autonomie |
|
||||
|
||||
**Combineren met array:**
|
||||
```typescript
|
||||
stopWhen: [
|
||||
stepCountIs(50), // max budget
|
||||
hasToolCall("submit"), // OF submit-tool aangeroepen
|
||||
]
|
||||
```
|
||||
|
||||
**Custom conditie:**
|
||||
```typescript
|
||||
const budgetExceeded: StopCondition<typeof tools> = ({ steps }) => {
|
||||
const cost = computeCost(steps);
|
||||
return cost > 0.50; // stop bij $0.50
|
||||
};
|
||||
```
|
||||
|
||||
**Visual:** Drie traffic lights — rood (step limit), oranje (tool reached), groen (kostgrens).
|
||||
|
||||
---
|
||||
|
||||
## Slide 8: prepareStep
|
||||
### Per stap iets anders doen
|
||||
|
||||
**`prepareStep` runt VOOR elke stap. Je kunt aanpassen:**
|
||||
- Model wisselen (mini → sonnet voor complex)
|
||||
- Tools beperken (alleen search in fase 1, alleen write in fase 3)
|
||||
- Messages trimmen (context-budget bewaken)
|
||||
- `toolChoice` forceren
|
||||
|
||||
**Voorbeeld — dynamic model:**
|
||||
```typescript
|
||||
new ToolLoopAgent({
|
||||
model: "openai/gpt-4o-mini", // default
|
||||
prepareStep: async ({ stepNumber, messages }) => {
|
||||
if (stepNumber > 2 && messages.length > 10) {
|
||||
return { model: "anthropic/claude-sonnet-4.5" };
|
||||
}
|
||||
return {}; // niks aanpassen
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
**Voorbeeld — fase-gebaseerde tools:**
|
||||
```typescript
|
||||
prepareStep: async ({ stepNumber }) => {
|
||||
if (stepNumber <= 3) return { activeTools: ["webSearch"] };
|
||||
if (stepNumber <= 6) return { activeTools: ["readPage"] };
|
||||
return { activeTools: ["saveReport"], toolChoice: "required" };
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Slide 9: Wat we vandaag bouwen
|
||||
### Research-agent from scratch
|
||||
|
||||
**Doel:** Een agent die zelfstandig een onderzoeksvraag beantwoordt door web te zoeken, pagina's te lezen, en een rapport in Supabase op te slaan.
|
||||
|
||||
**Stack:**
|
||||
- Next.js 16 + TypeScript + Tailwind + Shadcn
|
||||
- AI SDK v6 (ToolLoopAgent)
|
||||
- Tavily API (web search)
|
||||
- Supabase (rapport opslag)
|
||||
- OpenAI gpt-4o / Anthropic Sonnet
|
||||
|
||||
**Tools die we bouwen:**
|
||||
|
||||
| Tool | Wat | Gebruik in flow |
|
||||
|------|-----|-----------------|
|
||||
| `webSearch` | Tavily query → 5 resultaten | Fase 1: oriëntatie |
|
||||
| `readPage` | URL → fulltext extractie | Fase 2: diepgang |
|
||||
| `saveReport` | Insert in `research_reports` | Fase 3: opslaan |
|
||||
| `done` | No-execute, signaal klaar | Eind: stoppen |
|
||||
|
||||
**Visual:** Diagram van research-flow: vraag → search → read → save → done.
|
||||
|
||||
---
|
||||
|
||||
## Slide 10: LIVE DEMO 1 — Setup research-agent
|
||||
### ~25 min
|
||||
|
||||
**Wat ik laat zien:**
|
||||
1. `pnpm create next-app research-agent --typescript --tailwind --app`
|
||||
2. `pnpm add ai @ai-sdk/openai @ai-sdk/anthropic zod @supabase/supabase-js`
|
||||
3. `.env.local` — TAVILY_API_KEY, OPENAI_API_KEY, SUPABASE_*
|
||||
4. Supabase schema: `research_reports` (id, query, summary, sources jsonb, created_at)
|
||||
5. `lib/tools.ts` — eerste tool `webSearch` met Tavily fetch
|
||||
6. `lib/agent.ts` — `ToolLoopAgent` initialiseren
|
||||
7. `app/api/research/route.ts` — POST endpoint die agent.generate() doet
|
||||
8. Eerste test in terminal: `curl /api/research -d '{"query": "..."}'`
|
||||
|
||||
**Belangrijk:** Tavily API key is gratis tier (1000 calls/maand). Studenten krijgen instructie om zelf account te maken.
|
||||
|
||||
**Visual:** Folder-tree van de research-agent repo.
|
||||
|
||||
---
|
||||
|
||||
## Slide 11: LIVE DEMO 2 — ToolLoopAgent + stopWhen
|
||||
### ~25 min
|
||||
|
||||
**Wat ik laat zien:**
|
||||
1. Tweede en derde tool toevoegen: `readPage`, `saveReport`
|
||||
2. `ToolLoopAgent` configuren met alle drie + `stopWhen: stepCountIs(30)`
|
||||
3. System prompt schrijven: "plan eerst, search dan, lees relevante pagina's, vat samen, sla op"
|
||||
4. Test met `"Schrijf een rapport over de Polderfest 2027 lineup"`
|
||||
5. `result.steps` loggen — zien dat agent 8-15 stappen doet
|
||||
6. Wisselen naar `isLoopFinished()` — zien dat agent zelf stopt
|
||||
7. `hasToolCall("saveReport")` toevoegen — stoppen zodra opgeslagen
|
||||
|
||||
**Visual:** Console-log met alle stappen (toolName, input, output) — laat zien hoe agent zelf plant.
|
||||
|
||||
---
|
||||
|
||||
## Slide 12: Pauze
|
||||
### 15 minuten
|
||||
|
||||
---
|
||||
|
||||
## Slide 13: LIVE DEMO 3 — prepareStep
|
||||
### ~25 min
|
||||
|
||||
**Wat ik laat zien:**
|
||||
|
||||
**Use case 1 — Dynamic model:**
|
||||
- Default: `gpt-4o-mini` (snel + goedkoop)
|
||||
- Vanaf stap 5: `claude-sonnet-4.5` (beter reasoning)
|
||||
- Console-log per stap welk model
|
||||
|
||||
**Use case 2 — Fase-gebaseerde tools:**
|
||||
- Stap 0-3: alleen `webSearch` (oriëntatie)
|
||||
- Stap 4-7: alleen `readPage` (diepgang)
|
||||
- Stap 8+: alleen `saveReport` + `done` (afronden)
|
||||
- Zien: agent kan niet 'cheaten' door te vroeg op te slaan
|
||||
|
||||
**Use case 3 — Context-trimming:**
|
||||
- Na 15 messages: trim oude tool-results
|
||||
- System prompt + laatste 10 berichten
|
||||
- Voorkomt token-explosie bij lange agents
|
||||
|
||||
**Visual:** Timeline-strip met de drie fasen + model-switch.
|
||||
|
||||
---
|
||||
|
||||
## Slide 14: LIVE DEMO 4 — Done-tool + custom stop
|
||||
### ~20 min
|
||||
|
||||
**Wat ik laat zien:**
|
||||
|
||||
**Pattern 1 — Done-tool (no-execute):**
|
||||
```typescript
|
||||
done: tool({
|
||||
description: "Signaleer dat onderzoek klaar is.",
|
||||
inputSchema: z.object({
|
||||
finalReportId: z.number(),
|
||||
summary: z.string(),
|
||||
}),
|
||||
// GEEN execute — stopt de loop
|
||||
})
|
||||
```
|
||||
Met `toolChoice: "required"` forceer je dat agent ALTIJD een tool aanroept — en `done` is hoe-ie stopt.
|
||||
|
||||
**Pattern 2 — Custom stop op kosten:**
|
||||
```typescript
|
||||
const costLimit: StopCondition<typeof tools> = ({ steps }) => {
|
||||
const tokens = steps.reduce((sum, s) =>
|
||||
sum + (s.usage?.totalTokens ?? 0), 0);
|
||||
return tokens > 50_000;
|
||||
};
|
||||
```
|
||||
|
||||
**Pattern 3 — Plan-act-reflect loop:**
|
||||
- Stap 0: agent maakt plan (text)
|
||||
- Stap 1-N: voert plan uit (tools)
|
||||
- Eind: agent schrijft reflectie (text)
|
||||
|
||||
**Visual:** Plan-Act-Reflect ReAct-diagram.
|
||||
|
||||
---
|
||||
|
||||
## Slide 15: Wanneer agent vs tool-call vs workflow?
|
||||
### Niet alles hoeft autonoom
|
||||
|
||||
| Scenario | Aanpak |
|
||||
|----------|--------|
|
||||
| Vraag → 1 tool → antwoord | Plain tool-call |
|
||||
| Vraag → 2-5 tools → antwoord | `stopWhen: stepCountIs(5)` (Les 12) |
|
||||
| Onderzoek → 10-50 tools → rapport | **Agent** (Les 13) |
|
||||
| Productie pipeline met validatie | **Workflow** (explicit code) |
|
||||
| Wettelijk: must be reproducible | **Workflow** — geen agent |
|
||||
|
||||
**Wanneer GEEN agent gebruiken:**
|
||||
- Deterministisch nodig (audit, finance, juridisch)
|
||||
- Voorspelbare kosten
|
||||
- Latency-sensitive (< 1s antwoord)
|
||||
|
||||
**Wanneer WEL agent:**
|
||||
- Open-ended taken (research, planning, debugging)
|
||||
- Tot stand komen via meerdere stappen
|
||||
- Cost en latency mag wat hoger
|
||||
|
||||
**Quote uit AI SDK docs:** *"Agents are flexible and powerful, but non-deterministic. When you need reliable, repeatable outcomes, use workflow patterns."*
|
||||
|
||||
---
|
||||
|
||||
## Slide 16: Lesopdracht + Huiswerk
|
||||
### Bouw je eigen agent
|
||||
|
||||
**Lesopdracht (in-class, 30 min):**
|
||||
- Setup research-agent in eigen repo
|
||||
- Tavily account aanmaken + key in .env
|
||||
- Agent draaien met `stepCountIs(10)` en eerste eigen query stellen
|
||||
- Logs bekijken: hoeveel stappen, welke tools
|
||||
|
||||
**Huiswerk (voor Les 14):**
|
||||
- 4e tool toevoegen — `listReports` om opgeslagen rapporten op te halen
|
||||
- `prepareStep` toevoegen — minimaal 1 use case (model OF tools OF context)
|
||||
- Custom `StopCondition` schrijven (kosten OF specifieke conditie)
|
||||
- `AGENT.md` schrijven: tool-lijst + stop-strategie + 1 voorbeeld-run met logs
|
||||
|
||||
**Bonus:**
|
||||
- UI met live-step-rendering (elke stap als card)
|
||||
- Sub-agent: 2e agent die per pagina samenvat, hoofdagent gebruikt sub-agent als tool
|
||||
|
||||
---
|
||||
|
||||
## Slide 17: Volgende les + Afsluiting
|
||||
### Vragen?
|
||||
|
||||
**Vandaag gezien:**
|
||||
- Agent = LLM + tools + loop
|
||||
- `ToolLoopAgent` met `stopWhen` en `prepareStep`
|
||||
- 4 stop-condities: stepCountIs, hasToolCall, isLoopFinished, custom
|
||||
- prepareStep voor dynamic model / tools / context
|
||||
- Done-tool pattern voor forced tool calling
|
||||
- Wanneer wel/niet een agent
|
||||
|
||||
**Volgende les (Les 14): RAG + embeddings**
|
||||
- Semantic search op grote datasets
|
||||
- pgvector in Supabase
|
||||
- Embedding models — wat zijn vectors
|
||||
- RAG-pipeline: chunk → embed → retrieve → generate
|
||||
- Combo: RAG-tool in een agent — best of both worlds
|
||||
|
||||
**Daarna in deze leerlijn:**
|
||||
- Les 15-16: Testing + Deployment + Performance
|
||||
- Les 17-18: Eindopdracht-werkdagen + Pitch
|
||||
|
||||
**Vragen? Feedback?**
|
||||
|
||||
---
|
||||
|
||||
## Slide Summary
|
||||
|
||||
| # | Title | Type |
|
||||
|---|-------|------|
|
||||
| 1 | Title | Opening |
|
||||
| 2 | Terugblik | Recap |
|
||||
| 3 | Planning | 180-min |
|
||||
| 4 | Wat is een agent | Theorie |
|
||||
| 5 | Anatomie van de loop | Theorie |
|
||||
| 6 | ToolLoopAgent | Theorie |
|
||||
| 7 | Stop-condities | Theorie |
|
||||
| 8 | prepareStep | Theorie |
|
||||
| 9 | Wat we vandaag bouwen | Intro demo |
|
||||
| 10 | **LIVE DEMO 1** — Setup | Demo |
|
||||
| 11 | **LIVE DEMO 2** — stopWhen | Demo |
|
||||
| 12 | Pauze | Break |
|
||||
| 13 | **LIVE DEMO 3** — prepareStep | Demo |
|
||||
| 14 | **LIVE DEMO 4** — done + custom stop | Demo |
|
||||
| 15 | Wanneer wel/niet | Reflectie |
|
||||
| 16 | Lesopdracht + Huiswerk | Praktijk |
|
||||
| 17 | Afsluiting + Les 14 preview | Closing |
|
||||
|
||||
---
|
||||
|
||||
## Bronnen
|
||||
|
||||
- AI SDK Agents — Overview: https://ai-sdk.dev/docs/agents/overview
|
||||
- AI SDK Agents — Loop Control: https://ai-sdk.dev/docs/agents/loop-control
|
||||
- AI SDK Agents — Building Agents: https://ai-sdk.dev/docs/agents/building-agents
|
||||
- AI SDK Agents — Workflows: https://ai-sdk.dev/docs/agents/workflows
|
||||
- Tavily API: https://tavily.com/
|
||||
- Anthropic — Building effective agents: https://www.anthropic.com/research/building-effective-agents
|
||||
Reference in New Issue
Block a user