1. 为什么让 Agent 自己挑模型和工具是个真问题
做过 Agent 项目的人大概都有过这种体验:一开始图省事,把模型和工具全写死在代码里,跑起来看着挺美,一旦换个任务类型就抓瞎。比如你写了个专门做代码审查的 Agent,模型固定用某个擅长推理的大模型,工具固定挂一个静态分析器,结果某天产品经理跑来说“能不能顺便让它帮我总结一下会议纪要”,你就得回去改代码、重新部署、再测一轮。这种硬编码的方式在 Demo 阶段没问题,但一旦要面对真实场景里五花八门的需求,维护成本会指数级上升。
我前后花了大概三周时间,把手上一个 Agent 框架里的五个核心组件全部重写了一遍,目标就一个:让 Agent 在运行时自己决定用哪个模型、调哪个工具、走哪条执行路径。这五个组件我称之为“自制件”,因为它们不是现成的开源方案直接拿来用,而是根据实际业务需求自己设计的。整个过程踩了不少坑,也做了很多取舍,有些设计决策事后看是对的,有些则走了弯路。这篇文章就把这五件自制件的设计思路、实现细节和踩坑经验完整拆开讲一遍。
先说一下这个 Agent 框架的基本盘。它主要处理的是多步骤任务,比如“读取一份需求文档,提取关键信息,生成测试用例,再调用接口做验证”。这类任务的特点是步骤之间有依赖关系,每一步需要的模型能力和工具类型都不一样。早期版本里,我是在配置文件里写死一条流水线,每个节点指定模型和工具。后来发现,不同项目的需求差异太大,有的需要强推理,有的需要快响应,有的需要特定格式输出,写死的方案根本扛不住。
所以核心思路就变成了:把模型选择和工具选择从“编译时”挪到“运行时”,让 Agent 根据当前任务的上下文、历史执行结果、资源预算等因素,动态做出决策。听起来简单,但真做起来,涉及的问题远比想象中多。下面我按五个自制件逐一展开。
2. 自制件一:模型路由器的设计取舍
2.1 为什么不用简单的 if-else 做模型选择
最开始我想得很简单:搞一个函数,输入任务类型,输出模型名称,里面就是一堆 if-else。比如任务是“代码生成”就用模型 A,“文本摘要”就用模型 B,“逻辑推理”就用模型 C。这个方案上线第一天就出问题了。原因是任务类型分类本身就不准确,很多任务其实是混合型的,比如“根据错误日志推断根因并生成修复代码”,它既需要推理又需要代码生成,你把它归到哪一类都不对。
更麻烦的是,同一个任务在不同阶段对模型的要求也不一样。比如一个长文档处理任务,前期需要快速提取关键段落,这时候用个轻量模型就够了;后期需要对提取的内容做深度分析,就得换强模型。如果按任务类型一刀切,要么浪费算力,要么效果打折。
所以我最终设计的模型路由器,输入不是单一的任务类型标签,而是一个结构化的上下文对象,包含当前步骤的目标描述、历史步骤的执行结果摘要、剩余预算(token 数和费用)、以及一个粗略的复杂度评分。输出是一个模型候选列表,按推荐优先级排序,而不是只给一个答案。
2.2 复杂度评分怎么算
复杂度评分是整个路由器的核心输入之一。我试过几种方案,最后落在一个比较务实的做法上:用轻量模型对当前步骤的目标描述做一个快速评估,输出 1 到 5 的评分。1 代表“简单格式化或提取”,5 代表“需要多步推理和领域知识”。这个评估本身消耗很少,大概几十个 token,但能显著提升路由准确率。
具体实现上,我写了一个提示词模板,让轻量模型从四个维度打分:语义理解难度、推理深度、输出格式复杂度、领域知识依赖度。每个维度 1 到 5 分,然后取加权平均。权重是根据历史数据调出来的,推理深度权重最高,0.4,其余三个各 0.2。这个权重不是拍脑袋定的,我拿过去三个月的任务日志做了回归分析,发现推理深度和最终模型选择的相关性最强。
注意:复杂度评分不需要非常精确,它的作用是提供一个粗略的分档依据。如果你花太多精力去优化这个评分,边际收益很低。我一开始就是在这个环节过度设计,后来发现只要能把“简单任务”和“复杂任务”区分开,路由效果就已经很好了。
2.3 模型候选列表的排序逻辑
拿到复杂度评分后,路由器会从模型池里筛选候选。模型池里每个模型都有元数据:能力标签(推理、代码、摘要、翻译等)、成本系数、平均响应时间、上下文窗口大小、历史成功率。排序逻辑是一个加权打分公式:
总分 = 能力匹配度 × 0.5 + 成本效率 × 0.2 + 响应速度 × 0.15 + 历史成功率 × 0.15
能力匹配度是根据当前步骤需要的标签和模型标签做交集算出来的。成本效率是预算除以模型成本系数。响应速度是历史平均延迟的归一化值。历史成功率是这个模型在过去类似任务上的成功比例。
这个公式看起来简单,但每个因子的计算都有讲究。比如能力匹配度,我一开始用的是二值匹配(有标签就是 1,没有就是 0),结果发现太粗糙了。后来改成用标签的语义相似度做加权,效果好很多。具体做法是把标签转成向量,算余弦相似度,再映射到 0 到 1 之间。
2.4 踩坑记录:模型切换的上下文丢失问题
这是我在模型路由器上踩的最大的坑。当 Agent 在一个任务中途从模型 A 切换到模型 B 时,模型 B 并不了解之前发生了什么。我一开始的做法是把历史对话完整传过去,但很快发现两个问题:一是 token 消耗巨大,二是不同模型的对话格式不一样,直接传过去模型 B 可能理解不了。
后来我设计了一个“上下文摘要”机制。每次切换模型前,先用当前模型对历史执行结果做一个结构化摘要,包含已完成步骤、关键发现、待解决问题、当前状态。这个摘要用统一的 JSON 格式,然后作为系统提示的一部分传给新模型。这样既控制了 token 消耗,又保证了信息的可传递性。
实测下来,这个摘要机制能把切换后的任务成功率从 62% 提升到 87%。代价是每次切换多花大概 200 到 500 个 token 做摘要,但相比重新跑一遍任务,这个成本完全可以接受。
3. 自制件二:工具选择器的匹配策略
3.1 工具描述的结构化改造
工具选择器的核心问题是:怎么让 Agent 知道有哪些工具可用,以及每个工具能干什么。最原始的做法是维护一个工具列表,每个工具写一段自然语言描述。但自然语言描述有个致命问题:歧义。比如“搜索”这个描述,可能是网页搜索、文件搜索、数据库搜索,Agent 根本分不清该用哪个。
我的做法是把工具描述结构化,每个工具必须包含以下字段:工具名称、功能分类、输入参数 schema、输出格式、适用场景描述、限制条件、调用示例。其中功能分类和适用场景描述是给 Agent 看的,其余是给执行引擎用的。
功能分类我用了一个两级分类体系。一级分类包括:信息获取、数据处理、内容生成、外部交互、系统操作。二级分类更细,比如信息获取下面有网页搜索、文件读取、数据库查询、API 调用。这个分类体系不是固定的,可以根据业务需要扩展,但扩展时要保持层级一致性。
3.2 匹配算法:从关键词到语义
工具匹配我试过三种方案。第一种是关键词匹配,把任务描述里的关键词和工具描述做交集。这个方案在工具数量少的时候还行,超过二十个工具后准确率急剧下降。第二种是规则匹配,手写一堆 if-else 规则。这个方案维护成本太高,每加一个工具就要改规则。第三种是语义匹配,用嵌入模型把任务描述和工具描述都转成向量,算相似度。这个方案效果最好,但有个前提:工具描述必须写得足够清晰和区分度高。
我最终用的是语义匹配为主、关键词匹配为辅的混合方案。具体流程是:先用语义匹配召回 top 5 候选工具,然后用关键词匹配做二次排序,最后取 top 3 给 Agent 做最终选择。为什么要让 Agent 做最终选择而不是直接取 top 1?因为有些工具的适用性需要结合当前上下文判断,纯算法匹配做不到百分之百准确。给 Agent 两三个选项,让它结合当前任务状态做决定,成功率更高。
3.3 工具调用的参数填充
工具选对了,参数填错也是白搭。我遇到过好几次 Agent 选对了工具但参数格式不对导致调用失败的情况。比如一个日期查询工具需要 ISO 格式的日期,Agent 传了个“明天”这样的自然语言,工具直接报错。
解决方案是在工具定义里加一个参数预处理层。每个参数除了类型和描述,还要指定一个“规范化函数”。这个函数负责把 Agent 输出的自然语言参数转成工具能接受的格式。比如日期参数,规范化函数会调用一个日期解析器,把“明天”“下周三”“2024 年底”这类表达转成标准格式。
这个预处理层是工具选择器里最容易被忽视但最重要的部分。我统计过,在加入参数预处理之前,工具调用失败的原因里,参数格式错误占了将近一半。加入之后,这个比例降到了 8% 以下。
3.4 踩坑记录:工具冲突和循环调用
工具冲突是指两个工具功能重叠,Agent 不知道该用哪个。比如同时挂了两个搜索工具,一个搜内部文档,一个搜外部网页,任务描述是“查找关于 X 的资料”,Agent 可能随机选一个,也可能两个都调一遍。我的处理方式是在工具元数据里加一个“优先级”字段和“互斥”字段。优先级高的工具在匹配分数相近时优先推荐,互斥的工具在同一个任务步骤里只能选一个。
循环调用是另一个坑。Agent 调用了一个工具,拿到结果后发现不满足条件,又调用同一个工具,反复几次陷入死循环。我在执行引擎里加了一个调用计数器,同一个工具在同一个任务步骤里最多调用三次,超过就强制中断并返回错误。同时,在工具选择器的输入里加入“已尝试工具列表”,让 Agent 知道哪些工具已经试过了,避免重复选择。
4. 自制件三:执行引擎的状态管理
4.1 状态机的设计
执行引擎是整个 Agent 的调度中心,它负责按顺序执行任务步骤,每一步调用模型路由器选模型、调用工具选择器选工具,然后执行并记录结果。我一开始用的是一个简单的循环结构,但很快发现不够用,因为任务步骤之间有复杂的依赖关系,有的步骤可以并行,有的必须串行,有的步骤失败后需要回滚。
后来我把它改成了一个状态机。状态包括:初始化、规划中、执行中、等待工具结果、等待模型响应、步骤完成、任务完成、任务失败、回滚中。每个状态有明确的进入条件和退出条件,状态之间的转换由事件驱动。比如“执行中”状态下,如果当前步骤需要调用工具,就转换到“等待工具结果”;工具返回后,再转换回“执行中”继续下一步。
这个状态机的好处是逻辑清晰,每个状态只关心自己的事情,不用管全局。调试的时候也方便,可以清楚地看到任务卡在哪个状态。
4.2 上下文窗口的管理
Agent 执行多步骤任务时,上下文会不断增长。如果不加管理,很快就会超出模型的上下文窗口限制。我的做法是维护一个分层上下文:短期上下文保存最近三步的完整信息,中期上下文保存当前任务的所有步骤摘要,长期上下文保存跨任务的关键知识。
短期上下文直接传给模型,中期上下文按需检索,长期上下文存在外部存储里,需要时通过检索工具获取。这个分层结构让上下文窗口的使用效率提升了很多。实测下来,同样一个十步任务,不分层的话到第七步就超限了,分层之后可以完整跑完,而且 token 消耗还少了大概 30%。
4.3 错误处理和重试策略
错误处理是执行引擎里最复杂的部分。错误分几类:模型调用失败(超时、限流、返回格式错误)、工具调用失败(参数错误、服务不可用、返回结果不符合预期)、逻辑错误(步骤依赖不满足、条件判断失败)。
针对不同错误类型,我设计了不同的重试策略。模型调用失败,如果是超时或限流,等几秒后重试,最多三次;如果是返回格式错误,把错误信息反馈给模型,让它重新生成。工具调用失败,如果是参数错误,让 Agent 重新填充参数;如果是服务不可用,切换到备用工具;如果是结果不符合预期,把结果和预期一起反馈给 Agent,让它决定下一步。
提示:重试策略一定要设置上限,不然容易陷入无限重试。我的上限是每个步骤最多重试五次,超过就标记为失败,进入回滚或跳过逻辑。
4.4 踩坑记录:状态不一致和并发问题
状态不一致是我踩过的最隐蔽的坑。有一次任务执行到一半,模型路由器切换了模型,但执行引擎的状态还停留在旧模型的状态上,导致后续步骤全部错乱。排查了很久才发现是状态更新没有做到原子性。后来我在状态机里加了一个版本号,每次状态变更都递增版本号,模型路由器和工具选择器在读取状态时先检查版本号,不一致就重新读取。
并发问题出现在并行步骤上。有两个步骤可以并行执行,但它们共享同一个上下文对象,一个步骤写入的时候另一个步骤正在读取,导致数据竞争。解决方案是给上下文对象加读写锁,写的时候加排他锁,读的时候加共享锁。这个方案会稍微降低并发性能,但保证了数据一致性,我觉得是值得的。
5. 自制件四:反馈收集器的闭环设计
5.1 为什么要做反馈收集
Agent 自己选模型和工具,选得好不好需要有个评判机制。没有反馈,路由器和选择器就没法优化。我设计的反馈收集器负责在任务执行过程中和结束后收集各类信号,包括:模型输出的质量评分、工具调用的成功率、任务完成时间、token 消耗、用户显式反馈(如果有的话)。
这些信号汇总后,会定期用来更新模型路由器的权重参数和工具选择器的匹配模型。这是一个闭环系统,Agent 用得越多,选择就越准。
5.2 质量评分的自动化方案
用户显式反馈很难拿到,大部分时候用户不会主动评价。所以我主要依赖自动化评分。模型输出质量评分我用了一个“一致性检查”方案:同一个步骤用两个不同模型各跑一遍,比较输出结果的一致性。一致性高的步骤,说明模型选择没问题;一致性低的步骤,说明可能需要换模型或者调整提示词。
工具调用成功率是更直接的信号。每次工具调用都记录成功或失败,以及失败原因。这些数据按工具和任务类型分组统计,用来调整工具选择器的优先级。
5.3 反馈数据的存储和查询
反馈数据我存在一个轻量数据库里,每条记录包含:任务 ID、步骤 ID、模型名称、工具名称、执行时间、token 消耗、成功标志、错误信息、质量评分。查询接口支持按时间范围、模型、工具、任务类型等维度聚合。
这个数据库不大,但很实用。我每周会跑一次分析,看看哪些模型和工具的组合表现最好,哪些组合经常出问题。基于这些分析,我会手动调整路由器的权重参数。未来打算把这个调整过程也自动化,但目前手动调整的效果已经够用了。
5.4 踩坑记录:反馈偏差和过拟合
反馈收集最大的坑是偏差。如果某个模型被路由到的次数多,它的反馈数据就多,统计上看起来更可靠,路由器就更倾向于选它,形成正反馈循环。这会导致其他模型得不到足够的曝光,即使它们可能更适合某些任务。
我的解决方案是引入“探索机制”。路由器在推荐模型时,有 10% 的概率推荐一个“非最优但值得尝试”的模型。这个概率可以根据任务的重要程度调整,重要任务降低探索概率,普通任务保持默认。这样既保证了主要任务的稳定性,又给了其他模型曝光机会。
过拟合是另一个问题。如果路由器过度依赖历史数据,遇到新类型的任务时可能表现很差。我的做法是定期用新数据重新训练路由器的权重,同时保留一个“默认策略”作为兜底。当路由器对当前任务的置信度低于阈值时,就回退到默认策略。
6. 自制件五:配置中心的动态管理
6.1 配置项的分类和优先级
配置中心管理所有可调参数,包括:模型池配置、工具池配置、路由器权重、选择器阈值、执行引擎超时时间、反馈收集频率等。这些配置分三个优先级:系统级、任务级、步骤级。系统级配置是全局默认值,任务级配置覆盖系统级,步骤级配置覆盖任务级。
优先级机制让配置既有统一性又有灵活性。比如系统级默认用模型 A 做推理,但某个任务需要更快的响应,可以在任务级配置里改成模型 B。如果某个步骤特别复杂,还可以在步骤级配置里单独指定模型 C。
6.2 热更新和版本管理
配置中心支持热更新,修改配置后不需要重启 Agent 就能生效。实现方式是用一个配置监听器,定期检查配置版本号,发现变化就重新加载。重新加载时采用“双缓冲”策略:新配置先加载到备用缓冲区,加载成功后原子性地切换主备缓冲区,避免加载过程中出现配置不一致。
版本管理方面,每次配置修改都会生成一个新版本,旧版本保留最近十个。如果新配置导致问题,可以快速回滚到旧版本。这个机制在上线初期救了我好几次,有一次改了一个路由器权重导致所有任务都选了最贵的模型,回滚后立刻恢复正常。
6.3 配置的校验和默认值
配置修改最容易出的问题是格式错误或值超出范围。我在配置中心加了一个校验层,每个配置项都有类型、范围、依赖关系的定义。修改配置时先过校验层,不通过就拒绝并返回错误信息。
默认值的设计也很重要。每个配置项都必须有默认值,这样即使配置文件丢失或损坏,Agent 也能用默认值启动。默认值的选择原则是“保守优先”,比如超时时间默认设长一点,重试次数默认设少一点,避免因为配置问题导致任务大面积失败。
6.4 踩坑记录:配置漂移和依赖冲突
配置漂移是指不同环境(开发、测试、生产)的配置不一致,导致同一个任务在不同环境下表现不同。我的解决方案是把配置文件纳入版本控制,每个环境用不同的分支,合并时强制走代码审查流程。同时,Agent 启动时会打印当前生效的配置摘要,方便排查。
依赖冲突是指两个配置项之间存在隐含的依赖关系,修改一个会影响另一个。比如模型池里删除了一个模型,但路由器权重里还有这个模型的权重,就会导致路由失败。我在配置中心加了一个依赖检查器,修改配置时自动检查是否有其他配置项依赖它,有的话给出警告。
7. 五个自制件的协同工作和实际效果
7.1 数据流和调用关系
五个自制件不是孤立的,它们之间有一套清晰的调用关系。配置中心提供所有参数,模型路由器和工具选择器从配置中心读取模型池和工具池。执行引擎在每一步开始时,先向模型路由器请求模型候选,再向工具选择器请求工具候选,然后执行并收集结果。反馈收集器在执行过程中持续收集信号,任务结束后汇总写入数据库。反馈数据定期用来更新路由器和选择器的参数,更新后的参数写回配置中心。
这个闭环让整个系统有了自我优化的能力。我统计过,系统上线第一个月,任务平均完成时间下降了 23%,token 消耗下降了 18%,任务成功率从 71% 提升到了 89%。这些提升主要来自路由器和选择器的持续优化。
7.2 性能瓶颈和优化
性能瓶颈主要出现在两个地方:一是模型路由器的复杂度评分环节,每次都要调用轻量模型,增加了延迟;二是反馈收集器的数据库写入,高频写入时会有锁竞争。
针对第一个问题,我加了一个缓存层。相同或相似的任务描述,复杂度评分直接读缓存,不用重新计算。缓存命中率大概在 65% 左右,显著降低了延迟。针对第二个问题,我把反馈数据先写入内存队列,然后批量写入数据库,减少了锁竞争。
7.3 实际案例:一个多步骤任务的全流程
举个实际例子。任务描述是“读取一份产品需求文档,提取功能点,为每个功能点生成测试用例,调用接口验证测试用例的可行性”。
执行引擎首先进入规划状态,把任务拆成四个步骤。第一步是文档读取,复杂度评分 2,路由器推荐轻量模型 A 和文件读取工具。执行后拿到文档内容。第二步是功能点提取,复杂度评分 3,路由器推荐中等模型 B 和文本分析工具。执行后拿到功能点列表。第三步是测试用例生成,复杂度评分 4,路由器推荐强模型 C 和代码生成工具。执行后拿到测试用例。第四步是接口验证,复杂度评分 3,路由器推荐中等模型 B 和 API 调用工具。执行后拿到验证结果。
整个过程中,反馈收集器记录了每一步的模型、工具、耗时、token 消耗和成功标志。任务完成后,这些数据写入数据库,用于后续优化。
7.4 踩坑记录:协同工作中的信息丢失
五个自制件协同工作时,最容易出的问题是信息丢失。比如执行引擎把任务状态传给路由器,但路由器只用了其中的一部分信息做决策,导致决策不准确。或者反馈收集器收集了信号,但更新参数时没有正确关联到对应的模型和工具。
我的解决方案是定义一个统一的数据契约,所有自制件之间的数据传递都遵循这个契约。契约里明确了每个字段的含义、类型和必填性。任何一方修改契约都要通知其他方,并更新版本号。这个做法增加了前期设计成本,但大幅减少了后期的调试时间。
8. 常见问题速查和避坑指南
8.1 模型路由相关
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 总是选最贵的模型 | 成本效率权重过低 | 检查路由器权重配置 | 提高成本效率权重,或设置成本上限 |
| 模型切换后任务失败 | 上下文摘要不完整 | 查看切换时的摘要内容 | 完善摘要模板,增加关键字段 |
| 复杂度评分不准 | 评分提示词不清晰 | 抽样检查评分结果 | 优化提示词,增加评分示例 |
| 路由延迟高 | 缓存未命中或模型调用慢 | 查看缓存命中率和调用耗时 | 扩大缓存容量,或改用更快的轻量模型 |
8.2 工具选择相关
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 选错工具 | 工具描述区分度低 | 检查工具描述文本 | 重写描述,增加区分性关键词 |
| 参数格式错误 | 缺少规范化函数 | 查看工具定义 | 为每个参数添加规范化函数 |
| 工具循环调用 | 缺少调用计数 | 查看执行日志 | 加调用计数器,设置上限 |
| 工具冲突 | 功能重叠 | 检查工具分类 | 设置优先级和互斥标记 |
8.3 执行引擎相关
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 任务卡住不动 | 状态机死锁 | 查看当前状态和事件队列 | 检查状态转换条件,加超时机制 |
| 上下文超限 | 分层策略未生效 | 查看上下文大小 | 检查分层逻辑,压缩中期上下文 |
| 重试次数过多 | 重试策略太激进 | 查看重试日志 | 降低重试上限,区分错误类型 |
| 并发数据竞争 | 缺少锁机制 | 查看并发步骤日志 | 加读写锁,或改为串行执行 |
8.4 反馈收集相关
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 反馈数据偏差 | 正反馈循环 | 分析模型选择分布 | 引入探索机制,增加随机性 |
| 过拟合 | 历史数据权重过高 | 检查新任务表现 | 定期重新训练,保留默认策略 |
| 数据库写入慢 | 高频写入锁竞争 | 查看写入延迟 | 改用批量写入,加内存队列 |
| 质量评分不准 | 一致性检查阈值不合理 | 抽样对比评分和人工评价 | 调整阈值,增加评分维度 |
8.5 配置中心相关
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 配置不生效 | 缓存未刷新 | 检查配置版本号 | 加配置监听器,定期刷新 |
| 配置冲突 | 依赖关系未检查 | 查看配置依赖图 | 加依赖检查器,修改时警告 |
| 环境不一致 | 配置文件未同步 | 对比各环境配置 | 纳入版本控制,强制审查 |
| 回滚失败 | 旧版本被覆盖 | 检查版本保留策略 | 保留最近十个版本,定期归档 |
提示:这张速查表建议打印出来贴在工位上,出问题的时候按表排查,能省很多时间。我自己的经验是,80% 的问题都能在前三行找到答案。
8.6 三条独家避坑心得
第一条:不要追求一次性设计完美。我一开始想把五个自制件全部设计好再写代码,结果设计了两周还没动手,后来改成边设计边实现,先跑通最小闭环,再逐步优化,效率高了很多。Agent 系统本身就很复杂,很多问题只有跑起来才会暴露。
第二条:日志要打够,但不要打太多。我一开始日志打得太少,出问题找不到线索;后来打太多,日志文件一天几十个 G,排查的时候反而找不到关键信息。最后的做法是分级日志:关键决策点打 INFO,详细数据打 DEBUG,错误打 ERROR,生产环境只开 INFO 和 ERROR。
第三条:定期做全链路压测。五个自制件协同工作,单测都过不代表整体没问题。我每周会跑一次全链路压测,模拟各种任务类型和异常情况,确保系统在压力下也能稳定运行。这个习惯帮我提前发现了好几个只有在高并发下才会出现的 bug。
9. 后续可以继续深挖的方向
这套系统跑了一个多月,整体表现稳定,但还有几个方向我觉得值得继续深挖。一个是模型路由器的在线学习能力,目前权重调整还是半自动的,未来想做成完全自动的,让系统根据实时反馈自己调参。另一个是工具选择器的跨任务迁移能力,目前工具匹配主要依赖当前任务的上下文,如果能利用历史任务的经验,匹配准确率应该还能再提升一截。
还有一个方向是执行引擎的并行优化。目前只有明确标记为可并行的步骤才会并行执行,未来想做成自动识别可并行步骤,进一步提升执行效率。不过这个方向的风险也比较大,并行执行带来的状态一致性问题比串行复杂得多,需要谨慎推进。
反馈收集器这块,我想加入更多维度的信号,比如模型输出的风格一致性、工具返回结果的信息密度等。这些信号目前还没有被充分利用,如果能纳入评分体系,应该能让路由和选择更精准。
配置中心目前还是单机部署,未来如果 Agent 要支持多实例,配置中心需要改成分布式方案,保证多个实例之间的配置一致性。这个改造涉及的技术点比较多,暂时还在调研阶段。
整体来说,让 Agent 自己挑模型、自己挑工具这件事,核心不在于算法有多复杂,而在于工程细节有没有做到位。五个自制件每一个单独看都不算难,但把它们协同起来,让数据在它们之间顺畅流动,让反馈闭环真正转起来,这才是最花时间和精力的地方。我踩过的坑大部分都不是算法问题,而是工程问题:状态不一致、信息丢失、配置漂移、并发竞争。这些问题没有捷径,只能靠严谨的设计和充分的测试来避免。