refactor: rename teacher/student to optimizer/target, remove best skills, fix slow update

- Rename teacher -> optimizer, student -> target across all code, configs, docs, prompts
- CLI: --teacher_model -> --optimizer_model, --student_model -> --target_model
- Remove best_skill files, keep only initial skills
- Fix slow update gate (force write into skill)
- Fix SLOW_UPDATE marker stripping
- Remove deep_reflect and meta_reflect mechanisms
- Update .env.example with export prefix and azure_cli docs
- Add endpoint empty validation in azure_openai.py

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Cuzyoung
2026-05-24 19:15:03 +00:00
parent 6e165d5347
commit 4a1b984d87
70 changed files with 1083 additions and 2068 deletions
+4 -126
View File
@@ -8,7 +8,6 @@ from __future__ import annotations
import json
import os
from skillopt.gradient.deep_probe import generate_deep_probe_instruction
from skillopt.datasets.base import BatchSpec
from skillopt.envs.base import EnvAdapter
from skillopt.envs.spreadsheetbench.dataloader import SpreadsheetBenchDataLoader
@@ -18,7 +17,7 @@ from skillopt.envs.spreadsheetbench.rollout import (
run_spreadsheet_batch_codegen,
)
from skillopt.gradient.reflect import run_minibatch_reflect
from skillopt.model import get_student_backend, is_student_exec_backend
from skillopt.model import get_target_backend, is_target_exec_backend
# Task types used for per-category breakdowns
@@ -45,11 +44,7 @@ class SpreadsheetBenchAdapter(EnvAdapter):
failure_only: bool = False,
minibatch_size: int = 8,
edit_budget: int = 4,
seed: int = 42,
use_deep_reflect: bool = False,
deep_reflect_failures: int = 4,
deep_reflect_successes: int = 2,
) -> None:
seed: int = 42, ) -> None:
self.data_root = data_root
self.mode = mode # "single", "multi", or "react"
self.max_turns = max_turns
@@ -59,9 +54,6 @@ class SpreadsheetBenchAdapter(EnvAdapter):
self.failure_only = failure_only
self.minibatch_size = minibatch_size
self.edit_budget = edit_budget
self.use_deep_reflect = use_deep_reflect
self.deep_reflect_failures = deep_reflect_failures
self.deep_reflect_successes = deep_reflect_successes
self.dataloader = SpreadsheetBenchDataLoader(
split_dir=split_dir,
data_path=data_path,
@@ -75,9 +67,9 @@ class SpreadsheetBenchAdapter(EnvAdapter):
def setup(self, cfg: dict) -> None:
super().setup(cfg)
if is_student_exec_backend() and self.mode != "single":
if is_target_exec_backend() and self.mode != "single":
raise NotImplementedError(
"Exec student backends are currently supported only for SpreadsheetBench mode=single."
"Exec target backends are currently supported only for SpreadsheetBench mode=single."
)
self.dataloader.setup(cfg)
@@ -190,120 +182,6 @@ class SpreadsheetBenchAdapter(EnvAdapter):
update_mode=getattr(self, "_cfg", {}).get("skill_update_mode", "patch"),
)
def deep_reflect(
self,
results: list[dict],
skill_content: str,
out_dir: str,
**kwargs,
) -> list[dict | None]:
if not self.use_deep_reflect:
return []
env_manager = kwargs.get("env_manager")
if not isinstance(env_manager, list):
return []
prediction_dir = kwargs.get("prediction_dir", os.path.join(out_dir, "predictions"))
random_seed = kwargs.get("random_seed")
step_buffer_context = kwargs.get("step_buffer_context", "")
meta_skill_context = kwargs.get("meta_skill_context", "")
codex_backend = get_student_backend() == "codex_exec"
selected_items = self.select_representative_items(
results,
env_manager,
n_failures=self.deep_reflect_failures,
n_successes=self.deep_reflect_successes,
seed=random_seed,
)
if not selected_items:
return []
selected_ids = {str(item["id"]) for item in selected_items}
selected_results = [row for row in results if str(row.get("id")) in selected_ids]
selected_examples = (
self.attach_codex_probe_context(selected_results, prediction_dir)
if codex_backend
else selected_results
)
selected_metadata = [
{
"id": str(item["id"]),
"instruction_type": str(item.get("instruction_type") or ""),
"answer_position": str(item.get("answer_position") or ""),
}
for item in selected_items
]
deep_dir = os.path.join(out_dir, "deep_reflect")
rollout_dir = os.path.join(deep_dir, "rollout")
patches_dir = os.path.join(deep_dir, "patches")
os.makedirs(deep_dir, exist_ok=True)
print(
f" [2b/6 DEEP REFLECT setup] selected={len(selected_items)} "
f"mode={self.mode}"
)
probe = generate_deep_probe_instruction(
skill_content=skill_content,
items=selected_examples,
prediction_dir=prediction_dir,
system_prompt=self.get_codex_deep_probe_prompt() if codex_backend else self.get_deep_probe_prompt(),
step_buffer_context=step_buffer_context,
meta_skill_context=meta_skill_context,
output_requirements=[
"- The instruction must ask for a short structured diagnostic readout before the student writes code or starts tool use.",
"- The readout should focus on task family, source/target region, and decisive transformation rule.",
"- The student must still complete the original spreadsheet task.",
"- Keep the readout concise and avoid exhaustive cell enumeration.",
"- The instruction text should be ready to append directly to the student's prompt.",
],
)
if not probe:
return []
diagnostic_trace_context_by_id = None
if codex_backend:
selected_items, diagnostic_trace_context_by_id, probe = self.resolve_codex_probe_target(
selected_items=selected_items,
selected_examples=selected_examples,
prediction_dir=prediction_dir,
probe=probe,
)
with open(os.path.join(deep_dir, "probe.json"), "w", encoding="utf-8") as f:
json.dump(
{
**probe,
"selected_examples": selected_metadata,
},
f,
ensure_ascii=False,
indent=2,
)
deep_results = self.rollout(
selected_items,
skill_content,
rollout_dir,
diagnostic_mode=True,
diagnostic_instruction=probe["probe_instruction"],
diagnostic_trace_context_by_id=diagnostic_trace_context_by_id,
)
return run_minibatch_reflect(
results=deep_results,
skill_content=skill_content,
prediction_dir=os.path.join(rollout_dir, "predictions"),
patches_dir=patches_dir,
workers=self.analyst_workers,
failure_only=self.failure_only,
minibatch_size=self.minibatch_size,
edit_budget=self.edit_budget,
random_seed=random_seed,
error_system=self.get_error_minibatch_prompt(),
success_system=self.get_success_minibatch_prompt(),
step_buffer_context=step_buffer_context,
meta_skill_context=meta_skill_context,
update_mode=getattr(self, "_cfg", {}).get("skill_update_mode", "patch"),
)
def get_task_types(self) -> list[str]:
return list(TASK_TYPES)
+21 -21
View File
@@ -30,12 +30,12 @@ def _timeout_handler(signum, frame):
from skillopt.model.azure_openai import (
get_reasoning_effort,
get_student_client,
get_target_client,
_needs_responses_api,
tracker,
)
from skillopt.model import get_codex_exec_config, get_student_backend, is_student_exec_backend
from skillopt.model.codex_harness import prepare_workspace, render_skill_md, run_student_exec
from skillopt.model import get_codex_exec_config, get_target_backend, is_target_exec_backend
from skillopt.model.codex_harness import prepare_workspace, render_skill_md, run_target_exec
from skillopt.prompts import load_prompt
from skillopt.envs.spreadsheetbench.executor import run_generated_code
from skillopt.envs.spreadsheetbench.evaluator import evaluate
@@ -44,13 +44,13 @@ from skillopt.envs.spreadsheetbench.evaluator import evaluate
# ── Eval feedback helper (no golden value leakage) ─────────────────────────
def _build_eval_feedback(verify_report: str) -> str:
"""Build Student feedback from a verify report, hiding expected values.
"""Build Target feedback from a verify report, hiding expected values.
The verify report contains lines like:
Sheet1!D2: got=None, expected=0 ✗
Sheet1!D10: got=None, expected=None ✓
We strip the ``expected=...`` part so the Student sees only its own
We strip the ``expected=...`` part so the Target sees only its own
output and whether each cell is correct or wrong.
"""
import re
@@ -203,7 +203,7 @@ def _llm_call_with_retry(call_fn, *, retries: int = 5, timeout: int = 120):
def _get_deployment() -> str:
from skillopt.model import azure_openai as _llm
return _llm.STUDENT_DEPLOYMENT
return _llm.TARGET_DEPLOYMENT
def _build_codex_skill(skill_content: str) -> str:
@@ -242,7 +242,7 @@ def _build_codex_task(
return (
f"{prompt}\n\n"
"## Codex Harness Task\n"
"- Read `.agents/skills/skillopt-student/SKILL.md` before writing code; do not call a Skill tool.\n"
"- Read `.agents/skills/skillopt-target/SKILL.md` before writing code; do not call a Skill tool.\n"
"- Read and optionally inspect `input.xlsx` in this workspace.\n"
"- Write the final Python solution to `solution.py`.\n"
"- The script should use the provided `INPUT_PATH` and `OUTPUT_PATH` variables.\n"
@@ -296,7 +296,7 @@ def _prepare_codex_workspace(
diagnostic_trace_context=diagnostic_trace_context,
)
prompt = (
"Read `.agents/skills/skillopt-student/SKILL.md` directly; do not call a Skill tool.\n"
"Read `.agents/skills/skillopt-target/SKILL.md` directly; do not call a Skill tool.\n"
"Read `task.md`, inspect `input.xlsx` if useful, and write the final solution to `solution.py`.\n"
"You may run `python run_solution.py` to validate the script locally.\n"
"In your final response, briefly confirm whether `solution.py` was written and summarize the approach."
@@ -319,7 +319,7 @@ def _run_exec_backend(
model: str,
timeout: int,
) -> tuple[str, str]:
return run_student_exec(
return run_target_exec(
work_dir=work_dir,
prompt=prompt,
model=model,
@@ -416,7 +416,7 @@ def run_single(
Returns ``{"code": str, "raw": str, "n_turns": 1}``.
"""
if is_student_exec_backend():
if is_target_exec_backend():
deadline = time.time() + task_timeout
deployment = _get_deployment()
work_dir, skill_md, task_md, prompt = _prepare_codex_workspace(
@@ -449,12 +449,12 @@ def run_single(
"raw": raw or final_message,
"n_turns": 1,
"conversation": [{"role": "assistant", "content": final_message or raw}],
"student_system_prompt": skill_md,
"student_user_prompt": f"{prompt}\n\n## Task File\n\n{task_md}",
"target_system_prompt": skill_md,
"target_user_prompt": f"{prompt}\n\n## Task File\n\n{task_md}",
}
deadline = time.time() + task_timeout
client = get_student_client()
client = get_target_client()
deployment = _get_deployment()
system = _build_system(skill_content)
user = _build_user(
@@ -483,8 +483,8 @@ def run_single(
"raw": raw,
"n_turns": 1,
"conversation": [{"role": "assistant", "content": raw}],
"student_system_prompt": system,
"student_user_prompt": user,
"target_system_prompt": system,
"target_user_prompt": user,
}
@@ -520,7 +520,7 @@ def run_multi(
Returns ``{"code": str, "raw": str, "n_turns": int, "conversation": [...]}``.
"""
if is_student_exec_backend():
if is_target_exec_backend():
deadline = time.time() + task_timeout
deployment = _get_deployment()
work_dir, skill_md, task_md, initial_prompt = _prepare_codex_workspace(
@@ -613,12 +613,12 @@ def run_multi(
"raw": raw or final_message,
"n_turns": len([m for m in conversation if m["role"] == "assistant"]),
"conversation": conversation,
"student_system_prompt": skill_md,
"student_user_prompt": f"{initial_prompt}\n\n## Task File\n\n{task_md}",
"target_system_prompt": skill_md,
"target_user_prompt": f"{initial_prompt}\n\n## Task File\n\n{task_md}",
}
deadline = time.time() + task_timeout
client = get_student_client()
client = get_target_client()
deployment = _get_deployment()
system = _build_system(skill_content)
user = _build_user(
@@ -699,6 +699,6 @@ def run_multi(
"raw": raw,
"n_turns": turn + 1,
"conversation": conversation,
"student_system_prompt": system,
"student_user_prompt": user,
"target_system_prompt": system,
"target_user_prompt": user,
}
@@ -11,7 +11,7 @@ import json
import os
import subprocess
from skillopt.model import chat_student_messages
from skillopt.model import chat_target_messages
from skillopt.prompts import load_prompt
# ── Tool schemas ─────────────────────────────────────────────────────────────
@@ -298,7 +298,7 @@ def _react_loop(
n_turns = 0
for _ in range(max_turns):
message, _ = chat_student_messages(
message, _ = chat_target_messages(
messages=messages,
tools=[BASH_TOOL_CHAT, WRITE_FILE_TOOL_CHAT],
tool_choice="auto",
@@ -390,6 +390,6 @@ def run_react(
diagnostic_trace_context=diagnostic_trace_context,
)
result = _react_loop(system, user, work_dir, max_turns, max_output_tokens)
result["student_system_prompt"] = system
result["student_user_prompt"] = user
result["target_system_prompt"] = system
result["target_user_prompt"] = user
return result
+32 -32
View File
@@ -233,37 +233,37 @@ def process_one(
no1, ip1, _ = cases[0]
pred_path_1 = os.path.join(task_out_dir, f"{no1}_pred.xlsx")
student_prompt_parts = [
target_prompt_parts = [
f"# Instruction\n{instruction}",
f"# Input file\n{ip1}",
f"# Output file\n{pred_path_1}",
]
if instruction_type:
student_prompt_parts.append(f"# Instruction type\n{instruction_type}")
target_prompt_parts.append(f"# Instruction type\n{instruction_type}")
if answer_position_eval:
student_prompt_parts.append(f"# Answer position\n{answer_position_eval}")
target_prompt_parts.append(f"# Answer position\n{answer_position_eval}")
if diagnostic_trace_context.strip():
student_prompt_parts.insert(
target_prompt_parts.insert(
0,
"# Previous Codex Trace Snapshot\n"
"This is a partial transcript from an earlier attempt. Use it as your current reasoning context.\n\n"
f"{diagnostic_trace_context.strip()}",
)
if diagnostic_mode and diagnostic_instruction.strip():
student_prompt_parts.append(f"# Training readout\n{diagnostic_instruction.strip()}")
student_user_prompt = "\n\n".join(student_prompt_parts)
target_prompt_parts.append(f"# Training readout\n{diagnostic_instruction.strip()}")
target_user_prompt = "\n\n".join(target_prompt_parts)
try:
from skillopt.envs.spreadsheetbench.react_agent import _build_system
student_system_prompt = _build_system(skill_content)
target_system_prompt = _build_system(skill_content)
except Exception:
student_system_prompt = ""
if student_system_prompt:
with open(os.path.join(task_out_dir, "student_system_prompt.txt"), "w") as f:
f.write(student_system_prompt)
result["student_system_prompt"] = student_system_prompt
with open(os.path.join(task_out_dir, "student_user_prompt.txt"), "w") as f:
f.write(student_user_prompt)
result["student_user_prompt"] = student_user_prompt
target_system_prompt = ""
if target_system_prompt:
with open(os.path.join(task_out_dir, "target_system_prompt.txt"), "w") as f:
f.write(target_system_prompt)
result["target_system_prompt"] = target_system_prompt
with open(os.path.join(task_out_dir, "target_user_prompt.txt"), "w") as f:
f.write(target_user_prompt)
result["target_user_prompt"] = target_user_prompt
# ── Stage 1: run ReAct agent on test case 1 ─────────────────────
result["phase"] = "agent"
@@ -288,14 +288,14 @@ def process_one(
diagnostic_trace_context=diagnostic_trace_context,
)
result["n_turns"] = agent_result.get("n_turns", 0)
if agent_result.get("student_system_prompt"):
with open(os.path.join(task_out_dir, "student_system_prompt.txt"), "w") as f:
f.write(agent_result["student_system_prompt"])
result["student_system_prompt"] = agent_result["student_system_prompt"]
if agent_result.get("student_user_prompt"):
with open(os.path.join(task_out_dir, "student_user_prompt.txt"), "w") as f:
f.write(agent_result["student_user_prompt"])
result["student_user_prompt"] = agent_result["student_user_prompt"]
if agent_result.get("target_system_prompt"):
with open(os.path.join(task_out_dir, "target_system_prompt.txt"), "w") as f:
f.write(agent_result["target_system_prompt"])
result["target_system_prompt"] = agent_result["target_system_prompt"]
if agent_result.get("target_user_prompt"):
with open(os.path.join(task_out_dir, "target_user_prompt.txt"), "w") as f:
f.write(agent_result["target_user_prompt"])
result["target_user_prompt"] = agent_result["target_user_prompt"]
# Save conversation log
with open(os.path.join(task_out_dir, "conversation.json"), "w") as f:
@@ -606,7 +606,7 @@ def process_one_codegen(
task_out_dir = os.path.join(out_root, "predictions", task_id)
os.makedirs(task_out_dir, exist_ok=True)
# ── Save context for Teacher (Reflect stage) ──────────────────
# ── Save context for Optimizer (Reflect stage) ──────────────────
from skillopt.envs.spreadsheetbench.codegen_agent import (
_preview_workbook, _build_system, _build_user,
)
@@ -615,8 +615,8 @@ def process_one_codegen(
preview_text = _preview_workbook(first_input_for_preview)
except Exception:
preview_text = "(preview failed)"
student_system = _build_system(skill_content)
student_user = _build_user(
target_system = _build_system(skill_content)
target_user = _build_user(
instruction,
first_input_for_preview,
instruction_type,
@@ -628,14 +628,14 @@ def process_one_codegen(
with open(os.path.join(task_out_dir, "spreadsheet_preview.txt"), "w") as f:
f.write(preview_text)
with open(os.path.join(task_out_dir, "student_system_prompt.txt"), "w") as f:
f.write(student_system)
with open(os.path.join(task_out_dir, "student_user_prompt.txt"), "w") as f:
f.write(student_user)
with open(os.path.join(task_out_dir, "target_system_prompt.txt"), "w") as f:
f.write(target_system)
with open(os.path.join(task_out_dir, "target_user_prompt.txt"), "w") as f:
f.write(target_user)
result["spreadsheet_preview"] = preview_text
result["student_system_prompt"] = student_system
result["student_user_prompt"] = student_user
result["target_system_prompt"] = target_system
result["target_user_prompt"] = target_user
# ── LLM phase ──────────────────────────────────────────────────
result["phase"] = "llm"