商业产业 Hacker News Agent 2026-10-07T17:36 正文 ok 1059 words

🔶 Zed Editor、Docker Agent、ACP 都放进沙箱:使用 sbx 运行 Agent

英文标题:🔶 Zed Editor, Docker Agent, ACP, but in a sandbox: running the agent with sbx

本文介绍如何将通过 ACP 协议接入 Zed 的 Docker Agent 运行在 Docker sbx 沙箱中,同时继续使用宿主机上的本地 llmman 模型服务。核心改动只有两处:将模型端点从 `127.0.0.1` 改为 `host.docker.internal`,并让 Zed 通过 `sbx exec -i` 在沙箱内启动 `docker-agent serve acp`。sbx 基于 microVM 隔离 Agent,使其只能访问共享工作区,并通过网络策略和代理保护 API 密钥等机密信息。这种方式保留了本地运行和 GPU 推理能力,同时显著缩小了 Agent 执行 shell 命令、访问文件和联网时的安全风险范围。
打开原文相关 3 · 机会 1
media202.1 KB media495.4 KB media10.1 KB media443.0 KB media11.2 KB media27.7 KB media369.5 KB

中文

在上一篇文章中,我演示了如何借助 ACP 协议,将 Zed 接入 Docker Agent,并使用 llmman 在本地提供模型服务。

它运行得很好,但有一个“小”细节:Agent 是直接在我的机器上运行的,而且它可以使用 shell……😬

所以今天,我们来做同样的事情,不过改为在 Docker 沙箱(sbx) 中运行 Docker Agent,再将 Zed 连接到沙箱里的 Agent。你会发现,几乎不需要做任何改动。

下面是本文所使用配置的概览:

ACP、Zed、sbx、Docker Agent 和 llmman 架构图ACP、Zed、sbx、Docker Agent 和 llmman 架构图

sbx(Docker Sandboxes)是 Docker 用于在隔离沙箱中运行代码 Agent(Claude Code、Codex、Gemini、Docker Agent,或你自己手写的 Agent……)的工具,沙箱基于 microVM:

  • Agent 只能看到你与它共享的工作目录(workspace),该目录会以与宿主机相同的路径挂载;
  • 它无法访问文件系统中的其他内容(你的 SSH 密钥、其他项目等);
  • 出站网络流量会经过带有网络策略的代理:默认情况下,Agent 不能随意访问任何目标;
  • 同一个代理还能保护你的机密信息(API 密钥、GitHub Token 等):你可以在宿主机上使用 sbx secret set 声明它们,由代理将其注入到出站请求中。密钥永远不会出现在沙箱内部,因此 Agent 既无法读取它们,也无法泄露它们;
  • 它甚至拥有自己的 Docker daemon,因此可以运行容器而不会影响你机器上的容器。

代码 Agent 是一种会自行决定要执行哪些命令的程序。在我的配置中,Agent 可以使用 shell 工具集,并由一个“小型”本地模型驱动。即使 Zed 会在每条命令执行前要求我确认,只要一时疏忽(或者项目文件中被注入了一条提示),一次误放的 rm -rf 或一次发往未知服务器的 curl 就可能被执行。

而且在你的机器上,Agent 还能访问环境变量和配置文件:只需简单执行 env 或 cat ~/.config/...,你的 API 密钥就可能进入模型上下文(更糟的是,还可能被发送到其他地方)。

使用沙箱后,“爆炸半径”会被限制在工作区以及网络策略允许的范围内,而你的机密信息始终无法被 Agent 接触。这样,你既保留了 Agent 的全部能力,又不必把整间房子的钥匙交给它。

这一部分没有任何变化:llmman 依然运行在我的机器上(因此依旧使用我的 GPU):

关于安装和下载模型,请参阅我的专门文章以及上一篇文章。

我创建了配置文件的一个副本:sbx.llmman.docker-agent.yaml。唯一的变化是 base_url 这一行:

yaml providers: llmman: api_type: openai_chatcompletions #base_url: http://127.0.0.1:17434/v1 base_url: http://host.docker.internal:17434/v1

agents:

root: model: mellum description: A local code agent, powered by a small LLM. instruction: | Your name is Riker. You are a developer expert. You act as a mentor and coding partner.

  # Tool discipline

  Built-in tools:
  - shell: use the `shell` tool to explore the project, read files, or run commands.
    Chain commands as long as it is useful, then give a clear final answer.

  # Style

  Be concise. Prefer a small, correct, compilable example over prose.

toolsets:
  - type: shell

models: mellum: provider: llmman model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M

为什么?因为沙箱有自己的 localhost:在沙箱内部,127.0.0.1 指向沙箱自身,而不是我的机器。要访问运行在宿主机上的 llmman,需要通过 host.docker.internal。

在项目目录中,只需执行一条命令:

bash sbx create docker-agent . --name docker-agent-acp

  • docker-agent:要在沙箱中使用的 Agent;
  • .:当前目录,它将成为与沙箱共享的工作区;
  • --name docker-agent-acp:沙箱名称,后续配置 Zed 时会用到它。

💡 如果 Agent 无法连接 llmman(请求被代理拦截),需要在宿主机上通过网络策略放行该端口:sbx policy allow network localhost:17434。使用 sbx policy log 命令可以查看哪些请求被阻止,以及阻止原因。

在 Zed 的设置文件(settings.json)中,我们要在 agent_servers 部分加入一个新的 Agent。这一次,不再由 Zed 直接启动 docker-agent,而是启动 sbx exec,由它在沙箱内部执行 docker-agent serve acp:

"agent_servers": { "🤖 docker-agent (sbx llmman)": { "type": "custom", "command": "sbx", "args": [ "exec", "-i", "docker-agent-acp", "docker-agent", "serve", "acp", "/Users/k33g/kDrive/Rickub/demo-docker-agent-acp/sbx.llmman.docker-agent.yaml" ] } }

几点说明:

  • -i 至关重要:ACP 通过 stdin/stdout 通信,因此 Zed 与沙箱之间的标准输入必须保持打开;
  • docker-agent-acp 是我们刚刚创建的沙箱名称;
  • YAML 文件的路径与宿主机上的路径相同,因为工作区会以相同路径挂载到沙箱中。

显然,请根据你自己的配置文件调整沙箱名称和路径。

打开 Zed 的 Agent 面板,从可用 Agent 列表中选择 “🤖 docker-agent (sbx llmman)”,体验与上一篇文章完全相同:Agent 会探索项目、读取文件、运行 shell 命令……不同的是,这一切都发生在沙箱内部。Agent 在工作区中修改的文件当然会直接显示在 Zed 中,因为它们使用的是同一个目录。

ZedZed

  • Zed 使用 sbx exec -i 在沙箱中启动 Agent,并通过 ACP(基于 stdio 的 JSON-RPC)与其通信。
  • Docker Agent 在 sbx 沙箱中以隔离方式运行,只能访问项目工作区。
  • Docker Agent 通过 host.docker.internal 与宿主机上由 llmman 提供的 OpenAI API“通信”,请求会经过沙箱代理。
  • 该代理会过滤网络流量,并在需要时注入机密信息(API 密钥等),而 Agent 永远无法看到这些机密。
  • 从配置角度看:只需修改 llmman 端点的一行 YAML 配置,并在 Zed 设置中添加一个条目。仅此而已。

就是这样,几乎不需要额外操作,我们保留了上一篇文章中的同一套“100% 本地”配置,但 Agent 再也无法在你的机器上为所欲为了。欢迎提问 🙂

English / 原文

In the previous article, I showed you how to plug Zed into Docker Agent thanks to the ACP protocol, with llmman serving the model locally.

It works very well, but there's one "small" detail: the agent runs directly on my machine. And it's allowed to use the shell... 😬

So today, we're going to do the same thing, but by running Docker Agent inside a Docker sandbox (sbx) , and connecting Zed to the agent in that sandbox. And you'll see, there's almost nothing to change.

Here's a summary of my setup for this article:

diagram ACP Zed sbx Docker Agent llmmandiagram ACP Zed sbx Docker Agent llmman

sbx (Docker Sandboxes) is Docker's tool for running code agents (Claude Code, Codex, Gemini, Docker Agent, your own handmade agent...) in isolated sandboxes , based on microVMs:

  • the agent only sees the working directory (the workspace) you share with it, mounted at the same path as on your machine,
  • it has no access to the rest of your file system (your SSH keys, your other projects, ...),
  • outbound network traffic goes through a proxy with a network policy: by default, the agent can't reach just anything,
  • this same proxy protects your secrets (API keys, GitHub tokens, ...): you declare them on the host with sbx secret set, and the proxy injects them into outgoing requests. The keys are never present inside the sandbox, so the agent can neither read them nor leak them,
  • it even has its own Docker daemon, so it can run containers without touching the ones on your machine.

A code agent is a program that decides on its own which commands it's going to run. In my setup, the agent has access to the shell toolset, and it's driven by a "small" local model. Even though Zed asks me for confirmation before each command, all it takes is a moment of inattention (or a prompt injected into a project file) for a misplaced rm -rf or a curl to an unknown server to get executed.

And on your machine, the agent also has access to your environment variables and your config files: a simple env or cat ~/.config/... and your API keys end up in the model's context (or worse, sent somewhere else).

With a sandbox, the "blast radius" is limited to the workspace and to what the network policy allows, and your secrets stay out of the agent's reach. So you keep all the power of the agent, without handing it the keys to the whole house.

Nothing changes on that side: llmman keeps running on my machine (and therefore keeps using my GPU):

For installing and downloading the model, see my dedicated article and the previous article.

I created a copy of my configuration file: sbx.llmman.docker-agent.yaml. The only thing that changes is the base_url line:

yaml

providers:
  llmman:
    api_type: openai_chatcompletions
    #base_url: http://127.0.0.1:17434/v1
    base_url: http://host.docker.internal:17434/v1
    
agents:

  root:
    model: mellum
    description: A local code agent, powered by a small LLM.
    instruction: |
      Your name is Riker. You are a developer expert.
      You act as a mentor and coding partner. 

      # Tool discipline

      Built-in tools:
      - shell: use the `shell` tool to explore the project, read files, or run commands.
        Chain commands as long as it is useful, then give a clear final answer.

      # Style

      Be concise. Prefer a small, correct, compilable example over prose.

    toolsets:
      - type: shell

models:
  mellum:
    provider: llmman
    model: huggingface.co/jetbrains/mellum2-12b-a2.5b-instruct-gguf-q4_k_m:Q4_K_M

Why? Because the sandbox has its ownlocalhost: inside it, 127.0.0.1 refers to the sandbox itself, not my machine. To reach llmman running on the host, we go through host.docker.internal.

From the project directory, a single command:

bash

sbx create docker-agent . --name docker-agent-acp
  • docker-agent: the agent to use in the sandbox,
  • .: the current directory, which becomes the workspace shared with the sandbox,
  • --name docker-agent-acp: the name of the sandbox, which we'll need for the Zed configuration.

💡 If the agent can't reach llmman (request blocked by the proxy), you need to allow the port in the network policy, on your machine: sbx policy allow network localhost:17434. The sbx policy log command lets you see what was blocked and why.

In Zed's settings (settings.json), we add a new agent to the agent_servers section. This time, it's no longer docker-agent that Zed launches directly, but sbx exec, which will run docker-agent serve acp inside the sandbox:

json

"agent_servers": {
  "🤖 docker-agent (sbx llmman)": {
    "type": "custom",
    "command": "sbx",
    "args": [
      "exec",
      "-i",
      "docker-agent-acp",
      "docker-agent",
      "serve",
      "acp",
      "/Users/k33g/kDrive/Rickub/demo-docker-agent-acp/sbx.llmman.docker-agent.yaml"
    ]
  }
}

A few remarks:

  • the -i is essential : ACP goes through stdin/stdout, so standard input must stay open between Zed and the sandbox,
  • docker-agent-acp is the name of the sandbox we just created,
  • the path to the YAML file is the same as on my machine, since the workspace is mounted at the same path in the sandbox.

Obviously, adapt the sandbox name and the path to your own configuration file.

Open Zed's agent panel, pick "🤖 docker-agent (sbx llmman)" from the list of available agents, and it's exactly the same experience as in the previous article: the agent explores your project, reads files, runs shell commands... except that all of this happens inside the sandbox. Files modified by the agent in the workspace of course show up directly in Zed, since it's the same directory.

zedzed

  • Zed launches the agent in the sandbox with sbx exec -i, and talks to it over ACP (JSON-RPC over stdio).
  • Docker Agent runs isolated in the sbx sandbox, with only the project workspace.
  • Docker Agent "talks" to the OpenAI API served by llmman on the host via host.docker.internal, going through the sandbox proxy.
  • This proxy filters the network and injects secrets when needed (API keys, ...) without the agent ever seeing them.
  • On the configuration side: one YAML line changed for the llmman endpoint and one entry in Zed's settings. That's it.

There you go, with next to nothing, we keep the same "100% local" setup as in the previous article, but with an agent that can no longer do whatever it wants on your machine. Feel free to ask questions 🙂