443 lines
13 KiB
Markdown
443 lines
13 KiB
Markdown
# Les 15 — 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 14 — RAG + Embeddings
|
|
**Aansluit op:** Les 16 — MCP servers
|
|
|
|
---
|
|
|
|
## Slide 1: Title
|
|
### Les 15 — Agents
|
|
|
|
**Visual:**
|
|
- Background: CREAM
|
|
- "Les 15" 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 15: 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 15) |
|
|
| 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 16): MCP — Model Context Protocol**
|
|
- 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
|