本地 AI 想真正”干活”?先给它装个安全沙箱和长期记忆——我试过的几个方案对比

# 本地 AI 想真正”干活”?先给它装个安全沙箱和长期记忆——我试过的几个方案对比

之前折腾了几周 n8n + Ollama,能跑工作流了。但越用越发现两个问题:

1. AI 记不住事——今天让它总结了一篇文章,明天问它上周干了什么,它一脸懵
2. 不敢让它执行命令——万一它把 rm -rf / 当真了怎么办?

这就是沙箱和长期记忆的意义。这周我把几个主流方案都试了一遍,有些好用,有些纯属折腾。


先说长期记忆

方案一:n8n 内置的 Vector Store

n8n 支持多种向量数据库:Chroma、Pinecone、Qdrant、Weaviate 等。

我试的是 Chroma,因为它能本地跑,不依赖外部服务。

BASH
mkdir -p /opt/chroma && cd /opt/chroma

cat > docker-compose.yml << 'EOF' version: "3.8" services: chroma: image: chromadb/chroma:latest container_name: chroma restart: unless-stopped ports: - "8000:8000" volumes: - chroma-data:/chroma/chroma volumes: chroma-data: EOF

docker compose up -d

n8n 里添加 Chroma 节点,填 http://172.17.0.1:8000

效果: 能用。把之前总结过的文章丢进去,AI 能检索到相关内容。但有两个问题:

  • 每次工作流执行都要手动往 Chroma 里塞数据,得额外加一个节点
  • 向量检索对中文的支持一般,关键词偏差大了就找不到
  • 适合场景: 简单的知识检索,比如”之前总结过的某篇文章”。

    方案二:Ollama 自带的上下文窗口

    最简单的”记忆”就是在工作流里把历史对话带上。

    JSON
    // n8n 的 Ollama 节点里手动加 messages 数组
    [
      {"role": "system", "content": "你是一个助手,以下是你之前处理过的任务..."},
      {"role": "user", "content": "上次总结的那篇关于XX的文章,能再说一下要点吗?"}
    ]
    

    效果: 能跑,但有硬伤——上下文窗口有限(qwen2.5:7b 默认 8192 token),塞太多历史就爆了。

    适合场景: 短期记忆,比如”今天的工作流执行历史”。

    方案三:SQLite 手动存

    最土但最稳的方案。用 n8n 的 SQLite 节点,把每次执行结果存起来。

    SQL
    CREATE TABLE IF NOT EXISTS task_history (
        id INTEGER PRIMARY KEY,
        task_name TEXT,
        input TEXT,
        output TEXT,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP
    );
    

    n8n 工作流结尾加一个 SQLite 插入节点。下次执行时先用 SQLite 查询,把结果拼到 prompt 里。

    效果: 意外地好用。虽然不如向量检索”智能”,但对于”上周处理了什么任务”这种需求,SQL 查询比向量匹配更靠谱。

    适合场景: 任务历史记录、执行日志、可检索的长期记忆。


    再说沙箱

    方案一:Docker 隔离

    让 AI 执行的命令跑在 Docker 容器里,跟宿主机隔离。

    BASH
    cat > /opt/sandbox/run.sh << 'SCRIPT'
    #!/bin/bash
    # AI 沙箱执行脚本

    COMMAND="$1" TIMEOUT=30

    # 黑名单检查 BLACKLIST="rm -rf|mkfs|dd|chmod 777|sudo|curl.*|" if echo "$COMMAND" | grep -qiE "$BLACKLIST"; then echo "❌ 禁止执行此命令(包含危险操作)" exit 1 fi

    # 在轻量容器里执行 docker run --rm --memory=256m --cpus=0.5 --network=none --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m alpine:3.18 sh -c "$COMMAND" SCRIPT

    chmod +x /opt/sandbox/run.sh

    n8n 里执行 /opt/sandbox/run.sh "python3 -c 'print(1+1)'"

    效果: 安全,但限制太多。--network=none 导致 AI 没法联网查东西,--read-only 导致没法写文件。调得太松又不安全。

    适合场景: 纯计算类任务,不涉及网络和文件写入。

    方案二:n8n 自带的权限控制

    n8n 工作流里每个”执行命令”的节点可以单独开关,还能设置允许的命令列表。

    在 n8n 设置里:

  • EXECUTIONS_PROCESS=own — 每个工作流独立进程
  • N8N_SECURE_COOKIE=true — 安全 cookie

效果: 聊胜于无。主要是防误操作,不是防恶意行为。

方案三:独立的 AI 沙箱服务

我最后用的是这个思路。单独起一个服务,专门处理 AI 发来的执行请求:

PYTHON
#!/usr/bin/env python3
"""ai_sandbox.py - 简易 AI 执行沙箱"""

import subprocess import json import sys

ALLOWED_COMMANDS = { "ping": {"timeout": 10, "args_max": 2}, "curl": {"timeout": 30, "args_max": 5, "ban_args": ["-o", "--output"]}, "python3": {"timeout": 15, "args_max": 1, "allowed_args": ["-c"]}, "cat": {"timeout": 5, "args_max": 2, "allowed_paths": ["/tmp", "/opt/n8n/data"]}, "ls": {"timeout": 5, "args_max": 2, "allowed_paths": ["/tmp", "/opt/n8n/data"]}, }

def run_command(command_str): parts = command_str.strip().split() if not parts: return {"error": "空命令"} cmd = parts[0] if cmd not in ALLOWED_COMMANDS: return {"error": f"命令 '{cmd}' 不在白名单中"} config = ALLOWED_COMMANDS[cmd] # 参数数量检查 if len(parts) > config.get("args_max", 1) + 1: return {"error": "参数过多"} try: result = subprocess.run( parts, capture_output=True, text=True, timeout=config["timeout"] ) return { "stdout": result.stdout[:2000], # 限制输出长度 "stderr": result.stderr[:500], "returncode": result.returncode } except subprocess.TimeoutExpired: return {"error": "超时"} except Exception as e: return {"error": str(e)}

if __name__ == "__main__": command = sys.stdin.read().strip() result = run_command(command) print(json.dumps(result, ensure_ascii=False))

这个脚本跑在一个独立的 Docker 容器里:

BASH
cat > /opt/sandbox/docker-compose.yml << 'EOF'
version: "3.8"
services:
  sandbox:
    build: .
    container_name: ai-sandbox
    restart: unless-stopped
    ports:
      - "8001:8001"
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=32m
    security_opt:
      - no-new-privileges:true
    deploy:
      resources:
        limits:
          memory: 128M
          cpus: "0.25"
EOF

n8n 里通过 HTTP Request 节点调用 http://127.0.0.1:8001,把命令传进去。

效果: 比较满意。白名单机制比黑名单安全得多(黑名单总有漏网之鱼),资源限制也很明确。


对比总结

| 方案 | 记忆类型 | 实现难度 | 实用性 | 推荐场景 |














Chroma 向量库 语义检索 ⭐⭐⭐ 知识检索
上下文窗口 短期记忆 ⭐⭐ 单轮对话
SQLite 任务历史 ⭐⭐⭐⭐ 执行记录
Docker 沙箱 执行隔离 ⭐⭐⭐ 纯计算

| 白名单沙箱服务 | 受控执行 | 高 | ⭐⭐⭐⭐ | 日常任务 |

我的最终选择:SQLite 存记忆 + 白名单沙箱执行

不追求花哨的向量检索,够用就行。毕竟大多数时候我只需要知道”上次这个任务跑了没”、”结果是什么”,不需要语义级别的模糊匹配。


给想折腾的人几句忠告

1. 别一上来就搞复杂的记忆系统——先跑通工作流,发现真的需要记忆了再加
2. 沙箱一定要有白名单,别用黑名单——黑名单是永远补不完的漏洞
3. 资源限制必须设——内存、CPU、超时时间,一个都不能少
4. 别把沙箱和宿主机的敏感目录映射进去——哪怕你觉得”就映射一个目录没事”

下一篇我会写一个完整的工作流实战:从网页抓取到分类存档到推送提醒,全程本地零云端。把上面说的记忆和沙箱都用上。

发表评论