Skills

Agent Skills is an open specification for reusable, model-agnostic skill definitions that can be loaded from the filesystem and exposed to an LLM as tools. For general information about skills, see the LangChain4j Skills documentation.

The quarkus-langchain4j-skills extension provides integration with the LangChain4j Skills module. When configured, it automatically loads skills from specified directories and registers a SkillsToolProvider CDI bean that exposes them to your AI services.

Only the tool mode is supported for skills at the moment. Shell skills don’t have Quarkus integration (yet), but you may use the dev.langchain4j:langchain4j-experimental-skills-shell module directly if you want.

Dependency

<dependency>
    <groupId>io.quarkiverse.langchain4j</groupId>
    <artifactId>quarkus-langchain4j-skills</artifactId>
</dependency>

Configuration

The extension is configured via the quarkus.langchain4j.skills.directories property, which accepts a list of directories from which to load skills. Each directory is in the list is a relative or absolute filesystem path. If referencing a classpath resource, prefix the path with classpath:.

quarkus.langchain4j.skills.directories=skills,classpath:other-skills

Each listed directory is expected to contain subdirectories, where each subdirectory represents a single skill and must contain a SKILL.md file with YAML front matter defining the skill’s name and description. The body of the file becomes the skill’s instructions. See the Agent Skills specification for details on the SKILL.md format.

Example

Given the following directory structure on the classpath:

skills/
  poem-writing/
    SKILL.md

With skills/poem-writing/SKILL.md containing:

---
name: poem-writing
description: Instructions for writing a poem
---

When asked to write a poem, follow these rules strictly:

1. The poem MUST have exactly 12 lines.
2. The poem MUST be about a sad mushroom.

And application.properties:

quarkus.langchain4j.skills.directories=skills

The @Skills annotation

The @Skills annotation (io.quarkiverse.langchain4j.skills.Skills) is the recommended way to wire skills into your AI services and agents. It automatically configures both the tool provider (exposing the activate_skill tool) and a system message that tells the LLM which skills are available.

With @RegisterAiService

Place @Skills on the AI service interface:

@RegisterAiService
@Skills (1)
public interface PoemAiService {

    String chat(@UserMessage String message);
}
1 All loaded skills are available.

To restrict the service to specific skills, pass their names:

@RegisterAiService
@Skills("poem-writing") (1)
public interface PoemAiService {

    String chat(@UserMessage String message);
}
1 Only the poem-writing skill is available. If the name does not match any loaded skill, the application will fail at startup.

With @Agent

Place @Skills on the @Agent-annotated method:

public interface TripPlannerAgent {

    @Agent(description = "Plans trips", outputKey = "plan")
    @Skills("adventure-trip", "family-trip") (1)
    String planTrip(@V("request") String request);
}
1 Only the named skills are available to this agent.

Fail-fast validation

If any skill name passed to @Skills does not match a loaded skill, the application will fail at startup with a clear error listing the available skill names.

Using the SkillsToolProvider

Another way to wire skills to your AI services is through The skills ToolProvider which is automatically registered as a CDI bean of type SkillsToolProvider. Any @RegisterAiService that uses a ToolProvider will pick it up:

@RegisterAiService(systemMessageProviderSupplier = SkillsSystemMessageProvider.class)
public interface PoemAiService {

    String chat(String message);
}
The SkillsSystemMessageProvider (io.quarkiverse.langchain4j.skills.SkillsSystemMessageProvider) is a built-in sample implementation of SystemMessageProvider that generates a system message containing the descriptions of all loaded skills. You can implement your own SystemMessageProvider (and reuse parts of the SkillsSystemMessageProvider) if you need to customize the system message further. If you don’t specify a system message provider, then the LLM might not be aware which skills are available and thus won’t be able to use them unless the end user provides the exact skill name during the chat.
@Path("/poem")
public class PoemResource {

    @Inject
    PoemAiService poemAiService;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String poem() {
        return poemAiService.chat(
                "Activate the poem-writing skill and write a poem following its instructions.");
    }
}

A complete working example is available in the skills sample.