认识 Jev:以类型而非文字作答的第二意见
Agent! 现在会在执行 shell 命令之前询问 Jev(TypeSafe System One)该命令的破坏性有多大。本文解释为什么 Jev 是一个顾问,而不是又一个 LLM 提供商。
今天,Agent! 新增了一层安全机制:Jev,即 TypeSafe System One 的简称。在 shell 命令运行之前,Agent! 现在可以向 Jev 提出一个问题:这条命令不可逆地破坏数据的可能性有多大?一旦超过你设定的阈值,该命令就会被拒绝。
这项功能随提交 337c94a2“添加 Jev(TypeSafe System One)决策层”一同落地。本文将介绍其中最重要的设计决策:Jev 不是又一个 LLM 提供商。
为什么不直接问另一个模型?
Agent! 已经可以对接 23 个 LLM 提供商。最省事的做法是再加一个“安全模型”作为提供商,用自然语言问它某条命令看起来是否危险。问题在于它返回的是文字。“这条命令可能存在风险,具体取决于……”并不是一个结论。最终你只能靠解析句子来决定是否执行 rm。
Jev 的工作方式不同。它针对某个状态回答类型化的问题,返回 Choice、Score 或 Noul(一种类型化的“无”答案)。它不生成文本,也不生成工具参数。提交信息给出了结论:Jev“为现有的工具循环提供建议,而不是充当一个 APIProvider”。
这种分工就是整个设计的核心:
- 你选择的模型负责行动。它负责规划、调用工具和编写代码。
- Jev 负责判断。它返回一个数字,供代码与阈值进行比较。
它所处的位置
Jev 不会取代任何现有机制。每条 shell 命令的检查顺序如下:
ShellSafetyService.check:硬编码规则,拒绝灾难性命令。不涉及任何模型。- Jev:仅针对已通过第 1 步的命令,作为第二意见,覆盖模式规则无法察觉的情况。
- 执行命令。
从第一天起,这道关卡就覆盖了 ShellTools 中的两条 shell 执行路径(executeTCC 和 executeTCCStreaming)。
绝不拖住你的任务
一个能阻塞代理的顾问,本身也有其危险之处。如果服务宕机,你的通宵任务会卡住吗?答案是不会。JevAdvisor 采用失败放行(fail-open)策略。没有密钥、开关关闭或服务中断,都意味着“不发表意见”,命令会继续执行。
不过,失败放行只有在可观测的前提下才是安全的。在上线后的第一天内,两个后续提交确保了这一点:
- 使顾问可观测(
e25384f3)添加了用量回调和错误上报,因此一次失败的检查会被记录到日志中,而不是悄无声息地看起来像是“安全”。 - 记录判定结果,而不只是记录 Jev 已作答(
d3c47e67)让每次检查都输出实际结果:破坏性风险百分比,以及该命令是被允许还是被拒绝。
客户端是一个真正的包
第一个版本使用的是手写的 HTTP 客户端:用 POST /v1/systemone 提问,用 GET /v1/models 获取模型列表,采用 Bearer 认证,并在收到 429 和 529 响应时进行退避。几个小时之内,9e330357 就将其替换为 TypeSafeKit 包,而 f3a817b8 则把这个包内置到了 Agent 仓库中,使构建永远不必依赖于在线拉取。
如何设置
Jev 位于设置中,拥有独立的 API 密钥,该密钥存储在钥匙串中一个专用的、非提供商的位置。它还提供一个从 /v1/models 加载的模型选择器,以及一个顾问开关。关闭该开关后,Agent! 的行为将与之前完全相同。
为什么这很重要
代理正在各处获得 shell 访问权限,而标准的安全答案是“模型会小心的”。模式规则要好一些,因为它们是可核查的。但它们只了解有人写下来的那些模式。类型化的第二意见在不引入歧义的前提下增加了判断力:它返回一个数字,由代码做出决定。
我们将持续调整默认阈值并关注日志。如果 Jev 拒绝了不该拒绝的命令,或者漏掉了本应拦截的命令,请在 GitHub 上提交 issue,并附上相应的日志行。