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:
@@ -31,7 +31,7 @@ optimizer:
|
||||
learning_rate: 4 # Max edits per step (edit budget)
|
||||
lr_scheduler: cosine # cosine | linear | constant | autonomous
|
||||
use_slow_update: true # Epoch-boundary momentum
|
||||
use_meta_skill: true # Cross-epoch teacher memory
|
||||
use_meta_skill: true # Cross-epoch optimizer memory
|
||||
|
||||
# ── Evaluation ───────────────────────────────────
|
||||
evaluation:
|
||||
@@ -41,5 +41,5 @@ evaluation:
|
||||
# ── Model ────────────────────────────────────────
|
||||
model:
|
||||
backend: azure_openai # azure_openai | openai_chat | claude_code_exec | qwen
|
||||
teacher: gpt-5.5
|
||||
student: gpt-5.5
|
||||
optimizer: gpt-4o
|
||||
target: gpt-4o
|
||||
|
||||
@@ -4,7 +4,7 @@ Benchmark Environment Template
|
||||
Copy this file and implement the TODO sections to add a new benchmark.
|
||||
|
||||
The EnvAdapter is responsible for:
|
||||
1. Executing tasks using the student model + current skill document
|
||||
1. Executing tasks using the target model + current skill document
|
||||
2. Evaluating predictions against ground truth
|
||||
3. Returning structured results for the training loop
|
||||
"""
|
||||
@@ -25,12 +25,12 @@ class TemplateBenchmarkEnv(EnvAdapter):
|
||||
|
||||
async def execute(self, item, skill: str, model):
|
||||
"""
|
||||
Execute a single task with the student model.
|
||||
Execute a single task with the target model.
|
||||
|
||||
Args:
|
||||
item: DataItem with .id, .input, .ground_truth, .metadata
|
||||
skill: Current skill document content (Markdown string)
|
||||
model: Student model backend instance
|
||||
model: Target model backend instance
|
||||
|
||||
Returns:
|
||||
TaskResult with prediction, score, and trajectory
|
||||
@@ -38,7 +38,7 @@ class TemplateBenchmarkEnv(EnvAdapter):
|
||||
# Step 1: Build the prompt combining skill + task input
|
||||
prompt = self.build_prompt(item, skill)
|
||||
|
||||
# Step 2: Call the student model
|
||||
# Step 2: Call the target model
|
||||
# TODO: Customize the message format for your benchmark
|
||||
messages = [
|
||||
{"role": "system", "content": skill},
|
||||
|
||||
@@ -9,7 +9,6 @@ from dataclasses import dataclass
|
||||
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.alfworld.dataloader import ALFWorldDataLoader
|
||||
@@ -82,11 +81,7 @@ class ALFWorldAdapter(EnvAdapter):
|
||||
analyst_workers: int = 16,
|
||||
failure_only: bool = False,
|
||||
minibatch_size: int = 8,
|
||||
edit_budget: int = 4,
|
||||
use_deep_reflect: bool = False,
|
||||
deep_reflect_failures: int = 4,
|
||||
deep_reflect_successes: int = 2,
|
||||
) -> None:
|
||||
edit_budget: int = 4, ) -> None:
|
||||
self.max_steps = max_steps
|
||||
self.workers = max(int(workers or 1), 1)
|
||||
self.max_api_workers = max_api_workers
|
||||
@@ -94,9 +89,6 @@ class ALFWorldAdapter(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 = ALFWorldDataLoader(
|
||||
split_dir=split_dir,
|
||||
data_path=data_path,
|
||||
@@ -457,129 +449,6 @@ class ALFWorldAdapter(EnvAdapter):
|
||||
meta_skill_context=meta_skill_context,
|
||||
)
|
||||
|
||||
def deep_reflect(
|
||||
self,
|
||||
results: list[dict],
|
||||
skill_content: str,
|
||||
out_dir: str,
|
||||
**kwargs,
|
||||
) -> list[dict | None]:
|
||||
if not self.use_deep_reflect:
|
||||
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", "")
|
||||
selected_items = self.select_representative_items(
|
||||
results,
|
||||
results,
|
||||
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_reference_context(selected_results, selected_items)
|
||||
|
||||
field_counts: dict[str, int] = {}
|
||||
selected_metadata: list[dict] = []
|
||||
for item in selected_items:
|
||||
meta = self.get_reference_metadata(item)
|
||||
for field in meta["fields"]:
|
||||
field_counts[field] = field_counts.get(field, 0) + 1
|
||||
selected_metadata.append({
|
||||
"id": str(item["id"]),
|
||||
"task_type": str(item.get("task_type") or "alfworld"),
|
||||
"gamefile": str(item.get("gamefile") or ""),
|
||||
"reference_fields": meta["fields"],
|
||||
"reference_preview": meta["preview"],
|
||||
})
|
||||
|
||||
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)
|
||||
field_summary = ", ".join(
|
||||
f"{field}({count}/{len(selected_items)})"
|
||||
for field, count in sorted(field_counts.items())
|
||||
) or "none"
|
||||
print(
|
||||
f" [2b/6 DEEP REFLECT setup] selected={len(selected_items)} "
|
||||
f"reference_fields={field_summary}"
|
||||
)
|
||||
probe = generate_deep_probe_instruction(
|
||||
skill_content=skill_content,
|
||||
items=selected_examples,
|
||||
prediction_dir=prediction_dir,
|
||||
system_prompt=self.get_deep_probe_prompt(),
|
||||
step_buffer_context=step_buffer_context,
|
||||
meta_skill_context=meta_skill_context,
|
||||
output_requirements=[
|
||||
"- Some trajectories may include a hidden Reference block. Use it to target the student's latent subgoal, missing precondition, or next-step intent, but do not reveal or paraphrase that reference to the student.",
|
||||
"- The instruction must request a brief diagnostic readout inside the existing <think>...</think> block.",
|
||||
"- The student must still output exactly one admissible action inside <action>...</action>.",
|
||||
"- Do not ask for exhaustive inventories, full plans, or long chain-of-thought.",
|
||||
"- The instruction text should be ready to append directly to the student's prompt.",
|
||||
],
|
||||
)
|
||||
if not probe:
|
||||
return []
|
||||
|
||||
with open(os.path.join(deep_dir, "probe.json"), "w", encoding="utf-8") as f:
|
||||
json.dump(
|
||||
{
|
||||
**probe,
|
||||
"reference_summary": {
|
||||
"selected_count": len(selected_items),
|
||||
"field_counts": field_counts,
|
||||
},
|
||||
"selected_examples": selected_metadata,
|
||||
},
|
||||
f,
|
||||
ensure_ascii=False,
|
||||
indent=2,
|
||||
)
|
||||
|
||||
gamefiles = [str(item.get("gamefile") or "") for item in selected_items]
|
||||
if any(not gamefile for gamefile in gamefiles):
|
||||
return []
|
||||
eval_dataset, is_train = self._infer_dataset_from_gamefile(gamefiles[0])
|
||||
deep_env = ALFWorldBatchRun(
|
||||
env_num=len(selected_items),
|
||||
eval_dataset=eval_dataset,
|
||||
seed=random_seed or 42,
|
||||
is_train=is_train,
|
||||
specific_gamefiles=gamefiles,
|
||||
workers=min(self.workers, max(len(selected_items), 1)),
|
||||
result_ids=[str(item["id"]) for item in selected_items],
|
||||
)
|
||||
deep_results = self._run_batch(
|
||||
deep_env,
|
||||
skill_content=skill_content,
|
||||
out_dir=rollout_dir,
|
||||
diagnostic_mode=True,
|
||||
diagnostic_instruction=probe["probe_instruction"],
|
||||
)
|
||||
deep_results = self.attach_reference_context(deep_results, selected_items)
|
||||
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,
|
||||
)
|
||||
|
||||
def get_task_types(self) -> list[str]:
|
||||
return list(TASKS)
|
||||
|
||||
@@ -15,7 +15,7 @@ import time
|
||||
import concurrent.futures
|
||||
import numpy as np
|
||||
|
||||
from skillopt.model import chat_student
|
||||
from skillopt.model import chat_target
|
||||
|
||||
# ── Constants ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -210,7 +210,7 @@ def run_alfworld_batch(
|
||||
|
||||
def call_api(idx):
|
||||
try:
|
||||
response, _ = chat_student(
|
||||
response, _ = chat_target(
|
||||
system="You are an expert agent operating in the ALFRED Embodied Environment.",
|
||||
user=prompts[idx],
|
||||
max_completion_tokens=max_completion_tokens,
|
||||
|
||||
+1
-88
@@ -31,7 +31,6 @@ import os
|
||||
import random
|
||||
|
||||
from skillopt.datasets.base import BaseDataLoader, BatchSpec
|
||||
from skillopt.model.codex_harness import extract_codex_trace_prefix, format_codex_trace_steps, parse_codex_raw
|
||||
from skillopt.prompts import load_prompt
|
||||
|
||||
|
||||
@@ -60,24 +59,8 @@ class EnvAdapter(ABC):
|
||||
"""Return whether this adapter requires Ray runtime initialization."""
|
||||
return False
|
||||
|
||||
def deep_reflect(
|
||||
self,
|
||||
results: list[dict],
|
||||
skill_content: str,
|
||||
out_dir: str,
|
||||
**kwargs,
|
||||
) -> list[dict | None]:
|
||||
"""Optional deeper diagnostic reflection pass.
|
||||
|
||||
Default behavior is a no-op. Dataset-backed adapters may override this
|
||||
to re-query the student on a small representative subset of the current
|
||||
batch using minimally-perturbed diagnostic prompts that expose
|
||||
intermediate reasoning state.
|
||||
"""
|
||||
return []
|
||||
|
||||
def build_reference_text(self, item: dict) -> str:
|
||||
"""Return hidden reference material for deep reflection, if any."""
|
||||
"""Return hidden reference material for reflection, if any."""
|
||||
return str(item.get("reference_text") or "").strip()
|
||||
|
||||
def get_reference_metadata(self, item: dict) -> dict:
|
||||
@@ -90,65 +73,6 @@ class EnvAdapter(ABC):
|
||||
"preview": reference_text[:400],
|
||||
}
|
||||
|
||||
def get_codex_deep_probe_prompt(self) -> str | None:
|
||||
env_name = getattr(self, "_cfg", {}).get("env_name")
|
||||
return load_prompt("deep_probe_codex", env=env_name)
|
||||
|
||||
def attach_codex_probe_context(
|
||||
self,
|
||||
results: list[dict],
|
||||
prediction_dir: str,
|
||||
) -> list[dict]:
|
||||
"""Attach compact Codex step metadata for codex-aware deep reflection."""
|
||||
enriched: list[dict] = []
|
||||
for row in results:
|
||||
merged = dict(row)
|
||||
tid = str(row.get("id"))
|
||||
raw_path = os.path.join(prediction_dir, tid, "codex_raw.txt")
|
||||
if os.path.exists(raw_path):
|
||||
with open(raw_path, encoding="utf-8") as f:
|
||||
raw = f.read()
|
||||
parsed = parse_codex_raw(raw)
|
||||
merged["codex_probe_trace_steps"] = format_codex_trace_steps(raw)
|
||||
merged["codex_probe_step_count"] = len(parsed["steps"])
|
||||
enriched.append(merged)
|
||||
return enriched
|
||||
|
||||
def resolve_codex_probe_target(
|
||||
self,
|
||||
*,
|
||||
selected_items: list[dict],
|
||||
selected_examples: list[dict],
|
||||
prediction_dir: str,
|
||||
probe: dict,
|
||||
) -> tuple[list[dict], dict[str, str] | None, dict]:
|
||||
"""Resolve the teacher-selected codex probe target and raw trace prefix."""
|
||||
target_id = str(probe.get("probe_target_id", "")).strip()
|
||||
selected_id_set = {str(item["id"]) for item in selected_items}
|
||||
if target_id not in selected_id_set:
|
||||
target_id = str(selected_items[0]["id"])
|
||||
target_item = next(item for item in selected_items if str(item["id"]) == target_id)
|
||||
target_result = next(
|
||||
(row for row in selected_examples if str(row.get("id")) == target_id),
|
||||
None,
|
||||
)
|
||||
max_probe_step = int((target_result or {}).get("codex_probe_step_count", 0))
|
||||
default_probe_step = max_probe_step - 1 if max_probe_step > 1 else max_probe_step
|
||||
probe_after_step = int(probe.get("probe_after_step", default_probe_step))
|
||||
if max_probe_step > 0:
|
||||
probe_after_step = max(0, min(probe_after_step, max_probe_step))
|
||||
else:
|
||||
probe_after_step = 0
|
||||
raw_path = os.path.join(prediction_dir, target_id, "codex_raw.txt")
|
||||
trace_prefix = ""
|
||||
if os.path.exists(raw_path):
|
||||
with open(raw_path, encoding="utf-8") as f:
|
||||
trace_prefix = extract_codex_trace_prefix(f.read(), after_step=probe_after_step)
|
||||
updated_probe = dict(probe)
|
||||
updated_probe["probe_target_id"] = target_id
|
||||
updated_probe["probe_after_step"] = probe_after_step
|
||||
return [target_item], {target_id: trace_prefix}, updated_probe
|
||||
|
||||
def attach_reference_context(
|
||||
self,
|
||||
results: list[dict],
|
||||
@@ -383,14 +307,3 @@ class EnvAdapter(ABC):
|
||||
if prompt is not None:
|
||||
return prompt
|
||||
return self._load_env_prompt("analyst_success")
|
||||
|
||||
def get_deep_probe_prompt(self) -> str | None:
|
||||
return self._load_env_prompt("deep_probe")
|
||||
|
||||
def get_meta_reflect_prompt(self) -> str | None:
|
||||
update_mode = getattr(self, "_cfg", {}).get("skill_update_mode", "patch")
|
||||
if str(update_mode).strip().lower() == "rewrite_from_suggestions":
|
||||
prompt = self._load_env_prompt("meta_reflect_rewrite")
|
||||
if prompt is not None:
|
||||
return prompt
|
||||
return self._load_env_prompt("meta_reflect")
|
||||
|
||||
@@ -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.docvqa.dataloader import DocVQADataLoader
|
||||
from skillopt.envs.docvqa.rollout import run_batch
|
||||
from skillopt.gradient.reflect import run_minibatch_reflect
|
||||
@@ -28,11 +27,7 @@ class DocVQAAdapter(EnvAdapter):
|
||||
edit_budget: int = 4,
|
||||
seed: int = 42,
|
||||
limit: int = 0,
|
||||
image_detail: str = "auto",
|
||||
use_deep_reflect: bool = False,
|
||||
deep_reflect_failures: int = 4,
|
||||
deep_reflect_successes: int = 2,
|
||||
) -> None:
|
||||
image_detail: str = "auto", ) -> None:
|
||||
self.max_turns = max_turns
|
||||
self.exec_timeout = exec_timeout
|
||||
self.workers = workers
|
||||
@@ -41,9 +36,6 @@ class DocVQAAdapter(EnvAdapter):
|
||||
self.minibatch_size = minibatch_size
|
||||
self.edit_budget = edit_budget
|
||||
self.image_detail = image_detail
|
||||
self.use_deep_reflect = use_deep_reflect
|
||||
self.deep_reflect_failures = deep_reflect_failures
|
||||
self.deep_reflect_successes = deep_reflect_successes
|
||||
self.dataloader = DocVQADataLoader(
|
||||
split_dir=split_dir,
|
||||
data_path=data_path,
|
||||
@@ -109,38 +101,6 @@ class DocVQAAdapter(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 document image prompt, 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 visual region, field/table/figure label, OCR text read, candidate answer, and answer-format normalization.",
|
||||
"- Do not ask for exhaustive transcription 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 "docvqa"),
|
||||
"question_preview": str(item.get("question") or "")[:200],
|
||||
"image_path": item.get("image_path", ""),
|
||||
"docId": item.get("docId", ""),
|
||||
"page": item.get("ucsf_document_page_no", ""),
|
||||
},
|
||||
)
|
||||
|
||||
def get_task_types(self) -> list[str]:
|
||||
seen: list[str] = []
|
||||
|
||||
@@ -6,8 +6,8 @@ import time
|
||||
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
|
||||
|
||||
from skillopt.envs.docvqa.evaluator import evaluate
|
||||
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
|
||||
|
||||
|
||||
@@ -112,11 +112,11 @@ def _run_codex_once(
|
||||
images=[item["image_path"]],
|
||||
)
|
||||
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 the attached document image, and answer the DocVQA question.\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,
|
||||
@@ -158,7 +158,7 @@ def process_one(
|
||||
system_prompt = ""
|
||||
user_text = ""
|
||||
conversation: list[dict] = []
|
||||
if is_student_exec_backend():
|
||||
if is_target_exec_backend():
|
||||
from skillopt.model import azure_openai as _llm
|
||||
|
||||
conversation = [
|
||||
@@ -172,7 +172,7 @@ def process_one(
|
||||
pred_dir=os.path.join(out_root, "predictions", item_id),
|
||||
item=item,
|
||||
skill_content=skill_content,
|
||||
model=_llm.STUDENT_DEPLOYMENT,
|
||||
model=_llm.TARGET_DEPLOYMENT,
|
||||
timeout=exec_timeout,
|
||||
image_detail=image_detail,
|
||||
diagnostic_mode=diagnostic_mode if turn == 0 else False,
|
||||
@@ -198,7 +198,7 @@ def process_one(
|
||||
]
|
||||
for turn in range(max_turns):
|
||||
if turn == 0:
|
||||
resp_text, _ = chat_student_messages(
|
||||
resp_text, _ = chat_target_messages(
|
||||
messages=messages,
|
||||
max_completion_tokens=768,
|
||||
retries=5,
|
||||
@@ -212,7 +212,7 @@ def process_one(
|
||||
{"role": "assistant", "content": response},
|
||||
{"role": "user", "content": "Review the same image carefully and answer again. Keep the final answer inside <answer>...</answer>."},
|
||||
]
|
||||
resp_text, _ = chat_student_messages(
|
||||
resp_text, _ = chat_target_messages(
|
||||
messages=refinement_messages,
|
||||
max_completion_tokens=512,
|
||||
retries=5,
|
||||
@@ -230,9 +230,9 @@ def process_one(
|
||||
|
||||
pred_dir = os.path.join(out_root, "predictions", item_id)
|
||||
os.makedirs(pred_dir, exist_ok=True)
|
||||
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_prompt)
|
||||
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_text)
|
||||
|
||||
eval_result = evaluate(response, item.get("answers", []))
|
||||
|
||||
@@ -4,13 +4,12 @@ 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.gradient.reflect import run_minibatch_reflect
|
||||
from skillopt.envs.base import EnvAdapter
|
||||
from skillopt.envs.livemathematicianbench.dataloader import LiveMathematicianBenchDataLoader
|
||||
from skillopt.envs.livemathematicianbench.rollout import run_batch
|
||||
from skillopt.model import get_student_backend
|
||||
from skillopt.model import get_target_backend
|
||||
|
||||
|
||||
class LiveMathematicianBenchAdapter(EnvAdapter):
|
||||
@@ -61,11 +60,7 @@ class LiveMathematicianBenchAdapter(EnvAdapter):
|
||||
limit: int = 0,
|
||||
shuffle_choices: bool = True,
|
||||
use_theorem: bool = False,
|
||||
use_sketch: bool = False,
|
||||
use_deep_reflect: bool = False,
|
||||
deep_reflect_failures: int = 4,
|
||||
deep_reflect_successes: int = 2,
|
||||
) -> None:
|
||||
use_sketch: bool = False, ) -> None:
|
||||
self.max_turns = max_turns
|
||||
self.exec_timeout = exec_timeout
|
||||
self.workers = workers
|
||||
@@ -75,9 +70,6 @@ class LiveMathematicianBenchAdapter(EnvAdapter):
|
||||
self.edit_budget = edit_budget
|
||||
self.use_theorem = use_theorem
|
||||
self.use_sketch = use_sketch
|
||||
self.use_deep_reflect = use_deep_reflect
|
||||
self.deep_reflect_failures = deep_reflect_failures
|
||||
self.deep_reflect_successes = deep_reflect_successes
|
||||
self.dataloader = LiveMathematicianBenchDataLoader(
|
||||
split_dir=split_dir,
|
||||
data_path=data_path,
|
||||
@@ -161,122 +153,6 @@ class LiveMathematicianBenchAdapter(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")
|
||||
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 if isinstance(env_manager, list) else None,
|
||||
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_reference_context(selected_results, selected_items)
|
||||
if codex_backend:
|
||||
selected_examples = self.attach_codex_probe_context(selected_examples, prediction_dir)
|
||||
selected_metadata = []
|
||||
theorem_count = 0
|
||||
sketch_count = 0
|
||||
for item in selected_items:
|
||||
meta = self.get_reference_metadata(item)
|
||||
if "theorem" in meta["fields"]:
|
||||
theorem_count += 1
|
||||
if "sketch" in meta["fields"]:
|
||||
sketch_count += 1
|
||||
selected_metadata.append({
|
||||
"id": str(item["id"]),
|
||||
"task_type": str(item.get("theorem_type", ["math_mcq"])[0] if item.get("theorem_type") else "math_mcq"),
|
||||
"reference_fields": meta["fields"],
|
||||
"reference_preview": meta["preview"],
|
||||
})
|
||||
|
||||
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"reference_fields=theorem({theorem_count}/{len(selected_items)}),"
|
||||
f"sketch({sketch_count}/{len(selected_items)})"
|
||||
)
|
||||
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,
|
||||
)
|
||||
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,
|
||||
)
|
||||
probe_record = {
|
||||
**probe,
|
||||
"reference_summary": {
|
||||
"selected_count": len(selected_items),
|
||||
"field_counts": {
|
||||
"theorem": theorem_count,
|
||||
"sketch": sketch_count,
|
||||
},
|
||||
},
|
||||
"selected_examples": selected_metadata,
|
||||
}
|
||||
with open(os.path.join(deep_dir, "probe.json"), "w", encoding="utf-8") as f:
|
||||
json.dump(probe_record, f, ensure_ascii=False, indent=2)
|
||||
deep_results = run_batch(
|
||||
items=selected_items,
|
||||
out_root=rollout_dir,
|
||||
skill_content=skill_content,
|
||||
max_turns=self.max_turns,
|
||||
workers=min(self.workers, max(len(selected_items), 1)),
|
||||
use_theorem=self.use_theorem,
|
||||
use_sketch=self.use_sketch,
|
||||
diagnostic_mode=True,
|
||||
diagnostic_instruction=probe["probe_instruction"],
|
||||
diagnostic_trace_context_by_id=diagnostic_trace_context_by_id,
|
||||
task_timeout=self.exec_timeout,
|
||||
)
|
||||
deep_results = self.attach_reference_context(deep_results, selected_items)
|
||||
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 self.dataloader.get_task_types()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
You are an expert failure-analysis agent for theorem-grounded mathematical multiple-choice questions.
|
||||
|
||||
You will be given MULTIPLE failed trajectories from a single minibatch and the current skill document.
|
||||
Each trajectory includes the student's response and an evaluation result showing the predicted option
|
||||
Each trajectory includes the target's response and an evaluation result showing the predicted option
|
||||
versus the correct option.
|
||||
|
||||
Your job is to identify COMMON reasoning failures across the batch and propose concise skill edits.
|
||||
|
||||
@@ -7,8 +7,8 @@ import time
|
||||
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
|
||||
|
||||
from skillopt.envs.livemathematicianbench.evaluator import evaluate
|
||||
from skillopt.model import chat_student, 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, 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
|
||||
|
||||
def _build_system(skill_content: str) -> str:
|
||||
@@ -95,11 +95,11 @@ def _run_codex_once(
|
||||
work_dir = os.path.join(pred_dir, "codex_exec")
|
||||
prepare_workspace(work_dir=work_dir, skill_md=skill_md, task_text=task_text)
|
||||
prompt = (
|
||||
"Use the `skillopt-student` skill available in this workspace.\n"
|
||||
"Use the `skillopt-target` skill available in this workspace.\n"
|
||||
"Read `task.md` and solve the multiple-choice problem.\n"
|
||||
"Output only the final choice label inside <answer>...</answer>."
|
||||
)
|
||||
final_message, raw = run_student_exec(
|
||||
final_message, raw = run_target_exec(
|
||||
work_dir=work_dir,
|
||||
prompt=prompt,
|
||||
model=model,
|
||||
@@ -143,7 +143,7 @@ def process_one(
|
||||
pred_dir = os.path.join(out_root, "predictions", item_id)
|
||||
os.makedirs(pred_dir, exist_ok=True)
|
||||
|
||||
if is_student_exec_backend():
|
||||
if is_target_exec_backend():
|
||||
from skillopt.model import azure_openai as _llm
|
||||
|
||||
conversation: list[dict] = []
|
||||
@@ -155,7 +155,7 @@ def process_one(
|
||||
pred_dir=pred_dir,
|
||||
skill_content=skill_content,
|
||||
item=item,
|
||||
model=_llm.STUDENT_DEPLOYMENT,
|
||||
model=_llm.TARGET_DEPLOYMENT,
|
||||
timeout=exec_timeout,
|
||||
use_theorem=use_theorem,
|
||||
use_sketch=use_sketch,
|
||||
@@ -172,9 +172,9 @@ def process_one(
|
||||
result["agent_ok"] = True
|
||||
result["n_turns"] = len(conversation)
|
||||
|
||||
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)
|
||||
|
||||
eval_result = evaluate(response, item["correct_choice"], item["choices"])
|
||||
@@ -216,7 +216,7 @@ def process_one(
|
||||
|
||||
for turn in range(max_turns):
|
||||
if turn == 0:
|
||||
resp_text, _ = chat_student(
|
||||
resp_text, _ = chat_target(
|
||||
system=system,
|
||||
user=user,
|
||||
max_completion_tokens=16384,
|
||||
@@ -230,7 +230,7 @@ def process_one(
|
||||
"Re-evaluate the exact option wording. If needed, correct it. "
|
||||
"Output only the final choice label inside <answer>...</answer>."
|
||||
)
|
||||
resp_text, _ = chat_student(
|
||||
resp_text, _ = chat_target(
|
||||
system=system,
|
||||
user=refinement,
|
||||
max_completion_tokens=16384,
|
||||
@@ -247,9 +247,9 @@ def process_one(
|
||||
result["agent_ok"] = True
|
||||
result["n_turns"] = len(conversation)
|
||||
|
||||
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)
|
||||
|
||||
eval_result = evaluate(response, item["correct_choice"], item["choices"])
|
||||
|
||||
@@ -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] = []
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -4,13 +4,12 @@ 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.searchqa.dataloader import SearchQADataLoader
|
||||
from skillopt.envs.searchqa.rollout import run_batch
|
||||
from skillopt.gradient.reflect import run_minibatch_reflect
|
||||
from skillopt.model import get_student_backend
|
||||
from skillopt.model import get_target_backend
|
||||
|
||||
|
||||
class SearchQAAdapter(EnvAdapter):
|
||||
@@ -32,11 +31,7 @@ class SearchQAAdapter(EnvAdapter):
|
||||
minibatch_size: int = 8,
|
||||
edit_budget: int = 4,
|
||||
seed: int = 42,
|
||||
limit: int = 0,
|
||||
use_deep_reflect: bool = False,
|
||||
deep_reflect_failures: int = 4,
|
||||
deep_reflect_successes: int = 2,
|
||||
) -> None:
|
||||
limit: int = 0, ) -> None:
|
||||
self.max_turns = max_turns
|
||||
self.exec_timeout = exec_timeout
|
||||
self.workers = workers
|
||||
@@ -44,9 +39,6 @@ class SearchQAAdapter(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 = SearchQADataLoader(
|
||||
split_dir=split_dir,
|
||||
data_path=data_path,
|
||||
@@ -128,121 +120,6 @@ class SearchQAAdapter(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"]),
|
||||
"question_preview": str(item.get("question") or "")[:200],
|
||||
"has_context": bool(str(item.get("context") or "").strip()),
|
||||
"n_gold_answers": len(item.get("answers") 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=no_reference_probe"
|
||||
)
|
||||
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=[
|
||||
"- There is no hidden reference block. Use only the question, provided context, the student's output, and the 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 likely evidence span, top candidate and runner-up, decisive clue, or a few short intermediate conclusions.",
|
||||
"- Do not ask for exhaustive copying of the context or a full chain-of-thought.",
|
||||
"- 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 ["qa"]
|
||||
|
||||
@@ -16,8 +16,8 @@ import time
|
||||
import traceback
|
||||
from concurrent.futures import FIRST_COMPLETED, ThreadPoolExecutor, wait
|
||||
|
||||
from skillopt.model import chat_student, 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, 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.searchqa.evaluator import evaluate
|
||||
|
||||
@@ -123,11 +123,11 @@ def _run_codex_once(
|
||||
task_text=task_text,
|
||||
)
|
||||
prompt = (
|
||||
"Use the `skillopt-student` skill available in this workspace.\n"
|
||||
"Use the `skillopt-target` skill available in this workspace.\n"
|
||||
"Read `task.md` and answer the SearchQA question.\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,
|
||||
@@ -192,7 +192,7 @@ def process_one(
|
||||
pred_dir = os.path.join(out_root, "predictions", item_id)
|
||||
os.makedirs(pred_dir, exist_ok=True)
|
||||
|
||||
if is_student_exec_backend():
|
||||
if is_target_exec_backend():
|
||||
from skillopt.model import azure_openai as _llm
|
||||
|
||||
conversation: list[dict] = []
|
||||
@@ -205,7 +205,7 @@ def process_one(
|
||||
skill_content=skill_content,
|
||||
question=question,
|
||||
context=context,
|
||||
model=_llm.STUDENT_DEPLOYMENT,
|
||||
model=_llm.TARGET_DEPLOYMENT,
|
||||
timeout=exec_timeout,
|
||||
diagnostic_mode=diagnostic_mode if turn == 0 else False,
|
||||
diagnostic_instruction=diagnostic_instruction if turn == 0 else "",
|
||||
@@ -220,9 +220,9 @@ def process_one(
|
||||
result["agent_ok"] = True
|
||||
result["n_turns"] = len(conversation)
|
||||
|
||||
with open(os.path.join(pred_dir, "student_system_prompt.txt"), "w") as f:
|
||||
with open(os.path.join(pred_dir, "target_system_prompt.txt"), "w") as f:
|
||||
f.write(system)
|
||||
with open(os.path.join(pred_dir, "student_user_prompt.txt"), "w") as f:
|
||||
with open(os.path.join(pred_dir, "target_user_prompt.txt"), "w") as f:
|
||||
f.write(user)
|
||||
with open(os.path.join(pred_dir, "conversation.json"), "w") as f:
|
||||
json.dump(conversation, f, ensure_ascii=False, indent=2)
|
||||
@@ -266,7 +266,7 @@ def process_one(
|
||||
|
||||
for turn in range(max_turns):
|
||||
if turn == 0:
|
||||
resp_text, _ = chat_student(
|
||||
resp_text, _ = chat_target(
|
||||
system=system, user=user,
|
||||
max_completion_tokens=512,
|
||||
retries=5, stage="rollout",
|
||||
@@ -279,7 +279,7 @@ def process_one(
|
||||
f"If correct, repeat it. If wrong, provide a corrected answer.\n"
|
||||
f"Use <answer>...</answer> tags for your final answer."
|
||||
)
|
||||
resp_text, _ = chat_student(
|
||||
resp_text, _ = chat_target(
|
||||
system=system, user=refinement,
|
||||
max_completion_tokens=512,
|
||||
retries=5, stage="rollout",
|
||||
@@ -297,9 +297,9 @@ def process_one(
|
||||
result["n_turns"] = len(conversation)
|
||||
|
||||
# Save conversation
|
||||
with open(os.path.join(pred_dir, "student_system_prompt.txt"), "w") as f:
|
||||
with open(os.path.join(pred_dir, "target_system_prompt.txt"), "w") as f:
|
||||
f.write(system)
|
||||
with open(os.path.join(pred_dir, "student_user_prompt.txt"), "w") as f:
|
||||
with open(os.path.join(pred_dir, "target_user_prompt.txt"), "w") as f:
|
||||
f.write(user)
|
||||
with open(os.path.join(pred_dir, "conversation.json"), "w") as f:
|
||||
json.dump(conversation, f, ensure_ascii=False, indent=2)
|
||||
|
||||
@@ -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