让Agent自己选模型和工具:五件自制件设计与踩坑实录

📅 发布时间:2026/10/14 5:51:15
让Agent自己选模型和工具:五件自制件设计与踩坑实录
前一阵子在做某跨平台系统的智能客服 Agent 时遇到了一个特别典型的问题不管什么难度的提问系统都一律丢给最贵的那个大模型处理。一个月下来光调用费用就让人心惊肉跳而且复杂任务反而因为模型“用力过猛”回得又慢又啰嗦。痛定思痛我干脆给 Agent 做了“自制件”——让它自己判断模型的强弱和擅长方向自己挑选合适的工具链而不是把所有策略都焊死在代码里。整个过程最有价值的部分不是最终效果提升了多少而是那五件自制件在设计层面反复较劲的过程以及实际跑起来之后陆续踩到的一堆坑。这次就把整体拆解、取舍逻辑和问题排查一条条写清楚方便你直接参考。1. 为什么非要让 Agent 自己选而不是静态绑定很多团队的第一个念头都是“我写死不就行了吗”确实固定模型、固定工具在 demo 阶段完全够用。但一旦进入真实业务静态绑定很快会撞墙。我当时的场景是某电商业务的智能客服提问五花八门查订单、退换货、问库存、投诉人工、闲聊扯淡甚至有人拿数学题来问。如果所有流量都进同一个模型你会同时面对三个问题第一成本高所有请求都按最强模型的单价结算第二响应慢强模型推理时间更长用户等不起第三效果差某些简单任务上了最强模型反而容易“过度思考”给出冗长绕弯的答案。反过来讲静态绑定还有一个更隐蔽的问题工具调用。Agent 背后挂了订单查询、物流跟踪、优惠券计算、知识库检索、工单系统等一堆接口如果按“关键词规则”或者“固定编排流程”去调那和写死接口有什么区别用户换一种说法规则就失效了。所以让 Agent 自己挑模型、自己挑工具本质上不是炫技而是把“决策权”从研发手里移交到运行时。让模型在每一轮请求到来时根据当前任务的特征、上下文长度、目标用户、甚至实时成本预算动态决定自己怎么干。这涉及一个很难受的工程问题决策权交给模型之后还要不要给 Agent 画边界我的结论是必须画但边界要画在“策略层”而不是“代码层”。这就要引入那五件自制件了。它们的分工大致是自制件核心职责一句话口令模型感知路由器判断任务难度与类型动态选模型“这活儿到底该谁干”工具编排执行器根据目标拆解动作选择并调用工具“用哪个工具、按什么顺序”上下文压缩与记忆索引器管理长期记忆与上下文窗口避免爆窗“该记住什么该忘掉什么”结果评估与自我修正模块对 Agent 输出做质量判断触发重试或降级“这活儿干得咋样要不要返工”策略配置与控制面板以配置方式控制以上四者的行为“整个系统的规矩由谁定”下面我按工程落地顺序把这五个件挨个拆开聊。2. 五件自制件逐件拆解2.1 模型感知路由器任务难度分级与模型能力匹配模型路由是这五个件里最“快”见效的一个。它的核心任务很简单面对一条输入判断是“简单题”还是“复杂题”然后决定交给哪个模型。但是“判断任务难度”这件事天然是一个循环依赖——你又要用模型来判断要不要用模型。工程师的第一反应通常是“用一个小模型分类不就行了”我一开始也这么干然后踩了个不小的坑小模型把“帮我看下订单到哪了”这种需要调接口的任务误判成了简单闲聊直接清掉了工具调用能力用户问题一个都答不对。后来我调整了设计思路不再让路由器去“猜“任务类型而是改成“探测任务所需的最少能力”。具体说我给每个候选模型定义一个能力画像包括上下文窗口大小、是否支持工具调用、推理强度是否适合数学/代码/长链推理、单位成本、预估延迟。路由器在收到请求后先做两步探测。第一步意图与实体识别用最轻量的方式确认问题中是否包含业务实体订单号、商品名、用户ID、时间范围等以及是否包含明确的动作意图查、退、换、问、投诉。只要命中业务实体或动作意图就基本可以判定为“需要工具调用”直接走工具模型。第二步复杂度评估对文本长度、逻辑链数量、是否需要多轮对话、是否需要外部知识做综合分析。比如只有一句话的“我要退货”文本短、逻辑简单、不需要外部知识定为低复杂度而“对比一下A和B两个订单的物流轨迹告诉我哪个延误风险更高”涉及双实体、对比推理、外部数据校验直接定为高复杂度。路由器的输出不是“选某个模型”而是输出一条约束列表比如“必须支持工具调用”“上下文窗口≥16k”“最大预算≤0.05元/次调用”。真正选哪个模型由策略控制面板里的规则去匹配这样就把路由和最终决策解耦了。这块有个特别值得注意的取舍刚开始千万别搞超过3档的模型分级。我最初设计了五档模型池结果维护成本很高模型更新、基准测试、成本核算全都翻倍收益却几乎没提升。后来收敛成三档轻量级闲聊/FAQ、中量级工具调用基础业务、重量级复杂推理多工具编排效果瞬间稳定下来。分级越少越容易试出效果后续加档也来得及。2.2 工具编排执行器从“有工具就调”到“会用工具链”工具编排是五件里最复杂的一个也是 Agent“像人”的关键。我见过太多同学把工具调用做成“一句话匹配”输入里出现“查一下”就调查询接口。这种方案说白了就是把规则从代码搬进提示词换个说法立刻失灵。真正的工具编排执行器至少要处理三件事工具选择、参数填充、执行顺序。先看工具选择。我给每个工具都写了一份“工具能力说明”包括功能描述、可调用的业务实体、需要的参数格式、权限等级、以及典型使用场景。执行器在收到 Agent 的意图结构后做一次“意图—工具”的语义匹配而不是关键词匹配。比如用户说“我的手机尾号是 4821帮我看看最近那个快递到哪了”执行器需要同时命中“快递单号从手机尾号反查”和“物流轨迹查询”两个工具并判断“最近那个快递”可能来自多个候选订单不直接调单个查询接口而是先调订单列表接口锁定候选目标再调轨迹接口。这正是第二个关键点——执行顺序。很多工具调用失败不是工具本身的问题而是顺序错了。我在设计里引入了一个“依赖图谱”的概念每个工具声明自己的前置条件执行器在编排时根据前置条件自动排序。比如“查物流”的前置是“获得订单号”“获得订单号”的前置是“根据用户信息检索订单”这样执行器就会自动生成一条调用链。参数填充同样有讲究。一开始我让 Agent 自己从对话里提取参数结果经常出现幻觉字段比如把聊天里提到的“红色”硬填给商品颜色参数。后来我加了“参数来源校验”的约束每个参数必须标注提取来源用户原话、上一工具返回、常量凡是既不在对话里、也不在工具返回里的参数一律置为“待确认”宁可多追问一轮也绝不瞎填。这么做有个很有意思的副作用Agent 的追问话术变自然了。因为它知道缺什么、为什么缺所以问出来的话是“请问您的订单号是多少我这边需要用它来查询物流信息”而不是僵硬的“请提供订单号”。用户体感上的提升非常明显。这块的隐藏成本是工具文档的维护。一旦接了十几个工具文档描述写得含糊执行器选错工具的概率会直线上升。我最后规定一个工具的功能说明不得超过三句话超出就拆成多个工具。这个约束直接把选择准确率往上拉了一截。2.3 上下文压缩与记忆索引器让 Agent 记得住又不撑爆窗口上下文窗口是 Agent 项目绕不过去的物理限制。模型再强对话一长就开始遗忘或者超窗。我们之前的朴素做法是“只保留最近五轮”结果用户隔天回来说“我昨天问的那个退货进度呢”Agent 一脸茫然场面很尴尬。自制件里这个上下文压缩与记忆索引器核心目标就两个在有限窗口里存放最关键的信息同时把不重要的旧信息存到外部索引里需要时再捞回来。先说实时上下文管理。我给系统设定了一整套优先级当前用户问题的原文、最近两轮对话、未完成的意图与参数提取结果、路由器的决策记录这四类信息是最高优先级必须完整保留。业务实体的历史状态比如某个订单六个月前的状态不保留在上下文里压缩成一行摘要。聊天闲扯的内容直接压缩成“用户此前闲聊过XX话题”的标签不保留细节。再说长期记忆。记忆索引器把每轮对话的结构化结果实体、事实、意图、用户偏好写入向量索引同时保留时间戳和来源。当用户开启新对话时系统会先做一次“记忆召回”把与当前问题相关的历史记忆拉回到上下文里。比如用户第二次问“那个订单到底退没退”索引器会召回上一次关于订单号和退货进度的实体直接注入上下文Agent 就不用再问一遍订单号了。这里有一个非常典型的取舍问题记忆召回的精确率和召回率怎么平衡召回太宽上下文里塞一堆不相关往事反而干扰模型。召回太窄关键信息又丢失。我实测下来的经验是不要只看文本相似度要结合业务实体去过滤。也就是说先看用户当前问题涉及哪个实体域订单/商品/售后然后只在这个实体域内做向量召回跨域的不要。这个方案的准确率远远高于纯向量检索。记忆这块还有个坑我后来栽得很惨写入侧一定要做去重和时效管理。一开始我们的索引器每轮对话都写入同一个订单号的“退款中”状态被存了十几次召回时模型看到一堆互相矛盾的历史记录直接精神分裂。后来加了“主键去重状态覆盖”机制同一个业务实体只保留最新状态只追加“新增事实”不追加重复历史。这一改上下文干净了非常多。2.4 结果评估与自我修正模块Agent 的“质检员”如果说路由器和执行器是 Agent 的手臂那评估与修正模块就是它的眼睛。没有这一步Agent 做得对不对、需不需要返工完全没人知道。我最早做评估时用了最简单的思路直接拿规则去查。比如“回答里有没有包含订单号”“有没有说对不起”。后来发现规则能抓住的问题太少抓不住的根本问题全是“态度有问题”和“逻辑不自洽”。后来我换成了“双通道评估”。第一通道是结构化校验器用规则和 schema 去检查输出的硬指标参数是否完整、金额是否一致、订单状态是否匹配、是否包含“退款成功”但后续又出现“还在处理”这类冲突表述。第二通道是质量回放器把 Agent 的推理过程路由决策、工具调用链、中间结果摘要全部丢给评估模型让它判断这个流程是否合理、有没有更短路径、输出是否完整覆盖用户诉求。质量回放器听起来很奢侈因为它本身又是一次模型调用。为了控成本我设置了分层触发策略简单问答场景只跑结构化校验不触发质量回放只有路由复杂度和业务风险都达到阈值时才启动完整评估。比如订单金额计算、跨系统流程、投诉话术这类评估模型跑一趟还是很值的。自我修正模块的介入方式也要讲究。一开始我设计成“评估不通过就整条重跑”结果会陷入死循环Agent 反复重试用户等不起成本也翻倍。后来改成“分级修正”低风险错误直接在原答案上做局部打补丁比如补上缺失的订单号、修正金额格式高风险错误才触发重跑并带上评估模型的错误摘要作为第二轮的提示词让 Agent 知道自己刚才错在哪。这个改动对效果的提升是肉眼可见的第一版整条重跑时重试率有 18% 左右改成分级修正之后重试率降到 6%但补丁修正占比到了 12%整体最终通过率反而从 82% 提升到 94%。方向比力气重要这句话在这个模块里体现得淋漓尽致。不过要提醒一句评估器也有“自嗨”陷阱后面第 4 节我会专门讲。2.5 策略配置与控制面板所有调参都别碰代码这五件里最后一件事是一张控制面板。不是因为技术难度高而是因为它决定了整个系统能不能持续迭代。我把所有模型路由规则、工具选择权重、记忆压缩比例、评估触发阈值、成本预算上限全部外部化到一份动态配置里运行时不读代码只读配置。为什么要强调“动态配置”因为 Agent 系统的行为和传统接口完全不同它是一个依赖外部条件变化的系统。模型供应商会调整价格某个模型在特定场景的表现会波动用户的业务数据分布也会随季节变化。如果每次调参都发版、上线、灰度那这个 Agent 项目基本就废了。控制面板里最核心的配置项之一是“路由白名单与强制规则”。比如遇到售后投诉类任务无论路由器怎么判断都必须走重量级模型这是业务红线。另一组关键配置是“成本熔断阈值”如果实时成本超过预算上限的 80%路由器自动降档优先选便宜模型处理不影响可用性。还有“工具禁用名单”某些工具在某段时间出过问题可以直接在配置里临时下线不需要改代码。这个部件的设计取舍集中在“灵活与失控”的边界上。配置项少系统僵化但安全配置项多系统灵活但运维容易失控。我的体感是配置项应该只暴露“会影响业务结果的关键旋钮”而不是把内部实现全部暴露。比如你可以调“记忆压缩比例”但不需要调“向量索引的字段名”可以调“工具选择 Top-K”但不需要调“排序算法的温度参数”。把技术细节藏起来只暴露业务语言这张面板才真的能交给产品和运营去用。另外一个必须做的配套是配置版本管理。我们有过一次事故运营手动改错了路由白名单导致所有复杂任务都走了轻量模型线上回答质量骤降持续了整整两个小时才被发现。后来把所有配置改动用 Git 管理每次变更都生成版本记录支持一键回滚这个问题才彻底解决。3. 从零到一落地的完整实操路径讲完五个件我整理一下从零开始搭这套系统的具体步骤方便你照着推。3.1 第一步先做最小闭环别一上来就堆满五个件我建议的最小闭环只做两件事模型感知路由器 工具编排执行器。这两个件是能让 Agent “自己挑模型、自己挑工具”的最小组合先跑通它们再看哪里需要补第三、第四、第五个件。最小闭环的落地逻辑是定义三类任务轻量问答、工具调用、复杂推理定义两个工具一个查询接口、一个简单计算接口然后把路由器和执行器串起来。链路长这样用户提问 → 2. 路由器做意图与复杂度判断 → 3. 命中工具类任务 → 4. 执行器匹配工具并编排顺序 → 5. 调用模型生成最终话术 → 6. 返回用户。我强烈建议第一步不要引入记忆和评估原因很现实记忆和评估会引入很多变量一旦效果不好你根本分不清是路由的问题还是记忆的锅。先把主链路调顺再逐步加“外挂”。3.2 第二步给每个模型和工具做能力画像模型能力画像是一个 JSON 结构我在项目里的初始版本大概是这样的{ model_id: light-v1, capabilities: { tool_calling: false, context_window: 8192, reasoning_level: low, multilingual: true }, pricing: { per_1000_input_tokens: 0.002, per_1000_output_tokens: 0.008 }, latency: { p50_ms: 500, p95_ms: 1200 } }工具能力画像同样重要我还额外加了两个字段requires和conflicts。requires 声明这个工具的前置条件conflicts 声明这个工具与哪些工具不宜同时调用。这俩字段是执行器做依赖编排的关键依据。比如“订单退款”工具 requires“订单详情”工具的输出作为输入那执行器就会自动把订单详情排在前面。这里有个非常容易忽略的细节工具画像的描述语言一定要和用户的表达习惯对齐而不是和开发者的习惯对齐。我一开始写工具描述用的是后端术语比如“获取 ORS_OUT 数据结构”结果 Agent 根本不知道什么时候该调。后来改成“获取订单的退货申请状态与退款金额”匹配率立刻上来了。工具画像的说人话程度直接决定工具选择的成功率。3.3 第三步配置动态策略用规则给 Agent 画边界五件里我最后加的是策略配置面板但你第一轮迭代就可以先把策略配置文件建起来。一个最小可用的策略配置大概包含这么几块routing: budget_limit_no_more_than: 0.05 # 单次请求成本上限元 force_model_for_intent: 售后投诉: heavy-v1 订单查询: medium-v1 max_context_priority: - current_question - recent_2_turns - unfinished_intent tool_selector: top_k: 3 banned_tools: [] require_slot_confidence_over: 0.7 evaluator: trigger_level: medium_and_above retry_patch_threshold: 0.85 full_retry_threshold: 0.60这套配置的第一版不需要多复杂重要的是建立“改行为不碰代码”的习惯。我见过太多团队在配置文件里堆了上百个字段最后没人敢改反而比写死代码更僵化。3.4 第四步加记忆再评估最后做面板等最小闭环稳定运行一周并且你手里有了第一轮真实流量的数据再开始加第三件记忆索引器和第四件评估修正模块。这两个件的顺序不能反因为评估器需要覆盖“多轮记忆未命中”的场景如果记忆还没上评估器看到的全是单轮对话覆盖面不够容易把模型能力问题误判成记忆问题。最后再把分散的配置项收拢到控制面板加上可视化、版本回滚、操作审计。前四件跑通了第五件只是一个工程包装花不了太多精力。我还想多说一句每一步都要留基准测试集。我每迭代一个版本都会固定抽一个包含 500 条真实请求的测试集跑回归对比路由准确率、工具调用成功率、最终回答通过率、单次调用成本这四个指标。没有一个客观基准在手里你会被各种“好像好了一点”的感觉带偏。4. 踩坑实录与排查技巧4.1 路由器“自信地选错模型”复杂度评估被一句话带偏第一版路由器上线后最典型的问题是把带数字的简单问题判成复杂任务。比如“我家有三个订单加起来多少钱”里面有数字、有订单实体路由器直接判成高复杂度丢给重量级模型。结果响应慢、成本高而这个问题其实一个工具调用就搞定了。排查下来发现复杂度评估里“实体数量”和“逻辑链长度”两个维度严重耦合。三个订单意味着实体多但逻辑链求和很简单。我后来在评估规则里加了“算子复杂度”维度如果用户问题里只有聚合、比较、筛选类的单算子逻辑即使实体数量达到三五个复杂度也只能算中等不能直接升到高。这个规则修正之后路由准确率从 71% 提到了 86%。排查这类问题的通用方法是给每条路由记录加日志记录输入、路由向量、各维度的评分、最终决策结果。线上出问题后从日志里拉出所有“路由结果与实际难度不符”的样本人工打标后反哺规则库基本上一周就能迭代一轮。4.2 工具选择陷入“循环调谁都不对”依赖图谱有环有一次线上事故特别诡异用户问“帮我取消订单”执行器先调“订单详情”查出订单状态为“退款中”然后调“取消订单”返回“该状态不支持取消”再调“订单详情”查出状态没变又调“取消订单”……整整循环了六次才被熔断。用户都等疯了。查日志发现执行器的依赖图谱里“取消订单”工具声明的前置条件是“订单当前状态非取消/非退款中”而“订单详情”返回的状态字段更新没能反向刷新图谱里的前置条件。说白了工具之间的依赖判断只做了静态声明没有做动态状态校验。修复方案是给执行器加了一个“状态机护栏”主流程在每一步执行后都更新一次业务状态凡是下一个工具的前置条件与当前状态冲突就直接停止该工具链改为向用户说明情况并提供替代动作比如“当前订单退款中无法取消您可以申请加急退款”。这一改类似的循环再也没出现。4.3 记忆压缩“过度积极”关键参数被摘要吞了记忆压缩初见效果时我一度觉得特别满意直到有一天用户说“你现在应该知道收件地址了吧我上次跟你说了”Agent 却一本正经地问“请提供您的收货地址”。翻记忆索引发现上次对话确实存了收货地址但压缩摘要只写着“用户提供了收货信息”“地址”这个字段本身没被索引。问题出在我们的压缩策略压缩摘要用了模板化表述关键词反而丢了实体提取层和摘要层没有打通。改法不复杂——压缩时必须先做实体抽取然后把实体字段直接映射到索引 slot摘要只是给人看的索引的关键字段必须保留原文。说人话就是摘要归摘要核心字段单独存。我记得当时还有个额外的收获把实体字段单列后记忆召回率提升并不是最明显的最明显的是召回“精准度”召回内容不再是一大段摘要文本而是结构化的实体时间戳来源模型用它来回答问题比读摘要准确得多。4.4 评估器“自嗨”质量回放器说好结果用户完全不买账这是最打击我的一次“翻车”。我们上线评估器后内部基准测试的通过率一路涨到 90% 以上结果真实用户满意度调查不仅没涨反而掉了几分。分析样本后发现问题很扎心评估器喜欢“结构调整好、逻辑完整”的回答而用户其实只想要“一句话把问题解决掉”。比如用户问“到哪了”Agent 回答了一大段“您的订单XXX在派送中预计今日18点前送达如未收到请联系……”结构完整逻辑严丝合缝评估器给了高分但真实用户就是嫌啰嗦。修复之道不是改评估器阈值而是给评估器的质量标准里加入“回复简洁度”“是否直接命中用户的隐含期望”这两项并且用真实用户体验的打分样本去微调评估器自己的提示词。我后来还加了一个很损但有效的技巧把评估结果和后续用户行为关联起来。用户看了回答后有没有继续追问、有没有主动结束会话、有没有给正向反馈这些信号会回传成评估器的“人工校准信号”。这套机制跑起来后评估器才终于不再自嗨。4.5 动态配置“热更新”引发的连锁反应配置中心上线后的第三天我改了一条路由规则把“物流查询”强制模型从 medium-v1 改成 light-v1本意是省钱。结果不到一小时客服主管反馈物流查询准确率掉了。原因很简单物流查询这类任务虽然复杂度不高但仍然需要读取外部接口返回的 JSON 数据light-v1 模型处理 JSON 抽取的能力不够经常把“运输中”和“已签收”弄混。这个事故的关键教训是路由配置不能只看任务类型还要看工具的返回结构复杂度。我后来在工具画像里加了一个字段“返回结构复杂度”路由器在决策时会把这个字段纳入评估凡是调用了复杂返回结果的工具即使任务本身的逻辑简单也不能路由到太轻量的模型。这类配置事故的排查速度取决于你留没留“决策时间线”。我们在每次路由决策里记录了配置表的版本号出事后直接对比两个版本的行为差异一锤定音。5. 最后分享两个实操小经验第一五件自制件之间一定要埋日志链路追踪。我是用了一个超简单的 request_id 把路由决策、工具调用、记忆召回、评估结果串起来任何一轮对话出了问题都能顺着 ID 把全部行为回放一遍。没有这个所有排查都会变成盲猜。第二任何一次迭代改动先拿最影响业务体验的 5% 流量做灰度。Agent 系统的行为具有随机性同一个改动可能让一部分用户觉得更好另一部分觉得更差。我一般会分别对比静态指标成本、延迟、工具成功率和动态体验指标满意度回传、流失率两边都正向才全量放量。让 Agent 自己挑模型、自己挑工具不是一句口号而是把分散在代码里的决策逻辑收敛成一个个可测试、可回滚、可迭代的模块。这个方向我已经跑了比较长时间最直观的感受是系统的个性变得“可定制”了业务人员第一次觉得 AI 的行为是可以在运营层面调整的。这个感觉比任何技术指标都让人上瘾。