Skills
Skills API 目前仍处于实验阶段。API 与行为可能在后续版本中发生变化。
Skills 是一种机制,用于为 LLM 配备可复用、自包含的行为指令。 一个 skill 包含名称、简短描述以及一段指令正文(即其 content), 还可附带可选资源(例如 references、assets、templates 等)。 LLM 按需加载 skill,从而保持初始上下文较小,仅在实际需要时才拉取详细指令。
Skills 按照 Agent Skills 规范 设计。
创建 Skills
从文件系统加载
通常,每个 skill 位于各自的目录中,并包含一个 SKILL.md 文件。
该文件必须以 YAML front matter 块开头,声明 skill 的 name 与 description。
front matter 以下的全部内容成为 skill 的 content —— 即激活该 skill 时提供给 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 目录中的任何文件(SKILL.md 本身以及 scripts/
子目录下的文件除外)都会自动加载为 SkillResource,供 LLM 按需读取。
使用 langchain4j-skills 模块中的 FileSystemSkillLoader 从文件系统加载 skills:
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-skills</artifactId>
<version>1.18.1-beta28</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"));
从 Classpath 加载
ClassPathSkillLoader 的工作方式与 FileSystemSkillLoader 类似,但从
classpath 而非文件系统解析 skill 目录。当 skills 打包在 JAR 内,
或位于 src/main/resources 下时,这种方式很有用:
src/main/resources/
└── skills/
├── docx/
│ ├── SKILL.md
│ └── references/
│ └── tracked-changes.md
└── data-analysis/
└── SKILL.md
// Load all skills from a classpath directory:
List<FileSystemSkill> skills = ClassPathSkillLoader.loadSkills("skills");
// Or load a single skill:
FileSystemSkill skill = ClassPathSkillLoader.loadSkill("skills/docx");
默认情况下,ClassPathSkillLoader 使用线程的上下文类加载器。
如有需要,也可以传入自定义的 ClassLoader:
FileSystemSkill skill = ClassPathSkillLoader.loadSkill("skills/docx", myClassLoader);
与 FileSystemSkillLoader 相同的 SKILL.md 格式、资源加载规则以及 scripts/ 排除规则
均适用。
以编程方式创建
Skills 不必基于文件系统。 你可以从任意来源创建它们 —— 数据库、远程 API、运行时生成 —— 使用 builder 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 reference = SkillResource.builder()
.relativePath("references/tone-guide.md")
.content("Use warm, concise language. Avoid jargon.")
.build();
Skill skill = Skill.builder()
.name("customer-support")
.description("Handles customer support inquiries")
.content("Follow the tone guide in references/tone-guide.md ...")
.resources(List.of(reference))
.build();
模式
Skills 可以通过两种不同模式与 AI Service 集成,取决于你需要的 控制程度与信任级别。
工具模式(推荐)
类: Skills(来自 langchain4j-skills 模块)
这对应于 Agent Skills 规范 中描述的 基于工具的代理(Tool-based agents) 集成方式。
在此模式下,LLM 激活某个 skill 以获取分步指令,然后通过调用
你已显式注册的 工具 来执行这些指令。
推理时 LLM 无法访问文件系统 —— 所有 skill 内容与
资源都会预先加载到内存中(例如通过 FileSystemSkillLoader),而 activate_skill
与 read_skill_resource 工具返回的是这些预加载内容,而非从磁盘读取。
由于只能调用你预先定义的工具,不存在任意代码执行的风险。
已注册的工具
| 工具 | 何时注册 |
|---|---|
activate_skill | 始终注册。LLM 调用此工具以将 skill 的完整指令加载到上下文中。 |
read_skill_resource | 当至少一个 skill 拥有资源时注册。LLM 调用此工具以读取各个参考文件。 |
| Skill 作用域工具 | 在 skill 激活之后注册。 |
工作原理
- 系统消息列出可用 skills(名称与描述),以便 LLM 做出选择。
- 用户提出需要特定 skill 的问题。
- LLM 调用
activate_skill("my-skill")以获取其指令。 - LLM 遵循这些指令完成任务,过程中可选地读取资源文件。
Skill 示例
Skills 描述的是 策略 —— 确切的调用顺序、必需参数、错误处理步骤 以及示例 —— 而实际执行仍保留在类型安全、经过测试的 Java 代码中:
---
name: process-order
description: Processes a customer order end-to-end
---
To process an order:
1. Call `validateOrder(orderId)` to check the order is valid.
2. Call `reserveInventory(orderId)` to reserve the required stock.
3. Only if reservation succeeds, call `chargePayment(orderId)`.
4. Finally, call `sendConfirmationEmail(orderId)`.
If any step fails, call `rollbackOrder(orderId)` before reporting the error.
接入方式
将 Skills 提供的 ToolProvider 与常规工具一并传给 AI Service builder。
使用 formatAvailableSkills() 将 skill 目录注入系统消息,以便
LLM 知道可以激活哪些 skills:
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 格式的块,列出每个 skill 的名称与描述:
<available_skills>
<skill>
<name>process-order</name>
<description>Processes a customer order end-to-end</description>
</skill>
<skill>
<name>data-analysis</name>
<description>Analyse tabular data and produce charts</description>
</skill>
</available_skills>
自定义
每个工具的名称、描述与参数元数据都可通过 builder 上对应的 配置类进行覆盖:
Skills skills = Skills.builder()
.skills(mySkills)
.activateSkillToolConfig(ActivateSkillToolConfig.builder()
.name(...) // tool name (default: "activate_skill")
.description(...) // tool description
.parameterName(...) // parameter name (default: "skill_name")
.parameterDescription(...) // parameter description
.throwToolArgumentsExceptions(...) // throw ToolArgumentsException instead of ToolExecutionException (default: false)
.build())
.readResourceToolConfig(ReadResourceToolConfig.builder()
.name(...) // tool name (default: "read_skill_resource")
.description(...) // tool description
.skillNameParameterName(...) // skill_name parameter name (default: "skill_name")
.skillNameParameterDescription(...) // skill_name parameter description
.relativePathParameterName(...) // relative_path parameter name (default: "relative_path")
.relativePathParameterDescription(...) // static description (takes precedence over provider)
.relativePathParameterDescriptionProvider(...) // dynamic description based on available resources
.throwToolArgumentsExceptions(...) // throw ToolArgumentsException instead of ToolExecutionException (default: false)
.build())
.build();
Skill 作用域工具
你可以直接将工具附加到某个 skill。这些工具 仅在该 skill 通过
activate_skill 工具激活之后 才会暴露给 LLM。
这能保持 LLM 的工具列表精简且聚焦,并确保仅在相关时才出现
skill 专用工具。
使用带 @Tool 注解的方法
附加工具最简单的方式是传入带有 @Tool 注解方法的对象:
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();
也可以使用 toBuilder() 将工具附加到已构建的 skill —— 例如,
为从文件系统加载的 skill 添加工具:
FileSystemSkill skill = FileSystemSkillLoader.loadSkill(Path.of("skills/process-order"));
Skill skillWithTools = skill.toBuilder()
.tools(new OrderTools())
.build();
使用 Tool Providers
你也可以将 ToolProvider 附加到 skill —— 例如,仅在 skill 激活后
才暴露来自 MCP 服务器的工具:
ToolProvider mcpToolProvider = McpToolProvider.builder()
.mcpClients(mcpClient)
.toolFilter((tool, mcpClient) -> tool.name().startsWith("inventory_"))
.build();
Skill skill = Skill.builder()
.name("inventory-management")
.description("Manages warehouse inventory")
.content("""
Use inventory tools to check stock levels and update quantities.
""")
.toolProviders(mcpToolProvider)
.build();
使用 Map<ToolSpecification, ToolExecutor>
若要对工具规范与执行逻辑进行完全控制,可以直接传入一个 map:
ToolSpecification validateOrder = ToolSpecification.builder()
.name("validateOrder")
.description("Validates a customer order by ID")
.addParameter("orderId", JsonSchemaProperty.STRING, JsonSchemaProperty.description("The order ID"))
.build();
ToolExecutor validateOrderExecutor = (request, memoryId) -> {
String orderId = parseOrderId(request.arguments());
return validate(orderId);
};
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.
""")
.tools(Map.of(validateOrder, validateOrderExecutor))
.build();
三种方式可以组合使用 —— @Tool 方法、ToolProvider 以及 Map 条目
会合并为一组 skill 作用域工具:
Skill skill = Skill.builder()
.name("process-order")
.description("Processes a customer order end-to-end")
.content("...")
.tools(new OrderTools())
.tools(Map.of(validateOrder, validateOrderExecutor))
.toolProviders(mcpToolProvider)
.build();
接入方式
Skills skills = Skills.from(skill);
MyAiService service = AiServices.builder(MyAiService.class)
.chatModel(chatModel)
.chatMemory(MessageWindowChatMemory.withMaxMessages(100))
.toolProvider(skills.toolProvider())
.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.")
.build();