Skills
Skills (스킬)
:::note Skills API는 실험적이에요. API와 동작이 향후 릴리스에서 바뀔 수 있어요. :::
Skills는 LLM에 재사용 가능하고 자족적인 행동 지침을 장착하는 메커니즘이에요. 스킬은 이름(name), 짧은 설명(description), 그리고 지침 본문(그 content)을 묶고, 선택적으로 리소스(예: references, assets, templates 등)를 함께 담아요. LLM은 스킬을 요청 시에만 로드해서 초기 컨텍스트를 작게 유지하고, 실제로 필요할 때만 상세 지침을 가져와요.
:::note Skills는 Agent Skills 스펙에 따라 설계됐어요. :::
출처: 공식문서 - Skills
스킬 만들기
파일 시스템에서
보통 각 스킬은 SKILL.md 파일을 담은 자기 디렉토리에 존재해요. 파일은 스킬의 name과 description을 선언하는 YAML front matter 블록으로 시작해야 해요. front matter 아래의 모든 내용이 스킬의 content, 즉 스킬이 활성화될 때 LLM에 주어지는 지침이 돼요.
skills/
├── docx/
│ ├── SKILL.md
│ └── references/
│ └── tracked-changes.md ← loaded as a resource
└── data-analysis/
└── SKILL.md
SKILL.md 예시:
---
name: docx
description: Edit and review Word documents using tracked changes
---
When the user asks you to edit a Word document:
1. Always use tracked changes so edits can be reviewed.
...
스킬 디렉토리의 어떤 파일이든(SKILL.md 자체와 scripts/ 하위 디렉토리의 파일 제외) LLM이 요청 시 읽을 수 있는 SkillResource로 자동 로드돼요.
langchain4j-skills 모듈의 FileSystemSkillLoader를 사용해 파일 시스템에서 스킬을 로드해요.
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-skills</artifactId>
<version>1.20.0-beta30</version>
</dependency>
// Load all skills found in immediate subdirectories:
List<FileSystemSkill> skills = FileSystemSkillLoader.loadSkills(Path.of("skills/"));
// Or load a single skill by its directory:
FileSystemSkill skill = FileSystemSkillLoader.loadSkill(Path.of("skills/docx"));
클래스패스에서
ClassPathSkillLoader는 FileSystemSkillLoader처럼 동작하지만 파일 시스템 대신 클래스패스에서 스킬 디렉토리를 찾아요. 스킬을 JAR 안에 번들하거나 src/main/resources 아래에 둘 때 유용해요. 기본적으로 ClassPathSkillLoader는 스레드의 컨텍스트 클래스 로더를 사용해요. 같은 SKILL.md 형식과 리소스 로딩 규칙, scripts/ 제외 규칙이 적용돼요.
프로그래밍 방식으로
스킬이 파일 시스템 기반일 필요는 없어요. 데이터베이스, 원격 API, 런타임 생성 등 어떤 소스에서든 빌더 API로 만들 수 있어요.
Skill skill = Skill.builder()
.name("incident-response")
.description("Step-by-step runbook for diagnosing and resolving production incidents")
.content("""
When a production alert fires:
1. Call `fetchRecentLogs(serviceName)` to retrieve the last 5 minutes of logs.
2. Call `checkServiceHealth(serviceName)` to get current health metrics.
3. Based on the findings, call `createIncidentTicket(summary, severity)`.
4. If severity is CRITICAL, also call `pageOnCall(incidentId)`.
""")
.build();
SkillResource를 프로그래밍 방식으로 붙일 수도 있어요.
모드
스킬은 필요로 하는 제어와 신뢰 수준에 따라 AI Service에 두 가지 방식으로 통합할 수 있어요.
Tool Mode (권장)
클래스는 Skills(langchain4j-skills 모듈)예요. 이 모드는 Agent Skills 스펙의 Tool-based agents 통합 방식에 대응해요. 이 모드에서 LLM은 스킬을 활성화해 단계별 지침을 받은 뒤, 여러분이 명시적으로 등록한 도구를 호출해 그 지침을 수행해요. LLM은 추론 시점에 파일 시스템에 접근하지 못해요 — 모든 스킬 콘텐트와 리소스는 미리 메모리에 로드되고, activate_skill과 read_skill_resource 도구가 그 미리 로드된 콘텐트를 반환하지 디스크에서 읽지 않아요. 미리 정의된 도구만 호출할 수 있으므로 임의 코드 실행 위험이 없어요.
작동 방식은 다음과 같아요.
- 시스템 메시지가 사용 가능한 스킬들(이름과 설명)을 나열해서 LLM이 고를 수 있게 해요.
- 사용자가 특정 스킬이 필요한 질문을 해요.
- LLM이
activate_skill("my-skill")을 호출해 지침을 받아요. - LLM이 그 지침을 따라 작업을 완료하고, 필요하면 리소스 파일을 읽어요.
연결하는 방법은 다음과 같아요. Skills에서 ToolProvider를 가져와 일반 도구 옆에 AI Service 빌더로 전달하고, formatAvailableSkills()로 스킬 카탈로그를 시스템 메시지에 주입해 LLM이 활성화할 수 있는 스킬을 알게 해요.
Skills skills = Skills.from(FileSystemSkillLoader.loadSkills(Path.of("skills/")));
MyAiService service = AiServices.builder(MyAiService.class)
.chatModel(chatModel)
.tools(new OrderTools()) // your tools
.toolProvider(skills.toolProvider()) // or .toolProviders(myToolProvider, skills.toolProvider()) if you already have a tool provider configured
.systemMessage("You have access to the following skills:\n" + skills.formatAvailableSkills()
+ "\nWhen the user's request relates to one of these skills, activate it first using the `activate_skill` tool before proceeding.")
.build();
formatAvailableSkills()는 각 스킬의 이름과 설명을 나열한 XML 형식 블록을 반환해요.
각 도구의 이름·설명·파라미터 메타데이터는 빌더의 해당 config 클래스로 재정의할 수 있어요. activate_skill 도구는 항상 등록되고, 스킬에 리소스가 하나라도 있으면 read_skill_resource 도구가 등록돼요.
스킬 범위 도구 (Skill-Scoped Tools)
도구를 스킬에 직접 붙일 수도 있어요. 이런 도구는 activate_skill 도구로 스킬이 활성화된 후에만 LLM에 노출돼요. LLM의 도구 목록을 작고 집중적으로 유지하고, 관련될 때만 스킬 전용 도구가 나타나게 만들 수 있어요. @Tool 애너테이션 메서드를 넘기는 가장 간단한 방식부터, ToolProvider를 붙이는 방식(MCP 서버 도구를 스킬 활성화 후에만 노출), Map<ToolSpecification, ToolExecutor>를 직접 넘기는 방식까지 지원하며, 세 가지를 조합할 수도 있어요.
class OrderTools {
@Tool("Validates a customer order by ID")
String validateOrder(String orderId) {
// validation logic
return "valid";
}
@Tool("Charges payment for a customer order")
String chargePayment(String orderId) {
// payment logic
return "charged";
}
}
Skill skill = Skill.builder()
.name("process-order")
.description("Processes a customer order end-to-end")
.content("""
To process an order:
1. Call `validateOrder(orderId)` to check the order is valid.
2. Call `chargePayment(orderId)`.
""")
.tools(new OrderTools())
.build();
작동 방식을 정리하면, 스킬 활성화 전에는 LLM이 activate_skill(그리고 read_skill_resource) 도구만 보고 스킬 범위 도구는 도구 목록에 없어요. LLM이 activate_skill("process-order")를 호출하면 활성화가 ToolExecutionResultMessage에 기록되고, 같은 AI Service 호출 내에서 스킬 범위 도구가 보이게 돼 LLM이 즉시 호출할 수 있어요. 스킬 범위 도구는 다음 AI Service 호출에서도 계속 보이고, 스킬이 비활성화될 때만 안 보여요.
Tool Search와 함께 사용하기
Tool Search와 함께 사용할 때 서로 독립적으로 동작해요. 스킬 범위 도구는 검색 대상이 되지 않아요(검색 가능한 도구 풀에 나타나지 않고, tool_search_tool로 찾을 수도 없어요). 일반 도구는 검색 대상으로 남아요. activate_skill은 ALWAYS_VISIBLE로 표시되어 Tool Search가 켜져 있어도 항상 호출할 수 있어요.
Shell Mode (실험적)
클래스는 ShellSkills(langchain4j-experimental-skills-shell 모듈)예요. Agent Skills 스펙의 Filesystem-based agents 통합 방식에 대응해요. 이 모드에서 LLM은 단일 run_shell_command 도구를 받고, 셸 명령으로 파일 시스템에서 스킬 지침을 직접 읽어요. activate_skill이나 read_skill_resource 도구는 없고, LLM이 사람 개발자처럼 스킬 파일을 탐색해요.
:::warning 셸 실행은 본질적으로 안전하지 않아요. 명령이 샌드박스·컨테이너화·권한 제한 없이 호스트 프로세스 환경에서 직접 실행돼요. 오작동하거나 프롬프트 주입된 LLM이 애플리케이션이 실행되는 머신에서 임의의 명령을 실행할 수 있어요. 입력을 완전히 신뢰하고 관련 위험을 받아들일 수 있는 통제된 환경에서만 쓰세요. :::
이 모드의 등록 도구는 항상 등록되는 run_shell_command 하나뿐이에요. 이 모드는 실험과 프로토타이핑에 가장 적합해요. 커뮤니티에서 배포한 타사 스킬(예: agentskills.io 에코시스템)을 Java로 먼저 포팅하지 않고 바로 쓸 때도 유용해요. 빠르게 동작하는 워크플로우를 구성한 뒤, 솔루션이 성숙해지면 개별 동작을 도구로 옮기면 돼요.
더 알아보기
- AI Services - 스킬이 통합되는 선언형 AI 서비스
- Tools (Function Calling) - 스킬이 수행하는 데 쓰는 도구와 ToolProvider