最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringAI?Agent開發(fā)秘籍如何讓javaer也可以用上Agent?Skills

 更新時(shí)間:2026年04月16日 11:14:56   作者:一灰灰blog  
文章介紹了如何使用SpringA和Skills構(gòu)建一個(gè)代碼評(píng)審應(yīng)用,通過實(shí)戰(zhàn)示例展示如何利用SpringAI和大模型實(shí)現(xiàn)零配置的代碼評(píng)審功能,感興趣的朋友跟隨小編一起看看吧

要說最近AI相關(guān)話題中什么最火,毫無疑問是Claude Skills,讓我感到震驚的倒不是它為什么火爆,而是SpringAI居然已經(jīng)迅速支持上Skills了,這效率真的是堪比??了。

誰說AI時(shí)代java開發(fā)者要掉隊(duì)了? 肉雖然不一定吃得上,但是喝口湯還是妥妥的

接下來我們通過構(gòu)建一個(gè)code reviewer, 來實(shí)際體驗(yàn)一把,如何將SpringAI和Skills結(jié)合起來使用

一、項(xiàng)目創(chuàng)建

1. 基礎(chǔ)環(huán)境要求

要體驗(yàn)SpringAI & Skills,目前需要升級(jí)到SpringAI 2.x版本,同時(shí)我們的SpringBoot也可以升級(jí)到4.x

  • SpringAI: 2.0.0-M2
  • JDK21
  • SpringBoot: 4.0.1

除了這幾個(gè)基本依賴之外,我們可以選擇一個(gè)支持Function Tool的大模型來作為這個(gè)實(shí)現(xiàn)的大腦中樞

我們這里選擇智譜的大模型GLM-4.5-Flash (原因就是因?yàn)樗赓M(fèi),且效果還行,對(duì)所有想體驗(yàn)的小伙伴沒有任何額外成本投入)

2. 項(xiàng)目創(chuàng)建

接下來我們創(chuàng)建一個(gè)SpringAI應(yīng)用,對(duì)于一個(gè)標(biāo)準(zhǔn)的SpringAI應(yīng)用,在pom.xml配置中,你會(huì)看到下面這些基礎(chǔ)版本指定,這個(gè)也沒什么好說的

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>4.0.1</version>
    <relativePath /> 
</parent>
<properties>
    <maven.compiler.source>21</maven.compiler.source>
    <maven.compiler.target>21</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <spring-ai.version>2.0.0-M2</spring-ai.version>
</properties>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <releases>
            <enabled>false</enabled>
        </releases>
    </repository>
    <repository>
        <name>Central Portal Snapshots</name>
        <id>central-portal-snapshots</id>
        <url>https://central.sonatype.com/repository/maven-snapshots/</url>
        <releases>
            <enabled>false</enabled>
        </releases>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
</repositories>

接下來重點(diǎn)看一下我們這個(gè)項(xiàng)目所用到的幾個(gè)核心依賴

<dependencies>
    <dependency>
        <groupId>org.springaicommunity</groupId>
        <artifactId>spring-ai-agent-utils</artifactId>
        <version>0.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-zhipuai</artifactId>
    </dependency>
</dependencies>
  • spring-ai-agent-utils: 這個(gè)就是SpringAI進(jìn)行agent開發(fā)的關(guān)鍵依賴包
  • spring-ai-starter-model-zhipuai: 這個(gè)是智譜大模型進(jìn)行交互的依賴包

3. 項(xiàng)目配置

依賴搞定之后,接下來就是在配置文件中,配置LLM訪問的相關(guān)信息、以及agent相關(guān)配置參數(shù),對(duì)應(yīng)的配置文件 resources/application.yml

spring:
  ai:
    zhipuai:
      # api-key 使用你自己申請(qǐng)的進(jìn)行替換;如果為了安全考慮,可以通過啟動(dòng)參數(shù)進(jìn)行設(shè)置
      api-key: ${zhipuai-api-key}
      chat: # 聊天模型
        options:
          model: GLM-4.5-Flash
## Agent Configuration
agent:
  skills:
    dirs: classpath:/.claude/skills
  model: GLM-4.5-Flash

這幾個(gè)配置看起來和之前SpringAI相關(guān)的并沒有太多的區(qū)別,其中 agent 相關(guān)的配置中,主要設(shè)置了skills的存放路徑,使用的model

根據(jù)上面的定義,我們將skills信息,放在resources/.claude/skills目錄下

新增一個(gè)目錄code-reviewer,目錄下的文件為 SKILL.md

.claude/skills/code-reviewer/
└── SKILL.md

對(duì)應(yīng)的內(nèi)容如下

---
name: code-reviewer
description: Reviews Java code for best practices, security issues, and Spring Framework conventions. Use when user asks to review, analyze, or audit code.
---
# Code Reviewer
## Instructions
在審查代碼時(shí):
1. 檢查是否存在安全漏洞(如SQL注入、XSS等)
2. 驗(yàn)證是否遵循了Spring Boot的最佳實(shí)踐(如正確使用@Service、@Repository等注解)
3. 查找潛在的空指針異常
4. 提出提高代碼可讀性和可維護(hù)性的建議
5. 提供具體的逐行反饋,并附上代碼示例
6. 以中文的方式返回代碼評(píng)審結(jié)果

4. Skills簡要說明

我們上面的Skill比較簡單,就是一個(gè)markdown文檔,SpringAI支持的Skills中,除了包含基本的SKILL.md文件(包含元數(shù)據(jù)(名稱和描述)以及指導(dǎo)代理如何執(zhí)行特定任務(wù)的說明)之外,還可以有相關(guān)的腳本、模板和參考資料

一個(gè)常見的skills結(jié)構(gòu)如下

my-skill/
├── SKILL.md          # Required: instructions + metadata
├── scripts/          # Optional: executable code
├── references/       # Optional: documentation
└── assets/           # Optional: templates, resources

二、核心實(shí)現(xiàn)

現(xiàn)在前置準(zhǔn)備已經(jīng)完成,接下來開始正式的體驗(yàn)吧

2.1 交互日志打印 MyLoggingAdvisor

為了讓系統(tǒng)與大模型之間的交互更清晰,我們將雙方交互的日志進(jìn)行更友好的打?。ㄒ岔槺憧匆幌?,一次用戶感知的問答過程中,實(shí)際上有幾次交互)

public class MyLoggingAdvisor implements BaseAdvisor {
    private final int order;
    public final boolean showSystemMessage;
    public final boolean showAvailableTools;
    private AtomicInteger cnt = new AtomicInteger(1);
    private MyLoggingAdvisor(int order, boolean showSystemMessage, boolean showAvailableTools) {
        this.order = order;
        this.showSystemMessage = showSystemMessage;
        this.showAvailableTools = showAvailableTools;
    }
    @Override
    public int getOrder() {
        return this.order;
    }
    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        System.out.println("======================= 第 " + cnt.getAndAdd(1) + " 輪 ====================================");
        StringBuilder sb = new StringBuilder("\nUSER: ");
        if (this.showSystemMessage && chatClientRequest.prompt().getSystemMessage() != null) {
            sb.append("\n - SYSTEM: ").append(first(chatClientRequest.prompt().getSystemMessage().getText(), 300));
        }
        if (this.showAvailableTools) {
            Object tools = "No Tools";
            if (chatClientRequest.prompt().getOptions() instanceof ToolCallingChatOptions toolOptions) {
                tools = toolOptions.getToolCallbacks().stream().map(tc -> tc.getToolDefinition().name()).toList();
            }
            sb.append("\n - TOOLS: ").append(ModelOptionsUtils.toJsonString(tools));
        }
        Message lastMessage = chatClientRequest.prompt().getLastUserOrToolResponseMessage();
        if (lastMessage.getMessageType() == MessageType.TOOL) {
            ToolResponseMessage toolResponseMessage = (ToolResponseMessage) lastMessage;
            for (var toolResponse : toolResponseMessage.getResponses()) {
                var tr = toolResponse.name() + ": " + first(toolResponse.responseData(), 1000);
                sb.append("\n - TOOL-RESPONSE: ").append(tr);
            }
        } else if (lastMessage.getMessageType() == MessageType.USER) {
            if (StringUtils.hasText(lastMessage.getText())) {
                sb.append("\n - TEXT: ").append(first(lastMessage.getText(), 1000));
            }
        }
        System.out.println("before: " + sb);
        return chatClientRequest;
    }
    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        StringBuilder sb = new StringBuilder("\nASSISTANT: ");
        if (chatClientResponse.chatResponse() == null || chatClientResponse.chatResponse().getResults() == null) {
            sb.append(" No chat response ");
            System.out.println("after: " + sb);
            return chatClientResponse;
        }
        for (var generation : chatClientResponse.chatResponse().getResults()) {
            var message = generation.getOutput();
            if (message.getToolCalls() != null) {
                for (var toolCall : message.getToolCalls()) {
                    sb.append("\n - TOOL-CALL: ")
                            .append(toolCall.name())
                            .append(" (")
                            .append(toolCall.arguments())
                            .append(")");
                }
            }
            if (message.getText() != null) {
                if (StringUtils.hasText(message.getText())) {
                    sb.append("\n - TEXT: ").append(first(message.getText(), 1200));
                }
            }
        }
        System.out.println("after: " + sb);
        return chatClientResponse;
    }
    private String first(String text, int n) {
        if (text.length() <= n) {
            return text;
        }
        return text.substring(0, n) + "...";
    }
    public static Builder builder() {
        return new Builder();
    }
    public static class Builder {
        private int order = 0;
        private boolean showSystemMessage = true;
        private boolean showAvailableTools = true;
        public Builder order(int order) {
            this.order = order;
            return this;
        }
        public Builder showSystemMessage(boolean showSystemMessage) {
            this.showSystemMessage = showSystemMessage;
            return this;
        }
        public Builder showAvailableTools(boolean showAvailableTools) {
            this.showAvailableTools = showAvailableTools;
            return this;
        }
        public MyLoggingAdvisor build() {
            MyLoggingAdvisor advisor = new MyLoggingAdvisor(this.order, this.showSystemMessage,
                    this.showAvailableTools);
            return advisor;
        }
    }
}

2.2 準(zhǔn)備用于評(píng)審的代碼

我們直接使用 實(shí)戰(zhàn) | 零基礎(chǔ)搭建知識(shí)庫問答機(jī)器人:基于SpringAI+RAG的完整實(shí)現(xiàn) 中的代碼分塊的內(nèi)容作為待評(píng)審的內(nèi)容,看下這段簡單的文本分塊工具會(huì)評(píng)審出什么內(nèi)容

package com.git.hui.springai.app.demo;
import org.springframework.ai.document.Document;
import java.util.ArrayList;
import java.util.List;
/**
 * 文檔分塊工具類
 * 將長文檔分割成較小的塊,以便更好地進(jìn)行向量化和檢索
 */
public class DocumentChunker {
    private final int maxChunkSize;
    private final int overlapSize;
    public static DocumentChunker DEFAULT_CHUNKER = new DocumentChunker();
    public DocumentChunker() {
        this(500, 50); // 默認(rèn)值:最大塊大小500個(gè)字符,重疊50個(gè)字符
    }
    public DocumentChunker(int maxChunkSize, int overlapSize) {
        this.maxChunkSize = maxChunkSize;
        this.overlapSize = overlapSize;
    }
    /**
     * 將文檔分割成塊
     * 
     * @param document 輸入文檔
     * @return 分割后的文檔塊列表
     */
    public List<Document> chunkDocument(Document document) {
        String content = document.getText();
        if (content == null || content.trim().isEmpty()) {
            return List.of(document);
        }
        List<String> chunks = splitText(content);
        List<Document> chunkedDocuments = new ArrayList<>();
        for (int i = 0; i < chunks.size(); i++) {
            String chunk = chunks.get(i);
            String chunkId = document.getId() + "_chunk_" + i;
            // 創(chuàng)建新的文檔塊,保留原始文檔的元數(shù)據(jù)
            Document chunkDoc = new Document(chunkId, chunk, new java.util.HashMap<>(document.getMetadata()));
            // 添加塊相關(guān)的元數(shù)據(jù)
            chunkDoc.getMetadata().put("chunk_index", i);
            chunkDoc.getMetadata().put("total_chunks", chunks.size());
            chunkDoc.getMetadata().put("original_document_id", document.getId());
            chunkedDocuments.add(chunkDoc);
        }
        return chunkedDocuments;
    }
    /**
     * 將文本分割成塊
     * 
     * @param text 輸入文本
     * @return 分割后的文本塊列表
     */
    private List<String> splitText(String text) {
        List<String> chunks = new ArrayList<>();
        // 按多種分隔符分割,優(yōu)先在語義邊界處分割(包括中文句號(hào)、問號(hào)、感嘆號(hào)等)
        String[] sentences = text.split("(?<=。)|(?<=!)|(?<=!)|(?<=?)|(?<=\\?)|(?<=\\n\\n)");
        StringBuilder currentChunk = new StringBuilder();
        for (String sentence : sentences) {
            // 跳過空句子
            if (sentence.trim().isEmpty()) {
                continue;
            }
            // 如果當(dāng)前塊加上新句子不超過最大大小,就添加到當(dāng)前塊
            if (currentChunk.length() + sentence.length() <= maxChunkSize) {
                if (currentChunk.length() > 0) {
                    currentChunk.append(sentence);
                } else {
                    currentChunk.append(sentence);
                }
            } else {
                // 如果當(dāng)前塊為空,但是單個(gè)句子太長,需要強(qiáng)制分割
                if (currentChunk.length() == 0) {
                    List<String> subChunks = forceSplit(sentence, maxChunkSize);
                    for (int i = 0; i < subChunks.size(); i++) {
                        String subChunk = subChunks.get(i);
                        // 如果不是最后一個(gè)子塊,添加到當(dāng)前塊并保存
                        if (i < subChunks.size() - 1) {
                            chunks.add(subChunk);
                        } else {
                            currentChunk.append(subChunk);
                        }
                    }
                } else {
                    // 保存當(dāng)前塊
                    chunks.add(currentChunk.toString());
                    // 開始新塊,包含重疊部分
                    currentChunk = new StringBuilder();
                    // 添加重疊部分,如果句子長度大于重疊大小,則只取末尾部分
                    if (sentence.length() > overlapSize) {
                        String overlap = sentence.substring(Math.max(0, sentence.length() - overlapSize));
                        currentChunk.append(overlap);
                        currentChunk.append(sentence);
                    } else {
                        currentChunk.append(sentence);
                    }
                }
            }
        }
        // 添加最后一個(gè)塊
        if (currentChunk.length() > 0) {
            chunks.add(currentChunk.toString());
        }
        return chunks;
    }
    /**
     * 強(qiáng)制將長文本分割成指定大小的塊
     * 
     * @param text 輸入文本
     * @param maxSize 最大塊大小
     * @return 分割后的文本塊列表
     */
    private List<String> forceSplit(String text, int maxSize) {
        List<String> chunks = new ArrayList<>();
        int start = 0;
        while (start < text.length()) {
            int end = Math.min(start + maxSize, text.length());
            String chunk = text.substring(start, end);
            chunks.add(chunk);
            start = end;
        }
        return chunks;
    }
    /**
     * 將多個(gè)文檔分別分割成塊
     * 
     * @param documents 輸入文檔列表
     * @return 分割后的文檔塊列表
     */
    public List<Document> chunkDocuments(List<Document> documents) {
        List<Document> allChunks = new ArrayList<>();
        for (Document document : documents) {
            allChunks.addAll(chunkDocument(document));
        }
        return allChunks;
    }
}

2.3 核心實(shí)現(xiàn)

配置Agent實(shí)現(xiàn)代碼評(píng)審

Bean定義與依賴注入

  • CommandLineRunner: Spring啟動(dòng)后自動(dòng)執(zhí)行的接口
  • ChatClient.Builder: 用于構(gòu)建聊天客戶端
  • @Value(“${agent.skills.dirs:Unknown}”): 注入配置屬性,獲取技能目錄資源列表

ChatClient配置鏈

  • 系統(tǒng)提示詞配置:
  • 技能工具配置:
    • SkillsTool.builder().addSkillsResources(agentSkillsDirs).build(): 動(dòng)態(tài)加載預(yù)定義的技能資源
    • FileSystemTools.builder().build(): 提供文件系統(tǒng)訪問能力
    • ShellTools.builder().build(): 提供命令行執(zhí)行能力
  • Advisor配置:
    • ToolCallAdvisor.builder().build(): 處理工具調(diào)用邏輯
    • MyLoggingAdvisor.builder().showAvailableTools(false).showSystemMessage(false).build(): 自定義日志記錄,隱藏工具和系統(tǒng)消息詳情

代碼評(píng)審執(zhí)行流程

請(qǐng)求執(zhí)行

  • prompt(): 構(gòu)建提示詞
  • .call(): 發(fā)起AI請(qǐng)求
  • .content(): 獲取返回結(jié)果
@Bean
CommandLineRunner commandLineRunner(ChatClient.Builder chatClientBuilder,
                                    @Value("${agent.skills.dirs:Unknown}") List<Resource> agentSkillsDirs) throws IOException {
    return args -> {
        ChatClient chatClient = chatClientBuilder // @formatter:off
                .defaultSystem("始終運(yùn)用現(xiàn)有技能協(xié)助用戶滿足其要求.")
                // Skills tool
                .defaultToolCallbacks(SkillsTool.builder().addSkillsResources(agentSkillsDirs).build())
                // 支持讀取系統(tǒng)文件內(nèi)容,用于讀取我們需要評(píng)審的代碼
                .defaultTools(FileSystemTools.builder().build())
                // 支持執(zhí)行腳本,如果skills中存在script,那么這些腳本的執(zhí)行,靠的就是它
                .defaultTools(ShellTools.builder().build())
                .defaultAdvisors(
                        // Tool Calling advisor
                        ToolCallAdvisor.builder().build(),
                        // Custom logging advisor
                        MyLoggingAdvisor.builder()
                                .showAvailableTools(false)
                                .showSystemMessage(false)
                                .build())
                .build();
        // @formatter:on
        var answer = chatClient
                // 下面具體的代碼位置,請(qǐng)根據(jù)實(shí)際的位置進(jìn)行替換
                .prompt("""
                        按照最佳實(shí)際的方式,評(píng)審下面的代碼實(shí)現(xiàn):
                         D:\\Workspace\\hui\\project\\spring-ai-demo\\v2\\T01-agentic-skills-simple-design\\src\\main\\java\\com\\git\\hui\\springai\\app\\demo\\DocumentChunker.java
                         """)
                .call()
                .content();
        System.out.println("The Answer: " + answer);
    };
}

2.4 執(zhí)行演示

接下來我們啟動(dòng)項(xiàng)目,驗(yàn)證一下效果如何(在啟動(dòng)命令行參數(shù)中,配置上大模型的api-key,當(dāng)然也可以直接在yml配置文件中進(jìn)行配置)

@Slf4j
@SpringBootApplication
public class T01Application {
    public static void main(String[] args) {
        SpringApplication.run(T01Application.class, args);
    }
}

從上面的截圖也可以看出,系統(tǒng)與大模型之間進(jìn)行了三輪對(duì)話,

  1. 第一輪: 用戶的原始訴求 -> 大模型
  • 大模型識(shí)別到需要進(jìn)行代碼評(píng)審,給系統(tǒng)響應(yīng) {"command":"code-reviewer"}
  1. 第二輪:系統(tǒng)接收到響應(yīng)之后,讀取skills的約束進(jìn)行響應(yīng)
  • 大模型返回需要讀取需要評(píng)審的代碼內(nèi)容,對(duì)應(yīng)的響應(yīng)為 {"filePath":"D:\\Workspace\\hui\\project\\spring-ai-demo\\v2\\T01-agentic-skills-simple-design\\src\\main\\java\\com\\git\\hui\\springai\\app\\demo\\DocumentChunker.java"}
  1. 第三輪:系統(tǒng)讀取代碼內(nèi)容,發(fā)送給大模型
  • 大模型返回評(píng)審結(jié)果

下面是完整的返回文本,有興趣的小伙伴可以看看

基于對(duì)代碼的詳細(xì)分析,我為您提供以下代碼評(píng)審報(bào)告:
## ?? 代碼評(píng)審報(bào)告 - DocumentChunker.java
### ? **優(yōu)點(diǎn)**
1. **功能完整性強(qiáng)**:代碼實(shí)現(xiàn)了完整的文檔分塊功能,包括:
   - 單文檔分塊
   - 批量文檔分塊
   - 智能語義邊界分割
   - 重疊機(jī)制
   - 長文本強(qiáng)制分割
2. **良好的文檔注釋**:類和方法都有清晰的中文注釋,說明了功能用途
3. **合理的默認(rèn)值**:提供了合理的默認(rèn)參數(shù)(maxChunkSize=500, overlapSize=50)
4. **元數(shù)據(jù)保留**:正確保留了原始文檔的元數(shù)據(jù),并添加了塊相關(guān)的元數(shù)據(jù)
### ?? **需要改進(jìn)的問題**
#### 1. **線程安全問題**
// 問題:靜態(tài)實(shí)例不是線程安全的
public static DocumentChunker DEFAULT_CHUNKER = new DocumentChunker();
**建議**:使用 `volatile` 或 `AtomicReference`,或者移除靜態(tài)實(shí)例讓用戶自己管理。
#### 2. **參數(shù)驗(yàn)證不足**
// 問題:構(gòu)造函數(shù)沒有參數(shù)驗(yàn)證
public DocumentChunker(int maxChunkSize, int overlapSize) {
    this.maxChunkSize = maxChunkSize;
    this.overlapSize = overlapSize;
}
**建議**:添加參數(shù)驗(yàn)證,確保 `maxChunkSize > 0` 且 `overlapSize < maxChunkSize`。
#### 3. **正則表達(dá)式復(fù)雜度**
// 問題:正則表達(dá)式過于復(fù)雜,難以維護(hù)
String[] sentences = text.split("(?<={。})|(?<=!)|(?<=!)|(?<=?)|(?<=\\?)|(?<=\\n\\n)");
**建議**:提取為常量或使用更清晰的方式處理多語言分隔符。
#### 4. **性能優(yōu)化空間**
// 問題:StringBuilder操作可以優(yōu)化
if (currentChunk.length() > 0) {
    currentChunk.append(sentence);
} else {
    currentChunk.append(sentence);
}
**建議**:簡化為 `currentChunk.append(sentence);`
#### 5. **異常處理缺失**
代碼中沒有處理可能的異常情況,如:
- 內(nèi)存不足
- 文檔過大導(dǎo)致性能問題
### ?? **具體改進(jìn)建議**
#### 1. **添加參數(shù)驗(yàn)證**
public DocumentChunker(int maxChunkSize, int overlapSize) {
    if (maxChunkSize <= 0) {
        throw new IllegalArgumentException("maxChunkSize must be positive");
    }
    if (overlapSize >= maxChunkSize) {
        throw new IllegalArgumentException("overlapSize must be less than maxChunkSize");
    }
    this.maxChunkSize = maxChunkSize;
    this.overlapSize = overlapSize;
}
#### 2. **線程安全改進(jìn)**
public static final DocumentChunker DEFAULT_CHUNKER = new DocumentChunker();
// 或者移除靜態(tài)實(shí)例,讓用戶自己管理
#### 3. **提取常量**
private static final String SENTENCE_SEPARATOR_PATTERN = 
    "(?<={。})|(?<=!)|(?<=!)|(?<=?)|(?<=\\?)|(?<=\\n\\n)";
#### 4. **添加Builder模式**
考慮使用Builder模式來更靈活地配置參數(shù)。
#### 5. **性能優(yōu)化**
// 優(yōu)化StringBuilder操作
private List<String> splitText(String text) {
    List<String> chunks = new ArrayList<>();
    String[] sentences = text.split(SENTENCE_SEPARATOR_PATTERN);
    StringBuilder currentChunk = new StringBuilder();
    for (String sentence : sentences) {
        if (sentence.trim().isEmpty()) continue;
        if (currentChunk.length() + sentence.length() <= maxChunkSize) {
            currentChunk.append(sentence);
        } else {
            // 處理塊分割邏輯
        }
    }
    // ...
}
### ?? **總體評(píng)價(jià)**
這是一個(gè)功能完整、設(shè)計(jì)合理的文檔分塊工具類,主要問題集中在線程安全、參數(shù)驗(yàn)證和代碼可維護(hù)性方面。通過上述改進(jìn),可以顯著提升代碼的健壯性和可維護(hù)性。
**建議優(yōu)先級(jí)**:
1. 高優(yōu)先級(jí):參數(shù)驗(yàn)證、線程安全
2. 中優(yōu)先級(jí):性能優(yōu)化、代碼簡化
3. 低優(yōu)先級(jí):Builder模式、異常處理

三、小結(jié)

SpringAI的Agent開發(fā)范式配合Skills機(jī)制,非常簡單就實(shí)現(xiàn)了AI應(yīng)用的工程化。整個(gè)過程實(shí)現(xiàn)下來,門檻還是比較低的。 不得不高喊一聲:Spring????

實(shí)現(xiàn)方式雖然簡單,但是這個(gè)背后的設(shè)計(jì)哲學(xué)、開發(fā)思維的轉(zhuǎn)變,還是很值得我們學(xué)習(xí)參考的——在AI時(shí)代,如何將我們現(xiàn)有的技能(如可復(fù)用、模塊化思設(shè)計(jì)等)轉(zhuǎn)變到大模型應(yīng)用開發(fā),這可能是我們每一個(gè)舊時(shí)代程序員最大的財(cái)富。

那么這一套是怎么實(shí)現(xiàn)的呢?

Spring AI采用基于工具的集成方法,通過實(shí)現(xiàn)各種工具,使任何LLM都能回調(diào)執(zhí)行,Skills的運(yùn)行過程,通常是下面三步:

  1. 發(fā)現(xiàn)(啟動(dòng)階段)
  • 通過SKILL.md文件中的元數(shù)據(jù),快速實(shí)現(xiàn)技能的安裝注冊(cè)
  1. 語義匹配(對(duì)話過程中)
  • 當(dāng)用戶發(fā)出請(qǐng)求時(shí),LLM 會(huì)檢查工具定義中嵌入的技能描述。如果 LLM 判斷用戶請(qǐng)求在語義上與某個(gè)技能的描述匹配,則會(huì)調(diào)用該技能工具,并將技能名稱作為參數(shù)傳遞給它。
  1. 執(zhí)行(技能調(diào)用時(shí))
  • 當(dāng)調(diào)用技能工具時(shí),SkillsTool會(huì)從磁盤加載完整的SKILL.md內(nèi)容,并將其與技能的基礎(chǔ)目錄路徑一起返回給大型語言模型(LLM)。然后,LLM會(huì)按照技能內(nèi)容中的指令執(zhí)行。如果技能引用了其他文件或輔助腳本,LLM會(huì)使用FileSystemToolsRead函數(shù)或ShellToolsBash函數(shù)來按需訪問它們

項(xiàng)目源碼:

實(shí)戰(zhàn)

參考:

到此這篇關(guān)于SpringAI Agent開發(fā)秘籍如何讓javaer也可以用上Agent Skills的文章就介紹到這了,更多相關(guān)SpringAI Agent Skills使用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • springboot yml配置文件定義list集合、數(shù)組和map以及使用中的錯(cuò)誤

    springboot yml配置文件定義list集合、數(shù)組和map以及使用中的錯(cuò)誤

    這篇文章主要介紹了springboot yml配置文件定義list集合、數(shù)組和map以及使用中遇到的錯(cuò)誤問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 解決spring jpa中update的坑

    解決spring jpa中update的坑

    這篇文章主要介紹了spring jpa中update遇到的坑及解決方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • springboot?vue接口測試前后端樹節(jié)點(diǎn)編輯刪除功能

    springboot?vue接口測試前后端樹節(jié)點(diǎn)編輯刪除功能

    這篇文章主要為大家介紹了springboot?vue接口測試前后端樹節(jié)點(diǎn)編輯刪除功能,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-05-05
  • struts2數(shù)據(jù)處理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    struts2數(shù)據(jù)處理_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Struts2框架框架使用OGNL語言和值棧技術(shù)實(shí)現(xiàn)數(shù)據(jù)的流轉(zhuǎn)處理。下面通過本文給大家分享struts2數(shù)據(jù)處理的相關(guān)知識(shí),感興趣的朋友參考下吧
    2017-09-09
  • Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果

    Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果

    這篇文章主要為大家詳細(xì)介紹了Java中一個(gè)線程執(zhí)行死循環(huán)有什么后果,當(dāng)一個(gè)線程在執(zhí)行死循環(huán)時(shí)會(huì)影響另外一個(gè)線程嗎,下面為大家揭曉
    2016-05-05
  • 詳解springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑

    詳解springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑

    本文主要介紹了springboot使用異步注解@Async獲取執(zhí)行結(jié)果的坑,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java在PDF中添加表格過程詳解

    Java在PDF中添加表格過程詳解

    這篇文章主要介紹了Java在PDF中添加表格過程詳解,本文將介紹通過Java編程在PDF文檔中添加表格的方法。添加表格時(shí),可設(shè)置表格邊框、單元格對(duì)齊方式、單元格背景色、單元格合并、插入圖片、設(shè)置行高、列寬、字體、字號(hào)等,需要的朋友可以參考下
    2019-07-07
  • Java并發(fā)編程之線程間的通信

    Java并發(fā)編程之線程間的通信

    當(dāng)線程在系統(tǒng)內(nèi)運(yùn)行時(shí),程序通常無法準(zhǔn)確的控制線程的輪換執(zhí)行,但我們可以通過一些機(jī)制來保障線程的協(xié)調(diào)運(yùn)行,本文著重講解線程間的通信機(jī)制
    2021-06-06
  • Java實(shí)現(xiàn)md5和base64加密解密的示例代碼

    Java實(shí)現(xiàn)md5和base64加密解密的示例代碼

    這篇文章主要介紹了Java實(shí)現(xiàn)md5和base64加密解密的示例代碼,幫助大家更好的利用Java加密解密文件,感興趣的朋友可以了解下
    2020-09-09
  • JDK集合源碼之解析TreeMap(二)

    JDK集合源碼之解析TreeMap(二)

    下面小編就為大家?guī)硪黄獪\談java中的TreeMap 排序與TreeSet 排序。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2021-07-07

最新評(píng)論

宜阳县| 内乡县| 万源市| 康平县| 金坛市| 北川| 罗定市| 江陵县| 棋牌| 叙永县| 安庆市| 太仆寺旗| 视频| 临沭县| 永和县| 清远市| 米泉市| 炉霍县| 顺平县| 昌邑市| 卓尼县| 张家港市| 太和县| 纳雍县| 吉木萨尔县| 婺源县| 奉节县| 梓潼县| 历史| 巩留县| 湖州市| 乐清市| 昌邑市| 安丘市| 建瓯市| 泗洪县| 东光县| 都兰县| 织金县| 尚志市| 馆陶县|