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 และ
.gitdirectory ให้อยู่ภายใน approved workspace ทั้งคู่ statusและdiffเป็น read-only;diffปิด external diff/textconv และเลือก staged diff ได้addต้องส่ง path ที่ระบุชัด ห้ามส่ง.หรือ stage ทั้ง repository แบบ implicit และห้าม stage.gitcommitใช้เฉพาะ 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 capMCP_MAX_CHARS(default 4,000) และ timeoutMCP_TIMEOUT(default 60 วินาที)- registry เขียนแบบ temp +
fsync+ atomic replace ภายใต้ file lock และตั้ง permission0600; หาก registry เสีย การอ่านตกกลับแบบ best-effort แต่การเขียนใช้ strict mode เพื่อไม่ล้างรายการเดิมเงียบๆ
สิ่งที่ server ตั้งใจไม่ทำ
Hands ไม่คิดแทน ChatGPT, ไม่รันโมเดลซ้ำ, ไม่เปิด public MCP endpoint, ไม่เลือกทิศทางงานเอง และไม่รับประกันว่า reasoning ของ model จะถูกต้องทุกครั้ง หน้าที่ของมันคือทำให้คำสั่งที่ถูกส่งมาเกิดผลบนเครื่องได้จริง ภายในขอบเขตที่โค้ดและ OS ยอมให้ทำเท่านั้น
ความคิดเห็น
กำลังโหลดความคิดเห็น...