Files
SkillCompiler/scripts/dynamic_compile/deep/pipeline.py
T
2026-09-04 14:58:42 +08:00

827 lines
33 KiB
Python

"""Deep 编译流水线编排。"""
from __future__ import annotations
import shutil
import sys
import tempfile
import uuid
from dataclasses import asdict, replace
from pathlib import Path
from typing import Any
from scripts.dynamic_compile.fast.models import RolloutTrace
from scripts.dynamic_compile.fast.storage import (
atomic_write_json,
atomic_write_jsonl,
atomic_write_text,
load_json,
package_hash,
read_jsonl,
sha256_file,
sha256_text,
)
from scripts.dynamic_compile.fast.optimization.analyzer import SemanticClient
from .adapters.benchflow import (
BenchFlowInput,
SkillsBenchDevelopmentRolloutRunner,
load_benchflow_input,
)
from .adapters.semantic import DeepAnalyzer, valid_evidence
from .core.models import (
CellScore,
Coordinate,
DIMENSIONS,
LocalEdit,
ScoreMatrix,
SkillUnit,
)
from .core.markdown import (
parse_paragraphs,
parse_sections,
preserve_unit_boundary,
replace_unit_text,
validate_edit,
)
ROLLOUTS = 3
MAX_SECTION_ITERATIONS = 6
MAX_PARAGRAPH_ITERATIONS = 3
MAX_EDIT_GENERATION_ATTEMPTS = 3
GAP_THRESHOLD = 0.375
REJECTION_LIMIT = 2
MAX_PARALLEL = 3
DEFAULT_MODEL = "ali/deepseek-v4-pro-0813"
def _log(message: str) -> None:
print(f"[deep] {message}", file=sys.stderr, flush=True)
def _trace_from_dict(value: dict[str, Any]) -> RolloutTrace:
return RolloutTrace(**value)
def _column_to_dict(column: dict[str, CellScore]) -> dict[str, Any]:
return {unit_id: asdict(cell) for unit_id, cell in column.items()}
def _column_from_dict(
value: dict[str, Any], traces: list[RolloutTrace]
) -> dict[str, CellScore]:
return {
unit_id: CellScore(
float(cell["score"]), valid_evidence(cell.get("evidence"), traces), str(cell["reason"])
)
for unit_id, cell in value.items()
}
class DeepLoop:
def __init__(
self,
run_dir: Path,
analyzer: DeepAnalyzer | None = None,
runner: Any | None = None,
):
self.run_dir = run_dir.resolve()
self.state_path = self.run_dir / "run.json"
state = load_json(self.state_path)
if not isinstance(state, dict):
raise ValueError(f"invalid or missing run state: {self.state_path}")
self.state = state
self.temp = self.run_dir / ".tmp"
self.current = self.temp / "current"
self.model = state.get("model", state.get("semantic_model", DEFAULT_MODEL))
self.analyzer = analyzer or DeepAnalyzer(
SemanticClient(self.model), MAX_PARALLEL,
)
context = BenchFlowInput(
state["task"]["name"],
Path(state["task"]["directory"]),
state["task"]["agent"],
state["task"]["model"],
state["task"]["prompt"],
[],
)
self.runner = runner or SkillsBenchDevelopmentRolloutRunner(
context,
Path(tempfile.gettempdir()) / "skill-compiler-deep" / state["run_id"],
MAX_PARALLEL,
archive_root=self.run_dir / "rollouts",
)
@classmethod
def create(
cls,
skill: Path,
traces: Path,
output: Path | None = None,
*,
model: str = DEFAULT_MODEL,
analyzer: DeepAnalyzer | None = None,
runner: Any | None = None,
) -> "DeepLoop":
skill = skill.resolve()
if not (skill / "SKILL.md").is_file():
raise ValueError("--skill must be a skill package containing SKILL.md")
context = load_benchflow_input(traces)
target = (output or skill.parent / f"{skill.name}-deep").resolve()
if target.exists() and any(target.iterdir()):
raise ValueError(f"deep run directory is not empty: {target}")
target.mkdir(parents=True, exist_ok=True)
shutil.copytree(skill, target / "S_fast")
(target / ".tmp").mkdir()
(target / "levels").mkdir()
shutil.copytree(target / "S_fast", target / ".tmp" / "current")
atomic_write_jsonl(target / "input-traces.jsonl", [asdict(trace) for trace in context.traces])
state = {
"run_id": uuid.uuid4().hex[:12],
"status": "created",
"model": model,
"skill_name": skill.name,
"task": {
"name": context.task_name,
"directory": str(context.task_dir),
"agent": context.agent,
"model": context.model,
"prompt": context.prompt,
},
"current_rollouts": str(traces.resolve()),
"current_rollout_skill_sha256": sha256_file(skill / "SKILL.md"),
"levels": {},
}
atomic_write_json(target / "run.json", state)
return cls(target, analyzer=analyzer, runner=runner)
def _save(self) -> None:
atomic_write_json(self.state_path, self.state)
def _prompt(self) -> str:
return str(self.state["task"]["prompt"])
def _current_text(self) -> str:
return (self.current / "SKILL.md").read_text(encoding="utf-8")
def _load_or_rollout(
self,
package: Path,
trace_path: Path,
batch_id: str,
seed: list[RolloutTrace] | None = None,
) -> list[RolloutTrace]:
if trace_path.is_file():
return [_trace_from_dict(item) for item in read_jsonl(trace_path)]
if seed is not None:
traces = seed
else:
_log(f"Starting {ROLLOUTS} rollouts for {batch_id}")
traces = self.runner.run_batch(
package,
self._prompt(),
batch_id,
str(self.state["task"]["name"]),
ROLLOUTS,
progress=lambda done, total, trace_id: _log(
f"Rollout {done}/{total} complete: {trace_id}"
),
)
atomic_write_jsonl(trace_path, [asdict(trace) for trace in traces])
return traces
def _load_or_matrix(
self,
path: Path,
level: str,
units: list[SkillUnit],
traces: list[RolloutTrace],
) -> ScoreMatrix:
value = load_json(path)
if isinstance(value, dict):
return ScoreMatrix.from_dict(value)
columns_dir = path.parent / "matrix-columns"
cached_columns: dict[str, dict[str, CellScore]] = {}
for dimension in DIMENSIONS:
cached = load_json(columns_dir / f"{dimension.lower().replace(' ', '-')}.json")
if isinstance(cached, dict):
cached_columns[dimension] = _column_from_dict(cached, traces)
def save_column(dimension: str, column: dict[str, CellScore]) -> None:
atomic_write_json(
columns_dir / f"{dimension.lower().replace(' ', '-')}.json",
_column_to_dict(column),
)
_log(f"Scoring full {level} matrix with {self.model}")
matrix = self.analyzer.score_matrix(
self._current_text(), self._prompt(), units, traces, level,
existing_columns=cached_columns,
result_callback=save_column,
)
atomic_write_json(path, matrix.to_dict())
return matrix
def _refresh_matrix(
self,
level: str,
units: list[SkillUnit],
traces: list[RolloutTrace],
) -> ScoreMatrix:
level_state = self.state["levels"][level]
number = int(level_state.get("refreshes", 0)) + 1
path = (
self.run_dir
/ "levels"
/ level
/ "refreshes"
/ f"refresh-{number:02d}"
/ "matrix.json"
)
_log(f"Refreshing full {level} matrix")
matrix = self._load_or_matrix(path, level, units, traces)
atomic_write_json(self.run_dir / "levels" / level / "matrix.json", matrix.to_dict())
level_state["refreshes"] = number
level_state.pop("active_dimension", None)
self._save()
return matrix
def _decisions(self, level: str | None = None) -> list[dict[str, Any]]:
roots = (
[self.run_dir / "levels" / level]
if level else list((self.run_dir / "levels").glob("*"))
)
decisions = []
for root in roots:
for path in sorted((root / "iterations").glob("iteration-*/decision.json")):
value = load_json(path)
if isinstance(value, dict):
decisions.append(value)
return decisions
def _rejected(self, coordinate: Coordinate) -> list[dict[str, Any]]:
return [
decision["rejected_edit"]
for decision in self._decisions()
if not decision["accepted"]
and decision["rejected_edit"]["unit_id"] == coordinate.unit_id
and decision["rejected_edit"]["dimension"] == coordinate.dimension
]
def _exhausted(self, level: str) -> set[tuple[str, str]]:
counts: dict[tuple[str, str], int] = {}
for decision in self._decisions(level):
if decision["accepted"]:
continue
rejected = decision["rejected_edit"]
key = (rejected["unit_id"], rejected["dimension"])
counts[key] = counts.get(key, 0) + 1
return {key for key, count in counts.items() if count >= REJECTION_LIMIT}
@staticmethod
def _block_dimension(level_state: dict[str, Any], dimension: str) -> None:
blocked = set(level_state.get("blocked_dimensions", []))
blocked.add(dimension)
level_state["blocked_dimensions"] = sorted(blocked)
@staticmethod
def _candidate_units(
units: list[SkillUnit], target: SkillUnit, new_text: str
) -> list[SkillUnit]:
delta = len(new_text) - len(target.text)
updated = []
for unit in units:
value = replace(unit)
if unit.unit_id == target.unit_id:
value.text = new_text
value.end = value.start + len(new_text)
elif unit.start >= target.end:
value.start += delta
value.end += delta
updated.append(value)
return updated
def _apply_edit(
self,
unit: SkillUnit,
edit: LocalEdit,
candidate: Path,
section_depth: int | None,
) -> None:
self._validate_edit_candidate(unit, edit, section_depth)
text = self._current_text()
changed = replace_unit_text(text, unit, edit.new_text)
temporary = candidate.with_name(f".{candidate.name}.{uuid.uuid4().hex}.tmp")
shutil.copytree(self.current, temporary)
atomic_write_text(temporary / "SKILL.md", changed)
if candidate.exists():
shutil.rmtree(candidate)
candidate.parent.mkdir(parents=True, exist_ok=True)
temporary.rename(candidate)
def _validate_edit_candidate(
self,
unit: SkillUnit,
edit: LocalEdit,
section_depth: int | None,
) -> None:
"""Validate a local edit against both unit and whole-document invariants."""
validate_edit(unit, edit.new_text, section_depth)
text = self._current_text()
if text[unit.start:unit.end] != unit.text:
raise ValueError("target unit no longer matches current SKILL.md")
changed = replace_unit_text(text, unit, edit.new_text)
before = [(item.heading, item.heading_depth) for item in parse_sections(text)]
after = [(item.heading, item.heading_depth) for item in parse_sections(changed)]
if before != after:
raise ValueError("local edit changed section boundaries")
@staticmethod
def _validation_feedback(
attempts: list[dict[str, Any]],
) -> list[dict[str, Any]]:
return [
{
"edit_summary": str(item.get("edit_summary", "invalid generated edit")),
"reject_reason": f"structural_validation_failed: {item['error']}",
"new_text_hash": str(item.get("new_text_hash", "")),
}
for item in attempts
]
def _valid_edit_or_rejection(
self,
*,
level: str,
number: int,
iteration_dir: Path,
unit: SkillUnit,
coordinate: Coordinate,
cell: CellScore,
section_depth: int | None,
) -> tuple[LocalEdit | None, bool, list[dict[str, Any]]]:
"""Load or generate a valid edit, feeding structural failures back to the model."""
edit_path = iteration_dir / "edit.json"
attempts_path = iteration_dir / "edit-attempts.json"
attempts_value = load_json(attempts_path, [])
attempts = attempts_value if isinstance(attempts_value, list) else []
cached_value = load_json(edit_path)
if isinstance(cached_value, dict):
try:
cached = LocalEdit.from_dict(cached_value)
cached.new_text = preserve_unit_boundary(unit, cached.new_text)
self._validate_edit_candidate(unit, cached, section_depth)
return cached, False, attempts
except ValueError as exc:
text = str(cached_value.get("new_text", ""))
attempts.append({
"source": "cached",
"error": str(exc),
"edit_summary": str(cached_value.get("edit_summary", "")),
"new_text_hash": sha256_text(text) if text else "",
})
atomic_write_json(attempts_path, attempts)
_log(
f"{level} iteration {number}: cached local edit is invalid: {exc}; "
"regenerating"
)
for attempt in range(1, MAX_EDIT_GENERATION_ATTEMPTS + 1):
_log(
f"{level} iteration {number}: generating local edit for "
f"{coordinate.unit_id}/{coordinate.dimension} "
f"(attempt {attempt}/{MAX_EDIT_GENERATION_ATTEMPTS})"
)
feedback = self._rejected(coordinate) + self._validation_feedback(attempts)
edit: LocalEdit | None = None
try:
edit = self.analyzer.generate_edit(
coordinate,
unit,
cell,
feedback,
self._prompt(),
)
edit.new_text = preserve_unit_boundary(unit, edit.new_text)
self._validate_edit_candidate(unit, edit, section_depth)
except ValueError as exc:
text = edit.new_text if edit is not None else ""
attempts.append({
"source": "generated",
"generation_attempt": attempt,
"error": str(exc),
"edit_summary": edit.edit_summary if edit is not None else "",
"new_text_hash": sha256_text(text) if text else "",
})
atomic_write_json(attempts_path, attempts)
_log(
f"{level} iteration {number}: local edit validation failed "
f"(attempt {attempt}/{MAX_EDIT_GENERATION_ATTEMPTS}): {exc}"
)
continue
assert edit is not None
atomic_write_json(edit_path, asdict(edit))
return edit, True, attempts
return None, True, attempts
def _commit_iteration(
self,
level: str,
number: int,
iteration_dir: Path,
matrix: ScoreMatrix,
current_trace_path: Path,
) -> ScoreMatrix:
decision = load_json(iteration_dir / "decision.json")
if not isinstance(decision, dict):
raise ValueError("missing iteration decision")
if decision["accepted"]:
candidate = iteration_dir / "candidate" / self.state["skill_name"]
replacement = self.temp / "next-current"
shutil.rmtree(replacement, ignore_errors=True)
shutil.copytree(candidate, replacement)
shutil.rmtree(self.current)
replacement.rename(self.current)
matrix = ScoreMatrix.from_dict(decision["matrix_after"])
atomic_write_json(self.run_dir / "levels" / level / "matrix.json", matrix.to_dict())
candidate_traces = read_jsonl(iteration_dir / "candidate-traces.jsonl")
atomic_write_jsonl(current_trace_path, candidate_traces)
rollout_output = decision.get("rollout_output")
rollout_skill_sha256 = decision.get("rollout_skill_sha256")
if isinstance(rollout_output, str) and isinstance(rollout_skill_sha256, str):
self.state["current_rollouts"] = rollout_output
self.state["current_rollout_skill_sha256"] = rollout_skill_sha256
else:
self.state.pop("current_rollouts", None)
self.state.pop("current_rollout_skill_sha256", None)
level_state = self.state["levels"][level]
if int(level_state.get("iterations", 0)) < number:
level_state["iterations"] = number
self._save()
return matrix
def _run_iteration(
self,
level: str,
number: int,
level_dir: Path,
matrix: ScoreMatrix,
coordinate: Coordinate,
current_trace_path: Path,
section_depth: int | None,
) -> ScoreMatrix:
iteration_dir = level_dir / "iterations" / f"iteration-{number:02d}"
iteration_dir.mkdir(parents=True, exist_ok=True)
unit = matrix.unit(coordinate.unit_id)
edit, regenerated, validation_attempts = self._valid_edit_or_rejection(
level=level,
number=number,
iteration_dir=iteration_dir,
unit=unit,
coordinate=coordinate,
cell=matrix.columns[coordinate.dimension][coordinate.unit_id],
section_depth=section_depth,
)
if edit is None:
decision = {
"coordinate": asdict(coordinate),
"accepted": False,
"reason": "edit_validation_exhausted",
"target_delta": 0.0,
"validation_attempts": validation_attempts,
"rejected_edit": {
"unit_id": coordinate.unit_id,
"dimension": coordinate.dimension,
"edit_summary": (
"Could not generate a structurally valid local edit after "
f"{MAX_EDIT_GENERATION_ATTEMPTS} attempts."
),
"score_change": 0.0,
"reject_reason": "edit_validation_exhausted",
"new_text_hash": str(
validation_attempts[-1].get("new_text_hash", "")
) if validation_attempts else "",
},
}
atomic_write_json(iteration_dir / "decision.json", decision)
_log(
f"{level} iteration {number}: local edit validation exhausted; "
"recording rejection and continuing"
)
return self._commit_iteration(
level, number, iteration_dir, matrix, current_trace_path
)
edit_hash = sha256_text(edit.new_text)
if any(item["new_text_hash"] == edit_hash for item in self._rejected(coordinate)):
decision = {
"coordinate": asdict(coordinate),
"accepted": False,
"reason": "exact_duplicate_rejected_edit",
"target_delta": 0.0,
"rejected_edit": {
"unit_id": coordinate.unit_id,
"dimension": coordinate.dimension,
"edit_summary": edit.edit_summary,
"score_change": 0.0,
"reject_reason": "exact_duplicate_rejected_edit",
"new_text_hash": edit_hash,
},
}
atomic_write_json(iteration_dir / "decision.json", decision)
return self._commit_iteration(level, number, iteration_dir, matrix, current_trace_path)
candidate = iteration_dir / "candidate" / self.state["skill_name"]
if regenerated:
shutil.rmtree(candidate, ignore_errors=True)
for stale in (
iteration_dir / "candidate-traces.jsonl",
iteration_dir / "comparison.json",
):
if stale.exists():
stale.unlink()
if not (candidate / "SKILL.md").is_file():
self._apply_edit(unit, edit, candidate, section_depth)
candidate_units = self._candidate_units(matrix.units, unit, edit.new_text)
candidate_skill_sha256 = sha256_file(candidate / "SKILL.md")
batch_id = (
f"{self.state['run_id']}-deep-{level}-i{number:02d}-"
f"{candidate_skill_sha256[:12]}"
)
traces = self._load_or_rollout(
candidate,
iteration_dir / "candidate-traces.jsonl",
batch_id,
)
comparison_path = iteration_dir / "comparison.json"
comparison = load_json(comparison_path)
if not isinstance(comparison, dict):
_log(
f"{level} iteration {number}: comparing "
f"{coordinate.unit_id}/{coordinate.dimension}"
)
incumbent_traces = [
_trace_from_dict(item) for item in read_jsonl(current_trace_path)
]
comparison = self.analyzer.compare_cell(
self._prompt(),
unit,
next(item for item in candidate_units if item.unit_id == unit.unit_id),
incumbent_traces,
traces,
coordinate.dimension,
)
incumbent_timeout_rate = (
sum(trace.timed_out is True for trace in incumbent_traces)
/ len(incumbent_traces)
)
candidate_timeout_rate = (
sum(trace.timed_out is True for trace in traces) / len(traces)
)
if (
candidate_timeout_rate > incumbent_timeout_rate
and "timeout" not in comparison["runtime_regressions"]
):
comparison["runtime_regressions"].append("timeout")
atomic_write_json(comparison_path, comparison)
delta = float(comparison["candidate_score"]) - float(
comparison["incumbent_score"]
)
if not comparison["task_relevant"]:
accepted, reason = False, "target_unit_not_task_relevant"
elif comparison["runtime_regressions"]:
accepted = False
reason = "runtime_regressed:" + ",".join(comparison["runtime_regressions"])
elif delta < 0.5:
accepted, reason = False, "target_cell_did_not_improve"
else:
accepted, reason = True, "target_improved_without_runtime_regression"
decision: dict[str, Any] = {
"coordinate": asdict(coordinate),
"accepted": accepted,
"reason": reason,
"target_delta": delta,
"rollout_skill_sha256": candidate_skill_sha256,
}
artifacts_dir = getattr(self.runner, "artifacts_dir", None)
rollout_output = artifacts_dir(batch_id) if callable(artifacts_dir) else None
if isinstance(rollout_output, Path):
decision["rollout_output"] = str(rollout_output.resolve())
if accepted:
updated = ScoreMatrix(matrix.level, candidate_units, dict(matrix.columns))
updated.columns[coordinate.dimension] = dict(
matrix.columns[coordinate.dimension]
)
updated.columns[coordinate.dimension][coordinate.unit_id] = CellScore(
float(comparison["candidate_score"]),
list(comparison["candidate_evidence"]),
str(comparison["reason"]),
)
decision["matrix_after"] = updated.to_dict()
else:
decision["rejected_edit"] = {
"unit_id": coordinate.unit_id,
"dimension": coordinate.dimension,
"edit_summary": edit.edit_summary,
"score_change": delta,
"reject_reason": reason,
"new_text_hash": edit_hash,
}
atomic_write_json(iteration_dir / "decision.json", decision)
return self._commit_iteration(level, number, iteration_dir, matrix, current_trace_path)
def _run_level(
self,
level: str,
units: list[SkillUnit],
seed_traces: list[RolloutTrace] | None = None,
section_depth: int | None = None,
) -> tuple[ScoreMatrix, list[RolloutTrace], Coordinate | None]:
level_dir = self.run_dir / "levels" / level
level_dir.mkdir(parents=True, exist_ok=True)
level_state = self.state["levels"].setdefault(level, {
"iterations": 0, "completed": False,
})
current_trace_path = level_dir / "current-traces.jsonl"
traces = self._load_or_rollout(
self.current,
current_trace_path,
f"{self.state['run_id']}-deep-{level}-initial",
seed=seed_traces,
)
matrix = self._load_or_matrix(level_dir / "matrix.json", level, units, traces)
if level_state.get("completed"):
exhausted_value = level_state.get("exhausted_coordinate")
exhausted = Coordinate(**exhausted_value) if isinstance(exhausted_value, dict) else None
return matrix, traces, exhausted
exhausted_coordinate: Coordinate | None = None
max_iterations = (
MAX_SECTION_ITERATIONS if level == "section" else MAX_PARAGRAPH_ITERATIONS
)
while int(level_state["iterations"]) < max_iterations:
excluded = self._exhausted(level)
excluded.update(
(unit.unit_id, dimension)
for dimension in level_state.get("blocked_dimensions", [])
for unit in matrix.units
)
pending_number = int(level_state["iterations"]) + 1
pending_dir = level_dir / "iterations" / f"iteration-{pending_number:02d}"
pending_decision = load_json(pending_dir / "decision.json")
if isinstance(pending_decision, dict):
pending_coordinate = Coordinate(**pending_decision["coordinate"])
level_state.setdefault("active_dimension", pending_coordinate.dimension)
matrix = self._commit_iteration(
level, pending_number, pending_dir, matrix, current_trace_path
)
traces = [_trace_from_dict(item) for item in read_jsonl(current_trace_path)]
if len(self._rejected(pending_coordinate)) >= REJECTION_LIMIT:
exhausted_coordinate = pending_coordinate
level_state["stop_reason"] = "coordinate_exhausted"
level_state["exhausted_coordinate"] = asdict(pending_coordinate)
break
if matrix.select_coordinate(
GAP_THRESHOLD, level_state.get("active_dimension"), excluded
) is None:
matrix = self._refresh_matrix(level, matrix.units, traces)
continue
active_dimension = level_state.get("active_dimension")
coordinate = matrix.select_coordinate(
GAP_THRESHOLD, active_dimension, excluded
)
if coordinate is None:
if active_dimension is None:
level_state["stop_reason"] = (
"normalized_gap_converged"
if max(matrix.normalized_gaps().values(), default=0.0) <= GAP_THRESHOLD
else "available_coordinates_exhausted"
)
break
matrix = self._refresh_matrix(level, matrix.units, traces)
continue
if active_dimension is None:
level_state["active_dimension"] = coordinate.dimension
self._save()
number = int(level_state["iterations"]) + 1
matrix = self._run_iteration(
level, number, level_dir, matrix, coordinate,
current_trace_path, section_depth,
)
traces = [_trace_from_dict(item) for item in read_jsonl(current_trace_path)]
if len(self._rejected(coordinate)) >= REJECTION_LIMIT:
exhausted_coordinate = coordinate
level_state["stop_reason"] = "coordinate_exhausted"
level_state["exhausted_coordinate"] = asdict(coordinate)
break
else:
decisions = self._decisions(level)
last = decisions[-1] if decisions else {}
if matrix.select_coordinate(GAP_THRESHOLD) is None:
level_state["stop_reason"] = "normalized_gap_converged"
else:
level_state["stop_reason"] = (
"max_iterations_after_accept" if last.get("accepted") else "max_iterations"
)
level_state["completed"] = True
self._save()
return matrix, traces, exhausted_coordinate
def drive(self) -> Path:
if self.state.get("status") == "complete" and (self.run_dir / "S_final").is_dir():
return self.run_dir / "S_final"
_log(f"Deep Loop start/resume: {self.run_dir}")
input_traces = [
_trace_from_dict(item) for item in read_jsonl(self.run_dir / "input-traces.jsonl")
]
while True:
section_units = parse_sections(self._current_text())
section_matrix, traces, exhausted = self._run_level(
"section", section_units, seed_traces=input_traces
)
if exhausted is None:
break
target_score = section_matrix.columns[exhausted.dimension][exhausted.unit_id].score
current_sections = parse_sections(self._current_text())
section = next((item for item in current_sections if item.unit_id == exhausted.unit_id), None)
paragraphs = parse_paragraphs(section) if section is not None else []
section_state = self.state["levels"]["section"]
if (
target_score > 3.5
or len(paragraphs) < 2
or section_state.get("paragraph_returned")
):
self._block_dimension(section_state, exhausted.dimension)
section_state["completed"] = False
for key in ("stop_reason", "exhausted_coordinate", "active_dimension"):
section_state.pop(key, None)
self._save()
continue
_log(f"Descending into paragraphs of {exhausted.unit_id}")
self.state["levels"].setdefault(
"paragraph", {"iterations": 0, "completed": False}
)["active_dimension"] = exhausted.dimension
self._save()
_, paragraph_traces, _ = self._run_level(
"paragraph", paragraphs, seed_traces=traces,
section_depth=section.heading_depth if section else None,
)
atomic_write_jsonl(
self.run_dir / "levels" / "section" / "current-traces.jsonl",
[asdict(trace) for trace in paragraph_traces],
)
section_state["completed"] = False
section_state["paragraph_returned"] = True
self._block_dimension(section_state, exhausted.dimension)
for key in ("stop_reason", "exhausted_coordinate", "active_dimension"):
section_state.pop(key, None)
self._refresh_matrix(
"section", parse_sections(self._current_text()), paragraph_traces
)
return self._complete()
def _complete(self) -> Path:
target = self.run_dir / "S_final"
if target.exists():
shutil.rmtree(target)
shutil.copytree(self.current, target)
final_skill_hash = sha256_file(target / "SKILL.md")
final_rollouts = self.state.get("current_rollouts")
final_rollout_hash = self.state.get("current_rollout_skill_sha256")
if (
not isinstance(final_rollouts, str)
or not Path(final_rollouts).is_dir()
or final_rollout_hash != final_skill_hash
):
final_rollouts = None
report = {
"status": "complete",
"input_package_hash": package_hash(self.run_dir / "S_fast"),
"final_package_hash": package_hash(target),
"final_rollouts": final_rollouts,
"final_rollout_skill_sha256": final_skill_hash if final_rollouts else None,
"task": self.state["task"]["name"],
"agent": self.state["task"]["agent"],
"target_model": self.state["task"]["model"],
"model": self.model,
"rollout_backend": "skillsbench_development",
"production_rollout_backend": "blank_container_required",
"verifier_signal_used": False,
"levels": self.state["levels"],
"decisions": {
name: self._decisions(name)
for name in ("section", "paragraph")
if name in self.state["levels"]
},
}
atomic_write_json(self.run_dir / "report.json", report)
self.state["status"] = "complete"
self._save()
shutil.rmtree(self.temp, ignore_errors=True)
_log(f"Deep Loop complete: {target}")
return target