高级 📋 6 个步骤 第 577 / 578 篇

用 PolicyVisor 把 Agent 的改动先关进暂存区:安全预设、逐条审阅与选择性落盘实操

git diff 只能告诉你 Agent 改了什么,不能在你点头之前把改动按住。PolicyVisor 把 Agent CLI 当成一个 Job 来跑,写入先进写时复制的暂存区,审完再决定哪些路径落盘。本教程六步走完这条链路。

2026-10-29· 11 分钟上手· 约 3439 字· 🧪 PolicyVisor / 🗂️ 写时复制暂存

给 Agent 一把能改文件的权限,真正的风险往往不在它写错了什么,而在于你还没看清楚它写了什么。多数做法是先让它跑在容器里,再事后翻 git diff——可是 git 只能告诉你「变了什么」,不能在你点头之前把改动按住。PolicyVisor(pVisor)换了个位置:它把 Agent CLI 或脚本当成一个 Job 来跑,工作区的写入先进一层写时复制的暂存区,你审完再决定哪些路径落盘、哪些直接丢掉。本教程六步走完这条链路:装好并确认平台前提、用安全预设跑一个 Job、看清楚它改了什么、选择性落盘、换到更硬的执行器,最后把边界与红线一次说清。

🧪 本教程适合:已经在用命令行 Agent 改真实仓库、希望改动先过一道再进工作区的开发者。平台前提要看仔细:Linux x86_64 与 Apple Silicon macOS 有官方 wheel,带暂存的宿主执行在 macOS 上需要 macFUSE 与 FSKit 扩展,虚拟机执行分别依赖 KVM 与 Hypervisor.framework。项目当前是开发者预览版,Apache-2.0 许可。

Step 1:装好,并先确认你的平台走哪条路

1 三条前提,任何一条没满足都白做

安装本身有三种走法:Python wheel 最直接,nightly 构建不需要 Rust 工具链,Homebrew 走项目自己的 tap。稳定版与 nightly 的 GitHub 发布里都带原生压缩包与 SHA-256 校验值,装完先确认版本号。

pip install pvisor-sandbox
pvisor --version

# 不想装 Rust 工具链:滚动 nightly 构建
curl -fsSL https://raw.githubusercontent.com/DeepLink-org/pvisor/main/scripts/install-nightly.sh | bash

# Homebrew(项目自带 tap)
brew tap DeepLink-org/formula https://github.com/DeepLink-org/Formula.git
brew install DeepLink-org/formula/pvisor

装完先别急着跑 Agent,把三条平台前提确认掉。其一,wheel 只发 Linux x86_64 与 Apple Silicon macOS 两种,其他平台要从源码构建。其二,macOS 上带暂存的宿主执行需要 macFUSE 并启用 FSKit 扩展,README 给的门槛是 5.4.0 及以上,而站点文档里另有 6.3.0 的口径——以你所用版本的安装指南为准,装错了的表现通常是挂载直接失败。其三,虚拟机执行在 Linux 上走 KVM,在 Apple Silicon 上走 Hypervisor.framework,且 macOS 上还需要准备一个 Linux 镜像或 rootfs。

还有一处文档层面的坑值得先说:README 与站点文档混用了 -- 与 ++ 两种参数前缀,遇到命令不认的时候,以 pvisor help 后面接具体子命令的实际输出为准,不要照抄某一篇文档。另外仓库提供了中文文档,入口在官方文档站的 /zh/ 路径下。

先读一遍安装指南里的平台清单再动手:这个项目的隔离强度高度依赖内核特性,KVM、Hypervisor.framework、FUSE 各有一条独立前提,任何一条没满足,后面所有关于边界的说法都不成立。

Step 2:一个安全预设,把改动留在暂存区

2 不满足预设会拒绝派发,而不是悄悄降级

跑你熟悉的命令,只是在前面加一段:spawn 后面跟 --safe,再用两个短横线隔开真正要执行的命令。也可以显式指定暂存区位置,之后按路径引用它。想在配置文件里固化,TOML 的 run 段里写 profile 等于 safe,效果与命令行一致。

# 在项目目录下跑你熟悉的 Agent CLI
pvisor spawn --safe -- claude

# 显式指定暂存区位置,之后按路径引用它
pvisor spawn --stage /abs/path/draft -- claude

# 配置文件里固化:[run] profile = "safe"  等价于 --safe
# 注意:显式写 --filesystem host 与安全预设冲突,会在执行前就失败

--safe 请求的是安全预设,效果是工作区写入不直接落到目标目录,而是留在 Job 的存储里等你审。准入阶段会先解析执行器与所需能力,如果选中的执行器满足不了这个预设,它会拒绝派发,而不是悄悄降级——这一点比「尽量隔离」的做法可靠。last 指代默认存储里最近的那个 Job,同时管多个 Job 时请用打印出来的 Job ID。

有一条必须记牢:不带暂存的普通宿主 Job 是直接写工作区的,没有任何暂存可言。也就是说,安全预设不是默认行为,漏写就是直写。另外暂存只覆盖配置好的文件系统视图,HOME、rootfs 与显式挂载各有各自的生命周期,不要以为整台机器都被罩住了。

💡 刚接手一个陌生 Agent 时,先用一条最朴素的命令试:起一个空目录,用 --stage 指到工作区之外的暂存路径,让命令在里面写一个文件,再回原目录看有没有出现那个文件。这一步能验证暂存真的生效,成本只有十秒。

Step 3:审:看差异,并进暂存区取证

3 请求一项策略,不等于它被执行了

审阅这一步给的不是一个差异清单而已,而是一份 Run 记录或 Run Bundle:结果、文件系统变更、边界观测与产物引用都在里面。真正要读的是其中的证据与警告——它们说明这一次到底装上了哪些控制。项目自己的措辞很准确:请求一项策略,并不等于它被执行了。所以验收时请把 Bundle 里的证据当成结论,把你命令行里写过的参数当成意图。

pvisor review last                 # 审阅默认存储里最近的 Job
pvisor review 暂存区路径 --diff     # 带差异

# 在暂存区里直接跑命令取证,不影响原工作区
pvisor inspect 暂存区路径 -- /bin/cat report.txt

# 机器可读的状态
pvisor status last --json

还有一处容易踩空:如果一个 Attempt 的完成记录没有提交,带 --review 的状态查询与 review 会显示 UNKNOWN,并给出保留下来的暂存区与恢复入口,而不是编一份安全结论给你。这个只读诊断会正常退出,但它并不授权你执行落盘或丢弃。

不要拿 review 的输出当成「已经隔离」的证明:它给的是这一次观测到的边界,而缺少观测不等于某件事没发生——项目自己也明说代理记录只覆盖经过代理的流量。

Step 4:落盘:必须显式选择,空选择会被拒

4 只让你挑中的路径进工作区

审完之后两条路:接受你挑中的那部分,或者把剩下的整批丢掉。落盘时必须带上整批接受的开关,或者显式的路径、包含、排除选择;空选择会在动目标之前就被拒绝,整批接受也不能和过滤条件同时用。落盘时会拿记录下来的原像比对冲突,并在一份恢复账本里记录进度——即使中途断了,也知道哪些已经落地。

# 只接受 src 目录
pvisor apply last --path src

# 也可以按 --include / --exclude 组合,或者整批接受
pvisor apply last --all

# 剩下的不要了
pvisor drop last

这一节的红线比命令更重要。落盘不提供跨文件的多文件事务,也不针对其他写入者做保护:接受一批改动的时候,请把并发的编辑器与其他落盘进程停掉。冲突检测与恢复记录不会让任意并发写入变得安全。文档里还有一句值得原样转述给团队:崩溃恢复是不完整的,一次完成的提交会分别落多份持久记录,宿主进程在它们之间崩溃就需要人工对账。

远程调用撤不回来:发出去的 API 请求、写进数据库的记录、已经发送的消息,都在落盘与丢弃的范围之外,不会因为丢掉暂存区而回滚;已经落盘的文件在丢弃之后也留在原地。这一层管的是文件系统,不是副作用。

Step 5:换执行器:宿主、容器与虚拟机

5 容器并不满足安全预设

三种执行器的边界差别很大,选错等于白做。宿主最轻,靠平台控制加可选的文件系统暂存,网络代理除非配了强制边界否则是协作式的。容器把执行委派给 OCI 运行时,但 README 明确写了它不满足安全预设,并且委派结果的来源可信度是当前实现的已知限制。虚拟机跑一个独立的 Linux guest,隔离最强,原生 VMM 目标只有 Linux x86_64 的 KVM 与 macOS ARM64 的 Hypervisor.framework。

# 宿主执行(默认):平台控制 + 可选文件系统暂存
pvisor spawn --safe -- claude

# 容器:经 OCI 运行时委派,不满足 --safe 预设
pvisor spawn --executor container -- claude

# 虚拟机:独立 Linux guest,virtio-fs + 用户态网络数据面
pvisor spawn --executor vm --rootfs image=ubuntu:24.04 -- /bin/echo hello

更硬的隔离之外还有一批配套入口:检查点与分叉适合「同一个任务跑两种做法再挑一个」,轨迹回放与交互式审阅前端分别由相邻的可执行文件提供,守护进程则负责缓存与内存池这类单节点服务。要看清的是,逻辑检查点是文件系统层面的:它不捕获进程内存,回放也不会把外部服务回滚。虚拟机的实时控制另有一组命令级开关,暂停、卸载与恢复分别对应不同的 CPU 与设备静默边界,日常用不到,排障时很有用。

配套入口(各自独立契约)

pvisor checkpoint create / list / show    逻辑文件系统检查点与分叉
pvisor fork                               从某个检查点分出一条独立尝试
pvisor-tui  /  --ask                      交互式审阅前端
pvisor-replay                             轨迹回放
pvisor-daemon serve --cache               缓存与内存池等单节点服务
containerd-shim-pvisor-v2                 Linux containerd 集成
💡 想要真正强隔离就上虚拟机,并且接受它的成本:macOS 上要准备 Linux 镜像与 rootfs,Linux 上要能跑 KVM。想在 CI 里跑,先把镜像缓存准备好再谈并发密度。

Step 6:边界、红线与成熟度

6 开发者预览版,别当成品沙箱用

把跨执行器的通用限制整理成一张表,贴进团队 wiki 比事后解释省力。这几条都是 README 里明写的,不是推测。

跨执行器的通用限制

1) 远程 API 调用、数据库写入、已发送消息在落盘与丢弃之外,保持生效
2) 落盘不提供针对其他写入者的多文件事务;接受批次时停止并发写入
3) 代理记录只覆盖经过代理的流量;缺少观测不证明某件事没发生
4) 崩溃恢复不完整:一次完成会分别落多份持久记录,中途宿主崩溃需对账
5) 逻辑检查点不捕获进程内存;回放不回滚外部服务
6) 信任宿主内核、其监管进程与宿主账号的管理代码与配置

最后一段预期管理必须写在方案里。项目自称开发者预览版:架构评审与针对性测试覆盖的是单个行为,它们并不能确立稳定的崩溃恢复、敌对租户之间的隔离,或者完整的 OCI 与 SDK 兼容性。它也不声称在同一账号下互为敌对的租户之间有加固边界。想把它用在多人共享的机器上,请先想清楚信任模型——它信任宿主内核与宿主账号的管理代码,这不是一句客套。

别把它当成一个安全沙箱的成品:它是一套把「先审后落」变成默认动作的执行框架,隔离强度取决于你选的执行器与平台前提。涉及不可信代码或敌对多租户,请另配一层真正的隔离,并把上面的限制逐条转述给团队成员。

常见问题速查

现象原因与处理
命令参数不认README 与站点混用 -- 与 ++ 前缀;以 pvisor help 子命令的输出为准
macOS 上暂存挂载失败需要 macFUSE 与 FSKit 扩展;版本门槛以安装指南为准
虚拟机跑不起来Linux 需 KVM,Apple Silicon 需 Hypervisor.framework,macOS 还需 Linux 镜像或 rootfs
漏写安全预设不带暂存的宿主 Job 直接写工作区;--safe 不是默认行为
准入阶段被拒选中的执行器满足不了安全预设,换宿主或虚拟机
审阅显示 UNKNOWN完成记录未提交;这是只读诊断,不授权落盘或丢弃
落盘报空选择必须给 --all 或显式 --path / --include / --exclude;--all 不能与过滤同用
丢了暂存区但改动还在已落盘的文件在丢弃后保留;远程调用与数据库写入本就不回滚
容器执行想当安全沙箱OCI 执行器明确不满足 --safe 预设,且来源可信度是已知限制
崩溃后状态对不上崩溃恢复不完整,完成记录分多次落盘,需人工对账
想找中文文档官方文档站的 /zh/ 路径下提供中文版
想在 CI 里批量用先备好镜像缓存与平台前提;守护进程只实现部分 OpenSandbox 与 E2B 配置
多人共享一台机器不承诺敌对租户之间的隔离,请另配隔离层并明确信任模型
← 返回教程中心