SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor

วิธีติดตั้ง SandBase agent runtime บน VPS ของคุณเอง

เรียนรู้วิธีรัน SandBase Harness v0.3.2 บนเซิร์ฟเวอร์ส่วนตัว ครอบคลุมขั้นตอนการติดตั้งแบบ tagged การตั้งค่า agent YAML การเชื่อมต่อ MCP servers และการชี้ Anthropic SDK มายังโฮสต์ของคุณ

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

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

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 และไม่ติดไปกับไฟล์สำรองข้อมูลทุกครั้งที่คุณทำ backup ให้ใส่ 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 ของผู้ให้บริการควรเก็บไว้ที่นี่ ส่วนข้อมูลลับ (secrets) ที่ agent ใช้ระหว่าง session ควรเก็บไว้ใน credential vault ของ runtime แทน ซึ่งเป็นปัญหาคนละส่วนและมีขอบเขตความเสียหายที่ต่างกัน การอ่าน การเก็บข้อมูลลับให้ห่างจาก AI agents จึงเป็นสิ่งที่ควรทำก่อนที่คุณจะนำ production token ไปวางไว้ในที่ใดที่หนึ่ง

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

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

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 ไม่แสดงข้อมูลดังกล่าว แสดงว่าไฟล์ไม่ได้ถูกประมวลผล และ .managed-agents/logs/runtime.log คือตำแหน่งที่ระบุสาเหตุของปัญหา

mcp_servers ใช้สำหรับประกาศจุดเชื่อมต่อ MCP (model context protocol) ค่า type: url หมายความว่ารันไทม์จะสื่อสารผ่าน HTTP กับเซิร์ฟเวอร์ที่ทำงานในตำแหน่งอื่น ดังนั้นบริการใดก็ตามที่คุณใช้งานอยู่แล้วสามารถนำมาใช้ที่นี่ได้ รวมถึง MCP servers ที่โฮสต์บน VPS เดียวกัน กับรันไทม์

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

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

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

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

การเรียกใช้เครื่องมือ (tool calls) ที่มีการประมวลผลโค้ดจะทำงานอยู่ภายในแซนด์บ็อกซ์ โดยระบบหลังบ้านจะถูกเลือกตามสภาพแวดล้อมผ่าน 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 ที่ใช้หนึ่งคอนเทนเนอร์ต่อการรันหนึ่งครั้ง ดังนั้นเหตุผลเกี่ยวกับสิ่งที่กระบวนการที่หลุดรอดออกมา (escaped process) สามารถเข้าถึงได้จึงนำมาใช้ในกรณีนี้ได้เช่นกัน

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

ประการที่สอง ให้วาง reverse proxy ไว้ด้านหน้าและทำ TLS (transport layer security) termination ที่นั่น runtime ถูกออกแบบมาให้ให้บริการผ่าน 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 ไปยังเซิร์ฟเวอร์ของคุณเอง

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

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

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

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 ซึ่งเป็นจุดแตกต่างที่สำคัญระหว่างการทำงานนี้กับการเรียกใช้โมเดลแบบดิบ (raw model call) กล่าวคือ 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 และเก็บสถานะไว้ที่อื่น เอกสารการ deployment รองรับการกำหนดตำแหน่งของสถานะด้วย --data-dir บน start

การกู้คืนทำได้โดยการย้อนขั้นตอน: ให้ checkout 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 การติดตั้งจาก source ที่มีการระบุ tag จะไม่สร้างไฟล์ดังกล่าวขึ้นมา ดังนั้น 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 อาจถูกหยุดโดย OOM killer ของ kernel ซึ่งจะรายงานเหตุการณ์นี้ไปยัง 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 จะแสดงชื่อกระบวนการดังกล่าว ให้หยุดกระบวนการนั้นหรือเริ่มรันไทม์ด้วย --port 3001 แล้วอัปเดตค่าใน proxy

Dashboard ไม่โหลดเมื่อเข้าใช้งานจากแล็ปท็อปของคุณ นี่เป็นพฤติกรรมที่ตั้งใจไว้ เนื่องจากรันไทม์จะผูกไว้กับ 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 จากนั้นตรวจสอบว่ารันไทม์สามารถเข้าถึง URL นั้นได้จากตัวเซิร์ฟเวอร์เองด้วย curl -i <url> โดย MCP server แบบ URL ถือเป็น dependency ทางเครือข่าย ซึ่ง VPS มีการจัดการการ resolve ชื่อและการกำหนดเส้นทาง traffic ที่แตกต่างจากแล็ปท็อปของคุณ

FAQ

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

ได้ หากคุณมี endpoint ที่รองรับมาตรฐาน OpenAI ตัว runtime รองรับผู้ให้บริการทั้ง OpenAI, Anthropic และผู้ให้บริการที่รองรับ API ของ 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 บนโฮสต์ โดยใช้สิทธิ์ของผู้ใช้ที่รันโปรแกรมและไม่มีการแยกส่วน (isolation) docker จะสร้าง container แยกสำหรับแต่ละ session โดยมีระบบไฟล์, ขีดจำกัดหน่วยความจำ และส่วนแบ่ง CPU ของตัวเอง และจะลบ container ทิ้งเมื่อจบ session ส่วน kubernetes จะรัน session ในรูปแบบ pod และควบคุมด้วย kubectl exec ซึ่งต้องการ kubectl ภายในอิมเมจของ runtime รวมถึงต้องมีการตั้งค่า RBAC บน pod และสิทธิ์ exec subresource ใน namespace ปลายทาง

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

ไดเรกทอรี .managed-agents/ ใน workspace ซึ่งเก็บ config.yaml, ฐานข้อมูล SQLite data.db ที่มีข้อมูล agents, sessions, รายการใน credential vault และหน่วยความจำ รวมถึงไฟล์ที่อัปโหลด, skill packages และ snapshots ของ session ให้หยุดบริการก่อนทำการคัดลอกเพื่อป้องกันไม่ให้ SQLite ถูกเขียนข้อมูลระหว่างการทำ archive สำหรับ 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 ที่ระบุเวอร์ชันไว้อย่างชัดเจนแทน