返回博客列表

AgentJev:0.6B 开源决策模型,一次前向 50ms,零 token 解码

2026-09-26T21:30:00+08:00
AgentJevJevSystem One决策模型Qwen3开源训练

AgentJev:0.6B 开源决策模型,一次前向 50ms,零 token 解码

它不会写字。这正是它的价值。

Jev 生态又添一员。AgentJev-0.6B 是一个开源(Apache-2.0)的 System One 决策模型,基于 Qwen3-0.6B 骨干,面向 AI Agent 场景。它的自我介绍很极端:"AgentJev 不写作。你递给它一个状态和你已经措辞好的问题,一次前向返回每个选项的概率。解码 token 数为零。"

基准成绩:Typed Decisions 官方测试集(400 cases、2000 questions)上 top-1 79.25%,超过同赛道的 Laya(77.00%,+2.25 点)和 TypeSafe Jev 1.13.0 的零样本成绩(72.7%)。单次前向约 50-70ms。

这篇文章讲清楚三件事:它是什么、怎么用、以及如果你想训练一个类似的模型,步骤是什么。

本文提纲

  1. 它是什么:给 Agent 循环装"反射弧"
  2. 架构:去掉 LM head,加一个候选头
  3. 三种原语:Boolean、Choice、Score
  4. 基准成绩与 Laya 对比
  5. 怎么用:从下载到门控 Claude Code
  6. 怎么训练一个类似的模型:四步拆解

它是什么:给 Agent 循环装"反射弧"

先看它解决的问题。一个编码 Agent、triage bot 或工作流,大部分步骤消耗在不是写作的问题上:测试过了吗、下一个调哪个工具、这条命令安全吗。通常的做法是让一个 27B-70B 的大模型"把布尔值聊出来",然后解析它说的话。

README 里有张对比表,一刀切中要害:

用"作家"当"开关" AgentJev
输出 你希望是 JSON 的文本 你提供的选项上的分布
解码 一次一个 token 无
失败模式 畸形对象,或一句自信的话 一个可以设阈值的概率
在循环中的位置 整个回合 回合之下的反射

状态可以是 diff、堆栈跟踪、工单线程或表格(序列化成稳定 JSON)。它把大模型从"决策者"位置上换下来——散文交给大模型,反射交给 AgentJev。

架构:去掉 LM head,加一个候选头

技术实现很干净:Qwen3-0.6B 去掉语言模型头,每个候选选项在其最后一个 token 处被读取,然后一个小型的**排列等变头(permutation-equivariant head)**对整个选项集合打分——选项顺序不会偷偷引入排名偏见。Softmax 按问题计算。

三个值得注意的工程属性:

1. 什么都不解码。 隐状态直接到 logits,没有输出词表步骤,所以没有 JSON 需要修复——这就是"零 token"的含义。

2. 上下文 2,048 token,超长直接拒绝。 装不下的 diff 或 trace 会报错,而不是悄悄裁剪问题或候选——对生产系统,显式失败好过静默截断。

3. 共享前缀复用。 状态只编码一次,同问题的所有候选从该前缀分支。官方实测(64 选项 Choice + 1 Boolean,33,547 path tokens):不共享中位数 609.65ms,共享后 298.91ms,最大概率差异 0.000508,选项选择不变——骨干 token 操作从 33,547 降到 2,551,降 92.4%。

flowchart LR
  state["State"] --> enc["Qwen3-0.6B"]
  questions["Boolean, choice, score"] --> enc
  enc --> head["Candidate head"]
  head --> dist["Distribution"]

三种原语:Boolean、Choice、Score

每个原语都返回完整分布,不只是赢家:

Boolean Choice Score
问什么 一个命题 给定这些选项选哪个 在有序量规的哪一档
你发送 可选的 true/false 判据 2-255 个选项(列表或映射) 2-10 档描述,从低到高
你拿回 value、probability、两边的质量 value、top_probability、margin、全分布 level(argmax)、score = Σ i·Pᵢ

一个重要的诚实提醒(README 原话):margin 是最佳选项与第二名的差距。Choice 是"这组选项内的偏好",不是"这个动作会成功的独立概率"。如果你需要后者,为每个动作问一个 Boolean,并在自己的结果数据上校准它。

基准成绩与 Laya 对比

Typed Decisions 官方测试集(400 cases、2000 questions、四种工作流,每状态五问):

模型 类型 Top-1 Brier ↓ Score MAE ↓
AgentJev-0.6B Specialist 79.25% 0.0448 0.2096
Laya(公开 checkpoint) Specialist 77.00% 0.0615 0.2423
TypeSafe Jev 1.13.0 Generalist(零样本) 72.7% 0.148 0.391
ModernBERT-base,149M Specialist 64.6% 0.119 0.444
MiniLM-L6,22M Specialist 58.7% 0.143 0.515

对 Laya 的 +2.25 点优势,400 case 的 bootstrap 95% 区间是 [+0.65, +3.90]。按工作流:发票处理 86.20%(Laya 81.20%)、客服 82.20%(76.40%),安全事件和 Agent trace 上两者互有胜负。

和 Laya 的取舍很清晰(README 说得很坦率):

  • 短问题:Laya 更快(P50 41.53ms vs 约 60-70ms)——它的 encoder 少 1.77 亿参数。
  • 宽候选集(64 选项):AgentJev 快约 2 倍(298.91ms vs 500-600ms)——Laya 的 ModernBERT 骨干没有因果前缀缝,每个候选要完整前向一遍状态文本;AgentJev 用 KV 前缀复用。
  • 上下文:AgentJev 2,048 tokens,Laya 1,024(超长截断或拒绝)。

一个诚实的细节:这个对比里,Laya 在全部 1,200 个官方训练 case 上训练,而 AgentJev 这轮留出了 120 个开发 case 和 120 个校准 case,用开发集 soft CE 选 checkpoint——在测试集打开之前。Specialist 和 Generalist(Jev 1.13.0 零样本)也不是同一种测量。

怎么用:从下载到门控 Claude Code

三步跑起来:

git clone https://github.com/malevrigns/agent-jev.git
cd agent-jev
python -m venv .venv
pip install -r requirements.txt huggingface_hub safetensors

下载权重并包装成 torch checkpoint(服务器要 *.pt 格式):

from huggingface_hub import hf_hub_download
from safetensors.torch import load_file
import torch

src = hf_hub_download("aimeigaoshou/agent-jev", "model.safetensors")
torch.save({"state_dict": load_file(src)}, "agentjev_v1.pt")
hf_hub_download("aimeigaoshou/agent-jev", "temperatures.json", local_dir=".")

启动服务器(只绑定 127.0.0.1):

python -m jev_service.server \
  --checkpoint agentjev_v1.pt \
  --model-path Qwen/Qwen3-0.6B \
  --temperatures temperatures.json \
  --port 8149

注意:model.safetensors 是完整模块(骨干+候选头),不是因果语言模型,AutoModelForCausalLM 加载不了,要用 AgentJevModel + load_state_dict(strict=True),dtype 用 bfloat16。

Python 客户端(标准库依赖):

from agentjev_client import AgentJev

jev = AgentJev("http://127.0.0.1:8149")

state = {
    "task": "Fix NullPointerException in UserAuthService.verifyToken",
    "test_output": "Tests run: 14, Failures: 1 — test_expired_token",
}

gate = jev.decide_boolean(
    state,
    "Have all tests passed?",
    criteria={
        "true": "The suite is green and the project builds.",
        "false": "Any test is still failing.",
    },
)

route = jev.decide_choice(
    state,
    "What should the agent do next?",
    options={
        "read_failed_test": "Open test_expired_token and read the assertion.",
        "rewrite_file": "Ask a larger model to rewrite the service.",
        "commit": "Commit the current diff anyway.",
        "retry": "Rerun the suite without changing the code.",
    },
)

gate 返回 decision、prob_true、prob_false、confidence、wall_ms;route 返回 best_action、probability、margin、distribution。HTTP API(POST /api/evaluate)支持批量:一次调用最多 32 个状态、128 个问题、1,024 个候选路径。

最有意思的用法:给 Claude Code 装门。 仓库里的 agentjev_hook.py 是一个 Claude Code PreToolUse hook(针对 Bash/Write/Edit):把工具调用负载发给 AgentJev,问一个 Boolean(这操作安全吗)+ 一个四级 Score(风险多大),只在"风险 3 级 且 Boolean 说不可靠"时才拦截。服务器挂了 hook 直接放行(exit 0)——门是增强,不是单点故障。

怎么训练一个类似的模型:四步拆解

这是这篇的重点。仓库的 agentjev/ 目录有完整训练代码(train.py 训练入口、synth.py 合成数据生成、losses.py 损失、data.py 数据管道)。核心流程四步:

第一步:生成合成数据(synth.py)

最反直觉也最聪明的一步:不靠人工标注,靠"已知分布的概率环境"程序化生成训练数据。

生成器内置这些环境:偏置硬币、灌铅骰子、抽 urn、抽牌、小马尔可夫链、贝叶斯更新场景。每个样本程序化生成,带精确的 gold 分布或精确计数——因为分布是已知的,标签无需标注、不可能出错。

数据覆盖 Agent 场景的六个问题族:completion(完成了吗)/ evidence(证据支持哪个)/ progress(进度如何)/ retry(下一步该重试吗)/ risk(风险多大)/ stop(该停吗)——把 Agent 循环里的典型判断数学化。

输出 JSONL schema(每状态一行):

{
  "id": "synth-000001",
  "env": "coin",
  "state": "[STATE] ...",
  "questions": [
    {"family": "stop", "qtype": "boolean",
     "text": "...", "candidates": ["TRUE", "FALSE"],
     "gold": {"distribution": [0.93, 0.07]},
     "supervision": "known_distribution", "weight": 1.0, "ordinal": false}
  ]
}

监督信号分四级加权:known_distribution / deterministic(权重 1.0)、binomial_counts / multiclass_counts(按试验数加权)、heuristic(权重 0.2,软策略标签)、teacher(预留给后续阶段,权重 0.1-0.3——即用大模型蒸馏的标签)。

第二步:配置训练(分层学习率 + 混合数据源)

train.py 支持 YAML 配置,两个关键设计:

分层学习率衰减(layer-wise LR decay):参数分五组——embedding / 骨干底部 12 层 / 中间 8 层 / 顶部 8 层(含 final norm)/ 决策头。每组独立学习率,越底层越保守、决策头最激进。这是微调预训练骨干的标准做法。

数据源混合(SourceMixer):可以配置多个数据源按权重混合——比如合成数据 + 教师蒸馏数据 + 真实场景数据。

第三步:跑训练(单 GPU BF16 全参数)

python -m agentjev.train --config configs/your.yaml

训练设置:单 GPU、BF16(autocast)全参数训练——0.6B 模型单卡可跑,这是它对个人开发者的最大友好。优化器 AdamW,cosine 调度带 warmup,梯度累积,梯度裁剪。

一个值得学习的工程细节——NaN 防护(v2):累积窗口维护自己的"有效微批次"计数,遇到非有限 loss 或梯度范数就丢弃整个窗口、从零重启,保证优化器步进总是在恰好 grad_accum 个有效微批次上执行;连续失败有熔断(max_consec_fail)。BF16 全参数训练容易出 NaN,这个防护是实战经验的结晶。训练过程输出 JSONL 事件日志,可接 dashboard。

第四步:温度校准(不进测试集)

训练完成后还有一步:温度校准(temperature calibration)。对每种原语(Boolean/Choice/Score)拟合一个正的标量温度,只在留出的校准 case 上拟合——校准前先在开发集上按 soft cross-entropy 选 checkpoint,测试集在整个流程结束前不打开。

这步很关键:决策模型的价值在"概率可信",而原始 logits 往往过度自信。温度校准让输出的概率分布真正可用——你设 0.9 的阈值时,0.9 真的意味着 90% 的经验正确率。发布到 HF 的 temperatures.json 就是这步的产物。

一张图总结训练管线

synth.py 生成合成数据(已知分布 → 精确 gold)
        │
        ▼
SourceMixer 混合数据源(合成 + 教师 + 真实)
        │
        ▼
train.py 单 GPU BF16 全参数训练
(分层 LR:embed/bottom/middle/top/head)
(AdamW + cosine warmup + NaN 防护)
        │
        ▼
开发集选 checkpoint(soft CE,测试集未动)
        │
        ▼
校准集拟合 per-primitive 温度
        │
        ▼
发布权重 + temperatures.json

如果你想训练自己领域的决策模型,路径是现成的:换掉 synth.py 里的"环境"(比如你的领域是工单分类,就生成带已知 gold 的工单场景),保留架构(Qwen3 小骨干 + 排列等变头)和训练管线(分层 LR + 温度校准),单 GPU 起步。

参考链接

你会在 Agent 循环的哪个位置装这颗"反射"?评论区聊聊你的场景,觉得有用点个赞让更多人看到。


作者: itech001 来源: 公众号:AI人工智能时代(the-ai-era) 网站: https://www.theaiera.top/ 关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top

本文首发于 AI人工智能时代,转载请注明出处。

分享给朋友