Use Skills Subsystem
Use this when you want agents to discover and activate skill packages at runtime, loading instructions, resources, and scripts on demand.
Prerequisites
Section titled “Prerequisites”- A working awaken agent runtime (see First Agent)
- Feature
skillsenabled on theawakencrate
[dependencies]awaken = { git = "https://github.com/AwakenWorks/awaken", features = ["skills"] }tokio = { version = "1", features = ["full"] }serde_json = "1"- Create skill directories.
Each skill lives in a directory with a SKILL.md file containing YAML frontmatter:
skills/ my-skill/ SKILL.md another-skill/ SKILL.mdExample skills/my-skill/SKILL.md:
---name: my-skilldescription: Helps with data analysis tasksallowed-tools: read_file web_search---# My Skill
When this skill is active, focus on data analysis.Use read_file to load datasets and web_search for reference material.- Discover filesystem skills.
use std::sync::Arc;use awaken::ext_skills::{FsSkill, InMemorySkillRegistry, SkillSubsystem};
let result = FsSkill::discover("./skills").expect("skill discovery failed");for warning in &result.warnings { eprintln!("skill warning: {warning:?}");}
let skills = FsSkill::into_arc_skills(result.skills);let registry = Arc::new(InMemorySkillRegistry::from_skills(skills));-
Use embedded skills (alternative).
For compile-time embedded skills, use
EmbeddedSkill:
use std::sync::Arc;use awaken::ext_skills::{EmbeddedSkill, EmbeddedSkillData, InMemorySkillRegistry};
const SKILL_MD: &str = "\---name: builtin-skilldescription: A compiled-in skillallowed-tools: read_file---
Follow these instructions when active.";
let skill = EmbeddedSkill::new(&EmbeddedSkillData { skill_md: SKILL_MD, references: &[], assets: &[],}).expect("valid skill");
let registry = Arc::new(InMemorySkillRegistry::from_skills(vec![Arc::new(skill)]));- Wire into the runtime.
use std::sync::Arc;use awaken::engine::GenaiExecutor;use awaken::ext_skills::SkillSubsystem;use awaken::registry_spec::ModelSpec;use awaken::registry_spec::AgentSpec;use awaken::{AgentRuntimeBuilder, Plugin};
let subsystem = SkillSubsystem::new(registry);let mut agent_spec = AgentSpec::new("skills-agent") .with_model_id("gpt-4o-mini") .with_system_prompt("Discover and activate skills when specialized help is useful.") .with_hook_filter("skills-discovery") .with_hook_filter("skills-active-instructions");agent_spec.plugin_ids.extend([ "skills-discovery".into(), "skills-active-instructions".into(),]);
let runtime = AgentRuntimeBuilder::new() .with_provider("openai", Arc::new(GenaiExecutor::new())) .with_model(ModelSpec::new("gpt-4o-mini", "openai", "gpt-4o-mini")) .with_agent_spec(agent_spec) .with_plugin( "skills-discovery", Arc::new(subsystem.discovery_plugin()) as Arc<dyn Plugin>, ) .with_plugin( "skills-active-instructions", Arc::new(subsystem.active_instructions_plugin()) as Arc<dyn Plugin>, ) .build() .expect("failed to build runtime");plugin_ids loads both skill plugins. The hook filter keeps discovery and active
instruction injection enabled for this agent.
The SkillDiscoveryPlugin injects a skills catalog into the LLM context before inference and registers three tools:
| Tool | Purpose |
|---|---|
skill | Activate a skill by name |
load_skill_resource | Load a skill resource or reference |
skill_script | Run a skill script |
Constrain a skill to a tool subset
Section titled “Constrain a skill to a tool subset”A skill is more than instructions — it can also scope which tools the agent
sees while the skill is active. Declare allowed_tools in the SKILL.md
frontmatter (or SkillSpec.allowed_tools when you build the spec
programmatically):
---name: refund-flowdescription: Process customer refunds against the billing systemallowed-tools: billing_lookup issue_refund send_receipt---Note that allowed-tools (hyphenated) is a space-delimited string in the
SKILL.md frontmatter — not a YAML list — matching the Claude Code skills
convention. The runtime parses it into SkillSpec.allowed_tools: Vec<String>.
While refund-flow is active, the agent’s tool catalog is intersected with this
list, even if its AgentSpec.tool_loading_policy would normally include more.
This lets one agent host many task-shaped skills without leaking unrelated
tools (e.g. shell access) into a sensitive flow. Tuning allowed_tools from
the admin-console skill editor (or /v1/config/skills/:id) is the recommended
way to harden a workflow without recompiling the runtime.
Verify
Section titled “Verify”- Run the agent and ask it to list available skills.
- The LLM should see the skills catalog in its context and use the
skilltool to activate one. - After activation, the
ActiveSkillInstructionsPlugininjects the skill instructions into subsequent inference calls.
Common Errors
Section titled “Common Errors”| Symptom | Cause | Fix |
|---|---|---|
| No skills discovered | Wrong directory path | Ensure each skill has a SKILL.md in a subdirectory |
SkillMaterializeError | Invalid YAML frontmatter | Check that name and description fields are present in SKILL.md |
| Skill tools not available | Plugin not registered | Register both discovery_plugin() and active_instructions_plugin() |
| Feature not found | Missing cargo feature | Enable features = ["skills"] in Cargo.toml |
Related Example
Section titled “Related Example”crates/awaken-ext-skills/tests/
Key Files
Section titled “Key Files”| Path | Purpose |
|---|---|
crates/awaken-ext-skills/src/lib.rs | Module root and public re-exports |
crates/awaken-ext-skills/src/registry.rs | FsSkill, InMemorySkillRegistry, SkillRegistry trait |
crates/awaken-ext-skills/src/plugin/subsystem.rs | SkillSubsystem facade |
crates/awaken-ext-skills/src/plugin/discovery.rs | SkillDiscoveryPlugin |
crates/awaken-ext-skills/src/embedded.rs | EmbeddedSkill for compile-time skills |
crates/awaken-ext-skills/src/tools.rs | Skill tool implementations |