Feedback verzamelen en opslaan
Expliciete beoordelingen en impliciete gedragssignalen uit interacties met agents vastleggen.
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.
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
- Feedback verzamelen en opslaan
- Reflectie- en zelfkritiekloops
- Zelfverbetering op basis van actietrajecten
- Wanneer zelfverbetering misgaat