Spring AI這樣玩才高級!注解式AI服務(wù)開發(fā),這些技巧 GitHub 都找不到
AI Services是什么
AI Services 的設(shè)計(jì)靈感來源于 Spring Data JPA 和 Retrofit 等框架,采用聲明式接口定義所需的 API,然后由框架自動生成實(shí)現(xiàn)該接口的代理對象。這種方法隱藏了與 LLM 交互的復(fù)雜性,提供了簡單直觀的 API。
AI Services 主要處理以下常見操作:
- 格式化輸入以發(fā)送給 LLM
- 解析 LLM 的輸出
同時(shí)還支持更高級的功能:
- 聊天記憶管理
- 工具調(diào)用(Function Calling)
- 檢索增強(qiáng)生成(RAG)
基本使用示例
LangChain4j是 Java 生態(tài)系統(tǒng)中的一個(gè)流行框架,它提供了兩種抽象級別:低級 API 和高級 API。其中,AI Services 是一種專為 Java 量身定制的高級 API 解決方案。以下是 LangChain4j AI Services 的最簡單示例:
// 定義接口
interface Assistant {
String chat(String userMessage);
}
// 創(chuàng)建低級組件
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
// 創(chuàng)建AI Service實(shí)例
Assistant assistant = AiServices.create(Assistant.class, model);
// 使用AI Service
String answer = assistant.chat("Hello");
System.out.println(answer); // Hello, how can I help you?
使用系統(tǒng)消息和用戶消息
lanchain4j 提供了@SystemMessage 和@UserMessage 注解來自定義提示:
// 使用系統(tǒng)消息
interface Friend {
@SystemMessage("You are a good friend of mine. Answer using slang.")
String chat(String userMessage);
}
// 使用用戶消息模板
interface Advisor {
@UserMessage("You are a professional advisor. Please answer this question: {{it}}")
String getAdvice(String question);
}
基于 Spring AI 的自定義注解實(shí)現(xiàn)
雖然 lanchain4j 提供了全面的解決方案,但對于已經(jīng)使用 Spring AI 的項(xiàng)目,我們可以創(chuàng)建一個(gè)輕量級的自定義注解來簡化集成過程。
所需依賴
首先,我們需要添加以下依賴到項(xiàng)目中:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
配置 ChatClient
接下來,我們需要使用 OllamaChatModel 配置 ChatClient。在 Spring 配置類中添加以下 Bean 定義:
@Bean
public ChatClient chatClient(ChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultSystem("你是一個(gè)有用的AI助手,能夠回答用戶的問題并提供幫助。")
.build();
}
創(chuàng)建自定義注解
現(xiàn)在,讓我們創(chuàng)建@AiPrompt
注解,它將用于標(biāo)記需要 AI 處理的方法:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AiPrompt {
String systemMessage() default "";
}
實(shí)現(xiàn) AOP 切面
接下來,我們需要實(shí)現(xiàn)一個(gè) AOP 切面來攔截帶有@AiPrompt
注解的方法調(diào)用:
@Aspect
@Component
@RequiredArgsConstructor
publicclass AiPromptAspect {
privatefinal ChatClient deepSeekChatClient;
@Around("@annotation(aiPrompt)")
public Object processAiPrompt(ProceedingJoinPoint joinPoint, AiPrompt aiPrompt) throws Throwable {
// 獲取方法參數(shù)
Object[] args = joinPoint.getArgs();
if (args.length == 0) {
return joinPoint.proceed();
}
// 假設(shè)第一個(gè)參數(shù)是用戶的輸入消息
String userMessage = args[0].toString();
// 創(chuàng)建ChatClient請求
ChatClient.ChatClientRequestSpec requestSpec = deepSeekChatClient.prompt();
// 如果注解中指定了系統(tǒng)消息,則使用它
if (!aiPrompt.systemMessage().isEmpty()) {
requestSpec = requestSpec.system(aiPrompt.systemMessage());
}
return requestSpec
.user(userMessage)
.call()
.content();
}
}
使用示例
現(xiàn)在,我們可以在服務(wù)類中使用@AiPrompt
注解:
@Service
@RequiredArgsConstructor
public class ChatService {
@AiPrompt(systemMessage = "你是一個(gè)專業(yè)的Java開發(fā)顧問,擅長解答Spring框架相關(guān)問題。")
public String getJavaAdvice(String question) {
return null; // 這個(gè)返回值會被AOP切面中的返回值覆蓋
}
}
控制器示例
最后,我們可以在controller中使用這個(gè)服務(wù):
@RestController
@RequestMapping("/api")
@RequiredArgsConstructor
public class ChatController {
private final ChatService chatService;
@PostMapping("/chat")
public String getJavaAdvice(@RequestBody String question) {
return chatService.getJavaAdvice(question);
}
}
總結(jié)
參考 lanchain4j的高級 API 設(shè)計(jì)形式,基于 Spring AI 自定義注解實(shí)現(xiàn)的 AI services 可以有效地實(shí)現(xiàn)提示詞模板的集中管理和復(fù)用,通過 AOP 機(jī)制自動處理 AI 接口的調(diào)用邏輯,顯著提升開發(fā)效率。 這種模式不僅降低了與 AI 服務(wù)交互的代碼復(fù)雜度,還通過標(biāo)準(zhǔn)化注解配置實(shí)現(xiàn)了以下優(yōu)勢:
- 提示詞工程的可維護(hù)性提升;
- 業(yè)務(wù)代碼與 AI 基礎(chǔ)設(shè)施解耦;
- 可以無縫集成 Spring 生態(tài)的其他能力,比如 Spring Cache 實(shí)現(xiàn) AI 注解式緩存