04 ตรวจสอบโดยมนุษย์
เปลี่ยนสัญญาณเชิงลบจากผู้ใช้ให้เป็นการวินิจฉัยและการแก้ไขที่ผ่านการตรวจสอบโดยมนุษย์ โดยไม่นำฟีดแบ็กมาสับสนกับความจริงพื้นฐาน (ground truth)
จุดเริ่มต้น
โมดูล 03 สร้าง Chat trace ที่เชื่อถือได้
หนึ่งรายการสำหรับคำถาม How many active customers do we have? พร้อมความขัดแย้งที่ตั้งใจให้เกิดขึ้น:
| หลักฐาน | ค่าที่คาดหวัง |
|---|---|
| root observation | chat_turn |
sql-execution-success | true |
user-thumbs | false |
metadata policyversion | policy-v1 |
เก็บ trace ID/URL และค่าอ้างอิงทั้งสองจากเวิร์กชีตของคุณไว้ อย่าใช้ curl diagnostic ที่ยังไม่ได้ให้คะแนนจากโมดูล 03
เหตุใดจึงต้องมีการสอบสวนโดยมนุษย์
thumbs-down บอกทีมว่าควรดูที่ไหน แต่ไม่ได้บอกว่าอะไรล้มเหลว ผู้ใช้อาจตั้งใจสื่อความหมายอื่น คำขออาจกำกวม SQL ที่สร้างขึ้นอาจไม่ถูกต้อง หรือคำนิยามทางธุรกิจสองแบบอาจขัดแย้งกัน การเลื่อนสัญญาณเชิงลบทุกรายการ เข้าสู่ golden dataset โดยตรงจะเปลี่ยนการเดาให้กลายเป็นความจริงพื้นฐานไปเสียเปล่า ๆ
ในโมดูลนี้ ผู้ตรวจสอบจะบันทึกสิ่งที่สังเกตได้ก่อน จากนั้นทดสอบคำอธิบาย ที่เป็นไปได้ต่าง ๆ แล้วจึงบันทึกการวินิจฉัยและการแก้ไขในลำดับสุดท้าย การตัดสินใจที่ผ่านการทบทวนนั้น—ไม่ใช่ thumbs-down—คือความจริงพื้นฐานที่ส่งต่อให้โมดูล 05
เป้าหมาย
ทำงาน annotation production-investigation-<session> หนึ่งงานให้เสร็จสมบูรณ์
สำหรับ root chat_turn ของโมดูล 03 งานที่เสร็จสมบูรณ์ต้องมีการสังเกต ประเภทความล้มเหลว
SQL ที่แก้ไขแล้วแบบตรงตัว การอนุมัติสำหรับ golden dataset และแหล่งที่มาจากการใช้งานจริง (production provenance)
ขั้นตอนที่ 1 — ค้นหาเหตุการณ์ฟีดแบ็กที่ตรงกัน
ใน Langfuse เปิด Tracing แล้วกรองด้วยคะแนน Boolean
user-thumbs = false เปิด trace ที่ตรงกับเงื่อนไขทั้งหมดต่อไปนี้:
- name/root observation คือ
chat_turn; - คำถามคือ
How many active customers do we have?; config_idของตัวชนะและ trace ID ที่บันทึกไว้ในโมดูล 03;- metadata
policyversion=policy-v1; และ - คะแนน
sql-execution-success=trueและuser-thumbs=false
แหล่งที่ให้บริการ (serving source) ส่งค่า policy_version ออกมา แต่ OpenTelemetry adapter
จะลบขีดล่างออก ดังนั้นคีย์ metadata ใน Langfuse จึงเป็น policyversion
ให้ annotate ที่ root chat_turn ไม่ใช่ generation ย่อยที่เป็น llm_call root
มีคำถามแบบ end-to-end และผลลัพธ์ที่มีโครงสร้าง—SQL, คอลัมน์, แถว, ข้อผิดพลาด, และผลลัพธ์
สุดท้าย—ซึ่งจำเป็นสำหรับการสอบสวน ส่วน child มีเพียง transcript ของโมเดลและ
SQL ที่สร้างขึ้นเท่านั้น และไม่ใช่เหตุการณ์ฟีดแบ็กที่เชื่อถือได้
ขั้นตอนที่ 2 — สร้าง score config สำหรับรีวิวทั้งสามรายการ
การตั้งค่านี้เป็นขั้นตอนตรวจสอบโดยมนุษย์ผ่าน UI เท่านั้นโดยเจตนา repository ของเวิร์กชอปนี้ ไม่มีคำสั่งใดที่จะสร้างหรือทำ annotation task นี้ให้เสร็จแทนคุณได้
ก่อนสร้างคิว เปิด Settings → Scores → Create และสร้าง config เหล่านี้:
| ชื่อ | ชนิดข้อมูล | ค่าที่อนุญาต / วัตถุประสงค์ |
|---|---|---|
observed-issue | TEXT | อธิบายเฉพาะหลักฐานที่มองเห็นได้ใน trace และการเปรียบเทียบเท่านั้น |
failure-category | CATEGORICAL | stale-business-policy, incorrect-sql, ambiguous-request, not-actionable |
approved-for-golden | BOOLEAN | อนุมัติได้เฉพาะหลังจากตรวจสอบยืนยันการแก้ไขแล้ว |
ใช้ชื่อและการใส่ยัติภังค์ตามที่แสดงไว้ทุกตัวอักษร การสร้าง config เหล่านี้ก่อนคือ วิธีที่ปลอดภัยที่สุด เพราะชุด score-config ID ที่ผูกกับคิวจะถูกกำหนดตายตัว ตั้งแต่ตอนสร้างคิว หาก config ใดถูกละไว้จากชุดที่ผูกไว้นั้น ให้สร้างคิวใหม่ ด้วยส่วนต่อท้าย (suffix) ใหม่ ส่วน score config เองสามารถแก้ไขได้: การแก้ไขชื่อ, schema, หรือ category ที่รองรับต้องทำผ่านการอัปเดต score-config ที่มีการตรวจสอบ (audited) และการแก้ไขนั้น จะไม่เขียนทับคะแนนที่มีอยู่แล้ว
ขั้นตอนที่ 3 — สร้างคิวและกำหนดเป้าหมายที่ logical root
เปิด Annotations → Queues → Create แล้ว:
- ตั้งชื่อว่า
production-investigation-<session>โดยแทน<session>ด้วยตัวระบุ สั้น ๆ ที่ไม่ซ้ำกันของเวิร์กชอป - ผูก score config ทั้งสามรายการจากขั้นตอนที่ 2
- สร้างคิว
- กลับไปที่ trace ของโมดูล 03 เลือก root observation
chat_turnเปิด dropdown Annotate แล้วเลือกคิวนี้ - เปิด task ใหม่และตรวจสอบว่าเป้าหมายคือ
chat_turnไม่ใช่llm_call
คิวไม่สามารถเปลี่ยนชุด score-config ID ที่ผูกไว้ได้หลังสร้างแล้ว ให้สร้างคิวใหม่ เฉพาะเมื่อชุดที่ผูกไว้ผิดพลาดเท่านั้น ส่วนการแก้ไขที่รองรับสำหรับ config ที่ผูกอยู่แล้ว ให้ใช้การอัปเดต score-config ที่มีการตรวจสอบแทน
ขั้นตอนที่ 4 — เขียนสิ่งที่คุณสังเกตได้แบบเปิดเผย (open-code)
โมดูล 03 เปิดเผย seeded setup ให้เห็นโดยเจตนา สำหรับการสอบสวนนี้ ให้พักความรู้
เชิงเวิร์กชอปก่อนหน้านั้นไว้ก่อน แล้วฝึกใช้เวิร์กโฟลว์แบบที่ผู้ตรวจสอบจะใช้กับ
เหตุการณ์ที่ยังไม่รู้สาเหตุ: ตรวจสอบคำถาม, SQL ที่สร้างขึ้น, จำนวนที่ส่งกลับมา, โมเดล/พรอมป์,
และคะแนนทั้งสองตัว ก่อนที่จะระบุสาเหตุ ให้บันทึกโน้ตที่มีเฉพาะหลักฐานลงใน
observed-issue เช่นตัวอย่างนี้:
The answer returned a count and its SQL executed. The observed count differs from the
second reference count recorded in Module 03. The generated query uses a 90-day
customer signup window, and the trace metadata reports policy-v1.ข้อความนี้ยังไม่ได้กล่าวหาว่าโมเดล, SQL engine, ผู้ใช้ หรือ policy เป็นฝ่ายผิด การแยกเช่นนี้ป้องกันไม่ให้การวินิจฉัยที่ถูก seed ไว้แอบแทรกเข้ามาในการทบทวน ก่อนที่จะได้ตรวจสอบหลักฐานจริง
ขั้นตอนที่ 5 — ตรวจสอบหลักฐานทั้งหมดของ trace
ขณะยังอยู่ที่ root chat_turn ให้ตรวจสอบ:
- metadata
policyversion=policy-v1; - SQL ที่สร้างขึ้นใช้
v_customersและหน้าต่างเวลาsignup_date90 วัน; - ผลลัพธ์ที่มีโครงสร้างมีจำนวนที่ตรงกับ trace count ที่สังเกตได้จากโมดูล 03;
- คะแนนด้านการทำงานคือ Boolean
sql-execution-success=true; และ - สัญญาณจากผู้ใช้คือ Boolean
user-thumbs=false
SQL ที่สร้างขึ้นสอดคล้องกับคำสั่งของ policy-v1 ที่มาพร้อม release นี้
คะแนนการรันสำเร็จก็ถูกต้องเช่นกันภายในขอบเขตที่ตั้งใจให้แคบ ณ จุดนี้
ยังไม่มีข้อเท็จจริงใดที่บอกได้ว่า policy ที่ deploy อยู่นั้นตรงกับคำนิยาม
ที่อยู่ภายใต้การกำกับดูแลในปัจจุบันหรือไม่
ขั้นตอนที่ 6 — ทดสอบคำนิยาม policy ทั้งสองแบบเทียบกัน
จาก ClickHouse_Demos/workshops/agent_arena ให้รันคำนิยามแบบอ่านอย่างเดียว (read-only)
ทั้งสองรายการในสภาพแวดล้อมเดียวกัน:
source .env
.venv/bin/python - <<'PY'
from arena.config import load_config
from agents.chclient import ROClickHouseClient
queries = {
"policy-v1": """SELECT count() FROM v_customers
WHERE signup_date >= today() - INTERVAL 90 DAY""",
"policy-v2": """SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')""",
}
client = ROClickHouseClient(load_config().clickhouse)
for version, sql in queries.items():
result = client.query(sql)
print(f"{version}: {result.rows[0][0]}")
PYค่าทั้งสองต้องตรงกับค่าในเวิร์กชีตและต้องแตกต่างกัน ตอนนี้คุณมีหลักฐานเพียงพอที่จะ
วินิจฉัยว่าคำนิยามทางธุรกิจที่ deploy อยู่นั้นล้าสมัย (stale): trace ระบุว่าใช้
policy-v1 และ SQL ของมันก็เป็นไปตาม policy นั้น ในขณะที่ query ปัจจุบันที่ได้รับการตรวจสอบแล้ว
ใช้ policy-v2
ขั้นตอนที่ 7 — ใส่ annotation, แก้ไข, อนุมัติ, และทำให้เสร็จสมบูรณ์
กลับไปที่ annotation task และบันทึก:
| ฟิลด์ | ค่า |
|---|---|
observed-issue | เก็บโน้ตแบบหลักฐานก่อนไว้ตามเดิม แล้วเพิ่มการเปรียบเทียบ policy ที่ตรวจสอบแล้วต่อท้าย |
failure-category | stale-business-policy |
| เอาต์พุตที่แก้ไขแล้ว (Corrected Output) | SQL ที่ตรงตัวด้านล่าง |
approved-for-golden | true |
สลับ Corrected Output ไปเป็น plain-text mode แล้วป้อน SQL ดิบนี้ตรงตัว:
SELECT uniqExact(customer_id) FROM v_orders
WHERE order_ts >= now() - INTERVAL 30 DAY
AND status NOT IN ('cancelled', 'returned')Langfuse จะบันทึกการแก้ไขนี้ไว้ แต่ไม่ได้รัน SQL นั้นจริง ๆ ไคลเอนต์ ClickHouse แบบอ่านอย่างเดียวในขั้นตอนที่ 6 ต้องรันข้อความตรงตัวนี้สำเร็จมาก่อนแล้วจึงจะอนุมัติได้ หากคุณแก้ไขการแก้ไขนี้เพิ่มเติม ให้รันข้อความนั้นผ่านไคลเอนต์ตัวเดียวกันอีกครั้ง จากนั้นเลือก Complete (หรือ Complete + next) การแก้ไขที่ผิดรูปแบบ รันไม่ได้ หรือยังไม่ได้รับการยืนยัน จะต้องไม่ได้รับการอนุมัติให้เป็น golden ground truth
ขั้นตอนที่ 8 — บันทึกที่มา (provenance) สำหรับโมดูล 05
คัดลอกค่าเหล่านี้ไปยังเวิร์กชีตของคุณ เก็บ ID ให้เป็นความลับภายในโปรเจกต์เวิร์กชอป:
| ฟิลด์ข้อมูลที่มา | ค่าที่ต้องบันทึก |
|---|---|
source | production-feedback |
source_trace_id | trace ID ของ Chat ที่เชื่อถือได้จากโมดูล 03 |
failure_category | stale-business-policy |
source_policy_version | policy-v1 |
annotation_id | ID ของ annotation task ที่เสร็จสมบูรณ์ เมื่อมีให้ใช้ |
| การแก้ไขที่ผ่านการทบทวน | SQL ตามนโยบายปัจจุบันที่ตรงตัวด้านบน |
source_trace_id, failure_category, และ source_policy_version เป็นข้อมูลที่จำเป็น
สำหรับ golden record ที่มาจากการใช้งานจริง ส่วน annotation_id เป็นทางเลือกในระดับ runtime
แต่ให้บันทึกไว้เมื่อ UI แสดงให้เห็น เพื่อให้การตัดสินใจยังตรวจสอบย้อนกลับได้
วิธีตรวจสอบว่าคุณทำเสร็จแล้ว
- คุณได้สอบสวน Chat trace ของโมดูล 03 รายการเดียวที่มี
user-thumbs=false - เป้าหมายของ annotation คือ root
chat_turnไม่ใช่ childllm_callไม่ว่ากรณีใด - คิวมีชื่อว่า
production-investigation-<session>และมี score config ทั้งสามรายการ ที่พิมพ์ประเภทถูกต้องผูกอยู่ครบ observed-issueบันทึกพฤติกรรมก่อนการวินิจฉัย- คุณได้รัน SQL แบบล้าสมัยและแบบปัจจุบันเทียบกัน และยืนยันว่าค่าที่ได้แตกต่างกัน
- task ที่เสร็จสมบูรณ์บันทึก
stale-business-policy, SQL ที่แก้ไขแล้วแบบตรงตัว และapproved-for-golden=true - เวิร์กชีตของคุณเก็บรักษา production provenance สำหรับโมดูล 05 ไว้ โดยไม่เผยแพร่ trace ID หรือ URL โปรเจกต์ที่ใช้งานจริง (live)
- คุณสามารถอธิบายได้ว่าทำไม thumbs-down จึงเป็นตัวจัดลำดับความสำคัญให้มนุษย์ตรวจสอบ แต่ตัวมันเองไม่ได้กลายเป็นความจริงพื้นฐาน
ไปยัง โมดูล 05 — ปิดวงจร เพื่อยกระดับการแก้ไขที่ผ่านการตรวจสอบแล้ว เปรียบเทียบเวอร์ชันของ policy และป้องกัน ความล้มเหลวในคลาสเดียวกันนี้ไม่ให้เกิดขึ้นซ้ำบนระบบจริง
03 ปล่อยใช้งานและตรวจจับ
ปล่อยใช้งานเอเจนต์ที่เลือกไว้พร้อมจุดบอดของนโยบายที่รู้อยู่แล้ว จากนั้นใช้การประเมินระหว่างปฏิบัติงานร่วมกับคำติชมจริงจากผู้ใช้เพื่อตรวจจับจุดบอดนั้น
05 ปิดวงจรการปรับปรุง
เปลี่ยนความล้มเหลวในโปรดักชันที่ผ่านการตรวจสอบแล้วหนึ่งกรณีให้กลายเป็นข้อมูลโกลเดน ผู้ประเมิน (evaluator) นโยบายธุรกิจที่ผ่านการปรับเทียบ และการป้องกันสำหรับทราฟฟิกในอนาคต