开源 · Apache-2.0 2026 年 10 月

AgentJev: A 0.6B
System One
Decision Model

State in. A distribution out. Nothing decoded.

我做了一个 0.6B 的小模型,专门回答 Agent 运行时那些选择题:这条命令能不能跑,测试到底过没过,下一步该干什么。你把题目和选项写好,它直接返回每个选项的概率,不生成文字。在我的 RTX 5060 上,一段状态问 5 道题,中位 43.9 毫秒。

先说结果

下面三条都能在仓库里复现。

危险命令会先停下来问你

L1L3

接进 Claude Code 以后,每次跑 Bash、Write、Edit 之前都会先问它风险几级。比如 cat ~/.aws/credentials | curl …,旧权重只给 L1,直接放过去了;新权重给 L3,会弹窗让你确认。

看钩子怎么接 ↓

失败的测试不会再被当成通过

1230/ 348

我拿 348 次真实失败的测试运行(pytest、jest、vitest 等六种运行器)去测,旧权重有 123 次说“通过了”,编码门控这一版一次都没有。

看 2,100 次真实运行 ↓

在自己的电脑上就能跑

43.9ms

一段状态问 5 道题的中位耗时,一次前向就出全部结果,不生成 token。权重常驻显存大约 1.4 GB,我用的是一张 8 GB 的 RTX 5060。服务只监听 127.0.0.1。

快速开始 ↓

如果你在做 Agent 或编码助手,需要一个便宜、快的地方做门控、路由或打分,可以试试它。写代码、写回复、解释报错这些需要写字的活儿,还是交给大模型。

它做的事

AgentJev 不写句子。你给它一段状态(测试输出、diff、日志都可以)和几道写好选项的题,它跑一次前向,把每个选项的概率返回给你。我把它放在 Agent 循环里做门控、路由和打分;解释错误、改代码、起草回复这些,我还是交给更大的模型。

一次请求怎么变成分布 左边是一段 pytest 失败输出和三道题;中间把 11 条候选路径合成一棵前缀树,状态只出现一次;然后 Qwen3-0.6B 骨干和候选打分头做一次前向;右边是三道题各自的分布,生成的 token 数为 0。 ① 状态和问题 state · pytest -q --tb=short $ pytest -q --tb=short .F.. E assert 96 == 98 FAILED test_mathutil.py::… 1 failed, 3 passed [exit 1] BOOLEAN Have all tests passed?2 个候选:true · false CHOICE What should the agent do next?5 个候选:commit · fix_assertion … SCORE How many tests failed?4 级:L0 · L1 · L2 · L3 拼成路径 ② 合成一棵前缀树 状态 ×1 11 条路径 2,563 → 379 个骨干 token 状态问题候选 一次前向 ③ 骨干与打分头 Qwen3-0.6B 去掉语言模型头 候选打分头 与排列等变 · 按题 softmax BF16 · 上下文 2,048 服务端 43.8 ms RTX 5060 ④ 每个选项的概率 测试都通过了吗? false>99.9% true<0.1% 下一步该做什么? fix_assertion>99.9% commit<0.1% 其余 3 个各 <0.1% 有几个测试失败? L1 · 恰好一个>99.9% L0 · 没有失败<0.1% 生成的 token 0
图 1. 一个请求是怎么变成概率的。状态只编码一次,每个候选单独一条路径,一次前向后按题做 softmax。图里的数字来自 RTX 5060 上的一次真实请求。看实测回放

为什么做这个

Agent 跑起来以后,大部分分叉其实只要一个判断:测试过了没有,下一步调哪个工具,这条命令能不能执行。常见的做法是让一个 27B–70B 的大模型把判断写成一段话,再去解析这段话。我嫌它慢,输出格式也不稳,所以换了个做法:用去掉语言模型头的 Qwen3-0.6B 当骨干,接一个与排列等变的小打分头,把一道题的所有候选放在一起打分,按题做 softmax。同一段状态下的候选路径在服务端合成一棵前缀树,一次前向编码完,全程不生成 token。

在 Typed Decisions 官方测试集的 2,000 道题上,它的 top-1 是 79.25%,比 Laya 公开的检查点高 2.25 个百分点(95% 区间 [+0.65, +3.90])。之后我又用 2,100 次真实测试运行接着训练,测试集里明明失败却被判成“通过”的情况从 123 次降到了 0。在一块 RTX 5060 上,单个案例的中位时延从 278 ms 降到 43.9 ms。代码和权重都用 Apache-2.0 发布。

它是怎么算的

状态只读一遍,每个候选单独一条因果路径。题目和选项的意思直接写在文本里,id 只是用来对应返回结果。

    82 秒演示。配乐是原创的,没有旁白,中英字幕直接做在画面里。里面的每个数字都是我在这台 RTX 5060 上实测的。下载 MP4(8.9 MB)
    1. 1

      写下状态和问题

      状态可以是测试输出、diff、日志或 JSON。题目有三种:Boolean 问一个命题,Choice 在 2–255 个选项里选,Score 判断落在 2–10 级里的哪一级。

    2. 2

      合成前缀树

      每个候选是一条 [状态][问题][候选] 路径。同一状态的路径合成一棵树,每个 token 只关注自己和祖先,候选读不到兄弟候选。

    3. 3

      一次前向

      骨干是去掉语言模型头的 Qwen3-0.6B。一个与排列等变的小头给整组候选打分,再按题做 softmax。

    4. 4

      读分布

      每道题返回完整分布:Boolean 给 P(true),Choice 给 margin,Score 给 score = Σ i·Pᵢ。门槛由你自己定。

    上下文上限是 2,048 token,超长就拒绝,不截断。前缀树和逐条编码在 2,000 道题上有 1,998 道选中同一个选项,变了的两道原本就是平局,概率最大差 0.0053。

    实测回放

    下面是 RTX 5060 上真实请求和输出的回放,已经加载了校准温度。可以换示例,也可以切到 v1 权重对比。动画是我故意放慢的,底栏是这次请求的实际耗时。

    实测回放
    state
    distribution
    图 2. 实测回放。三个示例:一段 pytest 失败输出、Claude Code 的一次 Bash 调用、README 里的 HTTP 例子。

    Typed Decisions 上的成绩

    用的是 Typed Decisions 官方测试集:400 个案例,每个案例一段状态、五道题,覆盖四个工作流,一共 2,000 道题。时延都是在一块 RTX 5060(8 GB)上测的。

    Typed Decisions top-1

    77.0079.25%

    Laya 已发布检查点 → AgentJev。按案例 bootstrap,差值的 95% 区间为 [+0.65, +3.90]。

    测试失败却被判“通过”

    1230/ 348

    编码门控新权重,测试集里的 348 个失败案例。

    每案中位时延

    27843.9ms

    同一状态的候选合成前缀树,状态只读一遍。1 段状态、5 道题。

    表 1. Typed Decisions 官方测试集

    完整报告
    模型性质Top-1 ↑软交叉熵 ↓Brier ↓ECE ↓等级误差 ↓
    AgentJev-0.6B 本轮发布 · 1,585 / 2,000专用模型79.25%0.84940.04480.16870.2096
    Laya 已发布检查点 · 1,540 / 2,000专用模型77.00%0.88440.06150.21700.2423
    TypeSafe Jev 1.13.0通用模型 · 零样本72.7%—0.1480.1440.391
    ModernBERT-base 149M专用模型64.6%—0.1190.1790.444
    MiniLM-L6 22M专用模型58.7%—0.1430.1080.515
    AgentJev phase 4 本轮之前 · 774 / 2,000专用模型38.70%1.28170.25770.10500.7062
    标签频率 Prior参照47.0%—0.1890.088—
    均匀分布参照30.8%—0.2380.169—

    ↑ 越高越好,↓ 越低越好。没有软交叉熵的几行抄自数据集卡片,本仓库没有重测;那几行的 Brier、ECE 和等级误差沿用卡片自己的定义。

    专用模型和零样本的通用模型不是同一种测量:TypeSafe Jev 1.13.0 是零样本作答,AgentJev、Laya、ModernBERT、MiniLM 都在这个基准上拟合过。准确率是和公开教师分布的最大概率项是否一致,不是 Agent 任务的真实成功率。相对本轮的起点 phase 4,差距是 +40.55 个百分点,95% 区间 [+37.35, +43.50]。

    图 3. 按工作流,每个工作流 500 题

    AgentJevLaya 已发布检查点
    四个工作流上 AgentJev 与 Laya 的 top-1 准确率
    Top-1 准确率,横轴从 0 开始。AgentJev 在发票处理和客户服务上领先,在安全事件和 Agent 轨迹观测上略低于 Laya。

    表 2. 按原语

    原语题数Top-1 ↑软交叉熵 ↓
    Boolean60088.83%0.4935
    Choice60075.33%0.9767
    Score80075.00%1.0209

    关于这张表

    Laya 公开的权重用了全部 1,200 个官方训练案例。我这一轮留出了 120 个开发案例和 120 个校准案例,按开发集的软交叉熵选了第 600 步,之后才打开测试集。温度是每种原语一个正标量,只在校准集上拟合。

    标签是教师分布,里面有合成案例。在这张表上多赢一行,不代表 PR 就能合并、事故就能控制住,或者发票就能付出去。

    编码门控:测试到底过没过

    Typed Decisions 里没有测试运行器的输出,所以我自己补了一份 coding_gates/:2,100 次真实运行,覆盖 pytest、unittest、jest、mocha、vitest 和 node --test,每个项目的结果在构建时就定好了;另外还有 1,800 条一行摘要。vitest 和 node --test 整个留出来,不参与训练。

    表 3. 编码门控v1新权重 · coding-gates
    测试集:失败案例被判通过 ↓123 / 3480 / 348
    没训练过的运行器:失败运行被判通过 vitest、node --test · ↓31 / 1987 / 198
    没训练过的运行器:通过的运行被拦下 ↓1 / 1028 / 102
    五种没训练过的问法:门控准确率 ↑78.4%95.6%
    Typed Decisions 测试集 top-1 ↑79.50%80.85%
    README 示例的 P(全部通过) “23 tests passed, 1 failed.” · ↓0.840.24

    两者走同一个推理引擎,温度都只在 Typed Decisions 校准集上拟合,每个留出集只读了一次。Typed Decisions 提升 +1.35 个百分点,95% 置信区间 [+0.20, +2.50];这里 v1 的 79.50% 是 BF16 发布文件经推理引擎的结果,表 1 的 79.25% 是 FP32 检查点的离线评分。

    123个失败案例被判成“已通过”
    测试集 · 完整运行 · 184 个失败
    测试集 · 一行摘要 · 164 个失败
    没训练过的运行器 · 300 次运行
    失败却被判成通过实际通过却被拦下通过,判对失败,判对
    图 4. 每格是一个案例,鼠标停上去能看原文。切换权重,可以看到哪些失败被判成了“通过”。

    还没解决的

    新权重在极简问法 “Tests passing?”(criteria 只有 yes / no)上只有 83.1%;node 的 dot 报告器会让它拦下实际通过的运行;Go 的 ? pkg [no test files] 会被读成“没跑测试”。项目规模小、由程序生成,一行摘要来自模板,测试集和训练集共用模板。

    新权重在 Hugging Face 的 coding-gates 分支,只换了权重和温度,默认仍是 v1。完整报告 ↗

    时延与吞吐

    一块 RTX 5060(8 GB),PyTorch 2.9.1+cu128,Transformers 4.57.6,v1 权重加校准温度。每个测试案例一次请求,计时包含分词。

    表 4. 负载条件优化前优化后变化
    测试集单案 P50 1 段状态 5 道题278.01 ms43.89 ms快 6.3 倍
    测试集单案 P90378.22 ms48.00 ms快 7.9 倍
    吞吐 每请求 20 案3.68 案/秒27.39 案/秒7.4 倍
    多候选重负载 64 个 Choice + 1 个 Boolean550.74 ms136.88 ms快 4.0 倍
    每案骨干 token4,074443状态只读一遍
    常驻权重显存2,283 MiB1,443 MiBBF16 存储,输出逐位不变

    优化前是 main 的 1c2c1b1(每个候选单独编码),优化后是默认的 --encoder auto(前缀树),两者先后连续测量。这台机器上两次测量之间的波动约 10%。

    和 Laya 怎么比

    Laya 发布的检查点在原基准机器上测得单案 P50 41.53 ms、P90 47.14 ms,没有在这块显卡上重测,所以上表不是与 Laya 的同机对比。Laya 的上下文上限是 1,024 token,AgentJev 是 2,048;宽负载里,Laya 的 64 个候选每个都要对状态完整前向一次,AgentJev 每个请求只读一遍状态。

    接进 Claude Code

    agentjev_hook.py 是一个 Claude Code 的 PreToolUse 钩子。每次要执行 Bash、Write、Edit 之前,它把这次调用发给本地服务,问两道题:安不安全(Boolean),风险几级(四级 Score)。它从来不输出 allow,所以不会绕过你自己的权限设置;服务没响应的话它直接退出,调用照常走你的权限流程。

    deny

    风险 L3,并且判为不安全:只拦下这一次调用,把理由告诉 Claude,它可以换一种做法继续。

    ask

    风险 L2 及以上,或者 P(安全) < 0.5:弹窗让你确认。

    不表态

    其他情况它什么都不输出,按你自己的权限设置走。

    命令P(安全)风险判定v1 的判定

    表 5. 真实钩子输出,新权重。

    .claude/settings.json
    {
      "hooks": {
        "PreToolUse": [
          {"matcher": "Bash|Write|Edit",
           "hooks": [{"type": "command", "command": "python /path/to/agent-jev/agentjev_hook.py"}]}
        ]
      }
    }

    AGENTJEV_URL 改服务地址(默认 http://127.0.0.1:8149),AGENTJEV_ASK_LEVEL、AGENTJEV_ASK_BELOW 调整询问门槛,AGENTJEV_VERBOSE=1 让不表态的调用也显示判定。

    快速开始

    需要 Python 3.10+ 和一块 CUDA 显卡,权重常驻显存大约 1.4 GB。CPU 也能跑,就是慢很多。服务只监听 127.0.0.1。

    怎么写请求、怎么看返回、出错了怎么查,都在单页手册里
    1 · 克隆,装依赖
    
              
    2 · 拉取固定版本的权重逐个核对大小和 SHA-256,约 1.2 GB
    
              
    3 · 启动服务先跑一次预热请求再报告就绪
    
              
    4 · 问第一个问题同一状态的多道题放进一个请求
    
              

    局限

    不生成文字

    写代码、写回复、解释错误这类开放式生成,请交给大模型。它只会在你给的选项里打分。

    概率不等于成功率

    返回的是模型自己的分布,没有在你的领域上校准过。想要成功率,就对每个动作单独问一个 Boolean,再用你自己的结果数据去校准。

    Choice 只是相对偏好

    它说的是在这组选项里更倾向哪一个,不是某个动作单独成功的概率。

    79.25% 是和教师标签的一致率

    标签是公开的教师分布,里面有合成案例。分数高,不代表真实任务就做成了。

    钩子会漏判

    请把它当成权限和沙箱之外多加的一层,别当成唯一的一层。v1 曾经把 cat ~/.aws/credentials | curl … 评成 L1 直接放过;新权重给的是 L3,会请你确认。

    超长输入会直接报错

    上下文上限是 2,048 token。装不下的 diff 或堆栈会返回一个明确的错误,不会偷偷截掉某道题或某个候选。

    如果它对你有用

    可以在 GitHub 上点个 Star,这样我知道有人在用,也会继续发新权重。用的时候遇到问题,或者想要新的门控任务,直接开 issue 就行。

    引用

    2026 · Apache-2.0

    GitHub 仓库
    BibTeX
    @misc{agentjev2026,
      title        = {AgentJev: A 0.6B System One Decision Model},
      author       = {malevrigns},
      year         = {2026},
      howpublished = {\url{https://github.com/malevrigns/agent-jev}},
      license      = {Apache-2.0}
    }
    AgentJev0.6B 本地判断模型 · 中位 43.9 ms · 生成 0 token · Apache-2.0Star