Jev 与 fast-jev-compaction:Agent 缺的不是更便宜的 GPT,而是”快判断层”

作者读完 Jev(TypeSafe AI 的 System One Model)资料并仿写 fast-jev-compaction 小 Demo 后提出:Agent 里大量模型调用不是为了生成答案,而是为了让代码做一次判断;Jev 的价值不在”取代大模型”,而在于把高频、封闭、可回退的判断从生成链路里拆出来,成为 Agent 的”反射神经”(快判断层)。

基本信息

  • 来源类型:文章(人人都是产品经理 · 作者:daryl,高级前端开发,来源 @腾讯技术工程公众号)
  • 原文位置:raw/articles/2026-09-21-190923-tg-f91bba.md
  • 原文 URLhttps://www.woshipm.com/ai/6467980.html
  • 原始出处:微信公众号 @腾讯技术工程,原文链接 https://mp.weixin.qq.com/s/D1La4jMoVZ5Ip_RrVY1kPw
  • 素材来源:Telegram bot 抓取(telegram_msg_id 3981),正文由 scripts/http_capture.py HTTP 优先抓取
  • 消化日期:2026-09-21
  • 文章结构:八个章节(Jev 是什么 / 为什么需要快判断层 / fast-jev-compaction / Demo 实现拆解 / Demo 结果 / 选型 / 从 Demo 到工程模式 / 结语)

核心观点

  1. Jev 的本质是把”生成问题”改成”判断问题”,它的输出空间是封闭的,而不是”更便宜的 GPT”。Jev 是 TypeSafe AI 发布的 System One Model,刻意放弃文本生成,只返回类型化的概率决策。输入可以很杂(用户目标、页面状态、工具结果摘要、业务字段、候选动作),输出必须很窄(选哪个、打几分、某命题成立的概率)。三种原语:Choice(单选题,选项 + 每选项概率 + confidence,用于工具路由/动作选择/分类)、Score(打分题,连续分数 + 概率分布 + confidence,用于风险/质量/相关性评分)、Noul(判断题,0~1 概率,用于是否保留/是否危险/是否达成目标)。

  2. 与 GPT/Claude 的 structured output 的差别不是”能不能返回 JSON”,而是”从设计上就放弃自由文本生成”。作者用同一个例子对照:问”这段测试日志还要不要保留”,大模型会输出一段自然语言解释(“这段日志包含失败原因、文件路径和鉴权相关信息,建议保留。”),人能看懂但代码还要继续解析;Jev 返回的是 {"answers": {"result_t3": {"noul": 0.87}}},可以直接进入代码分支 if (keepResult >= 0.5) { keepFullResult(); }。核心变化是”把模型输出从文本变成可执行的概率判断”。

  3. 由此得到一套 Agent 的四层架构分工慢思考层(规划、解释、生成、复杂推理 → GPT/Claude/Gemini)、快判断层(路由、筛选、评分、门禁 → Jev/Jev-like)、确定性层(权限、状态、副作用、回滚 → 普通代码)、兜底层(高风险或低置信度处理 → 人工/更强模型)。作者的定位比喻是:“Jev 最适合的位置,不是 Agent 的大脑,而是 Agent 的反射神经。”

  4. 真正撑爆上下文的不是用户或助手的文本,而是工具结果,而传统摘要会改写事实。作者列了逐类风险:用户需求(不长但必须全文保留,删掉会丢约束)、助手回复(中,通常要保留,删掉会丢计划和承诺)、read_file 结果(长,看情况,文件全文可能过期或可重读)、search_content 结果(长,很多匹配只是探索痕迹)、execute_command 日志(长,失败栈重要、成功日志可能无用)、list_dir 结果(长,目录树容易占空间)。举的具体反例:原始日志 FAIL order-export.test.ts / Error: Timeout of 5000ms exceeded / at exportOrders (/repo/src/services/orderExport.ts:42:19) / Expected authorization header to be preserved. 被摘要成”之前订单导出测试失败了”,测试文件名、5000ms 超时阈值、精确文件行号、authorization header 所牵涉的鉴权约束全部丢失——“上下文压缩最怕的不是删掉废话,而是把关键事实改写成一句’差不多’。”

  5. 通用大模型做高频压缩会遇到三层断裂:① 生成成本和判断需求不匹配——系统要的是 0~1 概率/枚举选项/分数,通用 LLM 却在生成解释文本、reasoning + action、自然语言评价、判断理由;② 摘要破坏可复核性——文件路径、错误码、栈信息、命令参数是后续定位问题的精确证据,被”含义相近”的描述替代后,Agent 下一步要执行动作而不是”理解大意”;③ 置信度没有进入代码分支——大模型的”我有 80% 把握”通常只是文本里的自我描述、不一定经过校准,Jev 的设计目标是把概率变成接口返回值让代码用阈值控制行为。

  6. fast-jev-compaction 的核心洞察是”不做摘要,只做保留决策”,它把上下文压缩拆成两个判断题:keepCall(这个工具调用本身还重要吗?)与 keepResult(这个工具结果全文还需要保留吗?),两个概率组合出三种动作:完整保留、只保留调用并截断结果、调用和结果一起删除。它最克制的约束是用户文本和助手文本不改写,只处理工具调用和工具结果——因为用户文本含原始需求和硬约束、助手文本含已承诺的计划,被摘要改写后容易变味;工具结果占空间最大、最容易过期、且很多可以重新读取或重新执行。它是个 Claude Code 插件,也可作为 npm 库使用。作者给它的定位是”不是总结器,而是工具历史清理器”。

  7. 它的三层设计与”一个问题只做一个判断”的原则是可复用的工程模式:对象层(把 tool_use 和 tool_result 配成一组,避免留下孤立调用或孤立结果)、判断层(为每组工具历史生成 keepCall / keepResult 两个问题,把复合压缩任务拆成两个窄判断)、执行层(按阈值执行 keep / drop_result / drop_call,让压缩动作可测试、可回退、可统计)。反面示例是不要问”这个工具调用和工具结果是否还重要?如果重要请决定是否截断。“——那混了多个判断。

  8. Demo 的关键工程细节:裁剪前先配对、state 只给判断所需信息、预算不够时逐级降级、每批重复发 state 但决策协议稳定。配对必须做,否则会出现”工具调用被删但结果还在”或”结果被删但上下文残留没有返回的调用”;state 构造不发送完整工具结果,只把它变成短说明(ok, 4213 chars (omitted) / error, 830 chars (omitted)),长文本可能 abridge、最近消息优先保留——“压缩器不需要读完整历史,它只需要知道哪些历史值得继续被读到”;fitState 的降级链路是 full(工具输入最多 1000 字符)→ inputs200 → inputs60 → texts abridged(长文本保留头尾)→ old messages collapsed → old calls compacted → old messages left out → old calls merged,原则是”先削弱细节,再删除内容;先处理旧消息,再处理最近消息;先保护任务连续性,再追求压缩率”;batchCalls 每批重复发送完整 state,作者承认成本上升但”实现简单,且每个判断都在同一上下文下完成”。

  9. Demo 跑出的实测数据与结果:样本是”修复订单导出超时”的会话,含四个工具调用(toolu_001 list_dir 长目录树 / toolu_002 read_file orderExport.ts 含 getAuthToken / toolu_003 execute_command 测试失败含超时和 authorization header / toolu_004 read_file 无关 legacy 文件)。npm test 结果 tests 11 / pass 11 / fail 0;四个调用被分成三类:t1 list_dir drop_result 0.54/0.00t2 read_file keep 0.59/0.69t3 execute_command keep 0.75/1.00t4 read_file drop_call 0.47/0.00;压缩前后 字符数 10029 → 852(减少 9177)压缩率 0.915(上下文减少 91.5%),工具调用 4 → 3 可见,完整保留 2 个,截断结果 1 个。

  10. 选型结论:Jev、Structured Output、Tool Calling、传统分类器不是互斥关系,而是各自占据不同位置。LLM + JSON Schema(表达能力强、能解释复杂理由,但逐 token 生成、成本和延迟高 → 低频复杂判断)、Tool Calling(能和工具系统集成,但决策仍依赖生成式模型 → Agent 主流程动作)、传统分类器(便宜、可本地部署,但标签变化不灵活、语义泛化弱 → 稳定标签 + 大量数据)、Jev(输出封闭、概率可用、适合高频,但不能生成、复杂推理弱 → 高频局部判断)。作者的量化门槛:“如果你的任务每小时只调用几十次,或者需要模型解释原因,通用大模型就够了。如果你的任务每天调用几十万次,并且输出空间固定,Jev 这类模型才开始有明显意义。”

  11. 两个容易被误读的宣传点被作者明确纠正:① “不会幻觉”——更准确的说法是”它不会输出 schema 之外的内容”(给它 keep / drop_result / drop_call 三个动作,它不会编一个 maybe_keep 出来),但它仍然可能选错,“这叫类型安全,不叫语义正确”;② “概率可信”——概率校准是 Jev 核心卖点,但目前公开资料里 RLCD 的训练细节和第三方校准数据还不充分,“所以在工程里不能直接把 0.9 当成上线规则”。正确姿势是:先 shadow mode 跑一段时间 → 用自己的业务样本统计不同概率段真实正确率 → 再定自动执行阈值 → 高风险场景保守处理。

  12. 按不可逆程度分级使用判断概率:L1 只读(搜索、读取、分类、重排)可以自动执行;L2 可逆(截断上下文、创建临时记录)低置信度保守保留;L3 不可逆(删除数据、扣款、关闭权限)不让 Jev 单独决定。作者的原话是”概率能帮代码分流,但不能替业务背锅。”

  13. Jev 的生态定位已经从”概念模型”落到”Agent 内部组件”,社区项目清单:浏览器自动化 browser-use/jev-ultrafast(页面元素编号后 Jev 选择下一步操作和目标元素)、Agent 框架 vercel/eve(用 Jev 做评估和路由)、上下文剪枝 fast-jev-compaction、代码审查 jev-review(把 code review 拆成一组是非判断和风险评分)、本地复现 SemIf / kev / laya(用开源小模型复刻 Jev-like 接口)、数据库扩展 pg-jev(在 SQL 里写语义判断条件)、游戏仿真 Doom / Mario / Snake。作者判断”游戏 Demo 很容易传播,但对开发者来说 fast-jev-compaction 更有参考价值”,因为上下文剪枝是每个 Agent 都会遇到的问题。

  14. 趋势判断:拆出判断层会带来效率、能力、模式三种变化——效率变化(不再为每个小判断启动一次完整生成链路)、能力变化(路由、筛选、评分、门禁可以沉淀成统一接口)、模式变化(Agent 从”全靠大模型思考”变成”生成、判断、执行分层协作”)。收尾原则:“生成负责表达,判断负责分流,代码负责执行。""把生成留给需要表达的地方,把判断交给可控的接口,把执行还给确定性代码。“

实操内容保留

本文包含完整可运行的 JavaScript 代码块(节选自 fast-jev-compaction Demo 仓库,标注了文件与行号)、概率阈值决策表失败回退策略表降级链路表,属于高密度可复用素材,按原文原样保留。

代码/配置

① 主流程 compact(来源:src/compact.js 第 28-49 行)

export async function compact(messages, asker, options = {}) {
const config = resolveOptions(options);
const startedAt = Date.now();
const calls = collectToolCalls(messages, config.preserveRecentMessages);
const candidates = calls.filter(call => !call.pinned);
const charsBefore = messages.reduce((sum, message) => sum + messageChars(message), 0);
const answers = new Map();
let fitted = { state: null, tokens: 0, stage: 'skipped' };
let requests = 0;
if (candidates.length > 0) {
fitted = fitState(messages, calls, config);
const batches = batchCalls(candidates, fitted.tokens, config);
const batchAnswers = await Promise.all(batches.map(batch => askBatch(asker, fitted.state, batch)));

关键词解读:collectToolCalls 先把工具调用和结果配成一组;fitState 把状态压到 Jev 能接受的预算里;batchCalls 问题太多时分批;Promise.all 多个 batch 并发问;answers 最后收集概率进入决策阶段。

② 工具调用配对 collectToolCalls(来源:src/state.js 第 80-123 行)

export function collectToolCalls(messages, preserveRecentMessages = 6) {
const calls = [];
const byToolUseId = new Map();
const total = messages.length;
const isPinned = index => index === 0 || index >= total - preserveRecentMessages;
messages.forEach((message, index) => {
const content = Array.isArray(message.content) ? message.content : [];
content.forEach(item => {
if (item.type === 'tool_use') {
const call = {
id: `t${calls.length + 1}`,
toolUseId: item.id,

配对后每个工具调用变成统一对象,字段为:id(内部短 ID,如 t1、t2)、toolUseId(原始工具调用 ID)、tool(工具名)、input(工具输入)、callIndex(调用所在消息位置)、resultIndex(结果所在消息位置)、resultChars(结果字符数)、isError(是否失败)、pinned(是否固定保留)。pinned 的规则是”首条消息和最近 N 条消息不参与删除”,作为避免刚发生的上下文被过早裁掉的工程保护。

配对的必要性(原文给出的危险状态示例):assistant 发起

{
"type": "tool_use",
"id": "toolu_003",
"name": "execute_command",
"input": {
"command": "npm test -- order-export"
}
}

user 返回

{
"type": "tool_result",
"toolUseId": "toolu_003",
"isError": true,
"content": "FAIL order-export.test.ts..."
}

③ state 构造 historyEntries(来源:src/state.js 第 146-183 行)

export function historyEntries(messages, calls, inputChars) {
const callsByMessage = new Map();
calls.forEach(call => {
if (!callsByMessage.has(call.callIndex)) {
callsByMessage.set(call.callIndex, []);
}
callsByMessage.get(call.callIndex).push(call);
});
return messages
.map((message, index) => {
const content = Array.isArray(message.content) ? message.content : [];
const text = content

每个工具调用整理成:

{
id: call.id,
tool: call.tool,
input: truncate(safeJson(call.input), inputChars),
result: call.resultNote,
}

“原始历史 → 判断用状态”的映射表(原文表格):

原始内容进入 state 后
完整文件内容不进入,只保留工具名和输入
完整测试日志不进入,只保留 error, N chars
工具输入参数截断后保留
用户文本保留,但长文本可能 abridge
最近消息优先保留

④ fitState 与降级链(来源:src/state.js 第 192-241 行)

export function fitState(messages, calls, options = {}) {
const maxStateTokens = resolveNumber(options.maxStateTokens, 25000, 1);
const preserveRecentMessages = resolveNumber(options.preserveRecentMessages, 6);
const goal = options.goal || goalFromMessages(messages);
const isPinnedIndex = index => index === 0 || index >= messages.length - preserveRecentMessages;
const stateOf = entries => ({
context: STATE_CONTEXT,
goal,
history: entries,
});

降级链路(原文表格,按顺序逐级降级):

阶段做什么损失程度
full工具输入最多 1000 字符
inputs<=200工具输入降到 200 字符
inputs<=60工具输入降到 60 字符
texts abridged长文本保留头尾
old messages collapsed旧文本折成省略说明
old calls compacted旧工具调用压成单行
old messages left out删除旧的纯文本消息很高
old calls merged合并连续旧工具调用很高

可观察的统计字段示例(原文):stateTokens: 669stateStage: fullrequests: 1。作者给出的运维判读:“如果线上发现某类会话经常落到 old messages left out,说明不是 Jev 判断问题,而是 state 预算本身已经太紧,需要调整策略。”

⑤ 两个 Noul 的问题构造 questionsFor(来源:src/jev.js 第 100-113 行)

export function questionsFor(call) {
const summary = `${call.tool} input=${truncate(safeJson(call.input), 200)} result=${call.resultNote}`;
return {
[`call_${call.id}`]: {
type: 'noul',
instructions: `Does knowing that this tool call was made, with its inputs, still matter for the rest of the task?\nTool call: ${summary}`,
},
[`result_${call.id}`]: {

⑥ 决策树 decideCall(来源:src/compact.js 第 131-149 行)

export function decideCall(call, answer, options) {
const threshold = options.keepThreshold ?? DEFAULT_OPTIONS.keepThreshold;
const keepCall = answer?.keepCall ?? 0;
const keepResult = answer?.keepResult ?? 0;
if (call.pinned) {
return { id: call.id, tool: call.tool, action: 'keep', reason: 'pinned', keepCall: 1, keepResult: 1 };
}
if (keepResult >= threshold) {
return { id: call.id, tool: call.tool, action: 'keep', reason: 'kept', keepCall, keepResult };

三种动作(原文表格):

动作条件处理方式
keepkeepResult >= threshold调用和结果都完整保留
drop_resultkeepResult < thresholdkeepCall >= threshold保留调用,截断结果
drop_call两者都低调用和结果一起删除

⑦ 分批请求 batchCalls(来源:src/compact.js 第 86-114 行)

export function batchCalls(calls, stateTokens, options) {
const budget = options.maxRequestTokens - stateTokens - REQUEST_OVERHEAD_TOKENS;
const batches = [];
let current = [];
let currentTokens = 0;
calls.forEach(call => {
const cost = estimateTokens(JSON.stringify(questionsFor(call)));
if (cost > budget) {
throw new Error('state leaves no room for questions');

⑧ asker 抽象与离线 mock(来源:src/compact.js 第 72-84 行)

export async function compactMessages(messages, options = {}) {
const apiKey = options.apiKey ?? process.env.TYPESAFE_API_KEY;
const asker = apiKey
? createHttpAsker({
apiKey,
model: options.model,
baseUrl: options.baseUrl,
fetchImpl: options.fetchImpl,
})
: createHeuristicAsker();

作者对此的建议:“先把接口抽象出来,真实模型只是一个 asker。这样后面想换官方 Jev、本地复现模型、普通 LLM 适配器,都不需要改主流程。模型可以替换,决策协议要稳定。

Prompt 模板

原文中的”Prompt”形态是 Jev 的窄问题指令(非通用对话模板),可直接复用的有两条:

Does knowing that this tool call was made, with its inputs, still matter for the rest of the task?
Tool call: {tool} input={truncated input, 200 chars} result={resultNote}

对应的中文语义(原文):

这个工具调用本身是否还重要?
这个工具结果全文是否还需要保留?

反例(原文明确禁止这样问):

这个工具调用和工具结果是否还重要?如果重要请决定是否截断。

操作步骤

Demo 运行方式(原文命令)

cd jev-compaction-demo
npm test
npm start

主流程六步(原文”从历史消息到压缩决策”)

  1. 收集历史里的 tool_usetool_result
  2. tool_use_id 配对;
  3. 标记 pinned:首条消息、最近 N 条消息固定保留;
  4. 构造给 Jev 看的 state
  5. 对每个非 pinned 工具调用问两个 Noul 问题;
  6. 根据 keepCall / keepResult 重建消息列表。

压缩决策的阈值策略(原文表格,供代码分支参考)

概率区间系统动作说明
>= 0.8自动执行适合低风险、高重复场景
0.5 ~ 0.8保守处理截断、保留摘要、请求补充信息
< 0.5删除或回退低价值内容可清理,高风险场景转人工

失败回退策略(原文表格)

失败情况建议处理
Jev 请求失败回退到内置 summary 或直接不压缩
返回格式异常丢弃本轮压缩结果
压缩收益不足保留原 transcript
state 无法 fit调大预算或切换传统压缩
低置信度保守保留,不要激进删除

上线前的概率校准步骤(原文”正确姿势”)

  1. 先 shadow mode 跑一段时间;
  2. 用自己的业务样本统计不同概率段的真实正确率;
  3. 再定自动执行阈值;
  4. 对高风险场景保守处理。

配置/参数速查(作者给出的取舍速查表)

方案优点缺点适合位置
LLM + JSON Schema表达能力强,能解释复杂理由仍然逐 token 生成,成本和延迟高低频复杂判断
Tool Calling能和工具系统集成决策仍依赖生成式模型Agent 主流程动作
传统分类器便宜、可本地部署标签变化不灵活,语义泛化弱稳定标签、大量数据
Jev输出封闭,概率可用,适合高频不能生成,复杂推理弱高频局部判断

选型自检表(原文)——问”输出选项能提前枚举吗?”→ 适合 Jev;“是否需要自然语言解释?”→ 更适合 LLM;“是否高频调用?”→ 适合 Jev;“错误是否可兜底?”→ 适合 Jev;“任务是否长期稳定且有大量标注?”→ 可以考虑传统分类器;“是否需要跨文件、多步推理?”→ 不适合 Jev 单独承担。

关键概念

  • [待创建] Jev(System One Model) — TypeSafe AI 发布的”只做判断不生成文本”的模型;三种原语 Choice / Score / Noul;本文的全部论点都建立在它之上,值得独立实体页。
  • [待创建] 快判断层(Fast Judgment Layer) — 与慢思考层相对,承担路由、筛选、评分、门禁,作者称之为”Agent 的反射神经”。
  • [待创建] fast-jev-compaction — Claude Code 插件 / npm 库,用 Jev 对工具历史做保留价值判断;本文 Demo 的来源项目。
  • [待创建] Noul / Choice / Score — Jev 的三种输出原语,封闭输出空间的具体形态。
  • 上下文工程 — 本文属于该领域的一个具体子问题:工具结果的保留与裁剪策略。
  • Context 上下文 — 上下文膨胀的成因与结构(工具结果是主要占用方)。
  • Claude Code — fast-jev-compaction 是一个 Claude Code 插件,处理其会话 compact。
  • Coding Agent / Agent Loop 智能体循环 — 本文描述的”搜索→读文件→跑测试→看日志→改代码”循环正是 Coding Agent 的上下文来源。
  • Agent Memory — 上下文压缩与记忆管理的相邻问题(前者管窗口内,后者管跨会话)。
  • 提示词工程 — 本文的”窄问题”设计是提示词工程在判断型任务上的一个反向用法(把任务拆窄而非写长 prompt)。
  • [待创建] 概率校准(Calibration) — 让”模型说 0.8 时长期真有 ~80% 正确”的工程前提,作者强调公开数据不足。

与其他素材的关联

  • AI 文字垃圾正在淹没互联网:从体育新闻幻觉说起 的关系:两者都在谈大模型输出不可信,但落点相反——那篇讲”生成式模型不知道自己扯谎”(生成侧风险),本文讲”把生成换成封闭判断来规避这类风险”(架构侧对策),可以并读为一组”问题—对策”。
  • Agent Skill · MCP · 模型三层架构 的关系:本文提出的”慢思考层 / 快判断层 / 确定性层 / 兜底层”是另一种 Agent 分层切法,可与该方法论对照,看分层依据是”能力类型”还是”组件类型”。
  • Context 上下文上下文工程 的关系:为这两个页面补充了一个具体的、带实测数据的裁剪实现(fast-jev-compaction 的配对 / state / 降级 / 三分法),是”上下文压缩”从概念到工程参数的落地案例。
  • Claude Code 的关系:补充了 Claude Code 生态中”第三方 compact 策略”的实例(插件形态,可替换内置 summary 流程)。
  • Agent 六层架构多Agent架构 的关系:本文是对”Agent 内部哪些环节必须调用大模型”这一问题的一次反问(大量调用其实是判断题,不该走生成链路)。

原文精彩摘录

Agent 里很多模型调用不是为了生成答案,而是为了替代码做一次判断。

这句话如果成立,Jev 就不是”更便宜的 GPT”。它更像 Agent 系统里缺了很久的一个零件:快判断层。

真正撑爆上下文的,往往不是用户说的话,也不是助手说的话,而是工具结果。

……上下文压缩最怕的不是删掉废话,而是把关键事实改写成一句”差不多”。

这个约束很重要。用户文本里通常有原始需求和硬约束,助手文本里有已承诺的计划和正在执行的思路,这两类内容被摘要改写后容易变味。工具结果不同:它们占空间最大,也最容易过期,而且很多内容可以重新读取或重新执行。

所以 fast-jev-compaction 不是”总结器”,而是”工具历史清理器”。

Jev 的宣传里有两个点容易被误读。第一个是”不会幻觉”。更准确的说法是:它不会输出 schema 之外的内容……但它仍然可能选错。这叫类型安全,不叫语义正确。

概率能帮代码分流,但不能替业务背锅。

生成负责表达,判断负责分流,代码负责执行。

相关页面