@@ -125,6 +125,9 @@ _FLATTEN_MAP: dict[str, str] = {
|
||||
"evaluation.use_gate": "use_gate",
|
||||
"evaluation.gate_metric": "gate_metric",
|
||||
"evaluation.gate_mixed_weight": "gate_mixed_weight",
|
||||
"evaluation.use_semantic_density": "use_semantic_density",
|
||||
"evaluation.semantic_density_weight": "semantic_density_weight",
|
||||
"evaluation.leading_words": "leading_words",
|
||||
"evaluation.sel_env_num": "sel_env_num",
|
||||
"evaluation.test_env_num": "test_env_num",
|
||||
"evaluation.eval_test": "eval_test",
|
||||
|
||||
+47
-20
@@ -709,26 +709,26 @@ class ReflACTTrainer:
|
||||
effort=cfg.get("claude_code_exec_effort", cfg.get("reasoning_effort", "medium")),
|
||||
max_thinking_tokens=cfg.get("claude_code_exec_max_thinking_tokens", 16384),
|
||||
)
|
||||
configure_qwen_chat(
|
||||
base_url=cfg.get("qwen_chat_base_url") or None,
|
||||
api_key=cfg.get("qwen_chat_api_key") or None,
|
||||
temperature=cfg.get("qwen_chat_temperature"),
|
||||
timeout_seconds=cfg.get("qwen_chat_timeout_seconds"),
|
||||
max_tokens=cfg.get("qwen_chat_max_tokens"),
|
||||
enable_thinking=cfg.get("qwen_chat_enable_thinking"),
|
||||
optimizer_base_url=cfg.get("optimizer_qwen_chat_base_url") or None,
|
||||
optimizer_api_key=cfg.get("optimizer_qwen_chat_api_key") or None,
|
||||
optimizer_temperature=cfg.get("optimizer_qwen_chat_temperature"),
|
||||
optimizer_timeout_seconds=cfg.get("optimizer_qwen_chat_timeout_seconds"),
|
||||
optimizer_max_tokens=cfg.get("optimizer_qwen_chat_max_tokens"),
|
||||
optimizer_enable_thinking=cfg.get("optimizer_qwen_chat_enable_thinking"),
|
||||
target_base_url=cfg.get("target_qwen_chat_base_url") or None,
|
||||
target_api_key=cfg.get("target_qwen_chat_api_key") or None,
|
||||
target_temperature=cfg.get("target_qwen_chat_temperature"),
|
||||
target_timeout_seconds=cfg.get("target_qwen_chat_timeout_seconds"),
|
||||
target_max_tokens=cfg.get("target_qwen_chat_max_tokens"),
|
||||
target_enable_thinking=cfg.get("target_qwen_chat_enable_thinking"),
|
||||
)
|
||||
configure_qwen_chat(
|
||||
base_url=cfg.get("qwen_chat_base_url") or None,
|
||||
api_key=cfg.get("qwen_chat_api_key") or None,
|
||||
temperature=cfg.get("qwen_chat_temperature"),
|
||||
timeout_seconds=cfg.get("qwen_chat_timeout_seconds"),
|
||||
max_tokens=cfg.get("qwen_chat_max_tokens"),
|
||||
enable_thinking=cfg.get("qwen_chat_enable_thinking"),
|
||||
optimizer_base_url=cfg.get("optimizer_qwen_chat_base_url") or None,
|
||||
optimizer_api_key=cfg.get("optimizer_qwen_chat_api_key") or None,
|
||||
optimizer_temperature=cfg.get("optimizer_qwen_chat_temperature"),
|
||||
optimizer_timeout_seconds=cfg.get("optimizer_qwen_chat_timeout_seconds"),
|
||||
optimizer_max_tokens=cfg.get("optimizer_qwen_chat_max_tokens"),
|
||||
optimizer_enable_thinking=cfg.get("optimizer_qwen_chat_enable_thinking"),
|
||||
target_base_url=cfg.get("target_qwen_chat_base_url") or None,
|
||||
target_api_key=cfg.get("target_qwen_chat_api_key") or None,
|
||||
target_temperature=cfg.get("target_qwen_chat_temperature"),
|
||||
target_timeout_seconds=cfg.get("target_qwen_chat_timeout_seconds"),
|
||||
target_max_tokens=cfg.get("target_qwen_chat_max_tokens"),
|
||||
target_enable_thinking=cfg.get("target_qwen_chat_enable_thinking"),
|
||||
)
|
||||
configure_minimax_chat(
|
||||
base_url=cfg.get("minimax_base_url") or None,
|
||||
api_key=cfg.get("minimax_api_key") or None,
|
||||
@@ -964,6 +964,15 @@ class ReflACTTrainer:
|
||||
f"got {gate_metric!r}"
|
||||
)
|
||||
gate_mixed_weight = float(cfg.get("gate_mixed_weight", 0.5))
|
||||
use_semantic_density = bool(cfg.get("use_semantic_density", False))
|
||||
semantic_density_weight = float(cfg.get("semantic_density_weight", 0.05))
|
||||
leading_words_raw = cfg.get("leading_words", None)
|
||||
leading_words = None
|
||||
if leading_words_raw is not None:
|
||||
if isinstance(leading_words_raw, str):
|
||||
leading_words = [w.strip() for w in leading_words_raw.split(",") if w.strip()]
|
||||
else:
|
||||
leading_words = list(leading_words_raw)
|
||||
if not 0.0 <= gate_mixed_weight <= 1.0:
|
||||
raise ValueError(
|
||||
f"evaluation.gate_mixed_weight must be in [0, 1], "
|
||||
@@ -1003,6 +1012,10 @@ class ReflACTTrainer:
|
||||
baseline_hard, baseline_soft = compute_score(baseline_results)
|
||||
current_score = select_gate_score(
|
||||
baseline_hard, baseline_soft, gate_metric, gate_mixed_weight,
|
||||
skill_content=skill_init,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
)
|
||||
best_score = current_score
|
||||
sh = skill_hash(skill_init)
|
||||
@@ -1450,9 +1463,16 @@ class ReflACTTrainer:
|
||||
cand_soft=cand_soft,
|
||||
metric=gate_metric,
|
||||
mixed_weight=gate_mixed_weight,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
) if use_gate else None
|
||||
cand_gate_score = select_gate_score(
|
||||
cand_hard, cand_soft, gate_metric, gate_mixed_weight,
|
||||
skill_content=candidate_skill,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
)
|
||||
if not use_gate:
|
||||
# Validation ran (scores recorded above) but the gate is
|
||||
@@ -1844,6 +1864,9 @@ class ReflACTTrainer:
|
||||
cand_soft=slow_sel_soft,
|
||||
metric=gate_metric,
|
||||
mixed_weight=gate_mixed_weight,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
)
|
||||
slow_result["selection_hard"] = slow_sel_hard
|
||||
slow_result["selection_soft"] = slow_sel_soft
|
||||
@@ -2093,6 +2116,10 @@ class ReflACTTrainer:
|
||||
final_gate_score = select_gate_score(
|
||||
final_selection_hard, final_selection_soft,
|
||||
gate_metric, gate_mixed_weight,
|
||||
skill_content=current_skill,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
)
|
||||
print(
|
||||
f"\n [final skill val] items={fval_n} "
|
||||
|
||||
@@ -43,11 +43,55 @@ class GateResult:
|
||||
best_step: int
|
||||
|
||||
|
||||
def compute_semantic_density(
|
||||
skill_content: str,
|
||||
leading_words: list[str] | None = None,
|
||||
) -> float:
|
||||
"""Compute the semantic density of leading words in a skill document."""
|
||||
if not skill_content or not skill_content.strip():
|
||||
return 0.0
|
||||
if leading_words is None:
|
||||
leading_words = [
|
||||
"MUST", "ALWAYS", "NEVER", "ONLY", "CRITICAL", "IMPORTANT",
|
||||
"RESOLVE", "PREFER", "ENSURE", "STRICT", "VERIFY"
|
||||
]
|
||||
|
||||
# Strip metadata comments to focus purely on instruction text
|
||||
skill = skill_content
|
||||
for start, end in [
|
||||
("<!-- SLOW_UPDATE_START -->", "<!-- SLOW_UPDATE_END -->"),
|
||||
("<!-- APPENDIX_START -->", "<!-- APPENDIX_END -->")
|
||||
]:
|
||||
while True:
|
||||
s_idx = skill.find(start)
|
||||
if s_idx == -1:
|
||||
break
|
||||
e_idx = skill.find(end, s_idx)
|
||||
if e_idx == -1:
|
||||
skill = skill[:s_idx] + skill[s_idx + len(start):]
|
||||
break
|
||||
skill = skill[:s_idx] + skill[e_idx + len(end):]
|
||||
|
||||
import re
|
||||
words = re.findall(r'[a-zA-Z0-9]+', skill.lower())
|
||||
if not words:
|
||||
return 0.0
|
||||
|
||||
leading_set = {w.lower() for w in leading_words}
|
||||
leading_count = sum(1 for w in words if w in leading_set)
|
||||
return leading_count / len(words)
|
||||
|
||||
|
||||
def select_gate_score(
|
||||
hard: float,
|
||||
soft: float,
|
||||
metric: GateMetric = "hard",
|
||||
mixed_weight: float = 0.5,
|
||||
*,
|
||||
skill_content: str = "",
|
||||
use_semantic_density: bool = False,
|
||||
semantic_density_weight: float = 0.05,
|
||||
leading_words: list[str] | None = None,
|
||||
) -> float:
|
||||
"""Project (hard, soft) onto a single comparison metric.
|
||||
|
||||
@@ -60,17 +104,32 @@ def select_gate_score(
|
||||
mixed_weight
|
||||
For ``"mixed"``: weight given to ``soft``. Must be in ``[0, 1]``.
|
||||
Ignored for ``"hard"`` / ``"soft"``.
|
||||
skill_content
|
||||
The raw skill document content.
|
||||
use_semantic_density
|
||||
Whether to adjust the score based on semantic density of leading words.
|
||||
semantic_density_weight
|
||||
Scaling weight for the semantic density bonus.
|
||||
leading_words
|
||||
Optional custom list of high-influence words to prioritize.
|
||||
"""
|
||||
if metric == "hard":
|
||||
return float(hard)
|
||||
if metric == "soft":
|
||||
return float(soft)
|
||||
if metric == "mixed":
|
||||
score = float(hard)
|
||||
elif metric == "soft":
|
||||
score = float(soft)
|
||||
elif metric == "mixed":
|
||||
w = max(0.0, min(1.0, float(mixed_weight)))
|
||||
return (1.0 - w) * float(hard) + w * float(soft)
|
||||
raise ValueError(
|
||||
f"unknown gate metric {metric!r}; expected 'hard', 'soft', or 'mixed'"
|
||||
)
|
||||
score = (1.0 - w) * float(hard) + w * float(soft)
|
||||
else:
|
||||
raise ValueError(
|
||||
f"unknown gate metric {metric!r}; expected 'hard', 'soft', or 'mixed'"
|
||||
)
|
||||
|
||||
if use_semantic_density:
|
||||
density = compute_semantic_density(skill_content, leading_words)
|
||||
score += float(semantic_density_weight) * density
|
||||
|
||||
return score
|
||||
|
||||
|
||||
def evaluate_gate(
|
||||
@@ -86,6 +145,9 @@ def evaluate_gate(
|
||||
cand_soft: float = 0.0,
|
||||
metric: GateMetric = "hard",
|
||||
mixed_weight: float = 0.5,
|
||||
use_semantic_density: bool = False,
|
||||
semantic_density_weight: float = 0.05,
|
||||
leading_words: list[str] | None = None,
|
||||
) -> GateResult:
|
||||
"""Pure gate decision: compare candidate score to current/best.
|
||||
|
||||
@@ -111,6 +173,12 @@ def evaluate_gate(
|
||||
the original gate behavior.
|
||||
mixed_weight
|
||||
Weight on ``soft`` when ``metric == "mixed"``.
|
||||
use_semantic_density
|
||||
Whether to adjust the score based on semantic density of leading words.
|
||||
semantic_density_weight
|
||||
Scaling weight for the semantic density bonus.
|
||||
leading_words
|
||||
Optional custom list of high-influence words to prioritize.
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -118,7 +186,16 @@ def evaluate_gate(
|
||||
Updated state; the caller decides what to do with it (print,
|
||||
mutate trainer state, log, etc.).
|
||||
"""
|
||||
cand_score = select_gate_score(cand_hard, cand_soft, metric, mixed_weight)
|
||||
cand_score = select_gate_score(
|
||||
cand_hard,
|
||||
cand_soft,
|
||||
metric,
|
||||
mixed_weight,
|
||||
skill_content=candidate_skill,
|
||||
use_semantic_density=use_semantic_density,
|
||||
semantic_density_weight=semantic_density_weight,
|
||||
leading_words=leading_words,
|
||||
)
|
||||
|
||||
if cand_score > current_score:
|
||||
if cand_score > best_score:
|
||||
|
||||
Reference in New Issue
Block a user