0Pricing
AI Agents · Lezione

Raccolta e archiviazione dei feedback

Acquisizione delle valutazioni esplicite e dei segnali comportamentali impliciti dalle interazioni con gli agenti.

Raccolta e archiviazione dei feedback è una lezione AI Agents gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento AI Agents, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso AI Agents include 4 lezioni in totale.

Perché gli agenti hanno bisogno di feedback

Un agente che non riceve mai feedback rimane fermo nel tempo: non può migliorare oltre il livello raggiunto durante l'addestramento iniziale. Il feedback chiude il ciclo tra ciò che l'agente fa e ciò che gli utenti desiderano realmente.

Le due categorie più importanti sono: feedback esplicito (l'utente valuta consapevolmente l'output) e feedback implicito (il comportamento dell'utente segnala la qualità senza che la esprima a parole).

Feedback esplicito: pollice su/giù

La forma più semplice: un segnale binario dopo ogni risposta. È facile da raccogliere e archiviare, ma contiene poche informazioni.

Pattern di implementazione: dopo la risposta dell'agente, mostri una richiesta di feedback e registri il risultato insieme all'ID del turno della conversazione.

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)

Feedback esplicito: valutazioni a stelle

Una valutazione da 1 a 5 stelle offre una granularità maggiore rispetto ai pollici. Permette di distinguere tra appena accettabile (2 stelle) ed eccellente (5 stelle), cosa utile per migliorare la qualità del segnale usato nel fine-tuning.

Normalizzi il valore tra 0 e 1 prima di utilizzarlo nelle pipeline di addestramento.

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}

Feedback esplicito: correzioni in testo libero

Il feedback in testo libero è il segnale più ricco. L'utente scrive esattamente ciò che desiderava: «Il riepilogo era troppo lungo», «Ha omesso il punto principale», «Valuta errata: avevo chiesto EUR».

Archivi le correzioni collegandole all'output originale, così in seguito potrà creare coppie (output errato → output corretto) per il fine-tuning supervisionato.

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)

Feedback implicito: segnale della ripetizione della domanda

Quando un utente ripete immediatamente la stessa domanda usando parole diverse, questo è un forte segnale implicito che la risposta precedente era errata o insufficiente. Non è necessario che l'utente selezioni alcunché: è il comportamento stesso a costituire il segnale.

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)

Feedback implicito: segnale della modifica dell'output

Se l'agente genera del testo e l'utente lo modifica prima di utilizzarlo, la differenza tra l'originale e la versione modificata costituisce un feedback implicito. La versione modificata è quella che l'utente desiderava realmente.

Questo è comune negli assistenti di scrittura, nei generatori di codice e negli strumenti per la stesura di e-mail.

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 di archiviazione del feedback

Tutti i tipi di feedback condividono uno schema comune con campi payload specifici per il tipo. L'utilizzo di un'unica tabella con un discriminatore type e una colonna JSON payload mantiene semplici le query e supporta qualsiasi tipo di feedback.

# 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)

Scrittura di una classe per la raccolta del feedback

Centralizzare tutta la raccolta del feedback dietro un'unica classe mantiene pulito il resto del codebase. Il collector gestisce la deduplicazione, l'elaborazione in batch e le scritture asincrone, così il feedback non blocca mai il ciclo principale dell'agente.

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'])

Aggregazione del feedback per l'analisi

I record grezzi del feedback devono essere aggregati prima di poter essere utili per le decisioni di miglioramento. Aggregazioni comuni: tasso di approvazione per tipo di intent, tasso di correzione nel tempo e categorie di output modificati più frequentemente.

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 e consenso nella raccolta del feedback

Il feedback contiene spesso dati sensibili degli utenti. Buone pratiche: ottenga il consenso esplicito prima di registrare correzioni in testo libero, anonimizzare gli ID di sessione prima dell'analisi, impostare limiti di conservazione (ad esempio, eliminazione dopo 90 giorni) e non registrare mai dati personali nei payload del feedback.

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))

Pipeline di feedback end-to-end

Unendo tutti i passaggi: raccolta → anonimizzazione → buffering → flush → aggregazione → report. In produzione, la pipeline viene eseguita insieme all'agente e produce un report settimanale sui miglioramenti, mostrando quali intent hanno i tassi di approvazione più bassi.

# 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]
        )

Verifica delle conoscenze

Quale segnale di feedback non richiede alcuna azione consapevole da parte dell'utente?

Riepilogo: raccolta e archiviazione del feedback

Ottimo lavoro! Ecco che cosa ha imparato in questa lezione:

  • Feedback esplicito: pollici (binario), stelle (graduato), correzioni (dati di addestramento abbinati)
  • Feedback implicito: il rilevamento delle domande ripetute e la distanza tra gli output modificati segnalano la qualità senza richiedere alcuno sforzo all'utente
  • Schema di archiviazione: un'unica tabella con discriminatore del tipo e payload JSONB
  • Pipeline: raccolta → anonimizzazione → buffering → flush → aggregazione
  • Privacy: applicare l'hashing agli ID di sessione, rimuovere i dati personali e impostare limiti di conservazione

Nella prossima lezione imparerà come gli agenti possono riflettere sulle proprie prestazioni e archiviare queste riflessioni come memoria episodica.

Domande Frequenti

La lezione «Raccolta e archiviazione dei feedback» è gratuita?

Sì — il testo completo di «Raccolta e archiviazione dei feedback» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso AI Agents, passa a CoddyKit PRO. Il corso AI Agents include 4 lezioni in totale.

Cosa imparerò in «Raccolta e archiviazione dei feedback»?

Acquisizione delle valutazioni esplicite e dei segnali comportamentali impliciti dalle interazioni con gli agenti. Eserciti AI Agents con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare AI Agents?

Non è richiesta alcuna esperienza precedente. AI Agents su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Raccolta e archiviazione dei feedback»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione AI Agents?

Sì. Ogni lezione AI Agents include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Raccolta e archiviazione dei feedback
  2. Cicli di riflessione e autocritica
  3. Auto-miglioramento basato sulle traiettorie
  4. Quando l’auto-miglioramento fallisce
← Torna a AI Agents