给Coding Agent加一层快速决策层:Jev如何让AI编程不跑偏

发布时间:2026/10/13 7:27:40
给Coding Agent加一层快速决策层:Jev如何让AI编程不跑偏
如果你也被下面这个场景折磨过那你大概能秒懂这篇文章想解决什么问题一个 Coding Agent 在某个中等规模页面功能上已经跑了二十多分钟。它先是翻遍了全仓库找类似实现然后犹豫要不要引入一个状态管理库接着又花了好几步去重构一个跟需求无关的模块最后提交了一个你自己看了都想摔键盘的版本。这不是模型不够聪明这是典型的 agent 决策失控。模型每一步都写得很好但整体方向却在无限发散。我在做模拟项目 X 的日常开发时反复遇到这类问题后来干脆在 Claude Code 和 Codex 外面套了一层轻量决策编排模块名字就叫 Jev。这篇文章会把 Jev 的设计思路、接入方式、踩坑经验完整记录下来——核心目标是给 Coding Agent 加一层快速决策能力让它在正确的时间选对引擎、在跑偏之前及时止损。1. 为什么 Coding Agent 需要一层快速决策——先看痛点1.1 模型没变笨是方向感丢了我有个很明确的观点现在大家抱怨 Coding Agent 不行绝大多数情况不是模型能力不行而是 agent 在错误的决策路径上走得太远。拿一个很常见的需求举例给用户详情接口加一层基于缓存的读写优化。如果没有决策层agent 的典型行为是先搜索全仓库里有没有现成的缓存工具类发现没有开始翻官方文档琢磨要不要引入第三方缓存库又顺手打开当前接口的实现觉得入口函数命名不够好于是开始顺手重构中途跑一次测试结果失败又花三步去排查测试环境问题最后确实加了缓存但同时也交回来十几个与本次需求无关的文件改动。这个过程里每一步单独看都合理合在一起就是灾难。问题出在哪出在每一步的局部最优决策没有服务于全局目标。模型并不知道哪些探索是必要的它只能靠上下文里的信息来决定下一步动作。如果没有一层机制在关键节点快速收敛方向它就很容易陷入无限探索的循环。我见过最夸张的一次是某个 agent 为了给一个老接口加参数竟然把隔壁两个服务的数据流都梳理了一遍美其名曰评估影响范围。实际上那部分代码和需求毫无关系纯粹是上下文里出现了相关字样它就顺着读下去了。1.2 慢决策带来的连锁反应慢不只是晚几分钟出结果的问题它会把三个问题一起放大token 消耗、上下文污染、错误累积。token 消耗是最容易感知的。一个中型任务让 agent 自由探索动辄烧掉几十万 token很多还是烧在无关代码阅读上。上下文污染更隐蔽agent 一旦读了几段无关代码这些内容就会残留在大脑里后续每一步判断都被这些噪声干扰。最直接的体现是它写着写着突然开始用另一个模块的命名风格或者引用一个根本不相关的工具类。错误累积则是雪崩式的。第一次小误判导致一个无关文件被修改第二次误判就会在这个文件已经被动过的前提下继续扩大改动。等开发者回头看 diff整个变更集里真正有价值的改动可能只占三分之一剩下的都是 agent 在错误方向上制造的历史包袱。我在模拟项目 X 里做过一次统计无决策层时一个 agent 完成改版任务平均要执行 12 到 18 步其中 30% 到 40% 属于探索性、低产出步骤。这个现象不是某一个模型独有的我分别用 Claude Code 和 Codex 跑过同一组任务都有类似表现只是具体形式不太一样。1.3 Jev 的定位不是新模型是纪律层所以 Jev 从一开始就不是更聪明的模型而是夹在任务与模型之间的一层纪律。它不写代码它管节奏和边界。需要回答的问题不是这行代码怎么写而是这个任务属于什么类型该用哪个引擎当前这个方向值不值得继续投入预算还剩多少要不要降级什么时候必须叫停哪怕会中断看起来很顺的推进。我把 Jev 比作一个贴身项目经理它不一定比工程师更懂技术但它负责盯着时间、范围、人力这三件事。模型负责生成能力Jev 负责收敛能力。两者配合Coding Agent 才既跑得快又不跑偏。2. Jev 决策层的整体设计与工作流2.1 决策层要回答的四个问题设计 Jev 时我把所有需要做的决策收敛成了四个问题。几乎所有坑都能归到这四个问题里做什么任务本身是否清晰模糊需求要先拆解不能裸着丢给模型。谁来干选 Claude Code 还是 Codex未来可能就是选 A 工具还是 B 工具。怎么干允许改哪些路径、禁止动哪些路径、用什么测试来验证。何时停什么时候该继续什么时候该切换什么时候该止损。这四个问题对应 Jev 里的四个模块预检器、路由表、预算控制器、健康监测器。模块之间通过一个统一的任务上下文对象通信这个对象贯穿任务生命周期相当于 agent 的共享记忆。2.2 接入两个引擎时的架构形态Jev 在架构上分三层。最上面是入口层接收开发者的任务请求无论通过命令行还是 IDE 插件接入都行。中间是决策层也就是 Jev 本体它不直接碰任何模型。最下面是执行层里面有两个适配器Claude Code 适配器和 Codex 适配器。决策层和执行层之间用一套引擎无关协议通信。所谓协议就是我定义的一个统一接口输入侧是任务描述、仓库快照和一组约束条件输出侧是事件流。适配器负责把协议翻译成各个引擎听得懂的话再把引擎的实时输出转换回统一事件流。这样做最大的好处是Jev 的核心逻辑完全不感知对面是 Claude Code 还是 Codex。换引擎时只需要新写一个适配器决策层、预算控制、健康监测这些公共能力完全不用动。当时决定这么搞就是不想把路由逻辑跟两套 SDK 的细节绑死否则每改一次都要同步改两边迟早把人逼疯。2.3 核心模块拆解预检器的职责是任务启动前做一次快速体检。它会扫描任务描述、涉及的文件路径范围、仓库里相关模块的规模然后输出一个复杂度评分。这个评分决定了后续的预算大小和路由策略。路由表根据复杂度评分和任务特征做引擎选择。初始规则很简单大范围重构类任务优先 Claude Code多方案探索验证类任务优先 Codex小改动直接走最快路径不绕弯。具体规则后面专门讲。预算控制器为每个任务分配 token 预算、步数预算和时间预算。预算不是死的任务推进过程中会根据实际完成度动态调整。比如任务预期 10 步完成但跑到第 7 步时只完成了一小部分Jev 会预警而不是盲目加预算。健康监测器是 Jev 最关键的模块。它盯的不是代码质量而是agent 是否在空转。判断标准很直接连续 N 步没有文件修改、没有触发测试、没有输出有信息量的工具调用结论就判定为空转触发干预。3. 接入 Claude Code 与 Codex接口与协议层的实现细节3.1 为什么不能直接分别调用两边 SDK很多人第一反应是接入两个引擎直接各自调 SDK 不就行了可以但你会被两个问题折磨。第一是接口风格差异巨大。Claude Code 偏向本地终端 agent 的交互方式工具调用、文件编辑、命令执行是主要动作它习惯在你当前的工作区里做事情。Codex 偏向云端沙箱的批处理方式强调隔离环境里的多步任务执行跑完之后交回一个变更集。如果你在 Jev 里直接对着两套 SDK 写逻辑路由、预算、健康检查这些公共能力就得写两遍每改一个参数都要同步两边。第二是难以统一做交叉判断。Jev 的价值在于把两个引擎的事件放在同一套语义下分析。如果两边事件格式都不一样健康监测器看到的东西就完全是两码事没法做横向对比。所以我在底层定义了一套统一协议用 TypeScript 描述大概是这样type EngineTask { id: string; instruction: string; repoSnapshot: string; allowedPaths: string[]; forbiddenPaths: string[]; budget: { maxTokens: number; maxSteps: number; maxSeconds: number; }; }; type EngineEvent | { type: step_started; stepIndex: number } | { type: tool_called; tool: string; args: unknown } | { type: file_modified; path: string; diffStats: { added: number; removed: number } } | { type: test_triggered; command: string; result: pass | fail } | { type: task_aborted; reason: string };输入侧统一了任务描述、仓库快照和约束条件输出侧统一了事件流。两个适配器只做一件事翻译。Claude Code 适配器把协议翻译成终端 agent 的任务提示Codex 适配器把协议翻译成云端沙箱的执行指令。3.2 两个适配器的具体适配点具体到实现两个适配器的差别比想象中大。Claude Code 适配器要做三件事把统一任务包转换成适合终端 agent 的任务提示监听它的步骤事件、工具调用事件和文件变更事件在任务包里附带一份战术约束文件相当于给它一段系统级的边界说明明确哪些事绝对不做。Codex 适配器就麻烦一点需要先把仓库快照同步到云端沙箱环境它的输入输出天然更结构化任务定义和验收条件都可以直接写清楚跑完一轮之后返回完整变更集Jev 轮询拿到变更集再转换回统一事件流。有一个差异特别值得提醒Claude Code 对当前分支上的存量修改有感知它会尊重你已有的未提交改动Codex 的沙箱是干净环境存量修改必须显式打包成 patch 传进去否则它完全不知道这回事。这个差异在我接入第一天就坑了一次——我最初以为两边对工作区状态的理解一致结果 Codex 跑出来的方案直接忽略了我本地一堆未提交的改动。但核心逻辑还是要落在统一协议里。协议稳了其他小差异都能通过适配器消化掉。3.3 路由策略什么时候选谁路由不能搞玄学我总结了几条很直白的规则任务描述里出现重构、迁移、拆分、大范围修改这类信号且涉及文件数量较多优先 Claude Code。它处理长上下文和既有代码结构的能力更强适合在一坨旧代码里做手术。任务描述接近搜索答案、对比方案、验证某个假设优先 Codex。这类任务交给隔离沙箱跑更安全、更干净不会把当前工作区搞得一团糟。非常明确的小改动比如改一个配置项、加一个字段直接交给一个引擎快速完成压根不需要决策层做太多判断。这里有个重要的设计原则路由的目的不是选最好的引擎而是减少来回切换和重复建上下文的成本。模型切换的成本非常高一个新引擎没有你前面积累的上下文等于把所有工作重做一遍。所以路由宁可保守一点也不要频繁换人。4. 快速决策的真正难点延迟预算与降级策略4.1 决策本身也有成本文章标题里有个关键词是快速决策能力这里的快速不是形容词是硬指标。如果 Jev 的每个决策都要去调一次完整大模型整个流程会变得更慢决策层反而成为新瓶颈。我做过一次很蠢的尝试让 Jev 在每个工具调用之后都问一遍下一步怎么走结果单任务耗时翻了将近一倍token 消耗也暴涨。这次尝试直接确立了 Jev 的第一原则能用规则决策就绝不调模型。规则决策的成本在毫秒级而且可解释、可调试。比如判断是否在小文件上做单行修改直接看 diff 统计就够了完全不需要模型出场。用最大型的工具解决最小的问题听着很爽实际上是在烧钱买延迟。4.2 四级决策降级策略Jev 的决策分四级按成本从低到高排列Level 0 纯规则决策。用关键词、文件路径统计、diff 规模这类硬信号直接输出结论。耗时一般小于 5 毫秒用于大多数确定性判断。Level 1 小型模型预判。需要做意图分类、任务类型识别这类模糊判断时用轻量模型跑一次。耗时在几百毫秒级别输出不是一个终局而是一个带置信度的候选推荐。Level 2 完整 LLM 评审。这是真正烧预算的决策只在关键节点触发比如要不要切换引擎、要不要终止任务、要不要修改预算。这种决策会带上完整任务上下文去问大模型耗时从两三秒到十几秒不等。Level 3 人工介入。当连续两次 Level 2 的决策置信度都低于阈值或者预算已经耗尽Jev 会把现场整理成一份结构化报告交给开发者做最终判断。这一步不追求自动化只追求输得明明白白。每个决策器输出结论时同时输出一个 confidence 分数。置信度低时Jev 会自动升一级决策方式再做一次交叉确认而不是直接采信低质量的结论。4.3 延迟预算与超时控制我给自己定的延迟预算很简单普通任务的全流程决策总耗时不超过 2 秒复杂重构任务不超过 15 秒。预算的含义就是决策层额外占用的时间这个时间必须从任务总时长里省回来否则没有意义。为了严格执行预算每个 Level 2 决策器都挂了超时器。超过 10 秒没返回Jev 直接采信 Level 1 的结果继续跑决策绝对不能阻塞执行。超时会自动记录到决策日志里事后复盘能看到底是哪个节点老超时。这个思路有点像高并发系统里的熔断机制决策服务再重要也不能拖垮主流程。宁可给一次次优判断也好过让整个 agent 停在原地干等。很多人在设计 agent 框架时忽略了这一点总是追求每一步都最优结果每一步都慢整体体验反而最差。5. 实测效果与关键调优从能用到好用5.1 接入前后的对比数据坦白说这类数据在不同仓库、不同模型版本下波动很大我不建议把我下面这组数字当标准答案只当参考。这是我们在模拟项目 X 上连续两周、共 40 个真实开发任务里统计的中位数指标裸跑无决策层接入 Jev 后单任务总耗时18.6 分钟9.4 分钟无效探索步骤占比35%12%token 消耗中等规模任务约 22 万约 11.5 万一次通过率不返工55%72%需要人工干预的任务占比30%18%数字好看的原因不只是决策变快更关键的是 Jev 砍掉了大量无意义探索让每一步都花在刀刃上。尤其是无效探索步骤从 35% 砍到 12%这一项直接带动了 token 和耗时的同步下降。5.2 踩坑记录误判、来回切换、过度压缩过程不可能一帆风顺挑三个印象最深的坑说。坑一用文件数量衡量复杂度严重误判。最初预检器把涉及文件数量当复杂度核心指标。结果遇到一个单文件三千行、早该拆没拆的老模块预检器给了一个低复杂度直接走了小改动快速路径。跑起来才发现改造量远超预期预算和引擎全都不对。修复方案是加入文件内部的信号TODO 数量、魔法值密度、重复代码块占比、导入关系复杂度综合起来评分不能只看文件个数。坑二频繁切换引擎反而更慢。我一开始在每一步都做路由看起来很智能实际是灾难。典型的场景是Claude Code 已经构建了丰富上下文刚准备发力Jev 看到新文件出现就切到 CodexCodex 需要重新同步整个仓库、重建上下文一次完整的任务启动成本就被白白浪费掉。修复方案是引入切换冷却期一个引擎至少要跑完当前里程碑或者累计 6 步以上才被允许切换。坑三上下文压缩过度agent 开始自由发挥。为了省 token我把历史记录压成摘要压到一定程度后 agent 忘了需求原文开始按自己的理解扩张改动范围。这比没压缩还可怕因为它连自己最初的出发点都丢了。修复方案是划出两个不可压缩区需求原文区和已作出决策的结论区。这两块内容永久保留完整信息其他过程细节才允许压缩。5.3 三个值得抄的参数如果你也想做一个类似的决策层我建议优先抄这三个参数空转判定阈值连续 3 步没有 file_modified 或 test_triggered 事件就判定为空转立刻让 agent 输出一段解释再由 Jev 决定继续、切换还是终止。切换冷却期默认 6 步。冷却期内除非出现测试全红或者明确的安全风险否则不换引擎。预算收尾模式剩余预算低于 15% 时Jev 强制把任务从继续探索切成整理产物并提交。这保证了任务没做完时也能留下一个能看、能跑、能回滚的半成品而不是所有成果都烂在 agent 的上下文里。这三个参数每个单独拿出来都不复杂但组合在一起就形成了完整的止损机制。空转判定保证了不白跑切换冷却保证了不白切收尾模式保证了不白烧。6. 从双引擎决策到多工具编排扩展思路与个人体会6.1 Jev 还能管什么Claude Code 和 Codex 只是我接入的第一批执行引擎。统一协议稳定之后Jev 的决策能力可以往外扩展很多。一个是多仓库任务拆解。大型改动横跨多个仓库时Jev 可以先用 Level 2 决策做一次全局拆解把子任务分发给不同引擎并行执行再统一汇总和验证。这个场景下决策层的价值比单仓库场景更明显因为跨仓库的上下文断裂问题更严重。另一个是接入更多工具信号。比如把测试框架的失败结果直接喂给健康监测器看 agent 是不是在同一个测试上反复碰壁把 lint 结果作为文件修改质量的实时反馈把编译错误频率作为任务难度的动态指标。这些信号都是现成的、结构化的接入成本很低但能显著提升决策的判断质量。如果再接上持续集成反馈决策层甚至可以往自动化运维大脑的方向走什么时候该跑验证、什么时候该降级到人工、什么时候该直接回滚它都能提前给出判断方向。6.2 我在实测中的几点体会做完这个接入项目我最大感受是给 Coding Agent 加决策能力本质上是给开发流程加一套止损纪律。模型没有边界感再聪明的模型也会在局部最优里越走越远。Jev 的价值不在于每次都做出最聪明的选择而在于它会主动叫停、主动切换、主动降级。宁可让 agent 少走几步冤枉路也好过让它闷头狂奔到最后交出一个不可用的结果。另一个经验是决策层的日志比决策本身更值钱。Jev 会把每次路由、每次干预都写成结构化日志比如{ taskId: demo-task-2311, ts: 2025-06-12T14:32:18.021Z, decisionType: engine_route, level: 1, confidence: 0.82, conclusion: route_to_claude, signals: { task_type: refactor, file_count: 14, complexity_score: 7.2 } }复盘的时候你能清清楚楚看到 agent 在哪个节点开始跑偏。这些数据比任何抽象建议都更有用。我现在已经养成了习惯每次任务结束先翻决策日志而不是只看最终 diff。6.3 后面打算怎么做目前 Jev 还只覆盖任务启动前和任务执行中两段决策。下一件事是把决策日志做成可回放的事件流这样任务失败后可以精确看到 agent 从哪一步开始偏离目标再针对这个节点做专项约束。这个回放能力我目前只能靠人工翻日志实现还没有完全自动化。另一个方向是把预算控制从按任务分配升级成按收益动态分配。同样 100 个 token 的预算投入在验证假设上的收益更高那决策层就该给探索性步骤让路而不是平均分配。这个模型还在打磨等稳定了我再单独写一篇。至于最后会不会把 Jev 整理成一个独立开源项目我倾向于会但还需要一段时间把现有的内部逻辑和协议文档化毕竟现在它身上还带着很多模拟项目 X 的特化逻辑。