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
+1 -40
View File
@@ -4,7 +4,6 @@ import os
from skillopt.datasets.base import BatchSpec
from skillopt.envs.base import EnvAdapter
from skillopt.envs.deep_reflect import run_no_reference_deep_reflect
from skillopt.envs.officeqa.dataloader import OfficeQADataLoader
from skillopt.envs.officeqa.rollout import run_batch
from skillopt.gradient.reflect import run_minibatch_reflect
@@ -37,11 +36,7 @@ class OfficeQAAdapter(EnvAdapter):
search_timeout_seconds: int = 20,
use_local_tools: bool = True,
data_dirs: list[str] | str | None = None,
docs_dirs: list[str] | str | None = None,
use_deep_reflect: bool = False,
deep_reflect_failures: int = 4,
deep_reflect_successes: int = 2,
) -> None:
docs_dirs: list[str] | str | None = None, ) -> None:
self.workers = workers
self.analyst_workers = analyst_workers
self.failure_only = failure_only
@@ -58,9 +53,6 @@ class OfficeQAAdapter(EnvAdapter):
self.search_timeout_seconds = int(search_timeout_seconds)
self.use_local_tools = bool(use_local_tools)
self.data_dirs = data_dirs if data_dirs is not None else docs_dirs
self.use_deep_reflect = use_deep_reflect
self.deep_reflect_failures = deep_reflect_failures
self.deep_reflect_successes = deep_reflect_successes
self.dataloader = OfficeQADataLoader(
split_dir=split_dir,
data_path=data_path,
@@ -133,37 +125,6 @@ class OfficeQAAdapter(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]:
return run_no_reference_deep_reflect(
self,
results,
skill_content,
out_dir,
env_manager=kwargs.get("env_manager"),
prediction_dir=kwargs.get("prediction_dir"),
random_seed=kwargs.get("random_seed"),
step_buffer_context=kwargs.get("step_buffer_context", ""),
output_requirements=[
"- There is no hidden reference block. Use only the question, candidate files, tool trace, student output, and evaluation result to infer what intermediate state is worth probing.",
"- The instruction must explicitly request a short <analysis>...</analysis> block before the final <answer>...</answer>.",
"- The readout should focus on selected document/file, evidence span or table, extracted value, units, and any date or fiscal-period normalization.",
"- Do not ask for exhaustive copying of source text or a full chain-of-thought.",
"- The instruction text should be ready to append directly to the student's prompt.",
],
metadata_builder=lambda item: {
"id": str(item.get("id")),
"task_type": str(item.get("task_type") or "officeqa"),
"question_preview": str(item.get("question") or "")[:200],
"source_files": item.get("source_files", []),
"source_docs": item.get("source_docs", []),
},
)
def get_task_types(self) -> list[str]:
seen: list[str] = []
+18 -18
View File
@@ -14,8 +14,8 @@ try:
from skillopt.envs.sealqa.tool_runtime import custom_search
except ImportError:
custom_search = None # type: ignore[assignment]
from skillopt.model import chat_student_messages, 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 chat_target_messages, 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
_TOOL_SCHEMAS = [
{
@@ -299,12 +299,12 @@ def _run_codex_once(
link_dirs=_docs_link_targets(docs_roots),
)
prompt = (
"Use the `skillopt-student` skill available in this workspace.\n"
"Use the `skillopt-target` skill available in this workspace.\n"
"Read `task.md`, inspect or search the full OfficeQA corpus under `docs/`, and answer the question.\n"
"Treat candidate files in `task.md` as hints, not an access limit.\n"
"Return the final answer inside <answer>...</answer>."
)
final_message, raw = run_student_exec(
final_message, raw = run_target_exec(
work_dir=work_dir,
prompt=prompt,
model=model,
@@ -356,8 +356,8 @@ def _run_custom_search_process(
raise ValueError("custom_search mode requires a non-empty search_api_url")
if not os.environ.get(search_auth_env, "").strip():
raise ValueError(f"custom_search mode requires auth token env var {search_auth_env}")
if get_student_backend() not in {"openai_chat", "qwen_chat"}:
raise ValueError("custom_search mode is only supported with student_backend='openai_chat' or 'qwen_chat'")
if get_target_backend() not in {"openai_chat", "qwen_chat"}:
raise ValueError("custom_search mode is only supported with target_backend='openai_chat' or 'qwen_chat'")
system = _build_system(
skill_content,
search_mode=_CUSTOM_SEARCH_MODE,
@@ -385,7 +385,7 @@ def _run_custom_search_process(
fail_reason = ""
last_response_metadata: dict = {}
for turn in range(1, max_tool_turns + 1):
message, _ = chat_student_messages(
message, _ = chat_target_messages(
messages=messages,
max_completion_tokens=max_completion_tokens,
retries=5,
@@ -439,8 +439,8 @@ def _run_azure_search_process(
diagnostic_mode: bool,
diagnostic_instruction: str,
) -> tuple[str, str, str, str, list[dict], str, dict]:
if get_student_backend() != "openai_chat":
raise ValueError("azure_search mode is only supported with student_backend='openai_chat'")
if get_target_backend() != "openai_chat":
raise ValueError("azure_search mode is only supported with target_backend='openai_chat'")
system = _build_system(skill_content, search_mode=_AZURE_SEARCH_MODE)
user = _build_user(
item,
@@ -453,7 +453,7 @@ def _run_azure_search_process(
{"role": "user", "content": user},
]
conversation: list[dict] = [{"role": "user", "content": user}]
message, _ = chat_student_messages(
message, _ = chat_target_messages(
messages=messages,
max_completion_tokens=max_completion_tokens,
retries=5,
@@ -494,7 +494,7 @@ def _run_offline_no_tools_process(
{"role": "user", "content": user},
]
conversation: list[dict] = [{"role": "user", "content": user}]
message, _ = chat_student_messages(
message, _ = chat_target_messages(
messages=messages,
max_completion_tokens=max_completion_tokens,
retries=5,
@@ -616,7 +616,7 @@ def process_one(
candidate_files=candidate_files,
oracle_context=oracle_context,
)
elif is_student_exec_backend():
elif is_target_exec_backend():
from skillopt.model import azure_openai as _llm
response = ""
system = ""
@@ -628,7 +628,7 @@ def process_one(
skill_content=skill_content,
candidate_files=candidate_files,
docs_roots=docs_roots,
model=_llm.STUDENT_DEPLOYMENT,
model=_llm.TARGET_DEPLOYMENT,
timeout=180,
diagnostic_mode=diagnostic_mode if turn == 1 else False,
diagnostic_instruction=diagnostic_instruction if turn == 1 else "",
@@ -650,7 +650,7 @@ def process_one(
{"role": "user", "content": user},
]
for turn in range(1, max_tool_turns + 1):
message, _ = chat_student_messages(
message, _ = chat_target_messages(
messages=messages,
max_completion_tokens=768,
retries=5,
@@ -688,9 +688,9 @@ def process_one(
break
except Exception as e: # noqa: BLE001
fail_reason = f"error: {e}"
with open(os.path.join(pred_dir, "student_system_prompt.txt"), "w", encoding="utf-8") as f:
with open(os.path.join(pred_dir, "target_system_prompt.txt"), "w", encoding="utf-8") as f:
f.write(system)
with open(os.path.join(pred_dir, "student_user_prompt.txt"), "w", encoding="utf-8") as f:
with open(os.path.join(pred_dir, "target_user_prompt.txt"), "w", encoding="utf-8") as f:
f.write(user)
with open(os.path.join(pred_dir, "conversation.json"), "w", encoding="utf-8") as f:
json.dump(conversation, f, ensure_ascii=False, indent=2)
@@ -714,8 +714,8 @@ def process_one(
"agent_ok": not fail_reason,
"n_turns": len(conversation),
"last_finish_reason": last_response_metadata.get("finish_reason", ""),
"student_system_prompt": system,
"student_user_prompt": user,
"target_system_prompt": system,
"target_user_prompt": user,
}
return result
def run_batch(