System One判断下沉:AI Agent延迟与成本优化的架构实践 Jev 这一波热度起来之后圈子里都在聊它的推理表现和本地部署体验。但我更感兴趣的反而是它带火的一个副产品——“System One 判断下沉”。这个词最初是认知心理学里的概念被 Jev 相关讨论带进 AI Agent 工程化之后我发现它其实是解决 Agent 延迟高、成本贵、稳定性差的一把钥匙。于是我把这套思路完整搬进了自己的开源 AI Agent 项目里这期就来聊聊我是怎么设计、怎么落地、又踩了哪些坑的。先说结论判断下沉不是让 AI 变笨而是把“不需要思考”的事从“思考链路”里剥离出去。一个 Agent 如果每次请求都让大模型从头到尾推理一遍那它本质上是在用高射炮打蚊子。真正合理的做法是让 Agent 像人一样——大事慢想小事快做。下面我会从概念拆解、架构设计、代码落地、问题排查四个维度把整套方案完整铺开。1. 为什么 Jev 爆火之后我重新盯上了“判断下沉”1.1 从 Jev 的火爆说起大家关注的不只是模型本身Jev 的爆火表面上是模型效果好、开源、可本地部署但更深层的原因是它让很多人第一次感受到“开源模型的推理能力已经可以下沉到个人开发者手里”。我自己的感受是当模型能力不再是瓶颈时工程侧的问题才真正浮出水面——你的 Agent 响应要多久跑一次要多少钱高并发下扛不扛得住这些问题模型本身不会替你回答。我在 Jev 相关讨论区看到不少人晒自己的部署经验有拿它做聊天助手的有基于 FastAPI LangChain LangGraph 搭复杂 Agent 的甚至有人拿 Jev 构建数据系统。但真正让我眼前一亮的不是谁的 Agent 功能多而是有人提出了一个反直觉的观点最好的 Agent 应该是“大部分时候根本没在用大模型”。这句话直接击中了我。因为我自己的开源 Agent 项目早期恰恰是“每个请求都强行过一遍大模型”结果就是响应慢、账单吓人、偶尔还会出一些莫名其妙的幻觉。Jev 的热度给了我一个重新审视架构的契机。我意识到模型能力越强越要把“强”用在刀刃上而不是浪费在“今天天气怎么样”这种只需要调一个天气 API 就能回答的请求上。判断下沉就是把低价值的、确定性的、模式化的判断从昂贵的模型推理链路中拿出来放到便宜、快速、可控的前置规则层去做。1.2 什么是“System One 判断下沉”一次认知双系统理论的工程化迁移“System One 判断下沉”源自卡尼曼在《思考快与慢》里提出的双系统理论。系统一是快思考直觉、自动、几乎不耗精力系统二是慢思考理性、需要注意力、费脑力。人不会对“11等于几”启动系统二也不会对“要不要换工作”只靠系统一。工程化的 Agent 也该这样。但这里有个关键点很多人以为“判断下沉”就是把所有能规则化的东西全部硬编码让 AI Agent 退化成普通脚本。这其实是一种误读。真正的判断下沉是给请求做一个“认知分级”——先让一个轻量判定器去评估这个请求是低风险、高重复、可以被规则和缓存直接满足的“系统一类请求”还是高风险、开放、需要模型深度推理的“系统二类请求”。前者走快路径后者才走慢路径。我用一个生活化的类比。老司机开车换挡、踩刹车、看后视镜这些动作完全是自动化的不需要大脑刻意计算但遇到复杂路况、突然爆胎、导航失效时才会瞬间切换到“专注模式”调动全部注意力。判断下沉就是给 Agent 装上这种“自动化的肌肉记忆”让它不用事事都全神贯注。1.3 判断下沉解决的核心问题延迟、成本、稳定性我在实际项目里最直接感受到的是三个问题的改善。延迟方面一次本地模型推理通常需要几百毫秒到几秒如果请求还要经过多轮工具调用耗时直接翻倍。而走快路径的请求一次字典查询、一次缓存命中、一个参数校验函数往往在几毫秒到几十毫秒内就能返回。我上线判断下沉后平均响应延迟从约 1.8 秒降到了约 300 毫秒这在用户可感知的范围内是从“转圈”到“秒回”的体验跨越。成本方面大模型调用是按 token 计费的哪怕本地部署不花钱也要耗电、耗显存、占并发名额。把 60% 的请求从慢路径分流到快路径意味着同一个模型后端能支撑的用户并发量和总调用量大幅提升。我的开源项目在社区部署场景里实测模型负载下降了将近一半这在多人同时使用时优势非常明显。稳定性方面模型推理天然有随机性同一句话问两次可能得到两个答案这在一些确定性要求高的场景是不可接受的。而规则和缓存路径输出是可预测的、可测试的。判断下沉让 Agent 在面对高频重复问题时从“每次都在创作”变成“稳定复读标准答案”这个特性在客服、文档问答、运维诊断等场景意义极大。2. 开源 AI Agent 的分层架构设计把快思考与慢思考分开2.1 快路径System One规则、缓存、工具预检快路径的设计目标是“不看大模型也能答对”。我在自己的开源 Agent 里规划了三个主要组件。第一是关键词和语义规则库。这类规则适合处理明确、低风险的指令。比如用户说“查看当前服务器负载”我会先做一次请求解析如果命中“查看”“服务器”“负载”这些关键词组合并且上下文里没有歧义就直接调系统命令或监控 API 返回结果全程不经过大模型。规则库不只是简单的 if-else我建议用可配置化的 JSON 或 YAML 文件管理让触达条件、响应模板、关联工具都能清晰维护。第二是缓存层。缓存不只是 KV 缓存那套而是“请求语义缓存”。也就是说即使两句话字面不同但经过向量化后距离很近且对应答案可以复用我就认为它们是同一类请求。这背后需要一个小型的向量编码器和向量数据库不一定很重可以用轻量的本地向量库。缓存命中时直接返回历史结果这是快路径里性价比最高的一环。第三是工具预检。很多 Agent 调用工具时参数是大模型现场生成的这既慢又容易出错。判断下沉后我会在快路径里直接做参数校验和默认值填充如果请求里已经带齐了必要参数并且符合预定义格式就直接发起工具调用跳过模型生成参数那一步。只有当参数缺失、格式错误、或存在歧义时才把问题抛给慢路径。2.2 慢路径System TwoLLM 推理编排慢路径不是什么复杂的事就是把原先 Agent 该干的活保留下来但明确它的边界只有在快路径无法解决时才启用大模型进入深度推理。我用 LangGraph 来做慢路径的有向图编排把“用户意图识别”“工具选择”“参数生成”“结果整理”这些节点串起来。对比之前的做法最大的改变是加了入口门槛。LangGraph 里我设置了一个名为“system_one_router”的起始节点它会先尝试快路径如果快路径判定不通过或执行失败再进入“system_two_planner”节点由大模型接管。这样就确保了慢路径的每一次调用都是“不得不调用”的。慢路径内部我依然保留了多轮规划能力比如用户说“帮我分析一下刚才那条日志异常的原因并给出排查建议”这种请求快路径没法处理模型就需要拆解步骤、调用日志查询工具、综合上下文生成结论。在整个编排里我还会嵌入工具调用后的结果校验环节防止模型拿到一个错误结果继续往下推。2.3 中间判定层谁来决定走哪条路判断下沉最核心的技术难点不是快路径怎么搭也不是慢路径怎么编排而是“谁来决定一个请求该走哪条路”。我实际尝试了三种方案最后采用了混合策略。第一种是纯规则判定。写一批正则和关键词规则命中就直接走快路径。优点是零成本、延迟最低、可解释性最强缺点是覆盖不了语义变化比如用户说“帮我看看机器是不是挂了”和“查一下服务器状态”关键词完全不同但意图是相同的。第二种是小模型分类。用一个微调过的轻量分类器比如基于 Jev 蒸馏出的小模型或传统 Bert 类模型把请求分成“快”“慢”“不确定”三类。这种方法语义覆盖更好但需要标注数据而且分类器本身也有误判率需要在工程上兜底。第三种是混合策略也是我最终采用的。先把请求丢给一组极少量的高置信规则做硬过滤凡是规则明确命中的直接走快路径没命中或规则有冲突的再用小模型分类器打分如果分类器置信度也低比如介于 0.4 到 0.6 之间就默认走慢路径求稳。这个设计的好处是“宁慢勿错”确保判断下沉不会轻易牺牲答案质量。我还在判定层加了一个“影子模式”。日常线上请求会同步进判定层做分类但最初不会真的分流只是记录“如果当时走了快路径结果会是什么”。通过回放这些影子日志可以评估快路径方案的建议质量等准确率达到阈值再切换真实分流。这招对降低上线风险非常管用。2.4 技术选型为什么是 FastAPI LangChain LangGraph在技术选型上我并没有追新。框架选择 FastAPI LangChain LangGraph更多是出于稳定、生态成熟和适合开源项目协作的考虑。FastAPI 承担整个 Agent 的 HTTP 入口和异步调度。它原生的 async/await 支持是我应对并发需求的基础。Agent 的快路径里有大量 IO 操作比如查缓存、调工具、读向量库这些在异步框架下可以用并发方式执行不会阻塞事件循环。实测下来单机异步处理快路径请求吞吐能力比同步实现高出数倍。LangChain 在项目里主要承担工具封装和模型统一调用。它把大量常用工具封装成了标准接口比如搜索引擎、数据库查询、文件读写等这让快路径预检和慢路径模型调用能够复用同一套工具定义。LangChain 的 callback 机制也被我用来做全链路追踪后面排查问题时帮了大忙。LangGraph 则负责把慢路径的处理流程定义成有向图。它比简单链式调用的优势在于可以支持条件分支、循环、并行节点这对复杂任务规划非常关键。LangGraph 还带有检查点机制可以在每一步落盘状态一旦某一步出错可以回滚或重试这比“一条链走到黑”的旧模式稳健得多。3. 落地实现把“判断下沉”真正搬进代码3.1 第一步构建分级路由判定器我在项目里新建了一个router.py模块核心是一个route_request函数它按照“规则硬匹配 - 分类器打分 - 默认慢路径”的顺序返回路由结果。代码逻辑大概长这样# router.py from dataclasses import dataclass dataclass class RouteDecision: path: str # fast 或 slow reason: str confidence: float def route_request(user_input: str, context: dict) - RouteDecision: # 第一层规则硬匹配 for rule in RULE_SET: if rule.matches(user_input): return RouteDecision(pathfast, reasonfrule:{rule.name}, confidence0.99) # 第二层小模型分类器打分 label, conf classifier.predict(user_input) if label fast and conf 0.6: return RouteDecision(pathfast, reasonclassifier, confidenceconf) if label unclear and conf 0.4: return RouteDecision(pathslow, reasonclassifier_low_conf, confidence0.5) # 第三层默认慢路径求稳 return RouteDecision(pathslow, reasonfallback, confidence0.5)RULE_SET是加载自外部配置文件的一组规则对象每条规则由正则、关键词列表和匹配阈值组成。规则匹配时我特意加了“上下文辅助校验”的钩子比如用户问“重启服务”如果 context 里已经有明确的服务名就走快路径如果连服务名都没有规则即使命中关键词也要升级给慢路径。这个细节很重要避免规则在缺少上下文时乱接活。分类器我没有用大模型而是用一个极轻量的文本分类模型单独训练了“可规则化意图”和“需要深度推理意图”两类。训练数据就是历史请求日志把成功走快路径的样本打上“fast”标签把需要多轮工具调用的样本打上“slow”标签。实际效果基本够用拦截率大约七成。3.2 第二步给确定性请求加缓存层缓存是快路径响应速度的最大功臣。我这里的缓存不是简单的字符串缓存而是“语义缓存 结果过期策略”的组合。实现上用了两层第一层是精确匹配缓存用请求文本的 hash 直接查第二层是语义缓存用向量编码后在向量库里查最近邻。# cache.py import hashlib def get_cached_answer(user_input: str, context: dict): # 精确层加盐哈希避免固定 hash 被滥用 cache_key hashlib.sha256( (user_input.strip().lower() str(context.get(project, ))).encode() ).hexdigest() result exact_cache.get(cache_key) if result: return result, exact # 语义层先编码再查向量库 emb embedder.encode(user_input) vec_result vector_cache.search(emb, top_k1) if vec_result and vec_result.distance 0.12: return vec_result.payload[answer], semantic return None, None这里有个经验缓存键一定要带上必要的上下文维度。我一开始只拿用户输入当键结果不同项目、不同用户问同一句话缓存互相串差点出事故。加了context.get(project)作为隔离维度后才彻底解决。语义缓存的阈值也不要拍脑袋定我建议用一批历史请求回放画出距离分布曲线选择既能命中足够多相似请求、又不会引入明显错误答案的临界值。我这边最终挑的是 0.12背后大概对应“文本改了几个词但核心意图完全一致”的相似度水平。缓存过期策略是按业务场景配置的像“查询服务器CPU状态”这种实时性要求高的过期时间设成 5 秒像“如何配置某个参数”这种固定知识直接设 24 小时。配置化过期避免了“一刀切”导致的数据新旧混杂问题。3.3 第三步用工具预检代替部分模型调用工具预检是判断下沉里我觉得最实用的一环。过去 Agent 调工具流程是大模型读用户输入生成参数 JSON再执行工具。现在我在路由层里直接预检。# tool_precheck.py TOOL_REGISTRY { get_server_load: { required_params: [host], param_rules: { host: lambda v: isinstance(v, str) and v in HOST_LIST } } } def try_fast_tool_call(user_input: str, context: dict): # 尝试从文本中直接提取参数不走模型 parsed extract_params_by_template(user_input, context) if not parsed: return None, param_missing tool_conf TOOL_REGISTRY.get(parsed[tool_name]) if not tool_conf: return None, unknown_tool for param, validator in tool_conf[param_rules].items(): if param not in parsed or not validator(parsed[param]): return None, finvalid_param:{param} # 参数齐全且合法直接执行工具 try: result execute_tool(parsed[tool_name], parsed) return result, success except Exception as e: return None, ftool_error:{e}这里的核心思路是“模板抽取参数”加“强制参数校验”。比如用户说“查一下 web01 的负载”我用一个简单的命名实体规则就能抽出hostweb01没必要让大模型去生成参数。只有当提取失败、参数不符合预定义格式时才交给慢路径。这样做不仅省了一次模型调用还减少了幻觉参数的风险——模型生成参数时偶尔会编造 host 名而规则抽取不会。为了能兜住更多请求我维护了一份“工具调用样本库”把历史上成功的工具调用存下来用相似匹配复用。倘若用户这次问题的表述和样本库里某条高度相似就直接复用当时的参数模板再额外做必填参数校验。3.4 第四步并发与限流设计保证 Agent 扛得住判断下沉不只是让单次请求变快它更大的价值是让 Agent 在高并发下依然稳定。我的开源 Agent 在部署后被社区同学反馈说并发一高就超时排查下来发现模型后端成了瓶颈。引入判断下沉后快路径请求根本不打模型后端所以并发压力被大幅分流。在 FastAPI 侧核心接口全部设计为异步。快路径中的工具调用、缓存查询、向量检索都封装成 async 函数让它们在事件循环里并发执行。配合asyncio.Semaphore控制外部服务的最大并发连接数避免瞬时把下游打爆。# main.py from fastapi import FastAPI import asyncio app FastAPI() sem asyncio.Semaphore(20) app.post(/agent) async def agent_endpoint(request: Request): payload await request.json() async with sem: decision route_request(payload[input], payload.get(context, {})) if decision.path fast: result await fast_path.handle(payload) return {path: fast, data: result} # 慢路径也走异步但通常会调用模型后端 result await slow_path.handle(payload) return {path: slow, data: result}并发限制这块我多说一句。很多同学有个误区觉得异步就是“无限并发”其实异步只解决了 IO 等待的问题如果下游数据库或外部 API 扛不住照样会全线超时。所以信号量限流必须按下游能力来调。我这里模型后端并发上限是 4外部工具 API 并发上限是 10快路径规则引擎不限制。判断下沉的意义正在于此让有限的大模型并发名额只分配给真正需要慢思考的请求。3.5 部署后的实测效果对比代码落地后我在两台相同配置的机器上做了对比测试一组跑旧版全模型推理架构一组跑新版判断下沉架构。用同样一份 1000 条真实请求回放包含 60% 的重复性问答、25% 的工具调用请求、15% 的复杂分析任务。结果非常直观。新版快路径请求占比达到 62%这些请求平均响应时间 80 毫秒P95 在 150 毫秒以内慢路径请求虽然平均耗时约 1.2 秒没太大变化但总请求数量不变的情况下模型后端实际承担的压力只有原来的四成。整体成本估算降了约 55%而核心复杂任务的质量和旧版基本持平没有因为分流而明显变差。我还做了一次混沌性验证把判定层强制全部打回慢路径模拟“判断下沉失效”的情况。结果并发场景下模型后端直接被打满响应时间从 300 毫秒的中位数飙到 3 秒以上。这从反面证明了判断下沉不是可有可无的优化而是决定了 Agent 能否在真实业务负载下活下来的关键设计。4. 实际落地中的常见坑与排查思路4.1 下沉过度智能体变“人工智障”判断下沉最容易犯的错就是规则写得太狠把所有请求都拦在快路径里导致一些本来需要模型理解的请求被错误套用了固定模板。我遇到过最典型的例子是用户问“服务器负载有点高能帮我看看是不是某个进程导致的”结果被关键词规则误判成“查服务器负载”直接返回了一个监控数据完全没回答“是不是某个进程导致”的这层分析需求。用户体验非常糟糕。这个坑的根因是我把规则的匹配阈值调得太敏感。后来我加了两道防线第一所有规则命中后必须校验“是否完整覆盖了用户问题的核心诉求”如果不确定宁可升级到慢路径第二规则库单独维护一份“负面样本清单”凡是历史上被错误拦截的请求统一加入规则排除集。经过一段时间迭代误判率显著下降。4.2 缓存命中率低键设计不合理我早期上线语义缓存时命中率只有 10% 出头几乎不起作用。排查下来问题出在缓存键和向量检索的相似度阈值上。精确缓存那边因为键里带上了用户的 token 信息同一个问题换个人问就 cache miss语义缓存那边阈值设得太严格0.05 的距离要求基本只有原句复读才能命中。修正方法是把缓存键的维度收敛到“业务隔离 归一化文本”去掉用户无关信息阈值则回放历史请求做统计最终调到 0.12。调完之后语义缓存的命中率提升到 35% 左右再加上精确缓存整体缓存贡献率接近 45%。这里有个建议缓存命中率不是越高越好如果因为追求命中率而放低相似度阈值导致返回了错答案反而会对用户产生严重误导。4.3 路由误判边界场景谁来兜底路由判定器的边界场景是最大的隐患。比如“这个错误日志是什么意思”这种请求既可以被当作日志查询走快路径也可能需要模型分析原因走慢路径。我在实际运行中发现小模型分类器对这种边界请求的置信度通常很低加上规则也不容易覆盖。我的解决方案是“双保险兜底”路由判定器输出低置信度时默认走慢路径同时给慢路径加一个快速的“预检反馈”机制让模型在真正推理前先快速判断“这个请求是否需要我”如果模型自己也觉得不需要就直接用一个精简模板返回而不是执行完整的多轮工具编排。这个兜底方案牺牲了一点效率但避免了大量边界误判带来的质量滑坡。4.4 冷启动判断层也需要预热判断下沉引入了一个新问题冷启动时快路径的命中率特别低。因为规则库不可能一开始就完整语义缓存里也没有历史积累向量分类器也需要真实请求去适配。我的开源项目刚发布时社区用户反馈“响应速度和旧版差不多”其实就是因为冷启动阶段所有请求都在落慢路径分流能力还没发挥出来。后来我做了两件事缓解冷启动。第一发布前用历史真实请求离线预跑一遍把高频问题的答案预置进缓存第二把规则库的维护做成“社区可贡献”模式让用户在使用过程中上报被错误丢给慢路径的高频请求我定期把这些请求提炼成新规则。上线大约一周后快路径命中率就稳定在六成左右了。4.5 可观测性如何定位一次慢响应判断下沉让 Agent 的调用链变复杂了一个请求可能经历规则匹配、缓存查询、工具预检、模型推理多个环节。如果没有可观测性线上出了问题会非常难排查。我自己吃过亏有一次用户反馈响应特别慢我查了半天日志才发现是某个被规则命中的快路径请求里工具预检居然去调了一个外部慢 API本来不该慢的请求被拖到 2 秒。我在项目里引入了全链路 ID 和分阶段打点。每收到一个请求就生成trace_id在路由判定、快路径处理、慢路径处理、工具调用等关键节点记录耗时和结果统一输出结构化日志。这样排查问题时只要拿到用户的请求 ID就能一眼看出时间都耗在哪个环节。我还把每个阶段的命中情况做了统计面板比如“规则命中了多少”“缓存命中率多少”“分类器低置信度多少”方便持续优化。5. 后续扩展判断下沉还能用在哪些场景5.1 把判断下沉应用到多 Agent 协作中最初我只对单个 Agent 做了判断下沉后来发现多 Agent 协作场景同样受益。在多 Agent 系统里不同 Agent 之间会互相传递任务请求如果每个接收方都重新做一次完整推理开销极其浪费。我在接收方的入口同样加入判断下沉层用一套轻量规则和缓存判断“这个任务我是否已经处理过相似请求”如果命中就直接返回历史结果或调用本地缓存的任务模板不再触发新的模型推理。在多 Agent 的编排调度里判断下沉还被我用在了“任务是否值得分配”的预判上。比如调度器收到一个任务时先判断任务复杂度如果是简单的信息检索直接调用快路径 Agent只有需要多步骤推理、多源信息聚合的任务才分配给慢路径的专家 Agent。这样既保证了整体响应速度又不牺牲复杂任务的处理质量。5.2 用学习到的历史轨迹更新快路径判断下沉不是一次性工程而是一个持续积累的过程。我现在把 Agent 每次慢路径的成功处理轨迹记录下来通过离线分析提取“哪些类型的请求其实可以被规则化”。比如用户在慢路径里问了一百次“某个服务的版本号是多少”这个问题的答案就完全可以沉淀成固定知识进入快路径规则库。我会定期跑一个“快路径候选挖掘”任务把最近一周慢路径请求聚类找出那些输出高度相似、工具调用路径一致的请求簇把它们标记为“可下沉候选”。人工审核后将这批请求生成新的规则或预置缓存。这个闭环让我在不知不觉中快路径的覆盖范围越来越大模型的负担越来越小。5.3 开源社区里的其他实践我做这个开源项目时也看到社区里有很多类似的判断下沉思路。有人把 System One 判断下沉用在了 Jev 本地部署的负载均衡上先判断请求类型再分配 GPU 资源也有人基于 LangGraph 把判断下沉做成了通用模板可以在任意 Agent 项目里直接复用。最有意思的是有个开发者把这个思想用在了测试场景里让 Agent 先判断哪些测试用例需要真实执行、哪些可以直接查历史结果大幅缩短了回归测试时间。这些实践让我确信判断下沉是个通用的工程思想不只适用于某个具体模型或框架。它本质上是在回答一个问题一个智能系统的算力和注意力应该优先花在哪里如果你也在做 AI Agent我建议先别急着堆功能可以重新审视一下你的请求链路里有多少工作其实根本不需要大模型参与。我在实际使用中发现判断下沉最难的不是技术实现而是克制。克制住“每个请求都要让模型回答”的冲动克制住“把所有能力都塞进快路径”的懒惰才能真正拿捏好快与慢的平衡。我现在的体会是最好的 Agent 架构是让用户感觉不到“它到底有没有在用大脑”——在该快的时候快到察觉不到在该慢的时候又耐心得足够聪明。这大概就是 System One 判断下沉最终的理想形态吧。