ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

本地AI Agent任务拆分:L0硬规则前置+L1模型兜底两级流水线实战

本地AI Agent任务拆分:L0硬规则前置+L1模型兜底两级流水线实战 1. 为什么要在本地做任务拆分1.1 从一次线上事故说起去年冬天我负责的一个内部工单处理系统出了个不大不小的事故。用户提交了一段自然语言描述系统需要把它拆成结构化的子任务再分派给不同的处理模块。当时我们用的是纯大模型方案所有请求都走同一个提示词模板结果那天模型服务抖动响应时间从 800ms 飙到 12s整个工单队列直接堵死。更麻烦的是有些明显能靠规则判断的简单请求比如帮我查一下上周的报销单状态也被塞进模型里绕了一圈白白消耗了 token 和等待时间。那次之后我就开始琢磨任务拆分这件事真的需要全部交给模型吗答案显然是否定的。大量请求其实有非常明确的模式比如包含查询状态编号这类关键词的直接走规则匹配就能拆得八九不离十。只有那些语义模糊、需要理解上下文的才值得动用模型。这就是L0 硬规则前置 L1 模型兜底两级流水线的由来。L0 层用确定性规则快速处理高频、结构化的请求L1 层用模型处理 L0 无法覆盖的长尾场景。两级之间通过置信度阈值衔接既保证了速度又保留了灵活性。1.2 这套方案适合谁如果你正在做以下事情这套思路大概率能帮到你本地部署的 AI Agent 系统需要把用户输入拆成可执行步骤工单、客服、审批类系统请求模式相对固定但又有长尾对响应延迟敏感不能接受每次都等模型返回预算有限希望减少模型调用次数不适合的场景也很明确如果你的请求几乎全是开放式创作、语义极度发散那 L0 规则层能覆盖的比例会很低维护规则的成本可能不划算。这种情况下纯模型方案或者模型 缓存可能更合适。1.3 核心设计原则整套流水线围绕三个原则展开第一规则能判的绝不问模型。这不是抠门而是对确定性的追求。规则匹配的结果是可预测、可测试、可审计的模型输出则带有随机性。能用规则的地方用规则系统的稳定性会高一个档次。第二模型只做兜底不做主力。L1 层的定位是处理 L0 搞不定的剩余部分而不是处理所有请求。这个定位决定了 L1 的提示词设计、超时设置、降级策略都和纯模型方案不同。第三两级之间要有明确的交接协议。L0 输出什么格式、置信度怎么算、什么情况下转给 L1、L1 返回后怎么和 L0 的结果合并这些都必须提前定义清楚否则两级流水线会变成两套互不兼容的逻辑。2. L0 硬规则层的设计细节2.1 规则从哪里来L0 层的规则不是拍脑袋想出来的而是从真实请求里挖出来的。我的做法是先收集至少 500 条历史请求人工标注每条应该拆成哪些子任务然后统计高频模式。具体操作上我会把请求按意图 实体两个维度归类。意图比如查询状态提交申请修改信息取消操作实体比如订单号时间范围人员姓名。归类完成后你会发现某些组合出现频率极高这些就是 L0 规则的第一批候选。举个例子统计下来发现查询 订单号这个组合占了全部请求的 23%那就可以写一条规则如果请求中包含查看状态等动词且能提取到形如ORD-\d{8}的编号就直接判定为查询类任务拆成提取订单号和执行查询两个子步骤。2.2 规则的三种类型在实际落地中我把 L0 规则分成三类每类的处理方式和置信度计算都不一样。关键词规则是最简单的一类靠词典匹配。比如维护一个动词词典[查询, 查看, 搜索, 找一下]命中就标记为查询意图。这类规则速度快但容易误判所以置信度给得保守一般 0.6 到 0.7。正则规则用于提取结构化实体。订单号、手机号、日期、金额这些都有相对固定的格式用正则提取准确率很高。命中正则的规则置信度可以给到 0.85 以上。模板规则处理的是句式固定但内容变化的请求。比如把 X 的 Y 改成 Z这种可以用模板匹配加槽位填充的方式处理。这类规则置信度最高能到 0.9 以上。三类规则的置信度不是随便定的而是根据历史数据统计出来的准确率。我一般会跑一个离线评估看每条规则在标注数据上的精确率和召回率然后取精确率作为置信度基准。2.3 置信度怎么算单条规则的置信度好定但一个请求往往同时命中多条规则这时候怎么综合我的做法是加权投票。假设请求命中了三条规则置信度分别是 0.9、0.7、0.6对应权重按规则类型给模板规则权重 1.0正则规则 0.8关键词规则 0.5。那么综合置信度就是(0.9×1.0 0.7×0.8 0.6×0.5) / (1.0 0.8 0.5) (0.9 0.56 0.3) / 2.3 ≈ 0.765这个 0.765 就是最终置信度。如果它高于阈值我一般设 0.75就直接采用 L0 结果低于阈值就转给 L1。注意阈值的设定需要根据实际数据调。设太高L0 覆盖率低模型调用多设太低L0 误判多用户体验差。建议先用 0.75 跑一周看误判率和覆盖率再调整。2.4 规则库的维护规则库不是写完就完事了它需要持续维护。我的做法是每周跑一次规则体检统计每条规则的命中次数长期零命中的规则考虑下线统计 L0 转 L1 的请求里有多少其实是规则能覆盖但没覆盖的这些是规则库的缺口统计 L0 直接处理的请求里有多少被用户反馈拆错了这些是规则的误判这个体检流程跑下来规则库会越来越精准。我自己的系统跑了三个月L0 覆盖率从最初的 41% 提升到了 68%模型调用量降了将近一半。3. L1 模型兜底层的实现3.1 模型选型和本地部署L1 层的定位是兜底所以对模型的要求和纯模型方案不同。我不需要它处理所有请求只需要它处理好 L0 漏下来的那部分。这部分请求的特点是语义模糊、上下文依赖强、模式不固定。模型选型上我试过几种方案。7B 级别的模型在本地跑量化后大概占 5GB 显存推理速度在消费级显卡上能到 30 tokens/s 左右处理一条任务拆分请求大概 2 到 4 秒。13B 的模型效果更好但速度降到 15 tokens/s 左右延迟翻倍。最终我选了 7B 量化版本原因是L1 是兜底请求量本来就不大2 到 4 秒的延迟可以接受而且兜底场景下用户对延迟的容忍度比 L0 高因为 L0 已经把快的部分处理掉了。本地部署用 llama.cpp 或者 ollama 都行。我用的 ollama主要是图它管理模型方便一条命令就能拉起来。启动参数里比较关键的是num_ctx任务拆分场景下上下文不用太长2048 足够设太大反而占显存。3.2 提示词设计L1 的提示词和纯模型方案有个关键区别它需要知道 L0 已经尝试过什么。所以提示词里我会把 L0 的匹配结果作为上下文传进去让模型知道规则层已经识别出了这些关键词和实体但置信度不够你来判断怎么拆。提示词结构大概是这样你是一个任务拆分助手。用户请求如下 {user_input} 规则层已识别到以下信息 - 命中关键词{matched_keywords} - 提取实体{extracted_entities} - 规则层置信度{l0_confidence} 请基于以上信息将用户请求拆分为可执行的子任务列表。 输出格式为 JSON 数组每个元素包含 task_type 和 task_content 两个字段。 如果规则层的识别有误请纠正。这个提示词的关键在于如果规则层的识别有误请纠正这句话。它让模型不是从零开始拆而是在 L0 结果的基础上做修正这样输出更稳定也更容易和 L0 的结果对齐。3.3 输出解析和容错模型输出 JSON 这件事做过的人都知道有多不靠谱。有时候多个逗号有时候少个引号有时候干脆给你一段解释文字。所以 L1 层的输出解析必须做容错。我的做法是三层解析第一层直接json.loads能过就过。第二层用正则把 JSON 部分抠出来再解析。第三层如果前两层都失败就调一次模型让它只输出 JSON不要任何其他文字重新生成。实测下来第一层能过 70% 左右第二层能再捞回 20%剩下 10% 需要第三层。第三层虽然多一次调用但概率低对整体延迟影响不大。实操心得在提示词里加一句输出必须是合法的 JSON不要包含 markdown 代码块标记能显著提升第一层解析成功率。我加了这句话之后第一层通过率从 55% 提到了 70%。3.4 超时和降级L1 层必须有超时机制。我设的是 8 秒超过就降级。降级策略分两种如果 L0 有结果但置信度不够就采用 L0 的结果同时标记低置信度让下游模块知道这个拆分可能不准。如果 L0 完全没结果就返回一个默认拆分比如把整个请求当成一个任务标记未拆分。降级不是失败而是保证系统始终有输出。用户宁可看到一个不太准的结果也不愿意看到系统卡死或者报错。4. 两级流水线的衔接与调度4.1 请求进来后的完整流程一个请求从进入到返回完整流程是这样的请求进入 L0 层并行跑所有规则汇总规则命中情况计算综合置信度如果置信度 ≥ 0.75直接返回 L0 结果流程结束如果置信度 0.75把请求和 L0 中间结果一起传给 L1L1 调用模型解析输出如果 L1 成功返回 L1 结果如果 L1 超时或失败按降级策略返回整个流程里L0 的处理时间在 10ms 以内L1 在 2 到 4 秒。所以对于 L0 能覆盖的请求用户几乎感觉不到延迟。4.2 并发和队列管理L1 层是瓶颈因为模型推理是串行的单卡情况下。所以需要一个队列来管理 L1 请求。我用的是简单的 FIFO 队列加超时丢弃。队列长度设 50超过就拒绝新请求直接走降级。这个数字是根据模型推理速度和可接受的等待时间算出来的假设每条请求推理 3 秒队列 50 条最坏等待 150 秒这已经太长了。所以实际上我把队列长度设成 10最坏等待 30 秒超过就降级。注意队列长度不是越大越好。队列太长会导致请求积压用户等半天拿到一个结果体验反而更差。宁可快速降级也不要让用户干等。4.3 结果合并策略有些场景下L0 和 L1 的结果需要合并。比如 L0 识别出了订单号但意图判断置信度不够转给 L1。L1 返回了意图但可能没提取到订单号因为模型有时候会漏。这种情况下我的合并策略是实体以 L0 为准意图以 L1 为准。因为实体提取是规则层的强项正则匹配的准确率远高于模型而意图判断是模型层的强项规则层容易误判。这个策略不是绝对的需要根据实际数据调整。但核心思路是让每一层做它最擅长的事合并时取长补短。5. 实测数据和调优经验5.1 性能对比上线前后我做了对比测试用同一批 1000 条历史请求跑指标纯模型方案两级流水线平均延迟3.2s0.9sP99 延迟12s4.5s模型调用次数1000320拆分准确率87%89%系统可用性依赖模型服务L0 独立可用平均延迟降了 72%模型调用量降了 68%准确率反而略升。准确率提升的原因是 L0 规则处理高频请求时比模型更稳定不会出现模型那种偶发的抽风。5.2 调优过程中踩过的坑坑一规则写得太细。一开始我把规则写得很具体比如查询订单状态单独一条规则查询物流状态又一条。结果规则库膨胀到 200 多条维护成本极高而且新场景一来就得加规则。后来改成按意图 实体类型组合规则数降到 40 多条覆盖率反而更高。坑二置信度阈值设太高。最初设的 0.85导致 L0 覆盖率只有 30%大部分请求都转给 L1流水线形同虚设。降到 0.75 后覆盖率提到 65%效果明显。坑三L1 提示词太长。一开始我把所有 L0 的中间结果都塞进提示词包括每条规则的命中详情。结果提示词长达 800 多 token推理速度慢了不少。后来精简成只传命中关键词 提取实体 综合置信度提示词降到 200 token 以内速度提升明显。坑四忽略冷启动。系统刚上线时没有历史数据规则库是空的所有请求都走 L1延迟很高。后来我准备了一批种子规则根据经验手写的 20 条先把 L0 覆盖率撑到 30%再通过线上数据慢慢迭代。5.3 常见问题速查问题现象可能原因排查方向解决方法L0 覆盖率突然下降请求模式变化看最近一周的请求分布补充新规则或调整阈值L1 超时率升高模型服务负载高看 GPU 利用率和队列长度扩容或降低队列长度拆分结果不稳定L1 提示词有歧义对比多次调用的输出优化提示词增加格式约束实体提取错误正则过于宽松检查误匹配的样本收紧正则或增加校验系统整体变慢L0 规则太多看 L0 处理耗时合并规则或优化匹配逻辑6. 后续扩展方向这套流水线跑稳之后我陆续加了一些扩展。一个是规则自动挖掘用历史数据跑频繁模式挖掘自动生成候选规则人工审核后入库。另一个是L1 结果回流把 L1 处理过的请求和结果存下来定期分析哪些模式可以沉淀成 L0 规则。还有一个方向是多级流水线在 L0 和 L1 之间加一个 L0.5 层用轻量级分类模型处理那些规则搞不定但又不值得动用大模型的请求。不过这层目前还在实验阶段效果有待验证。我个人在实际操作中的体会是这套方案的核心不在于技术多复杂而在于对业务请求的理解深度。规则写得准不准阈值设得合不合理都取决于你对请求分布的掌握程度。所以前期花时间做数据分析和标注比急着写代码更重要。
返回列表