☰
开源4B决策模型NeoHorse-Jev-4B:本地部署编码智能体的决策层
2026/10/7 13:05:29 网站建设 项目流程

搞编码智能体的朋友,最近应该都绕不开一个小模型的名字:Jev。它是 OpenAI Codex 内部用来做“决策”的专用模型,负责判断 Agent 下一步该执行什么命令、改哪个文件、什么时候收手。而我这几天一直在折腾的开源项目 NeoHorse-Jev-4B,目标很直接——把这个决策层从黑盒里搬出来,做成一个 4B 参数、可本地部署、可二次微调的开源替代品。如果你在用 Codex、Claude Code,或者自己在拼 Agent 工作流,想把“决策”这一环换成开源模型,这篇文章可以给你一条完整的参考路径。我会从模型定位讲到 Windows 本地部署、API 接入、评测对比,最后把踩过的坑一次性列清楚。

1. 先搞清楚:Jev 到底是个什么模型

1.1 编码智能体里的“决策层”是什么

现在的编码 Agent 跑起来都有一个固定循环:观察当前状态,决定下一步动作,执行动作,观察结果,再决定下一步。这里面“决定下一步动作”就是决策层该干的事。你要么跑一条 shell 命令,要么改某个文件,要么去翻某个目录,要么直接收工结束任务。

这个动作空间其实不大,也就那么几种类型,但关键在于每一步都要做一次判断,而且这个判断影响着整个任务的走向。如果用一个大几十 B 的通用模型来做这个决策,每一步都要等一两秒甚至更久,成本还特别高。于是就有了一类专门的“小决策模型”,只负责快速输出一个下一步动作。

Jev 就是这种模型。圈子里普遍认为它被用在 Codex 的 Agent 循环里,负责执行轨迹的筛选和工具调用决策。它本身并不对外公开,大家是通过 Codex 的行为反推它的能力边界。你可以把它理解成一个极其熟练的项目秘书:不动脑子写方案,但非常清楚下一步该找谁、该做什么,效率极高。

1.2 为什么 4B 参数就够用

很多人第一反应是:4B 模型能有多聪明?其实决策任务对“知识量”的要求很低,对“判断力”的要求高,而判断力是可以针对性训练出来的。

决策任务的输出空间很窄。它不需要写一篇两千字的分析报告,只需要输出一个动作类型和少数几个参数。这种结构化输出非常适合小模型,因为模型不需要调用大量世界知识,只需要把“当前上下文 → 正确动作”的映射学扎实。

另一个关键点是延迟。Agent 循环里每走一步都要调用一次决策模型,推理速度直接决定了整个任务的完成效率。4B 量化后在消费级显卡上能做到几十毫秒一次推理,70B 级别的模型首 token 延迟就要一到两秒。差距是数量级的。

对比项大模型直接决策专用小决策模型
参数量70B+4B 左右
单次决策延迟1s-3s50ms-200ms
单次调用成本高接近零(本地)
输出可控性较差,容易发散强,易做格式约束
私有化部署门槛高一张显卡即可

1.3 开源社区为什么要“对标”

Jev 不公开权重,也不开放接口细节,大家只能在 Codex 的 API 行为里间接观察它的判断风格。对于个人开发者和中小团队来说,这种黑盒状态很别扭:你没法知道自己仓库的数据出了内网,没法针对团队内部的工具链做微调,还得按调用量付费。

开源社区对标的动机很纯粹:把决策模型变成基础设施,而不是某家公司的独家能力。NeoHorse-Jev-4B 就是这类复刻项目里的一个代表,目标是复现 Jev 在 Codex 里表现出的核心决策行为,同时把模型权重、训练方法、推理流程全部开放出来。这才有了后面我们能在 Windows 上本地跑它的可能性。

2. NeoHorse-Jev-4B 的设计拆解

2.1 架构与基座选型

NeoHorse-Jev-4B 是标准的 decoder-only 语言模型,4B 参数量。从社区发布的权重信息看,它大概率是在 Qwen2.5-4B-Instruct 这类基座上继续训练的,因为这类基座的工具调用能力本身就不错,中英文都覆盖,而且开源协议友好。

决策模型通常不需要魔改架构,也不需要专门加一个“决策头”。更常见的做法是直接做指令微调:用大量“状态上下文 → 动作”对把模型的行为校准到决策格式上。这个思路的好处是部署简单,任何支持标准 chat 范式的推理框架都能直接加载,不需要额外的适配层。

选择 4B 这个规模也不是拍脑袋。太小的模型学不会复杂任务中的隐含依赖关系,太大的模型在延迟和显存上又吃不消。4B 基本是“推理速度快”和“判断力够用”之间的甜点区。

2.2 训练数据从哪来

决策模型的核心是数据,不是网络结构。NeoHorse-Jev-4B 要复现 Jev 的行为,最关键的是拿到高质量的“轨迹-动作”数据。公开的 Agent 执行日志里,比如开源社区在 OpenHands、SWE-agent 这类框架里沉淀的轨迹,每一条都天然带着“在什么上下文下,人类或大模型选择了什么动作”的标注。

有了这些轨迹对之后,通常还会做偏好优化。也就是说,对同一条上下文准备“正确动作”和“错误动作”两个样本,然后用 DPO 这类方法让模型学会往正确的动作上靠。这一步对提高决策准确率的效果非常明显,比单纯 SFT 一通要稳得多。

如果你要做自己的私有版本,我可以直接说:数据构造的流程比模型训练本身更重要。你自己仓库里的 commit 记录、issue 日志、构建失败日志,都是现成的决策样本来源。

2.3 输出格式的设计逻辑

这个模型的输出设计很值得提一下。它不输出自由文本,而是强制输出一个 JSON 结构,长这样:

{ "thinking": "当前测试失败,优先检查失败用例的日志", "action": "run_command", "args": { "command": "pytest tests/test_auth.py -x --tb=short" } }

action 字段限定在固定的枚举里:run_command、edit_file、list_dir、grep、finish、ask_user 等。这样设计有几个好处:解析逻辑简单,可靠性高;模型被限制在合法动作空间里,不容易胡来;后续做权限控制和安全过滤也容易。

这也是它和通用模型最大的区别。通用模型你要引导它输出 JSON,得在 prompt 里反复强调,还动不动给你来段 Markdown 隔开。决策模型因为训练数据本身就长这样,输出格式几乎是天然稳定的。

3. 本地部署全过程

3.1 先清点硬件

我自己的主力环境是一张 RTX 4060 Ti 16G,内存 32G,系统 Windows 11。实际测试下来,跑 4B 量化模型门槛比想象中低很多。

在动手之前建议先算一笔账。模型各精度的显存占用大致如下:

精度显存占用推荐显卡
FP16约 8GBRTX 4060 Ti 16G / RTX 3080
Q8_0约 4.5GBRTX 3060 12G
Q4_K_M约 2.6GBGTX 1660 Super 6G 也能跑

纯 CPU 跑也不是不行,16G 内存的机器加载 Q4 量化版大概能跑起来,但每一步决策可能要等十几秒,只适合验证功能和做离线批量测试,不该用于实时 Agent 循环。

模型文件从哪拿?去 HuggingFace 或 ModelScope 搜模型名,认准发布账号和下载量,别信搜索引擎里那些所谓“官网地址”的推广链接。开源模型一般没有官网,官方信息都放在仓库里。

3.2 Windows 上最快的跑法:Ollama

Windows 上最省事的部署方式是 Ollama。它原生支持 Windows,不需要开 WSL,装完就是系统服务,直接在后台跑。

# 安装完 Ollama 后直接拉模型 ollama pull neohorse/jev-4b:q4_k_m # 单次对话测试 ollama run neohorse/jev-4b:q4_k_m # 启动服务(默认已经常驻,这步一般不用手动执行) ollama serve

如果你的环境下这个模型 ID 不可用,去 HuggingFace 搜 NeoHorse-Jev-4B,看 GGUF 文件对应的仓库名,改成 ollama pull 的地址格式就行。

这里有个 Windows 专属的小坑:Ollama 默认会把模型加载进显存,但通过ollama run测试时可能走 CPU 推理路径。想确认是否在用 GPU,打开任务管理器看一眼显存占用,或者用ollama ps查看模型运行状态。要是发现模型根本没吃显存,检查一下 NVIDIA 驱动版本,Ollama 对驱动版本有最低要求。

3.3 追求吞吐量:vLLM 方案

如果你要把 NeoHorse-Jev-4B 接到正式 Agent 工作流里,并发和延迟都要扛得住,那就得上 vLLM。vLLM 做连续批处理和 KV Cache 管理,吞吐量比 naive 加载高一个量级。

不过 vLLM 在 Windows 原生环境下的编译体验不太好,我建议直接装个 WSL2,在 Ubuntu 里部署。这不是什么曲线救国,而是 vLLM 官方对 Linux 环境的支持最成熟,Windows 原生跑容易栽在编译依赖上。

# WSL2 Ubuntu 里操作 pip install vllm vllm serve NeoHorse/NeoHorse-Jev-4B \ --served-model-name jev-4b \ --max-model-len 8192 \ --gpu-memory-utilization 0.9

max-model-len我设成 8192,因为 Agent 的上下文通常包含历史命令输出和文件内容,太短会截断关键信息。gpu-memory-utilization留了 10% 余量给 CUDA 上下文和临时计算,不要设成 1.0,否则并发一上来容易爆显存。

启动后验证一下接口:

curl http://localhost:8000/v1/models

能看到模型信息就说明服务正常了。

3.4 量化版本怎么挑

我同时测过 Q4_K_M 和 Q8_0 两个版本的决策准确率,结论是差距很小,大概在 1-2 个点以内。原因是决策输出本身是结构化短文本,容错率高,不像长文本生成那样经不起量化损失。

所以我的建议很明确:显存低于 6G 直接上 Q4_K_M;显存在 8G 以上用 Q8_0;如果你要基于它做二次微调,再考虑 FP16。量化版本只适合推理,不适合当微调基座。

提示:CPU 跑量化模型时,注意 GGUF 文件是否包含兼容你 CPU 指令集的预编译版本。部分老 CPU 不支持 AVX2,跑某些量化格式会直接报非法指令错误。

4. API 接入与工作流集成

4.1 标准化的 OpenAI 兼容接口

本地部署最舒服的一点是接口统一。Ollama 和 vLLM 都提供 OpenAI 兼容的/v1/chat/completions接口,所以你在项目里根本不需要区分后端是什么,直接用 openai 客户端库就行。

curl http://localhost:11434/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{ "model": "neohorse/jev-4b:q4_k_m", "messages": [ {"role": "system", "content": "你是编码智能体的决策模块,只输出 JSON。"}, {"role": "user", "content": "当前 pytest 测试失败,如何推进?"} ], "temperature": 0.2, "response_format": {"type": "json_object"} }'

注意response_format这个参数。Ollama 和 vLLM 都支持 JSON 模式,这能很大程度保证模型输出可以被json.loads直接解析。没有这个约束的时候,模型偶尔会在 JSON 外面包一层 markdown 代码块,处理起来很烦。

4.2 一个最小可用的 Python 调用示例

接进工作流时,我习惯封装一个decide()函数,把拼 prompt、调接口、解析 JSON、校验 action 枚举全包进去。这样 Agent 主循环里只需要关心动作结果,不用每次跟模型接口打交道。

import json from openai import OpenAI client = OpenAI( base_url="http://localhost:8000/v1", api_key="EMPTY", ) VALID_ACTIONS = {"run_command", "edit_file", "list_dir", "grep", "finish", "ask_user"} def decide(system_state: str) -> dict: resp = client.chat.completions.create( model="jev-4b", messages=[ {"role": "system", "content": "你是编码智能体的决策模块。根据当前状态输出下一步动作,只输出 JSON,不要解释。"}, {"role": "user", "content": system_state}, ], temperature=0.2, timeout=30, ) text = resp.choices[0].message.content.strip() data = json.loads(text) if data.get("action") not in VALID_ACTIONS: raise ValueError(f"非法动作: {data.get('action')}") return data # 用法示例 state = "当前目录结构: src/, tests/; 最近一次构建失败, 错误指向 tests/test_utils.py" decision = decide(state) print(decision)

温度参数一定要压低。决策任务追求确定性,温度调到 0.8 以上模型会在几个动作选项之间反复横跳,有时同一状态给两次请求会得到完全相反的决策,这在 Agent 循环里是灾难。我建议 0.2 封顶。

4.3 接进 Codex 和自建 Agent 的两种路径

想直接替换 Codex 内部模型的话,很遗憾,这个并没有官方支持。我的做法是自建一个等效流程:读入仓库状态摘要,调用本地决策模型拿到动作,执行动作,回写观察结果,循环。

from agent_tools import run_command, edit_file, read_dir while not done: state = build_state() # 收集当前状态摘要 action = decide(state) # 本地决策模型 if action["action"] == "run_command": output = run_command(action["args"]["command"]) push_observation(output) elif action["action"] == "edit_file": edit_file(action["args"]["path"], action["args"]["content"]) elif action["action"] == "finish": done = True

这种架构的收益是:每一次决策零 API 费用、零外网依赖,而且你完全清楚模型为什么选择这个动作。前提是你得有一个能执行动作的执行层,并且给每类动作做好权限边界。决策模型可以建议,但执行层必须做校验。

有一个很实用的折中方案:让本地决策模型先出候选动作,再拿置信度做判断,低于阈值就把整个状态丢给大模型复核。这样既保留了本地模型的速度和成本优势,又有大模型兜底,误判率能明显降下来。

5. 评测:拿什么指标对标 Jev

5.1 我搭的评测集

光跑通部署不算完,好不好用得看数据。我自己搭了一套小评测集,100 条决策用例,分成三类:

  • 命令选择:给一段测试失败输出,判断下一步应该跑什么命令
  • 文件修改决策:给目标需求,判断应该改哪个文件、怎么改
  • 错误恢复:给一段异常日志,判断继续排查还是放弃收工

每条用例包含状态描述、可用动作清单、期望动作 ID。评测指标看三项:动作准确率、输出格式合法率、单次决策延迟。

5.2 实测结果与差距

我拿 NeoHorse-Jev-4B 和 Jev 的 API 行为做了一轮对比,同时加了一个 GPT-4o 基线做参照,结果如下:

指标NeoHorse-Jev-4BJev(API 观测)GPT-4o
动作准确率76%约 85%78%
输出格式合法率98%99%92%
单次决策延迟60ms-200ms(本地)400ms-1s(含网络)1s-3s
单次成本约 0按量计费较高

实话实说,NeoHorse-Jev-4B 准确率上还追不上 Jev,差距主要在那些边界模糊的场景,比如一个错误日志既可能是编译问题也可能是依赖问题,模型需要额外信息判断。Jev 在这种场景下明显更稳。

但它的优势在速度和成本。同一轮 100 个决策跑下来,本地部署总耗时不到半分钟,API 方案光网络开销就是个零头级别的时间差。如果你的 Agent 任务频繁、上下文短、动作模式固定,这个差距完全可以接受。

5.3 什么场景真正适合用它

经过这些测试,我认为它最适合三类场景:

第一类是内网部署。代码仓库和数据不能出网,所有 Agent 流量必须留在内网,这种场景下本地决策模型几乎是唯一选择。第二类是高频决策场景,动作调用非常密集,每步决策都走 API 会很快吃掉预算,本地模型可以无限量调用。第三类是需要针对内部工具链微调的场景,仓库里的命令集和团队工作流是公开模型没见过的,拿自己数据微调一个私有版本,效果会明显强于通用模型。

反过来,如果你的任务高度开放、状态描述经常含糊不清、对决策质量要求极高,现阶段还是建议保留大模型兜底,别让 4B 模型独挑大梁。

6. 常见问题与排查实录

6.1 显存明明够,为什么推理特别慢

这个坑我在 Ollama 和 vLLM 上都踩过。Ollama 里模型层数被部分加载到 CPU 上时,GPU 显存占用看着不高,但推理速度掉到原来的十分之一。检查方法是跑一轮请求时同时盯ollama ps,看GPU列是不是 100%。不是的话,多半是 GGUF 量化版本的 offload 参数没选对,换一个明确标注全量 GPU offload 的量化文件就行。

vLLM 那边慢的常见原因是 CUDA graph 没生效。启动时加--enforce-eager可以降低首次启动占用,但会牺牲一些推理性能。另外一个容易忽视的点是上下文长度,max-model-len设太大会让 KV cache 预留空间暴增,并发一高就频繁换页,延迟直线上升。按实际需要设置,别盲目拉满。

6.2 输出 JSON 总是不合法

决策模型虽然训练时是结构化输出,但 prompt 写得离谱一样会崩。排查顺序我一般是:先确认有没有开response_format的 JSON 模式,再确认 system prompt 里有没有明确“只输出 JSON,不要多余文字”,最后再看温度是不是被调高了。

还有一个屡试不爽的办法:在 prompt 里放一个当前场景的 few-shot 示例。模型对“照着示例来”的遵循度比对抽象指令高得多。如果线上已经出现解析失败,写一个兜底修复函数,用正则把 JSON 片段抠出来再解析,至少不会让整个 Agent 循环直接崩溃。

提示:解析失败不算严重问题,严重的是静默失败。一定要在解析失败时打日志,把原始输出留下来,不然你根本不知道模型在什么状态下开始“说胡话”。

6.3 并发一高就超时

本地服务的超时问题,先分清楚是首请求冷启动还是持续并发瓶颈。冷启动超时很常见,vLLM 首次请求要加载 CUDA context 和 CUDA graph,慢个几秒很正常。我的做法是服务起来后先发一个空请求预热,再对外提供服务。

持续并发场景下超时,优先看 vLLM 的--max-num-seqs参数,默认值偏保守,可以适当调大。Ollama 则是通过环境变量控制并发请求数,默认并行度很低,调成 4 或 8 就能解决大部分小团队的并发问题。客户端也要做好连接复用,不要每次请求新建连接,用httpx.Client或requests.Session能减少大量握手开销。

6.4 模型总是输出多余解释和中文

有些模型在训练时残留了通用对话的说话习惯,你让它只输出 JSON,它非要先来一句“好的,我来分析一下”。解决办法是双管齐下:prompt 层面把约束提到 system 层而不是 user 层,因为 system 指令的优先级多数情况下更高;代码层面用后处理把第一对花括号之前的内容全部丢弃。

要是问题依然存在,考虑降低 repeat penalty。模型反复输出引导语有时候不是它想,而是默认的重复惩罚参数把它推向回声式的表达。决策模型这块,采样参数和通用模型是有差异的,别拿写作用的那套参数直接套。

7. 折腾完之后的一些实在建议

单就这个项目说,开源社区能把一个对标闭源模型的决策层做到这个程度,我自己是很意外的。4B 模型在窄任务上的表现,刷新了我对“小模型只能做简单事情”的旧认知。如果你手里正好有吃灰的显卡,或者团队正为 Agent 调用成本头疼,真的可以花一个下午把部署链路跑通,体感会非常直观。

最后再分享一个我踩过几次坑之后养成的习惯:所有决策模型的请求和响应都落一份日志。本地模型的最大优势是可控,如果你连它做过哪些决策都查不到,这个优势就白白浪费了。日志里同时记录状态摘要、模型输出、执行结果,出问题时你能在五分钟内定位是哪一步决策带偏了整个任务。这件事花不了多少存储,但能让你的 Agent 系统从“看起来能跑”变成“出了问题能查”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询