ความสามารถ Spawn Agent — ENDMEMEX สั่ง Claude/Codex/Antigravity อีกตัวทำงานแทนแบบมีขอบเขต

ENDMEMEX ให้ agent หนึ่งเริ่ม ติดตาม และยกเลิกงานของอีก agent ได้ผ่าน endeavor_agent_start, endeavor_agent_status และ endeavor_agent_cancel โดยกำหนดขอบเขตงานชัดเจน เหมาะกับงานที่แบ่งเป็นส่วนย่อยได้
ความสามารถนี้คืออะไร
endeavor_agent_start เริ่ม sub-agent (Codex, Claude หรือ Antigravity — Google’s agy CLI) รันงานหนึ่งชิ้นแบบbounded (มีขอบเขต, มี timeout, มีเป้าหมายชัดเจน) ในเบื้องหลัง คืน run_id กลับมาทันที — agent ที่เรียกใช้ต้องpoll สถานะต่อด้วย endeavor_agent_status (ได้ผลลัพธ์บางส่วนระหว่างทางด้วย ไม่ต้องรอจนจบ) และยกเลิกได้ด้วย endeavor_agent_cancel
ใช้เมื่อ: ต้องการความเห็นที่สอง (second opinion) ต่อ diff หนึ่งชิ้น, ต้องการวิเคราะห์แบบขนาน, หรือมีงานที่ agent อีกตัวถูกเทรนมาให้ถนัดกว่า — ทั้งหมดนี้เกิดขึ้นโดยผู้ใช้ไม่ต้องสลับเครื่องมือเอง
จุดออกแบบที่สำคัญที่สุด: แยก MCP server ต่างหากโดยเจตนา
คอมเมนต์ต้นไฟล์ agent_mcp_server.py อธิบายเหตุผลตรงๆ: “This server deliberately stays separate from mcp_server.py: delegation can start external CLI/model processes, while the existing server remains a project-memory-only trust and failure boundary.”
หมายความว่า ENDMEMEX มี MCP server 2 ตัวที่ทำงานคนละหน้าที่โดยตั้งใจ:
mcp_server.py— จัดการความจำโปรเจกต์เท่านั้น (query, checkpoint, record ฯลฯ) ไม่มีทางรันโปรเซสภายนอกได้agent_mcp_server.py— จัดการการสั่งงาน sub-agent เท่านั้น ซึ่งหมายถึงการรันโปรเซสภายนอกจริง
การแยกนี้ทำให้ “ความจำเสียหาย” กับ “โปรเซสภายนอกหลุดควบคุม” เป็นความเสี่ยงคนละชั้นที่ไม่ปนกัน — ถ้า memory server มีบั๊ก ผลกระทบจำกัดอยู่ที่ข้อมูลความจำเท่านั้น ไม่มีทางลามไปเป็นการรันโปรเซสที่ควบคุมไม่ได้
Role policy: worker เขียนได้ แต่ reviewer/advisor ต้องอ่านอย่างเดียวเสมอ
การเรียก endeavor_agent_start ต้องระบุ role เป็นหนึ่งใน 3 แบบ ที่แต่ละแบบมีสิทธิ์ต่างกันแบบบังคับด้วยโค้ด (role_policy_error ตรวจสอบก่อนรันทุกครั้ง):
worker(ดีฟอลต์) — เขียนไฟล์ได้ถ้าขอaccess=workspace_writeชัดเจนreviewer— ต้องเป็นread-onlyเสมอ ถ้าเป็น Codex หรือ Antigravity และ sandbox ไม่ใช่ read-only จะถูกปฏิเสธทันทีadvisor— เช่นเดียวกับ reviewer บังคับ read-only เสมอ
สำหรับ Claude sub-agent ที่ role ไม่ใช่ worker ระบบยังเช็คเพิ่มว่า tool ที่ขอ (allowed_tools) ต้องอยู่ใน READ_ONLY_CLAUDE_TOOLS เท่านั้น — ถ้าขอ tool ที่เขียนได้ (เช่น Edit, Write) ทั้งที่ role เป็น reviewer จะถูกปฏิเสธก่อนแม้แต่จะเริ่มรัน
แม้ role เป็น worker และขอ workspace_write ได้จริง Claude worker ก็ได้แค่เครื่องมือแก้ไข (Read/Grep/Glob/Edit/Write) โดยไม่มี Bash เลย — ป้องกันไม่ให้ sub-agent รันคำสั่งระบบใดๆ แม้จะเขียนไฟล์ได้ Antigravity worker ก็ถูกจำกัดแบบเดียวกันโดยธรรมชาติของตัว agy เอง: access=workspace_write แปลว่าส่ง --mode accept-edits ให้ (ไฟล์แก้ได้อัตโนมัติ) แต่ไม่เคยส่ง --dangerously-skip-permissions ให้เลย ซึ่งหมายความว่าคำสั่ง shell (run_command) ยังถูกปฏิเสธอยู่ดี แม้จะแก้ไฟล์ได้แล้วก็ตาม — ยืนยันด้วยการรันจริง (2026-08-24): สั่ง Antigravity แก้ไฟล์ในโหมด workspace_write สำเร็จ แต่พอสั่งให้รันคำสั่ง shell กลับ exit code 1 พร้อมข้อความ permission check failed for command
Target ที่ 3: Antigravity (agy) — และกับดักที่ทดสอบจริงแล้วเจอ
ตอนแรก ENDMEMEX รองรับแค่ Codex กับ Claude เป็น sub-agent ต่อมาเพิ่ม Antigravity CLI ของ Google (agy) เป็น target ที่ 3 — แต่กว่าจะรองรับได้ถูกต้องต้องทดสอบกับ agy ตัวจริง (v1.1.19) ก่อน ไม่ใช่เชื่อเอกสารจากบล็อกภายนอกอย่างเดียว เพราะเอกสารที่หาเจอบอกพฤติกรรมผิดไปจากที่รันจริง
กับดักที่อันตรายที่สุด: --add-dir ไม่ใช่ตัวเลือก ทดสอบจริงพบว่าถ้าไม่ใส่ --add-dir <cwd> ให้ agy มันจะแก้ไฟล์ใน scratch directory ภายในของตัวเอง (~/.gemini/antigravity-cli/scratch/) แทนที่ไดเรกทอรีที่ผู้เรียกตั้งใจ แล้วยังรายงานกลับมาว่า status:"SUCCESS" เหมือนไม่มีอะไรผิดปกติเลย — คือแก้ไฟล์ผิดไฟล์แบบเงียบๆ ไม่มี error ให้เห็น ไม่ใช่แค่ฟีเจอร์ที่ขาดหายไป ทาง ENDMEMEX แก้ด้วยการใส่ --add-dir <cwd> ให้อัตโนมัติทุกครั้งที่ spawn Antigravity ผู้เรียกไม่ต้องรู้เรื่องนี้เลย
Antigravity ไม่มีโหมด read-only ของตัวเองแบบตรงๆ (ต่างจาก Codex ที่มี -s read-only ชัดเจน) — ทดสอบจริงพบว่าการไม่ใส่ flag --mode เลย คือค่าเริ่มต้นที่ปลอดภัยอยู่แล้ว: agy จะเข้าสู่โหมด request-review และถ้ามีความพยายามเขียนไฟล์หรือรันคำสั่งจะถูกปฏิเสธด้วย exit code 1 พร้อมข้อความ permission check failed — ไม่ใช่ silent no-op แบบที่เอกสารบางแหล่งบอกไว้ผิดๆ (เอกสารบอกว่า soft-deny แล้ว exit 0 แต่ทดสอบจริงได้ exit 1)
การแปลงระดับ sandbox ใช้ enum เดิมที่มีอยู่แล้ว (read-only / workspace-write / danger-full-access) แปลงเป็น flag ของ agy เอง:
read-only→ ไม่ใส่--mode(ปฏิเสธการเขียนทุกอย่าง)workspace-write→--mode accept-edits(แก้ไฟล์อัตโนมัติ แต่ shell ยังถูกปฏิเสธ)danger-full-access→ เพิ่ม--dangerously-skip-permissions(อนุญาตทุกอย่างรวม shell — ใช้เฉพาะ sandbox ทิ้งได้เท่านั้น)
อีกจุดที่ต้องเขียนโค้ดเพิ่มโดยเฉพาะ: agy --output-format stream-json ส่ง event เป็น NDJSON คนละรูปแบบกับทั้ง Codex และ Claude (ใช้ event["event"] เป็นสตริงอย่าง "step_update"/"result" แทนที่จะเป็น dict ซ้อนแบบที่ Claude ใช้) — ถ้าไม่เขียน parser เพิ่มให้ตรงรูปแบบนี้ ผลลัพธ์ที่ส่งกลับให้ agent ที่เรียกจะเป็นJSON ดิบที่อ่านไม่รู้เรื่องแทนคำตอบจริง
พิสูจน์ของจริง: ใช้ Antigravity หาบั๊กใน production code แล้วเจอจริง
วิธีทดสอบ target ใหม่ที่ดีที่สุดคือใช้งานจริง — สั่ง Antigravity (gemini-3.1-pro-high) ผ่าน endeavor_agent_start ตัวเดียวกันนี้ ให้ role เป็น reviewer (read-only บังคับ) ไปตรวจโค้ดจริงในโปรเจกต์ (ฟีเจอร์แสดง token/s ของ dashboard monitor) ผลคือ agy รายงานบั๊กกลับมา 3 ข้อ
จุดสำคัญคือห้ามเชื่อผลจาก sub-agent เฉยๆ — ต้องตรวจกลับกับซอร์สโค้ดจริงเองก่อนเชื่อ ผลตรวจพบว่า:
- 1 ข้อเป็น false positive — agy อ้างว่ามี race condition ที่จะทำให้ background worker loop crash แต่ตรวจซอร์สจริงพบว่า call site ทั้งสองจุดที่เรียกฟังก์ชันนั้นห่อด้วย
try/except Exceptionอยู่แล้ว ไม่มีทาง crash ได้จริงตามที่อ้าง - 2 ข้อยืนยันเป็นบั๊กจริง — ตัวเลข token/s ที่แสดงบน dashboard ไม่มีการเช็คว่า“เก่าแค่ไหน” เลย ทำให้โชว์ค่าจากหลายชั่วโมงก่อนราวกับเป็นค่าปัจจุบัน แก้ด้วยการอ่าน timestamp จาก log แล้วทิ้งค่าที่เก่าเกิน 60 วินาที
ผลคือ แก้บั๊กจริงในโค้ด production ได้จากการทดสอบความสามารถใหม่นี้เอง — พิสูจน์ทั้งว่า pipeline ทำงานได้จริงตั้งแต่ต้นจนจบ (spawn → poll → อ่านผล) และย้ำวินัยสำคัญ: ผลจาก sub-agent คือข้อเสนอที่ต้องตรวจสอบ ไม่ใช่ข้อสรุปที่เชื่อได้ทันที ไม่ว่า sub-agent ตัวไหนก็ตาม
Admission control: กันการ spawn agent วนไม่หยุด
ก่อนจะยอม start agent ใหม่จริง มีการตรวจสอบ 2 ชั้นภายใต้ file lock (fcntl.flock) เพื่อกัน race condition เวลามีการเรียกพร้อมกันหลาย process:
- จำนวน run ที่ยัง active พร้อมกัน ต้องไม่เกิน 4 — นับจาก state file ของทุก run ที่ยังไม่ถึงสถานะ terminal (
completed/failed/timed_out/cancelled) - อัตราการ start ต้องไม่เกิน 6 ครั้งต่อ 60 วินาที — ป้องกันการ spawn ถี่เกินไปแม้จะไม่มี run ค้างอยู่ก็ตาม
ทั้งสองเงื่อนไขทำงานผ่านไฟล์บนดิสก์ที่แชร์ระหว่าง process (ไม่ใช่ตัวแปรใน memory) เพราะแต่ละครั้งที่ MCP client เรียก tool อาจเป็นคนละ process กัน — ถ้าใช้ตัวแปรใน memory จะนับจำนวนไม่ถูกต้องข้าม process
ป้องกัน PID reuse race ด้วย “birth token”
จุดที่ละเอียดที่สุดในระบบนี้: การเช็คว่า sub-agent process ยัง “มีชีวิตอยู่” จริงหรือไม่ ไม่ได้เช็คแค่ PID ยังมีอยู่ในระบบหรือเปล่า เพราะ PID สามารถถูก reuse โดย process อื่นที่ไม่เกี่ยวข้องได้ หลัง process เดิมตายไปแล้ว — ถ้าเช็คแค่ PID เฉยๆ อาจนับ process คนละตัวที่บังเอิญได้ PID เดิมมาว่ายัง “active” อยู่ ทำให้ concurrency limit นับผิด
ทางแก้คือเก็บ “birth token” (process_start_token) ไว้ตอน spawn process แล้วเทียบซ้ำตอนเช็คสถานะ — ถ้า token ไม่ตรงกัน แปลว่า PID นั้นถูก process อื่น reuse ไปแล้ว ไม่ใช่ตัวเดิม คอมเมนต์ในซอร์สอธิบายเหตุผลของ fallback ตรงๆ: “undercounting active runs would let admission exceed the concurrency limit, which is less safe than a rare overcount” — ถ้าคำนวณ token ไม่ได้ (ทั้งสองฝั่ง) ระบบเลือกนับว่ายัง active อยู่ (overcount) แทนที่จะเสี่ยงนับต่ำเกินจริงจนปล่อยให้ concurrency limit ถูกละเมิด
Cancel ปลอดภัยเสมอ แม้เรียกซ้ำ
endeavor_agent_cancel ออกแบบให้เรียกซ้ำได้อย่างปลอดภัย (idempotent) — ยกเลิก run ที่จบไปแล้วซ้ำไม่ทำให้เกิด error และมีรายละเอียดความปลอดภัยสำคัญ: MCP control process ไม่เคยส่งสัญญาณ kill ไปยัง process group ID (PGID) ที่อ่านมาจากไฟล์บนดิสก์โดยตรง — มีแต่ run manager ตัวที่ยังมีชีวิตอยู่จริงเท่านั้นที่ signal และ reap process group ของตัวเอง เหตุผลตรงไปตรงมา: PGID ที่อ่านจากไฟล์เก่าอาจถูก reuse ไปเป็นของ process อื่นแล้ว (ปัญหาเดียวกับ PID reuse ข้างบน) การ kill ตรงจากไฟล์เสี่ยงไป kill process ที่ไม่เกี่ยวข้อง
Depth cap: ห้าม sub-agent spawn agent ต่อ
Docstring ของ agent_delegate.py ระบุกฎเข้มงวด: “Never ask the child to delegate onward — depth is capped at 1 and the nested call will fail with exit 2” — sub-agent ที่ถูก spawn ขึ้นมาห้าม spawn agent ตัวที่สามต่อ ถ้าพยายามทำจะถูกปฏิเสธทันทีด้วย exit code 2 การจำกัดความลึกไว้แค่ 1 ชั้นป้องกันปัญหา exponential fan-out (agent spawn agent spawn agent ไม่รู้จบ) ที่จะทำให้ resource หมดและควบคุมไม่ได้
Sub-agent เริ่มต้นแบบ “เย็น” เสมอ — ต้องใส่บริบทเองทุกครั้ง
จุดสำคัญที่ agent ที่เรียกใช้ต้องรู้: “the child starts cold — it knows nothing about your session” — sub-agent ที่ถูก spawn ไม่มีความทรงจำใดๆ จากบทสนทนาของ agent ที่เรียกมันเลย ต้องใส่บริบทที่จำเป็นเข้าไปใน prompt ตรงๆ หรือบอกให้มันไปอ่าน ENDMEMEX handoff เอง (python3 ENDMEMEX/endeavor_db.py handoff --project <P> --json) — ทุกงานที่มอบหมายต้องเป็นงานเดียวที่มีขอบเขตชัดเจน พร้อม deliverable ที่ระบุไว้ ไม่ใช่ภารกิจเปิดกว้างที่ไม่รู้จบ
ผลลัพธ์ไม่ถูกผลักกลับเข้า context อัตโนมัติ — ต้อง poll เอง
ข้อควรระวังที่สำคัญที่สุดสำหรับโหมด background: “completion is not pushed back into your LLM context” — เมื่อ sub-agent ทำงานเสร็จ ผลลัพธ์ไม่ได้ปรากฏขึ้นในบทสนทนาของ agent ที่เรียกมันเองโดยอัตโนมัติ ต้อง poll endeavor_agent_status เอง หรือใช้กลไก endeavor_memory_event_poll/endeavor_memory_event_ack (ระบบ event แบบ at-least-once ที่ publish event เมื่องาน background เสร็จในขอบเขตโปรเจกต์) เพื่อให้ host/orchestrator รู้ว่าต้องกลับมาอ่านผลลัพธ์เมื่อไร — ถ้าลืมจัดการ polling ผลลัพธ์ของงานที่ spawn ไปอาจไม่มีใครอ่านเลย
ทำไมเรื่องนี้ถึงสำคัญ
ความสามารถ spawn agent ของ ENDMEMEX แสดงสถาปัตยกรรมที่ระมัดระวังเรื่องความปลอดภัยของกระบวนการทำงานพร้อมกันในระดับที่ไม่ค่อยเห็นในเครื่องมือ AI agent ทั่วไป — ตั้งแต่การแยก trust boundary ทั้ง MCP server, role-based policy ที่บังคับด้วยโค้ดไม่ใช่แค่คำแนะนำ, admission control ที่กันการ spawn ล้นเครื่อง, ไปจนถึงการป้องกัน PID/PGID reuse race ที่ผิดพลาดได้ยากในโค้ดทั่วไปแต่มีจริงเมื่อ process จำนวนมากเกิด-ตายเร็ว — ทั้งหมดนี้จำเป็นเพราะการ “spawn agent” คือการยกระดับจากงานที่ทำภายใน process เดียว ไปสู่การควบคุมโปรเซสภายนอกจริงๆ ซึ่งมีความเสี่ยงต่างชั้นไปเลยจากการเรียก tool ทั่วไป การเพิ่ม Antigravity เป็น target ที่ 3 ตอกย้ำหลักการเดียวกันนี้อีกชั้น: ทุก CLI ใหม่ที่จะเชื่อมเข้ามาต้องผ่านการทดสอบจริงก่อนไว้ใจเอกสาร และผลลัพธ์ที่ sub-agent ส่งกลับมา — ไม่ว่าจะเป็นตัวไหน — ต้องผ่านการตรวจสอบซ้ำโดย agent ที่เรียกใช้เสมอ ไม่ใช่เชื่อและนำไปใช้ต่อทันที
ความคิดเห็น
กำลังโหลดความคิดเห็น...