
前阵子接手了一个内部技能后台的维护模块重构项目代号叫 Hermes Curator。这个模块的核心职责很简单管理一个平台里所有可用的“技能”——包括技能定义、参数说明、触发条件、版本历史和调用频次。但实际做起来问题比想象中多技能数量从几十个涨到几百个再涨到上千个后台开始变得混乱不堪——老版本没人清理历史配置散落各处新来的实习生不知道某个技能是干什么的全靠翻聊天记录。后期接手的人一多维护成本直接爆炸。所以当时定了一个明确的目标让后台具备“自我消化”能力。于是就有了 Hermes Curator 的核心设计——双相位机制第一相位负责自动归档把不再活跃、处于边缘状态的技能按规则打包、降级、存好第二相位负责 LLM 整合用大语言模型对归档内容做语义重写、摘要提炼和检索注入让旧技能不是死在仓库里而是变成随时可以唤醒的知识资产。这篇文章我打算把整个设计思路、实现细节、踩过的坑全部梳理一遍。无论你是在做知识库管理系统、自动化运维后台还是自研了 Agent 技能市场这套“自动归档 LLM 整合”的双相位框架都有参考价值。尤其适合那些技能或配置数量已经多到手忙脚乱的团队。1. Hermes Curator 的双相位设计到底在解决什么问题1.1 技能后台维护的典型痛点先说一个现象大多数技能后台在一开始的形态都是“文件夹 文档 负责人”。每个人负责一块往目录里扔文件、写说明到了月底把更新记录统一合一遍。听起来挺规整但一旦技能数量上来问题就出现了。第一是遗忘成本。三个月前开发的某个内部工具没人维护但也没有删。它仍然挂在后台的技能列表里每次检索都会被命中可它依赖的接口早就变了调用它大概率报错。不处理吧占着资源处理吧又怕历史上有业务在依赖。第二是检索噪音。技能越多语义冲突越严重。比如“用户画像生成”这个技能可能不同版本存在三个地方新人在后台搜索时根本不知道该用哪个最后只能挨个打开看元信息。第三是知识断层。很多技能是某个同事离职前写的注释稀碎文档缺失只有一段代码能跑。后来的人想维护只能人肉逆向原逻辑效率极低。Hermes Curator 最初就是为了解决这三层问题才立项的。不是单纯做一个清理工具而是要把“后台维护”从“人肉管理”变成“半自动化循环”。1.2 为什么拆成“自动归档”和“LLM 整合”两个相位拆成两个相位的原因比较实际归档解决的是“东西太多、太乱”的问题LLM 整合解决的是“东西存了但用不上”的问题。如果只做自动归档结果大概率是建了一堆压缩包存储倒是干净了可历史技能的信息密度仍然为零。后续检索时没人知道这些归档包里到底有什么。如果只做 LLM 整合不先做归档那 LLM 就要面对几千个七零八落的技能碎片。上下文窗口容不下检索结果互相打架生成的摘要质量也很不稳定。所以设计成线性协作先归档把技能从“运行态”切换到“归档态”形成标准化的、压缩过的、去掉了临时信息的技术单元然后 LLM 再对这些归档单元做二次加工生成摘要、标记依赖关系、构建语义索引。这样 LLM 处理的数据面是清洗过的效果才能稳定。1.3 核心收益与适用边界这套机制跑通之后最直观的收益有三个后台列表数量从“几千条”降到了“几百条活跃项 一批按时间分层归档的资产”新人接手技能时可以直接通过检索归档库得到带上下文的摘要而不是面对一段裸代码历史技能的恢复时间从“小时级”缩到了“分钟级”因为归档时已经保留了可校验的快照和依赖清单。不过也要说清楚边界。这个设计适合“技能数量大、更新频率不均、存在明显冷热分层”的场景。如果你的后台只有几十个技能而且每一个都被长期重度调用那直接引入归档机制反而是过度设计不如把精力花在完善 CI/CD 和接口文档上。2. 相位一自动归档机制的工程实现细节2.1 归档触发条件与技能生命周期定义自动归档的第一步是给技能定义一套生命周期状态。我这边用的是四态模型活跃Active、待观察Watch、可归档Archivable、已归档Archived。活跃最近 30 天内有调用记录且配置无重大变更。待观察连续 30 天调用次数低于设定的阈值或者出现连续依赖错误。可归档待观察状态持续 60 天后仍无业务方认领维护责任。已归档经过确认或自动策略判定后执行归档操作数据从活跃存储迁到归档存储。归档触发条件并不只是“调用次数低”。我还加了三个信号作为参考依赖接口的失败率、负责人活跃状态、配置文件最近更新时间。只有四个信号综合判断后才会进入待观察或可归档状态。具体实现时用的是后台定时任务每天凌晨跑一次状态扫描。扫描时会生成一份技能状态报告记录每个技能当前的状态、阈值命中原因、下一次评估时间。报告会推送给负责人但不会在第一次就自动归档而是给一个 7 天的申诉窗口。这个缓冲期很重要后面讲常见问题时我会单独提。2.2 归档策略冷热分级与时效窗口归档策略的经典思路是冷热分层核心不是“哪些技能垃圾”而是“哪些技能在当前时间窗口内不再活跃”。我给每个技能计算一个活跃度分数活跃度 最近30天调用次数 * 0.5 依赖稳定性系数 * 0.3 配置变更频率系数 * 0.2。分数低于设定阈值的自动进入待观察名单。分数高于阈值但依赖失败率达到高位的也会被强制标记为风险技能进入待观察名单。这里的实效窗口需要谨慎。我试过太短的观察周期比如 15 天就直接归档结果某个季度性任务调用的技能被误伤差点影响线上业务。后来放宽到“待观察 60 天后才可归档”并且增加了业务方可申诉的缓冲期才把误杀率降下来。归档时还需要决定压缩级别。技能通常由配置、代码片段、测试用例、文档四部分组成。其中测试用例和文档往往是体积大头。我做了一个细化归档时把测试用例压缩成“可执行清单 关键输入输出对”文档则只保留最近三次修订版本的完整内容更早的历史版本只保留差异记录配合对象存储的版本管理机制。2.3 归档文件结构与恢复流程归档不是简单打一个 zip 包它需要可恢复、可审计。我在设计归档结构时参考了备份系统的思路保留了三层信息元数据层技能 ID、名称、状态、归档时间、归档原因、原负责人、最后活跃时间。内容层技能定义、配置、入参出参 schema、依赖接口清单、相关文档摘要。校验层配置文件的哈希值、核心文件的完整性校验和、必要的恢复脚本。恢复流程也分两种。一种是整体恢复把归档包解压回活跃存储重新注册到技能列表并标记为“已恢复-待观察”。第二种是部分恢复只从归档包中抽取某个配置片段或历史版本来参考不影响当前线上技能。2.4 关键参数与配置示例这里给一份参考配置是我在 Hermes Curator 中实际使用的参数值不保证通用但可以作为起步值{ lifecycle: { watch_days: 30, archivable_days: 60, appeal_window_days: 7 }, activity_scoring: { call_weight: 0.5, dependency_weight: 0.3, config_change_weight: 0.2, call_threshold: 50 }, archive_storage: { compression: zstd, retention_versions: 3, checksum_algo: sha256 } }注意几个细节压缩算法我选了 zstd因为它在压缩率和解压速度之间平衡比较好。保留版本数设为 3不是拍脑袋而是经过几个月的归档记录观察超过 3 个旧版本几乎不会再被读取继续保留只会增大存储成本。3. 相位二LLM 整合与归档技能的语义化再利用3.1 从归档到知识库的转化路径自动归档解决了存储混乱但归档包仍然是“死数据”。要让这些数据重新产生价值就需要 LLM 整合阶段介入。思路是把每个归档包当作一个文档单元交给 LLM 做结构化的“二次提炼”再写入单独的知识库索引中。具体路径分为三步第一步读取归档包的内容层还原技能核心逻辑第二步让 LLM 基于模板生成结构化摘要包括技能用途、适用场景、调用方式、依赖信息和潜在风险第三步把摘要和原文同时写入向量数据库建立语义索引。这里面比较关键的是不要让 LLM 直接生成归档包的“替代文档”而是生成“导读文档”。替代文档会导致原始信息被压缩掉导读文档则只是告诉后来者“这东西是干什么的、要看什么”需要细节时仍可以打开原始归档包。3.2 LLM 在技能维护中的三个实际角色我在项目里把 LLM 定位成三个角色不是让它做全能的自动机器人。第一个角色是“摘要生成器”。负责把归档包里的技术细节转换成适合人读的摘要并标注出技能的前置条件和副作用。第二个角色是“语义检索器”。通过向量化把用户提问和技能摘要做相似度匹配替代传统的关键词搜索解决同义表达问题。第三个角色是“版本对比助手”。当同一个技能有多个归档版本时让 LLM 对比两份内容提取差异点梳理演进路线。这样的分工让 LLM 的上下文场景很清晰输出质量也更稳定。比如某个技能被归档后又想恢复后台管理者可以直接问类似“某某脚本功能”的技能有哪些系统通过向量检索给出候选列表和摘要管理者一眼就能判断是否需要恢复。3.3 检索增强生成RAG与技能调用挂钩技能管理的终极需求不只是让人能查到还要让 Agent 能够调用到正确的技能。所以 LLM 整合这一步我直接和 Agent 的技能调用流程做了联动。当 Agent 在处理用户问题时先通过意图识别锁定一个或多个候选技能。这时候选技能列表不只来自活跃技能还会带上“靠前的归档技能”。如果活跃技能无法满足系统会通过 RAG 把归档技能摘要作为上下文注入给大模型由模型判断是否可以临时使用归档技能来解决任务。3.4 Prompt 模板与上下文注入的坑这里有个非常容易出问题的地方直接注入完整归档包会导致上下文爆炸而只注入摘要又容易丢失关键参数。我在实践后采用的折中方案是默认注入“摘要 关键入参出参 schema 依赖约束”完整代码和详细文档不进入 Prompt而是在需要时通过工具动态读取。另外Prompt 模板必须固定。不固定模板的后果是同一个技能在不同时间点的摘要风格差异很大检索时语义质量也忽高忽低。我后来用一套统一的 JSON 输出格式要求 LLM 按固定字段返回skill_id、summary、scenarios、dependencies、risks、last_active。然后程序再解析 JSON 入库而不是直接存储大段自然语言文本。4. 完整流程与关键实现细节4.1 双相位协作的事件驱动链路双相位设计在实际工程里是通过事件驱动串起来的。归档系统每完成一个归档包就会往消息队列里发一条archive.completed事件。LLM 整合模块监听这个事件再异步创建整合任务。这样设计的好处是解耦。归档算法和 LLM 调用频率不互相影响。即便某段时间大模型服务不稳定归档也不会停等模型恢复后整合任务会重新消费积压消息慢慢追上进度。事件载荷本身也需要设计好。我传的是归档包的元数据摘要包含存储路径、技能 ID、归档原因、时间戳不会把整个归档包塞进消息体里。因为过度臃肿的上抛会影响消息队列性能同时也存在隐私风险。4.2 核心模块伪代码示例这里给一段简化的伪代码展示双相位协作的核心逻辑# Phase 1: Auto Archive def scan_and_archive(threshold): for skill in active_skills(): score compute_activity_score(skill) if score threshold: mark_watch(skill) if skill.watch_days 60 and not skill.has_appeal(): archive_id create_archive(skill) event_bus.publish({ event_type: archive.completed, archive_id: archive_id, skill_id: skill.id, storage_path: archive_storage_path(skill) }) # Phase 2: LLM Integration def on_archive_completed(payload): archive load_archive(payload[storage_path]) summary llm_extract(archive.content) vector_embedding embed(summary archive.schema) knowledge_store.save( skill_idpayload[skill_id], summarysummary, embeddingvector_embedding, archive_refpayload[storage_path] )实际代码会比这个复杂但核心事件流的骨架就是这样。重点在于把归档逻辑和 LLM 处理逻辑隔离在两个独立的 worker 池中避免互相阻塞。4.3 数据一致性与幂等设计双相位设计会引入一个新的风险归档事件可能重复投递LLM 整合任务也可能重复执行。所以幂等是必须的。我为每个归档包生成唯一的archive_idLLM 整合任务执行前先查知识库中是否已存在相同 ID 的记录。如果存在就直接跳过。这里用的是数据库唯一索引 插入冲突更新策略比“先查再插”要更可靠因为并发下会出现多个任务同时读到“不存在”的情况。另外归档包本身也带哈希校验。每次恢复或读取时都要重新校验内容完整度。否则会出现一种很尴尬的情况归档包在对象存储里损坏了但元数据库里还标记着“可恢复”。4.4 性能考量与异步化一开始我尝试在同一个进程里串行执行归档和 LLM 整合结果处理一个技能要等十几秒大批量扫表时直接拖垮了后台服务。后来改成异步任务队列后情况好了很多。归档过程主要是 IO 密集批量压缩和上传对象存储占主要时间LLM 整合是网络延迟和推理延迟为主。两条链路完全隔离后各自扩容互不影响。但异步化也带来了观测难度。为此我加了一个任务状态追踪表记录每个archive_id的事件触发时间、整合开始时间、结束时间和失败原因。这样能快速定位是归档阶段等待过长还是 LLM 调用超时。还要特别注意限流。LLM 接口不像内部数据库它有严格的 QPS 限制。我在整合模块里加了一个令牌桶限流器默认每秒最多处理 2 个技能批量补齐旧归档时不会压垮外部接口。5. 常见问题与排查技巧实录5.1 归档夹生部分技能无法恢复踩过最典型的一个坑某技能归档后里面所有配置文件的哈希值都校验通过但恢复后就是无法运行。排查了很久才发现技能依赖的某个内部 SDK 版本已经下线而归档时只记录了“依赖包名称”没有冻结“版本号”。后来我在归档策略里加了一条归档前必须导出依赖锁定文件保证技能恢复到任何时间点都可以准确还原运行环境。从这以后恢复失败率直接下降了不少。5.2 LLM 生成摘要信息丢失摘要生成的另一个坑是模型偶尔会把最关键的参数说明漏掉。比如某个函数技能摘要里讲清楚了用途却漏了“必须在深夜执行”这种重要约束导致后续被 Agent 在白天误调用。解决办法是两层第一在 Prompt 模板里增加一条硬性要求强制输出constraints字段并把它放在所有描述字段的最前面第二在整合管线中增加一个自动校验节点如果 LLM 生成的 JSON 缺少必填字段就重试一次重试多次仍失败则转入人工审核队列不直接入库。5.3 双相位事件重复消费导致索引错乱消息队列的“至少一次投递”特性导致归档完成后 LLM 整合任务经常被重复消费。如果没做幂等校验知识库里会出现同一个技能的多条摘要记录检索时资源会跳来跳去。排查思路是先看知识库中是否有多条相同skill_id的记录再看任务执行日志里是否出现同archive_id的重复处理记录。修复方式就在三小节讲的唯一索引和幂等逻辑这个一定要在初始设计时就做后补会费很大劲。5.4 避坑清单整理我把这段时间踩过的坑整理成了一张速查表给后续接手的人参考问题坑点解决建议归档后技能不可恢复只记录依赖名不记录版本归档前生成依赖锁定文件LLM 摘要缺关键约束Prompt 模板太随意强制 JSON 输出必填字段缺失重试事件重复消费未做幂等处理数据库唯一索引 冲突更新归档误杀活跃技能观察期太短设置 7 天以上申诉缓冲期LLM 接口被压垮任务批量执行无节流令牌桶限流 异步队列知识库语义检索不准原始技能描述风格不一统一摘要模板固定术语库这套机制上线跑了三个多月最大的感受是自动归档和 LLM 整合并不是一劳永逸的银弹它们只是把“维护成本”从无规则的人肉操作变成了可预测、可追踪、可反馈的半自动流程。真正决定这套系统有没有价值的地方在于你是否愿意投入时间去调阈值、改模板、补校验。技术实现可以抄但冷静分层的运维逻辑是抄不来的。最后再分享一个小技巧归档和 LLM 整合两个模块的日志格式从一开始就约定好统一的结构化 JSON方便后续接入各类日志分析工具。你永远无法预测哪一条日志会在事故复盘时救你一命。