Recopilación y almacenamiento de comentarios
Captura de valoraciones explícitas y señales de comportamiento implícitas de las interacciones con agentes.
Recopilación y almacenamiento de comentarios es una lección gratuita de AI Agents en CoddyKit. Esta es la lección 1 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de AI Agents, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de AI Agents incluye 4 lecciones en total.
Por qué los agentes necesitan retroalimentación
Un agente que nunca recibe retroalimentación permanece congelado en el tiempo: no puede mejorar más allá de su entrenamiento inicial. La retroalimentación cierra el ciclo entre lo que hace el agente y lo que los usuarios realmente quieren.
Hay dos categorías especialmente importantes: la retroalimentación explícita (el usuario califica conscientemente la salida) y la retroalimentación implícita (el comportamiento del usuario indica la calidad sin que tenga que decirlo).
Retroalimentación explícita: pulgar arriba o abajo
La forma más sencilla: una señal binaria después de cada respuesta. Es fácil de recopilar y almacenar, pero ofrece poca información.
Patrón de implementación: después de que el agente responda, muestre una solicitud de retroalimentación y registre el resultado junto al ID del turno de conversación.
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)Retroalimentación explícita: valoraciones con estrellas
Una valoración de 1 a 5 estrellas proporciona más granularidad que un pulgar. Permite distinguir entre apenas aceptable (2 estrellas) y excelente (5 estrellas), lo que resulta útil para mejorar la calidad de la señal de ajuste.
Normalice el valor entre 0 y 1 antes de utilizarlo en las canalizaciones de entrenamiento.
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}Retroalimentación explícita: correcciones en texto libre
La retroalimentación en texto libre es la señal más completa. El usuario escribe exactamente lo que quería: "El resumen era demasiado largo", "No mencionó el punto principal", "Moneda incorrecta: solicité EUR".
Almacene las correcciones vinculadas a la salida original para poder emparejar (salida incorrecta → salida corregida) y utilizarlas posteriormente en el ajuste fino supervisado.
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)Retroalimentación implícita: señal de repetición de la pregunta
Cuando un usuario vuelve a formular inmediatamente la misma pregunta con otras palabras, es una señal implícita clara de que la respuesta anterior era incorrecta o insuficiente. No es necesario que el usuario haga clic en nada: el propio comportamiento es la señal.
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)
Retroalimentación implícita: señal de edición de la salida
Si el agente genera un texto y el usuario lo edita antes de utilizarlo, la diferencia entre el original y el texto editado es retroalimentación implícita. La versión editada es la que el usuario realmente quería.
Esto es habitual en asistentes de escritura, generadores de código y redactores de correos electrónicos.
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}")Esquema de almacenamiento de la retroalimentación
Todos los tipos de retroalimentación comparten un esquema común con campos de carga útil específicos para cada tipo. Utilizar una sola tabla con un discriminador type y una columna JSON payload mantiene sencillas las consultas y permite admitir cualquier tipo de retroalimentación.
# 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)
Escritura de una clase recopiladora de retroalimentación
Centralizar toda la recopilación de retroalimentación detrás de una sola clase mantiene limpio el resto del código. El recopilador gestiona la deduplicación, el procesamiento por lotes y las escrituras asíncronas para que la retroalimentación nunca bloquee el bucle principal del 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'])
Agregación de la retroalimentación para su análisis
Los registros de retroalimentación sin procesar deben agregarse antes de resultar útiles para tomar decisiones de mejora. Agregaciones habituales: tasa de aprobación por tipo de intención, tasa de corrección a lo largo del tiempo y categorías de salidas que se editan con mayor frecuencia.
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))Privacidad y consentimiento en la recopilación de retroalimentación
La retroalimentación suele contener datos confidenciales de los usuarios. Prácticas recomendadas: obtenga consentimiento explícito antes de registrar correcciones en texto libre, anonimice los ID de sesión antes del análisis, establezca límites de retención (por ejemplo, elimine los datos después de 90 días) y no registre nunca PII en las cargas útiles de retroalimentación.
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))
Canalización integral de retroalimentación
Integración de todos los pasos: recopilar → anonimizar → almacenar temporalmente → vaciar → agregar → informar. La canalización se ejecuta junto al agente en producción y genera un informe semanal de mejora que muestra qué intenciones tienen las tasas de aprobación más bajas.
# 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]
)Comprobación de conocimientos
¿Qué señal de retroalimentación no requiere ninguna acción consciente por parte del usuario?
Repaso: recopilación y almacenamiento de retroalimentación
¡Buen trabajo! Esto es lo que ha aprendido en esta lección:
- Retroalimentación explícita: pulgares (binaria), estrellas (graduada), correcciones (datos de entrenamiento emparejados)
- Retroalimentación implícita: la detección de repeticiones de preguntas y la distancia de edición de las salidas indican la calidad sin esfuerzo por parte del usuario
- Esquema de almacenamiento: una sola tabla con un discriminador de tipo y una carga útil JSONB
- Canalización: recopilar → anonimizar → almacenar temporalmente → vaciar → agregar
- Privacidad: aplicar un hash a los ID de sesión, eliminar la PII y establecer límites de retención
En la siguiente lección aprenderá cómo los agentes pueden reflexionar sobre su propio rendimiento y almacenar esas reflexiones como memoria episódica.
Preguntas frecuentes
¿La lección «Recopilación y almacenamiento de comentarios» es gratis?
Sí — el texto completo de «Recopilación y almacenamiento de comentarios» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de AI Agents, actualiza a CoddyKit PRO. El curso de AI Agents incluye 4 lecciones en total.
¿Qué aprenderé en «Recopilación y almacenamiento de comentarios»?
Captura de valoraciones explícitas y señales de comportamiento implícitas de las interacciones con agentes. Practicas AI Agents con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar AI Agents?
No se requiere experiencia previa. AI Agents en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 1 de 4.
¿Cuánto tiempo toma la lección «Recopilación y almacenamiento de comentarios»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de AI Agents?
Sí. Cada lección de AI Agents incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Recopilación y almacenamiento de comentarios
- Bucles de reflexión y autocrítica
- Automejora basada en trayectorias
- Cuando la automejora sale mal