【learn-claude-code】S05SkillLoading - 技能加载:用时再加载,不用不加载

张开发
2026/4/9 5:39:13 15 分钟阅读

分享文章

【learn-claude-code】S05SkillLoading - 技能加载:用时再加载,不用不加载
核心理念“技能是模型 KNOWS 的东西工具是模型 CAN DO 的东西” – 按需注入知识不预设工作流。源码https://github.com/xiayongchao/learn-claude-code-4j/blob/main/src/main/java/org/jc/agents/S05SkillLoading.java原版https://github.com/shareAI-lab/learn-claude-code上篇S04Subagent - 子 Agent上篇回顾上篇文章我们实现了 Subagent 机制通过独立的 messages[] 隔离子任务的上下文不污染父 Agent 的对话历史。问题知识注入有两种方式预设到 System Prompt- 全部加载上下文膨胀按需注入到 tool_result- 用时加载不用不加载哪种更好显然是第二种。但怎么实现按需解决方案------------------ ------------------ | SkillLoader | | SKILLS_DIR | | | --- | /skills | | getContent(name)| | /agent-builder| | getDescriptions()| | /SKILL.md | ------------------ | /mcp-builder | | | /SKILL.md | v ------------------ ---------------- | load_skill tool | | name: agent-builder | ------------------ | v skill nameagent-builder # Agent Builder Build AI agents for any domain... /skill核心思想通过load_skill工具按需加载专业知识返回skill标签包裹的内容。Java 实现详解1. 技能文件结构src/main/resources/skills/ └── excel/ ├── SKILL.md └── scripts/ # Java 脚本目录JBang 调用 └── ExcelTool.java2. SKILL.md 格式YAML Front Matter--- name: excel description: | 基于JavaJBang实现的Excel自动化操作技能无需手动安装依赖、无需配置环境、无需打包。 支持自动检测并安装JBang运行环境支持读取Excel、导出Excel、指定Sheet读取、自定义输入输出路径。 工具类统一放在 scripts/ 目录下与SKILL.md同级。 适用于脚本调用、自动化任务、批处理、外部系统调用、定时调度等场景。 Keywords: excel, jbang, 自动安装, 动态依赖, 命令行, 导入, 导出, 脚本 --- # Excel 操作技能 基于 Java JBang 实现真正做到零配置运行。 ## JBang 机制 **JBang**Java 脚本运行器自动下载 JDK 依赖 特点 - 无需手动安装 JDK - 自动下载依赖pom.xml 声明的库 - 一行命令执行jbang run ExcelTool.java --input data.xlsx --output result.xlsx ## 使用方式 bash # 自动检测并安装 JBang执行 Excel 操作 jbang scripts/ExcelTool.java --input path/to/input.xlsx --output path/to/output.xlsx核心功能读取 Excel支持指定 Sheet导出 Excel自定义输入输出路径…### 3. SkillLoader技能加载器 java public class SkillLoader { private final String skillsDir; private final MapString, Skill skills new HashMap(); public SkillLoader(String skillsDir) { this.skillsDir skillsDir; loadAll(); // 启动时扫描所有 SKILL.md } private void loadAll() { var dirPath Paths.get(skillsDir); if (!Files.exists(dirPath)) { return; // 目录不存在则跳过 } try { Listjava.nio.file.Path files Files.walk(dirPath) .filter(Files::isRegularFile) .filter(p - SKILL.md.equals(p.getFileName().toString())) .sorted() // 排序保证顺序一致 .toList(); for (var file : files) { String text Files.readString(file); ParseResult result parseFrontMatter(text); String name result.getMeta().getOrDefault(name, file.getParent().getFileName().toString()); Skill skill new Skill(); skill.meta result.getMeta(); skill.body result.getBody(); skill.path file.toString(); // 记录文件路径 skills.put(name, skill); } } catch (Exception e) { e.printStackTrace(); } } public String getContent(String args) { String name JSON.parseObject(args).getString(name); if (!skills.containsKey(name)) { return 错误未知技能 name 。 可用的技能: String.join(, , skills.keySet()); } Skill skill skills.get(name); return skill name\ name \\n skill.body \n/skill; } public String getDescriptions() { if (skills.isEmpty()) { return (无可用技能); } ListString lines new ArrayList(); for (var entry : skills.entrySet()) { String name entry.getKey(); Skill skill entry.getValue(); String desc skill.meta.getOrDefault(description, 无描述); String tags skill.meta.getOrDefault(tags, ); String line - name : desc; if (!tags.isBlank()) { line [ tags ]; // 支持 Tags 显示 } lines.add(line); } return String.join(\n, lines); } }4. 前置元数据解析privateParseResultparseFrontMatter(Stringtext){PatternpatternPattern.compile(^---\\n(.*?)\\n---\\n(.*),Pattern.DOTALL);Matchermatcherpattern.matcher(text);MapString,StringmetanewHashMap();Stringbodytext;if(matcher.find()){StringmetaStrmatcher.group(1).trim();bodymatcher.group(2).trim();YamlyamlnewYaml();metayaml.load(metaStr);}returnnewParseResult(meta,body);}5. 工具注册privatestaticfinalSkillLoaderSKILL_LOADERnewSkillLoader(Commons.SKILLS_DIR);privatestaticfinalMapString,FunctionString,StringTOOL_HANDLERSnewHashMap();static{TOOL_HANDLERS.put(bash,Tools::runBash);TOOL_HANDLERS.put(readFile,Tools::runReadFile);TOOL_HANDLERS.put(writeFile,Tools::runWriteFile);TOOL_HANDLERS.put(editFile,Tools::runEditFile);TOOL_HANDLERS.put(loadSkill,SKILL_LOADER::getContent);// 新增}privatestaticfinalListChatCompletionTooltoolsList.of(Tools.bashTool(),Tools.readFileTool(),Tools.writeFileTool(),Tools.editFileTool(),Tools.loadSkillTool()// 新增);6. SYSTEM 提示词privatestaticfinalStringSYSTEM你是工作目录 Commons.CWD 下的编程智能体遇到陌生业务场景前请先调用 load_skill 加载专属专业知识。可用技能列表SKILL_LOADER.getDescriptions()\n\n如果需要进一步加载资源或脚本可以在 Commons.SKILLS_DIR 目录下进行搜索禁止自己创建资源或脚本文件;生成的 SYSTEM 示例你是工作目录 /path/to/project 下的编程智能体... 可用技能列表 - excel-operation-skill: 基于JavaJBang实现的Excel自动化操作技能 [excel, jbang] - mcp-builder: Build MCP servers for any service [mcp, server, api] 如果需要进一步加载资源或脚本可以在 skills/ 目录下进行搜索禁止自己创建资源或脚本文件7. loadSkillTool 定义publicstaticChatCompletionToolloadSkillTool(){MapString,JsonValueparamMapnewHashMap();paramMap.put(type,JsonValue.from(object));MapString,JsonValuenamePropnewHashMap();nameProp.put(type,JsonValue.from(string));nameProp.put(description,JsonValue.from(技能名称如 excel));paramMap.put(properties,JsonValue.from(Map.of(name,JsonValue.from(nameProp))));paramMap.put(required,JsonValue.from(List.of(name)));returnChatCompletionTool.ofFunction(ChatCompletionFunctionTool.builder().function(FunctionDefinition.builder().name(loadSkill).description(加载技能文档获取专业领域知识).parameters(FunctionParameters.builder().putAllAdditionalProperties(paramMap).build()).build()).build());}工具 vs 技能核心区别维度工具 (Tool)技能 (Skill)作用模型 CAN DO模型 KNOWS存在形式可执行代码文档/Markdown注入方式工具定义 (JSON Schema)skill标签包裹的文本加载时机始终可用按需调用示例bash, readFileexcel相对 s04 的变更组件s04s05Tools5 (基础) task5 (基础) loadSkill知识管理无SkillLoader 按需加载SYSTEM固定提示动态注入技能列表试试看有哪些可用技能加载 excel 技能帮我把 data.xlsx 的第一个 sheet 导出为 CSV核心要义“Load knowledge when you need it, not upfront”知识按需加载工具始终在线设计原则工具是能力技能是知识工具预定义技能按需加载skill标签包裹返回内容方便模型识别

更多文章