
1. Hermes Curator 到底在维护什么做技能管理和知识库后台维护的团队基本都逃不过一个尴尬期内容越攒越多有价值的东西却越来越难找。我接触过不少类似系统有的是客服团队的排障手册库有的是研发部门的脚本工具集有的干脆就是个巨型 Wiki。前半年大家都很有热情每个月都在沉淀新内容到了一年之后库里堆了几千条“技能卡片”真正被反复使用的可能不到三成剩下的都是历史遗留、过期步骤、或者写着“待补充”就再也没人碰过的半成品。Hermes Curator 就是在这种背景下出现的一套后台维护机制。它解决的并不是“如何写技能”而是“如何让技能库保持健康”这件事。名字里带 Hermes我当时取名的思路也挺直白Hermes 在神话里是传递信息的信使而 Curator 是策展人 / 管理员合在一起就是“给技能库担任策展和管理工作的信使”。这个定位一开始就定死了它不是创作工具而是维护工具负责把技能库里的内容持续地归档、重组、再激活。整个设计最核心的部分就是标题里写的“自动归档 LLM 整合”双相位设计。我把它拆成两个互相独立、又由后台任务串联起来的环节第一相位用规则引擎自动识别冷门、过期、失效的技能把它们从活跃区挪进归档区第二相位再让大语言模型对归档区和活跃区的内容做整合提炼摘要、生成标签、发现重复和关联最后把整合结果推给人工进行确认。之所以叫“双相位”是因为这两个动作在时间上是错开的在职责上也是分开的。归档靠的是确定性的业务规则LLM 整合靠的是概率性生成模型。把两者硬捏成一个流程容易出问题归档需要快速、稳定、可追溯LLM 适合做理解、归纳、联想不适合做需要严格一致性的决策。所以干脆让它们分阶段跑用状态机衔接。这篇文章我会把双相位的设计思路、表结构、判定逻辑、提示词与参数调整、调度与重试机制以及我们实际上线过程中踩过的坑都摊开讲。如果你也在维护一个类似的技能库、知识库或文档系统希望这篇能给你一个可以直接参考的落地框架。2. 自动归档相位让技能库自己保持瘦身2.1 技能卡片的生命周期定义归档动作的前提是给每条技能内容定义清晰的生命周期。我们用的模型比较简单一条技能记录一共有五个状态draft、active、deprecated、archived、merged。draft刚录入还没经过校验和审核。active正式生效可以被检索、引用、被其他模块调用。deprecated被标记为不再推荐使用但内容还留在原处供参考。archived已经移入归档区不参与常规搜索和推荐。merged被合并到另一条技能中原记录保留指针和合并记录。这个状态机看起来简单但真正重要的是状态之间的跃迁条件。我们内部对“进入归档”做了严格约束active不能直接跳到archived必须先进入deprecated状态观察一段时间。这是为了防止误判。比如某个技能只是暂时没人用实际上下个月有大型活动要用到直接归档会造成业务空窗。一条技能进入deprecated需要同时满足三个条件在过去 90 天内没有被任何用户主动使用过这里的“使用”包括查看详情、复制命令、调用脚本接口。没有被其他技能卡片引用或者引用它的技能也全部进入了冷门状态。没有未完成的人工反馈标记如果最近有人给它打了“内容过时”标签则强制跳过自动归档流程。这些条件全部写入规则引擎每天凌晨定时扫描一次。设计上刻意不去用复杂的机器学习模型来判定是否归档原因很简单归档牵涉业务可用性宁可规则死板一点也要每个判定都能解释清楚、能回溯。等规则跑熟了再考虑引入更智能的权重也不迟。2.2 多因子打分模型与阈值选择说实话“90 天没使用”这种单一规则还是太粗暴。早期版本我们就是这么做的结果误伤了一批低频但关键的运维脚本比如每年年底才跑一次的数据清理工具。后来我把判定逻辑改成了多因子打分模型。打分模型允许不同维度有不同的权重然后计算一个综合的“活跃度衰减指数”。计算公式大致如下score w1 * days_since_last_use / 365 w2 * use_count_90d / max_use_count_90d w3 * reference_count / max_reference_count w4 * age_days / 730这里days_since_last_use是距离最后一次被使用过去的天数use_count_90d是近 90 天使用次数reference_count是被其他技能引用的次数age_days是技能创建后的存活天数。权重w1到w4在我们的配置里分别是 0.45、0.25、0.2、0.1。注意这里为了让“未使用”这一项成为主导因子w1权重放得最高。阈值方面我们试过好几个值最终定为score 0.65就进入归档候选队列。这个值不是拍脑袋定的而是用历史数据做了回测拉出过去一年里已经被人工手动归档的大约 300 条技能记录用这个公式回算如果阈值设在 0.65能覆盖其中 82% 的记录如果把阈值降到 0.5覆盖率会提升到 91%但误伤率也上升了会有不少仍然被高频使用的技能因为“创建时间足够长”这种次要因素被高亮出来。误伤率从 6% 涨到了 15%对后台维护系统来说这是不可接受的。所以最终我保留了一个原则自动归档给出的都是候选列表而不是最终决定。系统每天生成候选后要经过一个轻量级的人工确认流程或者至少要有负责人收到通知可以一键放行或驳回。2.3 归档流程与人工确认闭环具体到执行链路自动归档相位分四步走凌晨扫描定时任务读取技能表结合使用日志表和引用关系表计算每一条active状态的技能分数。候选生成分数超过阈值的记录写入archive_candidates表状态保持active不变只做标记。通知确认给技能维护负责人推送待处理列表后台管理界面出现“待归档”红点。列表里展示分数构成明细比如“距离上次使用 320 天近 90 天使用次数 1 次被引用 3 次”方便负责人快速判断。执行归档负责人点击确认后技能状态先改为deprecated再进入一个 7 天的缓冲观察期。7 天内如果没有任何用户发起访问或引用7 天后自动进入archived。如果有访问则自动回到active并清空候选标记。第四步这个“缓冲观察期”很关键。一开始我们做成点击确认后立即归档结果有两次团队成员上午确认归档下午就有人反馈在紧急处理问题时找不到这个技能了。因为负责人手快但业务侧还没收到通知。加了 7 天缓冲期之后这个问题基本绝迹。归档本身并不是删除。归档后的记录仍然保留在原来的skills表里只是status字段变成了archived同时写入一条归档时间、归档原因。所有内容完整的保留这样即使将来某个技能要复活也可以无损恢复。2.4 归档区不隔离但要降权很多类似的系统会把归档数据直接挪进冷表或者备份库这种做法不适用于 Hermes Curator。因为后续的 LLM 整合相位需要同时扫描活跃区和归档区将归档数据和活跃数据做关联对比。如果物理隔离整合相位就要频繁跨库读取性能和复杂度都会增加。我们的方案是逻辑归档统一使用status字段标记不建新表。所有常规查询默认过滤掉archived状态但在管理后台和 LLM 整合服务中可以指定include_archivedtrue来读取全量数据。这种逻辑隔离的方式让代码实现简单了很多也是后面双相位能顺畅串联的基础。要说有什么代价就是表数据量会一直增长。我们通过分区表按月拆分通常几个月才需要手动归档一次物理旧数据运维成本可以接受。而好处是每一次格式变更、字段调整都不用关心历史数据是否需要迁移反正它们还在老地方。3. LLM 整合相位从归档池里打捞可用信息3.1 LLM 在这个系统里的角色定位很多人一听“LLM 整合”下意识觉得是用模型自动生成技能文档。其实在我的设计里LLM 更多承担的是“理解、归类、链接”的工作而不是创作。它要做三件事从杂乱内容中提取结构化信息、发现技能之间的语义关联、以及用自然语言生成人类可读的整合摘要。为什么必须用 LLM因为传统的关键字匹配和正则表达式搞不定同义表达。比如一条技能叫“处理密码过期导致服务无法登录”另一条叫“账号登录失败排查”在字面上完全不重叠但语义上是强相关的。传统方法会把它们当成两个独立主题而 LLM 能把它们串起来提示维护人员这两条技能可以考虑合并或互相引用。不过这里要控制预期。LLM 不是规则引擎的替代品而是补充。归档相位解决“哪些内容该退居二线”LLM 整合相位解决“退居二线的内容里哪些价值还能被重新提炼出来哪些可以相互合并哪些真的可以废弃”。3.2 整合流水线的四个环节整个 LLM 整合流程我分成了四个环节每个环节产出的数据结构都不一样第一环节是标准化清洗。把归档技能的内容去除格式噪音比如删除多余换行、统一列表样式、剥离无效链接然后把纯文本交给模型。为什么要先清洗因为不干净的输入会导致模型输出不稳定。实测中发现内容里带有大量 HTML 标签和代码高亮标记时模型会把标签也当成语义内容来处理摘要质量直线下降。第二环节是结构化信息抽取。让模型从技能卡片中抽取标题核心词、适用场景、前置条件、操作步骤要点、注意事项这五个字段。这里不需要模型生成新内容而是从原文中摘取并重组。这个环节我用的是JSON mode输出格式要求模型严格返回 JSON方便后续直接落库。第三环节是语义相似度计算。将清洗后的内容文本做向量化然后与活跃区技能逐一计算相似度。相似度高于 0.82 的两条技能会被标记为“疑似重复”推送给维护人员决定是否需要合并。别小看这一步它是 LLM 相位里最有价值的部分直接能帮我们清理掉历史遗留的大量重复内容。第四环节是整合建议生成。把高相似度的组合包装成一条建议包括两条技能的标题对比、向量相似度分数、各自摘要、合并后的推荐标题和正文骨架。这个推荐并不是最终产物只是给人工审批者的参考草稿。审批者可以一键采纳、修改后再采纳或者直接忽略。3.3 提示词模板与关键调参提示词的设计直接决定整合效果。我最早犯的错是把所有要求全塞进一段话里让模型同时做摘要、分类、抽取、比较结果输出经常顾此失彼。后来拆成了四条独立任务每条任务只做一件事稳定性明显提升。举个例子摘要任务的提示词大致长这样你是一位技能库维护助手。给定一段技能文档内容请输出一段中文摘要。 要求 1. 摘要不超过100字。 2. 必须保留“解决什么问题”和“适用场景”两个要素。 3. 不要添加原文中不存在的信息。 4. 只输出摘要正文不要任何解释。分类任务的提示词则完全不同要求输出 JSON给定以下技能文档分析它属于哪一个预设分类。 分类候选故障排查、环境部署、脚本运维、安全加固、最佳实践。 输出格式{category: xxx, reason: 简要说明判断理由} 如果都不匹配输出 other。关于温度参数我在摘要和抽取类任务里固定设置为 0.1因为这类任务希望模型尽量服从原文不要发挥想象。在合并建议标题生成的环节才把温度调高到 0.4稍微给模型一点灵活空间否则生成的标题会和原标题几乎一样起不到整合优化的效果。上下文长度也要控制。试过直接给整篇技能文档几千字的内容全塞进去输出确实精细但成本和延迟都翻倍。后来做了截断策略只取正文前 1500 字加上从结构化字段中抽出的“核心场景”和“操作步骤”两项摘要效果几乎没有下降。因为技能文档最重要的信息通常集中在前半部分尾部往往是附录和参考链接。3.4 置信度与人工兜底模型输出的东西不能直接生效这条铁律写死在架构里。LLM 整合相位跑完的结果全部写入integration_suggestions表状态是pending_review。只有经过人工确认后才会真正应用到技能表。为了减少人工工作量我给每条建议增加了一个置信度字段confidence由三部分组成语义相似度分数、结构化抽取完整度、模型自评概率。如其相似度 0.9、抽取完整度 0.8、自评概率 0.95加权平均后置信度大概是 0.88。置信度高于 0.9 的建议后台显示为“高置信建议”可以批量勾选确认低于 0.7 的建议不主动推送只归档记录备查。说句实在话这里的置信度只是一个辅助排序指标参考价值大于绝对意义。模型自评概率尤其不能全信它对不确定答案也会给出比较高的概率。所以我们从不依赖单一信号也没有让建议自动生效。哪怕置信度到了 0.98只要涉及将两条技能合并就必须经过人工按钮确认。这是底线。4. 双相位如何串联调度、状态机与数据流4.1 整体数据流与状态转换自动归档相位和 LLM 整合相位不是并行两个功能而是一条流水线的上下游。数据流的整体顺序是技能内容 → 归档判定 → 归档候选 → 人工确认 → 归档完成 → LLM 批量扫描 → 整合建议 → 人工审批 → 更新技能状态。这条链路中归档相位产生的archived状态数据会成为 LLM 整合相位的重要输入之一。我去掉了直接把所有技能一股脑塞给 LLM 的做法因为全库扫描的成本高、噪音大。合理的做法是只扫描两类数据一类是新进入归档状态不足 30 天的技能另一类是活跃区内高频使用但内容长度较长、疑似涵盖多个主题的技能。归档和整合之间的状态衔接我用了简单的状态机来控制。技能记录上面的状态是技能本身的另外还有一张phase_status表记录每个批次任务的执行进度。每一条技能都有四个相位标志位scan_flag、embed_flag、extract_flag、suggestion_flag。只有当上一个标志位置为完成下一个环节才会被触发。这样设计的好处是便于发生异常时定位。如果某条技能在归档后没有生成任何整合建议排查问题时就去看embed_flag是否为success而不用从业务角度猜测是模型输出问题还是调度问题。4.2 任务编排与失败重试任务编排用的是定时任务 消息队列的组合。每天凌晨 2 点归档相位开跑归档相位如果生成候选会在早上 8 点推送给维护人员人工确认完成后系统生成一批新的整合任务投递到后台队列中。队列消费端的逻辑要注意防重。LLM 调用是外部接口可能超时但超时不代表服务端没有处理。如果重试时不加幂等就会造成同一条技能被重复写入两次抽取结果。我们的办法是给每条技能加入process_id字段每次整合任务生成时创建一个新的process_id结果写入时检查process_id是否已经存在存在则跳过。批处理大小也值得讲一下。我们最开始把 500 条技能作为一批一次性交给模型去处理结果经常触发服务端超时和限流。后来改成每批 50 条每条技能独自调用一次接口并发数控制在 5 左右。处理速度虽然慢一点但稳定性好得多基本没有因为超时导致整个队列卡死的情况。4.3 表结构设计的取舍这里放出最核心的几张表设计方便你直接参考。第一张是技能主表skills核心字段包括id, title, content, category, status, created_at, last_used_at, use_count_90d, reference_count, archived_reason第二张是归档候选表archive_candidatesid, skill_id, score, factor_detail, status, confirmed_by, confirmed_at第三张是整合建议表integration_suggestionsid, skill_id_a, skill_id_b, similarity_score, summary_a, summary_b, suggested_title, suggested_content, confidence, status, reviewed_by, reviewed_at这三张表之间的关系也很简单archive_candidates.feature_detail用 JSON 字段记录每一项打分因子的原始值integration_suggestions在skill_id_b为空时表示这是一条单独的内容规范化建议而不是合并建议。表结构设计上最重要的经验是不要在核心表里堆过多的状态字段把“业务状态”和“工作流状态”分开存放。技能本身的status只反映业务生命周期各个维护任务的状态全部放到独立的队列表和候选表里。这样即使任务调度出问题也不会污染技能主表的数据。5. 上线过程中踩过的坑与排查实录5.1 归档误杀的三种情况与对应处理归档相位最怕的就是误杀。我们上线三个月后复盘发现误杀情况主要集中在三种场景第一种是低频季节性技能。每年只在大促期间使用的批量对账技能在平时扫描时分数会很高被误判为冷门。处理办法是在打分模型中增加“季节性保护标记”。如果一条技能被打上这个标记系统会按照过去 365 天的使用记录来计算而不是只取 90 天窗口。第二种是引用链断裂。有些技能本身长期无人使用但它引用的脚本或配置文件还在生产环境跑着。原技能在系统中没有被直接引用所以reference_count为 0导致误判。后来我们增加了反向链路校验归档前要检查技能内容中提到的脚本文件名、配置文件路径是否仍然在生产清单中。第三种是负责人早期批量操作失误。人工确认界面上一开始支持“全选归档”结果某天负责人在手机上误触了全选直接归档了 80 条技能。虽然由于有 7 天缓冲观察期没有造成数据删除但用户体验很糟糕。后来我们在管理端移除了“全选”按钮只保留按页批量选择并对一次性归档超过 10 条的操作强制二次弹窗确认。5.2 LLM 输出不稳定的排查记录LLM 整合相位上线后的头两周最常收到的反馈是“摘要内容不错但偶尔会跑题”和“JSON 解析报错导致任务重试”。JSON 解析问题的根源有两类。一类是模型输出里夹带了 Markdown 的代码围栏比如把 JSON 包在json里面前端解析时没有先剥离围栏就报错。解决方式是让提示词明确“只输出 JSON 内容不要使用代码块围栏”另外在解析前加一道预处理用正则把代码围栏去掉。另一类问题是字段缺失。模型偶尔会漏掉某个必填字段比如只返回{title: xxx}缺少summary。单纯的提示词约束无法 100% 解决这个问题必须在解析代码中做默认值兜底缺省字段填空字符串并降低该条建议的置信度。这样能保证流水线不断只是把可疑结果留给人去判断。还有一个非常值得记录的坑LLM 在摘要中“脑补”不存在的操作步骤。比如原文只写了“重启服务”模型在摘要里给补充成了“先停止服务再重启并确认日志”这个新增细节看起来合理但根本不保真。为了解决这个问题我在提示词中显式加入“只基于原文不要补充任何未见过的操作步骤”这一句话并且在置信度计算中如果摘要句与原文中任意一处文本完全不重合就要扣掉一部分可信度。5.3 性能瓶颈与索引优化从功能可用到性能稳定中间还隔着一堆索引优化。最开始的表结构很简单查询skills表时直接按status active过滤没有组合索引。当数据量到几万条之后归档候选扫描一次要跑好几分钟拖慢了整个定时任务。后来做的优化有两个。第一个是建立组合索引(status, last_used_at, use_count_90d)让扫描能快速定位“活跃状态下长期未使用的技能”。第二个是把使用日志从主表分离到独立的访问流水表按天分区扫评分模型时先做聚合再关联主表。这个优化效果很明显原来几分钟的扫描压到了一分钟以内。向量相似度计算也是另一个潜在瓶颈。我们用向量数据库存储技能内容的向量每次扫描一批归档技能就执行一次相似度搜索。最开始十几条一批还好但整批处理 200 条的时候接口耗时明显上升。后续优化方式是把相似度计算拆成多线程并发每条技能一个线程控制线程池数量在 8 个最终单个批次的处理时间从接近 10 分钟降到 2 分钟左右。6. 个人实操体会与后续扩展建议项目做到这个阶段如果把核心经验压缩成一句话那就是自动归档负责确定性的“减法”LLM 整合负责不确定性的“加法”两者必须各自闭环、通过人工审批完成最终决策才能既高效又安全。实际开发中最容易被低估的不是算法设计而是工程韧性。LLM 接口会超时、会返回异常 JSON、会在高峰期限流、会产生幻觉我们需要在系统架构里给所有这些问题留好容错位。幂等写入、批处理拆分、置信度评分、人工兜底确认这些看起来不那么“酷”的工程手段反而是整个系统能稳定运行的根本。关于扩展方向我觉得最值得尝试的是把整合建议的“采纳记录”作为反馈数据持续优化打分模型。每当一个人工审批者接受或拒绝了系统给出的建议我们可以把这次决策记录到日志中用于后续训练一个小型的排序模型或者至少用来调整置信度计算的权重。这样系统会随着使用变得越来越“懂”这个团队的习惯。另一个值得做的是把自动归档阈值做成动态配置不要写成死代码。不同团队的技能库规模、内容时效和活跃周期都不一样“90 天未使用”在某个场景下是冷门在另一个场景下可能完全正常。把阈值的定义下沉到配置中心让维护者可以在后台按分类调整会比硬编码灵活得多。最后想提醒一点这类系统的价值不是让维护者变得“无事可做”而是把维护者从机械劳动中解脱出来让他们把精力真正放在那些 LLM 拿不准、需要经验判断的地方。我在实际运营中看到的最理想状态是后台每天自动列出 5 条归档候选和 3 条整合建议维护人员每天只用花 15 分钟看一遍点几个确认按钮技能库就能长久保持干净和活跃。这比让一个人每周专门拨出半天来手动整理要高效得多。