การรวบรวมและจัดเก็บข้อเสนอแนะ
เก็บคะแนนที่ผู้ใช้ระบุโดยตรงและสัญญาณพฤติกรรมโดยอ้อมจากการโต้ตอบกับเอเจนต์
การรวบรวมและจัดเก็บข้อเสนอแนะ เป็นบทเรียน AI Agents ฟรีบน CoddyKit นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน AI Agents และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
เหตุใดเอเจนต์จึงต้องมีข้อมูลป้อนกลับ
เอเจนต์ที่ไม่เคยได้รับข้อมูลป้อนกลับจะหยุดนิ่งอยู่กับที่ — ไม่สามารถพัฒนาได้เกินกว่าการฝึกครั้งแรก ข้อมูลป้อนกลับช่วยปิดวงจรระหว่างสิ่งที่เอเจนต์ทำกับสิ่งที่ผู้ใช้ต้องการจริง ๆ
มีอยู่สองประเภทที่สำคัญที่สุด: ข้อมูลป้อนกลับโดยตรง (ผู้ใช้ให้คะแนนผลลัพธ์อย่างตั้งใจ) และ ข้อมูลป้อนกลับโดยนัย (พฤติกรรมของผู้ใช้บ่งบอกคุณภาพโดยไม่ต้องพูดออกมา)
ข้อมูลป้อนกลับโดยตรง: การกดนิ้วโป้งขึ้นหรือลง
รูปแบบที่ง่ายที่สุดคือสัญญาณแบบสองค่าหลังการตอบแต่ละครั้ง เก็บรวบรวมและจัดเก็บได้ง่าย แต่มีข้อมูลต่อหน่วยไม่มาก
รูปแบบการนำไปใช้คือ หลังเอเจนต์ตอบแล้ว ให้แสดงคำขอข้อมูลป้อนกลับและบันทึกผลไว้คู่กับรหัสรอบการสนทนา
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 และคอลัมน์ข้อมูลแบบ JSON payload ช่วยให้การสอบถามข้อมูลง่าย พร้อมรองรับข้อมูลป้อนกลับทุกประเภท
# 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))ความเป็นส่วนตัวและความยินยอมในการรวบรวมข้อมูลป้อนกลับ
ข้อมูลป้อนกลับมักมีข้อมูลผู้ใช้ที่มีความละเอียดอ่อน แนวทางปฏิบัติที่ดีคือ ขอความยินยอมอย่างชัดเจนก่อนบันทึกการแก้ไขด้วยข้อความอิสระ ทำให้รหัสเซสชันไม่สามารถระบุตัวตนได้ก่อนการวิเคราะห์ กำหนดขีดจำกัดการเก็บรักษา เช่น ลบหลัง 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))
กระบวนการข้อมูลป้อนกลับแบบครบวงจร
เมื่อนำทุกอย่างมารวมกัน: รวบรวม → ทำให้ไม่สามารถระบุตัวตนได้ → เก็บพัก → เขียนลงถาวร → รวมข้อมูล → จัดทำรายงาน กระบวนการนี้ทำงานควบคู่กับเอเจนต์ในระบบจริง และจัดทำรายงานการปรับปรุงประจำสัปดาห์ที่แสดงว่าเจตนาใดมีอัตราการอนุมัติต่ำที่สุด
# 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
- กระบวนการ: รวบรวม → ทำให้ไม่สามารถระบุตัวตนได้ → เก็บพัก → เขียนลงถาวร → รวมข้อมูล
- ความเป็นส่วนตัว: แฮชรหัสเซสชัน ลบ PII และกำหนดขีดจำกัดการเก็บรักษา
ในบทเรียนถัดไป คุณจะได้เรียนรู้ว่าเอเจนต์สามารถ ไตร่ตรองประสิทธิภาพของตนเอง และจัดเก็บการไตร่ตรองเหล่านั้นเป็นความจำแบบเป็นตอน ๆ ได้อย่างไร
คำถามที่พบบ่อย
บทเรียน “การรวบรวมและจัดเก็บข้อเสนอแนะ” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การรวบรวมและจัดเก็บข้อเสนอแนะ” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส AI Agents ให้อัปเกรดเป็น CoddyKit PRO คอร์ส AI Agents มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การรวบรวมและจัดเก็บข้อเสนอแนะ”
เก็บคะแนนที่ผู้ใช้ระบุโดยตรงและสัญญาณพฤติกรรมโดยอ้อมจากการโต้ตอบกับเอเจนต์ คุณปฏิบัติ AI Agents ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน AI Agents หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน AI Agents บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 1 จากทั้งหมด 4 บทเรียน
บทเรียน “การรวบรวมและจัดเก็บข้อเสนอแนะ” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน AI Agents นี้ได้ไหม
ได้ บทเรียน AI Agents ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- การรวบรวมและจัดเก็บข้อเสนอแนะ
- วงจรการไตร่ตรองและวิจารณ์ตนเอง
- การปรับปรุงตนเองโดยอิงลำดับการกระทำ
- เมื่อการปรับปรุงตนเองผิดพลาด