<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>沙箱 &#8211; 95博客</title>
	<atom:link href="https://95bok.cn/tag/%e6%b2%99%e7%ae%b1/feed/" rel="self" type="application/rss+xml" />
	<link>https://95bok.cn</link>
	<description>云烟</description>
	<lastBuildDate>Mon, 13 Apr 2026 07:50:20 +0000</lastBuildDate>
	<language>zh-Hans</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://95bok.cn/wp-content/uploads/2025/11/cropped-1740116058152-32x32.jpg</url>
	<title>沙箱 &#8211; 95博客</title>
	<link>https://95bok.cn</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>本地 AI 想真正&#8221;干活&#8221;？先给它装个安全沙箱和长期记忆——我试过的几个方案对比</title>
		<link>https://95bok.cn/%e6%9c%ac%e5%9c%b0-ai-%e6%83%b3%e7%9c%9f%e6%ad%a3%e5%b9%b2%e6%b4%bb%ef%bc%9f%e5%85%88%e7%bb%99%e5%ae%83%e8%a3%85%e4%b8%aa%e5%ae%89%e5%85%a8%e6%b2%99%e7%ae%b1%e5%92%8c%e9%95%bf%e6%9c%9f%e8%ae%b0/</link>
					<comments>https://95bok.cn/%e6%9c%ac%e5%9c%b0-ai-%e6%83%b3%e7%9c%9f%e6%ad%a3%e5%b9%b2%e6%b4%bb%ef%bc%9f%e5%85%88%e7%bb%99%e5%ae%83%e8%a3%85%e4%b8%aa%e5%ae%89%e5%85%a8%e6%b2%99%e7%ae%b1%e5%92%8c%e9%95%bf%e6%9c%9f%e8%ae%b0/#respond</comments>
		
		<dc:creator><![CDATA[]]></dc:creator>
		<pubDate>Mon, 13 Apr 2026 07:50:20 +0000</pubDate>
				<category><![CDATA[Docker容器]]></category>
		<category><![CDATA[本地AI]]></category>
		<category><![CDATA[n8n]]></category>
		<category><![CDATA[Ollama]]></category>
		<category><![CDATA[向量数据库]]></category>
		<category><![CDATA[安全]]></category>
		<category><![CDATA[沙箱]]></category>
		<category><![CDATA[长期记忆]]></category>
		<guid isPermaLink="false">https://95bok.cn/%e6%9c%ac%e5%9c%b0-ai-%e6%83%b3%e7%9c%9f%e6%ad%a3%e5%b9%b2%e6%b4%bb%ef%bc%9f%e5%85%88%e7%bb%99%e5%ae%83%e8%a3%85%e4%b8%aa%e5%ae%89%e5%85%a8%e6%b2%99%e7%ae%b1%e5%92%8c%e9%95%bf%e6%9c%9f%e8%ae%b0/</guid>

					<description><![CDATA[# 本地 AI 想真正&#8221;干活&#8221;？先给它装个安全沙箱和长期记忆——我试过的几个方案对比  ... <a title="本地 AI 想真正&#8221;干活&#8221;？先给它装个安全沙箱和长期记忆——我试过的几个方案对比" class="read-more" href="https://95bok.cn/%e6%9c%ac%e5%9c%b0-ai-%e6%83%b3%e7%9c%9f%e6%ad%a3%e5%b9%b2%e6%b4%bb%ef%bc%9f%e5%85%88%e7%bb%99%e5%ae%83%e8%a3%85%e4%b8%aa%e5%ae%89%e5%85%a8%e6%b2%99%e7%ae%b1%e5%92%8c%e9%95%bf%e6%9c%9f%e8%ae%b0/" aria-label="阅读 本地 AI 想真正&#8221;干活&#8221;？先给它装个安全沙箱和长期记忆——我试过的几个方案对比">阅读更多</a>]]></description>
										<content:encoded><![CDATA[<p># 本地 AI 想真正&#8221;干活&#8221;？先给它装个安全沙箱和长期记忆——我试过的几个方案对比</p>
<p>之前折腾了几周 n8n + Ollama，能跑工作流了。但越用越发现两个问题：</p>
<p>1. <strong>AI 记不住事</strong>——今天让它总结了一篇文章，明天问它上周干了什么，它一脸懵<br />
2. <strong>不敢让它执行命令</strong>——万一它把 <code>rm -rf /</code> 当真了怎么办？</p>
<p>这就是沙箱和长期记忆的意义。这周我把几个主流方案都试了一遍，有些好用，有些纯属折腾。</p>
<hr />
<h2>先说长期记忆</h2>
<h3>方案一：n8n 内置的 Vector Store</h3>
<p>n8n 支持多种向量数据库：Chroma、Pinecone、Qdrant、Weaviate 等。</p>
<p>我试的是 <strong>Chroma</strong>，因为它能本地跑，不依赖外部服务。</p>
<pre><code class="language-bash">mkdir -p /opt/chroma &amp;&amp; cd /opt/chroma</p><p>cat &gt; docker-compose.yml &lt;&lt; &#039;EOF&#039;
version: &quot;3.8&quot;
services:
  chroma:
    image: chromadb/chroma:latest
    container_name: chroma
    restart: unless-stopped
    ports:
      - &quot;8000:8000&quot;
    volumes:
      - chroma-data:/chroma/chroma
volumes:
  chroma-data:
EOF</p><p>docker compose up -d
</code></pre>
<p>n8n 里添加 Chroma 节点，填 <code>http://172.17.0.1:8000</code>。</p>
<p><strong>效果：</strong> 能用。把之前总结过的文章丢进去，AI 能检索到相关内容。但有两个问题：</p>
<ul>
<li>每次工作流执行都要手动往 Chroma 里塞数据，得额外加一个节点</li>
<li>向量检索对中文的支持一般，关键词偏差大了就找不到</li>
<p><strong>适合场景：</strong> 简单的知识检索，比如&#8221;之前总结过的某篇文章&#8221;。</p>
<h3>方案二：Ollama 自带的上下文窗口</h3>
<p>最简单的&#8221;记忆&#8221;就是在工作流里把历史对话带上。</p>
<pre><code class="language-json">// n8n 的 Ollama 节点里手动加 messages 数组
[
  {"role": "system", "content": "你是一个助手，以下是你之前处理过的任务..."},
  {"role": "user", "content": "上次总结的那篇关于XX的文章，能再说一下要点吗？"}
]
</code></pre>
<p><strong>效果：</strong> 能跑，但有硬伤——上下文窗口有限（qwen2.5:7b 默认 8192 token），塞太多历史就爆了。</p>
<p><strong>适合场景：</strong> 短期记忆，比如&#8221;今天的工作流执行历史&#8221;。</p>
<h3>方案三：SQLite 手动存</h3>
<p>最土但最稳的方案。用 n8n 的 SQLite 节点，把每次执行结果存起来。</p>
<pre><code class="language-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
);
</code></pre>
<p>n8n 工作流结尾加一个 SQLite 插入节点。下次执行时先用 SQLite 查询，把结果拼到 prompt 里。</p>
<p><strong>效果：</strong> 意外地好用。虽然不如向量检索&#8221;智能&#8221;，但对于&#8221;上周处理了什么任务&#8221;这种需求，SQL 查询比向量匹配更靠谱。</p>
<p><strong>适合场景：</strong> 任务历史记录、执行日志、可检索的长期记忆。</p>
<hr />
<h2>再说沙箱</h2>
<h3>方案一：Docker 隔离</h3>
<p>让 AI 执行的命令跑在 Docker 容器里，跟宿主机隔离。</p>
<pre><code class="language-bash">cat &gt; /opt/sandbox/run.sh &lt;&lt; &#039;SCRIPT&#039;
#!/bin/bash
# AI 沙箱执行脚本</p><p>COMMAND="$1"
TIMEOUT=30</p><p># 黑名单检查
BLACKLIST="rm -rf|mkfs|dd|chmod 777|sudo|curl.*|"
if echo "$COMMAND" | grep -qiE "$BLACKLIST"; then
  echo "&#x274c; 禁止执行此命令（包含危险操作）"
  exit 1
fi</p><p># 在轻量容器里执行
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</p><p>chmod +x /opt/sandbox/run.sh
</code></pre>
<p>n8n 里执行 <code>/opt/sandbox/run.sh "python3 -c 'print(1+1)'"</code>。</p>
<p><strong>效果：</strong> 安全，但限制太多。<code>--network=none</code> 导致 AI 没法联网查东西，<code>--read-only</code> 导致没法写文件。调得太松又不安全。</p>
<p><strong>适合场景：</strong> 纯计算类任务，不涉及网络和文件写入。</p>
<h3>方案二：n8n 自带的权限控制</h3>
<p>n8n 工作流里每个&#8221;执行命令&#8221;的节点可以单独开关，还能设置允许的命令列表。</p>
<p>在 n8n 设置里：</p>
<li><code>EXECUTIONS_PROCESS=own</code> — 每个工作流独立进程</li>
<li><code>N8N_SECURE_COOKIE=true</code> — 安全 cookie</li>
</ul>
<p><strong>效果：</strong> 聊胜于无。主要是防误操作，不是防恶意行为。</p>
<h3>方案三：独立的 AI 沙箱服务</h3>
<p>我最后用的是这个思路。单独起一个服务，专门处理 AI 发来的执行请求：</p>
<pre><code class="language-python">#!/usr/bin/env python3
"""ai_sandbox.py - 简易 AI 执行沙箱"""</p><p>import subprocess
import json
import sys</p><p>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"]},
}</p><p>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) &gt; 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)}</p><p>if __name__ == "__main__":
    command = sys.stdin.read().strip()
    result = run_command(command)
    print(json.dumps(result, ensure_ascii=False))
</code></pre>
<p>这个脚本跑在一个独立的 Docker 容器里：</p>
<pre><code class="language-bash">cat &gt; /opt/sandbox/docker-compose.yml &lt;&lt; &#039;EOF&#039;
version: &quot;3.8&quot;
services:
  sandbox:
    build: .
    container_name: ai-sandbox
    restart: unless-stopped
    ports:
      - &quot;8001:8001&quot;
    read_only: true
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=32m
    security_opt:
      - no-new-privileges:true
    deploy:
      resources:
        limits:
          memory: 128M
          cpus: &quot;0.25&quot;
EOF
</code></pre>
<p>n8n 里通过 HTTP Request 节点调用 <code>http://127.0.0.1:8001</code>，把命令传进去。</p>
<p><strong>效果：</strong> 比较满意。白名单机制比黑名单安全得多（黑名单总有漏网之鱼），资源限制也很明确。</p>
<hr />
<h2>对比总结</h2>
<p>| 方案 | 记忆类型 | 实现难度 | 实用性 | 推荐场景 |</p>
<table>
<thead>
<tr>
<th>
<hr />
<hr />
</th>
<th>
<hr />
<hr />
<hr />
</th>
<th>
<hr />
<hr />
<hr />
</th>
<th>
<hr />
<hr />
<p>&#8212;</th>
<th>
<hr />
<hr />
<hr />
</th>
</tr>
</thead>
<tbody>
<tr>
<td>Chroma 向量库</td>
<td>语义检索</td>
<td>中</td>
<td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td>知识检索</td>
</tr>
<tr>
<td>上下文窗口</td>
<td>短期记忆</td>
<td>低</td>
<td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td>单轮对话</td>
</tr>
<tr>
<td>SQLite</td>
<td>任务历史</td>
<td>低</td>
<td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td>执行记录</td>
</tr>
<tr>
<td>Docker 沙箱</td>
<td>执行隔离</td>
<td>中</td>
<td><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /></td>
<td>纯计算</td>
</tr>
</tbody>
</table>
<p>| 白名单沙箱服务 | 受控执行 | 高 | <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/2b50.png" alt="⭐" class="wp-smiley" style="height: 1em; max-height: 1em;" /> | 日常任务 |</p>
<p>我的最终选择：<strong>SQLite 存记忆 + 白名单沙箱执行</strong>。</p>
<p>不追求花哨的向量检索，够用就行。毕竟大多数时候我只需要知道&#8221;上次这个任务跑了没&#8221;、&#8221;结果是什么&#8221;，不需要语义级别的模糊匹配。</p>
<hr />
<h2>给想折腾的人几句忠告</h2>
<p>1. <strong>别一上来就搞复杂的记忆系统</strong>——先跑通工作流，发现真的需要记忆了再加<br />
2. <strong>沙箱一定要有白名单，别用黑名单</strong>——黑名单是永远补不完的漏洞<br />
3. <strong>资源限制必须设</strong>——内存、CPU、超时时间，一个都不能少<br />
4. <strong>别把沙箱和宿主机的敏感目录映射进去</strong>——哪怕你觉得&#8221;就映射一个目录没事&#8221;</p>
<p>下一篇我会写一个完整的工作流实战：从网页抓取到分类存档到推送提醒，全程本地零云端。把上面说的记忆和沙箱都用上。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://95bok.cn/%e6%9c%ac%e5%9c%b0-ai-%e6%83%b3%e7%9c%9f%e6%ad%a3%e5%b9%b2%e6%b4%bb%ef%bc%9f%e5%85%88%e7%bb%99%e5%ae%83%e8%a3%85%e4%b8%aa%e5%ae%89%e5%85%a8%e6%b2%99%e7%ae%b1%e5%92%8c%e9%95%bf%e6%9c%9f%e8%ae%b0/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
