深入理解并掌握 Spring AI 與 Open AI 的使用方法
Open AI和Spring AI簡介
當OpenAI發布ChatGPT時,它引起了全球的關注。那是語言模型第一次能夠生成類似人類的響應。自那時以來,OpenAI又發布了其他幾款模型,包括可以根據文本提示生成圖像的DALL-E。
Spring AI是一個Java庫,提供了一個簡單易用的接口,可以與LLM模型進行交互。Spring AI提供了更高級的抽象,可以與Open AI, Azure Open AI, Hugging Face, Google Vertex, Ollama, Amazon Bedrock等各種LLM進行交互。
在本文中,我們將探討如何使用Spring AI與Open AI進行交互。
首先,我們需要在OpenAI中創建一個賬戶并獲取API密鑰。
前往OpenAI平臺并創建一個賬戶。在儀表板中,點擊左側導航菜單中的API Keys,然后創建一個新的API密鑰。如果您正在創建一個新賬戶,您將獲得一些免費的額度來使用OpenAI的APIs。 否則,您需要購買額度才能使用OpenAI的APIs。
一旦您擁有API密鑰,將環境變量OPENAI_API_KEY設置為API密鑰。
export OPENAI_API_KEY=<your-api-key>創建Spring AI項目讓我們使用Spring Initializr創建一個新的Spring Boot項目。
前往Spring Initializr https://start.spring.io/選擇Web,并且選擇OpenAI starters使用ChatClient與Open AI進行交互Spring AI提供了ChatClient抽象,能夠與不同類型的LLM進行交互,而無需與實際的LLM模型耦合。
例如,我們可以使用ChatClient與OpenAI進行如下交互:
@RestController
class ChatController {
private final ChatClient chatClient;
ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/ai/chat")
Map<String, String> chat(@RequestParam String question) {
String response = chatClient.call(question);
return Map.of("question", question, "answer", response);
}
}在上面的代碼中,沒有任何東西與OpenAI耦合。
我們可以通過在 application.properties 文件中提供 API 密鑰和其他參數來配置 ChatClient 以使用OpenAI。
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.chat.model=gpt-3.5-turbo
spring.ai.openai.chat.temperature=0.7現在,我們可以運行應用并測試聊天API。首先,啟動你的Spring Boot應用程序。然后,你可以使用 Postman 或者任何其他的 API 測試工具來發送 POST 請求到你的服務。記住,你應該在你的請求正文中包含一個消息體,這將使得 ChatClient 能夠與 OpenAI 進行交互。你將在響應中看到自由形式的答復。此答復是 OpenAI 模型根據你的消息生成的。
curl --location 'http://localhost:8080/ai/chat?question=Tell%20me%20about%20SpringBoot'
//OUTPUT:
{
"question":"請介紹下SpringBoot框架",
"answer":"Spring Boot是一個開源的基于Java的框架,用于構建和部署獨立的、生產就緒的應用程序。它是更大的Spring生態系統的一部分,提供了更簡單、更快捷的方式來設置和配置Spring應用程序。
Spring Boot消除了手動配置的需要,通過為大多數Spring項目提供默認設置,讓開發人員能夠快速開始他們的應用程序開發。它還提供了一系列的特性,如內嵌服務器、度量、健康檢查和安全性,這些都是預配置的,可以開箱即用。"
}使用提示詞模板我們可以使用提示詞模板為ChatClient提供一組預定義的提示詞。
@RestController
class ChatController {
private final JokeService jokeService;
ChatController(JokeService jokeService) {
this.jokeService = jokeService;
}
@GetMapping("/ai/chat-with-prompt")
Map<String,String> chatWithPrompt(@RequestParam String subject) {
String answer = jokeService.getJoke(subject);
return Map.of("answer", answer);
}
}
@Service
class JokeService {
private final ChatClient chatClient;
JokeService(ChatClient chatClient) {
this.chatClient = chatClient;
}
String getJoke(String subject) {
PromptTemplate promptTemplate = new PromptTemplate("告訴我一個關于 {subject} 的笑話"");
Prompt prompt = promptTemplate.create(Map.of("subject", subject));
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}通過使用提示詞模板,我們可以隱藏創建提示詞的復雜性,并為用戶提供一個簡單的接口。
在上述示例中,我們創建了代表用戶消息的提示詞。我們可以使用 SystemMessage 來表示 LLM 在對話中的角色。
@Service
class JokeService {
private final ChatClient chatClient;
JokeService(ChatClient chatClient) {
this.chatClient = chatClient;
}
String getJoke(String subject) {
SystemMessage systemMessage = new SystemMessage("你是一個有用又風趣的聊天機器人");
UserMessage userMessage = new UserMessage("告訴我一個關于 " + subject +" 的笑話");
Prompt prompt = new Prompt(List.of(systemMessage, userMessage));
ChatResponse response = chatClient.call(prompt);
return response.getResult().getOutput().getContent();
}
}在上述示例中,我們創建了一個系統消息和用戶消息,以代表用戶和 LLM 之間的對話。通過使用系統消息,我們可以定義角色并向 LLM 提供額外的上下文。
使用輸出解析器
在前面的例子中,我們將 LLM 的回應作為字符串獲取。我們可以使用輸出解析器來解析回應并以所需格式提取所需信息。
目前,Spring AI 提供了以下類型的輸出解析器:
BeanOutputParser - 用于解析回應并轉換成Java Bean。MapOutputParser - 用于解析回應并轉換成Map。ListOutputParser - 用于解析回應并轉換成List。
我們創建了一個新的 MovieController 控制器,用來獲取某位導演導演的電影列表。
@RestController
class MovieController {
private final ChatClient chatClient;
MovieController(ChatClient chatClient) {
this.chatClient = chatClient;
}
private static final String PROMPT_TEMPLATE = """
What are the best movies directed by {director}?
{format}
""";
//...
}現在,讓我們來看一下如何使用 BeanOutputParser 來解析響應并將其轉換為 Java Bean。
record DirectorResponse(String director, List<String> movies) {}
@RestController
class MovieController {
//...
@GetMapping("/ai/chat/movies")
DirectorResponse chat(@RequestParam String director) {
var outputParser = new BeanOutputParser<>(DirectorResponse.class);
var userPromptTemplate = new PromptTemplate(PROMPT_TEMPLATE);
Map<String, Object> model = Map.of("director", director, "format", outputParser.getFormat());
var prompt = userPromptTemplate.create(model);
var response = chatClient.call(prompt);
return outputParser.parse(response.getResult().getOutput().getContent());
}
}在上述示例中,我們創建了一個名為 DirectorResponse 的 Java Bean,用于表示 LLM 的響應。BeanOutputParser 將解析響應并將其轉為 DirectorResponse 對象。
同樣,我們可以使用 MapOutputParser 和 ListOutputParser 來解析響應并分別將其轉換為 Map 和 List。
@RestController
class MovieController {
//...
@GetMapping("/ai/chat/movies-as-map")
Map<String, Object> chatWithMapOutput(@RequestParam String director) {
var outputParser = new MapOutputParser();
var userPromptTemplate = new PromptTemplate(PROMPT_TEMPLATE);
Map<String, Object> model = Map.of("director", director, "format", outputParser.getFormat());
var prompt = userPromptTemplate.create(model);
var response = chatClient.call(prompt);
return outputParser.parse(response.getResult().getOutput().getContent());
}
@GetMapping("/ai/chat/movies-as-list")
List<String> chatWithListOutput(@RequestParam String director) {
var outputParser = new ListOutputParser(new DefaultConversionService());
var userPromptTemplate = new PromptTemplate(PROMPT_TEMPLATE);
Map<String, Object> model = Map.of("director", director, "format", outputParser.getFormat());
var prompt = userPromptTemplate.create(model);
var response = chatClient.call(prompt);
return outputParser.parse(response.getResult().getOutput().getContent());
}
}我們可以按照以下方式測試API:
curl --location 'http://localhost:8080/ai/chat/movies?director=Quentin%20Tarantino'
//OUTPUT:
{"director":"Quentin Tarantino","movies":["Pulp Fiction","Inglourious Basterds","Django Unchained","Kill Bill: Volume 1","Kill Bill: Volume 2"]}
curl --location 'http://localhost:8080/ai/chat/movies-as-map?director=Quentin%20Tarantino'
//OUTPUT:
{"best_movies":[{"title":"Pulp Fiction","year":1994},{"title":"Inglourious Basterds","year":2009},{"title":"Kill Bill: Volume 1","year":2003},{"title":"Kill Bill: Volume 2","year":2004},{"title":"Django Unchained","year":2012}]}
curl --location 'http://localhost:8080/ai/chat/movies-as-list?director=Quentin%20Tarantino'
//OUTPUT:
["Pulp Fiction","Kill Bill: Volume 1","Inglourious Basterds","Django Unchained","Once Upon a Time in Hollywood"]你需要根據 LLM 的響應以及你希望轉換的格式,使用相應的 OutputParser。
結論
在這篇文章中,我們了解了如何使用 Spring AI 與 OpenAI 進行交互。我們創建了Java Bean并使用了BeanOutputParser,MapOutputParser,和ListOutputParser來解析不同的響應類型。通過本文,我們可以了解到如何根據 LLM 的響應和預期的格式選擇適合的 OutputParser 。
































