fix: lessons

This commit is contained in:
2026-06-07 15:15:11 +02:00
parent 83779e0d7a
commit 4f13aadc1a
121 changed files with 2038 additions and 23690 deletions

View File

@@ -1,330 +1,77 @@
# Les 14: AI Chat Interface & Streaming
# Les 14: Agents — LLM in een loop met tools
> ✅ **Deze les is volledig uitgewerkt**
## Lesmateriaal
- [Slide Overzicht](../Les14-Agents/Les14-Slide-Overzicht.md)
- [Docenttekst](../Les14-Agents/Les14-Docenttekst.md)
- [Lesstof](../Les14-Agents/Les14-Lesstof.md)
- [Lesopdracht](../Les14-Agents/Les14-Lesopdracht.md)
- [Huiswerk](../Les14-Agents/Les14-Huiswerk.md)
---
## Hoofdstuk
**Deel 4: Advanced AI & Deployment** (Les 13-18)
## Deel
**Deel 3 — AI Development** (Les 11-16)
## Beschrijving
Bouwen van professionele chat interfaces met streaming responses. Message rendering, markdown support, error handling, loading states, en UX patterns voor AI-powered features.
We gaan een stap verder dan multi-step tool calling. Een agent kan 20-50 stappen autonoom uitvoeren, eigen plannen maken en dynamisch bijsturen. We bouwen een research-agent from scratch met `ToolLoopAgent`, `stopWhen` en `prepareStep`.
---
## Te Behandelen (~45 min)
## Te Behandelen
- Chat UI patterns en best practices
- useChat hook deep dive (state, loading, error)
- Streaming response handling en real-time updates
- Message rendering strategies en optimizations
- Markdown rendering in chat messages
- Error handling en error boundaries
- Loading states en skeleton loaders
- User input validation and sanitization
- Accessibility in chat interfaces (ARIA labels)
- Message persistence (localStorage of database)
- Performance optimization
### Theorie
- Wat is een agent eigenlijk? (LLM + loop + tools + stopconditie)
- Wanneer wel/niet een agent gebruiken
- AI SDK v6 agent-primitives: `ToolLoopAgent`, `stopWhen`, `prepareStep`
- `prepareStep`: dynamisch model + tools per stap wisselen
- Cost & latency van agents — wat moet je weten?
---
### useChat Hook Deep Dive
**State management met useChat:**
```typescript
'use client'
import { useChat } from 'ai/react'
export function ChatComponent() {
const {
messages, // All messages in conversation
input, // Current input text
handleInputChange, // Update input
handleSubmit, // Send message
isLoading, // Is AI responding?
error, // Any errors?
} = useChat({
api: '/api/chat',
initialMessages: [], // Optional: pre-load messages
})
return (
<>
<div>
{messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
{isLoading && <div>AI is thinking...</div>}
{error && <div>Error: {error.message}</div>}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>Send</button>
</form>
</>
)
}
```
---
### Message Rendering Patterns
**Basic pattern:**
```typescript
<div className="space-y-4">
{messages.map((msg) => (
<div
key={msg.id}
className={msg.role === 'user' ? 'ml-auto' : 'mr-auto'}
>
<div className="bg-gray-200 p-3 rounded">
{msg.content}
</div>
</div>
))}
</div>
```
**With markdown rendering:**
```typescript
import ReactMarkdown from 'react-markdown'
<div className="bg-gray-200 p-3 rounded">
<ReactMarkdown>{msg.content}</ReactMarkdown>
</div>
```
**With message types:**
```typescript
{messages.map((msg) => (
<div key={msg.id} className={msg.role === 'user' ? 'user-message' : 'ai-message'}>
{msg.content}
</div>
))}
```
---
### Error Handling
**Structured error handling:**
```typescript
try {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ messages }),
})
if (!response.ok) {
throw new Error(`API error: ${response.status}`)
}
// Handle streaming...
} catch (error) {
console.error('Chat error:', error)
setError({
message: 'Failed to send message',
code: error instanceof Error ? error.message : 'unknown'
})
}
```
**Error boundary:**
```typescript
<ErrorBoundary fallback={<div>Chat error occurred</div>}>
<ChatComponent />
</ErrorBoundary>
```
---
### Loading States
**Skeleton loader:**
```typescript
function MessageSkeleton() {
return (
<div className="animate-pulse">
<div className="bg-gray-300 h-4 rounded w-48 mb-2" />
<div className="bg-gray-300 h-4 rounded w-64" />
</div>
)
}
{isLoading && <MessageSkeleton />}
```
---
### Input Validation
**Validate before sending:**
```typescript
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
// Trim whitespace
const trimmedInput = input.trim()
// Validate non-empty
if (!trimmedInput) {
setError('Message cannot be empty')
return
}
// Validate length
if (trimmedInput.length > 1000) {
setError('Message too long (max 1000 chars)')
return
}
// Send message
handleSubmit(e)
}
```
---
### Message Persistence
**Save to localStorage:**
```typescript
const [messages, setMessages] = useState(() => {
const saved = localStorage.getItem('chat_history')
return saved ? JSON.parse(saved) : []
})
// Save whenever messages change
useEffect(() => {
localStorage.setItem('chat_history', JSON.stringify(messages))
}, [messages])
```
**Save to database:**
```typescript
const saveMessage = async (message: Message) => {
await fetch('/api/messages', {
method: 'POST',
body: JSON.stringify({
content: message.content,
role: message.role,
userId: user.id,
}),
})
}
```
### Live demo's
1. Setup nieuwe research-agent (los project)
2. Tools: webSearch, fetchPage, summarize, writeReport
3. `stopWhen: stepCountIs(20)` + `hasToolCall("writeReport")`
4. `prepareStep` om Sonnet te gebruiken voor summarize, Haiku voor search
5. Live: vraag de agent een onderwerp te onderzoeken
---
## Tools
- Vercel AI SDK
- React Markdown
- Cursor
- TypeScript
- Vercel AI SDK v6 (`ToolLoopAgent`, `stopWhen`, `prepareStep`)
- Tavily of Exa API voor web search
- Anthropic + OpenAI providers
---
## Lesopdracht (2 uur, klassikaal)
## Lesopdracht (in-class, 60 min)
### Build Professional Chat Interface
Studenten kijken mee. In-class oefening:
- Voeg een eigen tool toe aan de research-agent
- Tweak de stopconditie: laat de agent na 5 minuten stoppen
- Bekijk de trace: welke stappen koos de agent?
**Groepsdiscussie (15 min):**
Bespreek klassikaal de Vercel AI SDK ervaringen uit Les 13 - welke tool calling patterns werkten goed?
**Deel 1: Chat Component (45 min)**
Build components/ChatInterface.tsx:
1. Use useChat hook
2. Render messages with proper styling
3. User vs AI message styling
4. Input form with validation
5. Tailwind + shadcn/ui components
**Deel 2: Markdown & Error Handling (30 min)**
1. Install react-markdown: `npm install react-markdown`
2. Render AI responses with markdown
3. Add error boundary
4. Show error messages to user
5. Proper loading states
**Deel 3: UX Improvements (30 min)**
1. Auto-scroll to latest message
2. Disable input while loading
3. Show message count/token usage
4. Add clear chat history button
5. Save messages to localStorage
**Deel 4: Testing (15 min)**
Test chat interface locally with various inputs and error scenarios.
### Deliverable
- Werkende chat interface component
- Markdown rendering working
- Error handling implemented
- LocalStorage persistence
- GitHub commit with chat UI
**Inleveren:** Screenshot van werkende agent-loop.
---
## Huiswerk (2 uur)
## Huiswerk (take-home)
### Integrate Chat into Your Project
Bouw je eigen agent voor een eigen use-case:
- Minimaal 3 tools
- Slimme stop-conditie
- `prepareStep` voor model-routing
- Reflectie 500 woorden: kan dit ook met gewone tool calling?
**Deel 1: Project Integration (1 uur)**
1. Add chat component to your app
2. Connect to your API route with tools
3. Style to match your design
4. Test with actual tools/integrations
5. Fix any bugs
**Deel 2: Enhanced Features (30 min)**
Add one of these:
- Message copy button
- Regenerate response option
- Clear history confirmation
- Export chat history
- Message timestamps
**Deel 3: Performance & Polish (30 min)**
1. Optimize re-renders (useMemo, useCallback)
2. Virtual scrolling for long chats
3. Better accessibility (keyboard nav)
4. Mobile responsive tweaks
5. Update docs/AI-DECISIONS.md
### Deliverable
- Chat fully integrated in project
- Enhanced features implemented
- Performance optimized
- GitHub commits with improvements
**Inleveren:** GitHub URL via Teams.
---
## Leerdoelen
Na deze les kan de student:
- useChat hook volledig begrijpen en gebruiken
- Professionele chat UI patterns implementeren
- Markdown rendering in chat messages
- Error handling en error boundaries toepassen
- Loading states en skeletons bouwen
- User input valideren en sanitizen
- Message persistence (localStorage/DB)
- Accessibility in chat interfaces verbeteren
- Performance optimizations toepassen
- Complete chat feature in Next.js app integreren
- Het verschil uitleggen tussen tool calling en een agent
- Een agent opzetten met `ToolLoopAgent` en custom tools
- Een doordachte stopconditie schrijven met `stopWhen`
- Modellen dynamisch routen per stap via `prepareStep`
- Inschatten wanneer een agent de juiste oplossing is