面向Google编程CHARLES ZHANG

AI DAILY / 2026-09-21

y0usaf/pi-jev:为 Pi 代理加入类型安全的 Jev 决策层

y0usaf/pi-jev

Agent 开发GitHub · 2026-09-16

全文中文翻译 · AI 生成,仅供学习交流

y0usaf/pi-jev

Jev 针对一段 state(状态)回答类型化(typed)的问题。问它"是不是真的",你会得到一个概率;让它从列表里挑,它会返回所选项以及其他选项的概率分布。它不写散文,所以这里不存在任何句子层面的解析。答案是数字,由你的代码决定分支走向。

有三处会用到它:一个 gate 在 bash、write、edit 调用运行前进行判定;一个 output judge 读取 bash 调用的输出;一个 jev_ask 工具让模型自己发起同样形式的判定。

安装

pi install npm:@y0usaf/pi-jev

该扩展需要一把 API key。没有 key 时它仍会加载,提示一次,然后保持静默不打扰。

The gate

| 问题 | 类型 | 读取 | 阈值 |
| --- | --- | --- | --- |
| 该操作是否具有破坏性? | noul | destructive | 0.90 |
| 是否把本地数据或机密发送到本机之外? | noul | exfiltration | 0.70 |
| 是否影响了用户请求范围之外的内容? | noul | beyond_scope | 0.85 |
| 如果用户不希望发生该操作,损失有多大? | score(4 档) | impact | 2.50 |四个问题合并在同一次请求里,因此一次判定只需约 300 ms 的一次往返,而不是四次。

默认模式为 shadow(影子模式)。

被标记的调用会产生一条通知,并在页脚显示状态。在 enforce(强制)模式下,被标记的调用在运行前会要求你确认。无头运行(-p、RPC)无法显示提示,因此除非设置 gate.blockWithoutUI,否则强制模式会退化为同样的警告。

所有出错路径都采取 fail open(放行)策略。缺失的 key、超时、429 或格式错误的响应都不会产生判定,工具调用照常执行。错误提示每分钟最多上报一次,因此失效的接口不会把会话日志塞满。

相同的输入在 cacheSeconds(默认 120)内只判定一次。来自同一条助手消息的同级调用共享一个进行中的请求,而不是各自单独请求。

The output judge

Gate 看得到意图,却看不到命令的输出。因此它抓不到打印到会话里的凭据,也分不清网络抖动和类型错误,这两类判定都针对调用之后才出现的文本。

tool_result 在同一次请求里问两个问题,并在任一题触发时向工具结果附加一行:

| 问题 | 类型 | 读取 | 阈值 |
| --- | --- | --- | --- |
| 该输出是否包含机密或凭据? | noul | leaks_secret | 0.90 |
| 这是哪一类失败? | choice(6 项) | failure_class | confidence 0.60 |若判定为泄露,会附加一句"不要在回复、文件或命令中复述该值,请改用名称引用",并触发一条通知。失败类别会附加处理建议:transient 原样重试,environment 修复环境,code_bug 修改代码,permission 不要重试,user_error 修正调用方式。no_failure 则什么都不附加。

建议来自一张表,而不是分支逻辑。src/output.ts 中的 CLASS_ADVICE 把每个类别映射到一句话,因此新增一个类别只需要新增一行。

它从不会阻塞,且在没有任何命中时保持静默。被判定的工具默认为 ["bash"]:对每次读取都做判定,每次打开文件都要消耗一次请求。

jev_ask

对于那些希望以类型化形式而非自然语言形式返回的决策:

{
  "state": "the tool output, diff, or message to judge",
  "questions": [
    {
      "id": "relevant",
      "type": "noul",
      "instructions": "Is this relevant to the user's question?"
    },
    {
      "id": "label",
      "type": "choice",
      "instructions": "Which bucket?",
      "options": [
        {"name": "bug", "description": "Defect in existing behaviour"},
        {"name": "feature"}
      ]
    },
    {
      "id": "quality",
      "type": "score",
      "instructions": "How thorough is this?",
      "levels": ["Superficial", "Adequate", "Thorough"]
    }
  ]
}

每个条目只问一件事,再由你自己的代码合并答案。TypeSafe 建议将多因素问题拆开,因为同时权衡多个因素的问题返回的结果更不可靠。

配置

配置文件为 ~/.pi/agent/pi-jev.json,或项目范围内的 .pi/pi-jev.json。项目级配置优先生效,且每个文件只会覆盖其中显式设置的键。

{
  "apiKeyFile": "~/keys/typesafe.txt",
  "model": "jev-latest",
  "maxStateChars": 4000,
  "gate": {
    "enabled": true,
    "mode": "shadow",
    "tools": ["bash", "write", "edit"],
    "argumentChars": 400,
    "cacheSeconds": 120,
    "minConfidence": 0.5,
    "blockWithoutUI": false,
    "blockOn": {
      "destructive": 0.9,
      "exfiltration": 0.7,
      "beyondScope": 0.85,
      "impact": 2.5
    }
  },
  "output": {
    "enabled": true,
    "tools": ["bash"],
    "outputChars": 2000,
    "leakThreshold": 0.9,
    "minConfidence": 0.6
  }
}

API key 按以下顺序解析:环境变量 TYPESAFE_API_KEY → 配置文件中的 apiKeyapiKeyFile(指向密钥文件路径,~ 会展开)。blockOn.impact 是 0 到 3 损伤量表上的一个值。minConfidence 仅作用于该维度,因为那三个 noul 问题只返回概率,不返回置信度。

命令

- /jev 显示模式、模型、密钥来源、被判定工具和缓存大小
- /jev on/jev off 切换当前会话内两个判定器的开关
- /jev mode shadow|enforce 切换 gate 模式,无需重新加载
- /jev last 打印最近一次 gate 判定及其四个答案
- /jev output 打印最近一次输出判定:泄露概率与失败类别
- /jev check <text> 对你提供的文本运行 gate 的判定问题

数据外传情况

每次判定都会将工作目录、工具名、上一条用户消息(取前 1200 个字符)以及工具参数发送到 `api