SkillLite自进化实战:总空跑还刷屏,一次「调度 vs 提案」脱节的调优
一、业务场景:用户在经历什么、产品在赌什么
场景轮廓:产品里有一块 自进化 / 自动演化——根据聊天与工具调用沉淀下来的决策数据,周期性或按信号去 生成改进提案(prompt / 规则 / 技能等),再经策略与人工确认后落盘。SkillLite 里对应 Life Pulse、A9 增长调度、演化子进程 这一整条链路。
用户侧真实体验(任务卡原问题):
- 周期到了,系统认为「该跑一轮演化」,但 本轮其实没有任何可执行提案;
- 子进程照样起、日志里
evolution_run_outcome刷屏,面板也吵; - 用户问「为什么又跑」,运营只能答「定时到了」,对不上 cooldown、日 cap、模式开关等配置话术 → 自进化可信度被掏空。
产品在赌的:用户愿意打开「自动改进」,前提是 少打扰、可解释、别白烧钱(算力与磁盘)。若调度层只输出一个「该跑」,却说不清 是闹钟 tick 还是真有积压信号,体验就会像「随机骚扰」。
二、技术与价值:我们改了什么、换来了什么
2.1 技术要点(尽量短)
- 调度出口从单一
bool变为带语义的GrowthDueOutcome:due+periodic_only(本次触发能否 完全 用周期臂解释,没有信号臂、没有 sweep)。
锚点last_periodic_spawn_unix与判定 同一函数内完成,避免拆两个 helper 双写逻辑(任务CONTEXT已记录否决「单独would_periodic_fire」类方案)。
pub struct GrowthDueOutcome {
pub due: bool,
pub periodic_only: bool,
}
- 仅在
periodic_only时做「提案是否存在」预检:直接调用与正式路径 同源 的build_evolution_proposals判空,不在调度层手搓删节版 SQL。
pub fn would_have_evolution_proposals(
conn: &Connection,
mode: EvolutionMode,
force: bool,
) -> Result<bool> {
Ok(!build_evolution_proposals(conn, mode, force)?.is_empty())
}
- 桌面侧预检前合并环境:workspace
.env+ 聊天侧 override 与真实子进程 同一套闭包,再EvolutionMode::from_env();否则预检与执行 各说各话,典型 bug 是 静默该跑没跑 / 不该跑跑。
let _guard = EvolutionRunEnvGuard::push_from_merged(&merged_map);
return skilllite_evolution::would_have_evolution_proposals(
&conn,
skilllite_evolution::EvolutionMode::from_env(),
false,
)
-
信号 / sweep 触发路径不做事前提检:即使最终仍可能空提案,也保留 「有上下文的一次执行」 的诊断价值;只对 低信息密度的纯周期 砍空跑——这是 产品策略,不是实现偷懒。
-
预检 / 开库失败 fail-open(
unwrap_or(true)):宁可多跑一次,也不把周期演化 静默饿死;评审时要能答「为何不是 fail-close」。
2.2 价值对照(给产品 / 技术一起看的表)
| 痛点(业务语言) | 调优前 | 调优后 |
|---|---|---|
| 自进化「吵」 | 周期 tick 常带空跑,日志与面板噪声大 | 纯周期且无提案 时不再拉重路径,噪声显著收敛 |
| 自进化「不可解释」 | 空跑原因笼统,和配置项对不上 | 空提案原因可走结构化 NoScope(任务其余部分),与 调度是否该响 拆开谈 |
| 桌面与 Agent 行为漂移 | 同一策略,桌面多一层 env | 预检与 spawn 同闭包,减少「这边 skip 那边跑」的客诉 |
显式代价(别藏着):periodic_only 为真时会 多跑一轮完整提案构造;数据量上来后要么接受成本,要么抽 共享的轻量可行性阶段——那是下一阶段演进,不否定当前「同源换零分叉」的选择。
2.3 流程图:调度层 vs 工作层(提案)如何串起来
下面一张图对应第 2.1 节的实现顺序:先判定「是否 due、是否纯周期」,仅在 纯周期 分支上问 「本轮会不会有提案」;含信号或 sweep 时不做事前提检,直接进入演化路径。(CSDN 若不支持 Mermaid,可按图中节点顺序对照正文与代码。)
读图要点(和业务对齐):
- 左支「调度」:只回答「这一轮 tick 在规则上该不该响」,不回答「响完有没有活」。
- 中下「纯周期 + 预检」:把「有没有提案」拉回 与工作流同源 的判断,避免调度里再抄一套阈值。
- 右支「非纯周期」:宁可接受「执行后仍可能空提案」,也要保留 带业务上下文的一次运行(日志与排障)。
三、可沉淀的方案与思考(可复制到别的自进化 / 定时重任务)
3.1 合入 / 运营侧验收清单
任务侧已跑:cargo test -p skilllite-evolution -p skilllite-agent、Tauri cargo check(见 STATUS.md)。上线前建议再勾:
- 纯周期、提案必空:不重跑演化主路径;仅有 debug 级 skip,而非 outcome 洪水。
- 信号或 sweep 已触发:即使提案仍空,仍执行(保留审计与配置边界暴露)。
- 桌面只改 evolution 相关 UI 配置:预检结论应随之变化;不变则先查 env 合并顺序,而非先怀疑模型。
- 预检失败:行为为 fail-open 时,日志/监控能否区分「真无提案」与「预检未生效」。

3.2 方案沉淀(短文可直接贴 Wiki)
- 自进化调度至少要能区分「闹钟式触发」与「有业务信号触发」——一维
bool往往不够;否则调优只能 debounce,治标不治本。 - 「会不会有活」不要在工作流外再抄一套规则;预检与 建提案 / 建任务 同源,才能长期维护。
- 父进程预判、子进程执行 时,配置闭包必须一致;多入口(Agent / 桌面)要分别验证 env 来源是否等价。
- fail-open vs fail-close 要写进 ADR:后台类自动化默认偏向哪一端,团队要有共识。
- 与后续观测任务的关系:例如 learner 窗口、浅跳过日志(如
TASK-2026-032)解决的是 「跑了但学不到」 的可观测性;本文解决的是 「没活却老跑」——两层问题别混在一篇需求里谈。
3.3 不适用硬套的场景
强扣费 / 强配额(fail-open 可能超额)、预检成本远高于单次执行、触发源在业务上无法定义「纯周期」——需另建模,而不是再堆一个 bool。
github项目:https://github.com/EXboys/skilllite
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)