Skills(技能)是一种为 LLM 配备可复用的、自包含的行为指令的机制。一个技能(Skill)捆绑了名称、简短描述、一组指令正文(即其 content),以及可选的资源(例如参考资料、素材、模板等)。LLM 按需加载技能,保持初始上下文较小,仅在真正需要时才拉取详细指令。
注意: langchain4j Skills API 目前处于实验阶段。API 和行为在未来的版本中仍可能发生变化。
创建Skills
从文件系统创建
通常,每个技能存在于其自己的目录中,包含一个 SKILL.md 文件。该文件必须以 YAML front matter 块开头,声明技能的 name 和 description。front matter 以下的所有内容成为技能的 content——即 LLM 激活该技能时接收到的指令。
目录结构示例:
1 2 3 4 5 6 7
| skills/ ├── docx/ │ ├── SKILL.md │ └── references/ │ └── tracked-changes.md ← 自动作为资源加载 └── data-analysis/ └── SKILL.md
|
SKILL.md 示例:
1 2 3 4 5 6 7 8 9
| --- name: docx description: 使用修订模式编辑和审阅 Word 文档 ---
当用户要求你编辑 Word 文档时: 1. 始终使用修订模式,以便修改可以被审阅。 2. 不要直接删除原文,使用"插入批注"功能标注建议。 3. 修改完成后,生成一份变更摘要。
|
SKILL.md 所在目录中的任何文件(除了 SKILL.md 本身和 scripts/ 子目录下的文件)都会自动作为 SkillResource 加载,LLM 可以按需读取。
使用 langchain4j-skills 模块中的 FileSystemSkillLoader 从文件系统加载技能:
1 2 3 4 5 6 7 8 9
| import dev.langchain4j.skill.FileSystemSkillLoader; import java.nio.file.Path; import java.util.List;
List<FileSystemSkill> skills = FileSystemSkillLoader.loadSkills(Path.of("skills/"));
FileSystemSkill skill = FileSystemSkillLoader.loadSkill(Path.of("skills/docx"));
|
从 Classpath 加载
ClassPathSkillLoader 的工作方式与 FileSystemSkillLoader 类似,但从 classpath 而非文件系统解析技能目录。这在技能被打包在 JAR 中或位于 src/main/resources 下时非常有用:
1 2 3 4 5 6 7 8
| src/main/resources/ └── skills/ ├── docx/ │ ├── SKILL.md │ └── references/ │ └── tracked-changes.md └── data-analysis/ └── SKILL.md
|
1 2 3 4 5 6 7
| import dev.langchain4j.skill.ClassPathSkillLoader;
List<FileSystemSkill> skills = ClassPathSkillLoader.loadSkills("skills");
FileSystemSkill skill = ClassPathSkillLoader.loadSkill("skills/docx");
|
默认情况下,ClassPathSkillLoader 使用线程的上下文类加载器。如有需要,可以传入自定义 ClassLoader:
1
| FileSystemSkill skill = ClassPathSkillLoader.loadSkill("skills/docx", myClassLoader);
|
同样的 SKILL.md 格式、资源加载规则和 scripts/ 排除规则均适用于 ClassPathSkillLoader。
以编程方式创建
技能不必基于文件系统。你可以使用 builder API 从任何来源创建——数据库、远程 API、运行时生成等:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import dev.langchain4j.skill.Skill;
Skill skill = Skill.builder() .name("incident-response") .description("诊断和解决生产环境故障的逐步操作指南") .content(""" 当生产环境告警触发时: 1. 调用 fetchRecentLogs(serviceName) 获取最近 5 分钟的日志。 2. 调用 checkServiceHealth(serviceName) 获取当前健康指标。 3. 根据发现的问题,调用 createIncidentTicket(summary, severity) 创建故障单。 如果 severity 为 CRITICAL,同时调用 pageOnCall(incidentId)。 """) .build();
|
你也可以以编程方式附加资源:
1 2 3 4 5 6 7 8 9 10 11
| SkillResource reference = SkillResource.builder() .relativePath("references/tone-guide.md") .content("使用温暖、简洁的语言。避免行话。") .build();
Skill skill = Skill.builder() .name("customer-support") .description("处理客户支持咨询") .content("遵循 references/tone-guide.md 中的语气指南...") .resources(List.of(reference)) .build();
|
与 AI Service 集成
根据你对控制和信任的需求程度,技能可以通过两种截然不同的模式与 AI Service 集成。
模式一:工具模式(Skills)
类: Skills(来自 langchain4j-skills 模块)
在此模式下,LLM 激活一个技能以接收逐步指令,然后通过调用你显式注册的工具来执行这些指令。LLM 在推理时无权访问文件系统——所有技能内容和资源都预先加载到内存中(例如通过 FileSystemSkillLoader),activate_skill 和 read_skill_resource 工具返回的是那些预加载的内容,而不是从磁盘读取。由于只能调用你预定义的工具,不存在任意代码执行的风险。
技能描述了策略——调用的确切顺序、所需参数、错误处理步骤和已验证的示例——而实际执行始终停留在类型安全、经过测试的 Java 代码中。
典型用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| import dev.langchain4j.service.AiServices; import dev.langchain4j.model.chat.ChatLanguageModel; import dev.langchain4j.skill.FileSystemSkillLoader; import dev.langchain4j.skill.Skills; import java.nio.file.Path; import java.util.List;
public class Main { public static void main(String[] args) { List<FileSystemSkill> skillList = FileSystemSkillLoader.loadSkills(Path.of("skills/")); Skills skills = Skills.from(skillList);
MyAiService service = AiServices.builder(MyAiService.class) .chatModel(chatModel) .tools(new OrderTools()) // 你自己的 Java 工具方法 .toolProvider(skills.toolProvider()) // 注册技能的工具提供器 .systemMessage( "你有以下技能可用:\n" + skills.formatAvailableSkills() + "\n当用户的请求与某个技能相关时,请先使用 activate_skill 工具激活它,然后再继续处理。" ) .build();
service.chat("帮我处理订单 ORD-12345"); } }
interface MyAiService { String chat(String userMessage); }
|
formatAvailableSkills() 返回一个 XML 格式的块,列出每个技能的名称和描述:
1 2 3 4 5 6 7 8 9 10
| <skills> <skill> <name>docx</name> <description>使用修订模式编辑和审阅 Word 文档</description> </skill> <skill> <name>data-analysis</name> <description>分析数据并生成可视化图表</description> </skill> </skills>
|
为技能附加工具
你可以将工具直接附加到技能上。这些工具仅在技能通过 activate_skill 激活后才对 LLM 可见。这保持了 LLM 工具列表的精简和聚焦,确保技能特定的工具只在相关时才出现。
最简单的方式是传入包含 @Tool 注解方法的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class OrderTools { @Tool("验证订单是否有效") void validateOrder(String orderId) { ... }
@Tool("预留所需库存") void reserveInventory(String orderId) { ... }
@Tool("对订单进行扣款") void chargePayment(String orderId) { ... }
@Tool("发送确认邮件") void sendConfirmationEmail(String orderId) { ... }
@Tool("回滚订单") void rollbackOrder(String orderId) { ... } }
Skill skill = Skill.builder() .name("process-order") .description("处理客户订单") .content(""" 处理订单的步骤: 1. 调用 validateOrder(orderId) 检查订单是否有效。 2. 调用 reserveInventory(orderId) 预留所需库存。 3. 仅当预留成功时,调用 chargePayment(orderId) 进行扣款。 4. 最后,调用 sendConfirmationEmail(orderId) 发送确认邮件。 如果任何步骤失败,在报告错误之前调用 rollbackOrder(orderId) 回滚订单。 """) .tools(new OrderTools()) .build();
|
也可以使用 toBuilder() 向已构建的技能附加工具——例如,为从文件系统加载的技能添加工具:
1 2 3 4 5
| FileSystemSkill loadedSkill = FileSystemSkillLoader.loadSkill(Path.of("skills/process-order"));
Skill enhancedSkill = loadedSkill.toBuilder() .tools(new OrderTools()) .build();
|
你还可以向技能附加 ToolProvider——例如,在技能激活后才暴露来自 MCP 服务器的工具:
1 2 3 4 5 6
| Skill skill = Skill.builder() .name("advanced-analysis") .description("高级数据分析") .content("使用可用工具执行高级分析...") .toolProviders(mcpToolProvider) .build();
|
对于工具规范和执行逻辑的完全控制,可以直接传入一个 Map:
1 2 3 4 5 6 7 8 9
| Map<ToolSpecification, ToolExecutor> toolMap = new HashMap<>();
Skill skill = Skill.builder() .name("custom-skill") .description("自定义技能") .content("...") .tools(toolMap) .build();
|
以上三种方式可以组合使用——@Tool 方法、ToolProvider 和 Map 条目会合并为一组技能作用域内的工具。
技能可以与工具搜索(Tool Search)协同工作。当两者同时配置时,它们独立运行:
1 2 3
| Skills skills = Skills.builder() .toolSearchStrategy(myToolSearchStrategy) .build();
|
覆盖工具元数据
每个工具的名称、描述和参数元数据可以通过 builder 上对应的配置类进行覆盖:
1 2 3 4 5
| Skills skills = Skills.builder() .toolName("activate_skill", "load_skill") .toolDescription("activate_skill", "加载指定技能的完整指令") .toolParameterDescription("activate_skill", "skill_name", "要激活的技能名称") .build();
|
模式二:Shell 模式(ShellSkills)
类: ShellSkills(来自 langchain4j-experimental-skills-shell 模块)
⚠️ Shell 执行本质上是不安全的。 命令直接在宿主机进程环境中运行,没有任何沙箱、容器化或权限限制。一个行为异常或被提示词注入的 LLM 可以在运行你应用程序的机器上执行任意命令。仅在完全信任输入并愿意承担相关风险的受控环境中使用。
在此模式下,LLM 被赋予一个单一的 run_shell_command 工具,并使用 shell 命令直接从文件系统读取技能指令。没有 activate_skill 或 read_skill_resource 工具——LLM 像人类开发者一样浏览技能文件。
Shell 执行位于一个独立的实验性构件中——将其添加到你的构建中:
1 2 3 4 5
| <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-experimental-skills-shell</artifactId> <version>1.17.2-beta27</version> </dependency>
|
所有技能必须基于文件系统(通过 FileSystemSkillLoader 加载)。使用 ShellSkills 代替 Skills:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| import dev.langchain4j.skills.shell.ShellSkills; import dev.langchain4j.skill.FileSystemSkillLoader; import java.nio.file.Path; import java.util.List;
public class Main { public static void main(String[] args) { List<FileSystemSkill> skillList = FileSystemSkillLoader.loadSkills(Path.of("skills/")); ShellSkills skills = ShellSkills.from(skillList);
MyAiService service = AiServices.builder(MyAiService.class) .chatModel(chatModel) .toolProvider(skills.toolProvider()) // 注入 Shell 命令能力 .systemMessage( "你有以下技能可用:\n" + skills.formatAvailableSkills() + "\n在正式处理任务之前,请使用终端命令读取技能文件以了解具体操作步骤。" ) .build();
service.chat("帮我分析这份数据"); } }
interface MyAiService { String chat(String userMessage); }
|
formatAvailableSkills() 包含一个 <path> 字段,以便 LLM 确切知道在哪里找到每个 SKILL.md:
1 2 3 4 5 6 7
| <skills> <skill> <name>docx</name> <description>使用修订模式编辑和审阅 Word 文档</description> <path>/absolute/path/to/skills/docx/SKILL.md</path> </skill> </skills>
|
此模式最适合实验和原型开发,或者当你想使用社区发布的第三方技能(例如来自 agentskills.io 生态系统)而无需先将其移植到 Java 时。它让你快速搭建一个可工作的流程,然后随着方案的成熟将各个操作逐步迁移为工具。
配置 Shell 命令工具
使用 RunShellCommandToolConfig 来调整工作目录、输出限制和参数名称:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| import dev.langchain4j.skills.shell.RunShellCommandToolConfig; import java.nio.file.Path;
RunShellCommandToolConfig config = RunShellCommandToolConfig.builder() .name("run_shell_command") .description("执行 shell 命令来完成任务") .commandParameterName("command") .commandParameterDescription("要执行的 shell 命令") .timeoutSecondsParameterName("timeout_seconds") .timeoutSecondsParameterDescription("命令超时时间(秒),默认 30 秒") .maxStdOutChars(10000) .maxStdErrChars(10000) .workingDirectory(Path.of("/workspace")) .build();
ShellSkills skills = ShellSkills.builder() .toolConfig(config) .build();
|
Maven 依赖
工具模式(Skills)
1 2 3 4 5
| <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-skills</artifactId> <version>1.17.2-beta27</version> </dependency>
|
Shell 模式(ShellSkills)
1 2 3 4 5
| <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-experimental-skills-shell</artifactId> <version>1.17.2-beta27</version> </dependency>
|
总结对比
| 特性 |
工具模式(Skills) |
Shell 模式(ShellSkills) |
| 安全性 |
高——无文件系统访问,仅调用预定义工具 |
低——可直接执行任意 shell 命令 |
| 技能加载 |
预加载到内存,activate_skill 按需激活 |
LLM 通过 shell 命令直接读取文件 |
| 适用场景 |
生产环境、需要严格控制的场景 |
实验原型、快速验证、第三方技能 |
| 代码执行风险 |
无——类型安全的 Java 工具 |
有——需沙箱/容器隔离 |
| 工具列表 |
精简聚焦,技能激活后才暴露相关工具 |
单一 run_shell_command 工具 |
| 技能来源 |
文件系统、classpath、编程创建 |
仅文件系统 |