AI-agenten · Les

Feedback verzamelen en opslaan

Expliciete beoordelingen en impliciete gedragssignalen uit interacties met agents vastleggen.

Les 1 van 413 stappen

Feedback verzamelen en opslaan is een gratis AI-agenten-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI-agenten. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI-agenten bevat in totaal 4 lessen.

Waarom agenten feedback nodig hebben

Een agent die nooit feedback ontvangt, blijft steken in zijn oorspronkelijke toestand — die kan niet verder verbeteren dan de initiële training. Feedback sluit de lus tussen wat de agent doet en wat gebruikers daadwerkelijk willen.

Twee categorieën zijn het belangrijkst: expliciete feedback (de gebruiker beoordeelt de uitvoer bewust) en impliciete feedback (gedrag van de gebruiker geeft de kwaliteit aan zonder dat die dat expliciet zegt).

Expliciete feedback: duimpjes omhoog/omlaag

De eenvoudigste vorm: een binair signaal na elke reactie. Makkelijk te verzamelen en op te slaan, maar met weinig informatiedichtheid.

Implementatiepatroon: bied na de reactie van de agent een feedbackvraag aan en leg het resultaat vast naast de ID van de gespreksbeurt.

import uuid
from datetime import datetime

def collect_thumbs_feedback(turn_id: str, rating: str) -> dict:
    """rating: 'up' or 'down'"""
    assert rating in ('up', 'down'), 'Invalid rating'
    record = {
        'feedback_id': str(uuid.uuid4()),
        'turn_id': turn_id,
        'type': 'thumbs',
        'value': 1 if rating == 'up' else -1,
        'created_at': datetime.utcnow().isoformat()
    }
    return record

feedback = collect_thumbs_feedback('turn_abc123', 'up')
print(feedback)

Expliciete feedback: sterbeoordelingen

Een beoordeling van 1–5 sterren biedt meer detail dan duimpjes. Hiermee kun je onderscheid maken tussen net aanvaardbaar (2 sterren) en uitstekend (5 sterren), wat nuttig is voor een betere signaalkwaliteit bij fijnafstemming.

Normaliseer de waarde naar 0–1 voordat je die in trainingspijplijnen gebruikt.

def collect_star_feedback(turn_id: str, stars: int) -> dict:
    if not 1 <= stars <= 5:
        raise ValueError('Stars must be between 1 and 5')
    return {
        'turn_id': turn_id,
        'type': 'star',
        'raw_value': stars,
        'normalized': (stars - 1) / 4.0  # maps 1->0.0, 5->1.0
    }

fb = collect_star_feedback('turn_xyz456', 4)
print(fb)
# {'turn_id': 'turn_xyz456', 'type': 'star', 'raw_value': 4, 'normalized': 0.75}

Expliciete feedback: correcties in vrije tekst

Feedback in vrije tekst is het rijkste signaal. De gebruiker schrijft precies wat die wilde: 'De samenvatting was te lang', 'Je hebt het belangrijkste punt gemist', 'Verkeerde valuta — ik vroeg om EUR'.

Sla correcties gekoppeld aan de oorspronkelijke uitvoer op, zodat je later paren (slechte uitvoer → gecorrigeerde uitvoer) kunt maken voor begeleide fijnafstemming.

def collect_correction_feedback(
    turn_id: str,
    original_output: str,
    corrected_output: str,
    user_note: str = ''
) -> dict:
    return {
        'turn_id': turn_id,
        'type': 'correction',
        'original': original_output,
        'corrected': corrected_output,
        'user_note': user_note
    }

fb = collect_correction_feedback(
    'turn_789',
    'The capital of Australia is Sydney.',
    'The capital of Australia is Canberra.',
    'Sydney is the largest city but not the capital.'
)
print(fb)

Impliciete feedback: signaal voor opnieuw vragen

Wanneer een gebruiker onmiddellijk dezelfde vraag in andere bewoordingen opnieuw stelt, is dat een sterk impliciet signaal dat het vorige antwoord onjuist of onvoldoende was. Je hoeft de gebruiker niets te laten aanklikken — het gedrag zelf is het signaal.

from datetime import datetime, timedelta

def detect_re_ask(
    current_msg: str,
    conversation_history: list,
    similarity_threshold: float = 0.7,
    window_seconds: int = 120
) -> bool:
    """
    Returns True if the current message is semantically similar
    to a recent message, suggesting dissatisfaction.
    """
    now = datetime.utcnow()
    for turn in conversation_history[-5:]:
        age = (now - turn['timestamp']).seconds
        if age <= window_seconds and turn['role'] == 'user':
            # In production: use embedding cosine similarity
            if simple_similarity(current_msg, turn['content']) >= similarity_threshold:
                return True
    return False

def simple_similarity(a: str, b: str) -> float:
    words_a = set(a.lower().split())
    words_b = set(b.lower().split())
    if not words_a or not words_b:
        return 0.0
    return len(words_a & words_b) / len(words_a | words_b)

if __name__ == '__main__':
    now = datetime.utcnow()
    history = [
        {'role': 'user', 'content': 'How do I reset my password', 'timestamp': now - timedelta(seconds=30)},
    ]
    result = detect_re_ask('How do I reset my password please', history)
    print('Re-ask detected:', result)

Impliciete feedback: signaal van uitvoerbewerking

Als de agent tekst genereert en de gebruiker die bewerkt voordat die de tekst gebruikt, is het verschil tussen de oorspronkelijke en de bewerkte tekst impliciete feedback. De bewerkte versie is wat de gebruiker daadwerkelijk wilde.

Dit komt vaak voor bij schrijfassistenten, codegeneratoren en e-mailopstellers.

import difflib

def extract_edit_feedback(original: str, edited: str) -> dict:
    differ = difflib.unified_diff(
        original.splitlines(),
        edited.splitlines(),
        lineterm=''
    )
    diff_lines = list(differ)
    edit_ratio = difflib.SequenceMatcher(None, original, edited).ratio()
    return {
        'type': 'edit',
        'original': original,
        'edited': edited,
        'edit_distance': 1.0 - edit_ratio,  # 0=unchanged, 1=fully rewritten
        'diff': '\n'.join(diff_lines)
    }

fb = extract_edit_feedback(
    'Dear John, I am writing to inform you...',
    'Hi John, Just a quick note...'
)
print(f"Edit distance: {fb['edit_distance']:.2f}")

Opslagschema voor feedback

Alle feedbacktypen delen een gemeenschappelijk schema met typegebonden gegevensvelden. Eén tabel met een type-discriminator en een payload-JSON-kolom houdt query's eenvoudig en ondersteunt tegelijkertijd elk feedbacktype.

# SQL schema for feedback storage

CREATE_TABLE_SQL = '''
CREATE TABLE agent_feedback (
    id          UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    session_id  TEXT NOT NULL,
    turn_id     TEXT NOT NULL,
    agent_id    TEXT NOT NULL,
    type        TEXT NOT NULL CHECK (type IN ('thumbs','star','correction','re_ask','edit')),
    value       FLOAT,           -- numeric signal: +1/-1, 0-1, edit distance
    payload     JSONB,           -- type-specific data
    created_at  TIMESTAMPTZ DEFAULT now()
);

CREATE INDEX ON agent_feedback (agent_id, created_at);
CREATE INDEX ON agent_feedback (type);
'''

# Example insert
INSERT_SQL = '''
INSERT INTO agent_feedback (session_id, turn_id, agent_id, type, value, payload)
VALUES ($1, $2, $3, $4, $5, $6)
'''

if __name__ == '__main__':
    print('Feedback table schema:')
    print(CREATE_TABLE_SQL)
    print('Insert statement:')
    print(INSERT_SQL)

Een feedbackverzamelingsklasse schrijven

Door alle feedbackverzameling achter één klasse te centraliseren, blijft de rest van de codebasis overzichtelijk. De verzamelaar verwerkt het verwijderen van duplicaten, het bundelen en asynchrone schrijfacties, zodat feedback nooit de hoofdverwerkingslus van de agent blokkeert.

import asyncio
from collections import deque
from datetime import datetime

class FeedbackCollector:
    def __init__(self, agent_id: str, flush_interval: int = 30):
        self.agent_id = agent_id
        self.buffer: deque = deque(maxlen=1000)
        self.flush_interval = flush_interval

    def record(self, turn_id: str, fb_type: str, value: float, payload: dict):
        self.buffer.append({
            'turn_id': turn_id,
            'agent_id': self.agent_id,
            'type': fb_type,
            'value': value,
            'payload': payload,
            'created_at': datetime.utcnow().isoformat()
        })

    async def flush(self, db_client):
        while self.buffer:
            record = self.buffer.popleft()
            await db_client.insert('agent_feedback', record)

    async def start_auto_flush(self, db_client):
        while True:
            await asyncio.sleep(self.flush_interval)
            await self.flush(db_client)

if __name__ == '__main__':
    fc = FeedbackCollector(agent_id='agent-1')
    fc.record('turn-1', 'thumbs', 1.0, {'comment': 'Great answer'})
    fc.record('turn-2', 'thumbs', -1.0, {'comment': 'Wrong ticker'})
    print(f'Buffered {len(fc.buffer)} feedback records:')
    for rec in fc.buffer:
        print(' -', rec['type'], rec['value'], rec['payload'])

Feedback samenvoegen voor analyse

Ruwe feedbackrecords moeten worden samengevoegd voordat ze bruikbaar zijn voor beslissingen over verbetering. Veelgebruikte samenvoegingen zijn: goedkeuringspercentage per intentietype, correctiepercentage in de loop der tijd en uitvoercategorieën die het vaakst worden bewerkt.

from collections import defaultdict
from statistics import mean

def aggregate_feedback(records: list) -> dict:
    by_type = defaultdict(list)
    for r in records:
        by_type[r['type']].append(r['value'])

    summary = {}
    if 'thumbs' in by_type:
        values = by_type['thumbs']
        summary['approval_rate'] = (values.count(1) / len(values)) * 100
    if 'star' in by_type:
        summary['avg_star'] = mean(by_type['star']) * 4 + 1  # denormalize
    if 'edit' in by_type:
        summary['avg_edit_distance'] = mean(by_type['edit'])
    if 'correction' in by_type:
        summary['correction_count'] = len(by_type['correction'])
    return summary

records = [
    {'type': 'thumbs', 'value': 1},
    {'type': 'thumbs', 'value': -1},
    {'type': 'star', 'value': 0.75},
    {'type': 'edit', 'value': 0.3}
]
print(aggregate_feedback(records))

Privacy en toestemming bij het verzamelen van feedback

Feedback bevat vaak gevoelige gebruikersgegevens. Goede werkwijzen zijn: vraag expliciete toestemming voordat je correcties in vrije tekst vastlegt, anonimiseer sessie-ID's vóór analyse, stel bewaarbeperkingen in (bijvoorbeeld verwijderen na 90 dagen) en registreer nooit PII in feedbackgegevens.

import hashlib
import re

def anonymise_feedback(record: dict) -> dict:
    """Anonymise feedback record before storing for training."""
    safe = record.copy()

    # Hash the session_id so it can't be traced back to a user
    if 'session_id' in safe:
        safe['session_id'] = hashlib.sha256(
            safe['session_id'].encode()
        ).hexdigest()[:16]

    # Strip emails and phone numbers from correction text
    if 'payload' in safe and 'corrected' in safe['payload']:
        text = safe['payload']['corrected']
        text = re.sub(r'[\w.+-]+@[\w-]+\.[\w.]+', '[EMAIL]', text)
        text = re.sub(r'\+?[0-9][\s\-().]{7,}[0-9]', '[PHONE]', text)
        safe['payload'] = dict(safe['payload'], corrected=text)

    return safe

if __name__ == '__main__':
    record = {
        'session_id': 'sess-abc123',
        'payload': {'corrected': 'Contact me at jane@example.com or 555-123-4567'}
    }
    print('Anonymised record:', anonymise_feedback(record))

Volledige feedbackpijplijn

Alles samengevoegd: verzamelen → anonimiseren → bufferen → leegmaken → samenvoegen → rapporteren. De pijplijn draait in productie naast de agent en maakt wekelijks een verbeteringsrapport met de intenties die de laagste goedkeuringspercentages hebben.

# Simplified end-to-end feedback pipeline sketch

class FeedbackPipeline:
    def __init__(self, agent_id: str):
        self.collector = FeedbackCollector(agent_id)
        self.records = []

    def on_thumbs(self, turn_id: str, rating: str):
        value = 1.0 if rating == 'up' else -1.0
        record = self.collector.record(turn_id, 'thumbs', value, {})
        self.records.append(record)

    def on_edit(self, turn_id: str, original: str, edited: str):
        fb = extract_edit_feedback(original, edited)
        record = self.collector.record(
            turn_id, 'edit', fb['edit_distance'], fb
        )
        self.records.append(record)

    def weekly_report(self) -> dict:
        return aggregate_feedback(
            [r for r in self.records]
        )

Kennistoets

Welk feedbacksignaal vereist geen bewuste handeling van de gebruiker?

Samenvatting: feedback verzamelen en opslaan

Goed gedaan! Dit heb je in deze les geleerd:

  • Expliciete feedback: duimpjes (binair), sterren (beoordeling), correcties (gekoppelde trainingsgegevens)
  • Impliciete feedback: detectie van opnieuw vragen en de bewerkingsafstand van uitvoer geven de signaalkwaliteit aan zonder inspanning van de gebruiker
  • Opslagschema: één tabel met een type-discriminator en JSONB-gegevens
  • Pijplijn: verzamelen → anonimiseren → bufferen → leegmaken → samenvoegen
  • Privacy: sessie-ID's hashen, PII verwijderen en bewaarbeperkingen instellen

In de volgende les leer je hoe agenten op hun eigen prestaties kunnen reflecteren en die reflecties als episodisch geheugen kunnen opslaan.

Gratis beginnen

Leer AI-agenten met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
60
Lessen
239

Veelgestelde vragen

Is de les “Feedback verzamelen en opslaan” gratis?

Ja — de volledige tekst van “Feedback verzamelen en opslaan” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI-agenten wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI-agenten bevat in totaal 4 lessen.

Wat leer ik in “Feedback verzamelen en opslaan”?

Expliciete beoordelingen en impliciete gedragssignalen uit interacties met agents vastleggen. Je oefent met AI-agenten door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met AI-agenten te beginnen?

Ervaring vooraf is niet nodig. AI-agenten op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.

Hoe lang duurt de les “Feedback verzamelen en opslaan”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over AI-agenten?

Ja. Elke les over AI-agenten bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Feedback verzamelen en opslaan
  2. Reflectie- en zelfkritiekloops
  3. Zelfverbetering op basis van actietrajecten
  4. Wanneer zelfverbetering misgaat
← Terug naar AI-agenten