Mac 上用 OrbStack + Docker + Pi Web 搭一套长期可用的 AI Coding Agent 环境

从 Docker 隔离、Pi Web 自动启动到 Context 管理,一篇完整记录实际搭建过程与踩坑的实战教程。

1. 为什么要这样搭

Pi 本身是一个自由度很高的 Coding Agent。

它可以:

  • 读取文件
  • 写文件
  • 修改代码
  • 执行 Shell
  • 安装依赖
  • 运行测试
  • 调用 Git
  • 持续循环完成开发任务

这种能力很好用,但也意味着:

如果直接把 Pi 放在 macOS 本机并给它很大的 Shell 权限,它理论上也能访问当前用户能访问的大量目录。

所以我的思路是:

给 Pi 一个完整 Linux 开发环境,让它在里面尽可能自由地工作,只把真正需要操作的项目目录共享进去。

最后得到的结构是:

┌────────────────────────┐
│ macOS                  │
│                        │
│ Chrome / Edge / Safari │
│ http://127.0.0.1:30141 │
└───────────┬────────────┘
            │
            ↓
┌────────────────────────┐
│ OrbStack               │
│ Docker Runtime         │
└───────────┬────────────┘
            │
            ↓
┌────────────────────────┐
│ Debian 13 Container    │
│                        │
│ Git                    │
│ SSH                    │
│ Python                 │
│ Go                     │
│ Node.js                │
│ Pi                     │
│ Pi Web                 │
│                        │
│ /workspace             │
└───────────┬────────────┘
            │
            ↓
┌────────────────────────┐
│ macOS 本地项目目录       │
│ ~/work/pi-devbox/      │
│ workspace/             │
└────────────────────────┘

最终日常体验其实很简单:

打开 Mac
↓
OrbStack 自动启动
↓
Docker 容器自动恢复
↓
Pi Web 自动启动
↓
浏览器打开 http://127.0.0.1:30141
↓
开始 Coding

2. OrbStack 到底是什么

这是我一开始最疑惑的地方。

真正用下来以后,我会把 OrbStack 理解成:

Mac 上负责运行 Linux / Docker 的底层运行环境。

macOS 本身没有 Linux Kernel,而常见 Docker 镜像绝大多数运行的是 Linux。

所以在 Mac 上运行:

docker run alpine

背后一定需要某种 Linux 虚拟化环境。

常见选择包括:

Docker Desktop
Colima
OrbStack

这里使用 OrbStack。

它真正做的事情大致是:

Docker CLI
↓
OrbStack 提供 Docker Engine
↓
Linux 环境
↓
Container

所以实际使用以后,OrbStack 的存在感会非常低。

这反而是好事。

平时真正操作的是:

浏览器
↓
Pi Web

OrbStack 只负责安静地在后台托住 Docker / Linux 环境。

3. 安装并验证 OrbStack

如果已经安装 Homebrew:

brew install orbstack

然后启动:

open -a OrbStack

也可以:

orb start

查看当前 Docker context:

docker context show

正常应该输出:

orbstack

验证 Docker

执行:

docker run --rm alpine uname -m

Apple Silicon Mac 正常会输出:

aarch64

第一次执行时出现:

Unable to find image 'alpine:latest' locally
Pulling from library/alpine
...

是正常现象。

Docker 实际做了:

发现本地没有 Alpine
↓
从 Registry 下载
↓
创建临时 Container
↓
执行 uname -m
↓
输出 aarch64
↓
删除 Container

看到:

aarch64

说明:

OrbStack        ✅
Docker Engine   ✅
Linux Container ✅
ARM64           ✅

4. 创建项目目录

建议统一把这一套环境放在:

~/work/pi-devbox

为方便阅读,下文中的用户名、邮箱、密码都使用脱敏占位符。

创建目录:

mkdir -p ~/work/pi-devbox/workspace

cd ~/work/pi-devbox

最终目录结构:

pi-devbox/
├── Dockerfile
├── docker-compose.yml
├── mac.entrypoint.sh
├── .env
└── workspace/

以后真正的项目都放:

workspace/

例如:

workspace/
├── project-a/
├── project-b/
├── agent-demo/
└── rag-demo/

5. 先理解四个核心文件

这一部分非常重要。

刚开始接触 Docker 时,很容易把 Dockerfile、Compose 和 Shell 脚本混在一起。

其实它们负责的事情完全不同。

5.1 Dockerfile

可以理解为:

Linux 开发环境的装机清单。

例如:

RUN apt-get install ...
RUN npm install ...

最后得到:

Dockerfile
↓
docker build
↓
Docker Image

Image 可以理解成:

已经装好 Node、Go、Python、Pi、Pi Web 的系统模板。

5.2 docker-compose.yml

可以理解为:

这台 Container 启动时要怎么运行。

它定义:

  • 容器叫什么
  • 开放哪些端口
  • 挂载哪些目录
  • 使用哪些 Volume
  • 传哪些环境变量
  • 是否自动重启

5.3 .env

可以理解为:

会变化的参数配置文件。

例如:

ROOT_PASSWORD=<your-password>
GIT_USER_NAME=<your-git-name>
GIT_USER_EMAIL=<your-git-email>

这样用户名、密码、邮箱就不需要写死在 Dockerfile 中。

5.4 mac.entrypoint.sh

它才是真正的 Shell 脚本。

作用:

Container 每次启动以后,要执行哪些初始化操作。

例如:

恢复 /root 默认文件
↓
设置 Git 用户
↓
启动 SSH
↓
启动 Pi Web

6. 完整 Dockerfile

创建:

nano Dockerfile

内容:

FROM debian:13-slim

ENV LANG=C.UTF-8 \
    LANGUAGE=C.UTF-8

# 基础工具
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        ca-certificates \
        curl \
        git \
        nano \
        openssh-client \
        openssh-server \
        passwd \
        unzip \
        zip \
        vim \
        wget \
    && apt-get autoremove -y \
    && apt-get clean

# Git / SSH
RUN git config --system tag.gpgSign false \
    && git config --system commit.gpgsign false \
    && mkdir -p /run/sshd \
    && sed -i 's/^#*PermitRootLogin.*/PermitRootLogin yes/' /etc/ssh/sshd_config \
    && sed -i 's/^#*PasswordAuthentication.*/PasswordAuthentication yes/' /etc/ssh/sshd_config

ENV PATH=$PATH:/usr/local/go/bin:/root/go/bin:/root/.local/bin

# 各种开发缓存统一放在容器内部 /cache
ENV GOMODCACHE=/cache/go-mod \
    GOCACHE=/cache/go-build \
    npm_config_cache=/cache/npm \
    PIP_CACHE_DIR=/cache/pip

# Python
ENV PIP_BREAK_SYSTEM_PACKAGES=1

RUN apt-get install -y --no-install-recommends \
        python3 \
        python3-pip \
    && pip3 config set --global global.index-url \
        https://pypi.tuna.tsinghua.edu.cn/simple

# Go
ARG GOLANG_VERSION=1.27.1

RUN arch="$(dpkg --print-architecture)" \
    && wget -q https://go.dev/dl/go${GOLANG_VERSION}.linux-${arch}.tar.gz \
    && tar -C /usr/local -xzf go${GOLANG_VERSION}.linux-${arch}.tar.gz \
    && rm -f go${GOLANG_VERSION}.linux-${arch}.tar.gz \
    && go env -w GOPROXY=https://goproxy.cn,direct \
    && go install -v golang.org/x/tools/gopls@latest \
    && go install -v github.com/go-delve/delve/cmd/dlv@latest \
    && go install -v honnef.co/go/tools/cmd/staticcheck@latest \
    && go clean -modcache \
    && go clean -cache

ENV GOPATH=/root/go

# Node.js 24 + TypeScript
RUN curl -fsSL https://deb.nodesource.com/setup_24.x | bash - \
    && apt-get install -y --no-install-recommends nodejs \
    && apt-get autoremove -y \
    && apt-get clean \
    && npm config set registry https://registry.npmmirror.com --location=global \
    && npm install -g typescript ts-node \
    && npm cache clean --force

# Bash 自动补全
RUN apt-get update \
    && apt-get install -y --no-install-recommends bash-completion \
    && apt-get autoremove -y \
    && apt-get clean \
    && printf '%s\n' \
        '' \
        'if ! shopt -oq posix; then' \
        '    if [ -f /usr/share/bash-completion/bash_completion ]; then' \
        '        . /usr/share/bash-completion/bash_completion' \
        '    elif [ -f /etc/bash_completion ]; then' \
        '        . /etc/bash_completion' \
        '    fi' \
        'fi' \
        >> /etc/bash.bashrc

# Pi Agent
RUN npm install -g --ignore-scripts \
        @earendil-works/pi-coding-agent \
    && npm cache clean --force

# Pi Web
RUN npm install -g @agegr/pi-web@latest \
        --registry=https://registry.npmjs.org \
        --fetch-retries=5 \
        --fetch-retry-mintimeout=20000 \
        --fetch-retry-maxtimeout=120000 \
    && npm cache clean --force

# 备份 /root 默认内容
RUN set -eux; \
    mkdir -p /root-defaults; \
    cp -a /root/. /root-defaults/

COPY mac.entrypoint.sh /usr/local/bin/entrypoint.sh

RUN chmod +x /usr/local/bin/entrypoint.sh

WORKDIR /workspace

EXPOSE 22222
EXPOSE 30141

CMD ["/usr/local/bin/entrypoint.sh"]

保存:

Ctrl + O
Enter
Ctrl + X

7. 为什么要设置 cache

这里:

ENV GOMODCACHE=/cache/go-mod \
    GOCACHE=/cache/go-build \
    npm_config_cache=/cache/npm \
    PIP_CACHE_DIR=/cache/pip

很实用。

它把:

Go Module Cache
Go Build Cache
npm Cache
pip Cache

统一放到:

/cache

而 /cache 不映射到 Mac。

所以这些临时文件:

留在 Container

Container 真删除时:

缓存一起回收

不会持续污染 Mac 本地目录。

8. 为什么要有 root-defaults

稍后会给 /root 挂持久化 Volume:

pi-root:/root

问题是:

Docker Volume 一旦挂到 /root,镜像中原本 /root 的内容会被遮住。

所以构建镜像时先:

/root
↓
/root-defaults

备份一份。

Container 启动后再:

/root-defaults
↓
/root

恢复默认内容。

9. 完整 mac.entrypoint.sh

创建:

nano mac.entrypoint.sh

内容:

#!/bin/bash

set -e

# 恢复镜像构建阶段备份的 /root 默认内容
cp -an /root-defaults/. /root/ 2>/dev/null || true

# Git 用户名
if [ -n "$GIT_USER_NAME" ]; then
    git config --global user.name "$GIT_USER_NAME"
fi

# Git 邮箱
if [ -n "$GIT_USER_EMAIL" ]; then
    git config --global user.email "$GIT_USER_EMAIL"
fi

# SSH root 密码
echo "root:${ROOT_PASSWORD:-root123}" | chpasswd

# 创建 SSH Host Keys
ssh-keygen -A

mkdir -p /run/sshd

echo "======================================"
echo " Pi Development Container"
echo "======================================"

echo ""
echo "Workspace:"
pwd

echo ""
echo "Architecture:"
uname -m

echo ""
echo "Node:"
node --version

echo ""
echo "npm:"
npm --version

echo ""
echo "Python:"
python3 --version

echo ""
echo "Go:"
go version

echo ""
echo "Pi:"
pi --version

echo ""
echo "======================================"

# SSH 后台运行
echo "Starting SSH..."
/usr/sbin/sshd -p 22222

# Pi Web 成为 Container 主进程
echo "Starting Pi Web..."
echo "Pi Web: http://127.0.0.1:30141"

exec pi-web \
    -H 0.0.0.0 \
    --no-open

保存:

Ctrl + O
Enter
Ctrl + X

然后:

chmod +x mac.entrypoint.sh

10. 逐行理解 entrypoint 脚本

10.1 Shebang

#!/bin/bash

表示:

使用 Bash 执行这个文件。

10.2 set -e

set -e

表示:

关键命令失败时停止整个脚本。

有些允许失败的命令会显式写:

xxx || true

表示:

这条失败我知道,继续执行。

10.3 恢复 root 默认内容

cp -an /root-defaults/. /root/ 2>/dev/null || true

其中:

cp
= copy

-a
= 保留目录、权限、时间等属性

-n
= 已经存在的文件不覆盖

10.4 判断环境变量是否为空

if [ -n "$GIT_USER_NAME" ]; then
    git config --global user.name "$GIT_USER_NAME"
fi

其中:

[ -n "$GIT_USER_NAME" ]

表示:

如果这个字符串不为空。

10.5 默认值语法

${ROOT_PASSWORD:-root123}

意思是:

ROOT_PASSWORD 有值
→ 用 ROOT_PASSWORD

没有值
→ 用 root123

10.6 ssh-keygen -A

ssh-keygen -A

生成 SSH Server 所需的 Host Keys。

10.7 启动 SSH

/usr/sbin/sshd -p 22222

这里没有 -D。

所以 sshd 会自己进入后台。

10.8 exec pi-web

exec pi-web \
    -H 0.0.0.0 \
    --no-open

这里的:

\

表示下一行仍然属于同一条命令。

等价于:

exec pi-web -H 0.0.0.0 --no-open

普通:

pi-web

类似:

bash
↓
pi-web 子进程

而:

exec pi-web

表示:

用 Pi Web 直接替换当前 Bash 进程。

于是:

entrypoint.sh
↓ exec
Pi Web

Pi Web 会成为 Container 的主进程。

这意味着:

Pi Web 活着
↓
Container 活着

如果 Pi Web 意外退出:

Pi Web 退出
↓
Container 退出
↓
Docker restart policy 生效
↓
Container 自动恢复

这是整个自动启动链路里非常关键的一点。

11. 完整 docker-compose.yml

创建:

nano docker-compose.yml

内容:

services:
  pi-dev:
    build:
      context: .

    container_name: pi-dev

    restart: unless-stopped

    ports:
      - "127.0.0.1:22222:22222"
      - "127.0.0.1:30141:30141"

    volumes:
      - ./workspace:/workspace
      - pi-root:/root

    environment:
      ROOT_PASSWORD: ${ROOT_PASSWORD}
      GIT_USER_NAME: ${GIT_USER_NAME}
      GIT_USER_EMAIL: ${GIT_USER_EMAIL}

volumes:
  pi-root:

12. 为什么端口要绑定 127.0.0.1

例如:

- "127.0.0.1:30141:30141"

表示:

Mac
127.0.0.1:30141
↓
Docker
↓
Container
30141

这样 Pi Web 只提供给本机。

局域网其他机器无法直接通过:

Mac-IP:30141

访问。

考虑到 Pi Web 背后的 Agent 可以:

  • 执行 Shell
  • 修改代码
  • 访问 workspace

默认只开放 localhost 更合理。

为什么 Container 内部又监听 0.0.0.0

entrypoint:

pi-web -H 0.0.0.0

这里是 Container 内部。

如果 Pi Web 只监听:

127.0.0.1

这个 localhost 指的是:

Container 自己

Docker 端口转发无法正常访问。

因此内部:

0.0.0.0:30141

外部:

127.0.0.1:30141

最终安全边界仍然在 Mac 侧。

13. 创建 .env

执行:

nano .env

例如:

ROOT_PASSWORD=<your-password>
GIT_USER_NAME=<your-git-name>
GIT_USER_EMAIL=<your-git-email>

保存以后:

chmod 600 .env

14. 构建并启动容器

执行:

docker compose up -d --build

其中:

--build

表示先重新构建 Image。

-d

表示 Container 后台运行。

查看 Container

docker ps

应该看到:

pi-dev

状态:

Up

查看日志

docker logs pi-dev

大致应该看到:

======================================
 Pi Development Container
======================================

Workspace:
/workspace

Architecture:
aarch64

Node:
...

npm:
...

Python:
...

Go:
...

Pi:
...

======================================

Starting SSH...
Starting Pi Web...
Pi Web: http://127.0.0.1:30141

15. 实际踩坑一:docker.sock 不存在

第一次运行 Docker 时遇到过:

failed to connect to the docker API at

~/.orbstack/run/docker.sock

connect: no such file or directory

原因:

Docker CLI
已经指向 OrbStack

但

OrbStack Docker Engine
还没有真正启动

解决:

open -a OrbStack
orb start

然后:

docker info

最后重新验证:

docker run --rm alpine uname -m

16. 实际踩坑二:Pi Web npm 下载失败

构建阶段真实遇到过:

npm error network
In most cases you are behind a proxy
or have bad network settings.

最后:

failed to solve
RUN npm install -g @agegr/pi-web@latest
exit code: 1

因为前面全局配置过:

registry.npmmirror.com

而 Pi Web 这一步下载不稳定。

最终给 Pi Web 单独强制使用 npm 官方 Registry:

RUN npm install -g @agegr/pi-web@latest \
        --registry=https://registry.npmjs.org \
        --fetch-retries=5 \
        --fetch-retry-mintimeout=20000 \
        --fetch-retry-maxtimeout=120000 \
    && npm cache clean --force

然后重新:

docker compose up -d --build

成功。

17. 实际踩坑三:重启后 Pi Web is offline

有一次 Mac 重启以后,浏览器页面仍然存在,但显示:

Pi Web is offline

这个现象一开始很容易误判。

其实:

浏览器

缓存了 Pi Web 的前端页面。

所以 UI 还能显示。

但:

Pi Web Server

并没有真正启动。

于是前端只能显示:

offline

最后做了两个改动:

  1. Pi Web 全局安装进 Docker Image
  2. Pi Web 通过 exec 成为 Container 主进程

也就是:

Container 启动
↓
entrypoint.sh
↓
启动 SSH
↓
exec pi-web

这样 Container 重启时,Pi Web 会自动恢复。

18. 项目目录如何映射

Compose:

- ./workspace:/workspace

例如 Mac:

/Users/<username>/work/pi-devbox/workspace/project-a

Container:

/workspace/project-a

Pi 修改:

/workspace/project-a/src/main.ts

Mac 上对应文件也会立刻变化。

这是 Docker bind mount。

注意安全边界

Docker 隔离了:

macOS 系统
其他未挂载目录
大量本地配置

但:

/workspace

是主动共享进去的。

所以:

rm -rf /workspace/*

真会删除 Mac 上 workspace 中的文件。

因此真实项目最好:

Git 管理
+
Container 隔离

例如执行大任务前:

git add .
git commit -m "checkpoint before agent task"

19. Pi 配置和会话如何持久化

Pi 的主要数据在:

/root/.pi

Compose:

- pi-root:/root

因此:

Pi Sessions
插件
模型配置
Provider 配置
其他 Pi 数据

都会进入:

pi-root

这个 Docker Volume。

所以:

关闭浏览器
✅ 数据还在

停止 Container
✅ 数据还在

重启 Container
✅ 数据还在

docker compose down
✅ 数据还在

重新 Build
✅ 数据还在

真正需要小心的是:

docker compose down -v

这里的:

-v

会删除 Volume。

没特殊需求不要执行。

20. Container 要不要一直挂后台

我的选择是:

日常一直挂着。

因为使用频率较高。

结构:

Mac 登录
↓
OrbStack
↓
Docker Engine
↓
pi-dev
↓
Pi Web

空闲时主要就是:

Linux Runtime
Node Web Server
Container

模型推理在远端 API。

所以不是 Mac 本地一直跑大模型。

如果几天不用,也可以直接在 OrbStack 中:

Containers
↓
pi-dev
↓
Stop

下一次:

Start

即可。

项目、Pi 配置、会话不会丢。

21. Pi Web 插件配置

环境跑通以后,在 Pi Web 中进入:

Settings
↓
Plugins

建议第一轮安装下面这些插件:

npm:@juicesharp/rpiv-ask-user-question
npm:@narumitw/pi-btw
npm:pi-context-view
npm:pi-open-tui
npm:@xyzensun/pi-mctx

建议使用:

Global

全局安装。

因为 /root 已经做了持久化,所以这些插件不会因为 Container 重建而消失。

22. 插件说明

22.1 ask-user-question

npm:@juicesharp/rpiv-ask-user-question

作用是给 Agent 增加结构化提问能力。

例如:

你准备采用哪一种认证方案?

○ JWT
○ Session
○ OAuth

遇到关键分叉时,Agent 可以先问你,而不是直接猜。

22.2 pi-btw

npm:@narumitw/pi-btw

这是很实用的一个插件。

例如当前 Agent 正在:

修改代码
↓
跑测试
↓
分析 Bug

突然想问:

这里为什么不用 Redis?

直接问会把这个问题塞进主 Context。

可以用:

/btw 为什么这里不用 Redis?

它相当于:

主 Agent Context
────────────────────→

        ↓
      临时问题
        ↓
       回答

────────────────────→
继续主任务

很适合开发过程中临时冒出来的小问题。

22.3 pi-context-view

npm:pi-context-view

主要用于观察:

System Prompt
Tools
Messages
Context

Pi Web 已经能显示不少 Context 信息,所以这个插件的重要性相对下降。

但第一轮体验可以保留。

22.4 pi-open-tui

npm:pi-open-tui

主要改善 Pi 终端 TUI。

如果主要入口是:

Pi Web

这个插件实际使用频率会比较低。

但保留也没问题。

22.5 pi-mctx

npm:@xyzensun/pi-mctx

这个非常值得理解。

它主要解决:

Coding Agent 长 Session 的 Context 管理。

/mctx sink

长时间 Coding 时,Agent 会产生大量:

read 文件结果
bash 输出
测试日志
Git diff
构建日志

这些 Tool Result 很容易把 Context 撑大。

执行:

/mctx sink

可以把已经不需要继续常驻 Context 的 Tool Result 移出去。

恢复:

/mctx sink --undo

/mctx new

这个更有意思。

可以理解成:

当前长 Session
↓
总结当前工作状态
↓
新建 Session
↓
注入总结
↓
继续工作

也就是把:

“总结一下当前工作”
↓
清理 Context
↓
重新开始

自动化。

对长时间 Coding 很实用。

23. 为什么暂时不继续堆插件

目前已经有:

Pi Web
├── Session
├── Models
├── Plugins
├── Skills
├── Files
├── Git
├── Context
└── Agent Tools

再继续堆:

MCP Adapter
Subagent
各种 Web 插件
各种 UI 插件

很容易出现:

Agent 还没真正用起来,环境已经越来越复杂。

更合理的思路是:

真实使用
↓
发现痛点
↓
再安装对应能力

24. 下一阶段:Skills

Plugins 更多是在修改:

Agent 的能力和运行行为

Skills 更像是:

告诉 Agent:

遇到某一类任务
应该怎么做
遵守什么流程
使用什么工具

例如以后可以沉淀:

Java / Spring Skill
RAG Skill
代码 Review Skill
数据库变更 Skill
Git 提交 Skill
测试 Skill

甚至项目自己带:

项目规范 Skill

这一块比盲目继续堆插件更值得投入。

25. 最终日常使用方式

搭好以后,日常基本不需要一直敲 Docker 命令。

使用方式:

打开 Mac
↓
OrbStack 自动工作
↓
pi-dev 自动启动
↓
Pi Web 自动启动
↓
浏览器打开收藏夹

地址:

http://127.0.0.1:30141

然后在 Pi Web 中选择:

/workspace/project-a

创建 Session,开始 Coding。

26. 最终架构

                          ┌─────────────────────┐
                          │      Browser        │
                          │                     │
                          │ Pi Web              │
                          │ localhost:30141     │
                          └──────────┬──────────┘
                                     │
                                     ↓
┌────────────────────────────────────────────────────────┐
│                       macOS                            │
│                                                        │
│  ┌──────────────── OrbStack ────────────────────────┐  │
│  │                                                   │  │
│  │              Docker Engine                        │  │
│  │                     │                             │  │
│  │                     ↓                             │  │
│  │          ┌────────────────────┐                   │  │
│  │          │ pi-dev Container   │                   │  │
│  │          │                    │                   │  │
│  │          │ Debian 13          │                   │  │
│  │          │ Git                │                   │  │
│  │          │ SSH                │                   │  │
│  │          │ Python             │                   │  │
│  │          │ Go                 │                   │  │
│  │          │ Node               │                   │  │
│  │          │ Pi                 │                   │  │
│  │          │ Pi Web             │                   │  │
│  │          │                    │                   │  │
│  │          │ /workspace ───────────────┐            │  │
│  │          │ /root      ───────────┐   │            │  │
│  │          └────────────────────┘  │   │            │  │
│  │                                  │   │            │  │
│  │          Docker Volume           │   │            │  │
│  │          pi-root ◀───────────────┘   │            │  │
│  └──────────────────────────────────────┼────────────┘  │
│                                         │               │
│                                  bind mount              │
│                                         │               │
│                                         ↓               │
│                      ~/work/pi-devbox/workspace          │
│                                                        │
└────────────────────────────────────────────────────────┘

27. 总结

这套东西搭完以后,最大的感受是:

一开始看起来是在折腾:

OrbStack
Docker
Shell
Volume
Port

真正获得的是一套:

可以长期使用、可以重建、可以持久化、隔离边界清晰的 AI Coding Agent 开发环境。

Pi 可以在 Linux Container 内拥有足够大的操作空间去:

安装依赖
运行命令
修改代码
跑测试
管理 Git

Mac 负责:

浏览器
IDE
项目文件

Docker 负责:

Agent 工作环境

两边职责非常清楚。

最终结构可以记成:

【可随时重建】

Docker Image
├── Debian
├── Node
├── Go
├── Python
├── Pi
└── Pi Web


【运行实例】

Container
└── pi-dev


【永久代码】

Mac
└── workspace


【永久 Agent 数据】

Docker Volume
└── pi-root

这里最值得记住的一句话是:

运行环境尽量做到可重建,真正的数据独立保存。

接下来真正值得投入时间的已经不是继续折腾 Docker,而是:

用真实项目跑 Pi
↓
熟悉 Context 管理
↓
沉淀 Skills
↓
优化自己的 Agent Harness
↓
观察哪些流程值得自动化

到这一步,Pi 才开始从“一个 Coding Agent”变成“自己可以逐渐塑造的开发环境”。