作者复盘从 Agent OS 到 Agent CLI 的开发历程,核心心得:Agent 的信任建立在证据、权限隔离、可验证性和系统性边界上,而非功能数量。
在过去的一个月里,我一直在开发一个名为 Agent CLI 的开源项目。
如今它是一个本地优先、终端原生的 agent 框架,可以跨代码、shell 命令、Git、浏览器自动化、研究、DOCX/PDF 以及远程交付等工作。
但它的诞生并非因为我想要做又一个编程 agent,而是源于一个更简单的问题:
如果一个 AI agent 我不那么在意它有多少能力,而更在意它实际做了什么——这件事我能不能信任——那会是什么样?
这个问题最终塑造了整个项目。
在过度建设之后重新开始
在 Agent CLI 之前,我已经构建了一个更大的本地 AI 系统 called Agent OS。
它探索了多 agent 编排、记忆、Git 工作流、部署、浏览器自动化、恢复、研究、技能和图形界面。
这是一个有用的学习项目,但也暴露了一个问题:在架构保障尚未完善之前就快速添加能力,会让 agent 看起来比实际更成熟。
精心打磨的 UI 可能掩盖模糊的边界;确认提示可能被误认为是真正的沙箱隔离;大型功能增量越来越难以审查;模型生成的可信摘要终究只是摘要。
因此,我没有继续扩展同一个系统,而是在一个更狭窄的命题上启动了 Agent CLI:
先构建一个可信的执行内核,workflow 的扩展留到以后。
基本循环变成了:理解 → 规划 → 执行 → 观察 → 验证 → 记录 → 恢复
这个设计原则逐渐演变成对几乎所有新增功能的检验标准。
Agent 不等于 LLM 加上工具
有趣的工程问题不在于模型本身。
一旦 agent 可以修改文件、执行进程、访问网络或发布代码,重要的部分就变成了模型周围的执行框架。
模型可以提议一个动作;运行时决定它是否被允许。
模型可以说测试通过了;运行时应该知道是哪个进程运行的以及退出码。
模型可以说文件已修复;运行时应该知道哪些字节发生了变化,以及之后是否进行了验证。
这引出了一条几乎在 Agent CLI 每次迭代中都保留下来的规则:
模型可以推理现实,但不应该定义现实。
在可能的情况下,现实来自文件、哈希、进程结果、diff、Git 状态、浏览器观察以及其他外部证据。
证据优于叙述
Agent 运行自然会生成有说服力的叙述:
实现了功能、修复了 bug、所有测试通过。
但许多微妙的失败可以隐藏在这句话背后。
也许测试是在最终编辑之前运行的;也许它在错误的包里运行;也许命令超时了;也许模型将部分输出解释为成功。
因此,Agent CLI 将重要操作记录到一个只追加的事件日志中:批准、命令、文件变更、哈希、验证结果以及其他后果。
报告从这些证据中导出,而不是从模型的记忆重建。
验证也是有时序的。
文件只有在相关的最后一次变更之后真正的验证过程成功时,才被认为是已检查的;一个项目中的成功构建不能为另一个项目中的变更提供证明;一个没有退出码的被终止命令不能悄然变成通过。
这听起来像是实现细节,但它显著改变了架构。
对话是上下文。模型的解释是解读。执行记录是证据。
这是三件不同的事。
权限、批准和沙箱隔离是不同的课题
另一个教训来自尝试定义"安全执行"的实际含义。
Agent 工具通常暴露宽泛的模式,如安全、自动或完全访问。它们很方便,但可能隐藏实际授权了什么。
Agent CLI 改为通过集中式策略门来评估单个操作。
工具声明其效果的事实——运行命令、变更文件、远程读取、外部发送数据——运行时决定该操作是被允许、需要批准还是应该拒绝。
这也迫使我将三个之前处理得过于宽松的概念区分开来:
信任:在一个工作空间内操作的权限。
批准:对特定有后果的操作的授权。
沙箱隔离:由操作系统执行的技术强制。
确认提示不等于沙箱隔离。工作空间路径检查不等于沙箱隔离。信任一个项目不意味着批准每一个未来的操作。
Agent CLI 目前在 Windows 上对符合条件的自动运行命令有真正的操作系统级沙箱隔离,使用 Low Integrity 和 Job Object。但这个边界的描述是有意狭窄的:它不防止读取,也不控制网络访问,明确批准的命令在沙箱外运行。
在没有相同强制执行的平台上,命令自动运行会fail closed,而不是假装保证是等效的。
这产生了另一个我现在在意的原则:
安全降级应该是可见的降级。
一个诚实但弱的边界比一个听起来很强但虚假的标签更有用。
可逆性是一种被低估的自主形式
随着 agent 获得更多权限,显而易见的解决方案是更频繁地要求用户确认。
但一个在每个小编辑之前都要询问的 agent 很快就不再像一个 agent,更像是一个你需要一直照看的婴儿。
对于许多本地操作来说,一个更好的解决方案是让它们变得便宜可逆。
Agent CLI 在应用变更之前对工作空间变更进行快照。/undo 可以在之后恢复它们,并拒绝覆盖已漂移的文件。更大的恢复点可以使用隐藏的 Git 引用,而不需要移动用户的分支或 HEAD。
变更委托的 agent 在一次性 Git worktree 中工作,它们的变更只通过显式集成到达主工作空间。
这将问题从:
agent 是否被允许修改这个文件?
转变为:
这个操作是否可以变得足够可逆,使得事后审查比事前中断更便宜?
并非每个操作都适合这个模型。发布、破坏性操作和敏感的外部效应仍然值得更强的批准边界。
但对于普通的本地工作,可逆性通常可以购得有用的自主性,而不是简单地移除控制。
通用目的不等于即兴发挥一切
Agent CLI 始于一个编程 agent,但我想知道它的架构实际上是通用的还是仅仅是编程特定的。
DOCX 和 PDF 成为早期测试。
而不是创建一个单独的"文档 agent",相同的运行时编写结构化规范,确定性软件渲染制品,结果被解析回来进行验证,页面可以被光栅化,具有视觉能力的模型可以检查真实输出。
这引出了一个更广泛的想法:
许多高质量的 agent 工作流在语言和效果之间需要一种中间表示。
代码已有源文件。文档可以有文档规范。幻灯片可以有布局模型。视频可以有剪辑决策列表。部署可以有类型化的期望状态。
模型处理歧义和高层次决策。确定性软件处理可重复的转换和验证。
这对我来说比让模型每次都即兴重新发现一个临时工作流要更有前景。
更多的 agent 不等于更好的 agent 系统
Agent CLI 最终获得了规划、委托研究、审查和隔离的变更 worker。
但构建这些功能实际上让我对"agent 团队"这个比喻越来越不感兴趣。
一旦实现变得具体,多 agent 执行看起来更像是熟悉的系统问题:我们如何委托有边界的工作、隔离并发变更、保留溯源、并安全地集成结果?
子 agent 应该继承相同或更窄的权限。变更中的 worker 不应该随意共享父级的工作空间。批准的计划应该引用特定内容,而不仅仅是旧的对话中的"是"。
这些边界比终端里出现多少个 agent 更重要。
核心教训:在增加智能之前先消除歧义
Agent CLI 现在是一个相当规模的项目:超过 50,000 行 TypeScript 源代码、超过 2,400 个密封测试、五个模型提供者、多个工作流领域,以及一个在真实软件工作上端到端运行过的运行时。
但最有价值的改进很少来自添加更多功能。
它们来自用精确的概念替换模糊的概念。
"安全命令"变成了操作契约加上策略加上执行边界。"已验证"变成了相关变更后的成功进程。"计划已批准"变成了对特定语义内容的批准。"记忆"变成了不携带任何权限的上下文。"多 agent"变成了有边界的委托和隔离的工作空间。"完成"变成了应该由证据支持的东西。
这改变了我在 agent 行为不可靠时问的问题。
不再是立即问:
我应该如何改进 prompt?
而是问:
模型目前正在解决的什么歧义,系统本可以替它解决?
有时候模型真的应该推理它。
但令人惊讶的是,更好的答案往往是一个类型、一个状态机、一个哈希、一个退出码、一个沙箱边界或一个确定性渲染器。
Agent 工程日益成为系统工程
模型会越来越强大。
但更强大的模型也意味着我们将把更多重要的工作委托给它们,这使得周围的框架变得更加重要,而不是更不重要。
对我来说,有趣的问题越来越多的是:可能会发生什么?实际发生了什么?我们怎么知道它成功了?如果执行到一半失败了会发生什么?其他人能否检查证据并得出相同的结论?
这些并不完全是新问题。
操作系统、数据库、构建系统、版本控制、安全工程和分布式系统几十年来一直在处理它们的各种变体。
模型是新的。但围绕它的许多工程问题并不是。
Agent CLI 仍然是一个实验,而不是我找到了"正确"架构的主张。
该项目在 MIT 许可下完全开源,我分享它主要是作为学习过程的一部分,而不是商业发布。
如果你也在研究编程 agent、本地 AI 系统、沙箱隔离、验证、agent 运行时或相关基础设施,我真诚地欢迎批评或替代方法。
GitHub: github.com/earthwalker17/agent-cli
Issues、PR 和技术讨论都欢迎。