mirror of
https://github.com/ThearchyHelios/AgentLab.git
synced 2026-11-15 04:01:58 +00:00
dd57c12016
跑一段不可信代码不需要一整套容器运行时。macOS 用 Seatbelt (sandbox-exec,系统自带),Linux 用 bubblewrap;冷启动从 ~250ms 降到 ~25ms,且不再需要装任何东西。 守住三件事:默认断网、家目录不可读、只有工作区可写。策略由内核 强制且对子进程继承——沙箱内 subprocess 出来的 cat 读家目录同样是 Operation not permitted。解释器改用 sys.base_prefix 下那个干净的 Python,沙箱代码因此 import 不到后端的 FastAPI、SQLAlchemy 和凭据 处理代码。 如实标注能力边界,不给虚假的安全感: - macOS 不强制 RLIMIT_AS,内存限不住(实测设 256MB 仍可分配 900MB), health 接口申报 enforced 字段,界面把限不住的项划掉 - Seatbelt 是访问控制而非虚拟化:进程表共享,以当前用户身份运行 - bubblewrap 侧多隔离 PID/IPC/挂载视图,内存限额真实生效 顺带修两个 bug: - LocalSandbox 设 RLIMIT_NPROC=64 会误伤——macOS 上它按 uid 全局 计数,用户已有上千进程时沙箱内任何 fork 都失败,连 bash 都起不来 - 模型所属 provider 被停用时,原先静默换一家继续用原模型名,必然 404 且报错指不到原因;改为直接说清是哪个 provider 被停用了 写 SBPL 的坑记在注释里:(deny default) 会让 Python 直接 SIGABRT, 改用「默认允许 + 精确拒绝」。 7 个模板全部回归通过。
88 lines
2.4 KiB
Python
88 lines
2.4 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import lru_cache
|
|
from pathlib import Path
|
|
|
|
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
|
|
BACKEND_DIR = Path(__file__).resolve().parents[2]
|
|
PROJECT_DIR = BACKEND_DIR.parent
|
|
|
|
|
|
class Settings(BaseSettings):
|
|
"""全局配置。所有项都可以用 AGENTLAB_ 前缀的环境变量覆盖。"""
|
|
|
|
model_config = SettingsConfigDict(
|
|
env_prefix="AGENTLAB_",
|
|
env_file=(PROJECT_DIR / ".env", BACKEND_DIR / ".env"),
|
|
extra="ignore",
|
|
)
|
|
|
|
# --- 服务 ---
|
|
host: str = "127.0.0.1"
|
|
port: int = 8000
|
|
cors_origins: list[str] = ["http://localhost:5273", "http://127.0.0.1:5273"]
|
|
|
|
# --- 存储 ---
|
|
data_dir: Path = PROJECT_DIR / "data"
|
|
|
|
# --- 密钥加密 ---
|
|
# 用于加密落库的 provider api key。留空则自动生成并存到 data_dir/.secret_key。
|
|
secret_key: str = ""
|
|
|
|
# --- 沙箱 ---
|
|
sandbox_backend: str = "auto" # auto | seatbelt | bubblewrap | local | off
|
|
sandbox_timeout: int = 30
|
|
sandbox_memory_mb: int = 512
|
|
sandbox_cpus: float = 1.0
|
|
sandbox_network: bool = False
|
|
sandbox_max_output: int = 20_000
|
|
|
|
# --- 执行引擎护栏 ---
|
|
max_run_seconds: int = 600
|
|
max_graph_steps: int = 200
|
|
max_agent_steps: int = 25
|
|
max_concurrent_runs: int = 20
|
|
|
|
# --- 工具 ---
|
|
http_tool_timeout: int = 20
|
|
http_tool_max_bytes: int = 2_000_000
|
|
# 逗号分隔的域名白名单;为空表示不限制(仅拦截内网地址)
|
|
http_tool_allowlist: list[str] = []
|
|
|
|
@property
|
|
def db_path(self) -> Path:
|
|
return self.data_dir / "agentlab.db"
|
|
|
|
@property
|
|
def db_url(self) -> str:
|
|
return f"sqlite+aiosqlite:///{self.db_path}"
|
|
|
|
@property
|
|
def checkpoint_path(self) -> Path:
|
|
"""LangGraph checkpointer 用独立的库文件,避免和业务表抢写锁。"""
|
|
return self.data_dir / "checkpoints.db"
|
|
|
|
@property
|
|
def workspace_dir(self) -> Path:
|
|
"""沙箱与文件工具的可写根目录,一切文件访问都被限制在这里面。"""
|
|
return self.data_dir / "workspace"
|
|
|
|
@property
|
|
def uploads_dir(self) -> Path:
|
|
return self.data_dir / "uploads"
|
|
|
|
def ensure_dirs(self) -> None:
|
|
for d in (self.data_dir, self.workspace_dir, self.uploads_dir):
|
|
d.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
@lru_cache
|
|
def get_settings() -> Settings:
|
|
s = Settings()
|
|
s.ensure_dirs()
|
|
return s
|
|
|
|
|
|
settings = get_settings()
|