System-1 判断模型接入 agent loop:分层决策与 MCP 实践

发布时间:2026/10/6 20:20:16
System-1 判断模型接入 agent loop:分层决策与 MCP 实践 1. 为什么我会想到把 System-1 判断模型塞进 agent loop先说清楚我在折腾什么。System-1 判断模型指的是那种不追求长链推理、专门做快速直觉式判定的轻量模型——你可以把它理解成 agent 的条件反射层。而 agent loop就是智能体反复执行观察-思考-行动-再观察的那个主循环。我做的事情很直接在原本由大模型主导的循环里插入一个专门负责快速判断的小模型让它先对当前状态做一次要不要继续、要不要换路、要不要停的即时决策再把结果交给主模型去执行。为什么会有这个念头因为我在用 Claude Code 这类工具做长任务时反复遇到同一个痛点主循环里每一步都要调用一次大模型哪怕只是判断这个文件是不是已经改过了这个命令的输出是不是报错也要走一遍完整推理。延迟高、成本高而且大模型经常想太多——一个本该秒判的事情它能给你写三段分析。这就像你开车时每个路口都要停下来开个会讨论要不要转弯效率低得离谱。System-1 这个概念本身来自认知科学里快慢思考的划分。快思考负责直觉判断慢思考负责复杂推理。我把它搬到 agent 架构里核心诉求就一个把高频、低复杂度、模式化的判断从大模型手里剥离出来交给一个又快又便宜的小模型。Laya 这类模型之所以被我在标题里点名是因为它在轻量判断任务上的响应速度和稳定性实测下来比让主模型兼职做判断要靠谱得多。这套东西适合谁如果你正在用 Claude Code、Codex 或者自己搭 agent 框架并且已经被每步都调大模型的成本和延迟折磨过那这篇就是写给你的。如果你只是偶尔用用对话式 AI那可能暂时用不上但理解这个分层思路对你后面搭任何自动化流程都有好处。下面我会把整个设计思路、接入细节、踩过的坑全部摊开讲。2. 整体架构设计System-1 与主循环怎么分工2.1 分层判断的核心逻辑我最终定下来的架构是三层感知层、判断层、执行层。感知层负责把当前环境状态文件变化、命令输出、工具返回整理成结构化输入判断层就是 System-1 模型它只回答几个封闭式问题执行层才是主模型负责真正干活。这里最关键的设计决策是判断层的输出必须是枚举值不能是自由文本。我一开始让 System-1 输出自然语言判断结果它偶尔会发挥给出模棱两可的答案主循环拿到之后还得再解析一遍反而更慢。后来我改成强制它只输出CONTINUE、RETRY、SWITCH_TOOL、ABORT、NEED_REASONING这五个标签之一整个链路立刻清爽了。为什么是这五个因为它们覆盖了 agent loop 里 90% 以上的分支决策。CONTINUE是正常推进RETRY是当前动作失败但值得重试SWITCH_TOOL是当前工具不适用要换ABORT是任务已经无法完成要止损NEED_REASONING是判断不了、需要升级给主模型。最后这个标签是整个设计的安全阀——System-1 拿不准的时候必须能主动把球踢回给大模型而不是硬猜。2.2 为什么不让主模型自己判断有人会问主模型本来就能判断为什么要多此一举我实测过两种方案差距很明显。让主模型每步都判断一个中等复杂度的重构任务平均要 40 到 60 次大模型调用插入 System-1 之后其中大约 70% 的判断被小模型消化掉大模型调用降到 15 次左右。延迟从平均 8 分钟压到 2 分半成本直接砍掉一大半。更重要的原因是判断质量。大模型做判断时容易被上下文里的无关信息干扰比如它看到前面有一大段代码就会倾向于认为这里需要仔细分析哪怕当前只是判断一个文件是否存在。System-1 因为输入被裁剪得很干净只看到判断必需的信息反而判得更准。这就像让一个专注的哨兵和一个博学的教授同时看门哨兵的反应往往更快更准。2.3 与 MCP 协议的关系这里必须提一下 MCP。MCP 协议本质上是给 agent 提供标准化工具调用的通道我的 System-1 判断层也是通过 MCP 暴露成一个工具给主循环调用的。这样做的好处是解耦——判断模型可以独立部署、独立升级主循环不需要知道它背后是 Laya 还是别的模型只需要按 MCP 的格式发请求、收结果。我试过两种接入方式一种是直接在 agent 代码里 import 判断函数另一种是走 MCP 工具调用。前者快但耦合重后者多一层网络开销但灵活。最终我选了 MCP因为判断模型需要频繁调整 prompt 和阈值走 MCP 可以热更新不用重启整个 agent。这个取舍后面在实操部分会详细讲。3. 核心细节拆解判断层的输入输出怎么设计3.1 输入裁剪只喂判断必需的信息System-1 判断准不准八成取决于输入干不干净。我踩过的最大坑就是一开始把完整上下文都塞给它结果它被无关信息带偏。后来我定了一套裁剪规则每次判断只传三类信息——当前动作、动作结果、以及一个极简的状态摘要。具体来说当前动作就是刚才执行了什么比如write_file、run_command动作结果就是成功/失败/输出摘要注意是摘要不是全文命令输出超过 500 字符就截断状态摘要是一句话描述任务进度比如已完成 3/7 个文件修改。这三样加起来通常不超过 800 tokenSystem-1 处理起来毫无压力。提示裁剪规则里最容易出错的是输出摘要。我建议对报错信息做特殊处理——保留错误类型和关键行号丢掉堆栈里的路径细节因为路径往往包含随机字符串会干扰判断。3.2 输出约束用 logit 屏蔽强制枚举光在 prompt 里写只输出这五个标签是不够的模型偶尔还是会输出别的。我的做法是在推理层做 logit 屏蔽把这五个标签对应的 token 之外的概率全部压到负无穷。这样无论模型多想发挥物理上只能吐出这五个词之一。如果你用的是本地部署的 Laya 或者类似模型大多数推理框架都支持这种约束解码。如果走 API那就退而求其次在 prompt 里加 few-shot 示例再在解析层做兜底——解析不到合法标签就一律当成NEED_REASONING交给主模型处理。这个兜底逻辑救过我很多次因为判断层偶尔抽风时最安全的做法就是让它认怂。3.3 阈值与置信度给判断加一道保险System-1 输出标签的同时我还会让它给一个 0 到 1 的置信度。这个数字不是让它自己感觉而是从 logit 分布里算出来的——取最高概率标签的 softmax 值。置信度低于 0.6 的时候即使它输出了CONTINUE我也会强制升级成NEED_REASONING。这个阈值是调出来的。我一开始设 0.8结果太多判断被升级System-1 形同虚设设 0.4 又太激进误判率上升。最后在 0.6 附近找到了平衡点误判率控制在 3% 以内同时保留了 70% 的判断分流率。这个数字跟具体模型和任务类型有关你在自己场景里要重新标定。判断标签触发条件后续动作CONTINUE动作成功且进度正常主循环推进下一步RETRY动作失败但可重试重试当前动作最多 3 次SWITCH_TOOL当前工具不适用主模型换工具重新规划ABORT任务无法完成终止循环并报告NEED_REASONING置信度低或情况复杂升级给主模型判断4. 实操过程从零把判断层接进 agent loop4.1 环境准备与模型部署我用的环境是 Ubuntu判断模型跑在本地。部署 Laya 这类轻量模型显存占用大概 4 到 6 GB一张消费级显卡就够。如果你没有本地显卡也可以走 API但延迟会高一些判断层的优势就没那么明显了。部署步骤大致是拉模型权重、起推理服务、暴露一个 HTTP 接口。我用的推理框架支持约束解码这是关键。起服务的时候记得把最大输出长度设成 8 个 token 就够因为输出就是五个标签之一设长了纯属浪费。批处理大小设 1因为判断请求是串行的批处理反而增加延迟。# 启动判断模型服务注意约束解码和短输出 python -m inference_server \ --model laya-judge \ --max-tokens 8 \ --constrained-labels CONTINUE,RETRY,SWITCH_TOOL,ABORT,NEED_REASONING \ --port 8100服务起来之后先别急着接 agent用 curl 单独测几轮确认它稳定输出合法标签。我见过有人跳过这步结果接进 agent 后各种解析失败排查半天才发现是模型服务本身没配好约束解码。4.2 通过 MCP 暴露判断工具接下来把判断服务包装成 MCP 工具。MCP 工具的定义很直白一个名字、一段描述、一组参数 schema。我的判断工具叫judge_step参数就三个action、result、progress。{ name: judge_step, description: 对当前 agent 步骤做快速判断返回下一步动作标签, inputSchema: { type: object, properties: { action: {type: string}, result: {type: string}, progress: {type: string} }, required: [action, result, progress] } }这里有个细节MCP 工具的 description 会被主模型读到所以描述要写得让主模型明白什么时候该调这个工具。我写的是在每次工具调用返回后、决定下一步之前调用这样主模型就知道该在循环的哪个位置插入判断。4.3 改造主循环插入判断节点主循环的改造是整件事的核心。原来的循环是执行动作 → 看结果 → 大模型决定下一步现在变成执行动作 → 看结果 → System-1 判断 → 根据标签分流。分流逻辑我写成了一个 switchCONTINUE就直接进下一步RETRY就重试SWITCH_TOOL和NEED_REASONING都交给主模型ABORT就退出。注意SWITCH_TOOL和NEED_REASONING虽然都升级给主模型但语义不同——前者是确定要换工具后者是我不确定主模型收到的提示词也不一样。def agent_loop(task): state init_state(task) while not state.done: action state.next_action result execute(action) verdict call_mcp_tool(judge_step, { action: action.name, result: summarize(result), progress: state.progress_summary() }) label, confidence verdict.label, verdict.confidence if confidence 0.6: label NEED_REASONING if label CONTINUE: state.advance() elif label RETRY: state.retry() elif label in (SWITCH_TOOL, NEED_REASONING): state main_model_replan(state, hintlabel) elif label ABORT: state.abort() return state.result这段代码看着简单但每一行都是踩坑换来的。比如summarize(result)这个函数我改了不下十版因为摘要质量直接决定判断准确率。还有重试次数我一开始没限制结果遇到一个必然失败的动作System-1 一直判RETRY循环卡死。后来加了硬上限 3 次超过就强制ABORT。4.4 参数标定置信度阈值怎么调阈值标定我建议用真实任务跑一批数据。我的做法是收集 200 个历史步骤人工标注每个步骤的正确判断然后让 System-1 跑一遍画出不同阈值下的准确率和分流率曲线。准确率是判断正确且置信度达标的比例分流率是没被升级给主模型的比例。实测下来阈值 0.6 时准确率 97%分流率 70%阈值 0.7 时准确率 98.5%但分流率掉到 55%。我选了 0.6因为分流率对成本影响更大而 97% 的准确率已经够用——剩下 3% 的误判大部分是CONTINUE和RETRY之间的混淆后果不严重。如果你的任务对误判零容忍那就往 0.7 甚至 0.8 调代价是更多判断升级给主模型。5. 常见问题与排查技巧实录5.1 判断层过度自信怎么办最常见的坑是 System-1 在它其实不该有把握的时候给出高置信度。比如一个从没见过的报错它照样输出RETRY且置信度 0.9。这种情况靠阈值拦不住得从输入下手。我的解法是在输入里加一个未知标记——如果当前动作或报错类型在训练分布之外就在 prompt 里显式标注这是新情况引导模型输出NEED_REASONING。另一个办法是维护一个已知动作白名单白名单外的动作一律不走 System-1直接给主模型。这个白名单我维护了大概 20 个常见动作覆盖了日常 80% 的场景剩下的长尾交给大模型稳得很。5.2 循环卡死与死锁排查死锁是 agent loop 的经典问题插入判断层之后反而更容易出现因为判断层可能反复给出同一个标签。我遇到过一次某个文件写入一直失败System-1 一直判RETRY重试三次后我设的硬上限触发了ABORT但主模型收到ABORT后又重新规划又走到同一个文件又失败无限循环。解法是给状态加一个失败历史。同一个动作连续失败超过阈值就在状态摘要里标注此动作已失败 N 次System-1 看到这个信息就会倾向SWITCH_TOOL或ABORT。这个改动很小但彻底解决了循环卡死。问题现象可能原因排查方向解决手段判断层输出非法标签约束解码未生效检查推理服务配置开启 logit 屏蔽分流率异常低阈值设太高统计置信度分布下调阈值到 0.6循环卡死重复失败无记录查看失败历史加失败计数与硬上限判断被无关信息带偏输入未裁剪检查传入字段只传三类核心信息延迟反而变高判断服务串行阻塞看服务响应时间本地部署或加缓存5.3 判断层与主模型的甩锅问题还有个隐蔽的坑判断层和主模型互相甩锅。System-1 判NEED_REASONING升级给主模型主模型重新规划后又调一次 System-1System-1 又判NEED_REASONING来回踢皮球。我加了一个规则同一个步骤连续两次NEED_REASONING就强制主模型自己做决定不再调判断层。这个熔断机制很有必要否则遇到判断层搞不定的情况整个循环就在两个模型之间空转。5.4 实操心得先跑影子模式如果你准备上手我强烈建议先跑影子模式——判断层照常运行但它的输出只记录不生效实际决策还是主模型做。跑一两天对比判断层和主模型的决策差异看看判断层在哪些场景下会出错。这个阶段能帮你发现大量输入裁剪和阈值的问题等影子模式准确率稳定在 95% 以上再切换成实际生效。我当初跳过这步直接上线结果第一天就误判了一个删除操作差点把重要文件清了血的教训。6. 这套架构还能怎么扩展跑通基础版之后我陆续加了几个扩展。一个是判断缓存相同的动作加结果组合直接返回上次的判断省掉一次模型调用。命中率大概 30%对重复性任务很有效。另一个是多判断模型投票对关键决策同时调两个不同的小模型一致才采纳不一致就升级。这个成本翻倍但准确率提升明显适合高风险场景。再往深了想判断层其实可以不止做下一步判断还能做这一步值不值得做的预判。比如主模型规划了一个动作先让 System-1 判断这个动作的成功概率低于阈值就直接跳过省得执行了才发现白干。这个思路我还在试初步看能再省 15% 左右的无用动作。最后分享一个我调了很久才稳定的细节判断层的 prompt 里示例的顺序很重要。把NEED_REASONING的示例放在最后模型遇到模糊情况时更倾向于选它相当于给认怂加了个近因效应。这个技巧没有理论依据纯粹是实测出来的但确实管用。你要是也在搭类似的判断层不妨试试把最希望模型保守选择的那个标签的示例放最后。