All posts

13 min readAI

Intent Debt: เจตนาที่ Agent จ่ายแทนไม่ได้ ต่อให้งานดูเสร็จแล้วก็ตาม

Prompt สองบรรทัดกลับมาเป็นฟีเจอร์ครบ test เขียว แต่การตัดสินใจครึ่งหนึ่งในนั้นไม่มีใครเป็นคนตัดสิน บันทึกจากเดฟ·PM·PO หลังอ่าน Addy Osmani ว่าทำไมเป้าหมาย ข้อจำกัด และเหตุผลที่ไม่ได้เขียนไว้จึงเป็นหนี้ที่มีแต่คนจ่ายได้ และทำไมการเขียน prompt จึงเป็นงานคิด ไม่ใช่งานพิมพ์

Sutthiphong NuanmaSoftware Developer

งานที่น่ากังวลที่สุดจาก agent อาจไม่ใช่งานที่ผิดครับ แต่คืองานที่ตอบคำถามซึ่งไม่เคยมีใครถาม

ลองนึกภาพ requirement จากลูกค้าที่มาในแชตประโยคเดียวว่า "ขอให้ export รายงานยอดขายได้" เราส่งประโยคนี้ให้ agent แทบจะตรงตัว ไม่กี่นาทีต่อมาได้ปุ่ม ได้ไฟล์ CSV ได้ test ครบ และได้สรุปใน PR ที่อ่านแล้วสบายใจ

แต่ในงานชิ้นนั้นมีการตัดสินใจซ่อนอยู่เต็มไปหมด ใครมีสิทธิ์กด ไฟล์มีข้อมูลส่วนตัวไหม ตัวเลขตัดรอบตามเขตเวลาไหน และคนที่ขอจะเอาไฟล์นี้ไปทำอะไร ทุกข้อมีคำตอบอยู่ในโค้ดแล้ว เพียงแต่ไม่มีใครเป็นคนตอบ

ผมเป็นทั้ง developer, PM และ PO ที่ Mulberrysoft ดูแลทีม junior ทำงานกับลูกค้าที่ requirement แทบไม่เคยครบ และใช้ Claude, Codex กับ agent อื่น ๆ แทบทุกวัน ซีรีส์นี้เริ่มจากเรื่องการให้ agent ทำงานแทนโดยไม่ให้มันคิดแทน ต่อด้วยวันที่คำตอบของ agent กลายเป็นความเห็นของเรา และหนี้ความเข้าใจที่ไม่ขึ้นบน dashboard สามโพสต์นั้นพูดถึงตอนที่งานกลับมาถึงมือเราและหลังจากนั้น โพสต์นี้ขอย้อนไปดูช่วงก่อนงานเริ่ม คือตอนที่เรากำลังพิมพ์สั่งมัน

จุดยืนของผมมีอยู่ว่า ช่องว่างใน prompt จะถูกเติมเสมอ คำถามมีแค่ว่าใครเป็นคนเติม และคนคนนั้นรู้ตัวหรือเปล่าว่ากำลังตัดสินใจ

หนี้ก้อนที่สาม

Addy Osmani เขียนเรื่อง intent debt โดยใช้กรอบ Triple Debt Model ของ Margaret-Anne Storey ซึ่งแยกหนี้สามก้อนตามที่อยู่ของมันครับ technical debt อยู่ในโค้ด cognitive debt อยู่ในหัวคน ซึ่ง Addy เรียกว่า comprehension debt และ intent debt อยู่ใน artifact

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

Triple Debt Model นี้ผมอ่านผ่านบทความของ Addy ไม่ได้ลงไปอ่าน paper ฉบับเต็ม ใครอยากเช็กกดลิงก์ด้านบนได้เลยครับ

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

ก้อนเดียวที่ agent ช่วยไม่ได้

Technical debt เราให้ agent ช่วย refactor ได้ Comprehension debt เราให้ agent อธิบายโค้ดกลับมาได้ ถึงจะต้องตรวจคำอธิบายนั้นอีกทีก็ตาม แต่ intent ไม่เหมือนกันครับ Addy เขียนไว้ตรง ๆ ว่า "An agent can't generate intent, because intent is the one input that has to come from you." แล้วตอกอีกประโยคว่า "A guess about intent isn't the intent."

ถ้าเราถาม agent ว่าทำไมโค้ดตรงนี้ถึงเขียนแบบนี้ Addy บอกว่ามันจะแต่งเหตุผลที่ฟังดูมั่นใจขึ้นมา "which is worse than admitting it doesn't know" ตรงนี้คือจุดที่หนี้ก้อนนี้เชื่อมกับโพสต์ Cognitive Surrender เพราะเหตุผลที่ฟังขึ้นกับเหตุผลที่จริงหน้าตาเหมือนกัน ถ้าไม่มีของจริงไว้เทียบ เราก็จะรับฉบับที่ฟังขึ้นไปใช้ แล้วรอบถัดไปมันก็กลายเป็น "เหตุผล" ของระบบไปโดยปริยาย

งานดูเสร็จ เพราะช่องว่างถูกอุดด้วยคำตอบที่ฟังขึ้น

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

ปัญหาคือค่าเหล่านั้นดีพอให้ผ่านรีวิว และดูเหมือนมีคนคิดมาแล้ว สมมติ agent เลือกให้ทุก role กด export ได้ เพราะหน้ารายงานเดิมก็เปิดให้ทุก role อยู่แล้ว ฟังขึ้นมาก แต่การดูบนจอกับการได้ไฟล์ทั้งก้อนกลับบ้านอาจไม่เท่ากันในสายตาลูกค้า และไม่มี test ไหนจะล้มเพราะเรื่องนี้ test ที่ agent เขียนจะยืนยันว่าทุก role กดได้ด้วยซ้ำ

Addy ยกอาการใกล้เคียงกันไว้ว่า refactor ทำให้พฤติกรรมที่ผู้ใช้เห็นพังทั้งที่ test เขียว เพราะ test บันทึกพฤติกรรมเดิม ไม่ได้บันทึกเจตนา ผมขอต่อว่าในงานใหม่มันแย่กว่าอีกขั้น เพราะ test บันทึกคำเดาของ agent แล้วเอาสีเขียวมาประทับให้ดูเหมือนข้อตกลง

Addy ยังมองว่า intent debt คือปัญหาแบบ cognitive surrender ในระดับทีมที่ถูกเขียนลงไปในระบบแล้ว ซึ่งผมว่าตรงครับ คำเดาที่ไม่ถูกตรวจวันนี้ จะกลายเป็น "ระบบเดิมมันเป็นแบบนี้" ของคนที่มาทีหลัง

ลูกค้าไม่ได้ส่ง intent มาให้เรา

ส่วนนี้ไม่ได้มาจากบทความของ Addy ครับ แต่เป็นส่วนที่ผมอยากเขียนที่สุด

ลูกค้าแทบไม่เคยส่งเจตนามาตรง ๆ สิ่งที่ส่งมาคือทางแก้ที่เขานึกออก "ขอปุ่ม export" "ขอแจ้งเตือนทางอีเมล" ผมไม่ได้โทษลูกค้านะครับ ข้อจำกัดหลายข้อปกติเกินกว่าที่เขาจะนึกว่าต้องบอก

ปุ่ม export อาจมีไว้ให้ฝ่ายบัญชีปิดงบ ซึ่งตัวเลขต้องตรงกับอีกระบบ หรือให้หัวหน้าเปิดดูในประชุม ซึ่งสิ่งที่ต้องการจริงอาจไม่ใช่ไฟล์เลย

เมื่อก่อนช่องว่างพวกนี้มีตัวกรองตามธรรมชาติ ก็คือ junior ในทีม น้องเขียนไปถึงจุดหนึ่งแล้วเดินมาถามว่า "พี่ครับ ถ้าเป็น user ธรรมดาให้กดได้ไหม" คำถามแบบนี้ขัดจังหวะก็จริง แต่มันคือระบบเตือนหนี้ที่ดีมาก เพราะการเขียนเองช้าพอที่คนจะชนช่องว่างแล้วรู้สึกว่าติด

Agent ไม่ได้เดินมาถึงโต๊ะผมแบบนั้นครับ มันเลือกให้แล้ว และ PR ก็มาพร้อมคำอธิบายที่ดูครบ น้องที่ยังไม่มีสัญชาตญาณว่าข้อไหนเป็นเรื่องธุรกิจ อาจไม่เห็นด้วยซ้ำว่าเมื่อกี้มีการตัดสินใจเกิดขึ้น สิ่งที่ผมกังวลจึงไม่ใช่โค้ดผิด แต่คือคำถามที่หายไป โดยไม่รู้ว่าหายไปกี่ข้อแล้ว

หนี้ก้อนนี้มีคนจ่ายหลายหมวกครับ PO จ่ายเรื่องเพื่ออะไร ปัญหาของใคร วัดความสำเร็จอย่างไร PM จ่ายเรื่องในกรอบไหน ขอบเขต สิ่งที่ตกลงกับลูกค้า และสิ่งที่รอคำตอบอยู่ Dev จ่ายเรื่องห้ามพังอะไร ข้อจำกัดที่อ่านจากไฟล์เดียวไม่เห็น และสิ่งที่เคยลองแล้วไม่เวิร์ก ในทีมเล็ก สามหมวกนี้บางทีอยู่บนหัวผมคนเดียว ซึ่งยิ่งอันตราย เพราะทุกอย่างอยู่ในหัวครบจนรู้สึกว่าทุกคนน่าจะรู้ แต่ทั้งน้องในทีมและ agent ไม่ได้อยู่ในหัวผม

แล้วถ้าลูกค้าก็ยังไม่รู้ล่ะ คำตอบของผมคล้ายเรื่องยอดหนี้ในโพสต์ที่แล้ว "ยังไม่รู้" เป็น intent ที่ใช้ได้ ถ้าถูกเขียนไว้ เช่น "ระหว่างรอลูกค้ายืนยันเรื่องสิทธิ์ ให้จำกัดที่ admin ก่อน แล้วแยกส่วนนี้ให้เปลี่ยนง่าย" ประโยคนี้คือสมมติฐานที่มีเจ้าของ ต่างจากคำเดาที่ฝังในโค้ดโดยไม่มีใครรู้ตัวมาก

จากจ่ายนาน ๆ ที เป็นจ่ายทุก session

ทำไมเพิ่งมาเป็นเรื่องใหญ่ตอนนี้ ทั้งที่เอกสารไม่ครบกันมานานแล้ว คำอธิบายของ Addy ชัดมากครับ แต่ก่อนทีมพกเจตนาไว้ในหัวคนและเอกสารเก่า ๆ มันแพงและตกหล่น แต่พอใช้ได้ แต่ "An agent starts most sessions cold." อะไรที่ไม่ได้เขียนไว้ในที่ซึ่งมันอ่านได้ สำหรับมันก็เท่ากับไม่มี

ประโยคที่ผมว่าเจ็บที่สุดคือ "Un-externalized intent used to cost you once in a while, at onboarding or after someone left. Now you pay it every session, multiplied by every agent you run."

ในมุม PM ผมเห็นเป็นตัวคูณครับ กฎข้อเดียวในหัวผม ตอนนี้ต้องเล่าใหม่ทุก session หรือปล่อยให้มันเดาใหม่ทุกครั้ง และถ้าสั่ง agent หลายตัวพร้อมกัน แต่ละตัวก็อาจเดาไม่เหมือนกัน

เขียน prompt คือการคิด ไม่ใช่การพิมพ์

ถ้าโพสต์นี้จะเหลือไว้ได้เรื่องเดียว ผมอยากให้เป็นเรื่องนี้ครับ เรามักพูดถึง critical thinking ว่าเป็นสิ่งที่ทำหลังเห็นผลลัพธ์ ทั้งตรวจ สงสัย และถามกลับ แต่ผมเริ่มคิดว่าจุดที่คุ้มที่สุดอาจอยู่ก่อนหน้านั้น เพราะเราตรวจงานได้แค่เทียบกับสิ่งที่รู้ว่าต้องการ ถ้ายังไม่รู้ การรีวิวก็เหลือแค่ดูว่างานสวยไหม

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

ลองเทียบ prompt ของงาน export เดียวกันดู

เพิ่มปุ่ม export รายงานยอดขายเป็นไฟล์

กับ

ฝ่ายบัญชีต้องใช้รายงานยอดขายรายเดือนไปกระทบยอดตอนปิดงบ เพิ่ม export เป็น CSV ในหน้ารายงาน ตัวเลขต้องตรงกับบนจอ ตัดรอบตามเวลาประเทศไทย ให้เฉพาะ role บัญชีและ admin กดได้ และไฟล์ต้องไม่มีเบอร์โทรหรืออีเมลของลูกค้าปลายทาง รอบนี้ยังไม่ทำ PDF เรื่องจำนวนแถวสูงสุดรอลูกค้ายืนยัน อย่าเลือกเอง ให้ใส่ไว้ในรายการคำถาม ก่อนลงมือ ขอให้สรุปสมมติฐานทั้งหมดให้อ่านก่อน

แบบที่สองยาวกว่าไม่กี่บรรทัด แต่ส่วนที่แพงไม่ใช่การพิมพ์ครับ ส่วนที่แพงคือต้องไปถามให้รู้ว่าฝ่ายบัญชีเป็นคนใช้ ต้องตัดสินเรื่องข้อมูลส่วนตัว และต้องยอมรับว่ายังไม่รู้เรื่องจำนวนแถว ทั้งหมดนี้เป็นงานคิดของคน

ผมไม่ได้เสนอให้กลับไปเขียน spec สามสิบหน้านะครับ Addy เองก็ยังยืนยันว่า spec ละเอียดแค่ไหนก็เก็บได้ไม่หมด แต่เขาเติมอีกด้านว่าการเก็บไม่หมดไม่ได้แปลว่าไม่ต้องเก็บเลย ผมจึงตั้งเป้าที่ส่วนซึ่ง agent เดาผิดแล้วเจ็บที่สุด ส่วนงานที่ลูกค้าบอกไม่ได้จนกว่าจะเห็นของ prototype จาก agent ก็ช่วยดึง intent ได้ดี ขอแค่จำว่ามันคือคำถาม ไม่ใช่คำตอบ

Intent Checklist ก่อนสั่ง Agent

รายการนี้ผมเรียบเรียงเองครับ ข้อ 2 และ 3 ได้แรงบันดาลใจจากสิ่งที่ Addy บอกว่า spec ที่ดีควรมี คือเป้าหมาย ข้อจำกัด สิ่งที่ต่อรองไม่ได้ และนิยามของคำว่าเสร็จ ผมไม่ได้ใช้ทุกข้อกับทุกงาน แต่ถ้างานแตะเงิน สิทธิ์ ข้อมูลลูกค้า หรือสิ่งที่ลูกค้าจะเห็น ผมตั้งใจไล่ให้ครบ

  1. งานนี้แก้ปัญหาอะไร ให้ใคร เขียนเป็นปัญหาของคน ไม่ใช่รายการสิ่งที่ต้องสร้าง
  2. เสร็จแปลว่าอะไร ต้องตรวจได้ และมากกว่าแค่ "ทำงานได้"
  3. อะไรต่อรองไม่ได้ สิทธิ์ ข้อมูลส่วนตัว และสิ่งที่ตกลงกับลูกค้าไว้แล้ว
  4. ถ้าต้องแลก เรายอมแลกอะไร ถ้าไม่บอก agent จะเลือกให้
  5. อะไรที่รอบนี้ไม่ทำ เขียนขอบเขตด้านนอกให้ชัดเท่าด้านใน
  6. อะไรที่ยังไม่รู้ ระบุไปเลยว่าข้อนี้ให้หยุดถาม หรือให้สมมติแล้วจดไว้
  7. ถ้าเหตุผลนี้ไม่อยู่ใน prompt มันจะเดาว่าอะไร ถ้าคำเดาที่นึกออกน่ากลัว ประโยคนั้นต้องถูกเขียนลงไป

แล้วผมจะปิดท้ายด้วยการขอให้ agent ลิสต์สมมติฐานที่ใช้ทั้งหมด และอ่านรายการนั้นก่อนอ่าน diff มันอาจลิสต์ไม่ครบ แต่ผมได้เริ่มตรวจจากคำถามว่า "เราตั้งใจแบบนี้หรือเปล่า" ไม่ใช่ "โค้ดนี้ดูดีไหม"

จ่ายหนี้ให้ถูกที่

เจตนาที่อยู่แค่ใน prompt จะหายไปพร้อม session ครับ Addy สรุปทางออกไว้ว่า "take the intent out of your head and put it somewhere an agent can read" และเสนอไว้สี่ทาง

เขียน spec เพื่อบอกเจตนา ไม่ใช่วิธีทำ ใบงานของผมจะเริ่มที่ปัญหาและข้อจำกัด ไม่ใช่รายการหน้าจอ

ใช้ AGENTS.md เป็นสมุดบัญชีเจตนา ไม่ใช่ไฟล์ config Addy อยากให้ไฟล์นี้บันทึก convention ประโยคแบบ "เราไม่ทำแบบนี้เพราะ..." และข้อจำกัดที่มองไม่เห็นจากไฟล์ใดไฟล์หนึ่ง ผมจะเริ่มจากสิ่งที่ต้องบอกซ้ำบ่อย ๆ

จดการตัดสินใจตอนที่มันเกิด Addy แนะนำ decision log แบบเบา ๆ อย่าง ADR สำหรับผมรวมถึงข้อตกลงกับลูกค้าด้วย ถ้าตกลงกันทางโทรศัพท์แล้วไม่ถูกจด ก็เท่ากับทีมไม่เคยรับรู้

ให้วงจรการเรียนรู้เขียนกลับลงมา จบ session แล้วบันทึกสาเหตุจริงของความผิดพลาด และสิ่งที่ลองแล้วไม่เวิร์กเพราะอะไร

ส่วนอีกสามข้อนี้ผมเพิ่มเองครับ

ยืนยันกับลูกค้าด้วยประโยคสั้น ๆ เช่น "ผมเข้าใจว่าไฟล์นี้ให้ฝ่ายบัญชีใช้ปิดงบ จึงให้เฉพาะบัญชีกับ admin กดได้ ถูกไหมครับ" agent ช่วยไล่หาคำถามให้ผมได้ แต่คนตอบต้องเป็นลูกค้า

รีวิว intent ของน้องในทีม ไม่ใช่แค่รีวิว PR สำหรับงานสำคัญ ผมจะขอให้น้องเขียน intent ห้าบรรทัดมาคุยกันก่อนสั่ง agent ซึ่งเร็วกว่ารีวิว diff ยาว ๆ แล้วค่อยบอกว่าโจทย์ผิด และเป็นทักษะที่ผมคิดว่าจะมีค่าขึ้นเรื่อย ๆ

อย่าให้ agent เขียน "ทำไม" ย้อนหลังโดยไม่มีคนยืนยัน การสั่งว่า "ช่วยเขียน ADR ให้ PR นี้" สะดวกมาก แต่ถ้าเหตุผลไม่อยู่ใน input มันก็อาจแต่งที่ฟังขึ้นมาให้ เราจะได้เอกสารที่ดูเหมือนจ่ายหนี้แล้ว ทั้งที่หนี้ยังอยู่ครบ

สัญญาณว่าหนี้ก้อนนี้สูงแล้ว

Addy บอกว่า intent debt ไม่ค่อยแสดงตัวเป็นความติดขัด "It shows up as a particular kind of helplessness." ตัวอย่างของเขาคือ guard clause ที่ถูก agent ลบโดยไม่มีบันทึกว่ามีไว้ทำไม และ design ที่มีเหตุผลเดียวคือ agent เสนอมาและดูโอเคดี ผมขอเพิ่มอีกสองข้อ

ข้อแรกคือ ลูกค้าเซอร์ไพรส์กับสิ่งที่เราคิดว่าเขาอนุมัติแล้ว ถ้าตอน UAT ลูกค้าถามว่า "ทำไมตรงนี้เป็นแบบนี้" แล้วทีมตอบได้แค่ว่าระบบออกมาแบบนี้ นั่นคือเราเอาคำเดาไปให้ลูกค้าตรวจแทนการตัดสินใจ

ข้อที่สองคือ ต้องบอก agent เรื่องเดิมซ้ำใน session ใหม่ ปัญหาไม่ได้อยู่ที่มันลืม เพราะมันไม่เคยรู้ ปัญหาคือเรายังไม่ได้เขียนลงไป

ท้ายที่สุด

ผมยังจะเขียน prompt สั้น ๆ ในงานเล็กต่อไปครับ สิ่งที่อยากเปลี่ยนคือการรู้ตัวว่าตอนไหนผมกำลังมอบหมายการลงมือ และตอนไหนกำลังมอบหมายการตัดสินใจโดยไม่ได้ตั้งใจ

Addy เขียนไว้ว่า "Code is the answer; the intent was the question it was meant to solve." เราได้คำตอบที่เรียบร้อยจาก prompt ที่แย่ได้ แต่อาจไม่รู้ว่าคำถามคืออะไร และเขาปิดท้ายว่า "Write down the why, because it's becoming the most valuable thing you can leave in the repo."

งานที่เหลือให้เราจึงไม่ใช่งานเล็กเลยครับ คือฟังคนอื่นให้เข้าใจ ถามคำถามที่อึดอัด กล้าบอกว่ายังไม่รู้ และรับผิดชอบกับสิ่งที่เลือก ทั้งหมดนี้คือ critical thinking ที่เริ่มตั้งแต่ก่อนพิมพ์ตัวอักษรแรก

หนี้ก้อนอื่นเรายังพอให้เครื่องมือช่วยผ่อนได้ แต่ก้อนนี้มีชื่อเราเป็นผู้กู้คนเดียว

ดังนั้นก่อนกดส่ง prompt ถัดไป ผมจะถามตัวเองแค่ว่า ถ้า agent เติมช่องว่างในนี้ด้วยคำตอบที่ฟังขึ้นที่สุด ผมจะรู้ไหมว่ามันเลือกอะไรแทนผมไปบ้าง

ถ้ารู้ นั่นคือการมอบหมายงานครับ ถ้าไม่รู้ ก็ยังมีเวลาคิดอีกรอบก่อนจะกด Enter