fix: update lessons on behalf of nova feedback
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# Les 13: AI Agents & Custom GPTs
|
||||
# Les 13: Prompt Engineering & Custom GPTs
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
**Deel 4: Advanced AI Features** (Les 13-18)
|
||||
|
||||
## Beschrijving
|
||||
Leer werken met AI agents en custom GPTs. Bouw je eigen gespecialiseerde AI assistenten en begrijp hoe agents autonome taken kunnen uitvoeren.
|
||||
Verdiep je in advanced prompt engineering en leer eigen AI assistenten maken met Custom GPTs en Claude Projects. Focus op no-code manieren om AI te personaliseren voor jouw workflow.
|
||||
|
||||
---
|
||||
|
||||
@@ -15,54 +15,51 @@ Leer werken met AI agents en custom GPTs. Bouw je eigen gespecialiseerde AI assi
|
||||
### Groepsdiscussie (15 min)
|
||||
Bespreek klassikaal de Cursor reflecties uit Les 12 - welke features werken het beste voor welke taken?
|
||||
|
||||
### Wat zijn AI Agents?
|
||||
### Advanced Prompt Engineering
|
||||
|
||||
**Definitie:** AI systemen die autonoom taken kunnen uitvoeren, niet alleen antwoorden geven.
|
||||
**Recap van basis technieken (Les 4):**
|
||||
- Zero-shot vs few-shot prompting
|
||||
- Chain-of-thought reasoning
|
||||
- Role prompting
|
||||
|
||||
**Verschil Chat vs Agent:**
|
||||
**Nieuwe technieken:**
|
||||
|
||||
| Aspect | Chat | Agent |
|
||||
|--------|------|-------|
|
||||
| Gedrag | Beantwoordt vragen | Voert taken uit |
|
||||
| Autonomie | Wacht op input | Kan zelf beslissingen nemen |
|
||||
| Tools | Alleen tekst | Kan tools gebruiken |
|
||||
| Voorbeeld | "Hoe maak ik X?" | "Maak X voor mij" |
|
||||
|
||||
---
|
||||
|
||||
### Claude Projects
|
||||
|
||||
**Wat is een Claude Project?**
|
||||
- Verzameling van context specifiek voor één doel
|
||||
- Blijft behouden over conversaties
|
||||
- Kan bestanden bevatten
|
||||
|
||||
**Wanneer gebruiken:**
|
||||
- Terugkerend werk aan hetzelfde project
|
||||
- Consistente coding style nodig
|
||||
- Documentatie die AI moet kennen
|
||||
|
||||
**Project aanmaken:**
|
||||
1. Ga naar claude.ai → Projects
|
||||
2. Klik "New Project"
|
||||
3. Voeg project knowledge toe (files, instructies)
|
||||
4. Start conversaties binnen het project
|
||||
|
||||
**Voorbeeld Project Instructions:**
|
||||
#### 1. Structured Output Prompting
|
||||
```
|
||||
Je bent een expert React/Next.js developer.
|
||||
Analyseer deze code en geef feedback in dit exacte format:
|
||||
|
||||
Technologie stack:
|
||||
- Next.js 14 met App Router
|
||||
- TypeScript strict mode
|
||||
- Tailwind CSS
|
||||
- Supabase voor backend
|
||||
## Samenvatting
|
||||
[1 zin over de code]
|
||||
|
||||
Coding conventions:
|
||||
- Functional components met TypeScript
|
||||
- Named exports (geen default exports)
|
||||
- Error handling met try/catch
|
||||
- Nederlandse comments in code
|
||||
## Sterke punten
|
||||
- [punt 1]
|
||||
- [punt 2]
|
||||
|
||||
## Verbeterpunten
|
||||
- [punt 1 met code voorbeeld]
|
||||
- [punt 2 met code voorbeeld]
|
||||
|
||||
## Prioriteit
|
||||
[Hoog/Medium/Laag]: [waarom]
|
||||
```
|
||||
|
||||
#### 2. Constraint-Based Prompting
|
||||
```
|
||||
Schrijf een React component met deze constraints:
|
||||
- Maximaal 50 regels code
|
||||
- Geen externe dependencies
|
||||
- TypeScript met strict types
|
||||
- Alleen Tailwind voor styling
|
||||
- Inclusief error handling
|
||||
```
|
||||
|
||||
#### 3. Iterative Refinement
|
||||
```
|
||||
Stap 1: "Schrijf een basis login form"
|
||||
Stap 2: "Voeg validatie toe"
|
||||
Stap 3: "Voeg loading states toe"
|
||||
Stap 4: "Voeg error handling toe"
|
||||
Stap 5: "Optimaliseer voor accessibility"
|
||||
```
|
||||
|
||||
---
|
||||
@@ -71,14 +68,14 @@ Coding conventions:
|
||||
|
||||
**Wat zijn Custom GPTs?**
|
||||
Gespecialiseerde ChatGPT versies met:
|
||||
- Specifieke instructies
|
||||
- Specifieke instructies (personality, expertise)
|
||||
- Eigen kennis (uploaded files)
|
||||
- Optioneel: Actions (API calls)
|
||||
|
||||
**Wanneer een Custom GPT maken:**
|
||||
- Repetitieve taken met dezelfde context
|
||||
- Specifieke expertise nodig
|
||||
- Delen met anderen
|
||||
- Delen met team of anderen
|
||||
|
||||
**Custom GPT maken:**
|
||||
1. Ga naar chat.openai.com/gpts
|
||||
@@ -103,57 +100,94 @@ Geef feedback in dit format:
|
||||
- ❌ Issue: [problemen die gefixed moeten worden]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Agent Capabilities
|
||||
|
||||
**Wat kunnen agents:**
|
||||
- Code schrijven en testen
|
||||
- Files aanmaken en bewerken
|
||||
- Terminal commands uitvoeren
|
||||
- Web searches doen
|
||||
- Meerdere stappen plannen
|
||||
|
||||
**Cursor als Agent:**
|
||||
- Composer mode plant en voert multi-file changes uit
|
||||
- @ mentions geven context
|
||||
- Tab completion voorspelt volgende stappen
|
||||
|
||||
**Claude als Agent (met MCP):**
|
||||
- Kan tools gebruiken
|
||||
- Filesystem access
|
||||
- Database queries
|
||||
- External APIs
|
||||
|
||||
---
|
||||
|
||||
### Best Practices voor Agent Instructies
|
||||
|
||||
**Wees specifiek:**
|
||||
```
|
||||
❌ "Help me met mijn project"
|
||||
✅ "Review de auth logic in src/lib/auth.ts en check voor security issues"
|
||||
**Voorbeeld: Project Assistant GPT**
|
||||
```
|
||||
Instructions:
|
||||
Je bent mijn persoonlijke development assistant voor [project naam].
|
||||
|
||||
**Geef constraints:**
|
||||
```
|
||||
❌ "Maak het beter"
|
||||
✅ "Refactor naar max 50 regels, behoud dezelfde functionaliteit"
|
||||
```
|
||||
Tech stack:
|
||||
- Next.js 14 met App Router
|
||||
- TypeScript strict mode
|
||||
- Tailwind CSS
|
||||
- Supabase voor backend
|
||||
|
||||
**Definieer output format:**
|
||||
```
|
||||
Geef je antwoord in dit format:
|
||||
1. Samenvatting (1 zin)
|
||||
2. Gevonden issues (bullet points)
|
||||
3. Voorgestelde fix (code)
|
||||
Je kent de volgende conventies:
|
||||
- Named exports (geen default exports)
|
||||
- Nederlandse comments
|
||||
- Error handling met try/catch
|
||||
|
||||
Wanneer ik code vraag:
|
||||
1. Vraag eerst om context als die ontbreekt
|
||||
2. Geef TypeScript code met types
|
||||
3. Voeg korte uitleg toe
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Prompt Templates
|
||||
### Claude Projects
|
||||
|
||||
**Code Generation Template:**
|
||||
**Wat is een Claude Project?**
|
||||
- Verzameling van context specifiek voor één doel
|
||||
- Blijft behouden over conversaties
|
||||
- Kan bestanden bevatten als knowledge base
|
||||
|
||||
**Wanneer gebruiken:**
|
||||
- Terugkerend werk aan hetzelfde project
|
||||
- Consistente coding style nodig
|
||||
- Documentatie die AI moet kennen
|
||||
|
||||
**Project aanmaken:**
|
||||
1. Ga naar claude.ai → Projects
|
||||
2. Klik "New Project"
|
||||
3. Voeg project knowledge toe (files, instructies)
|
||||
4. Start conversaties binnen het project
|
||||
|
||||
**Voorbeeld Project Instructions:**
|
||||
```
|
||||
Je bent een expert React/Next.js developer die mij helpt met [project].
|
||||
|
||||
Technologie stack:
|
||||
- Next.js 14 met App Router
|
||||
- TypeScript strict mode
|
||||
- Tailwind CSS
|
||||
- Supabase voor backend
|
||||
- React Query voor data fetching
|
||||
|
||||
Coding conventions:
|
||||
- Functional components met TypeScript
|
||||
- Named exports (geen default exports)
|
||||
- Error handling met try/catch
|
||||
- Nederlandse comments in code
|
||||
|
||||
Wanneer je code schrijft:
|
||||
- Gebruik altijd TypeScript types
|
||||
- Voeg JSDoc comments toe voor complexe functies
|
||||
- Denk aan edge cases en error handling
|
||||
```
|
||||
|
||||
**Project Knowledge toevoegen:**
|
||||
- Upload je belangrijkste files (schema, README, .cursorrules)
|
||||
- Upload voorbeeldcode die je stijl toont
|
||||
- Upload documentatie van libraries die je gebruikt
|
||||
|
||||
---
|
||||
|
||||
### Custom GPTs vs Claude Projects
|
||||
|
||||
| Aspect | Custom GPT | Claude Project |
|
||||
|--------|------------|----------------|
|
||||
| **Beschikbaar** | ChatGPT Plus ($20/maand) | Claude Pro ($20/maand) |
|
||||
| **Knowledge** | File uploads (tot 20 files) | File uploads (tot 200k tokens) |
|
||||
| **Delen** | Kan gepubliceerd worden | Alleen persoonlijk |
|
||||
| **Actions** | Ja (API calls) | Nee |
|
||||
| **Context window** | ~128k tokens | ~200k tokens |
|
||||
| **Beste voor** | Gedeelde tools, API integratie | Persoonlijke projecten, grote codebases |
|
||||
|
||||
---
|
||||
|
||||
### Prompt Templates Library
|
||||
|
||||
**Code Generation:**
|
||||
```
|
||||
Context: [beschrijf je project/stack]
|
||||
Taak: [wat moet er gemaakt worden]
|
||||
@@ -165,7 +199,7 @@ Constraints:
|
||||
Output: [gewenst format]
|
||||
```
|
||||
|
||||
**Debugging Template:**
|
||||
**Debugging:**
|
||||
```
|
||||
Error message:
|
||||
[plak error]
|
||||
@@ -180,18 +214,43 @@ Wat er gebeurt:
|
||||
[actueel gedrag]
|
||||
```
|
||||
|
||||
**Code Review:**
|
||||
```
|
||||
Review de volgende code op:
|
||||
1. Best practices voor [framework]
|
||||
2. Potentiële bugs
|
||||
3. Performance issues
|
||||
4. Security vulnerabilities
|
||||
|
||||
[plak code]
|
||||
|
||||
Geef feedback in format: ✅ Goed / ⚠️ Suggestie / ❌ Issue
|
||||
```
|
||||
|
||||
**Refactoring:**
|
||||
```
|
||||
Refactor deze code met de volgende doelen:
|
||||
- [doel 1, bijv. "verbeter leesbaarheid"]
|
||||
- [doel 2, bijv. "reduceer duplicatie"]
|
||||
|
||||
Behoud dezelfde functionaliteit.
|
||||
Leg uit wat je verandert en waarom.
|
||||
|
||||
[plak code]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
- ChatGPT Custom GPTs
|
||||
- Claude Projects
|
||||
- Cursor (Composer mode)
|
||||
- ChatGPT (Custom GPTs)
|
||||
- Claude (Projects)
|
||||
- Prompt template documenten
|
||||
|
||||
---
|
||||
|
||||
## Lesopdracht (2 uur)
|
||||
|
||||
### Bouw Je Eigen AI Assistant
|
||||
### Bouw Je Eigen AI Assistants
|
||||
|
||||
**Deel 1: Claude Project (45 min)**
|
||||
|
||||
@@ -220,7 +279,7 @@ Test dezelfde taak met beide:
|
||||
- Welke is beter voor welk doel?
|
||||
|
||||
### Deliverable
|
||||
- Claude Project URL
|
||||
- Claude Project URL of screenshot
|
||||
- Custom GPT (als je ChatGPT Plus hebt) of instructies doc
|
||||
- Vergelijkingsnotities
|
||||
|
||||
@@ -279,9 +338,8 @@ Schrijf reflectie (300 woorden):
|
||||
|
||||
## Leerdoelen
|
||||
Na deze les kan de student:
|
||||
- Het verschil uitleggen tussen chat en agent
|
||||
- Een Claude Project aanmaken en configureren
|
||||
- Een Custom GPT maken met specifieke instructies
|
||||
- Effectieve agent instructies schrijven
|
||||
- Prompt templates gebruiken voor consistente resultaten
|
||||
- De juiste AI assistant kiezen per taak
|
||||
- Advanced prompt engineering technieken toepassen (structured output, constraints, iterative refinement)
|
||||
- Een Custom GPT maken met specifieke instructies en knowledge
|
||||
- Een Claude Project opzetten met project context
|
||||
- De juiste tool kiezen (Custom GPT vs Claude Project) per use case
|
||||
- Een persoonlijke prompt library opbouwen en onderhouden
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Les 17: Vercel AI SDK - AI Features in je App
|
||||
# Les 17: Vercel AI SDK, Tool Calling & Agents
|
||||
|
||||
---
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
**Deel 4: Advanced AI Features** (Les 13-18)
|
||||
|
||||
## Beschrijving
|
||||
Bouw AI-powered features in je apps met de Vercel AI SDK. Leer hoe je chat interfaces, streaming responses en AI-gegenereerde content implementeert.
|
||||
Bouw AI-powered features in je apps met de Vercel AI SDK. Leer niet alleen chat interfaces bouwen, maar ook hoe AI externe data kan ophalen via Tool Calling en autonome taken kan uitvoeren als Agent.
|
||||
|
||||
---
|
||||
|
||||
@@ -21,21 +21,22 @@ Bespreek klassikaal de Cursor .cursorrules ervaringen uit Les 16 - welke regels
|
||||
- Streaming handmatig implementeren
|
||||
- Error handling
|
||||
- State management
|
||||
- Tool calling implementeren
|
||||
|
||||
**De oplossing:** Vercel AI SDK
|
||||
- Simpele React hooks
|
||||
- Simpele React hooks (`useChat`, `useCompletion`)
|
||||
- Built-in streaming
|
||||
- Provider-agnostic (OpenAI, Anthropic, etc.)
|
||||
- Edge-ready
|
||||
- **Tool calling out-of-the-box**
|
||||
- **Agent capabilities met `maxSteps`**
|
||||
|
||||
---
|
||||
|
||||
### Installatie & Setup
|
||||
|
||||
```bash
|
||||
npm install ai @ai-sdk/openai
|
||||
# of voor Anthropic:
|
||||
npm install ai @ai-sdk/anthropic
|
||||
npm install ai @ai-sdk/openai zod
|
||||
# zod is nodig voor tool parameter validatie
|
||||
```
|
||||
|
||||
**Environment variable:**
|
||||
@@ -46,9 +47,9 @@ OPENAI_API_KEY=sk-xxxxx
|
||||
|
||||
---
|
||||
|
||||
### Core Hooks
|
||||
### Deel 1: Basic Chat (Herhaling)
|
||||
|
||||
#### useChat - Voor Conversaties
|
||||
#### useChat Hook
|
||||
|
||||
```tsx
|
||||
'use client'
|
||||
@@ -76,45 +77,16 @@ export function ChatComponent() {
|
||||
placeholder="Type a message..."
|
||||
className="w-full p-2 border rounded"
|
||||
/>
|
||||
<button type="submit" disabled={isLoading}>
|
||||
{isLoading ? 'Sending...' : 'Send'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
#### useCompletion - Voor Single Completions
|
||||
#### Basic API Route
|
||||
|
||||
```tsx
|
||||
import { useCompletion } from 'ai/react'
|
||||
|
||||
export function SummaryComponent() {
|
||||
const { completion, input, handleInputChange, handleSubmit, isLoading } = useCompletion()
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<textarea
|
||||
value={input}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Paste text to summarize..."
|
||||
/>
|
||||
<button type="submit">Summarize</button>
|
||||
</form>
|
||||
{completion && <p>{completion}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### API Routes
|
||||
|
||||
**app/api/chat/route.ts:**
|
||||
```typescript
|
||||
// app/api/chat/route.ts
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { streamText } from 'ai'
|
||||
|
||||
@@ -123,7 +95,7 @@ export async function POST(req: Request) {
|
||||
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
system: 'You are a helpful cooking assistant. Suggest recipes based on ingredients.',
|
||||
system: 'You are a helpful assistant.',
|
||||
messages,
|
||||
})
|
||||
|
||||
@@ -131,151 +103,331 @@ export async function POST(req: Request) {
|
||||
}
|
||||
```
|
||||
|
||||
**Met custom system prompt:**
|
||||
```typescript
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
system: `You are a recipe assistant for the AI Recipe Generator app.
|
||||
|
||||
When the user provides ingredients:
|
||||
1. Suggest 2-3 recipes they could make
|
||||
2. List required additional ingredients (if any)
|
||||
3. Provide brief cooking instructions
|
||||
|
||||
Be concise and practical.`,
|
||||
messages,
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Streaming Responses
|
||||
### Deel 2: Tool Calling - AI + Externe Data
|
||||
|
||||
**Waarom streaming?**
|
||||
- Betere UX (user ziet direct resultaat)
|
||||
- Snellere perceived performance
|
||||
- Geen wachten op complete response
|
||||
**Het probleem met basic chat:**
|
||||
- AI kent alleen zijn training data
|
||||
- Geen toegang tot realtime informatie
|
||||
- Kan geen acties uitvoeren
|
||||
|
||||
**Hoe het werkt:**
|
||||
1. Server stuurt tokens één voor één
|
||||
2. Client rendert elke token direct
|
||||
3. User ziet "typing" effect
|
||||
**De oplossing: Tool Calling**
|
||||
- Definieer "tools" die AI kan aanroepen
|
||||
- AI besluit zelf wanneer een tool nodig is
|
||||
- Tool haalt data op → AI interpreteert resultaat
|
||||
|
||||
**Loading indicator:**
|
||||
```tsx
|
||||
{isLoading && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="animate-pulse">●</div>
|
||||
<span>AI is thinking...</span>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Integratie met Supabase
|
||||
|
||||
**Conversations opslaan:**
|
||||
#### Voorbeeld: Cocktail Advisor met TheCocktailDB
|
||||
|
||||
```typescript
|
||||
// Maak tabel in Supabase:
|
||||
// conversations: id, user_id, created_at
|
||||
// messages: id, conversation_id, role, content, created_at
|
||||
// app/api/chat/route.ts
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { streamText, tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
// Na elke message:
|
||||
async function saveMessage(conversationId: string, role: string, content: string) {
|
||||
await supabase.from('messages').insert({
|
||||
conversation_id: conversationId,
|
||||
role,
|
||||
content
|
||||
export async function POST(req: Request) {
|
||||
const { messages } = await req.json()
|
||||
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
system: `Je bent een cocktail expert.
|
||||
Gebruik de tools om cocktails te zoeken en recepten op te halen.
|
||||
Geef persoonlijk advies op basis van de resultaten.`,
|
||||
messages,
|
||||
tools: {
|
||||
// Tool 1: Zoek cocktails op ingrediënt
|
||||
searchByIngredient: tool({
|
||||
description: 'Zoek cocktails die een specifiek ingrediënt bevatten',
|
||||
parameters: z.object({
|
||||
ingredient: z.string().describe('Het ingrediënt om op te zoeken, bijv. "rum" of "vodka"')
|
||||
}),
|
||||
execute: async ({ ingredient }) => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/filter.php?i=${ingredient}`
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.drinks?.slice(0, 5) || []
|
||||
}
|
||||
}),
|
||||
|
||||
// Tool 2: Haal cocktail details op
|
||||
getCocktailDetails: tool({
|
||||
description: 'Haal het volledige recept van een cocktail op',
|
||||
parameters: z.object({
|
||||
cocktailId: z.string().describe('Het ID van de cocktail')
|
||||
}),
|
||||
execute: async ({ cocktailId }) => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${cocktailId}`
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.drinks?.[0] || null
|
||||
}
|
||||
}),
|
||||
|
||||
// Tool 3: Zoek non-alcoholische opties
|
||||
searchNonAlcoholic: tool({
|
||||
description: 'Zoek non-alcoholische cocktails/mocktails',
|
||||
parameters: z.object({}),
|
||||
execute: async () => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/filter.php?a=Non_Alcoholic`
|
||||
)
|
||||
const data = await res.json()
|
||||
return data.drinks?.slice(0, 5) || []
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return result.toDataStreamResponse()
|
||||
}
|
||||
```
|
||||
|
||||
**In je component:**
|
||||
```tsx
|
||||
const { messages, input, handleSubmit } = useChat({
|
||||
onFinish: async (message) => {
|
||||
await saveMessage(conversationId, message.role, message.content)
|
||||
}
|
||||
**Wat gebeurt er?**
|
||||
```
|
||||
User: "Ik heb rum en limoen, wat kan ik maken?"
|
||||
|
||||
AI denkt: "Ik moet zoeken op rum"
|
||||
→ Roept searchByIngredient({ ingredient: "rum" }) aan
|
||||
→ Krijgt: [{ name: "Mojito", id: "11000" }, { name: "Daiquiri", id: "11006" }, ...]
|
||||
|
||||
AI denkt: "Mojito klinkt goed met limoen, laat me het recept ophalen"
|
||||
→ Roept getCocktailDetails({ cocktailId: "11000" }) aan
|
||||
→ Krijgt: { name: "Mojito", ingredients: [...], instructions: "..." }
|
||||
|
||||
AI antwoordt: "Met rum en limoen kun je een heerlijke Mojito maken!
|
||||
Je hebt nog nodig: verse munt en suiker..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Deel 3: Agents - Autonome Multi-Step AI
|
||||
|
||||
**Van Tool Calling naar Agent:**
|
||||
- Tool calling = AI roept 1 tool aan, klaar
|
||||
- Agent = AI blijft tools aanroepen totdat de taak af is
|
||||
|
||||
**Het verschil is één parameter: `maxSteps`**
|
||||
|
||||
```typescript
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o-mini'),
|
||||
system: `Je bent een cocktail party planner.
|
||||
Plan een compleet menu met alle details.`,
|
||||
messages,
|
||||
tools: { /* ... tools ... */ },
|
||||
maxSteps: 8 // ← Agent mag 8 tool-calls doen
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
#### Voorbeeld: Party Planner Agent
|
||||
|
||||
### Error Handling
|
||||
```typescript
|
||||
// app/api/party-planner/route.ts
|
||||
import { openai } from '@ai-sdk/openai'
|
||||
import { streamText, tool } from 'ai'
|
||||
import { z } from 'zod'
|
||||
|
||||
```tsx
|
||||
const { messages, error, reload } = useChat()
|
||||
export async function POST(req: Request) {
|
||||
const { messages } = await req.json()
|
||||
|
||||
{error && (
|
||||
<div className="p-4 bg-red-100 text-red-700 rounded">
|
||||
<p>Something went wrong. Please try again.</p>
|
||||
<button onClick={reload}>Retry</button>
|
||||
</div>
|
||||
)}
|
||||
const result = streamText({
|
||||
model: openai('gpt-4o'), // Gebruik slimmer model voor agent taken
|
||||
system: `Je bent een professionele cocktail party planner.
|
||||
|
||||
Wanneer iemand een feest wil plannen:
|
||||
1. Zoek eerst cocktails die passen bij de wensen
|
||||
2. Haal recepten op van de beste opties
|
||||
3. Denk aan non-alcoholische alternatieven
|
||||
4. Geef een compleet overzicht met ingrediënten
|
||||
|
||||
Wees proactief en denk mee.`,
|
||||
messages,
|
||||
tools: {
|
||||
searchByIngredient: tool({
|
||||
description: 'Zoek cocktails met een ingrediënt',
|
||||
parameters: z.object({
|
||||
ingredient: z.string()
|
||||
}),
|
||||
execute: async ({ ingredient }) => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/filter.php?i=${ingredient}`
|
||||
)
|
||||
return res.json()
|
||||
}
|
||||
}),
|
||||
|
||||
getCocktailDetails: tool({
|
||||
description: 'Haal recept details op',
|
||||
parameters: z.object({
|
||||
cocktailId: z.string()
|
||||
}),
|
||||
execute: async ({ cocktailId }) => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/lookup.php?i=${cocktailId}`
|
||||
)
|
||||
return res.json()
|
||||
}
|
||||
}),
|
||||
|
||||
searchNonAlcoholic: tool({
|
||||
description: 'Zoek mocktails',
|
||||
parameters: z.object({}),
|
||||
execute: async () => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/filter.php?a=Non_Alcoholic`
|
||||
)
|
||||
return res.json()
|
||||
}
|
||||
}),
|
||||
|
||||
searchByCategory: tool({
|
||||
description: 'Zoek cocktails per categorie (Cocktail, Shot, Beer, etc.)',
|
||||
parameters: z.object({
|
||||
category: z.string()
|
||||
}),
|
||||
execute: async ({ category }) => {
|
||||
const res = await fetch(
|
||||
`https://www.thecocktaildb.com/api/json/v1/1/filter.php?c=${category}`
|
||||
)
|
||||
return res.json()
|
||||
}
|
||||
})
|
||||
},
|
||||
maxSteps: 10 // Agent kan tot 10 tool calls doen
|
||||
})
|
||||
|
||||
return result.toDataStreamResponse()
|
||||
}
|
||||
```
|
||||
|
||||
**Agent in actie:**
|
||||
```
|
||||
User: "Plan cocktails voor mijn verjaardagsfeest.
|
||||
15 mensen, een paar drinken geen alcohol,
|
||||
we houden van citrus smaken."
|
||||
|
||||
Agent stappen:
|
||||
1. searchByIngredient("lemon") → 12 cocktails
|
||||
2. searchByIngredient("lime") → 15 cocktails
|
||||
3. searchByIngredient("orange") → 10 cocktails
|
||||
4. searchNonAlcoholic() → 8 mocktails
|
||||
5. getCocktailDetails("11000") → Mojito recept
|
||||
6. getCocktailDetails("11007") → Margarita recept
|
||||
7. getCocktailDetails("12162") → Virgin Piña Colada recept
|
||||
8. getCocktailDetails("12316") → Lemonade recept
|
||||
|
||||
Output: Compleet party plan met:
|
||||
- 3 alcoholische cocktails met citrus
|
||||
- 2 mocktails voor niet-drinkers
|
||||
- Gecombineerde ingrediëntenlijst
|
||||
- Tips voor bereiding
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Cost Management
|
||||
### Gratis APIs voor Projecten
|
||||
|
||||
**Model keuze:**
|
||||
| Model | Kosten | Gebruik voor |
|
||||
|-------|--------|--------------|
|
||||
| gpt-4o-mini | Goedkoop | Meeste taken |
|
||||
| gpt-4o | Duur | Complexe reasoning |
|
||||
| claude-3-haiku | Goedkoop | Simpele taken |
|
||||
| claude-3-sonnet | Medium | Balans |
|
||||
| API | Data | URL | Auth |
|
||||
|-----|------|-----|------|
|
||||
| TheCocktailDB | 636 cocktails, recepten | thecocktaildb.com/api.php | Geen (key=1) |
|
||||
| TheMealDB | 597 recepten, ingrediënten | themealdb.com/api.php | Geen (key=1) |
|
||||
| Open Trivia DB | 4000+ quiz vragen | opentdb.com/api_config.php | Geen |
|
||||
| REST Countries | Landen data | restcountries.com | Geen |
|
||||
| Open Library | Boeken data | openlibrary.org/developers | Geen |
|
||||
|
||||
**Bespaartips:**
|
||||
1. Gebruik gpt-4o-mini als default
|
||||
2. Korte system prompts
|
||||
3. Beperk conversation history
|
||||
4. Caching waar mogelijk
|
||||
---
|
||||
|
||||
### Best Practices
|
||||
|
||||
**Tool Design:**
|
||||
```typescript
|
||||
// ✅ Goed: Specifieke, duidelijke tools
|
||||
searchByIngredient: tool({
|
||||
description: 'Zoek cocktails die een specifiek ingrediënt bevatten',
|
||||
// ...
|
||||
})
|
||||
|
||||
// ❌ Slecht: Vage tool
|
||||
search: tool({
|
||||
description: 'Zoek iets',
|
||||
// ...
|
||||
})
|
||||
```
|
||||
|
||||
**Agent System Prompts:**
|
||||
```typescript
|
||||
// ✅ Goed: Geef duidelijke instructies
|
||||
system: `Je bent een cocktail expert.
|
||||
|
||||
Wanneer je een vraag krijgt:
|
||||
1. Zoek eerst relevante cocktails
|
||||
2. Haal details op van de beste matches
|
||||
3. Geef persoonlijk advies
|
||||
|
||||
Wees proactief en denk mee met de gebruiker.`
|
||||
|
||||
// ❌ Slecht: Te vaag
|
||||
system: `Je bent een assistent.`
|
||||
```
|
||||
|
||||
**Error Handling in Tools:**
|
||||
```typescript
|
||||
execute: async ({ ingredient }) => {
|
||||
try {
|
||||
const res = await fetch(`...`)
|
||||
if (!res.ok) {
|
||||
return { error: 'Kon geen cocktails vinden' }
|
||||
}
|
||||
return res.json()
|
||||
} catch (error) {
|
||||
return { error: 'API niet beschikbaar' }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Tools
|
||||
- Vercel AI SDK (`ai` package)
|
||||
- Zod (parameter validatie)
|
||||
- Next.js API Routes
|
||||
- OpenAI API / Anthropic API
|
||||
- Cursor
|
||||
- Supabase
|
||||
- Externe APIs (TheCocktailDB, TheMealDB, etc.)
|
||||
|
||||
---
|
||||
|
||||
## Lesopdracht (2 uur)
|
||||
|
||||
### Bouw een AI Chat Component
|
||||
### Bouw een AI Agent met Externe Data
|
||||
|
||||
**Deel 1: Setup (20 min)**
|
||||
1. `npm install ai @ai-sdk/openai`
|
||||
**Deel 1: Setup (15 min)**
|
||||
1. `npm install ai @ai-sdk/openai zod`
|
||||
2. Voeg `OPENAI_API_KEY` toe aan `.env.local`
|
||||
3. Maak `app/api/chat/route.ts`
|
||||
3. Kies je API: TheCocktailDB of TheMealDB
|
||||
|
||||
**Deel 2: Basic Chat (40 min)**
|
||||
1. Maak `components/Chat.tsx`
|
||||
2. Implementeer `useChat` hook
|
||||
3. Bouw chat UI met Tailwind
|
||||
4. Test streaming werkt
|
||||
**Deel 2: Basic Tool Calling (45 min)**
|
||||
1. Maak `/api/chat/route.ts`
|
||||
2. Implementeer 2 tools:
|
||||
- Zoek op ingrediënt
|
||||
- Haal details op
|
||||
3. Test: "Wat kan ik maken met [ingrediënt]?"
|
||||
|
||||
**Deel 3: System Prompt (30 min)**
|
||||
1. Schrijf system prompt voor je eindproject:
|
||||
- Recipe Generator: cooking assistant
|
||||
- Budget Buddy: financial advisor
|
||||
- Travel Planner: travel expert
|
||||
2. Test met relevante vragen
|
||||
**Deel 3: Agent met maxSteps (45 min)**
|
||||
1. Voeg `maxSteps: 5` toe
|
||||
2. Voeg een 3e tool toe (bijv. zoek per categorie)
|
||||
3. Verbeter je system prompt voor agent gedrag
|
||||
4. Test: "Help me een menu plannen voor..."
|
||||
|
||||
**Deel 4: Supabase Integratie (30 min)**
|
||||
1. Maak `messages` tabel
|
||||
2. Sla berichten op met `onFinish`
|
||||
3. Laad history bij page load
|
||||
**Deel 4: Frontend (15 min)**
|
||||
1. Bouw chat UI met `useChat`
|
||||
2. Voeg loading indicator toe
|
||||
3. Test de complete flow
|
||||
|
||||
### Deliverable
|
||||
- Werkende AI chat met streaming
|
||||
- Custom system prompt
|
||||
- Messages opgeslagen in Supabase
|
||||
- Werkende agent met minimaal 3 tools
|
||||
- Chat interface
|
||||
- Screenshot van agent die meerdere tools aanroept
|
||||
|
||||
---
|
||||
|
||||
@@ -283,50 +435,43 @@ const { messages, error, reload } = useChat()
|
||||
|
||||
### Bouw AI Feature voor Eindproject
|
||||
|
||||
**Deel 1: Core AI Feature (1 uur)**
|
||||
**Deel 1: Agent Design (30 min)**
|
||||
|
||||
Implementeer de AI chat die past bij je eindproject:
|
||||
Plan je agent voor de eindopdracht:
|
||||
- Welke externe API gebruik je?
|
||||
- Welke tools heeft je agent nodig? (minimaal 3)
|
||||
- Wat is de typische flow?
|
||||
|
||||
| Project | AI Feature |
|
||||
|---------|-----------|
|
||||
| Recipe Generator | "Wat kan ik maken met kip en rijst?" |
|
||||
| Budget Buddy | "Analyseer mijn uitgaven deze maand" |
|
||||
| Travel Planner | "Plan een weekend Barcelona" |
|
||||
Documenteer in `docs/AI-DECISIONS.md`
|
||||
|
||||
- Custom system prompt
|
||||
- Context uit je database meegeven
|
||||
**Deel 2: Implementatie (1 uur)**
|
||||
|
||||
**Deel 2: UX Polish (30 min)**
|
||||
Bouw de agent voor je eindproject:
|
||||
- Minimaal 3 tools
|
||||
- `maxSteps` van minimaal 3
|
||||
- Goede error handling
|
||||
- Relevante system prompt
|
||||
|
||||
Voeg toe:
|
||||
- Streaming indicator
|
||||
- Suggested prompts / quick actions
|
||||
- Copy response button
|
||||
- Clear chat button
|
||||
- Error handling
|
||||
**Deel 3: Integratie (30 min)**
|
||||
|
||||
**Deel 3: Documentatie (30 min)**
|
||||
|
||||
Maak `docs/AI-FEATURE.md`:
|
||||
- Welke AI feature heb je gebouwd?
|
||||
- Wat doet de system prompt?
|
||||
- Hoe integreert het met Supabase?
|
||||
- Welke model keuzes heb je gemaakt?
|
||||
Combineer met Supabase:
|
||||
- Sla user preferences op
|
||||
- Geef preferences mee als context aan agent
|
||||
- Sla conversation history op
|
||||
|
||||
### Deliverable
|
||||
- AI feature in eindproject
|
||||
- Deployed preview
|
||||
- AI-FEATURE.md documentatie
|
||||
- Werkende agent in eindproject
|
||||
- `docs/AI-DECISIONS.md` met agent design
|
||||
- Minimaal 5 prompts in `PROMPT-LOG.md`
|
||||
|
||||
---
|
||||
|
||||
## Leerdoelen
|
||||
Na deze les kan de student:
|
||||
- Vercel AI SDK installeren en configureren
|
||||
- `useChat` en `useCompletion` hooks gebruiken
|
||||
- Streaming responses implementeren
|
||||
- API routes opzetten voor AI providers
|
||||
- Custom system prompts schrijven
|
||||
- Chat history opslaan in Supabase
|
||||
- Error handling en loading states implementeren
|
||||
- Kostenbewust omgaan met AI APIs
|
||||
- Tools definiëren met Zod parameters
|
||||
- Tool Calling implementeren voor externe API integratie
|
||||
- Agents bouwen met `maxSteps` voor autonome taken
|
||||
- De juiste aanpak kiezen (basic chat vs tool calling vs agent)
|
||||
- Error handling implementeren in tools
|
||||
- Gratis externe APIs integreren in AI features
|
||||
|
||||
Reference in New Issue
Block a user