/// engine_eval.mbt —— 反馈收敛(R111 · Evaluator-Optimizer schema 蒸馏)。
/// 把自由文本反馈归一为四段式 Defects/Evidence/Fix/Acceptance 契约 + 确定性 verdict:
/// 无缺陷且 acceptance 非空 → pass(optimizer 可收敛);否则 fail + 缺段/证据/修复标注。
/// 纯计算只读不写库(决策建议,执行权在调用方;与 plan_revise 反馈修订互补)。
///|
/// 列表项正文(行已 trim):`- ` / `* ` / `+ ` 或有序标记 `1. ` / `1) `。
/// 非列表行返回 None——`## Defects` 下写「(无)」这类自由文本仍按零缺陷处理。
fn list_item_text(line : String) -> String? {
let n = line.length()
if n >= 2 {
let c0 = line[0]
if (c0 == '-' || c0 == '*' || c0 == '+') && line[1] == ' ' {
return Some(line[2:n].to_owned())
}
}
let mut i : Int32 = 0
while i < n && line[i] >= '0' && line[i] <= '9' {
i = i + 1
}
if i > 0 &&
i + 1 < n &&
(line[i] == '.' || line[i] == ')') &&
line[i + 1] == ' ' {
return Some(line[i + 2:n].to_owned())
}
None
}
///|
/// 从反馈文本解析某 `## 段名` 下的列表项(遇到下一个 `## ` 标题即结束)。
/// R135:只认 `- ` 会把 `* ` / `+ ` / 有序列表写下的缺陷静默吞成「零缺陷 → pass」。
fn parse_section(text : String, header : String) -> Array[String] {
let out : Array[String] = []
let mut in_section = false
for line in text.split("\n") {
let t = line.trim()
if t.has_prefix("## ") {
in_section = t == "## " + header
} else if in_section {
match list_item_text(t.to_owned()) {
Some(item) => out.push(item)
None => ()
}
}
}
out
}
///|
/// 反馈文本中是否存在某 `## 段名` 标题(段"在场"判定,与条目数无关)。
fn section_present(text : String, header : String) -> Bool {
for line in text.split("\n") {
if line.trim() == "## " + header {
return true
}
}
false
}
///|
/// 反馈收敛:归一为四段式 schema + verdict(E/O 蒸馏:生成→评估→定向修订→收敛)。
/// 返回 { ok, task_id, schema:{defects,evidence,fix,acceptance}, counts, verdict, flags, note }。
pub fn FistEngine::eval_feedback(
_self : FistEngine,
feedback : String,
task_id~ : String = "",
) -> Json {
let defects = parse_section(feedback, "Defects")
let evidence = parse_section(feedback, "Evidence")
let fix = parse_section(feedback, "Fix")
let acceptance = parse_section(feedback, "Acceptance")
let d = defects.length()
let e = evidence.length()
let f = fix.length()
let a = acceptance.length()
let flags : Array[String] = []
if d > 0 && e == 0 {
flags.push("evidence_missing")
}
if d > 0 && f == 0 {
flags.push("fix_missing")
}
let missing : Array[String] = []
let has_defects = section_present(feedback, "Defects")
if !has_defects {
missing.push("Defects")
}
let has_evidence = section_present(feedback, "Evidence")
if !has_evidence {
missing.push("Evidence")
}
let has_fix = section_present(feedback, "Fix")
if !has_fix {
missing.push("Fix")
}
let has_acceptance = section_present(feedback, "Acceptance")
if !has_acceptance {
missing.push("Acceptance")
}
if missing.length() > 0 {
flags.push("section_missing:" + missing.join(","))
}
let verdict = if d == 0 && a > 0 { "pass" } else { "fail" }
let (sc_d, sc_e, sc_f, sc_a) = (
d.to_double(),
e.to_double(),
f.to_double(),
a.to_double(),
)
Json::object({
"ok": Json::boolean(true),
"task_id": Json::string(task_id),
"schema": Json::object({
"defects": Json::array(defects.map(Json::string)),
"evidence": Json::array(evidence.map(Json::string)),
"fix": Json::array(fix.map(Json::string)),
"acceptance": Json::array(acceptance.map(Json::string)),
}),
"counts": Json::object({
"defects": Json::number(sc_d),
"evidence": Json::number(sc_e),
"fix": Json::number(sc_f),
"acceptance": Json::number(sc_a),
}),
"verdict": Json::string(verdict),
"flags": Json::array(flags.map(Json::string)),
"note": Json::string(
"反馈收敛(Evaluator-Optimizer schema 蒸馏):feedback 归一为 Defects/Evidence/Fix/Acceptance 四段式——无缺陷且 acceptance 非空 → pass 可收敛;否则 fail 定向回灌(Defects→Fix→再 eval→pass 闭环,硬上限防死循环)。",
),
})
}