feat: agent skill evaluation infrastructure#1605
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
For my understanding, this is the backbone needed to evaluate skills on model + benchmark? |
ffrujeri
left a comment
There was a problem hiding this comment.
Thanks for this — it's a clean, well-scoped implementation of #1256. The run-level skills.path design (decoupled from the dataset, with a content-hashed skills_ref for variant identity) is sound, and the smoke test plus unit coverage are appreciated. The core mechanics all check out: the skills_ref side-channel into the agent's /run handler, propagation through num_repeats/deepcopy/resume, path resolution, and the async/HTTP/subprocess handling. skills.py is at 100% line coverage and the suite is green.
The inline comments are polish rather than correctness blockers, in three groups:
- Skill loading robustness —
parse_skill_mdrejects aSKILL.mdsaved with a UTF-8 BOM (a one-lineutf-8-sigfix), andSkillMetadata.versionreads a top-levelversion:key while the Agent Skills spec nests it undermetadata:, so the field is effectively dead for spec-compliant skills. - Robustness / cleanup — the per-run config dir is created outside the
try/finally, so a badskills.pathleaks the directory on the error path; and a small doc note that staging skills broadens the runtime (dropping--barere-enables hooks, plugins, MCP, memory, and project instructions, not just skills) would help anyone trying to measure a skill's isolated impact. - Test coverage — two suggested additions: an end-to-end test that the
skills_ref→ metadata side-channel is threaded through/run(the most refactor-fragile line), and a round-trip test thatskills_refsurvivesresume_from_cache.
Adds skills as a run-level evaluation variable, decoupled from the dataset. Skills are specified via skills.path on ng_collect_rollouts and applied to a fixed, skill-agnostic dataset at rollout time. Each rollout result is stamped with a content-hashed skills_ref for provenance and variant comparison. - nemo_gym/skills.py: load_skill_directory, hash_skill_dir, stage_skills, and SkillsConfig/SkillsRef models (version parsed from spec metadata.version, SKILL.md decoded utf-8-sig to tolerate a BOM). - rollout_collection.py: optional skills config; resolve once at startup, stamp skills_ref onto each row and propagate to results (survives resume_from_cache). - claude_code_agent: reads skills_ref.path and stages it into the per-request CLAUDE_CONFIG_DIR/skills/, forcing --bare off so native discovery loads it; cleans up a partially-created config dir if staging fails. - docs: agent-server/agent-skills reference page. Signed-off-by: Chris Wing <cwing@nvidia.com>
2bfea3f to
097ef52
Compare
|
🌿 Preview your docs: https://nvidia-preview-cwing-agent-skills-evaluation.docs.buildwithfern.com/nemo/gym Here are the markdown pages you've updated: |
@ritaneves exactly, this allows you to isolate the impact of skills during evaluation. Regarding A vs B comparison you can compare by running the same dataset twice with different |
Replace the deprecated ng_collect_rollouts invocation with the current gym eval run syntax and note --no-serve usage against already-started servers. Signed-off-by: Chris Wing <cwing@nvidia.com>
Implements #1256 (skill evaluation infrastructure), part of epic #1494.
Summary
Adds skills as a run-level evaluation variable, decoupled from the dataset. Skills are specified via
skills.pathongym eval runand applied to a fixed, skill-agnostic dataset at rollout time — no dataset-row coupling (deliberately not modeled likeagent_ref). Each rollout result is stamped with askills_reffor provenance and variant comparison.Core (agent-agnostic)
nemo_gym/skills.py—load_skill_directory(parsesSKILL.mdfrontmatter, actionable errors for malformed skills),hash_skill_dir(short content sha256),stage_skills, andSkillsConfig/SkillsRefmodels.rollout_collection.py— optionalskillsconfig; resolve once at startup, stampskills_ref(path + hash + metadata) onto each row, propagate to results.skills_refcarries a contenthashso optimizer loops (e.g. ACE, GEPA, EvoSkill) that mutate a skill in place at the same path stay distinguishable in reward profiling. Identity is derived from bytes on disk — no optimizer cooperation required.Claude Code agent (thin per-agent adapter)
skills_ref.pathfrom the/runrequest, stages it into the per-requestCLAUDE_CONFIG_DIR/skills/, and forces--bareoff so native discovery loads it. No skill-interpretation logic added — Claude loads/activates natively.Docs
agent-server/agent-skillsreference page: workflow,skills_refhash rationale, and the three-step per-agent adapter contract for future agents (Codex, MCP).Design notes
The decoupled (run-level, not dataset-row) design and the content-hash variant identity are discussed in #1256 (comment). A committed teaching example + end-to-end "evaluate and optimize skills" tutorial (with a composite-reward example) is tracked separately in #1604.
Test plan
tests/unit_tests/test_skills.py(incl. spec-compliantmetadata.version, UTF-8 BOM tolerance), rolloutskills_refstamping +resume_from_cacheround-trip, and agent-side_build_commandskills/--barehandling, per-request config-dir cleanup on staging failure, andrun()→_create_responseskills_pathforwarding.ruffclean.Smoke test (reproducible)
Validates both halves end to end against the existing reasoning_gym + Claude Code agent setup, using two variants of the same skill with different content (so hashes differ) and a distinct output marker per variant (to prove activation).
1. Create two throwaway skill variants
/tmp/ng_skill_smoke/variant_a/logic_puzzle_solver/SKILL.md:/tmp/ng_skill_smoke/variant_b/logic_puzzle_solver/SKILL.md— identical except a truth-table approach in the body and the marker[solved-with-skill-v2](the differing content is what makes the hashes differ).2. Start the servers (with this branch's code)
gym env start "+config_paths=[resources_servers/reasoning_gym/configs/reasoning_gym_claude_code_agent.yaml]"3. Collect rollouts for each variant over the same dataset
4. Verify
Observed results
skills_ref.path/tmp/ng_skill_smoke/variant_a/tmp/ng_skill_smoke/variant_bskills_ref.hash4cff9c793395100c63f5ea97skills_ref.skillslogic_puzzle_solver(name + description parsed from frontmatter)solved-with-skill-v1solved-with-skill-v2Confirms:
skills_refstamped + propagated to results, with distinct content hashes for same-named variants.CLAUDE_CONFIG_DIR/skills/,--barewas dropped, and Claude loaded and followed the correct variant with no cross-contamination (A→v1, B→v2).