# SkillOpt: Executive Strategy for Self-Evolving Agent Skills > ⚠️ **This is a preliminary draft release. A formal open-source release will follow.** [![Python 3.10+](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) *Train agent skills like you train neural networks — with epochs, learning rates, and validation gates — but without touching model weights.* --- ## What is SkillOpt? SkillOpt is a framework for optimizing a natural-language **skill document** through iterative rollout, reflection, editing, and gated validation. It does **not** fine-tune model parameters. Instead, it treats the skill document as the optimization target: - The **student** model executes tasks with the current skill - The **teacher** model analyzes trajectories and proposes edits - The framework merges, ranks, applies, and validates those edits - Only validated skill updates are kept | Deep Learning | SkillOpt | |---|---| | Model weights | Skill document (Markdown) | | Forward pass | Rollout (student executes tasks) | | Loss computation | Reflect (teacher analyzes trajectories) | | Gradient | Edit patches (proposed skill improvements) | | Gradient clipping | Edit ranking & selection (`learning_rate`) | | Weight update | Patch application to skill document | | Validation | Gated evaluation on held-out split | | Learning rate schedule | `lr_scheduler`: cosine, linear decay | | Epochs | Multi-epoch training with slow update & meta skill | --- ## Method Overview ### Optimization Target Each run maintains a mutable markdown skill document. The framework repeatedly improves that document instead of changing model parameters. This gives a training-style loop for prompt / policy optimization: 1. Roll out the current skill on a batch of tasks. 2. Reflect on failures and successes. 3. Merge patch proposals into a coherent candidate update. 4. Rank and select a bounded number of edits. 5. Apply those edits to produce a candidate skill. 6. Validate the candidate skill on a held-out selection split. 7. Keep the update only if the gate accepts it. ### Per-Step Pipeline Every training step executes the following pipeline in `skillopt/engine/trainer.py`: 1. **Rollout** The student model runs a batch of tasks using the current skill. 2. **Reflect** The teacher analyzes minibatches of trajectories and emits raw patches. Failure-driven and success-driven patches are tracked separately. 3. **Aggregate** Raw patches are merged hierarchically. Metadata such as `support_count` and `source_type` is carried into the merged patch so later ranking can use it. 4. **Select** The teacher ranks the merged edit pool and keeps up to `edit_budget` edits. 5. **Update** The selected edits are applied to the skill document. The framework records an `edit_apply_report.json` so you can see which edits actually landed, which were skipped, and why. 6. **Evaluate / Gate** The candidate skill is evaluated on the selection split. A candidate update is accepted only if it improves over the current selection score; a new global best is tracked separately. ### Within-Epoch Memory Inside an epoch, the trainer maintains a step buffer containing: - Compact failure-pattern summaries from previous steps - Rejected edits and their score deltas That context is fed back into later reflection calls so the teacher can avoid repeating ineffective edits and can focus on unsolved error patterns. ### Epoch-Level Mechanisms #### Slow Update At the end of each epoch, `slow_update` compares the previous epoch's terminal skill and current epoch's terminal skill on a sampled train subset. It then writes longitudinal guidance into a protected slow-update region inside the skill document. This guidance is **not** blindly written through — it is converted into a candidate skill and sent through the same selection gate as step-level updates. #### Meta Skill `meta_skill` is teacher-side cross-epoch memory. It does not directly edit the current skill. Instead, it writes a compact memory artifact describing longer-term patterns across adjacent epochs. That memory is loaded into later reflection / merge / ranking calls as extra context. #### Meta Reflect `meta_reflect` runs at epoch end over the step history of the current epoch. It looks at accepted and rejected directions from the whole epoch, proposes higher-level patch edits, applies them to a meta candidate, and then sends that candidate through the same selection gate. --- ## Quick Start ### Install ```bash git clone https://github.com/AgenticOpt/SkillOpt.git cd SkillOpt pip install -e . ``` ### Configure API Credentials ```bash cp .env.example .env # Edit .env with your API credentials, then: source .env ``` **Azure OpenAI** (API key or managed identity): ```bash export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com/" export AZURE_OPENAI_API_KEY="your-key" # Or use managed identity: set azure_openai_auth_mode=managed_identity in config ``` **OpenAI** directly: ```bash export OPENAI_API_KEY="sk-..." ``` **Anthropic Claude**: ```bash export ANTHROPIC_API_KEY="sk-ant-..." ``` **Qwen (local vLLM)**: ```bash export QWEN_CHAT_BASE_URL="http://localhost:8000/v1" export QWEN_CHAT_MODEL="Qwen/Qwen3.5-4B" ``` ### Run Training ```bash python scripts/train.py --config configs/searchqa/default.yaml ``` --- ## Configuration SkillOpt uses a hierarchical YAML configuration system. Each benchmark config inherits from `configs/_base_/default.yaml`. ### Configuration Structure ```yaml model: teacher_backend: openai_chat # openai_chat | claude_chat | qwen_chat student_backend: openai_chat # openai_chat | claude_chat | codex_exec | qwen_chat teacher: gpt-5.5 # teacher model deployment name student: gpt-5.5 # student model deployment name reasoning_effort: medium # low | medium | high train: num_epochs: 4 batch_size: 40 seed: 42 gradient: minibatch_size: 8 # trajectories per reflection call analyst_workers: 16 # parallel reflection workers use_deep_reflect: false # deep multi-turn probing deep_reflect_failures: 4 deep_reflect_successes: 2 optimizer: learning_rate: 4 # max edits per step (edit_budget) min_learning_rate: 2 # min edits for decay schedulers lr_scheduler: cosine # constant | linear | cosine | autonomous skill_update_mode: patch # patch | rewrite_from_suggestions | full_rewrite_minibatch use_slow_update: true use_meta_skill: true use_meta_reflect: false evaluation: use_gate: true # gated validation (always recommended) env: name: "" # benchmark name skill_init: "" # path to initial skill document split_mode: ratio # ratio | split_dir split_ratio: "2:1:7" # train:val:test ``` ### CLI Overrides Override any config key from the command line: ```bash python scripts/train.py \ --config configs/searchqa/default.yaml \ --cfg-options model.teacher_backend=openai_chat \ model.student_backend=codex_exec \ train.batch_size=40 \ optimizer.learning_rate=4 # Legacy flat overrides also work for common keys: python scripts/train.py \ --config configs/searchqa/default.yaml \ --backend azure_openai \ --teacher_model gpt-5.5 \ --student_model gpt-5.5 \ --reasoning_effort medium ``` --- ## Model Backends All model access goes through the unified backend router in `skillopt/model/`. | Backend | Use case | Config key | |---|---|---| | `openai_chat` | Azure OpenAI / OpenAI API | teacher / student | | `claude_chat` | Anthropic Claude | teacher / student | | `codex_exec` | Codex execution harness | student only | | `qwen_chat` | Local Qwen via vLLM | teacher / student | Separate teacher/student endpoints are supported: ```yaml model: teacher_backend: openai_chat student_backend: codex_exec teacher: gpt-5.5 student: gpt-5.5-codex ``` --- ## Data Splits SkillOpt supports two split modes: **Ratio split** — auto-generate from raw data: ```bash python scripts/train.py \ --config configs/searchqa/default.yaml \ --split_mode ratio \ --data_path /path/to/searchqa_data.json ``` **Pre-split directory** — consume prepared splits: ```bash python scripts/train.py \ --config configs/searchqa/default.yaml \ --split_mode split_dir \ --split_dir /path/to/searchqa_split ``` --- ## Supported Benchmarks | Benchmark | Type | Config | |---|---|---| | SearchQA | QA | `configs/searchqa/default.yaml` | | SpreadsheetBench | Code generation | `configs/spreadsheetbench/default.yaml` | | ALFWorld | Embodied agent | `configs/alfworld/default.yaml` | | DocVQA | Document QA | `configs/docvqa/default.yaml` | | OfficeQA | Tool-augmented QA | `configs/officeqa/default.yaml` | | SealQA | Tool-augmented QA | `configs/sealqa/default.yaml` | | BabyVision | Vision QA | `configs/babyvision/default.yaml` | | LiveMathematicianBench | Math | `configs/livemathematicianbench/default.yaml` | | MathVerse | Multimodal math | `configs/mathverse/default.yaml` | | MMRB | Multimodal reasoning | `configs/mmrb/default.yaml` | | SWEBench | Software engineering | `configs/swebench/default.yaml` | --- ## Running Training Basic training: ```bash python scripts/train.py --config configs/searchqa/default.yaml ``` Exec harness (Codex student): ```bash python scripts/train.py \ --config configs/searchqa/default.yaml \ --teacher_backend openai_chat \ --student_backend codex_exec \ --teacher_model gpt-5.5 \ --student_model gpt-5.5-codex \ --use_deep_reflect true \ --skill_update_mode rewrite_from_suggestions ``` SWEBench: ```bash python scripts/train.py \ --config configs/swebench/default.yaml \ --cfg-options env.dataset_name=lite env.split_ratio=2:1:7 ``` ### Eval Only Evaluate a specific skill without training: ```bash python scripts/eval_only.py \ --config configs/searchqa/default.yaml \ --skill skillopt/envs/searchqa/skills/initial.md ``` --- ## Output Structure Each run writes a structured output directory: ``` outputs// ├── config.json # Flattened runtime config ├── history.json # Per-step history records ├── runtime_state.json # Resume state (for auto-resume) ├── best_skill.md # Current best validated skill ├── skills/skill_vXXXX.md # Skill snapshot per step ├── steps/step_XXXX/ # Per-step artifacts │ ├── merged_patch.json │ ├── ranked_edits.json │ ├── candidate_skill.md │ ├── edit_apply_report.json │ ├── rewrite_result.json # when rewrite mode is enabled │ └── selection_eval/ ├── slow_update/epoch_XX/ ├── meta_skill/epoch_XX/ └── meta_reflect/epoch_XX/ ``` ### Resume Behavior The trainer resumes from `runtime_state.json` when present. That state tracks: - Last completed step - Current skill path and score - Best skill path and score - Origin tags for current and best skill --- ## Extending SkillOpt ### Add a New Benchmark 1. Create `skillopt/envs//` with: - `adapter.py` — implements `EnvAdapter` - `dataloader.py` — data loading logic - `rollout.py` — student execution logic - `skills/initial.md` — initial skill document 2. Add a config at `configs//default.yaml` 3. Register in `skillopt/envs/__init__.py` See `skillopt/envs/_template/` for a scaffold. ### Add a New Model Backend Implement a backend in `skillopt/model/` following the interface in `skillopt/model/common.py`, then register it in `skillopt/model/router.py`. --- ## WebUI Launch the monitoring dashboard (optional): ```bash pip install -e ".[webui]" python -m skillopt_webui.app ``` Provides browser-based config selection, training launch, and real-time log monitoring. --- ## Minimal Setup ```bash conda create -n skillopt python=3.11 conda activate skillopt pip install -e . ``` Depending on the benchmark, you may also need: ```bash pip install datasets gymnasium numpy ``` For SWEBench, you also need a working Docker environment plus the SWE-bench harness dependencies. --- ## Citation ```bibtex @article{skillopt2026, title={SkillOpt: Executive Strategy for Self-Evolving Agent Skills}, author={SkillOpt Team}, year={2026} } ```