# 本地 AI 想真正”干活”?先给它装个安全沙箱和长期记忆——我试过的几个方案对比
之前折腾了几周 n8n + Ollama,能跑工作流了。但越用越发现两个问题:
1. AI 记不住事——今天让它总结了一篇文章,明天问它上周干了什么,它一脸懵
2. 不敢让它执行命令——万一它把 rm -rf / 当真了怎么办?
这就是沙箱和长期记忆的意义。这周我把几个主流方案都试了一遍,有些好用,有些纯属折腾。
先说长期记忆
方案一:n8n 内置的 Vector Store
n8n 支持多种向量数据库:Chroma、Pinecone、Qdrant、Weaviate 等。
我试的是 Chroma,因为它能本地跑,不依赖外部服务。
mkdir -p /opt/chroma && cd /opt/chromacat > 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 里塞数据,得额外加一个节点
- 向量检索对中文的支持一般,关键词偏差大了就找不到
EXECUTIONS_PROCESS=own— 每个工作流独立进程N8N_SECURE_COOKIE=true— 安全 cookie
适合场景: 简单的知识检索,比如”之前总结过的某篇文章”。
方案二:Ollama 自带的上下文窗口
最简单的”记忆”就是在工作流里把历史对话带上。
// n8n 的 Ollama 节点里手动加 messages 数组
[
{"role": "system", "content": "你是一个助手,以下是你之前处理过的任务..."},
{"role": "user", "content": "上次总结的那篇关于XX的文章,能再说一下要点吗?"}
]
效果: 能跑,但有硬伤——上下文窗口有限(qwen2.5:7b 默认 8192 token),塞太多历史就爆了。
适合场景: 短期记忆,比如”今天的工作流执行历史”。
方案三:SQLite 手动存
最土但最稳的方案。用 n8n 的 SQLite 节点,把每次执行结果存起来。
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 容器里,跟宿主机隔离。
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 设置里:
效果: 聊胜于无。主要是防误操作,不是防恶意行为。
方案三:独立的 AI 沙箱服务
我最后用的是这个思路。单独起一个服务,专门处理 AI 发来的执行请求:
#!/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 容器里:
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. 别把沙箱和宿主机的敏感目录映射进去——哪怕你觉得”就映射一个目录没事”
下一篇我会写一个完整的工作流实战:从网页抓取到分类存档到推送提醒,全程本地零云端。把上面说的记忆和沙箱都用上。