///|
/// evolve/curriculum.mbt —— 课程式自进化(L41 核心组件)
///
/// 无人值守自进化闭环的三个支柱:
/// 1. curriculum:按评分档位组织"由易到难"的学习路径(课程)
/// 2. mutation:对已有 artifact 生成文本变体(变异算子)
/// 3. skill_lib:技能库——把高分 artifact 的 note 蒸馏为可复用 skill
///
/// 评分驱动:scoring.mbt 的 score_rank 输出 L1~L4,curriculum 据此决定
/// 下一轮该从哪个档位采样父代、生成什么难度的子代。
///|
/// 课程阶段:从 L1(入门)到 L4(精通),由易到难
pub enum CurriculumLevel {
L1 // 入门:基础正确性
L2 // 进阶:覆盖度
L3 // 熟练:结构完整性
L4 // 精通:高正确率 + 稳健性
} derive(Eq, Debug)
///|
pub extend CurriculumLevel with Eq::{not_equal, equal}
///|
pub extend CurriculumLevel with @moonbitlang/core/debug.Debug::{to_repr}
///|
/// 课程条目:一个阶段 + 该阶段的学习目标
pub struct CurriculumItem {
level : CurriculumLevel
goal : String
min_score : Double // 进入下一阶段的最低分
}
///|
/// 生成标准四阶段课程
pub fn standard_curriculum() -> Array[CurriculumItem] {
[
{
level: L1,
goal: "实现基础功能,保证核心路径正确",
min_score: 0.55,
},
{
level: L2,
goal: "提升测试覆盖度,处理边界条件",
min_score: 0.70,
},
{
level: L3,
goal: "完善结构(fingerprint/schema),保证可验证性",
min_score: 0.85,
},
{
level: L4,
goal: "追求高正确率与稳健性,可蒸馏为 skill",
min_score: 0.95,
},
]
}
///|
/// 根据当前评分决定下一步该在哪个阶段学习
pub fn next_level(current_score : Double) -> CurriculumLevel {
if current_score < 0.55 {
L1
} else if current_score < 0.70 {
L2
} else if current_score < 0.85 {
L3
} else {
L4
}
}
///|
/// 给定当前评分,返回建议的下一阶段目标描述
pub fn suggest_next_goal(current_score : Double) -> String {
match next_level(current_score) {
L1 =>
"当前评分偏低,先保证基础功能正确性(覆盖率与正确率)"
L2 => "已有基础,重点提升测试覆盖度与边界处理"
L3 =>
"覆盖度达标,完善结构完整性(fingerprint/schema)以进入可验证状态"
L4 =>
"已达精通档,重点追求稳健性并可蒸馏为可复用 skill"
}
}
///|
/// 判断是否可以从当前阶段"毕业"进入下一阶段
pub fn can_graduate(
current_score : Double,
current_level : CurriculumLevel,
) -> Bool {
match current_level {
L1 => current_score >= 0.55
L2 => current_score >= 0.70
L3 => current_score >= 0.85
L4 => current_score >= 0.95 // L4 已是最高,"毕业"意味着可蒸馏
}
}
///|
/// 课程进度:已达阶段 / 总阶段数(0.0 ~ 1.0)
pub fn curriculum_progress(current_score : Double) -> Double {
let level = next_level(current_score)
match level {
L1 => 0.0
L2 => 0.33
L3 => 0.66
L4 => 1.0
}
}
///|
/// 变异算子类型
pub enum MutationKind {
Rewrite // 重写(保留意图,换实现)
Simplify // 简化(移除冗余)
Generalize // 泛化(参数化硬编码值)
Specialize // 特化(针对特定场景优化)
Combine // 组合(融合两个父代的特征)
} derive(Eq, Debug)
///|
pub extend MutationKind with Eq::{not_equal, equal}
///|
pub extend MutationKind with @moonbitlang/core/debug.Debug::{to_repr}
///|
/// 对 artifact 的 note/goal 生成变异提示词(供外部执行器使用)
/// 注意:这里只生成"变异指令",真正的代码变异由外部执行器完成
pub fn mutation_prompt(
kind : MutationKind,
goal : String,
note : String,
) -> String {
let base = "对以下产物执行变异,目标:" +
goal +
"。原设计意图:" +
note +
"。\n"
match kind {
Rewrite =>
base +
"【重写】保留功能语义,用不同的实现方式重写代码,保持接口不变。"
Simplify =>
base +
"【简化】识别并移除冗余代码、重复逻辑、不必要的抽象,保持行为等价。"
Generalize =>
base +
"【泛化】把硬编码的值/逻辑参数化,提取可复用的抽象,扩大适用范围。"
Specialize =>
base +
"【特化】针对特定高频场景做专门优化,牺牲通用性换取性能/简洁。"
Combine =>
base +
"【组合】融合多个父代的优点,取长补短,产出一个更优的综合版本。"
}
}
///|
/// 选择变异算子:根据当前评分档位选择合适的变异策略
/// - 低分(L1/L2):偏向 Simplify / Rewrite(先搞对、搞简)
/// - 中分(L3):偏向 Generalize / Rewrite(完善结构)
/// - 高分(L4):`parents < 2` ⇒ Specialize;`parents >= 2` ⇒ **Combine**(融合父代特征)
///
/// **BUG-479 / Lane DM(2026-10-29)**:`Combine` 过去是**文档承诺但永不产出** ——
/// 消费面早就建好了(`mutation_prompt` / 字符串化都写了 `Combine` 分支),
/// 但本函数的 4 个分支只返回 Simplify/Rewrite/Generalize/Specialize
/// ⇒ 判例-29 的又一实例(「消费面建好了、生产面没接」)。
///
/// **为什么按 `parents` 修而不是按阈值修**:
/// `Combine` 的语义是「**融合两个父代**」⇒ 它的前置条件是「**有 ≥2 个父代**」。
/// 而旧的 `select_mutation(score)` **只有分数、没有父代信息**
/// ⇒ **它结构上就不可能正确产出 `Combine`** —— 那是「参数面缺信息」,
/// 不是「阈值没调对」。按阈值修只会让它在单父代时也瞎「融合」。
///
/// **零回归**:`parents` 缺省 1 ⇒ 现有调用方(只传 score)行为**逐字不变**。
pub fn select_mutation(score : Double, parents~ : Int32 = 1) -> MutationKind {
let r = score.clamp(min=0.0, max=1.0)
// Combine 的语义前置:**有多个父代可融合** + 高分(追求极致)
if r >= 0.85 && parents >= 2 {
Combine
} else if r < 0.55 {
Simplify
} else if r < 0.70 {
Rewrite
} else if r < 0.85 {
Generalize
} else {
Specialize
}
}
///|
/// 技能库条目:从高分 artifact 蒸馏出的可复用技能
pub struct SkillEntry {
name : String
description : String
source_artifact_id : String
score : Double
tags : Array[String]
}
///|
/// 判断 artifact 是否值得蒸馏为 skill(评分 ≥ 0.85 且非空 note)
pub fn is_distillable(score : Double, note : String) -> Bool {
score >= 0.85 && note.trim() != ""
}
///|
/// 从 artifact 的 goal + note 生成 skill 名称
pub fn derive_skill_name(_goal : String, note : String) -> String {
// 取 note 的前 8 个字符作为 skill 名前缀
let prefix = if note.length() > 8 { note[0:8].to_owned() } else { note }
// R150:`replace` 只替首个空格 ⇒ skill 名里残留空格(名不是合法标识符)。改 `replace_all`。
let cleaned = prefix.trim().replace_all(old=" ", new="_").to_owned()
"skill_" + cleaned
}
///|
/// 从 artifact 蒸馏出 skill 条目
pub fn distill_skill(
artifact_id : String,
goal : String,
note : String,
score : Double,
) -> SkillEntry? {
if !is_distillable(score, note) {
return None
}
let name = derive_skill_name(goal, note)
let desc = goal + " | " + note
Some({
name,
description: desc,
source_artifact_id: artifact_id,
score,
tags: [],
})
}
///|
/// 给 skill 打标签(从 description 关键词提取)
pub fn tag_skill(skill : SkillEntry, keywords : Array[String]) -> SkillEntry {
let tags : Array[String] = []
let desc_lower = skill.description.to_lower()
for kw in keywords {
if desc_lower.contains(kw.to_lower()) {
tags.push(kw)
}
}
{
name: skill.name,
description: skill.description,
source_artifact_id: skill.source_artifact_id,
score: skill.score,
tags,
}
}
///|
/// 课程 + 变异 + 蒸馏的一体化决策:给定当前评分,返回
/// (建议阶段, 建议变异算子, 是否可蒸馏)
pub fn evolve_decision(
current_score : Double,
) -> (CurriculumLevel, MutationKind, Bool) {
let level = next_level(current_score)
let mutation = select_mutation(current_score)
let distillable = current_score >= 0.85
(level, mutation, distillable)
}
///|
/// 把 evolve_decision 转可读字符串
pub fn decision_to_string(
level : CurriculumLevel,
mutation : MutationKind,
distillable : Bool,
) -> String {
let level_str = match level {
L1 => "L1(入门)"
L2 => "L2(进阶)"
L3 => "L3(熟练)"
L4 => "L4(精通)"
}
let mut_str = match mutation {
Rewrite => "重写"
Simplify => "简化"
Generalize => "泛化"
Specialize => "特化"
Combine => "组合"
}
let distill_str = if distillable { "可蒸馏" } else { "暂不蒸馏" }
"阶段=" + level_str + ", 变异=" + mut_str + ", " + distill_str
}