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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user