先说结论

这个飞书私聊助手已经实际使用了好几个月。它最初只是一个方便在手机上随手记录和提问的入口,真正让它值得单独写下来的,是长期使用过程中逐渐暴露出来的权限边界问题。

让模型「少做危险的事」做不到,锁死「它能碰到的范围」可以。

这个项目把 Pi Agent 接到飞书私聊,但真正花功夫的不是接入,而是边界:工作区固定、path 参数过检、bash 命令拦截三道防线,全部在工具层完成,不依赖模型的自觉。

它和常见的 Code Agent、Assistant 不在一个赛道:响应快,适合随手用,比如记点子。

目录

1. 它适合什么场景

这个助手的定位不是「主力开发者」,而是「随手可用的帮手」。

和 Code Agent 那种一跑十几分钟、一个完整任务的调性不同,它坐在飞书私聊里,反应速度非常快,所以适合那些不值得开一个正式任务的小事:

  • 某个时刻脑子里冒出一个点子,想快速记下来——发一句「记一下:XX」,它写进 NOTE。
  • 一个简单问题、一段临时文本整理,随手问随手答。
  • 不用切换终端、不用打开 IDE,手机上和办公聊天混在一起顺手完成。

场景越小、越临时,它越显得顺手。简单任务交给快助手,重磅活再开 Code Agent,这是这套组合的用法。

2. 手机上的 Agent 入口

个人 Agent 平时在终端里用,想随手用就缺一个手机入口。IM 私聊是最省事的选择:飞书私聊文本消息接入 Pi Agent,每个用户保留自己的 session,默认继续上一次对话。

但接入本身不值钱,值钱的是边界——不解决边界问题的接入,等于给模型开了一个远程终端。

3. 第一道门:工作区固定

Pi SDK 的工具支持创建时绑定目录:

1
2
read / write / edit / grep / find / ls   →  全部绑定 NOTE_DIR
bash → cwd 固定到 NOTE_DIR

工具从出生起就只知道一个目录,模型无法通过参数把工作范围挪到外面。

提示词里写「请只改 NOTE」是软约束,模型不一定听;工具绑定才是硬约束

4. 第二道门:path 参数过检

文件类工具大多有一个 path 参数。包一层守卫:执行前把 path 解析成绝对路径,仍在 NOTE_DIR 内才放行,越界直接抛错:

1
guardPathTool(execute) → 先检查 path → 通过后才转调原始工具

关键细节是判断「在目录内」用 path.relative,而不是 startsWith。子串前缀不等于目录前缀——startsWith("/tmp/a") 会把 /tmp/abc 也当成 /tmp/a 的内部文件。

5. bash:最难的一关

bash 没有 path 参数,路径藏在命令文本里,静态审计做不到完备。这关只能做折中:

主防线是 cwd——bash 创建时就固定在 NOTE_DIR,模型 cd 不出去就动不了外面。

辅助防线是正则拦截明显越界:../ 父目录引用、cd / 绝对路径、cd ..rm / mv / cp 指向外部路径、cat / sed / awk / head / tail 读外部路径。

明说清楚:这不是通用沙箱,能拦住明显风险;要更强隔离得上容器,第一版不追求。

6. 会话与命令

以飞书用户(open_id)为粒度维护会话:Pi 端保存完整 JSONL 对话,助手自己只存一份轻量索引(当前 session + 历史列表),两者职责分开。

索引损坏时直接抛错,而不是悄悄覆盖——丢失映射最多新建一个,静默弄丢映射才是坏情况。

slash 命令只做六个:/help /new /list /current /resume /stop,未知的 /xxx 不猜。/help 的输出刻意保持简短——手机屏幕上没人想看长文。

7. mock:不连飞书也能验

本地验证是另一个容易忽视的工程点:

1
npm run mock:send -- "/help"

mock 入口绕开真实飞书 SDK,但完整走 AssistantService、命令路由、session 管理和 Pi Runtime。测试同理,通过注入 fake runtime 不依赖真实模型和网络。

接入层和业务层分离之后,边界逻辑可以在没有 IM、没有模型的情况下全部验证。

8. 收个尾

手段 性质
提示词 「请只改 NOTE」 软约束,模型可能不听
工具绑定 创建时绑定 NOTE_DIR 硬约束
path 过检 执行前解析并校验 硬约束
bash cwd 固定 + 正则拦截 折中,拦明显风险

多数 Agent 框架内置了权限与工具隔离能力,但默认往往是全开。接入之前先问「它能碰什么」,比接入之后救火便宜得多。