AgentJev:0.6B 开源决策模型,一次前向 50ms,零 token 解码
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。
这篇文章讲清楚三件事:它是什么、怎么用、以及如果你想训练一个类似的模型,步骤是什么。
本文提纲
- 它是什么:给 Agent 循环装"反射弧"
- 架构:去掉 LM head,加一个候选头
- 三种原语:Boolean、Choice、Score
- 基准成绩与 Laya 对比
- 怎么用:从下载到门控 Claude Code
- 怎么训练一个类似的模型:四步拆解
它是什么:给 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 起步。
参考链接
- GitHub: malevrigns/agent-jev - AgentJev-0.6B,304 stars,Python,Apache-2.0
- Hugging Face: aimeigaoshou/agent-jev - 模型权重与 temperatures.json
- Typed Decisions 数据集 - 官方评测基准
- Qwen3-0.6B - 骨干模型
- 我的 System One 拆解 - JEV、kev、laya 架构对比
- 我的 Jev+LangGraph 翻译 - LangChain 官方生产教程
你会在 Agent 循环的哪个位置装这颗"反射"?评论区聊聊你的场景,觉得有用点个赞让更多人看到。
作者: itech001 来源: 公众号:AI人工智能时代(the-ai-era) 网站: https://www.theaiera.top/ 关注每日最新AI新闻和技术博客,主页有更多的文章的AI 技术参考:https://www.theaiera.top
本文首发于 AI人工智能时代,转载请注明出处。