Endeavor Hands ทำงานอย่างไร — สถาปัตยกรรม เครื่องมือ และ guardrail

ภาพประกอบ: Endeavor Hands ทำงานอย่างไร — สถาปัตยกรรม เครื่องมือ และ guardrail

บทความ เริ่มต้นใช้งาน อธิบายการติดตั้งและเชื่อมต่อ ส่วนบทนี้อธิบายสิ่งที่เกิดขึ้นหลัง ChatGPT เรียก Hands: ใครเป็นคนคิด, คำสั่งเดินทางอย่างไร, และแต่ละ tool จำกัดผลกระทบไว้ตรงไหน

ความต่างพื้นฐาน: ไม่มี agent loop หรือโมเดลอยู่ในเครื่อง

Endeavor Hands ไม่มี LLM, RAG, web-search หรือ LangGraph loop อยู่ใน server จากมุมมองของ Hands ChatGPT (หรือ MCP client ตัวอื่น) เป็น planner ทั้งหมด ตัว server รับ MCP request ทีละ call, ตรวจขอบเขต, รันงานบน Mac และคืนผลลัพธ์กลับไปให้ planner ตัดสินใจขั้นถัดไป

นี่จึงต่างจาก local agent ที่วน คิด → เรียก tool → ตรวจผล ในเครื่องเอง Hands ไม่ได้เลือก tool ต่อให้โมเดล และไม่ได้ซ่อน reasoning อีกชุดไว้ข้างหลัง แต่ยังคงบังคับ policy สำคัญในโค้ด ไม่ฝากความปลอดภัยไว้กับ prompt อย่างเดียว

เส้นทางของคำสั่งหนึ่งคำสั่ง

ChatGPT Web (Developer-mode app)
        │ HTTPS ขาออก
        ▼
OpenAI Secure MCP Tunnel
        ▼
tunnel-client บน Mac
        │ stdio
        ▼
server.py (FastMCP)
        ├─ bash / git / bash_bg / python_exec
        ├─ read_file / write_file / edit
        ├─ computer
        └─ mcp_* bridge

server.py คุยกับ tunnel-client ด้วย stdio เท่านั้น จึงไม่มี MCP server ที่เปิด public port รออินเทอร์เน็ตเข้ามาโดยตรง หากใช้ launcher tunnel-client อาจเปิด endpoint แบบ loopback ตามพอร์ตที่ launcher กำหนด (เช่น 127.0.0.1:8765 หรือ deployment ที่ SERVER_MONITOR ดูแลใช้ 127.0.0.1:8768) เช่น /healthz, /readyz, /api/status, /metrics สำหรับ readiness เท่านั้น ไม่ใช่ endpoint สำหรับรับ MCP จากภายนอก

MCP client อื่น เช่น Claude Desktop, mcp dev หรือ Codex CLI สามารถ spawn server.py ตรงๆ ได้โดยไม่ต้องผ่าน tunnel หากต้องการใช้ในเครื่องเดียวกัน

12 tool names แบ่งเป็น 5 กลุ่ม

กลุ่ม Tool หน้าที่
Shell/process bash, bash_bg ค้นไฟล์ ตรวจระบบ รันคำสั่งสั้น และติดตามงานที่รันนานผ่าน log tail
Repository git status/diff, stage path ที่ระบุ, commit สิ่งที่ stage แล้ว และ push แบบไม่ force
Data python_exec วิเคราะห์ข้อมูล สถิติ regression และ machine learning ด้วย interpreter ของ server
Files/UI read_file, write_file, edit, computer อ่านหลายชนิดไฟล์ สร้าง/แก้ไฟล์ และควบคุมหน้าจอผ่าน Accessibility/OCR
Extensibility mcp_list_tools, mcp_call_tool, mcp_add_server, mcp_remove_server สำรวจ/เรียก/ลงทะเบียน/เอา MCP server แบบ HTTP หรือ local stdio ออก

การรวมความสามารถที่ใกล้กันไว้ใน tool เดียวเป็นความตั้งใจ: read_file รวม text, เอกสาร, รูปภาพ และ media; edit รวม string/line/batch; ส่วน computer รวม observation, action และ verification เพื่อลดการเลือก tool ซ้ำซ้อน

ทุก call มี activity และ diagnostic trail

server ครอบทุก @mcp.tool() ด้วย _logged() ซึ่งจะ:

  • สร้าง turn ID และบันทึกชื่อ/arguments ที่ redact แล้วลง logs/agent_activity.jsonl
  • แสดง call และผลลัพธ์แบบสดทาง stderr พร้อมสถานะ, diagnostic code และ duration
  • เก็บ lifecycle ของ process ใน logs/server_lifecycle.jsonl และ fault traceback ใน logs/server_faults.log
  • สงวน stdout ไว้สำหรับ MCP JSON-RPC เท่านั้น เพราะข้อความ debug ธรรมดาบน stdout ทำให้ protocol เสียได้

ผลลัพธ์ที่ยาวเกินขนาดจะถูกตัดพร้อม marker ที่ชี้ไปยังไฟล์ recovery ใต้ Endeavor_Hands/work/ และ diagnostic จะบอกว่าเป็น validation, timeout, sandbox หรือ process failure แทนการทำให้ agent เดาต่อจากผลลัพธ์ที่หายไป

Guardrail ชั้นที่ 1: protected paths

read_file และ file tools resolve path ด้วย realpath ก่อนตรวจ policy เพื่อกัน ../ และ symlink อ้อมออกจากขอบเขต รายการที่ปฏิเสธเสมอครอบคลุม system paths (/etc, /usr, /System, /Applications ฯลฯ), SSH/AWS/GPG/Claude credentials, Keychain, browser/app credential stores, cookies, HTTP storage และ Messages ที่อาจมี OTP

การอ่านจึงกว้างกว่า workspace ได้ แต่ไม่ได้หมายความว่าอ่าน secret ได้ ส่วนการเขียนใช้ policy คนละชุดตามความเสี่ยง

Guardrail ชั้นที่ 2: ห้ามลบไฟล์

การลบเป็น policy ระดับโค้ด ไม่ใช่แค่คำแนะนำให้โมเดลระวัง:

  • bash และ python_exec ใช้ตัวตรวจคำสั่งและ sandbox ที่ปฏิเสธ unlink/remove ในพื้นที่เขียน
  • computer ปฏิเสธ target หรือ hotkey ที่สื่อถึงการลบ/ทำลาย และไม่กดถังขยะให้
  • git เปิดสิทธิ์ unlink/rename เฉพาะ Git metadata ของ repository ที่ resolve แล้ว เพื่อให้ Git จัดการ index ได้ แต่ source file และ path อื่นยังถูกบล็อก

การลบบรรทัดหรือแทนที่ข้อความในไฟล์ผ่าน edit เป็นการแก้เนื้อหา ไม่ใช่การลบไฟล์ แต่ยังต้องผ่าน permission gate ของ file tool

Guardrail ชั้นที่ 3: permission gate สำหรับไฟล์เดิม

edit และ write_file(overwrite=true) ใช้ registry เดียวกัน: ครั้งแรกที่แตะ top-level folder ใต้ workspace ใน session จะตอบ [permission_required] พร้อม nonce ครั้งเดียว โมเดลต้องถามผู้ใช้ตรงๆ แล้ว retry ด้วย nonce ที่ตรงกันจึงจะแก้ไฟล์ใน folder นั้นต่อได้ตลอด session

ถ้าไฟล์เดิมอยู่นอก workspace จะไม่แก้ทับที่เดิม แต่ redirect ไป sibling working copy เช่น report.edited.md นโยบายนี้แยก “สร้างไฟล์ใหม่” ออกจาก “แทนที่ข้อมูลเดิม” ให้ชัดเจน และ nonce เป็น consent friction/audit trail ไม่ใช่การรับรองตัวตนมนุษย์แบบ cryptographic

Guardrail ชั้นที่ 4: Git มีขอบเขตของตัวเอง

git ถูกเพิ่มเป็น tool แยกเพื่อไม่ต้องให้ ChatGPT ใช้ shell mutation แบบกว้างเมื่อทำงานกับ repository:

  • resolve repository root และ .git directory ให้อยู่ภายใน approved workspace ทั้งคู่
  • status และ diff เป็น read-only; diff ปิด external diff/textconv และเลือก staged diff ได้
  • add ต้องส่ง path ที่ระบุชัด ห้ามส่ง . หรือ stage ทั้ง repository แบบ implicit และห้าม stage .git
  • commit ใช้เฉพาะ staged changes, ต้องมี message ไม่ว่าง (ไม่เกิน 4,000 ตัวอักษร) และไม่รับ path เพิ่ม
  • push ใช้ remote ที่มีอยู่แล้ว, ตรวจ transport ให้เป็น SSH/HTTPS, ใช้ branch ที่ระบุหรือ current branch และไม่รับ force/refspec ที่เสี่ยง
  • guarded commit/push ปิด repository hooks; commit ปิด signing เพื่อไม่ให้ code จาก repository ถูกเรียกเป็น side effect
  • HTTPS ดึง credential จาก trusted macOS Keychain helper โดยตรงและปิด shell-based credential helper; credential ไม่ถูกส่งกลับเป็นผลลัพธ์

ถ้าเจอ .git/index.lock ที่เก่าจริงและไม่มี writer ถืออยู่ ระบบจะย้ายไปชื่อ backup แบบมี timestamp ไม่ลบทิ้ง ส่วน lock ที่ไม่ว่างต้อง parse เป็น Git index ที่สมบูรณ์ก่อนจึงจะกู้ได้ หากตรวจไม่ได้จะหยุดไว้ให้เจ้าของ repo ตรวจเอง

รายละเอียด syntax และตัวอย่างอยู่ใน Tool: Git

Guardrail ชั้นที่ 5: macOS sandbox เป็น backstop

bash, bash_bg, python_exec และ local stdio child ใช้ sandbox-exec ผ่าน RealSandboxBackend ใน production profile เริ่มด้วย (allow default) แล้วใช้ deny-list ปิดการเขียนไปยัง system/sensitive folders และ Desktop/Documents/Downloads/Library จากนั้น allow workspace กับ /private/tmp ท้าย profile ตามกติกา last-match-wins พร้อม deny unlink ใน workspace

นี่เป็น deny-list ไม่ใช่ allow-list สมบูรณ์: path ที่ไม่อยู่ในรายการ deny อาจยังเขียนได้ จึงไม่ควรสรุปว่า “เขียนได้เฉพาะ workspace” จาก sandbox เพียงอย่างเดียว — file tools มี path policy และ permission gate ของตัวเองเป็นชั้นเสริม

Computer: observe → act → verify

computer ไม่รับพิกัดดิบเป็นเส้นทางหลัก แต่ใช้ screenshot ล่าสุด, [OBS obs_N] และ element ID eN เพื่อให้ action อ้างอิง observation ปัจจุบัน หลังทุก mutation ระบบตรวจ semantic AX/OCR change หรือ compact visual change และแนบผลกลับมาให้ model

ถ้า click แล้ว no_visible_change ต้อง see/inspect ใหม่และเลือก target ปัจจุบัน ห้ามกดซ้ำจากภาพเก่า expect= ตรวจ focus:, app:, window: หรือ text: ได้ โดยตอบ +verified, +expectation_not_met หรือ +expect_unknown เมื่อหลักฐานไม่พอ แทนการเดา

การพิมพ์ถูกตรวจ target ที่เพิ่ง focus เพื่อปฏิเสธ password/secure field โดยไม่บล็อกทั้งหน้าจอ และไม่รับ password, OTP, payment หรือ credential จาก model ส่วน hotkey ที่มีความหมายลบถาวรถูกปฏิเสธแยกจากการกด Delete ธรรมดา

MCP bridge: ต่อได้ทั้ง HTTP และ local stdio

bridge มี registry สองชั้น: config.MCP_SERVERS สำหรับรายการที่ developer provision และ dynamic registry ที่ Endeavor_Hands/work/tool_mcp/servers.json ซึ่ง ChatGPT จัดการผ่าน mcp_add_server/mcp_remove_server ได้เอง (ชื่อชนกัน dynamic entry ชนะ; ยังอ่าน legacy workspace registry เพื่อ compatibility)

  • Streamable HTTP ใช้ URL และ headers ที่กำหนด
  • local stdio ต้องใช้ executable path แบบ absolute, args เป็น JSON array และ cwd ที่อยู่ใน approved workspace
  • stdio child ถูก spawn ด้วย argv โดยตรง ไม่ผ่าน shell และใช้ sandbox เดียวกับ Hands
  • mcp_list_tools ต้องมาก่อน mcp_call_tool; ต่อรอบมี output cap MCP_MAX_CHARS (default 4,000) และ timeout MCP_TIMEOUT (default 60 วินาที)
  • registry เขียนแบบ temp + fsync + atomic replace ภายใต้ file lock และตั้ง permission 0600; หาก registry เสีย การอ่านตกกลับแบบ best-effort แต่การเขียนใช้ strict mode เพื่อไม่ล้างรายการเดิมเงียบๆ

สิ่งที่ server ตั้งใจไม่ทำ

Hands ไม่คิดแทน ChatGPT, ไม่รันโมเดลซ้ำ, ไม่เปิด public MCP endpoint, ไม่เลือกทิศทางงานเอง และไม่รับประกันว่า reasoning ของ model จะถูกต้องทุกครั้ง หน้าที่ของมันคือทำให้คำสั่งที่ถูกส่งมาเกิดผลบนเครื่องได้จริง ภายในขอบเขตที่โค้ดและ OS ยอมให้ทำเท่านั้น

อ่านเพิ่มเติม

ความคิดเห็น

กำลังโหลดความคิดเห็น...