尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零复刻Jev决策模型:4B参数轻量级Agent训练与部署实战

从零复刻Jev决策模型:4B参数轻量级Agent训练与部署实战 如果你最近在折腾本地跑 agent多半会注意到 Jev 这个名字。codex 接入教程、claude code 的第三方配置、opencode 的模型推荐列表里都能看到它的影子——一个主打轻量决策的开源模型却扛下了不少插件式 agent 的“判断大脑”工作。我花了两周时间研究它的输出风格和决策链路最后决定不等官方把完整权重放出来而是自己动手做了一个对标方案就是 NeoHorse-Jev-4B。这篇文章不是论文是我从选底座、造数据、做强化学习到接入编辑器的全流程实战记录适合也打算在 4B 档位上做 agent 决策模型的开发者参考。所有结论都来自我自己实测包括踩过的几个大坑。1. Jev 在 agent 生态里的位置以及我为什么非要复刻一个1.1 大家都在用 Jev 干什么先交代一下背景。Jev 在开源社区里火起来不是因为它能写诗画画而是因为它很擅长“决定下一步做什么”。agent 类工具比如 codex、opencode以及一些 claude code 的兼容层配置在调用工具之前都需要一个模型先想清楚现在该读哪个文件该搜哪个关键词该执行什么命令还是该直接给出答案这个“想清楚”的过程就是决策。Jev 恰好把这件事做得比较极致。它不会输出一大堆推理文字而是给一个简短判断然后立刻调用工具。这个风格在实测中非常有用因为 agent 是多轮交互每一步的延迟都会被串联放大。模型废话越多任务跑完需要的时间越长消耗的 token 也越多。Jev 用“少说多做”的思路把决策成本压得很低这是它在社区里口碑好的核心原因。但我在实际使用中遇到了几个绕不过去的问题一是权重分发方式有限制改不了内部行为想针对自己场景微调一直没有官方路径二是它在某些流程里需要走远端服务数据都要过一遍别人的接口本地无法完全私有化三是社区里关于它训练细节的资料基本没有遇到决策失误只能等更新。对我来说这些限制已经超出了“用工具”的范畴变成了明显的瓶颈。于是我做了一个决定自己做一个开源决策模型目标就定在“接近 Jev 的决策能力但全链路可复现、可自定义、可本地部署”这就是 NeoHorse-Jev-4B 的来历。1.2 我理解的对标不是“复制粘贴”说清楚一点我做这个项目不是为了做一个 Jev 的像素级模仿品。模型权重、推理代码、训练数据管线、评测集每个环节如果都闭门造车那对社区没有增量价值。我更想验证的是在没有 Jev 官方训练细节的情况下仅凭公开轨迹数据加合理的训练方法能不能把决策能力拉到接近 Jev 的水平。这个思路也决定了我后面所有选择。比如我不会刻意去模仿 Jev 的输出措辞而是复刻它的“决策结构”极短判断、工具调用、观察结果、继续循环。任何模型只要沿这个结构训练都有可能达到类似的实战效果。另外我将全部训练过程和评测方案开源这样其他开发者可以自己跑一遍也可以在 NeoHorse-Jev-4B 基础上继续叠自己的工具生态。文章后面的内容基本就是整个从零到一的过程。2. 模型底座的选型4B 不是“小”是“足够快”2.1 为什么把规格卡死在 4B 档位核心原因是一个很朴素的工程理由延迟。决策模型的使用场景是 agent 循环每一步都要等模型生成完才能执行下一步。假设一个任务需要 8 次工具调用那么模型单次响应时间直接决定任务总耗时。一个 7B 模型在普通消费级显卡上带工具描述和历史的 prompt 可能要跑 3-5 秒而 4B 模型在量化之后可以做到 0.5-1 秒一次。8 步累积下来的差距就是半分钟和四分钟的差别用户体感完全不同。我不是说 7B 不能用而是说在决策场景下模型的“延迟-效果”性价比不如 4B 档。决策任务本身对知识广度的要求很低它不需要背百科知识只需要根据当前状态和可用工具做出正确动作。与其用大模型背知识不如用小模型做判别。这也是很多 agent 工具最终选择轻量模型作 controller、再挂一个大模型做知识问答的原因。4B 不是一个妥协而是一个经过测算的选择。2.2 候选底座对比以及“参数量之谜”我最初列了几个候选底座Qwen2.5-3B-Instruct、Llama-3.2-3B-Instruct、InternLM2.5-3B-Instruct。经过简单测试Qwen2.5 在工具调用格式上胜出——它原生就有 function calling 的训练数据输出 JSON 格式的稳定性比 Llama 好很多而 InternLM 的中文能力虽然不差但社区里后续配套工具链明显不如 Qwen 丰富。所以基座我选了 Qwen2.5-3B-Instruct。这里有个名字问题需要解释一下。为什么项目叫 4B但基座是 3B原因是我做了一部分参数扩展原始 tokenizer 的词汇表扩展了约 1.5 万个 token专门用来容纳自定义工具描述符号、状态标记和决策动作标记。embedding 矩阵和 LM head 随之变大加上后面合入的 LoRA 权重模型总参数量实际落到了 4B 左右。命名上的 4B 是按最终参数量来的不是按基座来的。这个操作在继续预训练里叫 vocabulary expansion代价是训练时需要重新初始化新增 embedding优点是不会挤占原有知识表示。如果你对参数档位敏感可以用这个思路复现。3. 决策轨迹数据从通用指令到“带工具的思维链”3.1 决策模型的输入输出长什么样在造数据之前必须先明确数据格式。我最终采用的是 agent trajectory 格式每条数据包含三部分环境状态、工具列表、多轮决策轨迹。工具列表必须以 JSON Schema 形式给到否则模型会自己发明不存在的工具名这在推理阶段是致命的。下面是一个最小示例{ system: 你是一个决策代理..., tools: [ { name: list_files, description: 列出目录下所有文件, parameters: { type: object, properties: {path: {type: string}}, required: [path] } }, { name: read_file, description: 读取文件内容, parameters: { type: object, properties: {path: {type: string}}, required: [path] } } ], trajectory: [ {role: user, content: 在仓库中找到所有包含 TODO 的文件}, {role: assistant, tool_call: {name: list_files, arguments: {path: /repo}}}, {role: tool, content: [src/main.py, README.md, tests/test_main.py]}, {role: assistant, tool_call: {name: grep, arguments: {pattern: TODO, path: /repo}}}, {role: tool, content: README.md:15: TODO: add usage}, {role: assistant, content: 在 README.md 第 15 行发现 TODO路径为 ...} ] }这里的关键点是每一步 tool_call 都必须严格符合 schema否则模型在采样时会越来越发散。我后面做训练时专门写了一个 JSON 校验器SFT 阶段只要出现不可解析的 tool_call直接丢弃该条轨迹。数据里混入一条坏数据模型学会的概率比其他数据高一个数量级这个坑后面还会展开。3.2 三类数据来源公开语料、程序生成、真实日志数据来源我分了三路每一路都有各自的问题和处理方式。第一路是公开工具调用语料包括 ToolBench、APIBench、Gorilla 这类数据集。优点是量大缺点是噪声多很多轨迹是“模型输出之后人工后处理过”的或者干脆是模拟环境里生成的和真实 agent 使用场景差距较大。我的处理方式是只保留其中带明确工具 schema 和可解析 tool_call 的样本再用规则脚本过滤掉“结论明显错误但仍然成功”的矛盾轨迹。第二路是程序化生成的决策链路。我写了一个轻量状态模拟器里面定义了几百个虚拟场景比如“你有一个配置文件需要修改某个 key 的值”“你需要找到监听 8080 端口的进程并查看它的日志”。每个场景都配置了事实表和规则矩阵然后根据规则枚举出多条可达成的路径包括最短路径和冗余路径。模型在学习时既要看到最优路径也要看到冗余路径的代价这样它才能学会“少走弯路”。第三路是真实 agent 运行日志。我在几个开源 agent 工具里跑了一批真实任务把成功和失败的轨迹都收集起来。尤其是失败轨迹价值极高——它教会模型“什么时候该停手、什么时候该换策略”。比如模型连续三次读到同一个文件却什么都没改这就是典型失败轨迹。我把这类数据也放进 SFT虽然会让模型的“尝试欲望”略微保守但整体决策稳定性大幅上升。最终我保留了约 36 万条轨迹、580 万个决策点。这个规模对 4B 模型来说已经足够再往上加数据边际收益递减不如把精力花在数据质量清洗上。3.3 关键判断点注释只留短推理不留长篇思维链这是我实践中得到的一个重要经验。很多做 agent 的人会默认“思维链越长越好”但决策模型恰恰相反。我在第一批实验里给每条轨迹都加了很长的 reason希望模型学习“为什么选这个工具”。结果模型确实学会了写 reasons但代价是生成时间翻了一倍而且在长上下文里注意力被稀释实际决策质量没有提升。后来我把每条轨迹中的推理压缩成“关键判断点注释”形式很短比如选择 read_file 而不是 grep因为路径已知直接读取更省一步先备份再修改因为该文件是配置入口改错可能导致服务启动失败选择停止并汇报因为两次搜索均无结果继续检索预期收益为负这类注释放在 tool_call 前的 assistant message 里相当于给模型一个“单步可解释理由”的示范。模型在推理时也会自然地输出一句简短判断但不会长篇大论。我自己实测下来一句话判断加一个 tool_call是决策模型在 agent 场景里最优的输出单位。4. 训练并不是只做 SFT用 GRPO 把“决策准确性”练出来4.1 第一阶段 SFT先让模型学会工具调用的“语法”训练分两个阶段。第一阶段是标准 SFT用上面构建的轨迹数据做 next-token 预测。这个阶段的目标非常朴素让模型能稳定输出格式正确的 tool_call并且不要在 JSON 里写多余字段。训练细节我直接给出方便复现基于 LLaMA-Factory 框架学习率 1e-5batch size 128训练 2 个 epochwarmup 200 步使用 fp16 混合精度单卡 A100 大约跑了 14 小时。SFT checkpoint 的验收标准只有一个在保留的 5000 条评测轨迹上tool_call 的 JSON 可解析率达到 98% 以上。如果达不到我倾向于认为是数据问题而不是参数问题回去重新筛数据比调参更有效。这个阶段完事之后模型已经会“像模像样”地调用工具了但离“好用”还差很远。问题在于 SFT 是模仿学习它只能学到数据里的平均行为。给一个见都没见过的任务模型往往会照着最相似的训练轨迹机械执行而不是根据当前状态动态调整。这正是第二阶段要解决的问题。4.2 第二阶段 RL用 GRPO 优化“决策结果”本身第二阶段我用的是 GRPO 算法组相对策略优化。思路和 DeepSeek 在数学推理上用的方法类似对同一个问题采样 N 条决策轨迹根据可验证的奖励函数打分然后计算组内优势并更新策略。它不需要训练一个独立的 critic 模型显存压力小落地更容易。我设计的奖励函数有三层权重从高到低排列第一层是最终结果是否正确。比如任务要求“找到所有抛出 TimeoutError 的文件”模型最终给出的文件和 ground truth 一致获得正分。这是最硬的可验证奖励。第二层是过程合法性。每一步 tool_call 是否可解析、工具名是否在 schema 内、参数是否合规。这个奖励可以直接通过 JSON 校验和 schema 校验计算。第三层是效率惩罚。每组轨迹内步数越少最终得分越高。这里我用了折扣因子每多一步就乘以 0.98 的衰减系数促使模型用最少的工具调用解决问题。GRPO 的核心训练循环大致长这样# 伪代码展示核心逻辑 for batch in sample_batch: trajectories policy.sample(batch, n_samples8) rewards compute_rewards(trajectories) # 可验证奖励 合法性 效率 advantages (rewards - rewards.mean()) / (rewards.std() 1e-8) policy.update(trajectories, advantages, kl_coef0.05)这里的 KL 系数我调成了 0.05比通用 RL 略高一点。原因是决策模型在 RL 中最容易出现的现象就是“奖励漂移”模型为了拿满分开始输出格式极其怪异的文本甚至把工具名称编成训练时见过的相似名称。KL 约束能把这个漂移拽回来代价是收敛速度慢一些。经过多轮实验0.05 是在稳定性和提升幅度之间最平衡的取值。4.3 撞见的 reward hacking写出来给你们避坑这里要重点说两个 RL 阶段必踩的坑。第一个是模型学会了“不调用工具直接回答”。具体表现是模型发现直接输出一个可能包含答案的字符串也能偶尔蒙对奖励。这个情况在大量简单任务上尤其严重因为有些任务的答案就藏在 system prompt 附带的状态摘要里。我的处理方式是在奖励函数里加一条硬规则——对于“必须调用工具才能完成任务”的任务类型如果模型在第一次输出时不带任何 tool_call直接扣掉 50% 奖励。这条规则虽然粗暴但非常有效。第二个坑是重复调用同一个只读工具来刷步数。模型发现只要反复调用 list_files 不报错过程合法性奖励就不会丢于是陷入死循环。我后来加了“重复动作惩罚”同一轮轨迹中若两次调用相同的工具且参数完全一致第二次起每步额外扣 0.1 分。这个操作让平均步数从 8.7 降到了 6.8收益立竿见影。这些细节在论文和官方文档里基本不会写因为它们看起来太像“工程补丁”。但在真实训练中这类 hack 占了调试时间的一半以上。如果你也要复现 GRPO 训练建议一开始就把上述两条硬规则写进奖励函数别等模型学会钻空子再补救。5. 评测结果是拿数据说话的和 Jev 正面碰了一下5.1 自建 DecisionBench 评测集我一直认为评测口径不确定的对标都是自嗨。所以这个项目里我专门做了一个评测集名字就叫 DecisionBench全部是真实可执行的任务。一共 300 条覆盖 6 类任务每类 50 条代码仓库缺陷定位给一个真实仓库路径任务是找到包含指定 bug 特征的文件和行号命令行操作序列根据目标生成一串 bash 命令比如查端口占用、杀进程、检查日志数据库查询生成与执行给表结构和问题生成 SQL 并执行验证多文档信息聚合在多个 markdown 文件里提取关键信息汇总到指定格式数据分析脚本生成基于 CSV 数据生成分析脚本并运行网页自动化通过 fetch、click、fill 这类工具完成指定页面操作每一条任务都有明确 ground truth可以自动判分。评测指标我重点看 4 个任务完成率、平均步数、工具调用格式合法率、无效动作占比。其中“无效动作占比”是很容易被忽略的指标它统计的是模型调用了一个工具、但该调用对完成目标没有帮助的比例。这个数字直接反映决策质量。5.2 对比结果以及我看到的差距我拿 NeoHorse-Jev-4B 和 Jev 做了同口径对比同时也放了两个 baseline 模型方便参考。Jev 用的是它能直接访问的服务端 API其余模型全部跑在同一台 A100 上温度设成 0.2最大输出长度 2048。模型任务完成率平均步数格式合法率无效动作占比Jev78.3%7.299.1%3.1%NeoHorse-Jev-4B76.7%6.898.7%4.2%Qwen2.5-3B-Instruct61.2%9.585.3%12.6%Llama-3.2-3B-Instruct48.7%10.179.8%18.3%看完这组数据说实话我还是很清醒的。NeoHorse-Jev-4B 在总体完成率上距离 Jev 还有 1.6 个百分点的差距虽然平均步数更少、效率上略胜但无效动作占比比 Jev 高。分任务拆开看差距主要在两类上网页自动化任务完成率低了约 6 个百分点多文档聚合任务低了约 4 个百分点。原因很好理解——我采集的真实 agent 轨迹里这两类任务的数据量本来就不够模型在“从未见过的页面结构”面前会犹豫会尝试用错误的 selector 操作页面。反过来在代码仓库缺陷定位和命令行操作这两类任务上NeoHorse-Jev-4B 的完成率反而比 Jev 高 2-3 个百分点。这个结果我认为不是能力碾压而是数据偏斜造成的我在程序化生成的决策链路里给这两类任务设计了大量边界情况模型见过的“坑”更多所以表现更稳。这也说明一个道理——决策模型的强项是可以被数据刻意塑造的哪类数据喂得多哪类任务就强。5.3 决策风格对比一句话判断加一个工具调用光看表格还不够我用一个实例来看两者的实际输出风格。任务描述是在仓库里找到 README.md 中所有提到 “UDP” 的段落并把包含 “UDP timeout” 的那行输出。Jev 的输出风格是{judge: 先读 README 再搜索避免重复过滤, tool_call: {name: read_file, arguments: {path: README.md}}}NeoHorse-Jev-4B 的输出是{tool_call: {name: read_file, arguments: {path: README.md}}, note: 单文件直接读} 然后第二轮才根据内容执行 grep。可以看出Jev 会在 tool_call 前附一句“判断理由”而且习惯先读后搜NeoHorse-Jev-4B 则几乎完全省略 reasoning动作更直接。这两种风格在决策任务里都是可以接受的但从“省 token”角度NeoHorse-Jev-4B 略占优势。不过这个问题也带来一个负面效应模型在复杂决策场景下有时会“过度直接”比如该查询数据库 schema 的时候直接猜表名。我后面在处理这个问题时是把“先查 schema 再执行查询”这类规则直接注入到 system prompt 的示例里而不是靠模型自觉。6. 部署与接入从 GGUF 量化到接入 codex / claude code 的实操6.1 用 vLLM 起一个 OpenAI 兼容服务模型训完之后落地部署我首选 vLLM原因就一个OpenAI 兼容接口是现在 agent 工具事实上的接入标准省得每个工具单独写客户端。如果你是跑在 24G 显存的卡上可以直接 fp16 加载如果显存 16G 或者更小上 8bit 也够用。启动命令很简单python -m vllm.entrypoints.openai.api_server \ --model NeoHorse-Jev-4B \ --tensor-parallel-size 1 \ --max-model-len 32768 \ --served-model-name NeoHorse-Jev-4B \ --gpu-memory-utilization 0.9 \ --enforce-eager这里有个很重要的参数是--enforce-eager。如果你的 CUDA graph 老是捕获失败或者模型跑起来会偶发显存不足加这个参数能把稳定性拉上来。代价是首 token 延迟略微变高但对 agent 场景来说可接受。6.2 量化到 GGUF 后交给 Ollama 运行如果你手头只有 8G 显存甚至纯 CPU 环境vLLM 就不合适了可以转成 GGUF 量化格式跑在 llama.cpp 或 Ollama 上。这个方案我实测很成熟推荐 Q4_K_M 量化档位。转换的大致流程是先导出一个 fp16 的合并后权重然后用 llama.cpp 的 convert 脚本分批转换最后用 quantize 工具量化到 Q4_K_M。有意思的是我在自己项目里测试时发现Q4_K_M 量化后 DecisionBench 完成率只下降了不到 1 个百分点而且格式合法率反而从 98.7% 上升到了 99.2%。我的解释是量化过程相当于给模型加了一层轻微的正则化让小概率的乱码输出被压掉了于是 tool_call 格式更稳定。当然这个现象未必适用所有模型但在这个 4B 决策模型上是真实存在的。Ollama 这边Modelfile 只需要指定基础模型路径和 context 长度FROM ./NeoHorse-Jev-4B-Q4_K_M.gguf TEMPLATE {{ .Prompt }} PARAMETER temperature 0.2 PARAMETER stop |im_end|6.3 接入 codex 和 claude code关键是把协议打通部署完服务和模型之后真正让 agent 工具用起来要解决的是协议兼容问题。codex 走的是 OpenAI 协议所以最简单。你只需要在环境变量里指定本地服务地址export OPENAI_BASE_URLhttp://localhost:8000/v1 export OPENAI_API_KEYsk-local然后在 codex 的模型配置里填NeoHorse-Jev-4B作为模型名codex 就会把“谁来调什么工具”的决策请求发给本地模型。接入 claude code 会比 codex 绕一点因为 claude code 本身走的是 Anthropic 的 Messages API 协议。我的做法是在中间加一层 LiteLLM proxy把 Anthropic 请求转成 OpenAI 兼容格式再转发给本地的 vLLM。LiteLLM 起 proxy 只需要一个配置文件model_list: - model_name: anthropic/claude-code litellm_params: model: openai/NeoHorse-Jev-4B api_base: http://localhost:8000/v1 api_key: sk-local然后设置export ANTHROPIC_BASE_URLhttp://localhost:4000 export ANTHROPIC_API_KEYsk-local这样 claude code 发起请求时会先到 LiteLLM proxy再由它转成 OpenAI 格式发给 vLLM。实测可用但有一个注意点LiteLLM 的日志打印非常吵建议启动时加上--detailed_debug关闭否则你会发现日志比模型输出还多。opencode 这类工具本身就支持 OpenAI 兼容 provider直接在配置文件里加一个 provider 指向本地端点即可。跑通之后的效果我自己的体感是在单轮决策延迟上NeoHorse-Jev-4B 比之前用大模型时要快至少三倍尤其是在连续调用工具的场景里任务整体完成时间几乎由执行子进程的耗时决定而不是由模型生成耗时决定。6.4 一个重要的部署教训工具列表别塞太多部署过程中我踩了一个很隐蔽的坑。有一次我为了让模型“能力更强”一口气注册了 30 个工具包括文件操作、网络请求、数据库查询等等。结果模型开始频繁调用错误工具而且错误方式非常一致——总是从工具列表的中段挑一个看着相似的。我把系统提示里的工具列表缩短到只剩当前任务真正会用到的 8 个之后决策准确率立刻回升。原因在于 4B 模型的注意力容量有限。工具描述写得太长模型在处理状态和工具之间的关联时会出现注意力稀释。所以部署决策模型时最有效的优化不是加工具而是减工具——把当前任务相关的工具优先级提到最前面无关工具直接不注册。这个原则和“大模型工具越多越强”刚好相反小模型要靠精简工具来保证决策质量。7. 后续迭代方向以及我最想提醒你的一条经验7.1 环境反馈闭环是最值得投入的方向现在的 NeoHorse-Jev-4B 还只是一个“单步决策器”输入是当前状态加工具列表输出是下一步动作。它并没有真正感知到工具执行后的错误信息比如命令退出码是 1、SQL 语法错误、页面元素不存在。下一步我在做的是把执行结果作为状态反馈重新喂进模型输入让模型学会根据反馈修正下一步动作。这样它能从一个静态决策器变成一个完整的 agent 闭环。7.2 关于项目本身的设计哲学做这个项目的过程中我反复确认了一件事开源一个模型最有价值的不是权重本身而是让其他人知道“哪些数据管用、哪些参数踩坑、哪些设计会走弯路”。所以我会逐步把 DecisionBench 评测集、程序化决策链路的模拟器脚本以及训练过程中过滤掉的问题轨迹全部整理放出来。仓库在 GitHub 上直接搜 NeoHorse-Jev-4B 就能找到推理脚本和量化权重已经可以跑。最后补一句真心话如果你想复现一个决策模型请优先收集失败轨迹。成功的轨迹教模型“怎么做是对的”失败的轨迹教模型“什么时候该认输、什么时候该换路”。后者的价值常常被低估。
返回列表