Featured image of post Claude Code本地Skill与Reference调用大模型-实操笔记

Claude Code本地Skill与Reference调用大模型-实操笔记

Claude Code 本地 Skill 与 Reference 调用大模型

一、技能概述

1.1 核心定义

Claude Code 是 Anthropic 官方推出的命令行工具,通过自然语言交互帮助开发者完成软件工程任务。本技能聚焦于如何在 Claude Code 中利用本地 Skill 系统Reference 参考知识库来调用大语言模型,实现精准的领域知识问答与任务执行。

Skill(技能)是 Claude Code 的核心扩展机制,允许开发者将领域知识、指令模板和参考文档打包成可复用的单元,供 LLM 在对话中调用。Reference(参考知识库)则是 Skill 的重要组成部分,提供了 LLM 回答问题所需的事实依据和上下文信息。

参考来源:Anthropic 官方 Claude Code 文档、Spring AI 官方文档

1.2 学习背景

在 AI 面试平台项目中,需要实现多方向面试出题(Java后端、算法、前端、系统设计等)、结合简历信息生成个性化问题、调用大模型进行答案评估。

传统的做法是硬编码 Prompt,但这种方式难以维护、扩展性差。通过 Skill + Reference 机制,可以将领域知识与调用逻辑分离,实现知识模块化、动态加载和灵活分发。

1.3 发展历程与行业对比

维度 Claude Code Skill OpenAI GPTs LangChain Agents
知识组织 Markdown + YAML JSON 配置 Python 代码
Reference 机制 文件引用 + 分类索引 矢量数据库 文档加载器
调用方式 Tool Call 自动触发 手动选择 工具链编排
适用场景 面试问答、代码审查 通用对话 复杂推理任务

二、核心知识点

2.1 Skill 系统架构

2.1.1 目录结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
resources/skills/
├── _shared/
│   └── references/          # 跨 Skill 共享参考知识
│       ├── java.md
│       ├── mysql.md
│       ├── redis.md
│       └── spring.md
├── java-backend/
│   ├── SKILL.md             # Skill 主定义文件
│   ├── skill.meta.yml       # 元数据配置
│   └── references/          # Skill 私有参考(可选)
├── algorithm/
├── frontend/
└── system-design/

2.1.2 SKILL.md 格式规范

SKILL.md 采用 YAML front matter + Markdown body 的混合格式:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
---
name: java-backend
description: 用于 Java 后端面试出题,涵盖集合、并发、JVM 等核心知识
---

# Overview
你是一位资深的 Java 后端面试官...

# Instructions
1. 优先结合候选人简历中的技术栈出题
2. 每道题需包含难度评级和考察点

front matter 字段说明

字段 类型 必填 说明
name string Skill 唯一标识符
description string Skill 功能描述,供 LLM 判断调用时机

2.1.3 skill.meta.yml 元数据

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
displayName: Java 后端开发
display:
  icon: 
  gradient: from-blue-500 to-indigo-500
categories:
  - key: JAVA
    label: Java
    priority: CORE
    ref: java.md
    shared: true

2.2 Reference 参考知识库

2.2.1 Reference 文件格式

Reference 文件为纯 Markdown,支持多级标题和代码块。内容示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
## Java 集合框架

### ArrayList vs LinkedList

| 特性 | ArrayList | LinkedList |
|------|-----------|------------|
| 查找复杂度 | O(1) | O(n) |
| 插入复杂度 | O(n) | O(1) |

### 扩容机制
​```java 
int newCapacity = oldCapacity + (oldCapacity >> 1);  // 1.5倍
​```
! 注意:扩容因子 1.5 是为了减少频繁扩容带来的性能损耗

2.2.2 分类索引机制

_shared/references/ 下的文件会被索引为共享知识,所有 Skill 均可访问:

1
2
3
4
5
6
categories:
  - key: JAVA
    label: Java
    priority: CORE
    ref: java.md      # 指向 _shared/references/java.md
    shared: true

当 LLM 调用 Skill 工具时,Agent Utils 会根据 skill.meta.yml 中的 categories 配置,将对应的 Reference 内容注入到上下文中。

2.3 Spring AI 集成

2.3.1 LlmProviderRegistry 多 Provider 管理

 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
@Configuration
public class LlmProviderRegistry {

    private final Map<String, ChatClient> clientCache = new ConcurrentHashMap<>();
    
    // 获取带 SkillsTool 的 ChatClient(面试出题用)
    public ChatClient getChatClientOrDefault(String provider) {
        return clientCache.computeIfAbsent(provider, this::createChatClient);
    }
    
    // 获取不带 SkillsTool 的 ChatClient(语音面试用)
    public ChatClient getPlainChatClient(String provider) {
        return clientCache.computeIfAbsent(provider + "-plain", this::createPlainChatClient);
    }
    
    private ChatClient createChatClient(String provider) {
        var config = providers.get(provider);
        var chatModel = OpenAiChatModel.builder()
            .baseUrl(config.getBaseUrl())
            .apiKey(config.getApiKey())
            .model(config.getModel())
            .build();
    
        return ChatClient.builder(chatModel)
            .defaultTools(interviewSkillsToolCallback())  // 注入 SkillsTool
            .build();
    }
}

2.3.2 配置示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
app:
  ai:
    default-provider: dashscope
    providers:
      dashscope:
        base-url: https://dashscope.aliyuncs.com/compatible-mode
        api-key: ${AI_BAILIAN_API_KEY}
        model: qwen-plus
      lmstudio:
        base-url: http://localhost:1234
        api-key: lm-studio
        model: qwen2.5-7b-instruct
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

### 2.4 StructuredOutputInvoker 结构化输出

#### 2.4.1 核心原理

结构化输出通过以下机制保证 JSON 格式正确:

1. **BeanOutputConverter**:根据 Java record 类定义自动生成 JSON Schema
2. **重试机制**:解析失败时注入修复指令 + 上次错误原因
3. **Format 注入**:将 JSON Schema 以注释形式追加到 system prompt

#### 2.4.2 输出格式生成

​```java
// 定义接收 DTO
private record QuestionListDTO(List<QuestionDTO> questions) {}
private record QuestionDTO(String question, String type, String category,
    String topicSummary, List<String> followUps) {}

// 创建 converter
BeanOutputConverter<QuestionListDTO> outputConverter =
    new BeanOutputConverter<>(QuestionListDTO.class);

// outputConverter.getFormat() 返回 JSON Schema 说明

三、实操步骤

3.1 环境准备

3.1.1 项目依赖

确保 build.gradle 包含以下依赖:

1
2
3
4
5
6
7
8
dependencies {
    // Spring AI Core
    implementation 'org.springframework.ai:spring-ai-openai:1.0.0'
    // Agent Utils(SkillsTool)
    implementation 'org.springframework.ai:spring-ai-agent-utils:1.0.0'
    // Micrometer 监控
    implementation 'io.micrometer:micrometer-registry-prometheus'
}

3.1.2 配置文件

application.yml 中配置 AI 提供者:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
app:
  ai:
    default-provider: dashscope
    structured-max-attempts: 2
    structured-include-last-error: true
    structured-retry-use-repair-prompt: true
    providers:
      dashscope:
        base-url: https://dashscope.aliyuncs.com/compatible-mode
        api-key: ${AI_BAILIAN_API_KEY}
        model: qwen-plus
    agent-utils:
      skills-root: classpath:skills

3.2 创建自定义 Skill

3.2.1 步骤一:创建目录结构

1
2
mkdir -p resources/skills/java-backend/references
mkdir -p resources/skills/java-backend/img

3.2.2 步骤二:编写 SKILL.md

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
---
name: java-backend
description: Java 后端面试出题技能,涵盖集合、并发、JVM、Spring 生态等
---

# Role
你是一位拥有 10 年经验的 Java 后端技术专家...

# Capabilities
1. 结合简历信息生成个性化面试题
2. 根据难度级别调整题目复杂度

# Output Format
每个问题包含:
- question: 主问题
- type: SINGLE_CHOICE / MULTIPLE_CHOICE / CODING / DISCUSSION
- category: 问题分类
- topicSummary: 考察知识点摘要
- followUps: 追问列表

3.2.3 步骤三:编写 skill.meta.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
displayName: Java 后端开发
display:
  icon: 
  gradient: from-blue-500 to-indigo-500
categories:
  - key: JAVA
    label: Java 基础
    priority: CORE
    ref: java.md
    shared: true
  - key: CONCURRENT
    label: 并发编程
    priority: CORE
    ref: concurrent.md
    shared: false

3.2.4 步骤四:编写 Reference 文件

resources/skills/_shared/references/java.md:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
## Java 集合框架

### List 接口实现类对比

| 类 | 底层结构 | 查找 | 插入/删除 | 线程安全 |
|----|----------|------|-----------|----------|
| ArrayList | 动态数组 | O(1) | O(n) | 否 |
| LinkedList | 双向链表 | O(n) | O(1) | 否 |
| Vector | 动态数组 | O(1) | O(n) | 是 |

### 扩容机制

​```java
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // 1.5倍
}
> ! 警示:避免在 ArrayList 头部频繁插入删除操作,时间复杂度 O(n)

3.3 配置 SkillsTool Bean

3.3.1 AgentUtilsConfiguration

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
​```java
@Configuration
public class AgentUtilsConfiguration {

    @Bean("interviewSkillsToolCallback")
    public ToolCallback interviewSkillsToolCallback(ResourceLoader resourceLoader) {
        return SkillsTool.builder()
            .addSkillsResource(resourceLoader.getResource("classpath:skills"))
            .build();
    }
}

3.3.2 注入到 LlmProviderRegistry

1
2
3
4
5
@Bean
public LlmProviderRegistry llmProviderRegistry(
        @Qualifier("interviewSkillsToolCallback") ToolCallback callback) {
    return new LlmProviderRegistry(callback);
}

3.4 完整调用示例

3.4.1 面试出题服务

 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
34
35
36
37
38
39
40
@Service
@RequiredArgsConstructor
public class InterviewQuestionService {

    private final LlmProviderRegistry llmProviderRegistry;
    private final StructuredOutputInvoker structuredOutputInvoker;

    private static final String SYSTEM_PROMPT_PATH = "prompts/interview-question-skill.st";
    private static final String USER_PROMPT_PATH = "prompts/interview-question-skill-user.st";

    public QuestionListDTO generateQuestions(GenerateQuestionRequest request) {
        // 1. 获取 ChatClient(已注入 SkillsTool)
        ChatClient chatClient = llmProviderRegistry.getChatClientOrDefault("dashscope");

        // 2. 定义接收 DTO
        BeanOutputConverter<QuestionListDTO> converter =
            new BeanOutputConverter<>(QuestionListDTO.class);

        // 3. 加载并渲染模板
        String systemPrompt = loadTemplate(SYSTEM_PROMPT_PATH).render()
            + "\n\n" + converter.getFormat();
        String userPrompt = loadTemplate(USER_PROMPT_PATH).render(Map.of(
            "questionCount", request.getCount(),
            "skillName", request.getSkillName()
        ));

        // 4. 调用(自动重试 + 结构化解析)
        return structuredOutputInvoker.invoke(
            chatClient, systemPrompt, userPrompt, converter,
            ErrorCode.INTERVIEW_QUESTION_GENERATION_FAILED,
            "出题失败:", "面试出题", log
        );
    }

    private PromptTemplate loadTemplate(String path) {
        return new PromptTemplate(
            resourceLoader.getResource(path).getContentAsString(StandardCharsets.UTF_8)
        );
    }
}

3.4.2 Prompt 模板示例

resources/prompts/interview-question-skill.st:

1
2
3
4
5
6
7
8
# System Prompt
你是一位专业的面试官。请根据以下信息生成面试题目。

## Skill 指令
调用 Skill 工具获取 {skillName} 的参考知识

## 输出格式
{outputFormat}

resources/prompts/interview-question-skill-user.st:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# User Prompt
请生成 {questionCount} 道面试题

## 难度级别
{difficultyDescription}

## 面试方向
{skillName}{skillDescription}

## 已考知识点(避免重复)
{historicalSection}

3.5 结果验证

3.5.1 成功验证

1
2
3
QuestionListDTO result = interviewQuestionService.generateQuestions(request);
assert result.questions().size() == request.getCount();
assert result.questions().get(0).followUps() != null;

3.5.2 日志输出

1
2
INFO  InterviewQuestionService - 出题成功:方向=Java后端, 题目数=6, 耗时=1.2s
INFO  StructuredOutputInvoker - 结构化输出成功:attempt=1, model=qwen-plus

四、问题与解决方案

4.1 常见问题

4.1.1 LLM 不触发 Skill 调用

问题描述:对话中 LLM 没有自动调用 Skill 工具,知识库内容未注入上下文。

排查步骤

  1. 检查 SKILL.mddescription 字段是否清晰表达了 Skill 功能
  2. 确认 skill.meta.ymlcategories 配置了有效的 ref 指向
  3. 验证 LlmProviderRegistry 创建 ChatClient 时传入了 SkillsTool callback
  4. 查看日志确认 Skill 加载成功
1
2
grep "SkillsTool" application.log
# 期望输出:SkillsTool initialized with X skills

解决方案:确保 SkillsTool 被正确注册为 default tool callback

1
2
3
4
5
6
// LlmProviderRegistry.java
private ChatClient createChatClient(String provider) {
    return ChatClient.builder(chatModel)
        .defaultTools(interviewSkillsToolCallback)  // 关键:注入
        .build();
}

4.1.2 结构化输出解析失败

问题描述:LLM 返回的 JSON 格式不符合 DTO 定义,重试多次仍失败。

完整报错

1
2
Caused by: com.fasterxml.jackson.databind.exc.MismatchedInputException:
Cannot deserialize value of type `java.util.List<QuestionDTO>` from Array

排查步骤

  1. 检查 DTO 定义与 outputConverter.getFormat() 输出是否匹配
  2. 确认 Prompt 中是否正确注入了 JSON Schema
  3. 查看重试日志定位失败原因
1
2
grep "StructuredOutputInvoker" application.log
# 期望输出:Retry attempt=X, lastError=XXX

解决方案:启用修复型重试提示词

1
2
3
4
5
6
app:
  ai:
    structured-retry-use-repair-prompt: true
    structured-retry-append-strict-json-instruction: true
    structured-include-last-error: true
    structured-error-message-max-length: 200

4.2 拓展问题

4.2.1 如何支持离线场景?

方案:使用本地 LLM(如 LM Studio)替代云端 API

1
2
3
4
5
6
7
8
app:
  ai:
    default-provider: lmstudio
    providers:
      lmstudio:
        base-url: http://localhost:1234
        api-key: lm-studio
        model: qwen2.5-7b-instruct

4.2.2 如何动态切换 Skill 目录?

方案:通过配置属性注入不同的 SkillsTool 根路径

1
2
3
4
5
6
7
8
@Bean("customSkillsToolCallback")
public ToolCallback customSkillsToolCallback(
        @Value("${app.skills.root}") String skillsRoot,
        ResourceLoader resourceLoader) {
    return SkillsTool.builder()
        .addSkillsResource(resourceLoader.getResource(skillsRoot))
        .build();
}

五、学习总结与拓展

5.1 核心收获

  1. Skill 系统设计:通过 YAML front matter + Markdown body 格式,实现领域知识的模块化封装
  2. Reference 索引机制:共享 Reference 与私有 Reference 分层管理,节省 Token 消耗
  3. 结构化输出原理:通过 JSON Schema 生成 + 修复型重试提示词,确保 LLM 输出格式可靠
  4. 多 Provider 架构:LlmProviderRegistry 统一管理多个 AI 提供者,支持本地/云端灵活切换

5.2 未掌握的知识点

  • Spring AI 的 Tool Call 底层机制(StreamInterceptor 拦截原理)
  • Agent Utils 的运行时 Skill 动态更新机制
  • 多轮对话中 Reference 内容的上下文累积策略

5.3 拓展方向

  1. 进阶学习路径:基础使用 → 多 Skill 编排 → 动态 Skill 加载 → Tool Call 性能优化

  2. 相关技术栈

    • Spring AI + Vector DB(知识库向量化)
    • Claude Code + MCP(Model Context Protocol)
    • ReAct 模式(推理 + 行动协同)
  3. 行业前沿应用

    • 大厂 AI 面试系统:结合简历解析 + 实时语音 + 多维度评估
    • 法律/医疗专业问答:领域知识库 + 引用溯源

5.4 资源推荐

资源 类型 推荐理由
Spring AI 官方文档 官方文档 最权威的 AI 集成框架文档
Anthropic Claude Code 文档 官方文档 Skill 系统设计原理解释
spring-ai-agent-utils GitHub 开源项目 SkillsTool 源码参考

附录:关键文件清单

文件路径 用途
common/ai/LlmProviderRegistry.java 多 Provider 注册与缓存
common/ai/StructuredOutputInvoker.java 结构化输出 + 重试封装
common/ai/AgentUtilsConfiguration.java SkillsTool Bean 配置
modules/interview/skill/InterviewSkillService.java Skill 加载与解析服务
resources/skills/*/SKILL.md Skill 主定义文件
resources/skills/*/skill.meta.yml Skill 元数据配置
resources/skills/_shared/references/*.md 共享参考知识库
resources/prompts/*.st Prompt 模板文件
使用 Hugo 构建
主题 StackJimmy 设计

发布了 35 篇文章 | 共 90422 字