> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

SkillOpt: Training Prompt Libraries Like Neural Networks for Frozen LLMs

[ View on GitHub ]

SkillOpt: Training Prompt Libraries Like Neural Networks for Frozen LLMs

Hook

What if you could run gradient descent on a markdown file? SkillOpt treats natural-language skill documents as trainable parameters, applying epochs, validation sets, and learning-rate budgets to optimize frozen LLM agents through pure text-space edits.

Context

Prompt engineering has been the dirty secret of production LLM systems since GPT-3 launched. Engineering teams spend weeks manually tweaking system prompts, running expensive A/B tests, and watching performance degrade when models update. The process is artisanal, irreproducible, and doesn't scale—there's no systematic way to improve prompts beyond human intuition and trial-and-error.

Meanwhile, the most powerful models are served as frozen APIs. You can't fine-tune GPT-4, Claude 3.5, or Gemini Pro without enterprise contracts and substantial ML infrastructure. Even when fine-tuning is available, it's expensive, requires labeled datasets in specific formats, and creates deployment headaches. For the 99% of teams running agents on API endpoints, the only lever is the text you send: the system prompt, few-shot examples, and task instructions. SkillOpt emerged from Microsoft Research as the first framework to bring rigorous machine learning training discipline to this text-optimization problem, treating skill documents as the trainable parameters in a meta-learning loop around frozen LLMs.

Technical Insight

Training Loop

execute with current skill

failed trajectories

structured diff edits

candidate edits

accept/reject

checkpoint

next epoch

generates

prepend to prompt

deploy zero-cost

Task Mini-Batch

Rollout Phase

Reflection Phase

Optimizer LLM

Aggregation

merge + budget

Validation Phase

held-out tasks

Update Skill Doc

best_skill.md

Target Agent

frozen LLM

Production System

System architecture — auto-generated

SkillOpt's architecture implements a classic train-validate-update loop, but instead of backpropagating gradients through weights, it edits natural-language text through structured diff operations. The system runs in five distinct phases per training step, borrowed directly from neural network optimization.

The rollout phase executes a mini-batch of tasks using the current skill document injected into the agent's system prompt. For a coding agent, this might be "Fix the bug in this Python function" tasks where the skill document contains debugging strategies. The agent generates trajectories—sequences of actions, tool calls, and outputs—which are logged with success/failure labels. The reflection phase then feeds failed trajectories to a separate optimizer LLM (typically GPT-4 or GPT-4o) with a meta-prompt: "This skill document failed on these tasks. Propose specific text edits to fix the failures." The optimizer emits structured edits in a constrained format:

# Example optimizer output format
edits = [
    {
        "operation": "add",
        "location": "after_line_12",
        "content": "Before writing code, verify the input types match function signatures.",
        "reasoning": "Agent failed on 3/5 tasks by passing strings to int parameters"
    },
    {
        "operation": "replace",
        "target": "Always use print statements for debugging",
        "replacement": "Use logging with appropriate levels (DEBUG/INFO/ERROR)",
        "reasoning": "Print statements caused output parsing failures in 2 tasks"
    },
    {
        "operation": "delete",
        "target": "Never use external libraries",
        "reasoning": "This constraint was too restrictive and caused task failures"
    }
]

The aggregation phase merges overlapping edits from multiple failed trajectories while respecting a token-budget constraint—this is SkillOpt's equivalent of a learning rate. If the budget is 50 tokens per step, the system prioritizes edits by their expected impact (measured by how many failures they address) and drops lower-priority changes that would exceed the budget. This prevents skill bloat and forces the optimizer to make focused improvements rather than rewriting the entire document.

The critical innovation comes in the selection phase: validation-gated acceptance. Every proposed edit is applied to create a candidate skill document, which is then tested against a held-out validation set. The candidate is accepted only if it strictly improves validation accuracy compared to the current checkpoint. This single mechanism prevents the catastrophic drift that kills most autonomous prompt optimizers—the system can't accept clever-sounding edits that actually hurt performance. If validation accuracy drops or stays flat, the edit goes into a rejected-edit buffer for meta-reflection during epoch boundaries.

Here's a simplified implementation of the validation gate:

def validate_edit(current_skill: str, proposed_edit: Edit, 
                  validation_tasks: List[Task]) -> bool:
    # Apply edit to create candidate skill
    candidate_skill = apply_edit(current_skill, proposed_edit)
    
    # Run validation tasks with both skills
    current_accuracy = evaluate_skill(current_skill, validation_tasks)
    candidate_accuracy = evaluate_skill(candidate_skill, validation_tasks)
    
    # Strict improvement required
    if candidate_accuracy > current_accuracy:
        return True
    else:
        # Store for meta-reflection
        rejected_buffer.append({
            "edit": proposed_edit,
            "current_acc": current_accuracy,
            "candidate_acc": candidate_accuracy,
            "failure_reason": "no_improvement"
        })
        return False

The update phase commits accepted edits to the skill document and checkpoints the new version. After each epoch (typically 5-10 training steps), the system performs a meta-reflection where it prompts the optimizer with the rejected-edit buffer: "These edits seemed promising but failed validation. What patterns do you notice?" This feedback loop helps the optimizer learn what kinds of changes actually work.

The final artifact is a best_skill.md file—typically 300-2000 tokens—that you prepend to your agent's system prompt at deployment. No runtime dependencies, no API wrappers, no serving infrastructure. The optimizer-target separation is architecturally elegant: you can use GPT-4 to optimize skills for a frozen GPT-3.5 agent, or Claude Opus to optimize skills for Llama 3.1. The optimizer never needs API access to the target model's internals; it only observes task trajectories.

Cross-harness transfer is surprisingly robust. In Microsoft's benchmarks, skills trained against GPT-4 in direct chat mode transferred to Codex CLI and Claude Code agentic loops without retraining, maintaining 80-90% of their accuracy gains. This suggests the learned strategies capture fundamental task structure ("verify inputs before processing," "break complex requests into subtasks") rather than execution-harness quirks ("Claude prefers XML tags" or "GPT-4 responds better to numbered lists").

Gotcha

The validation-gated design has a hard requirement: you need a labeled validation set with automated evaluation. This is trivial for deterministic tasks like code generation (run unit tests), API calling (check return values), or web automation (verify DOM state). It breaks completely for subjective tasks like creative writing, marketing copy, or open-ended Q&A where ground truth is fuzzy. If you can't write a def evaluate(output) -> float function that correlates with human judgment, SkillOpt can't optimize.

Optimizer costs are also substantial but rarely disclosed in the paper. Each training run requires hundreds to thousands of LLM calls: the optimizer LLM generates edit proposals, validation runs execute tasks with candidate skills, and meta-reflection analyzes rejected edits. For a benchmark with 100 training tasks, 50 validation tasks, and 10 epochs, you're looking at roughly 1,500+ API calls to GPT-4-class models. At $0.03/1K tokens (GPT-4o pricing), a single skill optimization run can cost $50-200 depending on task complexity. The economics work when you're deploying that skill across millions of agent invocations, but it's expensive for one-off experiments.

Text-space optimization is fundamentally non-convex. Small wording changes—"Always verify inputs" versus "Verify inputs when necessary"—can cause catastrophic behavior shifts. The framework has no convergence guarantees, and failure modes are opaque. Unlike neural network training where you can visualize loss curves and gradients, SkillOpt's optimization landscape is discrete and poorly understood. Empirically it works across Microsoft's benchmarks, but you might encounter task domains where it thrashes between competing strategies or converges to local optima that are hard to escape.

Verdict

Use if: You're running agents at scale on frozen API models (OpenAI, Anthropic, Google) where upfront optimization cost amortizes over millions of inferences, you have deterministic evaluation metrics or high-agreement human labels, and you're tired of manual prompt engineering cycles that don't transfer between model versions. SkillOpt shines for repeated workflows—customer support bots, code review agents, data extraction pipelines—where you can invest in training once and deploy everywhere. The validation-gated stability is production-ready in a way most prompt optimizers aren't.

Skip if: You're working on subjective or creative tasks without clear success metrics, your evaluation requires expensive human labeling, you're already fine-tuning models (just train the weights directly), or you're optimizing for one-shot performance rather than repeated patterns. Also skip if you're doing adversarial work like jailbreak research—SkillOpt requires stable evaluation sets, which adversarial domains rarely provide. Finally, if you can't afford hundreds of dollars in optimizer LLM costs per training run, stick with manual prompt engineering and A/B testing.