SSD Nodes Learn Hosting plans →
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-28

วิธีติดตั้ง SandBase agent runtime บน VPS ด้วยตนเอง

เรียนรู้วิธีรัน SandBase Harness v0.3.2 บนเซิร์ฟเวอร์ส่วนตัว ตั้งแต่การตั้งค่าไฟล์ YAML การกำหนดค่า MCP servers ไปจนถึงการชี้ Anthropic SDK มายังเครื่องของคุณเพื่อควบคุมข้อมูล

สิ่งที่คุณจะได้รับเมื่อโฮสต์ SandBase agent runtime ด้วยตนเอง

การโฮสต์ SandBase agent runtime ด้วยตนเองหมายถึงการรัน SandBase Harness บนเซิร์ฟเวอร์ที่คุณเป็นเจ้าของ ดังนั้นเซสชัน ข้อมูลรับรอง หน่วยความจำ และบันทึกการตรวจสอบ (audit trails) จะถูกจัดเก็บไว้ในดิสก์ของคุณแทนที่จะเป็นของผู้อื่น บริการนี้เป็น Node service โดยจะฟังการเชื่อมต่อที่ 127.0.0.1:3000 ให้บริการ HTTP API ผ่าน /v1 และมีเว็บคอนโซล รวมถึงจัดเก็บสถานะไว้ใน SQLite ในตำแหน่งเดียวกับไฟล์เอเจนต์ของคุณ

/v1 API ถูกออกแบบตามโครงสร้างของ Claude Managed Agents (CMA) ซึ่งเป็น API สำหรับจัดการเอเจนต์แบบโฮสต์ นี่คือสิ่งที่ทำให้ runtime นี้มีความน่าสนใจในทั้งสองทิศทาง คุณสามารถเขียนโค้ดโดยใช้ Anthropic SDK แล้วชี้ baseURL ไปยังเซิร์ฟเวอร์ของคุณเอง จากนั้นจึงย้ายโค้ดชุดเดิมไปใช้งานบนระบบที่โฮสต์ไว้ในภายหลังได้

SandBase Harness ไม่ได้มาพร้อมกับโมเดลในตัว แต่จะเรียกใช้งานโมเดลผ่านการเชื่อมต่อ ณ เดือนสิงหาคม 2026 ระบบรองรับ OpenAI, Anthropic และ endpoint ที่เข้ากันได้กับ OpenAI ซึ่งครอบคลุมถึงเกตเวย์ที่โฮสต์เองและผู้ให้บริการอย่าง DeepSeek V4 คุณยังคงต้องเตรียม API key หรือมีเซิร์ฟเวอร์ภายในที่รองรับ OpenAI API ด้วยตนเอง

สิ่งที่คุณต้องมีก่อนเริ่มต้น

  • VPS ที่รัน Ubuntu 24.04 พร้อม RAM อย่างน้อย 2 GB ขั้นตอนการ build ด้วย TypeScript เป็นส่วนที่ใช้ทรัพยากรมากที่สุดในการติดตั้ง
  • Node.js 22 หรือใหม่กว่า และ npm 10 หรือใหม่กว่า ทั้งสองรายการเป็นข้อกำหนดขั้นต่ำที่ระบุโดยโปรเจกต์
  • git พร้อมด้วย API key สำหรับผู้ให้บริการโมเดลที่คุณวางแผนจะใช้งาน
  • Docker (ใช้เฉพาะในกรณีที่คุณต้องการทำ container sandbox แยกตามเซสชัน)

Ubuntu 24.04 มาพร้อมกับ Node 18.19 ใน repository ของระบบ ซึ่งต่ำกว่าข้อกำหนดขั้นต่ำ ดังนั้นให้ติดตั้ง Node จาก NodeSource แทน

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git
node -v
npm -v

node -v ควรแสดงผลเป็น v22 หรือสูงกว่า และ npm -v ควรแสดงผลเป็น 10 หรือสูงกว่า หาก node -v ยังคงแสดงผลเป็น v18.19.1 แสดงว่าแพ็กเกจของ distribution ยังคงถูกติดตั้งอยู่และมีลำดับความสำคัญสูงกว่าใน PATH ให้ลบออกก่อนดำเนินการต่อ เนื่องจากการ build จะทำงานโดยอ้างอิงจาก node ตัวที่ shell ค้นพบก่อน

การติดตั้ง SandBase จากแท็ก v0.3.2

ให้ติดตั้งจากแท็กเท่านั้น ห้ามติดตั้งจาก branch ที่มีการเปลี่ยนแปลงอยู่ตลอด การทำ bare clone ของ main จะทำให้คุณได้โค้ดล่าสุดที่เพิ่งอัปเดตเข้ามาเมื่อชั่วโมงก่อน ซึ่งอาจไม่ตรงกับคีย์การตั้งค่าด้านล่างนี้ v0.3.2 คือแท็กปัจจุบัน ณ วันที่ 16 สิงหาคม 2026

sudo install -d -o "$USER" -g "$USER" /opt/sandbase
cd /opt/sandbase
git clone --branch v0.3.2 --depth 1 https://github.com/sandbaseai/sandbase-harness.git
cd sandbase-harness
npm ci
npm run build

ให้ใช้ npm ci ไม่ใช่ npm install เพราะ ci จะติดตั้งเวอร์ชันที่ระบุไว้ใน lockfile ที่ถูกคอมมิตไว้เท่านั้น ทำให้โครงสร้างไฟล์ของคุณตรงกับที่ผู้ดูแลระบบได้ทดสอบไว้แล้ว ส่วน npm install จะอนุญาตให้ระบบค้นหาและติดตั้งเวอร์ชันที่ใหม่กว่า ซึ่งเป็นสาเหตุที่ทำให้แท็กที่ควรจะถูกล็อกไว้มีการเปลี่ยนแปลงโดยไม่รู้ตัว

จากนั้นให้สร้าง workspace ขึ้นมา workspace คือไดเรกทอรีแยกต่างหากสำหรับเก็บไฟล์ agent และสถานะการทำงานทั้งหมด การแยก workspace ไว้นอกโฟลเดอร์ source จะช่วยให้คุณสามารถดึงแท็กใหม่มาใช้งานได้โดยไม่กระทบกับข้อมูลของคุณ

mkdir -p /opt/sandbase/workspace
cd /opt/sandbase/workspace
node /opt/sandbase/sandbase-harness/dist/index.js init
node /opt/sandbase/sandbase-harness/dist/index.js start

init จะสร้างไดเรกทอรี .managed-agents/ ไว้ภายใน workspace ส่วน start จะเริ่มการทำงานของคอนโซลที่ http://127.0.0.1:3000/dashboard และ API ที่ http://127.0.0.1:3000/v1 ในขณะนี้คุณยังไม่สามารถเข้าถึงบริการเหล่านี้จากแล็ปท็อปได้ ซึ่งเป็นเรื่องปกติและจะอธิบายรายละเอียดในส่วนถัดไป ให้เข้าถึงคอนโซลผ่าน SSH ไปก่อนในขณะนี้:

ssh -N -L 3000:127.0.0.1:3000 you@your-server

การพิมพ์พาธ node .../dist/index.js ที่ยาวนั้นไม่สะดวก ให้ตั้งชื่อเรียกแทนพาธดังกล่าว

alias sandbase='node /opt/sandbase/sandbase-harness/dist/index.js'

คำสั่งด้านล่างนี้จะเขียนโดยใช้ sandbase <command> ตามชื่อที่ตั้งไว้ข้างต้น

ห้ามติดตั้งผ่าน npm

เอกสารการติดตั้งของโปรเจกต์ระบุไว้ชัดเจนว่า แพ็กเกจ managed-agents แบบไม่มี scope ที่ปรากฏบน npm ไม่ใช่โปรเจกต์นี้ ดังนั้นการใช้ npx managed-agents และ npm install -g managed-agents จะเป็นการดึงข้อมูลที่ไม่เกี่ยวข้องกับ runtime ที่คุณต้องการ ให้ติดตั้งจาก source code ที่ระบุ tag ไว้บน GitHub จนกว่าผู้ดูแลโปรเจกต์จะประกาศแพ็กเกจแบบ scoped อย่างเป็นทางการ นี่ไม่ใช่เพียงหมายเหตุเล็กน้อยในประวัติของโปรเจกต์ แต่เวอร์ชัน v0.3.1 ถูกสร้างขึ้นเพื่อแทนที่วิธีการเริ่มต้นใช้งานแบบเดิมบน npm ด้วยการระบุ path ของ source code ที่ติด tag ไว้โดยเฉพาะ

กำหนดผู้ให้บริการโมเดลให้กับ workspace

init เขียน .managed-agents/config.yaml โดยผู้ให้บริการหนึ่งรายจะถูกกำหนดค่าสำหรับทั้ง workspace และ agent แต่ละตัวจะเลือกใช้ model ID ที่เฉพาะเจาะจงในภายหลัง

model:
  provider: openai
  api_key: ${OPENAI_API_KEY}
storage:
  metadata:
    provider: sqlite
    options: {}
  artifacts:
    provider: local
    options:
      base_path: files

ฟอร์ม ${OPENAI_API_KEY} จะดึงค่ามาจาก environment ของ process เพื่อให้ key ไม่ถูกเก็บไว้ในไฟล์ config และไม่ติดไปกับไฟล์สำรองข้อมูลทุกครั้งที่คุณสำรองข้อมูล ให้ใส่ key ไว้ในไฟล์ environment ที่มีเพียง root เท่านั้นที่อ่านได้ เนื่องจาก systemd จะอ่าน EnvironmentFile= ในฐานะ root ก่อนที่จะลดระดับสิทธิ์ลง

sudo install -d -m 750 /etc/sandbase
sudo touch /etc/sandbase/runtime.env
sudo chmod 600 /etc/sandbase/runtime.env

เปิดไฟล์ดังกล่าวด้วยโปรแกรมแก้ไขข้อความแล้วเพิ่มบรรทัด OPENAI_API_KEY=sk-... ลงไป โดย key ของผู้ให้บริการควรเก็บไว้ที่นี่ ส่วนข้อมูลลับที่ agent ใช้ระหว่าง session ควรเก็บไว้ใน credential vault ของ runtime แทน ซึ่งเป็นปัญหาคนละส่วนที่มีขอบเขตความเสียหายต่างกัน และการอ่าน การเก็บข้อมูลลับให้ห่างจาก AI agents เป็นสิ่งที่ควรทำก่อนที่คุณจะนำ production token ไปวางไว้ในที่ใดที่หนึ่ง

YAML ของเอเจนต์: mcp_servers, เครื่องมือ และนโยบายสิทธิ์

เอเจนต์ถูกกำหนดไว้ในรูปแบบไฟล์ YAML ภายในไดเรกทอรี agents/ ของ workspace นี่คือส่วนของ runtime ที่คุณจะได้ใช้งานจริง คีย์ต่างๆ จะอ่านเข้าใจง่ายขึ้นเมื่อคุณได้ เขียนลูปของเอเจนต์ด้วยตัวเอง แล้ว เพราะแต่ละคีย์เปรียบเสมือนปุ่มปรับค่าที่คุณต้องเขียนโค้ดเองหากไม่มีสิ่งนี้ เช่น system prompt, รายการเครื่องมือ และการตรวจสอบก่อนที่เครื่องมือจะทำงาน

name: Incident commander
description: Triages alerts and coordinates response.
model: gpt-4o
system: |-
  You are an on-call incident commander.
mcp_servers:
  - name: sentry
    type: url
    url: https://mcp.sentry.dev/mcp
tools:
  - type: agent_toolset_20260401
    default_config:
      permission_policy: { type: always_ask }
    configs:
      - name: bash
        permission_policy: { type: always_ask }
  - type: mcp_toolset
    mcp_server_name: sentry
metadata:
  template: incident-commander

โหลดไฟล์และตรวจสอบว่าข้อมูลถูกบันทึกแล้ว:

sandbase reload
sandbase list
sandbase chat agent_assistant --message "hello"

reload จะนำเข้า YAML เริ่มต้นลงใน SQLite โดย list ควรแสดงเอเจนต์พร้อม ID หาก list ไม่แสดงข้อมูล แสดงว่าไฟล์ไม่ได้ถูก parse และ .managed-agents/logs/runtime.log คือที่ที่ระบุสาเหตุของปัญหาไว้

mcp_servers ใช้ประกาศ endpoint ของ MCP (model context protocol) type: url หมายความว่า runtime สื่อสารกับเซิร์ฟเวอร์ผ่าน HTTP และเซิร์ฟเวอร์นั้นทำงานอยู่ที่อื่น ดังนั้นสิ่งที่คุณดูแลอยู่แล้วก็สามารถนำมาใช้ได้ รวมถึง MCP servers ที่โฮสต์บน VPS เดียวกับ runtime ด้วย Web search มักเป็นเครื่องมือแรกที่ผู้ใช้เลือก และควรอ่าน การให้ agent ใช้ SearXNG instance ของคุณเอง ก่อนเชื่อมต่อเครื่องมือดังกล่าว เพราะเครื่องมือที่ส่งคืนหน้าซึ่งเขียนโดยบุคคลอื่นจะนำข้อความที่ไม่น่าเชื่อถือเข้าสู่ context ของ model โดยตรง แนวทางเริ่มต้นที่ปลอดภัยกว่าคือใช้ endpoint แบบ read-only กับข้อมูลที่คุณเป็นเจ้าของอยู่แล้ว ซึ่งเป็นสิ่งที่ openGym เปิดให้ใช้งานถัดจากตัวติดตามการออกกำลังกาย ทำให้ agent ตอบคำถามเกี่ยวกับประวัติการฝึกของคุณได้ โดยไม่สามารถเขียนทับข้อมูลใดๆ ได้

การประกาศเซิร์ฟเวอร์ไม่ได้เป็นการมอบเครื่องมือให้เอเจนต์โดยอัตโนมัติ รายการ tools จะทำหน้าที่นั้นผ่านรายการ mcp_toolset ซึ่งมี mcp_server_name ตรงกับ name ที่ระบุไว้ข้างต้น หากเอเจนต์ทำงานเหมือนกับว่าไม่มีเครื่องมือ MCP อยู่ ให้ตรวจสอบว่าสตริงทั้งสองตรงกันทุกตัวอักษรก่อนที่จะไปตรวจสอบส่วนอื่น

agent_toolset_20260401 คือชุดเครื่องมือที่มีมาให้ในตัว ส่วนต่อท้ายที่เป็นวันที่คือเวอร์ชันของ schema ดังนั้นเอเจนต์ที่ถูกล็อกไว้กับเวอร์ชันนี้จะยังคงใช้คำจำกัดความของเครื่องมือตามที่ถูกเขียนไว้แต่แรก default_config เป็นการตั้งค่านโยบายสำหรับทุกเครื่องมือในชุดนี้ และแต่ละรายการภายใต้ configs จะเป็นการแทนที่การตั้งค่าของเครื่องมือเฉพาะตัวตามชื่อ เช่น bash ในตัวอย่าง

permission_policy คือส่วนที่ทำให้ runtime มีความสำคัญมากกว่าการเรียกใช้โมเดลเปล่าๆ always_ask จะหยุดเซสชันไว้และรอให้มนุษย์อนุมัติการเรียกใช้ก่อนที่เครื่องมือจะทำงาน ส่วน always_allow จะอนุญาตให้ผ่านไปได้ การตั้งค่า bash เป็น always_ask หมายความว่าเอเจนต์ไม่สามารถรันคำสั่ง shell ได้หากคุณยังไม่ได้เห็นคำสั่งนั้นโดยละเอียดก่อน ซึ่งเป็นการควบคุมแบบเดียวกับที่คุณควรใช้เมื่อ รัน Claude Code บน VPS อย่างปลอดภัย หากคุณใช้งาน DeepSeek Harness ด้วย การควบคุมแบบเดียวกันจะมาในรูปแบบของ add-on แทนที่จะเป็นคีย์ใน YAML และ ปลั๊กอินที่จำกัดงบประมาณและตรวจสอบการเรียกใช้เครื่องมือ คือสิ่งที่ใกล้เคียงที่สุดกับบล็อกการตั้งค่านี้

โหมด Sandbox ทั้ง 3 รูปแบบและสถานการณ์ที่เหมาะสม

การเรียกใช้เครื่องมือ (tool calls) ที่มีการประมวลผลโค้ดจะทำงานอยู่ภายใน sandbox โดยระบบหลังบ้านจะถูกเลือกตามสภาพแวดล้อมผ่าน sandbox_provider ในออบเจกต์ config ของสภาพแวดล้อมนั้น หรือตั้งค่าได้ที่เมนู Settings แล้วเลือก Sandbox ในคอนโซล สภาพแวดล้อมสามารถสร้างขึ้นผ่าน API ได้ที่ POST /v1/environments

local จะรันโค้ดในฐานะ child process ของรันไทม์บนโฮสต์ โดยใช้สิทธิ์ของผู้ใช้งานรันไทม์นั้นๆ นี่เป็นค่าเริ่มต้นและเหมาะสมในกรณีที่คุณเป็นผู้ใช้งานเพียงคนเดียวและเอเจนต์อ่านเฉพาะไฟล์ที่คุณเป็นเจ้าของเท่านั้น โหมดนี้ไม่มีการแยกส่วน (isolation) การเรียกใช้เครื่องมือเพื่อลบไฟล์จะส่งผลต่อไฟล์ของคุณโดยตรง และการเรียกใช้เครื่องมือเพื่ออ่าน /etc/sandbase/runtime.env จะทำให้เข้าถึงคีย์ของผู้ให้บริการของคุณได้

docker จะเริ่มคอนเทนเนอร์หนึ่งรายการต่อหนึ่งเซสชัน

{
  "sandbox_provider": "docker",
  "image": "node:22-slim",
  "resources": { "memory": "1g", "cpu": 1 }
}

เซสชันจะได้รับระบบไฟล์ หน่วยความจำ และส่วนแบ่ง CPU ของตัวเอง และคอนเทนเนอร์จะถูกลบออกเมื่อจบเซสชัน ให้เปลี่ยนมาใช้โหมดนี้ทันทีที่เอเจนต์ต้องรันโค้ดที่คุณไม่ได้เป็นผู้เขียนเอง ข้อควรระวังคือผู้ใช้งานรันไทม์จำเป็นต้องเข้าถึง Docker socket ได้ และการเป็นสมาชิกในกลุ่ม docker นั้นเทียบเท่ากับการมีสิทธิ์ root บนโฮสต์ คอนเทนเนอร์แบบต่อเซสชันมีรูปแบบเดียวกับ self-hosted agent sandboxes with one container per run ดังนั้นเหตุผลเกี่ยวกับสิ่งที่กระบวนการที่หลุดออกมาจาก sandbox (escaped process) สามารถเข้าถึงได้จึงยังคงใช้ได้เช่นเดียวกัน

kubernetes จะรันเวิร์กโหลดของเซสชันในรูปแบบ pod และควบคุมด้วย kubectl exec และ kubectl cp อิมเมจของรันไทม์จำเป็นต้องมี kubectl และ ServiceAccount ของรันไทม์ต้องได้รับสิทธิ์ RBAC (role-based access control) ในการสร้าง ลบ ดึงข้อมูล แสดงรายการ และติดตาม (watch) pod ใน namespace เป้าหมาย รวมถึงสิทธิ์ใน subresource exec โหมดนี้คุ้มค่ากับการตั้งค่าก็ต่อเมื่อคุณมีการใช้งานคลัสเตอร์อยู่แล้วเท่านั้น

เหตุใด runtime จึงถูกผูกไว้กับ 127.0.0.1?

เนื่องจากมันเริ่มต้นทำงานโดยปิดระบบยืนยันตัวตนไว้ runtime จะเปิดใช้งานการยืนยันตัวตนด้วย bearer-token ก็ต่อเมื่อมี API key อย่างน้อยหนึ่งรายการ และการติดตั้ง init ใหม่จะยังไม่มีการสร้าง key ใดๆ การผูกไว้กับ 0.0.0.0 ในค่าเริ่มต้นดังกล่าวจะทำให้ agent runtime ที่ไม่มีการยืนยันตัวตน ซึ่งถือครองเครื่องมือ shell และ provider key ของคุณ ไปปรากฏอยู่บนอินเทอร์เน็ตสาธารณะ

ดังนั้น หากคุณต้องการให้เข้าถึงได้ ให้คงค่า bind address ไว้ตามเดิมแล้วดำเนินการอีกสองขั้นตอนดังนี้

ขั้นแรก ให้เปิดระบบยืนยันตัวตน กำหนดค่า MANAGED_AGENTS_API_KEY ในไฟล์ environment ของ service หรือสร้าง key ด้วย POST /v1/api-keys ซึ่งจะแสดงฟิลด์ secret_key ออกมาเพียงครั้งเดียวและจะไม่แสดงให้เห็นอีกเลย จากนั้น client จะต้องส่ง Authorization: Bearer <key> ในทุกคำขอ หนึ่ง key คือหนึ่งตัวตนที่ใช้ร่วมกัน ดังนั้นหากสิ่งที่คุณต้องการจริงๆ คือการแยก sandboxed agent สำหรับเพื่อนร่วมทีมแต่ละคน โดยเก็บ provider key ไว้ใน gateway เดียว OneCLI ถูกสร้างมาเพื่อรองรับรูปแบบนั้น

ขั้นที่สอง ให้วาง reverse proxy ไว้ด้านหน้าและทำ TLS (transport layer security) termination ที่นั่น โดยการออกแบบแล้ว runtime จะให้บริการผ่าน plain HTTP และคาดหวังให้ส่วนประกอบอื่นเป็นผู้จัดการเรื่อง certificate

server {
    listen 443 ssl;
    server_name agents.example.com;

    ssl_certificate     /etc/letsencrypt/live/agents.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/agents.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_buffering off;
        proxy_read_timeout 3600s;
    }
}

สองบรรทัดนั้นไม่ใช่แค่การตกแต่ง proxy_buffering off มีความสำคัญเนื่องจาก session จะสตรีมผ่าน server-sent events (SSE) และหากเปิดการทำ buffering ไว้ nginx จะกักเก็บ response ไว้จนกว่า buffer จะเต็ม ส่งผลให้ console ไม่แสดงผลใดๆ ในขณะที่ agent กำลังทำงาน แล้วจึงแสดงผลทั้งหมดออกมาในคราวเดียวเมื่อเสร็จสิ้น ส่วน proxy_read_timeout 3600s มีความสำคัญเนื่องจากค่าเริ่มต้นคือ 60 วินาที ดังนั้นสตรีมที่เงียบไปนานกว่าหนึ่งนาทีจะถูก proxy ตัดการเชื่อมต่อกลางคัน และความผิดพลาดนั้นจะดูเหมือน runtime เกิดการ crash

สำหรับ firewall ให้เปิดพอร์ต 22 และ 443 ส่วนพอร์ต 3000 ให้ปิดไว้ เพราะ proxy จะเข้าถึงพอร์ตดังกล่าวผ่าน loopback และไม่ควรมีสิ่งใดจากภายนอกเครื่องเข้าถึงได้

ชี้ Anthropic SDK ไปยังเซิร์ฟเวอร์ของคุณเอง

runtime นี้ใช้พื้นผิวแบบ CMA /v1 ดังนั้นไคลเอนต์ Anthropic SDK จึงสามารถสื่อสารกับมันได้โดยการเปลี่ยนฟิลด์เพียงหนึ่งรายการ

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  apiKey: process.env.MANAGED_AGENTS_API_KEY ?? 'local-dev-key',
  baseURL: 'http://127.0.0.1:3000'
});

นอกจากนี้ยังรองรับ beta headers ที่ไคลเอนต์ Claude Managed Agents ส่งมา ได้แก่ anthropic-beta: managed-agents-2026-04-01 และ anthropic-beta: agent-memory-2026-07-22 ซึ่งเป็นตัวเลือกเสริมเมื่อใช้งานกับ runtime ภายในเครื่อง โดยมีไว้เพื่อให้โค้ดที่เขียนสำหรับการใช้งานบนระบบคลาวด์สามารถทำงานได้โดยไม่ต้องแก้ไข

ความเข้ากันได้นั้นใกล้เคียงแต่ไม่สมบูรณ์ โปรดอ่าน docs/api-matrix.md ใน checkout ก่อนที่คุณจะอนุมานว่ามีพื้นผิวการทำงานนั้นอยู่จริง เนื่องจากโปรเจกต์ได้ระบุช่องว่างของตัวเองไว้ในเอกสาร รวมถึงเรื่อง custom tools ฝั่งไคลเอนต์ ซึ่งยังคงต้องมีการลงทะเบียนชื่อเหนือโปรโตคอล event-result ในปัจจุบัน

การใช้ HTTP แบบปกติก็ใช้งานได้ดีเช่นกัน และเป็นวิธีที่เร็วที่สุดในการตรวจสอบว่า runtime ทำงานอยู่:

curl -N -X POST http://127.0.0.1:3000/v1/sessions/SESSION_ID/messages \
  -H "Content-Type: application/json" \
  -d '{"content": "Hello", "stream": true}'

การตอบสนองที่ปกติคือการส่งสตรีมของเหตุการณ์ที่เข้ามาอย่างต่อเนื่อง หากการเชื่อมต่อหลุด ให้กลับมาทำงานต่อจากเหตุการณ์ล่าสุดที่คุณเห็นแทนการเล่นซ้ำทั้งเทิร์น:

curl -N http://127.0.0.1:3000/v1/sessions/SESSION_ID/events/stream \
  -H "Last-Event-ID: EVENT_ID"

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

ตำแหน่งจัดเก็บข้อมูลประจำตัว หน่วยความจำ และบันทึกการตรวจสอบบนดิสก์

ทุกสิ่งที่ runtime เป็นเจ้าของจะอยู่ภายใต้ .managed-agents/ ใน workspace

.managed-agents/
├── config.yaml
├── data.db
├── logs/runtime.log
├── files/
├── skills/
├── snapshots/
└── sandbox/
  • data.db คือ metadata ของ SQLite ซึ่งประกอบด้วย agents, sessions, รายการใน credential vault, รายการใน memory store และ API keys
  • files/ ใช้เก็บข้อมูลไฟล์ที่อัปโหลด และ skills/ ใช้เก็บแพ็กเกจ skill ที่อัปโหลด
  • snapshots/ ใช้เก็บ snapshot ของ session workspace และ sandbox/ ใช้เก็บไดเรกทอรีการทำงานของ session ในโหมด local
  • logs/runtime.log เป็นจุดแรกที่ควรตรวจสอบเมื่อระบบไม่ทำงานโดยไม่มีข้อความแจ้งเตือนใดๆ

Credential vaults คือกลุ่มของความลับ (secrets) ซึ่งแต่ละรายการจะถูกเพิ่มด้วย auth_type เช่น environment_variable และเชื่อมต่อเข้ากับ session ผ่าน vault_ids ในขณะที่สร้าง session ส่วน memory stores จะเก็บรายการที่ตั้งชื่อไว้ซึ่งคุณสามารถ mount เข้าไปใน session ในฐานะ memory_store โดยมีการตั้งค่าการเข้าถึงและคำสั่งเฉพาะ ทั้งสองส่วนนี้จัดเก็บอยู่ใน data.db ซึ่งเป็นจุดแตกต่างสำคัญระหว่างการทำงานนี้กับการเรียกใช้โมเดลทั่วไป กล่าวคือ runtime จะจดจำข้อมูลข้าม session และบันทึกเหตุการณ์ที่เกิดขึ้นไว้

เนื่องจากข้อมูลทั้งหมดอยู่ในไดเรกทอรีเดียว ให้สำรองข้อมูลโดยรวมเป็นชุดเดียวกัน

sudo systemctl stop sandbase
sudo tar czf /root/sandbase-$(date +%F).tgz -C /opt/sandbase/workspace .managed-agents
sudo systemctl start sandbase

หยุดการทำงานของ service ก่อนเสมอ การคัดลอกฐานข้อมูล SQLite ในขณะที่ runtime กำลังเขียนข้อมูลอยู่อาจทำให้ได้ไฟล์ที่ไม่สามารถเปิดใช้งานได้เมื่อกู้คืน ซึ่งคุณจะพบปัญหานี้ในวันที่จำเป็นต้องใช้งานจริง หากคุณต้องการเก็บ agent YAML ไว้ใน git และเก็บสถานะไว้ที่อื่น เอกสารการติดตั้งรองรับการกำหนดตำแหน่งจัดเก็บสถานะด้วย --data-dir บน start

การกู้คืนทำได้โดยย้อนขั้นตอน: ตรวจสอบ tag เดียวกันบนเครื่องใหม่ แตกไฟล์ archive ลงใน workspace แล้วเริ่มการทำงานของ service กุญแจผู้ให้บริการ (provider key) ของคุณจะไม่อยู่ใน archive หากคุณใช้รูปแบบ ${OPENAI_API_KEY} ดังนั้นควรเก็บกุญแจดังกล่าวไว้ในที่ที่คุณสามารถเข้าถึงได้เสมอ

การรันภายใต้ systemd

กำหนดให้ runtime ทำงานภายใต้ผู้ใช้เฉพาะ เพื่อป้องกันไม่ให้เครื่องมือที่เรียกผ่านโหมด local sandbox สามารถเข้าถึงสิทธิ์ของคุณได้

sudo adduser --system --group --no-create-home --home /opt/sandbase sandbase
sudo chown -R sandbase:sandbase /opt/sandbase

บันทึกไฟล์นี้เป็น /etc/systemd/system/sandbase.service

[Unit]
Description=SandBase Harness runtime
After=network-online.target

[Service]
User=sandbase
Group=sandbase
WorkingDirectory=/opt/sandbase/workspace
EnvironmentFile=/etc/sandbase/runtime.env
ExecStart=/usr/bin/node /opt/sandbase/sandbase-harness/dist/index.js start --host 127.0.0.1 --port 3000
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

ตัวอย่างการติดตั้งของโปรเจกต์เรียกใช้ไบนารี managed-agents ที่ PATH แต่การติดตั้งจาก tagged-source จะไม่สร้างไฟล์ดังกล่าวขึ้นมา ดังนั้น ExecStart จึงรัน node โดยอ้างอิงจาก entry point ที่ build ไว้แทน

sudo systemctl daemon-reload
sudo systemctl enable --now sandbase
sudo systemctl status sandbase
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3000/dashboard

ผลลัพธ์ที่ปกติคือ active (running) จาก status และ 200 จาก curl หากพบผลลัพธ์อื่น ให้ตรวจสอบ journalctl -u sandbase -n 50 เป็นอันดับแรก และ .managed-agents/logs/runtime.log เป็นอันดับถัดมา enable --now คือส่วนที่สำคัญที่สุด เนื่องจากกระบวนการที่เริ่มด้วยตนเองจะหายไปหลังจากรีบูตเครื่องครั้งถัดไป

สิ่งที่มักเกิดข้อผิดพลาดและข้อความที่คุณจะพบ

npm run build ถูกสั่งปิดโดยไม่มีข้อความแจ้งเตือนจาก npm บน VPS ขนาด 1 GB การคอมไพล์ TypeScript อาจถูกยุติโดย kernel out-of-memory killer ซึ่งจะรายงานเหตุการณ์นี้ไปยัง log ของ kernel แทนที่จะแจ้งผ่าน npm ให้ตรวจสอบด้วยคำสั่ง journalctl -k | grep -i "out of memory" ซึ่งจะแสดงบรรทัดที่ระบุชื่อกระบวนการ node ที่ถูกสั่งปิด ให้เพิ่ม swap หรือทำการ build บน instance ที่มีขนาดใหญ่กว่าแล้วคัดลอก dist/ มาแทน

Error: listen EADDRINUSE: address already in use 127.0.0.1:3000 มีกระบวนการอื่นใช้งานพอร์ตนั้นอยู่แล้ว ให้ใช้ sudo ss -lntp | grep 3000 เพื่อดูว่าคือกระบวนการใด คุณสามารถหยุดกระบวนการนั้น หรือเริ่ม runtime ใหม่ด้วย --port 3001 แล้วอัปเดตค่าใน proxy

Dashboard ไม่โหลดเมื่อเข้าจากแล็ปท็อปของคุณ นี่เป็นพฤติกรรมปกติเนื่องจาก runtime ผูกไว้กับ loopback ให้ใช้ SSH tunnel ตามที่ระบุไว้ข้างต้น หรือตั้งค่า reverse proxy ให้เสร็จสมบูรณ์ ห้ามแก้ไขด้วยการใช้ --host 0.0.0.0 เพราะระบบยืนยันตัวตนจะยังไม่ทำงานจนกว่าจะมีคีย์

Docker sandboxes ล้มเหลวด้วยข้อความ permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock ผู้ใช้ sandbase ไม่ได้อยู่ในกลุ่ม docker ให้แก้ไขด้วย sudo usermod -aG docker sandbase แล้วรีสตาร์ทบริการ โปรดเข้าใจสิทธิ์ที่คุณมอบให้: กลุ่มนี้มีสิทธิ์ระดับ root บนโฮสต์ ซึ่งจะทำให้เหตุผลที่คุณแยกผู้ใช้สำหรับรันไทม์ลดความสำคัญลงไป

Kubernetes sandboxes ล้มเหลวด้วยข้อความ Error from server (Forbidden) ServiceAccount ขาดสิทธิ์ในการจัดการ pod หรือ subresource exec ให้ตรวจสอบโดยตรงด้วย kubectl auth can-i create pods/exec -n <namespace> ซึ่งจะตอบกลับเป็น yes หรือ no

ทุกคำขอส่งค่า 401 หลังจากเพิ่ม API key ระบบยืนยันตัวตนจะเปิดใช้งานเมื่อมีคีย์แรกเกิดขึ้น และจะมีผลกับทั้งคอนโซลและ API ให้ส่ง Authorization: Bearer <key> หากคุณทำคีย์หาย ให้สร้างคีย์ใหม่ เนื่องจาก secret_key จะแสดงให้เห็นเพียงครั้งเดียวและไม่ได้ถูกจัดเก็บในรูปแบบที่อ่านได้

เครื่องมือของ MCP server ไม่ปรากฏในเซสชัน ให้ตรวจสอบ mcp_server_name ในบล็อก tools เทียบกับ name ใน mcp_servers จากนั้นตรวจสอบว่า runtime สามารถเข้าถึง URL นั้นจากเซิร์ฟเวอร์ได้จริงด้วย curl -i <url> MCP server แบบ URL ถือเป็น dependency ทางเครือข่าย ซึ่ง VPS มีการจัดการชื่อและการกำหนดเส้นทางทราฟฟิกที่แตกต่างจากแล็ปท็อปของคุณ

FAQ

ฉันสามารถรัน SandBase Harness โดยไม่มีคีย์ของ OpenAI หรือ Anthropic ได้หรือไม่?

ได้ หากคุณมี endpoint ที่รองรับมาตรฐาน OpenAI ตัว runtime รองรับผู้ให้บริการทั้ง OpenAI, Anthropic และผู้ให้บริการที่เข้ากันได้กับ OpenAI ดังนั้นเซิร์ฟเวอร์ภายในที่สื่อสารผ่าน OpenAI API จึงใช้งานได้ ให้ตั้งค่าผู้ให้บริการ workspace ใน .managed-agents/config.yaml และระบุ api_key รวมถึง endpoint ไปยังเซิร์ฟเวอร์นั้น ตัว runtime ไม่มีโมเดลในตัว จึงจำเป็นต้องมีบริการอื่นคอยตอบรับการเรียกใช้งาน

การเปิดใช้งาน runtime บนพอร์ตสาธารณะมีความปลอดภัยหรือไม่?

ไม่ปลอดภัยหากติดตั้งตามค่าเริ่มต้น ตัวโปรแกรมจะผูกกับ 127.0.0.1:3000 และเริ่มทำงานโดยปิดระบบยืนยันตัวตนไว้ ซึ่งการแก้ไขไม่ใช่การเปลี่ยนที่อยู่การผูกพอร์ต ให้สร้าง API key หรือตั้งค่า MANAGED_AGENTS_API_KEY เพื่อเปิดใช้งานการยืนยันตัวตนแบบ bearer-token จากนั้นให้ใช้ nginx หรือ Caddy วางไว้ด้านหน้าเพื่อทำ TLS และปิดพอร์ต 3000 บน firewall เพื่อให้เส้นทางเข้าถึงเดียวคือผ่าน proxy เท่านั้น

sandbox แบบ local, Docker และ Kubernetes แตกต่างกันอย่างไร?

local รันโค้ดเครื่องมือเป็น child process ของ runtime บนโฮสต์ โดยใช้สิทธิ์ของผู้ใช้งาน runtime และไม่มีการแยกส่วน (isolation) docker จะสร้าง container แยกสำหรับแต่ละ session โดยมีระบบไฟล์, ขีดจำกัดหน่วยความจำ และส่วนแบ่ง CPU ของตัวเอง และจะลบออกเมื่อจบ session ส่วน kubernetes จะรัน session ในรูปแบบ pod และควบคุมด้วย kubectl exec ซึ่งต้องใช้ kubectl ภายใน image ของ runtime และต้องมีการตั้งค่า RBAC บน pod รวมถึงสิทธิ์ exec subresource ใน namespace เป้าหมาย

ฉันจำเป็นต้องสำรองข้อมูลส่วนใดบ้าง?

ไดเรกทอรี .managed-agents/ ใน workspace ซึ่งเก็บ config.yaml, ฐานข้อมูล SQLite data.db ที่มีข้อมูล agents, sessions, รายการใน credential vault และหน่วยความจำ รวมถึงไฟล์ที่อัปโหลด, skill packages และ session snapshots ให้หยุดการทำงานของ service ก่อนคัดลอก เพื่อป้องกันไม่ให้ SQLite ถูกเขียนข้อมูลระหว่างการทำ archive สำหรับ Provider API keys ที่อ้างอิงเป็น ${OPENAI_API_KEY} จะไม่ได้รวมอยู่ในไฟล์สำรองข้อมูล ดังนั้นควรจัดเก็บแยกต่างหาก

ทำไมต้อง clone tag v0.3.2 แทนที่จะใช้ main?

Tag คือสถานะของโค้ดที่ถูกล็อกไว้ ทำให้คีย์การตั้งค่าและคำสั่ง CLI ที่คุณอ่านตรงกับสิ่งที่คุณได้รับจริง main มีการเปลี่ยนแปลงอยู่เสมอ และคีย์การตั้งค่าอาจถูกเปลี่ยนชื่อได้ระหว่างช่วงเวลาที่เขียนคู่มือกับช่วงเวลาที่คุณใช้งาน นอกจากนี้โครงการยังแจ้งเตือนว่าแพ็กเกจ managed-agents ที่ไม่มี scope บน npm ไม่ใช่ของโครงการนี้ ดังนั้นการใช้ npx managed-agents จะเป็นการติดตั้งสิ่งที่ไม่มีความเกี่ยวข้องกัน รุ่น v0.3.1 ถูกปล่อยออกมาเพื่อแทนที่การเริ่มต้นใช้งานผ่าน npm ด้วยเส้นทางของ source code ที่ระบุเวอร์ชันไว้อย่างชัดเจน