teach-me-ai / sources / MP-1
⟵ กลับหน้าแทร็กเสริม
MP-1 · mattpocock/skills

Alignment — คุยให้ตรงก่อนลงมือ (Grilling)

⏱️ ~20 นาที 🎯 ใช้ grilling ปิดช่องว่างความเข้าใจ และสร้างภาษาโดเมนร่วม 📦 ต้นทาง: skills/grill-me · grilling · grill-with-docs

บทที่แล้ว (MP-0) เราเห็นแล้วว่า misalignment — agent ทำไม่ตรงใจ — คือ failure mode อันดับหนึ่งที่ README ของ mattpocock/skills พูดถึง และยาที่ใช้แก้คือ grilling บทนี้เราจะเจาะลึกว่า grilling ทำงานยังไงจริงๆ ต่างจาก grill-me กับ grill-with-docs ยังไง และทำไม ubiquitous language ถึงเป็นผลพลอยได้ที่ทรงพลังของมัน ถ้าคุณเคยเจอ agent เขียนโค้ดผิดทิศทางทั้งที่คุณคิดว่าสั่งชัดแล้ว บทนี้คือคำตอบว่าทำไม และจะป้องกันยังไงตั้งแต่ก่อนพิมพ์ prompt แรก

ปัญหา: ไม่มีใครรู้แน่ว่าตัวเองต้องการอะไร

README อ้างประโยคจาก The Pragmatic Programmer ตรงๆ ว่า "no-one knows exactly what they want" — นี่ไม่ใช่ปัญหาเฉพาะยุค AI มันคือปัญหาคลาสสิกของงานซอฟต์แวร์ทุกยุค ลูกค้าอธิบายไม่ครบ, PM เขียน ticket กว้างเกินไป, dev สองคนตีความ requirement เดียวกันคนละแบบ

พอมี agent เข้ามาช่วยเขียนโค้ด ช่องว่างนี้ยิ่งขยายใหญ่ขึ้น เพราะ agent ไม่มีบริบทที่อยู่ในหัวคุณเลย ลองนึกภาพคุณพิมพ์สั้นๆ ว่า "เพิ่ม endpoint ยกเลิกออเดอร์ให้หน่อย" ใน NestJS project — agent อาจจะเดาเอาว่ายกเลิกได้ทุกสถานะ, ไม่ต้องเช็กสิทธิ์ใคร, ลบ record ทิ้งเลย ทั้งที่ในหัวคุณคิดว่า ยกเลิกได้เฉพาะ order ที่ยังไม่ shipped, ต้องเป็นเจ้าของ order เท่านั้นถึงยกเลิกได้, และต้อง soft-delete เก็บประวัติไว้ — ช่องว่างนี้แหละที่ทำให้ agent "ทำไม่ตรงใจ" ทั้งที่คุณตั้งใจสั่งงานถูกแล้ว

Grilling: สัมภาษณ์ทีละกิ่งจนกว่าจะเคลียร์ทั้งต้น

grilling คือ skill แบบ model-invoked ที่เป็น primitive อยู่เบื้องหลัง /grill-me และ /grill-with-docs ตัวเนื้อหาจริงใน SKILL.md เขียนไว้แบบนี้ (แปลจากต้นฉบับ):

"สัมภาษณ์ผมอย่างไม่ปรานีในทุกแง่มุมของแผนนี้ จนกว่าเราจะเข้าใจตรงกัน เดินลงไปทีละกิ่งของ design tree แก้ dependency ระหว่างการตัดสินใจแต่ละจุดทีละอัน สำหรับทุกคำถาม ให้เสนอคำตอบที่แนะนำมาด้วย"

พูดง่ายๆ คือ agent จะมองแผนงานเป็น design tree — ต้นไม้ของการตัดสินใจ ที่แต่ละกิ่งขึ้นอยู่กับกิ่งก่อนหน้า แล้วเดินลงไปทีละกิ่ง แก้ปัญหาทีละจุดจนครบทุกกิ่ง ไม่ใช่โยนคำถามทั้งหมดมาพร้อมกัน

Feature: ยกเลิกออเดอร์ design tree เริ่มต้น ① สถานะไหนยกเลิกได้? ✓ เฉพาะ PENDING/PAID ② ใครยกเลิกได้บ้าง? ✓ เจ้าของ order เท่านั้น ③ ลบจริงหรือ soft-delete? ⏳ กำลังถาม (current) ④ soft-delete → คืน stock ไหม? รอคิว (pending) ⑤ ต้อง log audit trail ไหม? รอคิว (pending) กติกาการเดิน: • ถามทีละข้อ รอคำตอบก่อนไปข้อถัดไป — ยิงหลายคำถามพร้อมกัน "bewildering" เกินไป • เรื่องที่หาคำตอบจาก codebase ได้ (fact) → agent ไปอ่านเอง ไม่ถามคุณ • เรื่องที่เป็น decision (การตัดสินใจ) → ต้องถามคุณเสมอ พร้อมเสนอคำตอบแนะนำมาด้วย
grilling เดิน design tree ทีละกิ่ง — กิ่งที่ตอบแล้ว (เขียว) ล็อกไว้ก่อนเดินไปกิ่งถัดไป (เหลือง = กำลังถาม, เทา = ยังไม่ถึงคิว)

สองกฎที่ SKILL.md เน้นย้ำและสำคัญมากในทางปฏิบัติ:

  • ถามทีละข้อเท่านั้น — ต้นฉบับเขียนตรงๆ ว่า "Asking multiple questions at once is bewildering" (ยิงหลายคำถามพร้อมกันทำให้งง) เพราะคำตอบของข้อก่อนอาจเปลี่ยนว่าข้อถัดไปควรถามอะไร
  • แยก fact ออกจาก decision — ถ้าเป็นข้อเท็จจริงที่หาได้จากโค้ด (เช่น "ตอนนี้ schema มี field deletedAt อยู่แล้วไหม") agent ควรไปเปิดโค้ดดูเอง ไม่ต้องถามคุณ แต่ถ้าเป็น การตัดสินใจ (เช่น "จะ soft-delete หรือ hard-delete") นั่นเป็นสิทธิ์ของคุณเสมอ ต้องถามและรอคำตอบ พร้อมข้อเสนอแนะจาก agent ประกอบการตัดสินใจ

และข้อสุดท้ายที่สำคัญไม่แพ้กัน: SKILL.md ปิดท้ายด้วย "Do not enact the plan until I confirm we have reached a shared understanding" — ห้ามเริ่มลงมือทำจริงจนกว่าคุณจะยืนยันว่าเข้าใจตรงกันแล้ว นี่คือเหตุผลที่ grilling ต้องมาก่อนเสมอ ไม่ใช่แค่เสริม

สามหน้าตาของ grilling: primitive กับ 2 ประตูทางเข้า

grilling เองเป็น model-invoked primitive — เป็นเทคนิคกลางที่ skill อื่น เรียกใช้ซ้ำได้ ไม่ต้องประดิษฐ์การสัมภาษณ์ใหม่ทุกครั้ง ส่วน grill-me และ grill-with-docs คือ user-invoked สองประตูที่ห่อ grilling ไว้คนละแบบ:

Skillเรียกด้วยใช้กับทิ้งอะไรไว้หลังจบ
grilling primitive เบื้องหลัง ไม่มี ทำหน้าที่แค่เป็นเครื่องยนต์ของการสัมภาษณ์
grill-me /grill-me งานทั่วไป ไม่จำกัดว่าต้องเป็นโค้ด ไม่มีอะไรเลย — stateless เขียนไฟล์ 0 ไฟล์ ผลลัพธ์คือความเข้าใจที่คมขึ้นในบทสนทนาเท่านั้น
grill-with-docs /grill-with-docs งานที่มีโค้ด/โดเมนเกี่ยวข้อง Stateful — เขียน/อัปเดต CONTEXT.md (glossary) และ ADR ใน docs/adr/ ระหว่างสัมภาษณ์

จาก SKILL.md ของ grill-with-docs เขียนสั้นมากแค่บรรทัดเดียว: "Run a `/grilling` session, using the `/domain-modeling` skill" — คือมันไม่ได้เขียน logic การสัมภาษณ์ซ้ำเลย แค่บอกให้รัน grilling พร้อมพ่วง domain-modeling (skill ที่ดูแลเรื่อง glossary/ADR) เข้าไปด้วยเท่านั้น นี่คือความหมายของ "primitive" — ของจริงอยู่ที่ grilling ตัวเดียว ที่เหลือคือ wrapper บางๆ

💡

เลือกยังไง: กำลังคิด design เรื่องทั่วไป (จะ deploy ยังไง, จะเขียน spec แบบไหน) ใช้ /grill-me พอ แต่ถ้ากำลังจะแตะโค้ด/โดเมนของระบบจริง และอยากได้ศัพท์ร่วมที่ค้างอยู่ในโปรเจกต์ ให้ใช้ /grill-with-docs ตั้งแต่ต้น เพราะมันจะบันทึกสิ่งที่ตกลงกันไว้ ไม่ระเหยหายไปตอนจบ session

Ubiquitous language: ทำไม CONTEXT.md ถึงลด token ได้จริง

ผลพลอยได้ที่ทรงพลังที่สุดของ grill-with-docs คือมันสร้าง ubiquitous language ให้กับ agent README อ้าง Eric Evans จาก Domain-Driven Design ว่า "เมื่อมีภาษาร่วมกัน บทสนทนาระหว่าง dev และการแสดงออกในโค้ดจะมาจากโมเดลโดเมนเดียวกัน" ตัวอย่างจริงจาก README เทียบให้เห็นชัด:

ประโยค
BEFORE "There's a problem when a lesson inside a section of a course is made 'real' (i.e. given a spot in the file system)"
AFTER "There's a problem with the materialization cascade"

ทั้งสองประโยคความหมายเดียวกัน แต่ประโยคที่สองสั้นกว่ามาก เพราะคำว่า materialization ถูกนิยามไว้แล้วใน CONTEXT.md ของโปรเจกต์ — agent ไม่ต้องอธิบายซ้ำทุกครั้งว่า "การทำให้ lesson มีที่อยู่จริงในไฟล์ระบบ" คืออะไร แค่พูดคำเดียวก็สื่อสารตรงกันได้ทันที

รูปแบบของ CONTEXT.md ตามที่ skill domain-modeling กำหนดไว้ เป็น glossary ล้วนๆ — ไม่มีรายละเอียด implementation ปนอยู่เลย:

# Ordering

## Language

**Order**:
คำสั่งซื้อที่ลูกค้าสร้างขึ้น มีสถานะเปลี่ยนไปตามวงจรชีวิต
_Avoid_: Purchase, transaction

**Materialization**:
การทำให้ lesson ในตำแหน่งหนึ่งของ course มี "ที่อยู่จริง" บน file system
_Avoid_: การ generate ไฟล์, การ build

ข้อสำคัญจาก domain-modeling SKILL.md: ต้องเจาะจงเฉพาะศัพท์ของโดเมนนี้จริงๆ — คำศัพท์ programming ทั่วไป (timeout, error type, utility pattern) ไม่นับ แม้จะใช้บ่อยแค่ไหนก็ตาม และ CONTEXT.md ห้ามใส่รายละเอียด implementation, ห้ามใช้เป็น spec หรือ scratch pad — มันคือ glossary เท่านั้น

ประโยชน์ที่ได้ไม่ได้มีแค่ลดความยาว: ตัวแปร ฟังก์ชัน และไฟล์ในโค้ดถูกตั้งชื่อสอดคล้องกันตามภาษาร่วมเดียวกัน ทำให้ codebase เดินสำรวจง่ายขึ้นสำหรับทั้งคนและ agent และ agent ใช้ token คิดน้อยลง เพราะมีภาษาที่กระชับกว่าให้ใช้ — ผูกกับงานคุณตรงๆ: ถ้า service ของคุณเรียก field materializedAt ตรงกับที่นิยามใน CONTEXT.md agent จะเดาชื่อ field ถูกตั้งแต่ครั้งแรก ไม่ต้องลองผิดลองถูก

ผูกกับงานจริง: grill ก่อนทำฟีเจอร์ใน NestJS

สมมติคุณจะสั่ง agent ว่า "เพิ่ม endpoint ยกเลิกออเดอร์ (cancel order) ให้หน่อย" ใน NestJS service ถ้าใช้ /grill-with-docs ก่อนเริ่มเขียนโค้ด agent ควรถามประมาณนี้ ทีละข้อ:

// รอบที่ 1 — agent ถาม (fact เช็กจากโค้ดเองก่อนแล้ว ไม่ถามซ้ำ)
Agent: จากที่ดู OrderEntity ตอนนี้มีสถานะ PENDING, PAID, SHIPPED, CANCELLED
ยกเลิกได้เฉพาะ PENDING กับ PAID ใช่ไหม? (แนะนำ: ใช่ เพราะ SHIPPED แล้วยกเลิกจะกระทบ stock/คลัง)

// รอบที่ 2 — รอคำตอบก่อน แล้วค่อยถามข้อถัดไป
Agent: ใครมีสิทธิ์ยกเลิก order ได้บ้าง? เฉพาะเจ้าของ order เอง หรือ admin ก็ยกเลิกแทนได้ด้วย?
(แนะนำ: ทั้งคู่ — เจ้าของยกเลิกเองได้ ส่วน admin ยกเลิกแทนได้แต่ต้อง log ว่าใครกดแทนใคร)

// รอบที่ 3 — decision ที่กระทบ schema โดยตรง
Agent: ยกเลิกแล้วลบ record จริง (hard-delete) หรือแค่เปลี่ยนสถานะเป็น CANCELLED (soft-delete)?
(แนะนำ: soft-delete เพราะต้องเก็บประวัติไว้ทำ report และ audit)

สังเกตว่าทุกคำถามมี คำตอบแนะนำ (recommended answer) แนบมาด้วยเสมอ — คุณแค่ตอบรับ, ปฏิเสธ, หรือแก้ไขสั้นๆ ไม่ต้องคิดคำตอบจากศูนย์ทุกข้อ นี่คือสิ่งที่ทำให้ grilling เร็วในทางปฏิบัติ ทั้งที่ฟังดูเหมือนต้องตอบเยอะ พอจบ 3 รอบนี้ grill-with-docs จะเขียนคำว่า Order, CANCELLED ลง CONTEXT.md (ถ้ายังไม่มี) และถ้าการตัดสินใจ soft-delete ถือว่า "ยากจะย้อนกลับ" (hard to reverse) พอจะเสนอเขียนเป็น ADR ให้ด้วย

⚠️

Pitfall ที่พบบ่อย: ปล่อยให้ agent ยิงคำถามรวดเดียวเป็นชุด 5-6 ข้อในข้อความเดียว — SKILL.md เตือนตรงๆ ว่านี่ "bewildering" เพราะคำตอบข้อแรกอาจทำให้คำถามข้อสามไม่จำเป็นอีกต่อไป ถ้าเจอแบบนี้ ให้บอก agent ตรงๆ ว่า "ถามทีละข้อ" อีก pitfall คือปล่อยให้ agent เขียนรายละเอียด implementation ลง CONTEXT.md — ถ้าเห็นแบบนั้นให้เตือนกลับ เพราะมันควรเป็น glossary ล้วนๆ เท่านั้น

🎯 แบบฝึกหัด
  1. นึกถึงฟีเจอร์เล็กๆ ที่คุณกำลังจะทำจริง (หรือเคยทำ) ใน repo ของคุณเอง — เขียน design tree ของมันมา 3-4 กิ่ง (decision ที่ต้องตัดสินใจ) แบบในอินโฟกราฟิกด้านบน แล้วบอกว่าข้อไหนเป็น fact (หาจากโค้ดได้) กับข้อไหนเป็น decision (ต้องเป็นคุณตัดสินเท่านั้น)
  2. พิมพ์ฟีเจอร์นั้นมาในแชท แล้วให้ผมเล่นบทบาท grill-with-docs ถามคุณทีละข้อ พร้อมเสนอคำตอบแนะนำ — ลองตอบไปเรื่อยๆ จนกว่าเราจะสรุปได้ว่า "เข้าใจตรงกันแล้ว"
  3. จากบทสนทนาข้อ 2 ลองร่างเนื้อหา CONTEXT.md เองสัก 1-2 term ตามฟอร์แมต **Term**: คำอธิบาย / _Avoid_: คำที่ไม่ใช้ — เช็กว่าเนื้อหาที่เขียนไม่มี implementation detail หลุดเข้าไปเลยสักบรรทัด