Agent Skills

Agent Skills provide a standardized way to extend agent capabilities with reusable, shareable skill packages. Skills are loaded dynamically and provide instructions, resources, and tools to agents.

Embabel implements the Agent Skills Specification.

What are Agent Skills?

An Agent Skill is a directory containing a SKILL.md file with YAML frontmatter and markdown instructions. Skills can also include bundled resources:

  • scripts/ - Executable scripts (Python, Bash, etc.)
  • references/ - Documentation and reference materials
  • assets/ - Static resources like templates and data files

Skills use a lazy loading pattern: only minimal metadata is included in the system prompt, with full instructions loaded when the skill is activated.

Using Skills with PromptRunner

The Skills class implements LlmReference, allowing it to be passed to a PromptRunner:

var skills = new Skills("financial-skills", "Financial analysis skills")
    .withGitHubUrl("https://github.com/wshobson/agents/tree/main/plugins/business-analytics/skills");

var response = context.ai()
    .withLlm(llm)
    .withReference(skills)
    .withSystemPrompt("You are a helpful financial analyst.")
    .respond(conversation.getMessages());

When skills are added as a reference, the agent can:

  • See available skills in the system prompt
  • Activate skills to get full instructions
  • List and read skill resources

Loading Skills from GitHub

The simplest way to load skills is from a GitHub URL:

var skills = new Skills("my-skills", "Skills for my agent")
    .withGitHubUrl("https://github.com/anthropics/skills/tree/main/skills");

Supported URL formats:

  • https://github.com/owner/repo - Load from repository root
  • https://github.com/owner/repo/tree/branch - Specific branch
  • https://github.com/owner/repo/tree/branch/path/to/skills - Specific path

For more control, use explicit parameters:

var skills = new Skills("my-skills", "Skills for my agent")
    .withGitHubSkills("anthropics", "skills", "skills", "main");

Loading Skills from Local Directories

Load a single skill from a directory containing SKILL.md:

var skills = new Skills("my-skills", "Local skills")
    .withLocalSkill("/path/to/my-skill");

Load multiple skills from a parent directory:

var skills = new Skills("my-skills", "Local skills")
    .withLocalSkills("/path/to/skills-directory");

It does not recurse into nested directories.

Skill Directory Structure

A skill directory must contain a SKILL.md file:

my-skill/
├── SKILL.md        # Required - metadata and instructions
├── scripts/        # Optional - executable scripts
├── references/     # Optional - documentation
└── assets/         # Optional - static resources

The SKILL.md file uses YAML frontmatter:

---
name: my-skill
description: A skill that does something useful
license: Apache-2.0
compatibility: Requires Python 3.9+
---

# My Skill Instructions

Step-by-step instructions for using this skill...

Skill Activation

Skills are activated lazily. The system prompt contains only minimal metadata (~50-100 tokens per skill). When an agent needs a skill, it calls the activate tool to load full instructions.

The Skills class exposes three LLM tools:

  • activate(name) - Load full instructions for a skill
  • listResources(skillName, resourceType) - List files in scripts/references/assets
  • readResource(skillName, resourceType, fileName) - Read a resource file

Selecting Skills by Embedding

Lazy activation asks the model to choose: it reads the descriptions, decides a skill is relevant, and calls activate. This is appropriate for procedural skills. For skills that carry reference knowledge — domain conventions, formulas, terminology a model may misread — the decision is the weak point, because a model confident in a wrong answer never reaches for help.

EmbeddingSkillSelector offers a second activation mode: choose skills by similarity between the query and each skill’s description, and inject the instructions directly. No tool call, no model decision, one embedding call.

A skill opts in through the specification’s open metadata map, so it stays portable — other Agent Skills runtimes ignore the key:

---
name: financial-metric-formulas
description: Derived financial metrics and how to compute them from statement line items -
  quick ratio, current ratio, EBITDA, operating and gross margin, CAGR, free cash flow.
metadata:
  activation: embedding
---

Quick ratio = (cash and equivalents + short-term investments + receivables) / current liabilities...

The natural way to use it with a PromptRunner is as a PromptContributor:

var selector = new EmbeddingSkillSelector(embeddingService);

var response = context.ai()
    .withLlm(llm)
    .withPromptContributor(selector.contributorFor(question, skills))
    .respond(conversation.getMessages());

The contributor is lazy: selection happens when the prompt is assembled, so a runner that is configured and never used costs no embedding call. Callers assembling a prompt by hand — rendering into a template, for example — can use guidanceFor(query, skills) for the same content as a string, or select(...) for the chosen skills and their scores.

Notes:

  • Skills without activation: embedding are never selected this way, so existing skill libraries are unaffected.
  • Similarity discriminates by subject, not by difficulty. A skill selected this way is injected for every question in its subject area, so its instructions must be safe to inject whenever the subject matches.
  • description is what gets embedded: write it in the vocabulary of the questions the skill serves.
  • The default threshold (0.30) and maximum (2 skills) are constructor parameters. Similarity scores are not calibrated across embedding models — tune the threshold if you change the model.
  • Selection is fail-open: an embedding failure yields no skills rather than an error.

Combining Skills with Other References

Skills can be combined with other LlmReference implementations:

var response = context.ai()
    .withLlm(properties.chatLlm())
    .withReference(
        new LocalDirectory("./data/financial", "Financial data files")
            .withUsageNotes("Search to find files matching user requests.")
    )
    .withReference(
        new Skills("analytics", "Business analytics skills")
            .withGitHubUrl("https://github.com/example/skills/tree/main/analytics")
    )
    .withSystemPrompt("You are a financial analyst assistant.")
    .respond(conversation.getMessages());

Validation

Skills are validated when loaded:

  • Frontmatter validation - Required fields (name, description) and field lengths
  • File reference validation - Paths in instructions (e.g., scripts/build.sh) must exist
  • Name matching - Skill name must match its parent directory name

To disable file reference validation:

var loader = new DefaultDirectorySkillDefinitionLoader(false);

Current Limitations

  • Script execution
    Skills with scripts/ directories are loaded, but script execution is not yet supported. A warning is logged when such skills are loaded.
  • allowed-tools field
    The allowed-tools frontmatter field is parsed but not currently enforced.

See the Agent Skills Specification for the full specification.

Was this page helpful?

Share