07-工具系统与安全边界
07 工具系统与安全边界
1. 内置工具
默认启用:read、bash、edit、write。
| 内置工具 | 用途 |
|---|---|
read | 读文本文件与支持的图片 |
bash | 执行 shell 命令 |
powershell | Windows 上执行 PowerShell |
edit | 对已有文件做精确文本替换 |
write | 新建或覆盖文件 |
grep | 搜索文件内容 |
find | 按 glob 模式找路径 |
ls | 列目录 |
另有两个由内置扩展提供的工具,默认关闭;MCP 扩展在需要时会自行打开;也可手动启用:
| 工具 | 用途 |
|---|---|
codemode | 跑 JavaScript 调用其他工具(可用 Promise.allSettled 并行);只有脚本输出进入模型 |
tool_search | 搜索尚未声明给模型的工具(codemode 与 deferred 暴露模式,如 MCP 工具),并把命中声明给下一次调用 |
2. 工具选择的优先级和语义
这是最容易踩坑的地方,按以下顺序理解:
2.1 defaultTools(设置项)
- 纯名称列表(如
["read","bash"])会替换默认选择。 - 只含
+name/-name的列表则在继承的选择上增减:
{ "defaultTools": ["+codemode"] }
上面这个保留 read / bash / edit / write 并加上 codemode。
{ "defaultTools": ["-bash", "+powershell", "+grep"] }
上面把 bash 换成 powershell 并启用 grep(同一列表里先应用纯名称形成选择集,再按顺序应用 +/-)。
- 空数组禁用所有内置工具,但不禁用扩展或 SDK 工具。
- 项目设置叠加在用户设置之上:项目列表若只含
+/-则改动用户选择,若含纯名称则直接替换。 /reload会启用新增到defaultTools的工具;但它不会禁用从中移除的工具,也不会重新启用你手动关掉的未变动工具。
2.2 CLI 参数(覆盖设置,仅本次进程)
| 参数 | 语义 |
|---|---|
-t / --tools <list> | 替换整个选择,只保留列出的(不支持 +/-) |
-xt / --exclude-tools <list> | 在其他选择选项之后禁用列出名称 |
-nbt / --no-builtin-tools | 关掉默认内置工具,但保留扩展与自定义工具 |
-nt / --no-tools | 内置、扩展、自定义全部关闭 |
因为 --tools 是替换语义,想加 codemode 必须把要的全部列出:
pi --tools read,bash,edit,write,codemode
这些 CLI 参数也覆盖 defaultTools 的 reload 行为。
3. codemode 的两种模式与预算
| 设置 | 取值 | 默认 | 含义 |
|---|---|---|---|
codemode.mode | "on" | "only" | "on" | on:已声明的工具在描述里附上“如何从脚本调用”的说明,codemode 只列出未声明的工具;only:codemode 列出脚本可调用的全部工具,且活动的内置/扩展工具对模型隐藏,只能经脚本到达 |
codemode.inlineBudget | number | 3000 | codemode 工具描述可用于工具声明的估算 token(字符数 / 4);放不下的工具留给 searchTools() 找;0 只列命名空间 |
脚本在 QuickJS 沙箱里运行,通过 tools.<name>(args) 触达其他工具。除了减少来回,它还有两个架构级好处:
- 把大输出挡在模型之外(在脚本里过滤后再返回);
- 并行调用工具(
Promise.allSettled),而不需要模型一轮一轮串行决策。
即使没有 MCP,codemode 也有用:它可以调分类器模型(models.classify())与生图模型(models.generateImages())。tool_search 使用与 searchTools() 相同的排序,加载过的工具会像其他工具变更一样记录在会话里,因此它们在该分支上保持已声明状态。
4. 安全模型(先把预期摆正)
这是 Pi 文档里最重要的一段价值观,值得原样记住:
- Pi 能做的事 = 启动它的那个账号的权限。Pi 能读、改、执行文件;扩展、包安装器、语言服务器等子进程同样如此。
- Pi 不会在每次工具调用前都请求批准。
- 文件和评论里的内容可以劫持模型(prompt injection)。项目信任控制的是“启动时加载哪些项目资源”,不是“内容安全”。
- 因此安全来自限制 Pi 能触达的文件/凭据/进程/网络,而不是“看着 transcript”。
5. 项目信任(project trust)
受信任门控保护的资源
只要在当前工作目录发现以下任一资源,Pi 就需要一个信任决策:
.pi/settings.json、.pi/mcp.json.pi/extensions、.pi/skills、.pi/prompts、.pi/themes.pi/SYSTEM.md或.pi/APPEND_SYSTEM.md- 当前目录或祖先目录里的项目
.agents/skills
一个空的 .pi 目录不会触发信任。
信任不是完整边界
Pi 在选/建会话时会读项目 sessionDir 设置,这发生在解析项目信任之前。拒绝信任能阻止其余项目设置与受保护资源加载,但无法撤回那次初始会话目录查找。
上下文文件(AGENTS.override.md / AGENTS.md / CLAUDE.md)不经项目信任也会加载(除非用 -nc / --no-context-files 关掉)。即使你拒绝信任,也要把文件夹里的指令当不可信输入。
决策顺序
- 命令行
--approve/--no-approve优先; - 否则用户级与命令行扩展可以处理
project_trust事件,第一个返回 yes/no 的扩展拥有决策权; - 否则查已保存决策(当前的目录或其祖先,取最近的);
- 否则看全局
defaultProjectTrust,默认"ask"。
保存的决策用规范目录路径,存于 ~/.pi/agent/trust.json;/trust 用于保存。
无交互提示的模式
print / JSON / RPC 模式弹不出内置信任提示:若没有命令行覆盖、扩展决策或已保存决策,则 defaultProjectTrust: "always" 才加载项目资源,"ask" 或 "never" 均跳过。自动化运行需要明确一次性决策时用 --approve / --no-approve。
6. 隔离强度的三档选择
| 运行方式 | 保护住了什么 |
|---|---|
| 直接以 OS 用户权限运行 | 只能靠该用户本就无法访问的东西;专用账号能收窄,但仍共享 OS 与网络 |
| 整个 Pi 在容器/虚拟机/沙箱内 | 未被暴露给该环境的主机文件与进程(通常是实践中最强的一档) |
| Pi 在外面,只有内置工具在隔离环境内 | 只保护“经由这些工具”的动作;Pi 自身与扩展仍在边界外,是较弱的隔离 |
无论选哪种:只给任务需要的文件与服务;凭据尽量放在环境外,或用窄范围、短期凭据;命令不需要网络时就限制网络。工具的实际默认目录由工作目录决定,但它不阻止命令访问 Pi 进程能访问的其他路径。
7. 降低影响的实践
- 只给任务需要的文件与服务。
- 大改动前用快照、备份或版本控制。
- 加载前审阅 extension 与 package(它们在 Pi 进程内执行)。
- 偏好窄范围、短期凭据。
- 把 diff 与生成结果审过再应用到其他系统。
- 导出/分享会话前审阅(可能含 prompt、工具参数、命令输出、文件内容与对话中暴露的凭据)。
8. 安全报告边界
官方安全策略说明:本地 agent 的预期行为、不可信内容引起的 prompt injection、缺乏内置沙箱、以及用户自行安装的扩展/技能的行为,一般不属于安全边界内——除非能证明存在权限边界绕过或访问了本地用户本不该拥有的东西。
溯源
- 官方
docs/security.md、docs/cli.md、docs/settings.md、docs/containerization.md(隔离实操见官方文档)