amla-sandbox
Mọi framework của agent phổ biến chạy mã do LLM sinh ra qua subprocess hoặc exec(), dẫn đến rủi ro thực thi mã tuỳ ý. Một sơ suất và mã của bạn có thể bị xâm nhập.
Phương thức Thực thi của Framework
| Framework | Phương thức Thực thi | Nguồn |
|---|---|---|
| LangChain | exec(command, globals, locals) | CVE-2025-68664, GitHub #5294 |
| AutoGen | subprocess.run() | Code Executors docs |
| SWE-Agent | subprocess.run(["bash", ...]) | SWE-ReX |
Một số framework cung cấp cách ly Docker (OpenHands, AutoGen), nhưng điều này yêu cầu một Docker daemon và quản lý cơ sở hạ tầng Container.
amla-sandbox là một sandbox WASM với khả năng kiểm soát nghiêm ngặt. Agents chỉ có thể gọi các công cụ bạn cung cấp với các giới hạn do bạn định nghĩa. Hệ thống tệp ảo được cách ly, không có mạng và không có khả năng thoát lệnh.
pip install "git+https://github.com/amlalabs/amla-sandbox"
Không cần Docker hoặc VM. Một binary duy nhất, hoạt động ở mọi nơi.
from amla_sandbox import create_sandbox_tool
sandbox = create_sandbox_tool(tools=[stripe_api, database])
# Agent viết một script thay vì 10 lệnh tool (JavaScript)
result = sandbox.run('''
const txns = await stripe.listTransactions({customer: "cus_123"});
const disputed = txns.filter(t => t.disputed);
console.log(disputed[0]);
''', language="javascript")
# Hoặc với các shell pipelines
result = sandbox.run('''
tool stripe.listTransactions --customer cus_123 | jq '[.[] | select(.disputed)] | .[0]'
''', language="shell")
Tại sao điều này quan trọng
Gọi công cụ rất tốn kém tài nguyên. Mỗi lần gọi là một chuyến đi khứ hồi qua mô hình:
LLM → tool → LLM → tool → LLM → tool → ...
Mười lần gọi công cụ = mười lần triệu gọi LLM. Chế độ mã giúp điều này trở nên đơn giản hơn:
LLM → script thực hiện tất cả 10 điều → kết quả
Nhưng bạn không thể chỉ đơn giản thực thi mã mà mô hình tạo ra. amla-sandbox cung cấp hiệu quả chế độ mã với sự cách ly thực sự.
Mô hình bảo mật
Sandbox chạy trong WebAssembly với WASI cho giao diện hệ thống cuộc gọi tối giản. WASM cung cấp cách ly bộ nhớ theo thiết kế—bộ nhớ tuyến tính được kiểm tra giới hạn, và không có cách nào để thoát ra không gian địa chỉ host.
from amla_sandbox import Sandbox, MethodCapability, ConstraintSet, Param
sandbox = Sandbox(
capabilities=[
MethodCapability(
method_pattern="stripe/charges/*",
constraints=ConstraintSet([
Param("amount") <= 10000,
Param("currency").is_in(["USD", "EUR"]),
]),
max_calls=100,
),
],
tool_handler=my_handler,
)
# This works
sandbox.execute('await stripe.charges.create({amount: 500, currency: "USD"})')
# This fails - amount exceeds capability
sandbox.execute('await stripe.charges.create({amount: 50000, currency: "USD"})')
Thiết kế này lấy ý tưởng từ bảo mật dựa trên khả năng, giống như trong hệ thống seL4. Truy cập được cấp phép rõ ràng và không có quyền hạn ngầm định.
Bắt đầu nhanh
from amla_sandbox import create_sandbox_tool
sandbox = create_sandbox_tool()
# JavaScript
sandbox.run("console.log('hello'.toUpperCase())", language="javascript") # -> "HELLO"
# Shell
sandbox.run("echo 'hello' | tr 'a-z' 'A-Z'", language="shell") # -> "HELLO"
# Với công cụ
def get_weather(city: str) -> dict:
return {"city": city, "temp": 72}
sandbox = create_sandbox_tool(tools=[get_weather])
sandbox.run("const w = await get_weather({city: 'SF'}); console.log(w);", language="javascript")
Với các ràng buộc:
sandbox = create_sandbox_tool(
tools=[transfer_money],
constraints={
"transfer_money": {
"amount": "<=1000",
"currency": ["USD", "EUR"],
},
},
max_calls={"transfer_money": 10},
)
Ghi chú API JavaScript
- Công cụ yêu cầu cú pháp đối tượng:
// WORKS - công cụ luôn nhận đối số đối tượng
await get_weather({city: "SF"});
await transfer({to: "alice", amount: 500});
// FAILS - đối số vị trí không hoạt động
await get_weather("SF"); // Error: argument after ** must be a mapping
- Sử dụng
returnhoặcconsole.log()cho đầu ra:
// Giá trị trả về được bắt và làm đầu ra
return await get_weather({city: "SF"}); // -> {"city":"SF","temp":72}
return {a: 1, b: 2}; // -> {"a":1,"b":2}
return "hello"; // -> hello (strings not double-quoted)
// console.log cũng hoạt động
console.log(JSON.stringify({a: 1})); // -> {"a":1}
// Không có return = không có đầu ra
const x = 42; // -> (no output)
- VFS chỉ có thể ghi dưới /workspace và /tmp:
// WORKS - /workspace và /tmp là ReadWrite
await fs.writeFile('/workspace/data.json', '{}');
await fs.mkdir('/tmp/cache');
// FAILS - root là read-only
await fs.mkdir('/mydir'); // EACCES: Permission denied
LangGraph
Để tích hợp với LangGraph:
from langgraph.prebuilt import create_react_agent
from langchain_anthropic import ChatAnthropic
from amla_sandbox import create_sandbox_tool
sandbox = create_sandbox_tool(tools=[get_weather, search_db])
agent = create_react_agent(
ChatAnthropic(model="claude-sonnet-4-20250514"),
[sandbox.as_langchain_tool()] # LLM viết JS/shell gọi các công cụ của bạn
)
Để kiểm soát khả năng chi tiết:
from amla_sandbox import SandboxTool, MethodCapability, ConstraintSet, Param
caps = [
MethodCapability(
method_pattern="mcp:search_db",
constraints=ConstraintSet([Param("query").starts_with("SELECT")]),
max_calls=5,
)
]
sandbox_tool = SandboxTool.from_functions([search_db], capabilities=caps)
agent = create_react_agent(model, [sandbox_tool.as_langchain_tool()])
Kiến trúc
┌────────────────────────────────────────────────┐
│ WASM Sandbox │
│ ┌──────────────────────────────────────────┐ │
│ │ Async Scheduler │ │
│ │ tasks waiting/running/ready │ │
│ └──────────────────────────────────────────┘ │
│ ┌────────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ VFS │ │ Shell │ │ Capabilities │ │
│ │ /workspace │ │ builtins │ │ validation │ │
│ └────────────┘ └──────────┘ └──────────────┘ │
│ ↓ yield │
└════════════════════════════════════════════════┘
│
▼
┌─────────────────────────────────────────────┐
│ Python Host │
│ │
│ while sandbox.has_work(): │
│ req = sandbox.step() # tool call │
│ sandbox.resume(execute(req)) │
│ │
└─────────────────────────────────────────────┘
Sandbox nhả lệnh trên các call công cụ, host thực thi chúng (sau kiểm tra khả năng) và tiếp tục. QuickJS chạy trong WASM cho runtime JS.
Tiền biên dịch
Lần chạy đầu biên dịch module WASM (khoảng 300ms). Cache lại:
amla-precompile
Tải sau đó: khoảng 0.5ms.
DSL Ràng buộc
from amla_sandbox import Param, ConstraintSet
constraints = ConstraintSet([
Param("amount") >= 100,
Param("amount") <= 10000,
Param("currency").is_in(["USD", "EUR"]),
Param("path").starts_with("/api/"),
])
Khớp mẫu cho tên phương thức:
stripe/charges/create— khớp chính xácstripe/charges/*— một phần đường dẫnstripe/**— không có hoặc nhiều phần
Ưu nhược điểm
- Lợi ích: Cách ly mà không cần hạ tầng, thực thi khả năng, hiệu quả token.
- Hạn chế: Không có môi trường Linux đầy đủ, không hỗ trợ module native, không truy cập GPU, không bảo vệ vòng lặp vô tận (một
while(true){}sẽ treo).
Nếu cần một VM thực sự với trạng thái thường trực và phụ thuộc tuỳ ý, hãy dùng e2b hoặc Modal. amla-sandbox phù hợp cho các tác vụ với mã sinh tự động và quyền truy cập công cụ được kiểm soát.
Giấy phép
Mã Python là MIT. Mã nhị phân WASM hiện đang là độc quyền - bạn có thể sử dụng cùng với gói này, nhưng không thể trích xuất hoặc phân phối riêng. Chúng tôi đang làm việc để mở mã nguồn runtime WASM.