การสั่งให้ AI เอเจนต์ว่า "ถามคนเมื่อไม่แน่ใจ" นั้นใช้ไม่ได้ — ข้อจำกัดของ subagent ใน Claude Code และการออกแบบการมอบหมายงานใหม่ที่ตามมา

AIエージェント Claude Code サブエージェント 運用 設計

บทนำ

ฉันเคยเขียนไปแล้วสองครั้งเกี่ยวกับการแบ่ง AI ลูกน้อง (subagent) ตามบทบาท และการสร้างกลไกที่เข้าไปตัดหยุดพวกมันทันทีก่อนการทำงาน

สร้างกลไก “ไม่ให้ทำ” สำหรับผู้ช่วย AI เขียนโค้ด — จากคำแนะนำสู่การบังคับ แล้วพบว่าประตูนั้นเองก็มีช่องโหว่ข้าง

สร้างแพลตฟอร์มสตรีมมิงและธนาคารไปพร้อมกัน — จัดวางกองเอเจนต์ AI ให้ลงมือพัฒนาฟีเจอร์จริง แล้วปรับจูนการแบ่งงานด้วยการวัดผล

บทความนี้เป็นภาคต่อของทั้งสอง และในขณะเดียวกันก็เป็นการกลับด้านของทั้งสอง ที่ใจกลางของกฎที่ฉันประกอบขึ้นมา — ทั้งกองเอเจนต์และชั้นบังคับ — มีประโยคหนึ่งนั่งอยู่ ซึ่ง AI ลูกน้องไม่สามารถทำตามได้ในทางกายภาพ ต่อจากนี้คือเรื่องที่ฉันอ่านเอกสารทางการของ Claude Code ซ้ำเพื่อปักหมุดสาเหตุ รื้อการออกแบบการมอบหมายงานใหม่ และตรวจสอบมัน

ประโยคที่ใจกลางของกฎ

กฎการทำงานที่ฉันให้ AI ลูกน้องไว้เขียนว่า “เมื่อไม่แน่ใจในการตัดสิน อย่าตัดสินใจเอง — ให้ถามเจ้าของ” ฉันเคยคิดว่ามันเป็นประโยคที่สมเหตุสมผล ในทีมมนุษย์ก็จะพูดแบบเดียวกัน ถ้าไม่มั่นใจกับการตัดสินใจ อย่าดันไปข้างหน้า ให้ถาม

แต่ AI ลูกน้อง ไม่ได้ถูกส่งมอบเครื่องมือสำหรับถามมาตั้งแต่แรก ต่อไปนี้ ฉันยืนยันหลักฐานของเรื่องนี้ทีละข้อจากเอกสารทางการ (sub-agents สภาพแวดล้อมที่วัด Claude Code 2.1.215)

ข้อบกพร่องที่ 1: ไม่มีวิธีถาม (ข้อหลัก)

เอกสารทางการมีรายการเครื่องมือที่ถูกถอดออกจาก subagent ทั้งแบบมีเงื่อนไขและไม่มีเงื่อนไข ฉันอ้างต้นฉบับตามที่มันเป็น

The first filter removes these tools, even when listed in the tools field:

  • Agent, when the subagent is at the depth limit; in a fork the tool stays listed but returns an error instead of spawning
  • AskUserQuestion
  • EndConversation, which can end only the main conversation
  • EnterPlanMode
  • ExitPlanMode, unless the subagent’s permissionMode is plan
  • ScheduleWakeup
  • TaskOutput
  • WaitForMcpServers
  • Workflow

รายการนี้ไม่ได้เหมือนกันหมด Agent มีเงื่อนไขว่า “เฉพาะเมื่อถึงขีดจำกัดความลึกแล้ว” และ ExitPlanMode มีเงื่อนไขว่า “เว้นแต่โหมดการทำงานคือ plan” ในทางกลับกัน AskUserQuestion — เครื่องมือสำหรับตั้งคำถามกับเจ้าของ — ไม่มีเงื่อนไขแบบนั้นเลย ไม่ว่า AI ลูกน้องจะทำงานอยู่ภายใต้การตั้งค่าใด และไม่ว่าจะเขียนระบุลงในรายการอนุญาต tools ชัดเจนแค่ไหน มันก็ถูกถอดออกทั้งหมด

พูดอีกอย่าง กฎที่ว่า “เมื่อไม่แน่ใจ ให้ถามเจ้าของ” ถูกเขียนขึ้นสำหรับคู่สนทนาที่ไม่มีเครื่องมือสำหรับถามอยู่ในมือ สิ่งเดียวที่คู่สนทนานั้นทำได้คือตัดสินใจเองโดยไม่ถาม สิ่งที่ฉันตั้งใจให้เป็นการเพิ่มความหนาแน่นของกฎ กลับกลายเป็นคำสั่งให้ทำตามการตัดสินใจของตัวเอง

              AI ลูกน้องเกิดไม่แน่ใจในการตัดสินระหว่างทำงาน

              ┌──────────────────┼───────────────────┐
              ▼                  ▼                    ▼
        พยายามเรียกเครื่องมือ   อยากได้ผลลัพธ์ทันที จึงพยายาม   พยายามส่งงานต่อ
        "ถามเจ้าของ"           รันในโหมดที่รอคำตอบโดยตรง        ลงไปอีกชั้น
              │                  │                             │
              ▼                  ▼                             ▼
      ⛔ ถูกถอดออกโดยไม่มีเงื่อนไข  ⛔ ถูกปฏิเสธโดยอุปกรณ์นิรภัย   (ความลึกของลำดับชั้น
         ตามสเปกทางการ              ที่ฉันสร้างขึ้นเอง (ดูด้านล่าง)    มีเพดานกำกับ)
              │                  │
              └────────┬─────────┘

            ไม่มีวิธีใดใช้ได้ จึง "ตัดสินใจเอง"


            การตัดสินหน้างานนั้นถูกเขียนลงในความจำระยะยาว

                       ▼ ……(ในเซสชันอื่น)
            AI ลูกน้องตัวถัดไปที่มาถึงสถานการณ์เดียวกัน
            ทำตามมันในฐานะ "มีแบบอย่างอยู่แล้ว"


            ตกหลุมเดิมอีกครั้ง (เส้นย้อนกลับ)

ข้อบกพร่องที่ 2: รับผลลัพธ์ไม่ได้

ผลของงานที่ส่งให้ AI ลูกน้องจะกลับมาเมื่อไหร่ คำอธิบายทางการเขียนว่า:

As of v2.1.198, subagents run in the background by default.

Claude runs a subagent in the foreground when it needs the result before continuing.

A background subagent’s results reach Claude as a completion notification in a later turn.

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

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

   "เทิร์นปัจจุบัน" ของผู้กระจายงาน   ① โยนงานให้ AI ลูกน้อง ──▶ ตอบกลับแล้วจบ

   AI ลูกน้อง                              ② ทำงานต่อในเบื้องหลัง

                                                    ③ ทำเสร็จ รายงานพร้อมแล้ว

   "เทิร์นถัดไป" ของผู้กระจายงาน              รายงานควรจะมาถึงตรงนี้
   (ปลายทาง)                            แต่ผู้กระจายงานจบเทิร์นของตัวเองไปตั้งแต่ ①
                                           และไม่ได้รออยู่ด้วยซ้ำ


                                                 ไม่มีอะไรเกิดขึ้น งานค้าง

                                มนุษย์สังเกตเห็นแล้วปลุกมัน ─┘
                                (เส้นย้อนกลับ: รูปแบบนี้เกิดขึ้นจริง 3 ครั้ง)

อุปกรณ์นิรภัยที่ฉันสร้างเองไปขวางทางออกที่เอกสารทางการระบุไว้

โครงสร้างนี้มีทางแก้ที่จัดไว้ให้อย่างเป็นทางการ ถ้าต้องการผลลัพธ์ทันที ให้รันในเบื้องหน้าแทนเบื้องหลัง ซึ่งก็คือ “Claude runs a subagent in the foreground when it needs the result before continuing” ที่อ้างไว้ข้างต้นนั่นเอง

แต่ในทางปฏิบัติ ทางออกนั้นถูกปิดกั้นอยู่ ไม่กี่วันก่อนหน้า เพื่อป้องกันไม่ให้ AI ลูกน้องเงียบไปนาน ฉันได้เพิ่มอุปกรณ์นิรภัย (ฮุก) ของตัวเองที่เข้าไปตัดทันทีก่อนการทำงาน ฮุกตัวนั้นถูกเขียนให้ ปฏิเสธการตั้งค่ารันเบื้องหน้าโดยไม่มีเงื่อนไข วิธีการที่เอกสารทางการกำหนดไว้สำหรับ “เมื่อต้องการผลลัพธ์ ให้ทำแบบนี้” กำลังถูกอุปกรณ์นิรภัยของฉันเองปิดกั้นอยู่

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

ข้อบกพร่องที่ 3: ตรวจสอบรายงานไม่ได้

นิยามของผู้กระจายงานไม่ได้รวมเครื่องมือสำหรับตรวจสอบสถานะเอาไว้ (สิทธิ์รันคำสั่งเชลล์) ดังนั้นเมื่อ AI ลูกน้องรายงานว่า “เสร็จแล้ว” ผู้กระจายงานจึงไม่มีทางยืนยันได้ และทำได้เพียงส่งต่อขึ้นไปตามที่รายงานมา

เคยมีครั้งหนึ่งที่รายงานกลับมาจากผู้กระจายงานว่า “ให้สร้างเบรนช์แล้ว” การวัดจริงให้ผลดังนี้

$ git status --porcelain
(no output)

$ git branch --list <branch name>
(no output)

$ git log --oneline develop..HEAD
(no output)

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

อีกหนึ่งข้อค้นพบ — ฉันวางกฎไว้ผิดที่

ระหว่างการสืบสวนมีข้อค้นพบหนึ่งที่รู้สึกเหมือนผลพลอยได้ ความเข้าใจของฉันเรื่องขอบเขตการตั้งค่าที่ AI ลูกน้องโหลดนั้นล้าสมัยไปแล้ว เอกสารทางการเขียนว่า:

…every level of the CLAUDE.md hierarchy the main conversation loads, including ~/.claude/CLAUDE.md

Explore and Plan are the only subagents that omit CLAUDE.md and git status. There is no frontmatter field or per-agent setting to change which agents skip them.

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

แต่บันทึกการออกแบบของฉันเขียนไว้เพียงว่า “สิ่งที่ AI ลูกน้องโหลดไปถึงแค่การตั้งค่าระดับโปรเจกต์” โดยตกบรรทัดที่ว่าการตั้งค่าร่วมก็ไปถึงพวกมันด้วย ช่องว่างนั้นสร้างสมมติฐานที่ผิดขึ้นมา: “กฎร่วมไปไม่ถึง AI ลูกน้อง ดังนั้นทางเลือกเดียวคือเขียนถ้อยคำเดียวกันฝังลงในไฟล์นิยามทีละไฟล์” ผลลัพธ์คือความบวมที่ข้อควรระวังเดียวกันถูกคัดลอกลงไปในไฟล์นิยามเกือบสิบไฟล์ ถ้าวางไว้ที่เดียว มันก็ไปถึงเกือบทั้งหมดอยู่แล้ว

รื้อการออกแบบใหม่

เนื่องจากสาเหตุทั้งสามกลายเป็นข้อบกพร่องเชิงโครงสร้างที่มีหลักฐานในสเปกทางการ ฉันจึงใส่มาตรการรับมือลงไปในเชิงโครงสร้างเช่นกัน

มาตรการที่ 1: จำกัดขอบเขตของอุปกรณ์นิรภัยให้แคบลง ฉันทำให้มันตัดสินจากการมีหรือไม่มีตัวระบุที่แนบมากับข้อมูลการเรียก ว่าผู้เรียกคือบทสนทนาหลักที่คุยกับคนโดยตรง หรือคือ AI ลูกน้อง และ จำกัดเงื่อนไขที่ปฏิเสธการรันเบื้องหน้าไว้เฉพาะการเรียกที่มีต้นทางจากบทสนทนาหลัก เบาะแสที่ใช้ตัดสินนั้นถูกจัดให้ตรงกับสิ่งที่กลไกแจ้งเตือนอีกตัวใช้อยู่แล้ว

guard scope test: PASS 15 / FAIL 0

นั่นคือผลหลังจากผ่านกรณีปกติ 4 กรณีและกรณีผิดปกติรวมถึงอินพุตเชิงปฏิปักษ์ ยืนยันว่าเส้นทางที่ AI ลูกน้องเลือกรันเบื้องหน้าได้ถูกกู้คืนแล้ว

มาตรการที่ 2: เขียนตัวเลือกที่ทำได้จริงไว้บนสุดของไฟล์นิยามทุกไฟล์ ฉันถอดสมมติฐานที่ว่า “คุณถามเจ้าของได้” ออกไปเอง และเขียนแทนว่า — ตัวเลือกที่มีอยู่คือ (ก) ตัดสินใจเองแล้วลงมือ หรือ (ข) ไม่ลงมือ ให้รายงานแล้วจบ เมื่อใดที่มีการตัดสินเข้ามาเกี่ยวข้อง ให้เลือก (ข) เสมอ ฉันลบ “การถาม” ออกจากกฎในฐานะตัวเลือก และแทนที่ด้วย “ถ้าถามไม่ได้ ให้หยุดแล้วรายงาน”

มาตรการที่ 3: เลิกใช้บทบาทที่ทำหน้าที่กระจายงานอย่างเดียว รากของข้อบกพร่องที่ 2 และ 3 อยู่ที่ “การมีชั้นผู้กระจายงานอยู่ ทั้งที่รับผลลัพธ์ไม่ได้และยืนยันไม่ได้” ฉันถอดบทบาทนั้นออกไปเอง และกลับไปใช้รูปแบบที่ฝ่ายมอบหมายสั่งการผู้ดำเนินการและผู้ตรวจสอบโดยตรง

reminder-correction verification: PASS 9 / FAIL 0

ฉันตรวจทั่วการตั้งค่าแล้วว่าไม่มีคำอธิบายใดที่ยังแนะนำบทบาทที่เลิกใช้ไปแล้วหลงเหลืออยู่

ยืนยันก่อนว่ามันทำได้จริงหรือไม่

สิ่งที่ข้อบกพร่องทั้งสามมีร่วมกันคือ ในวินาทีที่ฉันเขียนกฎนั้น ฉันไม่ได้ยืนยันว่าคู่สนทนาสามารถทำตามได้ในทางกายภาพหรือไม่ “ถามเมื่อไม่แน่ใจ” สำหรับคู่สนทนาที่ไม่มีเครื่องมือถาม มีความหมายเท่ากับ “ตัดสินใจเอง” และการตัดสินใจลำพังนั้นไม่ได้จบลงตรงนั้นเมื่อมันถูกบันทึกไว้ บันทึกของการตัดสินไปลงเอยในความจำระยะยาว และ AI ลูกน้องตัวถัดไปที่มาถึงสถานการณ์เดียวกันก็ทำตามมันในฐานะแบบอย่างที่มีอยู่แล้ว การเบี่ยงเบนของการตีความเพียงครั้งเดียวจะทำซ้ำไปเรื่อย ๆ จนกว่าโครงสร้างจะถูกแก้

หลุมพรางเดียวกันนี้ใช้กับการเขียนกฎห้ามหรือกฎอนุญาตด้วย ฮุกตัวนี้ถูกเขียนขึ้นเพื่อปิดกั้น “เส้นทางการรันเบื้องหน้า” และมันกวาดเอาดินแดนที่เกินจากวัตถุประสงค์เดิมของมัน คือ “การป้องกันอาการค้าง” เข้าไปด้วย กฎควรถูกเขียนในเชิง สิ่งที่คุณต้องการป้องกัน ถ้าเขียนในเชิง เส้นทางไหนที่มันแตะ มันจะฆ่าเส้นทางที่ถูกต้องซึ่งเพิ่มเข้ามาทีหลังไปด้วย

อีกเรื่องหนึ่ง มีช่วงเวลาหนึ่งที่ฉันเห็นบางอย่างพังอยู่ตรงหน้าแล้วยังใช้มันต่อโดยไม่ยืนยันสาเหตุ ไม่ว่าจะทำโดยคนหรือมี AI เข้ามาเกี่ยวข้อง นั่นคือสถานะที่อันตรายที่สุด ความไม่สอดคล้องที่คุณสังเกตเห็นควรถูกยืนยันตัวตนก่อนจะถูกยกข้ามไปยังงานชิ้นถัดไป การได้ลงมือทำเรื่องนี้จริง ๆ ในครั้งนี้ คือสิ่งที่ทำให้ฉันแก้ทั้งกฎ อุปกรณ์นิรภัย และการแบ่งบทบาทได้พร้อมกัน

หลังจากนั้น การอ่านสเปกทางการที่อ้างในบทความนี้ซ้ำทีละตัวอักษร ลงไปถึงอนุประโยคเงื่อนไข ทำให้พบสิ่งที่ตกหล่นไปหนึ่งจุดในฝั่งของฉันเอง รายละเอียดบันทึกไว้ด้านล่าง

ทำไมฉันจึงเลิกใช้ orchestrator subagent ใน Claude Code — ข้อจำกัดเชิงโครงสร้าง 3 ข้อจากสเปกฮาร์เนสทางการ

ส่งข้อความได้ตามสบาย

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