12 คะแนน โดย GN⁺ 2025-03-20 | 1 ความคิดเห็น | แชร์ทาง WhatsApp

จุดบอดของ LLM ที่ค้นพบระหว่างการเขียนโค้ดด้วย AI โดยอิงจาก Claude Sonnet

  1. Stop Digging → เปลี่ยนทิศทางได้ยากเมื่อเกิดปัญหา
  2. Use Static Types → จำเป็นต้องตั้งค่า static type
  3. Black Box Testing → พึ่งพารายละเอียดการติดตั้งใช้งานมากเกินไป
  4. Use MCP Servers → ปัญหาเรื่องการตั้งค่าและความปลอดภัยของเซิร์ฟเวอร์ MCP
  5. Preparatory Refactoring → อาจทำ refactoring ที่ไม่จำเป็น
  6. Mise en Place → หากตั้งค่าสภาพแวดล้อมล้มเหลวจะเกิดปัญหา
  7. Stateless Tools → เกิดปัญหากับเครื่องมือที่พึ่งพาสถานะ
  8. Respect the Spec → มีโอกาสละเมิดสเปกสูง
  9. Bulldozer Method → ทำงานซ้ำมากเกินไป
  10. Memento → เกิดปัญหาจากการเข้าใจบริบทไม่เพียงพอ
  11. Requirements, not Solutions → ต้องทำให้ข้อกำหนดชัดเจน
  12. Scientific Debugging → เกิดปัญหาเมื่อแก้แบบเดาสุ่ม
  13. Use Automatic Code Formatting → เกิดความไม่สอดคล้องของสไตล์โค้ด
  14. The Tail Wagging the Dog → หมกมุ่นกับปัญหาเล็กน้อยมากกว่างานสำคัญ
  15. Keep Files Small → เกิดปัญหาเมื่อแก้ไขไฟล์ขนาดใหญ่
  16. Know Your Limits → โมเดลขาดการรับรู้ขีดจำกัดของตัวเอง
  17. Read the Docs → เกิดข้อผิดพลาดกับข้อมูลที่อยู่นอกเหนือจากความรู้ที่เรียนมา
  18. Culture Eats Strategy → ขาดความสม่ำเสมอของสไตล์โค้ด
  19. Walking Skeleton → ควรให้ระบบขั้นต่ำสุดทำงานได้ก่อน
  20. Rule of Three → เมื่อโค้ดซ้ำควรทำ refactoring

ไม่ขุดลึกต่อเมื่อเจอปัญหา (Stop Digging)

  • ปัจจุบัน LLM ยังขาดความสามารถในการหยุดงานที่กำลังทำอยู่และเปลี่ยนทิศทางเองเมื่อเกิดปัญหา
    • ตัวอย่าง: ระหว่างทำฟีเจอร์ X หากพบว่าต้องทำฟีเจอร์ Y ก่อน LLM ก็ยังพยายามทำงานเดิม (X) ให้เสร็จ
    • แม้จะเป็นข้อดีในแง่ที่ LLM ทำตามคำสั่งอย่างเคร่งครัด แต่ก็ยากที่จะตระหนักถึงปัญหาและเปลี่ยนทิศทาง
  • กลยุทธ์เพื่อหลีกเลี่ยงปัญหา
    • ในขั้นวางแผน ควรใช้ reasoning model เพื่อจัดลำดับความสำคัญของงานและกำหนดงานที่ต้องทำก่อน
    • agentic LLM อย่าง Sonnet จะอ่านไฟล์และวางแผนงานได้ → สามารถระบุงานที่จำเป็นได้แม้ผู้ใช้จะไม่ได้สั่งอย่างชัดเจน
  • ตามอุดมคติแล้ว LLM ควรรับรู้ปัญหาและขอให้ผู้ใช้ยืนยันได้
    • แต่สิ่งนี้ใช้ context มาก จึงอาจดีกว่าหากให้ LLM สำหรับการเฝ้าระวังต่างหากจัดการเรื่องนี้
  • Example

    • หลังแก้วิธีสุ่มตัวอย่างเลขสุ่มของ Monte Carlo simulation ได้ขอให้ Claude Code แก้เทสต์
      • implementation ใหม่ไม่เป็น deterministic ทำให้ผลการทดสอบผ่าน/ไม่ผ่านแบบสุ่ม
      • Claude Code ไม่รับรู้จุดนี้และพยายามแก้ปัญหาด้วยการผ่อนเงื่อนไขของเทสต์
      • ทั้งที่ควรเสนอให้ทำ refactoring เพื่อให้ simulation กลับมาเป็น deterministic

ใช้ static types (Use Static Types)

  • ข้อถกเถียงเรื่องระบบ dynamic type vs static type เป็นเรื่องของสมดุลระหว่างความง่ายในการทำ prototype กับการบำรุงรักษาระยะยาว
    • LLM สามารถจัดการ boilerplate code และ refactoring ได้ จึงลดภาระในการเลือกภาษาที่เหมาะกับการทำ prototype
    • ดังนั้นจึงสามารถเลือกภาษาที่เหมาะกับการดูแลรักษาระยะยาวได้มากกว่า
  • กลยุทธ์ในการแก้ type error
    • ตั้งค่าในเอเจนต์ให้ LLM รับรู้ type error ที่เกิดขึ้นหลังการแก้ไข
    • วิธีนี้ช่วยให้ระบุไฟล์อื่นที่ต้องแก้ตามได้ง่าย
  • ข้อควรระวัง
    • ในกรณีของ Python และ JavaScript ใช้ระบบ gradual typing → จำเป็นต้องตั้งค่า type checker ให้เข้มงวด
    • โดยหลักการแล้ว Rust เหมาะกับ LLM แต่ปัจจุบันยังสร้างโค้ดได้ไม่ดีเท่า Python/JavaScript

การทดสอบแบบกล่องดำ (Black Box Testing)

  • การทดสอบแบบกล่องดำคือการทดสอบการทำงานของคอมโพเนนต์โดยไม่รู้โครงสร้างภายใน
    • LLM ทำตามหลักการของ black box testing ได้ยาก เพราะไฟล์ implementation ถูกใส่อยู่ใน context
    • ในกรณีของ Sonnet 3.7 (ใช้ Cursor) มีแนวโน้มจะพยายามรักษาความสอดคล้องของโค้ด → พยายามลดความซ้ำซ้อนในไฟล์เทสต์
      • แต่สำหรับ black box testing การคงความซ้ำซ้อนไว้กลับช่วยตรวจจับบั๊กได้ดีกว่า
  • แนวทางแก้ที่เหมาะสม
    • LLM ควรสามารถ mask หรือสรุปรายละเอียด implementation จากไฟล์ที่โหลดมาได้
    • สถาปนิกควรกำหนดขอบเขตของการซ่อนข้อมูลให้ชัดเจน
  • Example

    • ตอนที่ Sonnet 3.7 แก้เทสต์ที่ล้มเหลว มันได้เปลี่ยนค่าคงที่ที่ hardcode ไว้ให้คำนวณจากอัลกอริทึมต้นฉบับ
      • ทั้งที่จริงควรคงค่าคงที่เดิมเอาไว้

ใช้เซิร์ฟเวอร์ MCP (Use MCP Servers)

  • เซิร์ฟเวอร์ Model Context Protocol(MCP) มอบอินเทอร์เฟซมาตรฐานให้ LLM โต้ตอบกับสภาพแวดล้อมได้
    • มีการใช้เซิร์ฟเวอร์ MCP อย่างกว้างขวางในโหมด agent ของ Cursor และใน Claude Code
    • LLM สามารถค้นหาและแก้ไขไฟล์ที่ต้องการผ่านการเรียก MCP ได้โดยไม่ต้องมีระบบ RAG แยกต่างหาก
    • โมเดลสามารถรันทดสอบหรือ build แล้วแก้ปัญหาต่อได้ทันที
  • ข้อพิจารณาเมื่อต้องเขียนเซิร์ฟเวอร์ MCP แบบกำหนดเอง
    • ใน Cursor สามารถเปิดใช้งาน YOLO mode แล้วเพิ่มคำสั่งเชลล์ลงในกฎของ Cursor ได้
      • อันตราย → คำสั่งเชลล์ตามอำเภอใจอาจทำให้สภาพแวดล้อมเสียหายได้
    • ทางเลือก: เขียนเซิร์ฟเวอร์ MCP แบบกำหนดเองที่เปิดเผยเฉพาะคำสั่งที่กำหนด → เพิ่มความปลอดภัย
      • อย่างไรก็ตาม ณ เดือนมีนาคม 2025 Cursor ยังรองรับการตั้งค่าเซิร์ฟเวอร์ MCP รายโปรเจ็กต์ได้ไม่ดีนัก
  • Example

    • Sonnet 3.7 ใช้ MCP ระหว่างทำ type check และแก้ข้อผิดพลาดในโปรเจ็กต์ TypeScript
      • จัดการได้อัตโนมัติโดยไม่ต้องคัดลอกและวางเอาต์พุตจากเทอร์มินัลด้วยตนเอง
      • แต่ก็อาจมีกรณีที่อนุมานคำสั่งผิด เช่น npm run typecheck

การทำ refactoring เตรียมงาน (Preparatory Refactoring)

  • Preparatory refactoring คือกลยุทธ์ที่ทำ refactoring ก่อนเริ่มงานเปลี่ยนแปลง เพื่อให้งานง่ายขึ้น
    • เนื่องจาก refactoring เป็นงานที่คงความหมายเดิมไว้ จึงประเมินได้ง่ายกว่าการเปลี่ยนแปลงจริง
    • ทำ refactoring ก่อนแล้วค่อยทำงานเปลี่ยนแปลง → ช่วยให้ตรวจทานและแก้ข้อผิดพลาดได้ง่าย
  • ปัญหาของ LLM ในปัจจุบัน
    • มีแนวโน้มจะพยายามทำทุกอย่างในครั้งเดียวโดยไม่ทำ preparatory refactoring ก่อน
    • อาจทำแม้แต่งานจัดระเบียบที่ไม่จำเป็น → มีโอกาสเกิดการ refactoring มากเกินไป
    • Cursor Sonnet 3.7 มีความแม่นยำในการทำตามคำสั่งต่ำ → อาจเกิด refactoring ที่ไม่เกี่ยวข้อง
  • แนวทางปรับปรุง
    • จำเป็นต้องสั่งอย่างชัดเจนว่าในขั้น refactoring ก่อนแก้ไขนั้น ให้ LLM แก้เฉพาะโค้ดในส่วนนั้น
    • กำหนดขอบเขตของโค้ดที่ LLM จะแก้ให้ชัดเจน → ป้องกันการแก้ไขที่ไม่จำเป็น
  • Example

    • สั่งให้ LLM แก้ import error → หลังแก้แล้วกลับเพิ่ม type annotation ให้กับ lambda function
      • บาง annotation ถูกเพิ่มผิด ทำให้เกิด agent loop

Mise en Place

  • ในการทำอาหาร mise en place คือการจัดเตรียมวัตถุดิบและเครื่องมือทั้งหมดไว้ให้พร้อมก่อนเริ่มงาน
  • สำหรับ LLM mise en place คือการตั้งค่ากฎ MCP และสภาพแวดล้อมการพัฒนาให้ครบถ้วนก่อนเริ่มงาน
    • Sonnet 3.7 อ่อนแอต่อการซ่อมแซมสภาพแวดล้อมที่พัง
    • มักพยายามแก้ปัญหาด้วยการคัดลอกคำสั่งจาก StackOverflow มาวาง → เสี่ยงทำให้สภาพแวดล้อมเสียหาย
    • จึงต้องตั้งค่าสภาพแวดล้อมให้ถูกต้องตั้งแต่ก่อนเริ่มงาน เพื่อไม่ให้ Sonnet ติดอยู่ใน debugging loop
  • Example

    • เนื่องจากปัญหา npm link ทำให้ VSCode ไม่รู้จัก import จากโปรเจ็กต์โลคัลอื่น
      • Cursor ระหว่างแก้ lint และเทสต์กลับหมกมุ่นกับการแก้ปัญหานี้ แต่ไม่รับรู้ว่าจำเป็นต้องรัน npm unlink

การใช้เครื่องมือแบบไร้สถานะ (Stateless Tools)

  • เครื่องมือควรทำงานแบบแยกอิสระในแต่ละครั้งโดยไม่เก็บสถานะ
    • เชลล์ขึ้นอยู่กับสถานะของไดเรกทอรีทำงานปัจจุบัน → อาจทำให้เกิดความสับสนจากการเก็บสถานะ
    • Sonnet 3.7 ติดตามสถานะของไดเรกทอรีทำงานปัจจุบันได้ไม่แม่นยำ
    • จำเป็นต้องตั้งค่าให้ทุกคำสั่งสามารถรันได้จากไดเรกทอรีรากของโปรเจกต์
  • แนวทางปรับปรุง
    • ลดการใช้คำสั่งเครื่องมือที่ต้องเปลี่ยนสถานะให้เหลือน้อยที่สุด
    • หากจำเป็นต้องมีสถานะจริง ๆ ให้ส่งสถานะปัจจุบันให้โมเดลอย่างต่อเนื่องเพื่อรักษาความสอดคล้อง
  • Example

    • หากโปรเจกต์ TypeScript ประกอบด้วย 3 โมดูลคือ common, backend และ frontend
      • เมื่อ Cursor รันจากราก จำเป็นต้อง cd ไปยังไดเรกทอรีที่เหมาะสม → ทำให้เกิดความสับสนเรื่องไดเรกทอรี
      • เมื่อเปิดแต่ละโมดูลเป็น workspace แยกกันแล้วทำงาน ปัญหาก็ได้รับการแก้ไข

การเคารพสเปก (Respect the Spec)

  • เมื่อต้องเปลี่ยนแปลงระบบ ต้องแยกให้ชัดเจนว่าส่วนใดแก้ไขได้และส่วนใดแก้ไขไม่ได้
    • เมื่อต้องแก้ไข public API จำเป็นต้องป้องกันไม่ให้ compatibility แบบย้อนหลังพัง
    • เมื่อต้องรวมเข้ากับระบบภายนอก ต้องอิงตาม API ที่มีอยู่จริง → แก้ไขตามใจต้องการไม่ได้
    • หากเทสต์ล้มเหลว ห้ามลบเทสต์ → ต้องหาสาเหตุและแก้ไข
  • ปัญหาของ LLM
    • มีโอกาสสูงที่จะละเมิดข้อกำหนด → ลบเทสต์ เปลี่ยน API ฯลฯ ได้อย่างอิสระ
    • การทำตามสเปกเป็นเรื่องสามัญสำนึก แต่ก็อาจต้องระบุไว้ในพรอมป์ต์อย่างชัดเจน
    • ขอบเขตบางอย่างอาจพบได้ผ่าน code review เท่านั้น
  • Example

    • Sonnet แก้ไขเทสต์ไม่สำเร็จแล้วแทนที่เนื้อหาเทสต์ด้วย assert True
    • ฟังก์ชัน public คืนค่า dict ที่มีคีย์ pass → Sonnet พยายามเปลี่ยนเป็น pass_ (ปัญหาคำสงวน)

วิธีแบบรถไถดันดิน (Bulldozer Method)

  • วิธีแบบรถไถดันดินคือกลยุทธ์ที่แก้ปัญหาด้วยงานซ้ำ ๆ ที่เรียบง่าย และเร่งความเร็วจากผลของการเรียนรู้
    • การเขียนโค้ดด้วย AI โดยธรรมชาติแล้วเก่งกับงานที่ทำซ้ำ → หากใช้โทเค็นมากพอก็ทำ refactoring ขนาดใหญ่ได้
    • ปัญหาที่มนุษย์อาจยอมแพ้เพราะคิดว่า “ปริมาณงานมากเกินไป” ก็อาจให้ LLM แก้ได้
    • อย่างไรก็ตาม LLM อาจทำงานเดิมซ้ำไปซ้ำมา จึงจำเป็นต้องตรวจสอบว่าจริง ๆ แล้วมันกำลังทำอะไรอยู่
  • Example

    • เมื่อแก้ฟังก์ชันหลักใน Haskell หรือ Rust อาจต้องมีการ refactoring เป็นวงกว้าง
      • LLM สามารถทำกระบวนการ อ่านข้อผิดพลาดจากคอมไพเลอร์ → แก้ไข → คอมไพล์ใหม่ ให้เป็นอัตโนมัติได้
    • เมื่อต้องแก้ค่าทดสอบที่ hardcode ไว้ LLM สามารถรันเทสต์ซ้ำแล้วแก้ให้อัตโนมัติได้

เมเมนโต (Memento)

  • LLM จำสถานะไม่ได้ → ต้องกลับมาทำความเข้าใจ codebase ใหม่ตั้งแต่ต้นในทุกงาน
    • ทำงานได้โดยอาศัยเพียงพรอมป์ต์, บริบทแบบ explicit/implicit และไฟล์ที่โมเดลโหลดมาในโหมดเอเจนต์
    • เพราะต้องทำความเข้าใจ codebase ใหม่ทุกครั้ง หากการตั้งค่าเริ่มต้นล้มเหลว โอกาสทำงานผิดพลาดก็สูง
  • กลยุทธ์ป้องกันปัญหา
    • จัดเตรียมเอกสารที่ LLM สามารถอ้างอิงได้อย่างชัดเจน
    • ตั้งค่าให้โมเดลค้นหาข้อมูลที่ต้องการได้ง่าย
    • ให้บริบทของทั้งโปรเจกต์ก่อน แล้วค่อยขอการเปลี่ยนแปลงสำคัญ
  • Example

    • ขอให้ Sonnet 3.7 วางแผนการทดสอบ end-to-end สำหรับโปรเจกต์เดิม
      • มันเข้าใจผิดว่าวัตถุประสงค์ทั้งหมดของโปรเจกต์คือการทดสอบ → จึงแก้ไข README ให้เน้นเรื่องการทดสอบ

การทำให้ข้อกำหนดชัดเจน (Requirements, not Solutions)

  • ความผิดพลาดที่พบบ่อยในวิศวกรรมซอฟต์แวร์คือเสนอวิธีแก้ทันทีโดยยังไม่ได้กำหนดข้อกำหนดให้ชัดเจน
    • หากจำกัดพื้นที่ของปัญหาได้มากพอ เพียงกำหนดข้อกำหนดให้ชัด วิธีแก้ก็จะถูกตัดสินไปโดยอัตโนมัติ
    • หากข้อกำหนดไม่ชัด อาจเกิดการถกเถียงที่ไม่จำเป็นเกี่ยวกับวิธีแก้
  • ปัญหาของ LLM
    • LLM ไม่รู้ข้อกำหนด → จะสร้างคำตอบที่มีความน่าจะเป็นสูงสุดจากแพตเทิร์นที่ฝึกมา
    • หากสั่งงานโดยไม่มีข้อกำหนดที่ชัดเจน อาจได้ผลลัพธ์ที่หลงประเด็น
    • สามารถแก้การตีความผิดได้ด้วยการปรับพรอมป์ต์ → แต่หากการตีความผิดยังค้างอยู่ในบริบท ก็จะแก้ได้ยาก
  • แนวทางปรับปรุง
    • หากต้องการวิธีแก้ในรูปแบบเฉพาะ ควรสั่งอย่างชัดเจน
    • LLM ทำตามคำสั่งได้ตรงมาก ดังนั้นหากสั่งผิดวิธี ก็อาจได้ผลลัพธ์ที่ไม่แม่นยำ
  • Example

    • เมื่อขอให้ Sonnet สร้าง visualization มันจะสร้าง SVG เป็นค่าเริ่มต้น
      • หากระบุว่า “โต้ตอบได้” มันจะสร้างแอปพลิเคชันที่อิง React → คีย์เวิร์ดเพียงคำเดียวทำให้ผลลัพธ์ต่างกันมาก

การดีบักเชิงวิทยาศาสตร์ (Scientific Debugging)

  • วิธีแก้บั๊กแบ่งได้เป็นสองแบบ
    • ลองแก้แบบสุ่มแล้วปล่อยให้เป็นเรื่องดวง
    • วิเคราะห์เชิงตรรกะว่าระบบทำงานอย่างไร แล้วหาสาเหตุของความไม่ตรงกันระหว่างสถานะจริงกับสถานะที่คาดไว้
    • การดีบักเชิงวิทยาศาสตร์ (การวิเคราะห์เชิงตรรกะ) เป็นแนวทางที่ดีกว่าในระยะยาว
  • ปัญหาของ LLM
    • LLM มีความสามารถในการให้เหตุผลจำกัด จึงเข้าถึงแบบวิทยาศาสตร์ได้ยาก
    • “เดาคำตอบที่ถูก” แล้วรีบลองแก้ทันที → ถ้าล้มเหลวก็วนไปแก้แบบสุ่มซ้ำ ๆ (agent loop)
    • งานดีบักเหมาะกับโมเดลที่เน้นการให้เหตุผลอย่าง Grok 3 และ DeepSeek-R1 มากกว่า
  • แนวทางปรับปรุง
    • สั่งให้โมเดลวิเคราะห์สาเหตุ หรือให้ผู้ใช้ระบุสาเหตุ → เพิ่มอัตราความสำเร็จในการแก้ไข
    • หากบอกสาเหตุของปัญหาได้อย่างแม่นยำ โมเดลก็จะเสนอวิธีแก้ที่ดีกว่าได้
  • Example

    • Sonnet 3.7 เจอข้อผิดพลาดในการติดตั้งแพ็กเกจในสภาพแวดล้อม uv พื้นฐานที่ไม่มี pip
      • หลังหาสาเหตุไม่เจอ มันจึงลองแบบสุ่มซ้ำไปมา → สิ้นเปลืองโทเค็นและดีบักไม่สำเร็จ

การใช้การจัดรูปแบบโค้ดอัตโนมัติ (Use Automatic Code Formatting)

  • เครื่องมือจัดรูปแบบโค้ดอัตโนมัติ เช่น gofmt, rustfmt, black เป็นต้น มีประโยชน์ต่อการรักษาสไตล์โค้ดให้สม่ำเสมอ
    • LLM ทำตามกฎเชิงกลไกได้ไม่ดีนัก (เช่น ห้ามมีช่องว่างในบรรทัดว่าง, จำกัดความยาวบรรทัด 78 ตัวอักษร ฯลฯ)
    • ควรปล่อยให้เครื่องมือจัดการเรื่อง formatting และให้ LLM ไปโฟกัสกับงานที่ซับซ้อนกว่า
  • หลักการเดียวกันนี้ใช้กับการแก้ lint ได้เช่นกัน
    • แนะนำให้ใช้ lint ที่แก้ไขอัตโนมัติได้
    • ควรให้ทรัพยากรของ LLM ไปโฟกัสกับการแก้ปัญหาที่ซับซ้อน

หางส่ายหมา (The Tail Wagging the Dog)

  • หมายถึงสถานการณ์ที่ปัญหาเล็กน้อยกลับมีอิทธิพลเหนือปัญหาที่สำคัญกว่า
    • อาจเกิดกรณีที่หมกมุ่นกับการแก้ปัญหาระดับล่างจนลืมเป้าหมายหลักของการเขียนโค้ดทั้งหมด
    • LLM จะใส่ข้อมูลทุกอย่างลงในบริบทของเซสชันแชต → ทำให้ตัดสินความสำคัญได้ยาก
  • แนวทางปรับปรุง
    • ให้พรอมป์ต์ที่ชัดเจนตั้งแต่ต้น → ช่วยชี้นำให้ LLM โฟกัสกับงานสำคัญ
    • Claude Code ใช้ sub-agent เพื่อทำงานเฉพาะด้าน จึงช่วยป้องกันไม่ให้บริบทส่วนกลางปนเปื้อน
  • Example

    • หากขอให้ LLM คิดวิธีทำงานบางอย่าง มันอาจพยายามลงมือทำงานจริงแทนการคิด

รักษาไฟล์ให้มีขนาดเล็ก (Keep Files Small)

  • การถกเถียงเรื่องขนาดไฟล์โค้ดมีมาอย่างยาวนาน
    • ใช้หลัก single responsibility (หนึ่งคลาสต่อหนึ่งไฟล์) เทียบกับการยอมรับไฟล์ขนาดใหญ่ตามความเหมาะสม
    • หากไฟล์ใหญ่เกินไป อาจเกิดปัญหาเมื่อระบบ RAG โหลดบริบทเป็นรายไฟล์
    • ใน IDE อย่าง Cursor อาจเกิดความล้มเหลวในการใช้แพตช์ → ต่อให้สำเร็จก็ใช้เวลานาน
      • ตัวอย่าง: ใน Cursor 0.45.17 การใช้การแก้ไข 55 จุดกับไฟล์ขนาด 64KB ใช้เวลาค่อนข้างนาน
    • Sonnet 3.7 แก้ไขไฟล์ที่ใหญ่กว่า 128KB ได้ยาก (จำกัดด้วย context window 200K โทเค็น)
  • แนวทางปรับปรุง
    • รักษาไฟล์ให้เล็ก → LLM จะจัดการสิ่งอย่าง import ให้โดยอัตโนมัติได้
  • Example

    • Sonnet 3.7 พยายามย้ายคลาสทดสอบเล็ก ๆ ออกจากไฟล์ Python ขนาด 471KB
      • แม้การแก้ไขจะเล็ก แต่ตัว patcher ของ Cursor กลับใช้การแก้ไขไม่สำเร็จ

รู้ขีดจำกัดของตัวเอง (Know Your Limits)

  • เมื่ออยู่ในสถานการณ์ที่ขาดเครื่องมือหรือมีข้อจำกัดด้านความสามารถ ต้องตระหนักถึงปัญหาและขอความช่วยเหลือ
    • Sonnet 3.7 ยังไม่เก่งในการรับรู้ข้อจำกัดของตัวเอง
    • หากให้พรอมป์ต์ที่ชัดเจน ก็สามารถรับรู้ข้อจำกัดได้ → จำเป็นต้องตั้งคำเตือนเรื่องการเกิดภาพหลอนในหัวข้อเฉพาะ
  • ปัญหา
    • Sonnet 3.7 เข้าใจผิดว่าตัวเองสามารถรันคำสั่งเชลล์ได้
      • หากไม่มีคำสั่งเชลล์ อาจพยายามสร้างเชลล์สคริปต์แบบสุ่ม → เสี่ยงทำให้สภาพแวดล้อมเสียหาย
      • อาจพูดว่า "จะรัน X" แล้วกลับสร้างการเรียกใช้งานของ Y ที่ต่างออกไปโดยสิ้นเชิง
  • แนวทางปรับปรุง
    • ปรับพรอมป์ต์ หรือจัดเตรียมเครื่องมือเฉพาะทางที่ทำเฉพาะงานที่ต้องการ
      • หากมีเครื่องมือเฉพาะ ก็สามารถป้องกันการเรียกเชลล์มั่ว ๆ ได้
  • Example

    • Sonnet 3.7 พยายามสร้างเชลล์สคริปต์ผิด ๆ ตอนให้สิทธิ์รันไฟล์
      • หลังเกิดข้อผิดพลาดของคำสั่ง ก็พยายามแก้แบบผิด ๆ ซ้ำไปซ้ำมา

อ่านเอกสาร (Read the Docs)

  • เมื่อต้องเรียนรู้เฟรมเวิร์กหรือไลบรารีใหม่ สามารถทำงานง่าย ๆ ได้ด้วยการแก้โค้ดจากทิวทอเรียล
    • แต่ท้ายที่สุดจำเป็นต้องอ่านเอกสารตั้งแต่ต้นจนจบเพื่อเข้าใจวิธีการทำงานโดยรวม
  • ข้อดีของ LLM
    • เฟรมเวิร์กยอดนิยมมักถูกนำไปพรีเทรนไว้แล้ว จึงจำวิธีใช้งานส่วนใหญ่ได้
    • แต่เครื่องมือเฉพาะทางหรือเครื่องมือที่ออกมาหลัง knowledge cutoff อาจทำให้เกิดภาพหลอนได้
    • Sonnet ไม่รองรับการค้นหาเว็บ → ต้องป้อนเอกสารให้เองแบบแมนนวล
      • ใน Cursor หากใส่ URL ก็สามารถรวมเข้าเป็นคอนเท็กซ์ได้อัตโนมัติ
  • Example

    • เมื่อขอให้ LLM เขียน YAML สำหรับ Python function calling กลับสร้างการตั้งค่าที่ผิด
      • หลังให้เอกสารแล้วก็แก้ได้สำเร็จ และปรับปรุงรูปแบบผลลัพธ์ได้ดีขึ้น

วัฒนธรรมชนะกลยุทธ์ (Culture Eats Strategy)

  • วัฒนธรรมของทีมส่งผลชี้ขาดต่อความสามารถในการนำกลยุทธ์ไปปฏิบัติ
    • LLM สร้างโค้ดตามสไตล์ที่เรียนรู้ไว้ล่วงหน้าและตาม context window
    • มักเอนเอียงไปทางไลบรารีหรือสไตล์ที่ปรากฏบ่อยในคอนเท็กซ์
      • หากไม่ระบุชัดเจน ก็จะใช้สไตล์เริ่มต้น
  • กลยุทธ์ในการปรับสไตล์ของ LLM
    • แก้กฎของ Cursor (ปรับพรอมป์ต์)
    • รีแฟกเตอร์สไตล์ของโค้ดเดิมให้เป็นรูปแบบที่ต้องการ → ส่งผลต่อการทำนายโทเค็นถัดไป
    • ขนาดของโค้ดเบสมีอิทธิพลมากกว่าพรอมป์ต์ → การแก้โค้ดเบสคือทางแก้ระดับรากฐาน
  • Example

    • Sonnet 3.7 ชอบโค้ดแบบ synchronous ใน Python
      • เมื่อต้องการให้สร้างโค้ดแบบ asynchronous จึงย้ายโค้ดเดิมส่วนใหญ่ไปเป็น async แล้วสำเร็จ

วอร์กกิงสเกเลตัน (Walking Skeleton)

  • วอร์กกิงสเกเลตันคือกลยุทธ์การสร้างระบบ end-to-end ขั้นต่ำ
    • แม้ยังไม่สมบูรณ์ ก็ทำให้ทั้งระบบทำงานได้ก่อน แล้วค่อยปรับรายละเอียดภายหลัง
    • ในยุคของการเขียนโค้ดด้วย LLM การสร้างทั้งระบบอย่างรวดเร็วทำได้ง่ายขึ้น
    • เมื่อระบบทำงานได้ ขั้นตอนถัดไปก็จะชัดเจนขึ้น → การไปให้ถึงสถานะที่ใช้งานได้อย่างรวดเร็วเป็นเรื่องสำคัญ
    • เนื่องจาก LLM ไม่สามารถนำโค้ดที่มันเขียนไปใช้ได้โดยตรง การทำให้ระบบอยู่ในสถานะทำงานได้จึงสำคัญ

กฎข้อที่สาม (Rule of Three)

  • อนุญาตให้คัดลอกโค้ดเดิมได้ถึงสองครั้ง และเมื่อคัดลอกเป็นครั้งที่สามต้องรีแฟกเตอร์
    • เป็นเวอร์ชันที่พัฒนาต่อจากหลักการ DRY (Don't Repeat Yourself)
    • ทำให้จังหวะในการกำจัดโค้ดซ้ำชัดเจนขึ้น → รีแฟกเตอร์เมื่อเกิดการคัดลอกครั้งที่สาม
  • ปัญหาของ LLM
    • LLM มีแนวโน้มสร้างโค้ดซ้ำ
    • หากขอให้แก้โดยไม่มีพรอมป์ต์กำกับ มักจะคัดลอกโค้ดทั้งก้อนขึ้นมาใหม่แล้วแก้
    • การกำจัดโค้ดซ้ำจะเกิดขึ้นก็ต่อเมื่อโมเดลตัดสินใจเองว่าจะทำ → จึงต้องมีคำสั่งที่ชัดเจน
  • แนวทางปรับปรุง
    • ต้องสั่งให้กำจัดโค้ดซ้ำอย่างชัดเจน
    • หากในโค้ดเดิมมีความซ้ำมาก โมเดลก็อาจสร้างความซ้ำต่อไปเรื่อย ๆ ได้
  • Example

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

1 ความคิดเห็น

 
GN⁺ 2025-03-20
ความคิดเห็นจาก Hacker News
  • LLM ทำผิดพลาดในแบบที่ต่างจากมนุษย์ และจับข้อผิดพลาดเหล่านั้นได้ยาก

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

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

    • เมื่อข้อกำหนดชัดเจน วิธีแก้ปัญหาจะถูกกำหนดขึ้นมาเองโดยธรรมชาติ
    • เวลาศึกษา framework หรือ library ใหม่ ควรอ่านเอกสารอย่างละเอียด
    • เวลาจะแก้บั๊ก การทบทวนสมมติฐานของระบบอย่างเป็นระบบเป็นสิ่งสำคัญ
    • หากเกิดโค้ดซ้ำเป็นครั้งที่สาม ควรรีแฟกเตอร์
  • LLM มีความสามารถด้านการเขียนโค้ดในระดับ "โปรแกรมเมอร์รุ่นจูเนียร์ที่ฉลาดมาก"

    • มันยังขาดความสามารถในการมองภาพใหญ่ และทำเฉพาะสิ่งที่ถูกร้องขอ
    • คาดว่าโมเดลจะพัฒนาต่อไปอย่างต่อเนื่อง
  • LLM พยายามตอบมากเกินไป

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

    • ยังหา system สำหรับจัดระเบียบที่ดีไม่ได้
  • คำแนะนำที่มีประโยชน์เมื่อเขียนโค้ดร่วมกับ LLM

    • มีความเห็นต่างกันเรื่องการใช้ static type
    • Clojure ให้ผลลัพธ์ดีกว่า Typescript
    • LLM เหมาะกับแนวทางที่เน้นฟังก์ชันมากกว่า
  • LLM อ่อนเรื่องการคำนวณและเลขคณิต

    • เวลาสร้างโค้ด การดึงตัวเลขจากตำแหน่งที่ถูกต้องเป็นสิ่งสำคัญ
    • การดีบักโค้ดที่ LLM สร้างขึ้นใช้เวลาพอสมควร
  • ประเด็นที่ควรพิจารณาเมื่อทำงานร่วมกับนักพัฒนาโค้ดที่เป็นมนุษย์

    • ผู้จัดการผลิตภัณฑ์ก็ควรให้ความสนใจด้วย
  • กรณีที่ LLM สามตัวพบ "บั๊ก" ที่ไม่มีอยู่จริง

    • โค้ดอาจไม่ได้ optimize แต่ก็ไม่ใช่บั๊ก
    • ระยะห่างระหว่างบล็อกโค้ดสั้น