适用场景
你已经有一个能改代码的智能体——可能是自研的循环脚本,也可能是包装了某个模型 API 的小工具——它平时能跑通,但一旦出错就说不清:它到底调了哪些命令、改了哪些文件、哪一步开始跑偏。Prospero 这类 harness(执行外壳)解决的就是这件事:把模型和真实工具之间隔一层,工具调用按白名单放行,每一步落盘成轨迹,失败时能原样重放。
适合三类人:要把编码智能体接进 CI 的工程团队;需要给智能体划定"只能动这个目录、只能跑这几条命令"的内网环境;以及被线上事故教育过、想补上复盘能力的人。如果只是本地随手改两行代码,这套东西属于过度设计。
环境与前置条件
- 操作系统:Linux 或 macOS。Windows 建议走 WSL2,避免路径分隔符和进程权限带来的额外问题。
- 运行时:Python 3.10 以上(具体下限以官方文档当前版本为准),需要能创建虚拟环境。如果 Prospero 的适配层要驱动 Node 生态的工具,另外准备 Node LTS。
- Git:必须。后面复现失败要靠 commit 快照,没有版本控制就没有"回到当时那个工作区"。
- 磁盘:轨迹是主要开销。一个中等复杂度的编码任务,记录完整提示和工具返回后通常几 MB 到几十 MB;按每天几十次任务估,预留 20GB 以上比较稳妥。
- 内存:8GB 起步。如果模型走本地推理,显存和内存按所选模型的要求另算,harness 本身不吃显存。
- 模型访问:一个可用的 API Key,或者一个兼容 OpenAI 协议风格的本地推理端点。
一句话提醒:下文出现的子命令名、配置项名、字段名,请在安装完成后用 --help 和官方示例核对一遍。这类工具迭代快,命名会变,但"隔离环境 → 配置后端 → 划边界 → 开录制 → 跑任务 → 回放"这个顺序是稳定的。
分步骤部署
第 1 步:建一个干净的运行环境
harness 会调用系统命令,最怕和全局 Python 里的旧依赖打架。先隔离:
```bash
python3 -m venv ~/.venvs/prospero
source ~/.venvs/prospero/bin/activate
python -V
```
成功标志:python -V 输出的版本不低于官方要求的下限。
后面所有操作都在这个已激活的 shell 里做。开新终端要重新 source 一次,这是新手最容易忘的一步。
第 2 步:安装并确认 CLI 可用
按官方仓库 README 给出的方式安装(常见是 pipx install 或 pip install,包名以仓库为准):
```bash
包名与渠道以官方仓库说明为准
pipx install <官方包名>
或: pip install <官方包名>
```
装完立刻验证入口是否在 PATH 上:
```bash
prospero --version
prospero --help
```
成功标志:--version 打印出版本号,--help 列出可用子命令。请把 --help 的输出存下来,后面每一步的命令都对着它核对,别照抄网上过期的写法。
第 3 步:初始化工作区
harness 需要一个固定目录存放配置、策略和轨迹。在你打算接管的项目根目录下执行初始化(子命令名以 help 输出为准):
```bash
cd /path/to/your-repo
prospero init
```
成功标志:目录下多出一个配置目录(通常叫 .prospero/ 或类似名字),里面有主配置文件、一个策略文件、一个空的轨迹目录。把它加进 .gitignore 的是轨迹,策略文件建议提交进仓库——边界规则应该跟着代码一起被 review。
第 4 步:配置模型后端,密钥只走环境变量
打开初始化生成的主配置文件,填入后端类型、模型名、接口地址。密钥不要写进配置文件,用环境变量注入:
```bash
export PROSPERO_API_KEY="你的密钥"
如果是自建端点,通常还需要:
export PROSPERO_BASE_URL="http://127.0.0.1:8000/v1"
```
配置文件里只写引用,不写值(字段名以官方示例为准):
```yaml
示意:实际字段名请对照官方配置示例
model:
provider: openai-compatible
name: "<你的模型名>"
api_key_env: PROSPERO_API_KEY
base_url_env: PROSPERO_BASE_URL
```
为什么坚持环境变量:轨迹文件里会记录请求上下文,配置文件更容易被误提交、被截图、被打包进镜像。
成功标志:执行一次连通性检查(多数版本提供类似 prospero doctor 或配置校验的子命令),输出里模型后端显示为可用。
第 5 步:划工具白名单与目录边界
这是"可审计"的第一层,也是最有价值的一层。在策略文件里明确三件事:
```yaml
示意:语义正确,字段名以官方文档为准
tools:
allow:
- read_file
- write_file
- run_shell
deny:
- network_fetch # 先关掉,需要时再单独开
limits:
shell_allowlist:
- "git status"
- "git diff"
- "npm test"
- "pytest"
- "python -m"
shell_denylist:
- "rm -rf"
- "curl"
- "git push"
paths:
writable:
- "./src"
- "./tests"
readonly:
- "./"
```
思路是"默认拒绝、逐条开口"。先只放开 read_file 和查询类命令,把任务跑通,再逐步加写权限。一上来就全开,等于没有 harness。
成功标志:策略文件能通过校验命令;故意写一条不在白名单里的命令,启动时应该报错或直接拒绝。
第 6 步:打开轨迹录制
在配置里指定轨迹输出位置和记录粒度:
```yaml
示意
trace:
enabled: true
dir: ".prospero/traces"
format: jsonl
capture:
prompt: true # 完整提示,复盘必需
tool_args: true
tool_result: true
truncate_result_bytes: 65536 # 大输出截断,避免轨迹爆掉
```
录制粒度是个权衡:tool_result 全记最利于复盘,但读一个大文件就可能灌进来几百 KB。设一个截断阈值,完整内容另存到 artifacts 目录,轨迹里只放引用。
成功标志:跑完任意一次任务后,.prospero/traces/ 下出现新文件,用 wc -l 数一下,行数明显大于 0。
第 7 步:把现有编码任务改写成任务文件
原来你可能是在聊天框里手敲"帮我把 utils 里的时间格式化改成 ISO 格式,顺便补测试"。现在把它固化成一份可版本化的任务描述:
```yaml
tasks/fix-time-format.yaml(示意)
id: fix-time-format
goal: |
把 src/utils/time.py 里的 format_time 改为输出 ISO 8601 格式,
并在 tests/test_time.py 补一个覆盖空输入和带时区输入的用例。
constraints:
- 不要改动 src 目录下的其他文件
- 不要新增第三方依赖
acceptance:
- "pytest tests/test_time.py -q"
```
acceptance 这一项是关键:它把"做完了"从模型的自我陈述变成一条可执行的判定命令。没有它,你只能靠肉眼判断这次跑得好不好。
成功标志:任务文件能被加载,prospero run tasks/fix-time-format.yaml --dry-run(若有该开关)能打印出解析后的目标与约束。
第 8 步:先跑一个只读小任务
别急着上真实需求。第一次跑,把工具限制到只读:
```bash
git status --porcelain # 先确认工作区干净
prospero run tasks/fix-time-format.yaml --readonly
```
这步的目的是验证"模型 → harness → 工具 → 返回"这条链路通了,而不是验证它会不会写代码。
成功标志:终端有逐步的过程输出;任务结束后 git status 显示工作区没有变化;轨迹目录里多了一个文件。
第 9 步:确认轨迹里记了什么
打开最新一条轨迹,按行看:
```bash
ls -lt .prospero/traces | head
jq -c '{type, tool, duration_ms}' .prospero/traces/<最新文件>.jsonl | head -20
```
一行一个事件,典型字段包括时间戳、事件类型(模型回复 / 工具调用 / 工具结果 / 结束)、工具名、参数、耗时。字段名以实际输出为准,先用 head -1 看一行完整结构再写 jq 表达式。
成功标志:你能从轨迹里还原出"第几步调了什么工具、传了什么参数、返回了什么"。如果只看到模型文本、看不到工具参数,回去检查第 6 步的 capture 配置。
第 10 步:放开写权限,制造一次失败
现在把 write_file 放开,让它在真实文件上干活。然后故意制造一次失败——这是本教程的核心练习:
```bash
方式一:把验收命令改成一个必然失败的断言
方式二:在策略里去掉 pytest 的白名单,看它被拒绝时的表现
方式三:跑一个目标本身有歧义的任务
prospero run tasks/fix-time-format.yaml
```
观察失败时的表现:是工具被拒绝后模型反复重试,还是直接中止?轨迹里有没有明确标出失败点?
成功标志:你能指出"第 N 步,模型想调用 X,被策略拦下 / 参数错误 / 验收命令返回非零",而不是只知道"这次没成功"。
第 11 步:回放这次失败
回放是整套方案存在的理由。执行(子命令名以 help 为准):
```bash
prospero replay .prospero/traces/<失败的那次>.jsonl
```
回放要复现三件事,缺一不可:
1. 代码状态:从轨迹里记录的 commit(或任务开始时自动打的快照)恢复工作区,别在已经被改过的目录上重放。
2. 模型参数:温度、seed、模型名固定。多数 API 不保证完全确定性,所以回放的目标是"复现同样的失败模式",不是"逐字节相同"。
3. 工具返回:读文件、跑测试这类有副作用的调用,回放时优先用轨迹里录下的返回值,而不是真的再执行一遍。这样即使外部依赖挂了,你也能在本地把当时的现场重演。
成功标志:回放能走到与原来相同的失败步,并打印出该步的输入输出。到这里,你就有了一份可以贴进 issue、可以交给同事复现的失败样本。
验证部署是否成功
按顺序跑这四条,全过就算部署完成:
```bash
1. 入口可用
prospero --version
2. 配置与后端连通
prospero doctor # 或官方提供的等价校验命令
3. 一次完整任务产生了轨迹
prospero run tasks/fix-time-format.yaml --readonly
ls -1 .prospero/traces | wc -l # 预期:比执行前多 1
4. 轨迹内容可解析、可回放
head -1 .prospero/traces/<最新>.jsonl | jq . # 预期:合法 JSON,含事件类型字段
prospero replay .prospero/traces/<最新>.jsonl # 预期:能重走到结束或同一失败点
```
第 4 条是分水岭。只能跑不能放,说明轨迹缺字段或快照没打,回去补第 6 步和第 11 步的配置。
常见报错与解决
报错一:prospero: command not found
原因:安装到了某个虚拟环境但当前 shell 没激活,或者 pip install --user 的 bin 目录不在 PATH 上。
```bash
source ~/.venvs/prospero/bin/activate
which prospero
若用 pipx,执行:
pipx ensurepath && exec $SHELL
```
报错二:401 Unauthorized / invalid api key
原因:环境变量只在另一个终端里 export 过;或者变量名和配置文件里引用的名字不一致(大小写、拼写);或者请求被代理截走。
```bash
echo "${PROSPERO_API_KEY:+已设置}" # 预期输出“已设置”
env | grep -i prospero
确认变量名与配置文件里的 api_key_env 完全一致
排查代理:
curl -sS -o /dev/null -w '%{http_code}\n' "$PROSPERO_BASE_URL/models" \
-H "Authorization: Bearer $PROSPERO_API_KEY"
```
报错三:tool not allowed: run_shell 或工具被静默拒掉
原因:策略文件里只写了 tools.allow 的粗粒度工具名,没写 shell_allowlist 里对应那条具体命令;或者改完策略没重启进程。
```bash
1. 确认策略文件路径确实被加载(多数版本支持打印生效配置)
prospero config show # 或等价的子命令
2. 把需要的命令加进 shell_allowlist,注意匹配是按前缀还是精确匹配
3. 重新执行任务,不要复用旧进程
```
报错四:PermissionError: [Errno 13] 写轨迹失败
原因:轨迹目录属主不对(比如之前用 sudo 跑过一次),或者磁盘满了。
```bash
df -h .prospero
ls -ld .prospero/traces
sudo chown -R "$(id -u):$(id -g)" .prospero
```
报错五:回放结果与原始轨迹不一致
原因:工作区没恢复到原始 commit、任务里有时间/随机数/网络依赖、模型端非确定性。
```bash
git status --porcelain # 预期:为空
git log --oneline -1 # 与轨迹里记录的 commit 对比
把时间、随机种子之类的不确定输入在任务文件里固定下来
```
后续维护
备份。要备份的是两类东西:策略与任务文件(进 Git,跟着代码走,改动走 review);轨迹文件(不进 Git,定期归档到对象存储或带版本控制的备份盘)。轨迹里可能有内网地址、路径、甚至提示词中的业务信息,归档前做一次脱敏过滤,把密钥样式和内部域名替换掉。
升级。先在配置文件里把 harness 版本钉死,别用浮动版本。升级流程建议是:读 changelog 里关于配置字段和轨迹格式的变更 → 在一个影子目录里装新版本 → 用同一份任务文件跑一遍 → 确认轨迹字段兼容、回放还能用 → 再切主环境。轨迹格式变更往往不向后兼容,老轨迹要用老版本读。
日志与监控。除了单次轨迹,建议按天聚合几个指标:任务成功率、平均工具调用步数、被策略拒绝的次数、单任务 token 消耗。被拒绝次数突然上涨,通常意味着模型在试探边界或者策略写得太紧导致它在原地打转——这两种都值得看一眼。轨迹目录要配轮转策略,按周或按大小切分,超过保留期的归档而不是直接删。
边界要定期复查。白名单是活的:新需求会推动你不断加命令,加着加着就变成全开了。建议每季度把 shell_allowlist 从头看一遍,问一句"这条现在还必要吗"。可审计的价值不在于记录得多全,而在于边界始终是有人刻意维护的。
