那天我在看一批技能的状态。表面上,这是一次很普通的整理:最近 7 天改动过的技能超过 60 个,需要挑出值得审查和优化的部分。可真正往下追,我发现其中有几次是批量改路径,并不能直接算成真实开发。更麻烦的是,一批平时经常用到的能力,反而被放进了休眠状态。
我一开始以为,技能治理的难点是数量太多,得想办法让目录变轻。后来才发现,真正的问题不是目录有多大,而是系统在需要一个能力时,能不能及时找到正确的说明书,并且能不能调用到它依赖的运行时源码。治理技能的第一指标,不应该是它占了多少空间,而应该是它在真实工作中承担了什么作用。
一次看起来合理的整理
当时的做法并不荒唐。技能越来越多,说明书全部处于可见状态,会给自动路由带来上下文压力。于是很自然会想到:把体积最大的目录先藏起来,把空间和注意力都省下来。目录越大,似乎越值得优先处理;越少被看到,似乎就越不容易拖慢系统。
问题就在这个“似乎”里。磁盘上的体积,和说明书是否被加载,并不是一回事。一个技能可能因为附带数据、模型、虚拟环境或者构建产物而很大,但自动路由真正需要读的,往往只是很短的说明。把整个目录按体积排序,实际上把“占空间”和“影响调用”混成了一件事。
如果不处理这个误差,后果不是简单的空间浪费。高频能力被藏起来以后,用户只说一个自然任务,系统就可能不知道该调用哪个入口;如果用户没有点名技能,原本应该自动发生的路由就会断掉。表面上,整理动作完成了,实际工作却变得更依赖用户记住内部名称。
这件事让我重新意识到,治理一个工具集合时,最容易被优化的是看得见的指标:目录大小、文件数量、改动次数。真正重要的指标却常常藏在运行过程中:谁被调用,调用时依赖什么,缺失以后会不会让现场任务停下来。
休眠之后,真正坏掉的是什么
这次复盘里,“休眠”并不是把代码删除,也不是把数据清空,而是把技能的说明书藏到自动发现路径之外。直接点名时,它可能仍然能工作;但在只描述任务、不说内部名称的情况下,系统就不一定能自动发现它。
这个区别非常关键。有人看到代码和数据都还在,就会判断技能没有受影响。可对一个依赖自动路由的 Agent 来说,能不能被发现,本身就是可用性的一部分。文件还在,只说明物理数据没有丢;说明书没有被加载,说明行为入口已经变了。
我去看近两周的真实调用,结果和按体积排序的直觉几乎相反。那些被休眠的技能中,有的在不同会话里被调用了约 50 次,有的约 40 次、35 次或 30 次,另外几项也有约 20 次。它们恰好是目录里比较大的那批,所以才被先处理。但“目录大”和“使用少”在这里没有成立的关系。
文件是否还在,不能证明技能没有受影响;必须验证说明书能否被发现、运行入口能否被调用。
这里最容易误判的地方,是把“能强行运行”当成“系统正常”。用户如果明确说出一个内部名称,系统当然可能绕过自动路由;但真实工作往往只给目标,不给实现名。一次点名成功,不能替代自然语言场景下的自动发现测试。
源码和资料不能放在同一层
磁盘分层本身并没有错,错的是没有先区分数据的职责。复盘源码依赖时,我发现有些技能只是把下载下来的内容当资料看,真正开工时并不执行它们。这类源码不必一直占据本地热层,放在外部存储也可以,只要需要时还能找到。
另一类源码则完全不同。它们被技能包装起来,技能一开工就要启动其中的程序、调用其中的接口,或者读取其中的运行环境。这样的源码不是“附属资料”,而是技能的运行时依赖。只要它被搬到一个可能掉线、恢复不稳定或者路由复杂的位置,技能就可能在真正执行时失败。
我选择把这类被实际执行的源码留在本地热层,是因为它们直接决定技能能不能完成任务。这里的判断依据不是源码大小,也不是下载来源,而是“开工时是否必须读它”。纯资料、报告、下载缓存等过程数据,才适合在确认不影响运行后进入温层。
这也解释了为什么多份源码副本会带来新的问题。表面上,多留一份像是更安全;实际上,工作副本一旦有 2 份,更新就可能走向不同路径。今天更新了本地这一份,明天某个入口却指向外部那一份,版本差异就会变成隐蔽故障。运行时源码应该保持唯一工作副本,备份只承担恢复职责,不要同时承担工作职责。
如果不区分这两类数据,治理就会出现两种相反的错误:该留热层的源码被搬走,技能在调用时失效;不需要参与运行的过程数据一直留在热层,系统却没有获得任何真正的可用性提升。空间整理变成了路径搬运,版本更新也变成了猜测。
错误捷径:先搬最大的目录
最容易误判的错误捷径,就是把占空间最大的目录一律休眠或搬走,再用“文件还在”证明这次操作没有风险。这个办法之所以诱人,是因为它很容易执行,也很容易统计结果:搬了多少空间,目录就少了多少。
但它没有回答最重要的问题:这批数据到底是不是工作流的热路径。一个大目录里可能同时有说明书、运行源码、模型、报告和缓存。把它作为一个整体搬迁,相当于用同一个规则处理了完全不同的东西。最后得到的也许是一张漂亮的容量报表,却没有得到一个更可靠的技能系统。
我没有继续沿着这个捷径做,是因为前面已经出现了明确的代价:有一批被技能引用的仓库被搬走后,本地只剩下空壳,技能并不是在更新时才出问题,而是一运行就可能找不到真正的源码。这个细节把风险从“未来可能不方便”变成了“当前任务会失败”。
更稳妥的做法,是先问 3 个问题:说明书是否需要自动加载,源码是否会在运行时被执行,数据是否只是过程产物。只有把这 3 个问题分开,才知道哪些内容应该保持热,哪些内容可以放温,哪些内容需要保留回退副本但不能当工作副本使用。
从按体积清理改成按使用治理
这次调整的核心,不是取消治理,而是把治理依据换掉。第一步,我会看真实调用频率,而不是只看目录大小。近两周被频繁调用的能力,即使体积很大,也不能因为“看起来占地方”就休眠;长期没有调用的能力,才进入候选区。
第二步,我会确认运行时依赖。技能的说明书属于自动路由入口,被技能包装且开工要执行的源码属于热数据,必须和工作副本保持在稳定、可直接访问的位置。纯资料和过程数据则可以进入温层,但每次只处理一类,不能一次把整个目录打包搬走。
第三步,我会在迁移前留下可回退副本,并在迁移后运行正门自检。自检要验证实际行为:说明书能否被索引,技能能否被路由,脚本能否找到源码,运行时依赖是否仍然可用。目录状态只能作为辅助证据,不能作为最终验收。
这套流程看起来比“先搬最大的”慢一点,但它把风险放在了动作之前。一个目录一次只动一类数据,出了问题就能知道是哪一层导致的;保留回退副本,能够让错误治理及时撤回;自检直接针对行为,能够避免把“路径存在”误认成“功能正常”。
技能分层的正确顺序,是先看使用频率,再看运行时依赖,最后才看体积和存储成本。
这个判断也能用于其他资源治理。缓存、素材、构建产物和参考资料都可以讨论冷热,但不能因为它们大,就默认它们不重要。真正值得优先保留的,是那些一旦缺失就会阻断当前工作的依赖;真正适合搬走的,是那些不参与当场决策和执行的内容。
让验收回到真实工作
治理完成后,不能只统计“叫醒了多少个技能”或者“腾出了多少空间”。这类数字可以说明动作发生了,却不能说明系统恢复了。最终我更关心的是:随机选一个常用任务,不点名内部技能,看看自动路由是否能找到它;再选一个需要运行源码的任务,确认它能从唯一工作副本启动;最后检查索引是否已经同步。
这一步尤其重要,因为高频技能被叫醒以后,说明书可能已经重新可见,但索引未必及时更新;源码放回本地以后,路径可能恢复,但旧的快捷方式或重复副本仍然会让更新路径分裂。每个环节都要用行为验证,而不是凭目录状态推断完成。
我还会保留一次治理记录:为什么这个技能保持热层,为什么那份数据可以进入温层,迁移发生在什么时候,回退位置在哪里。记录不需要写成复杂报告,但必须能让下一次执行的人理解当时的判断。否则,过几周大家只看到一个“已经整理过”的状态,又会从体积开始重新猜。
表面上我是在整理目录,其实我是在整理系统对“重要”的定义。重要不是体积大,也不是最近改过,更不是看起来最复杂。重要是它是否在真实工作中被依赖,以及它缺席时会不会让任务停下来。
下次我会怎么做
下次再做技能治理,我会先拉取真实调用记录,标出高频、低频和长期未使用的能力;再逐项检查说明书、运行时源码和过程数据的关系。没有完成这两层判断之前,我不会因为某个目录很大,就把它列入休眠名单。
对高频技能,我会先确认自动发现和索引状态,再确认运行时依赖是否处在热层。对低频技能,我会区分“低频但关键”和“低频且可替代”,只有后者才适合进入休眠候选。对源码,我会坚持唯一工作副本;对备份,只保留能够恢复的副本,不让它悄悄变成第 2 个运行入口。
每次迁移,我只处理一种数据,并先保留回退路径。迁移后,我会用真实调用做一次自检:不点名时能否被找到,点名后能否正常运行,依赖缺失时是否能快速定位。若自检失败,就回退这一类数据,而不是继续扩大搬迁范围。
一个流程成熟的标志,不是它能把目录整理得多整齐,而是下一次面对相似问题时,不需要重新猜什么该留、什么该走。这件事让我重新确认,治理的价值不在于制造一个更轻的目录,而在于让真正重要的能力始终在需要它的地方,并且让每一次取舍都有证据可查。