Insamling och lagring av feedback
Samla in uttryckliga betyg och implicita beteendesignaler från agentinteraktioner.
Insamling och lagring av feedback är en gratis lektion i AI-agenter på CoddyKit. Detta är lektion 1 av 4. Ni kan läsa hela lektionen gratis nedan och sedan öva praktiskt i webbläsaren med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt. Den ingår i lärvägen för AI-agenter, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i AI-agenter innehåller totalt 4 lektioner.
Varför agenter behöver återkoppling
En agent som aldrig får återkoppling är frusen i tiden — den kan inte förbättras utöver sin ursprungliga träning. Återkoppling sluter kretsen mellan det agenten gör och det användarna faktiskt vill ha.
Två kategorier är viktigast: explicit återkoppling (användaren bedömer medvetet output) och implicit återkoppling (användarens beteende signalerar kvalitet utan att användaren säger det).
Explicit återkoppling: tummen upp/ner
Den enklaste formen: en binär signal efter varje svar. Den är enkel att samla in och lagra, men innehåller lite information.
Mönster för implementering: efter att agenten har svarat visar Ni en fråga om återkoppling och registrerar resultatet tillsammans med ID:t för konversationsvändan.
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)Explicit återkoppling: stjärnbetyg
Ett betyg på 1–5 stjärnor ger större detaljrikedom än tummen upp eller ner. Det gör det möjligt att skilja mellan knappt godtagbart (2 stjärnor) och utmärkt (5 stjärnor), vilket är användbart för att förbättra signalens kvalitet vid finjustering.
Normalisera till 0–1 innan värdet används i träningspipelines.
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}Explicit återkoppling: korrigeringar i fritext
Återkoppling i fritext är den mest informationsrika signalen. Användaren skriver exakt vad hen ville ha: 'Sammanfattningen var för lång', 'Du missade huvudpoängen', 'Fel valuta — jag bad om EUR'.
Lagra korrigeringar tillsammans med den ursprungliga outputen så att Ni senare kan para ihop (felaktig output → korrigerad output) för övervakad finjustering.
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)Implicit återkoppling: signalen att användaren frågar igen
När en användare omedelbart ställer samma fråga igen med andra ord är det en stark implicit signal om att det föregående svaret var felaktigt eller otillräckligt. Användaren behöver inte klicka på något — själva beteendet är signalen.
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)
Implicit återkoppling: signal från redigering av output
Om agenten genererar text och användaren redigerar den innan den används är skillnaden mellan originalet och den redigerade versionen implicit återkoppling. Den redigerade versionen är det användaren faktiskt ville ha.
Detta är vanligt i skrivassistenter, kodgeneratorer och verktyg för att skriva e-postutkast.
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}")Schema för lagring av återkoppling
Alla typer av återkoppling delar ett gemensamt schema med typspecifika nyttolastfält. En enda tabell med en type-diskriminator och en payload JSON-kolumn håller frågorna enkla och stöder samtidigt alla typer av återkoppling.
# 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)
Skriv en klass för insamling av återkoppling
Genom att centralisera all insamling av återkoppling bakom en enda klass hålls resten av kodbasen ren. Insamlaren hanterar deduplicering, batchning och asynkrona skrivningar så att återkoppling aldrig blockerar agentens huvudloop.
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'])
Aggregera återkoppling för analys
Råa återkopplingsposter måste aggregeras innan de kan användas för förbättringsbeslut. Vanliga aggregeringar är godkännandegrad per avsiktstyp, korrigeringsgrad över tid och de outputkategorier som redigeras mest.
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))Integritet och samtycke vid insamling av återkoppling
Återkoppling innehåller ofta känsliga användardata. Bästa praxis är att inhämta uttryckligt samtycke innan korrigeringar i fritext registreras, anonymisera sessions-ID:n före analys, ange lagringsgränser (t.ex. radera efter 90 dagar) och aldrig logga PII i återkopplingens nyttolaster.
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))
Återkopplingspipeline från början till slut
Så här hänger allt ihop: samla in → anonymisera → buffra → tömma → aggregera → rapportera. Pipelines körs parallellt med agenten i produktion och skapar en veckovis förbättringsrapport som visar vilka avsiktstyper som har lägst godkännandegrad.
# 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]
)Kunskapskontroll
Vilken återkopplingssignal kräver ingen medveten handling från användaren?
Sammanfattning: insamling och lagring av återkoppling
Bra arbete! Här är vad Ni har lärt Er i den här lektionen:
- Explicit återkoppling: tummen (binär), stjärnor (graderad), korrigeringar (parade träningsdata)
- Implicit återkoppling: upptäckt av nya frågor och signaler från redigeringsavstånd för output mäter kvalitet utan användarens insats
- Lagringsschema: en enda tabell med typdiskriminator och JSONB-nyttolast
- Pipeline: samla in → anonymisera → buffra → tömma → aggregera
- Integritet: hasha sessions-ID:n, ta bort PII och ange lagringsgränser
I nästa lektion får Ni lära Er hur agenter kan reflektera över sin egen prestation och lagra dessa reflektioner som episodiskt minne.
Lär dig AI-agenter med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 60
- Lektioner
- 239
Vanliga frågor
Är lektionen ”Insamling och lagring av feedback” gratis?
Ja – hela texten till ”Insamling och lagring av feedback” kan läsas gratis här på webben. Om Ni vill öva interaktivt med en inbyggd kodredigerare och en AI-handledare som är tillgänglig dygnet runt och låsa upp resten av kursen i AI-agenter, kan Ni uppgradera till CoddyKit PRO. Kursen i AI-agenter innehåller totalt 4 lektioner.
Vad lär jag mig i ”Insamling och lagring av feedback”?
Samla in uttryckliga betyg och implicita beteendesignaler från agentinteraktioner. Ni övar på AI-agenter med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig AI-agenter?
Du behöver inga förkunskaper. Utbildningen i AI-agenter på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 1 av 4.
Hur lång tid tar lektionen ”Insamling och lagring av feedback”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här AI-agenter-lektionen?
Ja. Varje AI-agenter-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Insamling och lagring av feedback
- Loopar för reflektion och självkritik
- Självförbättring baserad på handlingsförlopp
- När självförbättring går fel