pi-steering-hooks 是一个为 pi 智能体提供确定性工具调用护栏的开源项目。其核心思路是:基于提示词的规则(如“永远不要强制推送”)大多数时候有效,而转向钩子(steering hooks)则每次都能生效。钩子在工具调用执行之前进行拦截,确定性地阻止违规行为,并为智能体有正当理由时提供覆盖逃逸通道。此项目受“Strands Agents: Steering Accuracy Beats Prompts”启发。
主要能力
项目的主要能力包括:
- 在每次 bash、write、edit 调用时评估规则,若规则匹配则阻止调用并将原因返回给智能体;智能体看到阻止消息后调整方法。
- 覆盖机制:当规则触发时,智能体可以尝试使用覆盖注释
# steering-override: <rule> — <reason>重试。覆盖被允许通过但会记录到会话以供审计。标记为noOverride: true的规则(如no-rm-rf-slash)无法被覆盖。 - 文件编写护栏(0.3.0 版本新增):引导智能体使用写入和编辑工具(这些工具显示差异并经过 LSP),而不是通过 bash 命令不可见地编写文件。文件编写规则具有两个结构性逃逸通道,以避免阻止合法的生成流水线:
- 脚本模式:编写
tools/refactor.py或scripts/gen.sh,然后运行它。子进程的写入不会被检查。 - 临时目录:重定向到
/tmp/、/dev/、dist/、build/、generated/、out/、reports/、coverage/、target/、.cache/始终被允许。 - 此外还有标准的按命令覆盖机制(
# steering-override: <rule> — <reason>)用于一次性情况。
- 脚本模式:编写
使用方式
- 扩展注册一个
tool_call钩子。钩子评估规则是否匹配工具输入。 - 通过
steering.json配置规则或禁用默认规则。配置文件位置按顺序检查(先找到者优先):项目根目录的./steering.json,然后是全局的~/.pi/agent/steering.json。 - 如果不想使用默认规则,可通过
steering.json禁用。 - 规则可以使用正则表达式(
pattern)或使用测试函数(test)形式,二者互斥;若规则同时具有两者或两者都没有,compileRule会抛出错误。buildRules在第一个格式错误的规则处抛出;buildRuleSet跳过它并返回{ rules, errors },这是扩展使用的。readConfig返回解析错误而不是记录它们。函数形式的规则只能在代码中添加,不能从steering.json添加(因为它是 JSON)。 - 程序化 API:该包导出其内部组件,以便其他扩展可以基于此构建。
配置与容错
- 破损的配置永远不会阻止 pi 启动。不是有效 JSON 的文件会被跳过,并使用下一个位置(或默认规则)。具有无效正则表达式、既没有
pattern也没有test的规则会被跳过;rules或disable不是数组则被忽略,因此字符串形式的disable不会关闭任何规则。所有其他规则仍然适用。每个问题在会话启动时作为通知显示。在非交互式 TUI(打印、json 和 RPC 模式)中,它也会打印到 stderr。 - 如果规则在评估时抛出异常,则视为不匹配,并以相同方式报告,每个规则每个会话一次。
heredoc 处理
项目特别指出 heredoc-stripping 的重要性:对一个包含 if x > 80: 的 python3 - <<'EOF' ... EOF 进行简单的 > 扫描,会错误地将 Python 比较当作到名为 80: 的文件的 shell 重定向。导出的 stripHeredocBodies 帮助函数在元字符扫描前移除 heredoc 正文(保留开启行,其中经常包含真实的 shell 重定向)。同样的逻辑适用于 stripQuotedStrings,用于处理 grep '>' file 风格的误报。该回归案例已在 index.test.ts 中覆盖,防止再次发生。
适用场景与限制
该项目适用于希望严格约束 pi 智能体工具调用行为的开发场景,尤其是需要防止危险命令(如强制推送、rm -rf /)或引导文件写入方式的情况。它通过钩子实现确定性执行,不消耗 token,避免提示词漂移。限制方面:规则只能拦截 bash、write、edit 调用;函数规则仅限代码添加;配置文件必须位于指定位置;不适用于非 pi 智能体的项目。