피드백 수집 및 저장
에이전트 상호작용에서 명시적 평점과 암묵적 행동 신호를 수집합니다.
피드백 수집 및 저장은(는) CoddyKit의 무료 AI Agents 강의입니다. 이것은 4개 중 1번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 AI Agents 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
에이전트에 피드백이 필요한 이유
피드백을 전혀 받지 못하는 에이전트는 처음 학습한 상태에 머물러 초기 학습 수준 이상으로 개선될 수 없습니다. 피드백은 에이전트가 실제로 수행하는 일과 사용자가 실제로 원하는 것 사이의 순환 고리를 완성합니다.
가장 중요한 범주는 두 가지입니다. 명시적 피드백(사용자가 의식적으로 출력을 평가함)과 암묵적 피드백(사용자가 직접 말하지 않아도 행동으로 품질을 나타냄)입니다.
명시적 피드백: 엄지 위/아래
가장 간단한 형태는 각 응답 후에 받는 이진 신호입니다. 수집하고 저장하기는 쉽지만 정보 밀도는 낮습니다.
구현 방식은 다음과 같습니다. 에이전트가 응답한 후 피드백 요청을 표시하고, 대화 턴 ID와 함께 결과를 기록합니다.
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)명시적 피드백: 별점 평가
1~5점 별점은 엄지 평가보다 더 세밀한 정보를 제공합니다. 간신히 수용 가능한 수준(별 2개)과 훌륭한 수준(별 5개)을 구분할 수 있으므로, 미세 조정을 위한 신호 품질을 높이는 데 유용합니다.
학습 처리 과정에서 사용하기 전에 0~1 범위로 정규화하십시오.
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}명시적 피드백: 자유 형식 수정
자유 형식 피드백은 가장 풍부한 신호입니다. 사용자는 원하는 내용을 정확히 작성합니다. '요약이 너무 길었습니다', '핵심을 놓쳤습니다', '통화가 잘못되었습니다. EUR을 요청했습니다'와 같은 내용입니다.
나중에 지도 미세 조정을 위해 (잘못된 출력 → 수정된 출력) 쌍을 만들 수 있도록 수정 내용을 원래 출력과 연결하여 저장하십시오.
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)암묵적 피드백: 재질문 신호
사용자가 같은 질문을 다른 표현으로 곧바로 다시 묻는다면, 이전 답변이 잘못되었거나 충분하지 않았다는 강력한 암묵적 신호입니다. 사용자가 아무것도 클릭하지 않아도 됩니다. 그 행동 자체가 신호이기 때문입니다.
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)
암묵적 피드백: 출력 편집 신호
에이전트가 텍스트를 생성하고 사용자가 사용하기 전에 편집했다면, 원본과 편집본의 차이가 암묵적 피드백이 됩니다. 사용자가 실제로 원한 것은 편집된 버전입니다.
이는 글쓰기 도우미, 코드 생성기, 이메일 초안 작성기에서 흔히 발생합니다.
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}")피드백 저장 스키마
모든 피드백 유형은 유형별 데이터 필드가 포함된 공통 스키마를 공유합니다. type 판별자와 payload JSON 형식 열을 사용하는 단일 테이블을 사용하면 모든 피드백 유형을 지원하면서도 질의를 단순하게 유지할 수 있습니다.
# 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)
피드백 수집기 클래스 작성
모든 피드백 수집을 단일 클래스 뒤에 중앙화하면 나머지 코드베이스를 깔끔하게 유지할 수 있습니다. 수집기는 중복 제거, 일괄 처리, 비동기 기록을 담당하므로 피드백이 주 에이전트 루프를 차단하지 않습니다.
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'])
분석을 위한 피드백 집계
원시 피드백 레코드는 개선 결정을 내리는 데 유용하려면 먼저 집계해야 합니다. 일반적인 집계 항목으로는 의도 유형별 승인율, 시간에 따른 수정률, 가장 많이 편집된 출력 범주가 있습니다.
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))피드백 수집에서의 개인정보 보호와 동의
피드백에는 민감한 사용자 데이터가 포함되는 경우가 많습니다. 권장 방법은 자유 형식 수정을 기록하기 전에 명시적 동의를 받고, 분석 전에 세션 ID를 익명화하며, 보존 한도를 설정하고(예: 90일 후 삭제), 피드백 데이터에 PII를 절대 기록하지 않는 것입니다.
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))
종단 간 피드백 처리 과정
모든 과정을 연결하면 다음과 같습니다. 수집 → 익명화 → 버퍼링 → flush → 집계 → 보고. 이 처리 과정은 운영 환경에서 에이전트와 함께 실행되며, 승인율이 가장 낮은 의도 유형을 보여 주는 주간 개선 보고서를 생성합니다.
# 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]
)지식 확인
사용자가 의식적으로 아무 행동도 하지 않아도 얻을 수 있는 피드백 신호는 무엇입니까?
요약: 피드백 수집 및 저장
훌륭합니다! 이 수업에서 배운 내용은 다음과 같습니다.
- 명시적 피드백: 엄지 평가(이진), 별점(등급), 수정(쌍으로 연결된 학습 데이터)
- 암묵적 피드백: 사용자의 추가 노력 없이 재질문 감지와 출력 편집 거리가 신호 품질을 나타냄
- 저장 스키마: 유형 판별자와 JSONB 데이터가 포함된 단일 테이블
- 처리 과정: 수집 → 익명화 → 버퍼링 →
flush→ 집계 - 개인정보 보호: 세션 ID 해시 처리, PII 제거, 보존 한도 설정
다음 수업에서는 에이전트가 자체 성능을 성찰하고 그 성찰 내용을 에피소드 메모리로 저장하는 방법을 배웁니다.
자주 묻는 질문
“피드백 수집 및 저장” 강의는 무료인가요?
네 — “피드백 수집 및 저장” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 AI Agents 강의 전체를 잠금 해제할 수 있습니다. AI Agents 강의에는 총 4개의 강의가 포함되어 있습니다.
“피드백 수집 및 저장”에서 뭘 배우나요?
에이전트 상호작용에서 명시적 평점과 암묵적 행동 신호를 수집합니다. 브라우저에서 직접 실행하는 실습 코드로 AI Agents을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.
AI Agents을(를) 시작하는 데 경험이 필요한가요?
사전 경험은 필요하지 않습니다. CoddyKit의 AI Agents은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 1번째 강의입니다.
“피드백 수집 및 저장” 강의는 얼마나 걸리나요?
대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.
이 AI Agents 강의에서 코드를 작성하고 실행할 수 있나요?
네. 모든 AI Agents 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.
이 강의의 모든 강의
- 피드백 수집 및 저장
- 성찰 및 자기 비평 루프
- 궤적 기반 자기 개선
- 자기 개선이 잘못될 때