AgentJev-0.6B · 使用手册

给它状态和选项,拿回概率分布

AgentJev 不写文字。你把当前状态和已经写好的问题交给它,一次前向传播返回每个选项的概率,生成的 token 数是 0。把它放在 Agent 循环里做门控、路由和打分,把写作留给大模型。

上下文 2,048 tokens 三种问题 Boolean · Choice · Score Typed Decisions Top-1 79.25% 每案中位时延 生成 token 0
回放中
state
Qwen3 · 28 层
候选集合
评分头
生成 token0
distribution

控制台回放的是 v1 权重在 RTX 5060 上的真实请求与真实输出,已加载校准温度。为了看清过程,动画把阶段放慢了;底栏是这次请求的实测耗时。

适合做什么

Agent 的大部分步骤不是写作问题:测试过了没有,下一步调哪个工具,这条命令能不能跑。常见做法是让一个 27B–70B 的模型写一段话,再从话里解析出布尔值。AgentJev 直接给出你提供的每个选项的概率,没有 JSON 需要修,也没有一句自信的错话需要识别。

BOOLEAN

门控

测试全部通过了吗?这条命令安全吗?

返回 P(true)。你按自己的代价选门槛。

CHOICE

路由

下一步该做什么?这张工单归哪个队列?

2–255 个候选,返回完整分布、最优项和领先幅度 margin。

SCORE

打分

这次改动风险几级?多急?

2–10 个有序等级,返回期望分 Σ i·Pᵢ 和最可能的等级。

它不擅长的事。 写代码、写回复、开放式生成都交给大模型。返回的是模型分布,没有在你的领域上校准过。Choice 是你给的候选之间的相对偏好,不是某个动作的成功率;需要成功率时,对每个动作单独问一个 Boolean,并用你自己的结果数据校准。79.25% 是与公开教师标签的一致率,不是 Agent 任务成功率。

五分钟跑起来

需要 Python 3.10+ 和一块 CUDA 显卡(本次优化后权重常驻约 1.4 GB 显存);CPU 也能跑,但慢很多。左边是步骤,右边的终端按顺序回放这几步在本机的真实输出。

  1. 克隆代码,建虚拟环境

    git clone https://github.com/malevrigns/agent-jev.git
    cd agent-jev
    python -m venv .venv
    source .venv/bin/activate
  2. 装 PyTorch 和依赖

    先用 PyTorch 官方选择器 装好 CUDA 版 torch(2.5+;RTX 50 系列选 CUDA 12.8 或更新的构建),再装其余依赖。

    bash / PowerShell
    pip install -r requirements.txt
    pip install huggingface_hub safetensors
  3. 拉取固定版本的 v1 权重

    只下载修订 7d43399 的 7 个文件(权重、校准温度、配置、分词器),逐个核对大小和 SHA-256,确认 343 个张量都是 BF16,全部放进 models/agentjev-v1。共约 1.2 GB,不再额外下载 1.5 GB 的 Qwen3 底座模型。

    bash / PowerShell
    python fetch_v1_weights.py
    别直接从 Hugging Face 默认分支下载。默认分支现在是另一个 FP32 的代码补全检查点,体积翻倍,也不是基准表里那组权重。
  4. 启动服务

    bash / PowerShell
    python -m jev_service.server --checkpoint models/agentjev-v1/model.safetensors --model-path models/agentjev-v1 --temperatures models/agentjev-v1/temperatures.json --port 8149

    只监听 127.0.0.1,要从别的机器访问请用 SSH 端口转发。报告就绪前会先跑一次预热请求;在 Windows 上,服务还会让自己不受后台节能限速影响。浏览器打开 http://127.0.0.1:8149/ 是自带的工作台。

  5. 确认它活着,发第一个请求

    bash
    curl http://127.0.0.1:8149/health

    看到 "status": "ready" 和 "encoder": "auto" 就可以发请求了。

agent-jev · 本机实录
启动参数默认值说明
--checkpoint—models/agentjev-v1/model.safetensors;旧版快速开始生成的 agentjev_v1.pt 也能用
--model-path—带 config.json 和分词器的本地目录,即 models/agentjev-v1。只读配置和分词器,不读底座权重
--temperatures不校准在独立校准集上拟合的每类问题温度,建议总是带上
--port8149与客户端和钩子一致
--devicecuda:0没有显卡时传 cpu
--max-tokens2048单条候选路径的 token 上限,超长直接报错,不截断
--encoderautoauto:同一状态的所有问题共享一次状态编码(新)。question:只在同一问题内共享(旧行为)。shared/path:调试用

三种问题

一个请求是一段 state 加若干道题。state 可以是文字,也可以是对象或数组,对象会按键排序后紧凑地序列化成 JSON。题目的 id 只用来对应返回结果,模型看不到它;意思必须写进题目文字和选项描述里。

BOOLEAN是非题

{
  "id": "done",
  "type": "boolean",
  "question": "Are all tests passing?",
  "criteria": {
    "true": "The suite is green.",
    "false": "At least one test failed."
  }
}

criteria 可省略,此时两个候选就是 TRUE 和 FALSE。写清楚真假各指什么,效果通常更稳。

返回 probability(即 P(true))、value(P(true) ≥ 0.5)和两个选项的 distribution。

CHOICE选择题

{
  "id": "next",
  "type": "choice",
  "question": "What is the useful next action?",
  "options": {
    "debug_failure": "Read the failing assertion.",
    "submit_patch": "Open a pull request now."
  }
}

options 可以是对象(键作为返回的 value)或数组(返回下标字符串)。2–255 个,描述不能重复。

返回 value、description、top_probability、margin(第一名减第二名)和完整分布。选项顺序不影响结果。

SCORE等级题

{
  "id": "risk",
  "type": "score",
  "question": "How risky is this change?",
  "levels": [
    "Isolated change, no external behavior.",
    "A public signature changed.",
    "Authentication may be bypassed."
  ]
}

levels 是 2–10 个按从低到高排列的描述,顺序就是分值。

返回 level(最可能的等级)、score = Σ i·Pᵢ(0 到 n−1 的连续期望分)和 legend。

score 怎么算出来:编码 Agent 示例里的风险题(真实输出)

构造请求

在左边改状态和题目,右边实时生成请求体和各语言的调用代码,并按服务端的规则检查。“模型实际读到的文字”展示一条候选路径被拼成什么样。

模型实际读到的文字(第 1 题,第 1 个候选)

读懂返回

每道题的答案都带完整分布。下面是一个 Boolean 门控在六段测试输出上的真实 P(true)。拖动门槛或点“扫一遍”,看哪些会被放行。门槛放多高取决于放错一次的代价:自动合并代码要严,提醒人工复核可以松。

0.50

问题:“Have all tests passed?”,带 criteria。状态是为本页编写的示例,概率是 v1 权重的真实输出(已校准)。

字段出现在怎么用
distribution全部键是你传的选项 id(或下标),值相加为 1。永远先看它
probability / valueBooleanP(true) 和按 0.5 切出的布尔值。生产里用你自己的门槛,不要只看 value
top_probability / marginChoicemargin 小说明模型拿不准,适合转交大模型或人工
score / levelScorescore 连续,适合排序和设阈值;level 是最可能的那一档
usage每次请求generated_tokens 恒为 0;backbone_input_tokens 是骨干实际算的 token;shared_prefix_states 是共享了状态编码的状态数;wall_ms 是服务端耗时

Python 客户端

agentjev_client.py 只用标准库。三个高阶方法各问一道题,evaluate(state, questions) 一次问多道。

python
dict 状态会原样发给服务端,由服务端按排序键紧凑序列化,和基准测试的输入一致。同一状态的多道题用 evaluate(state, questions) 一次发;多个状态用 evaluate_many([{"state": …, "questions": […]}, …])。服务端拒绝请求时会抛出 AgentJevError,status 和 message 就是服务端给的原因。

批量与性能 本次优化 · PR #15

每个候选都会被拼成一条独立的路径:[状态][问题][候选]。以前,一个状态下有多少条候选路径,状态就要被骨干重算多少遍;只有当同一问题有 ≥8 个候选且前缀够长时才复用。现在,同一状态下的所有问题和候选合并成一棵 token 前缀树,一次前向算完。每个 token 只关注自己的祖先,位置编码取它在原路径中的深度,所以每个候选读到的仍然恰好是自己那条路径。

状态问题候选块宽与真实 token 数成正比 · Trace 审查示例,5 道题、18 个候选
以前 · 2 次前向
现在 · 1 次前向

测试集前 20 个案例,按实测耗时 1:1 回放
0 ms
优化前(main)
优化后

每格是一个案例(1 段状态 5 道题,一次请求),亮起的时刻就是它在本机实际完成的时刻。

单案 P50 时延,按业务(ms,越短越好)
优化前(main)优化后
查看数据表

README 结果表里的 79.25% 来自训练时的 FP32 权重;发布的 BF16 权重经服务端是 79.35%(1,587 / 2,000),优化前后相同。逐题对比里变了的两道题,在优化前就是平局或近似平局(第一名与第二名相差 0.0031 和 0.0000),一道因此改对、一道改错。

  • 把同一状态的所有问题放进一个请求,才能共享状态编码。分开请求,每次都会把状态重算一遍。
  • 一次请求最多 32 个状态、128 道题、1,024 条候选路径,请求体 1 B–1 MB。多个状态放在 {"requests": [{"id", "state", "questions"}, …]} 里。
  • 某个状态的前缀树太大(单次前向超过 8,192 个 token 或 64 MB 掩码)时,这个状态自动退回按问题共享的旧路径,结果不变。

写好问题

AgentJev 只看得到文字。题目和选项写得越像一个清楚的判断,分布就越可信。

别这样
这样
"options": {"a": "A", "b": "B"},意思藏在 id 里
把意思写进描述:"retry": "Rerun the suite without changing the code."
Boolean 只写一句模糊的问题
补上 criteria,说清楚 true 和 false 各指什么
Score 等级乱序,或者从高到低排
从低到高排,每档一句可区分的描述
把一段 3,000 token 的日志整段塞进 state
先截取相关片段;超过 2,048 token 会直接报错
用 Choice 的概率当“这个动作会成功的概率”
需要绝对概率时,每个动作单独问一个 Boolean,并在你的数据上校准
同一状态的五道题分五次请求
放在同一个请求里,状态只编码一次

接入 Claude Code

agentjev_hook.py 是一个 PreToolUse 命令钩子。Claude Code 执行 Bash、Write、Edit 前,它把工具调用发给本地服务,问一道 Boolean(安全吗)和一道四级 Score(风险多高),再给出三种判决之一:风险第 3 级并且判为不安全时,拦下这一次调用,理由交给 Claude,Claude 可以换一步继续;风险第 2 级及以上,或 P(安全) 低于 0.5 时,请你确认;其余情况不干预,按你自己的权限设置走。它从不回答 allow,所以不会绕过你的权限设置。服务没有响应时钩子直接退出,这次调用照常走权限流程。

.claude/settings.json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|Write|Edit",
        "hooks": [
          { "type": "command", "command": "python /path/to/agent-jev/agentjev_hook.py" }
        ]
      }
    ]
  }
}
Claude Code 想执行的命令P(安全)风险钩子

真实输出,问题与 agentjev_hook.py 完全相同。默认连 127.0.0.1:8149;AGENTJEV_URL 改地址,AGENTJEV_ASK_LEVEL、AGENTJEV_ASK_BELOW 调整询问门槛,AGENTJEV_VERBOSE=1 让不干预的调用也显示判定。

排错

启动时报找不到分词器(local_files_only)

--model-path 传的是仓库名而本机没有缓存。传 fetch_v1_weights.py 下载好的本地目录 models/agentjev-v1,里面有 config.json 和完整的分词器文件。

400:question 'x' needs N tokens; limit 2048

状态 + 题目 + 某个候选拼起来超过 2,048 token。服务不会截断任何内容,需要你缩短状态(只保留相关的日志或 diff 片段)或缩短选项描述。

400:candidate descriptions must be distinct / choice requires 2..255 candidates

同一道题的选项描述必须互不相同;Choice 要 2–255 个选项,Score 要 2–10 个等级。用上面的构造器检查一遍。

400:batch exceeds 128 questions or 1024 candidate paths

一次请求的题目总数或候选路径总数超限。例如 32 个五题案例是 160 道题,要拆成两次;同一状态的题目尽量留在同一个请求里。

CUDA 不可用,或 RTX 50 系列报 no kernel image

装了 CPU 版 torch,或 CUDA 构建太旧。按官方选择器重装;RTX 50 系列需要 CUDA 12.8 及以上的构建。临时可以 --device cpu。

请求比文档里慢一倍(Windows)

Windows 11 会给不在前台的进程做节能限速。服务现在启动时会让自己退出限速,就绪行里会写 "power_throttling_disabled": true。本机交替实测同一个请求:不退出时约 75 ms,退出后约 31 ms。用旧版代码的话,把服务所在的终端放到前台,或者更新代码。第一个请求的 CUDA 初始化(约 1 秒)也已经挪进启动时的预热。

客户端连不上

先 curl http://127.0.0.1:8149/health 确认服务在。服务、客户端和钩子默认都是 8149;旧版服务默认 18765,显式传 --port 8149 即可。客户端和钩子访问本机服务时不走系统代理,不用另配 NO_PROXY。

想要升级前的行为做对比

启动时加 --encoder question。两种模式在测试集上的概率差异见上面的质量表。