当 AI Agent 开始在本地创建文档、执行命令和安装扩展时,“模型说任务完成了”已经不足以成为可信结果。真正棘手的问题,是如何验证文件确实生成、如何按任务控制权限,以及怎样限制外部 Skill 带来的风险。通过阅读 OpenWorkBuddy 的源码,可以看到这些问题如何被落实为具体的工程机制。
不是项目推荐文。是读完一个 40 天、159 star 的开源 Agent 后,关于 Agent 工程里几个具体设计取舍的笔记。
先交代一下背景,免得误解。
我最近在翻一个开源项目:OpenWorkBuddy(github.com/CatCatUncle/openworkbuddy),一个本地优先的 AI 办公 Agent。它做的事是:你说一句话,它自己规划、调工具、验收,把 PPT / Word / Excel / 网页这些文件落到你磁盘上。
star 不算多,159 个。但它的代码结构对我有点启发,尤其是一个我原本没太重视的环节——文件验收。
这篇主要聊三件事,都是我觉得值得单独拿出来讨论的设计问题:
先说一个我踩过的坑。
用 Agent 干活,最常见的翻车不是它做不出来,而是它说自己做完了。你去目录里找文件,没有。回头问它,它很诚恳地道歉,然后再说一遍"已完成"。
这个问题的根源不难理解:LLM 的输出是文本,而"文件写了吗"是一个外部世界的事实。这两者之间没有天然绑定——除非你在工程上强制绑定。
大部分 Agent 框架的做法是让模型输出一个工具调用(比如 write_file),工具返回成功,流程继续。这看起来已经闭环了,但中间有一个容易被忽略的缝隙:
工具返回"成功",不等于文件真的在磁盘上。
比如路径写错、目录权限不够、写入被中断、或者工具实现本身有 bug——这些情况下工具可能仍然返回一个看起来正常的结果,模型据此认为任务完成。
OpenWorkBuddy 的做法可以概括成一句话:
模型声称写了文件,就去磁盘上验;验不到,任务不算完成,退回重做。
这是个很朴素的思路,朴素到几乎不值得写出来。但它把一个"信任模型"的问题,转换成了一个"检查事实"的问题。这个转换本身才是关键——因为一旦你决定去磁盘上验,后面所有的工程动作都有了锚点。
我把这个思路抽象一下,它是可迁移的:
| 环节 | 常规做法 | 验收式做法 |
|---|---|---|
| 生成文件 | 工具返回 200 即通过 | 校验目标路径上文件真实存在且非空 |
| 生成图片 | 返回 URL 即通过 | 回读图片尺寸 / 解码是否成功 |
| 生成文档 | 返回"已保存"即通过 | 重新打开文档,核对结构完整性 |
| 修改代码 | 返回 diff 即通过 | 跑一遍编译或测试 |
核心区别是:验收的依据来自被改造的对象本身,而不是执行者的自述。
这个模式我认为普适性很强。任何"Agent 产出物 vs 不确定的执行过程"的场景,都可以套这个结构。而它最大的成本,往往只是多写一个检查函数。
Agent 要干活就得有权限。给权限这件事,我见过两种典型做法。
第一种是二元开关:要么全开(能执行任何命令),要么全关(只能聊天)。这种设计的体验是:全开时你心里发慌,全关时它什么也干不了。
第二种是逐条审批:每一个命令都弹窗问你。安全感拉满,但用半天你就崩溃了——审批疲劳之后你会开始无脑点"允许",安全性反而低于全开。
OpenWorkBuddy 用的是第三种:四档权限档位。
text
plan → 只读不写,只能看和规划
ask → 关键动作前征求同意
auto → 常规操作自动执行,高风险动作仍拦截
full → 完全放开
我觉得这个设计有意思的地方在于:它把"授权"做成了一个随任务切换的连续变量,而不是一次性的开关决定。
这里有个细节值得注意——它不是全局设置,而是每次调用可以单独指定:
bash
openworkbuddy --perm plan "分析这份数据能得出什么结论"
openworkbuddy --perm full "把项目跑起来,自己修错"
差别在哪?我让 Agent 读一篇文章写总结时,plan 就够了,它连写权限都不该有。而让它自动修 bug 时,full 是必要代价。
同一个用户,在不同任务上需要的信任级别完全不同。 把权限绑到用户身上(要么都放开要么都收着),不如绑到任务上。
这个视角我觉得可以推广到很多 Agent 产品的设计里。权限的粒度不该是"这个用户可不可信",而应该是"这个动作值不值得信任"。
另外它还有一层闸门机制,和档位是并行的:命令审批、文件黑名单、URL 白名单、审计日志。
档位管的是"这一轮给多大自由度",闸门管的是"哪些动作无论如何不许做"。档位可以放宽,闸门不能突破——这两层分开,我觉得比揉成一个"安全等级"要清醒。
第三个问题想聊清楚一点,因为它涉及一个很具体的安全设计。
OpenWorkBuddy 的扩展方式是丢一个 Markdown 文件进 skills/ 目录。这是它体验上最舒服的地方——加个能力不用改代码、不用重启、不用打包,存盘后下一条任务就生效。
但这个设计有一个隐含前提:你信任往这个目录里放文件的人。
因为一个 skill 本质上是什么?是把一段陌生人写的自然语言指令,接到一个能在你机器上敲命令的 Agent 上。
这个组合的风险等级,我认为比很多人以为的要高。它不是"读了一段文本"那么简单——文本会变成行动。
所以这个项目在安装前做了一层静态检查(34 条规则),其中 10 条是真拦:反弹 shell、curl | bash、读 SSH 私钥、磁盘擦除、痕迹清除这类直接挡下,其余的把原文摊给用户看。
但真正让我觉得有意思的,不是这个检查器做了什么,而是它怎么说自己。
项目公开写清了自己的检出率:公开标注集上约 74.9% ,判定"不建议安装"的准确率约 60.1% ——说白了,四个漏一个。
并且给了一句我认为很实在的解释:
这就是静态检查的天花板,这也是强装入口必须保留的原因。
这句话把设计的取舍讲透了。
静态检查本质上是在用模式匹配去追一个对抗性的目标——恶意指令可以改写法、可以混淆、可以分散在多个文件里。只要攻击面是对抗性的,检测率就不可能收敛到 100%。承认这一点,然后据此保留"用户强制安装"的口子,同时把风险明确告知——这比宣称"我们全面拦截了恶意插件"要负责任。
我见过不少项目在安全能力上的表述是"已支持 XX 防护",具体检出率多少、漏判率多少,一个字不提。对使用者来说,这种模糊表述其实比明确写"我只有 74%"更难评估——因为你不知道该给它多少信任。
给出可量化的能力边界,本身就是产品成熟度的一部分。
顺带说一个实现细节:它的检查逻辑是基于行为组合而非关键词的。单独的 curl 不算问题,单独的 printenv 也不算;但同一个文件里既读取密钥又向外发送,构成完整外带特征时才拦。
这个思路比单纯的黑名单要聪明——黑名单的问题是误报高、绕过容易,而基于"读敏感数据 + 外发"这个行为组合来判定,鲁棒性会好很多。
写完上面三块,还有一些零碎的点也值得记一笔,不算结论,算观察。
扩展成本的下限。 上面提到的 skill 机制,我认为它对二次开发者的价值被低估了。传统的插件体系要先定义 manifest、注册钩子、走生命周期;Markdown 技能跳过了全部这些,代价是表达力受限,收益是几乎零摩擦。对一个"想让用户自己造能力"的产品来说,这个取舍是划算的。
部署脚本不假装成功。 它的 Docker 部署脚本会等健康检查真正通过才报成功,起不来就把日志打出来。这是个小事,但"部署失败时明确告诉你失败"在很多项目里是要额外做的工作。
运行过程有本地 Trace。 模型、工具、耗时、Token、输入输出全部记在本地,可选接 Langfuse。对调 prompt 的人来说,这个比日志有用。
如果只带走三句话:
不要落在执行者的自述上。模型说完成不算完成,磁盘上文件存在才算。这个原则适用于一切 Agent 产出。
用档位表达连续的信任级别,用闸门守住绝对底线,两者分开设计。
说清"我只有 74%",比说"我已全面防护"更可信,也更有用。
关于项目本身,补几句必要的信息,免得看着像软文。
它 2026-08-10 建仓,最新提交 2026-09-18,40 天内迭代了几十次,star 159。JavaScript 写的,Node 18+ 直接跑,零构建。协议是 PolyForm Noncommercial——个人、学习、非营利用免费,商用要授权。购买授权不解锁功能,因为只有一份代码,没有功能开关和灰按钮。
部署有三条路径:本机装(五步向导)、VPS + Docker(一条命令)、企业落地(SSO / 审计外送 / 内网 / SLA)。
想读代码的话,从 server.js 进去,再看 agent.js 怎么编排模型和工具,主要逻辑不长。
bash
git clone https://github.com/CatCatUncle/openworkbuddy.git
cd openworkbuddy && npm install && npm run app
本文所有数据取自仓库公开信息,获取时间 2026-09-19。
如果你也在做 Agent 相关的东西,尤其对上面"文件验收"那块有别的实践,欢迎评论区聊聊——我更好奇的是别人怎么解这个问题的。