Java 中的 OpenAI API 用戶端
1. 概述
特別是隨著生成式 AI 和 ChatGPT 的廣泛使用,許多語言已經開始提供與其OpenAI API互動的函式庫。 Java 也不例外。
在本教程中,我們將討論[openai-java](https://github.com/TheoKanning/openai-java) 。這是一個可以更方便與OpenAI API通訊的客戶端。然而,在一篇文章中回顧整個庫是不可能的。因此,我們將使用一個實際範例並建立一個連接到 ChatGPT 的簡單控制台工具。
2. 依賴關係
首先,我們必須導入專案所需的依賴項。我們可以在 Maven儲存庫中找到這些庫。這三個模組致力於互動的不同面向:
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>service</artifactId>
<version>0.18.2</version>
</dependency>
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>api</artifactId>
<version>0.18.2</version>
</dependency>
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>client</artifactId>
<version>0.18.2</version>
</dependency>
請注意,該名稱明確提到了 GPT3,但它也適用於 GPT4 。
3. 拜東導師
在本教程中,我們將建立一個工具,幫助我們根據我們最喜歡的學習平台上的文章和教程創建課程,或至少嘗試這樣做。雖然網路為我們提供了無限的資源,我們幾乎可以在網路上找到任何東西,但整理資訊卻變得更加困難。
嘗試學習新事物越來越讓人不知所措,因為很難確定最佳的學習路徑並過濾掉對我們沒有好處的東西。為了解決這個問題,我們將建立一個簡單的客戶端來與 ChatGPT 交互,並要求它在浩瀚的 Baeldung 文章海洋中指導我們。
4.OpenAI API 令牌
第一步是將我們的應用程式連接到 OpenAI API。為此,我們需要提供一個 OpenAI 令牌,該令牌可以在網站上產生:
但是,我們應該小心該令牌並避免暴露它。 openai-java範例為此使用環境變數。這可能不是生產的最佳解決方案,但對於小型實驗來說它很有效。
在運作過程中,我們不一定需要辨識整個機器的環境變數;我們可以在 IDE 中使用設定。例如,IntelliJ IDEA 提供了一種簡單的方法來執行此操作。
我們可以產生兩種類型的代幣:個人帳戶和服務帳戶。個人令牌是不言自明的。服務帳戶的代幣用於可以連接到 OpenAI 專案的機器人或應用程式。雖然兩者都可以,但個人令牌足以滿足我們的目的。
5. OpenAiService
OpenAI API 的入口點是名為OpenAiService類別。此類的實例允許我們與 API 互動並接收來自 ChatGPT 的回應。要創建它,我們應該傳遞在上一步中產生的令牌:
String token = System.getenv("OPENAI_TOKEN");
OpenAiService service = new OpenAiService(token);
這是我們旅程的第一步;我們需要識別資訊並填充請求。
5.1. ChatCompletionRequest
我們使用ChatCompletionRequest建立請求。最小的設定要求我們僅提供訊息和模型:
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
.builder()
.model(GPT_3_5_TURBO_0301.getName())
.messages(messages)
.build();
讓我們逐步回顧一下這些參數。
5.2.模型
選擇適合我們要求的型號非常重要,而且它也會影響成本。因此,我們需要做出合理的選擇。例如,通常不需要使用最先進的模型來清理文字或基於一些簡單的格式對其進行解析。同時,更複雜或更重要的任務需要更先進的模型來實現我們的目標。
雖然我們可以直接傳遞模型名稱,但最好使用ModelEnum:
@Getter
@AllArgsConstructor
public enum ModelEnum {
GPT_3_5_TURBO("gpt-3.5-turbo"),
GPT_3_5_TURBO_0301("gpt-3.5-turbo-0301"),
GPT_4("gpt-4"),
GPT_4_0314("gpt-4-0314"),
GPT_4_32K("gpt-4-32k"),
GPT_4_32K_0314("gpt-4-32k-0314"),
GPT_4_1106_preview("gpt-4-1106-preview");
private String name;
}
它並不包含所有模型,但在我們的例子中,這已經足夠了。如果我們想使用不同的模型,我們可以將其名稱作為String提供。
5.3.留言
接下來是我們創建的訊息。我們使用ChatMessage類別。在我們的例子中,我們只傳遞角色和訊息本身:
List<ChatMessage> messages = new ArrayList<>();
ChatMessage systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), PROMPT);
messages.add(systemMessage);
有趣的部分是我們發送一組訊息。雖然在平時的聊天中,我們是透過一條一條的發送訊息來進行交流的,但在這種情況下,它更類似於電子郵件線程。
系統在完成後工作並將下一則訊息附加到鏈中。這樣,我們就可以維持對話的脈絡。我們可以將其視為無狀態服務。然而,這意味著我們必須傳遞訊息來保留上下文。
同時我們還可以另闢蹊徑,創造一個助手。透過這種方法,我們可以將訊息儲存在線程中,並且不需要來回發送整個歷史記錄。
在傳遞時,訊息的內容是合理的,但角色的目的卻不合理。因為我們一次發送所有訊息,所以我們需要提供某種方法來根據使用者的角色來識別訊息和使用者之間的關係。
5.4.角色
如前所述,角色對於 ChatGPT 理解對話上下文至關重要。我們可以使用它們來識別訊息背後的參與者。這樣,我們可以幫助 ChatGPT 正確解釋訊息。 ChatMessages支援四種角色:聊天、系統、助理、功能:
public enum ChatMessageRole {
SYSTEM("system"),
USER("user"),
ASSISTANT("assistant"),
FUNCTION("function");
private final String value;
ChatMessageRole(final String value) {
this.value = value;
}
public String value() {
return value;
}
}
通常,SYSTEM 角色是指初始上下文或提示。用戶代表ChatGPT的用戶,助手就是ChatGPT本身。這意味著,從技術上來說,我們也可以從助手的角度來寫訊息。顧名思義,功能角色標識了助手可以使用的功能。
5.5.代幣
雖然我們之前討論過 API 的存取令牌,但其含義在模型和訊息的上下文中是不同的。我們可以將代幣視為我們可以處理的資訊量以及我們想要得到回應的資訊量。
我們可以透過限制回應中的令牌數量來限制模型產生巨大的回應:
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
.builder()
.model(MODEL)
.maxTokens(MAX_TOKENS)
.messages(messages)
.build();
單字和標記之間沒有直接映射,因為每個模型對它們的處理略有不同。此參數將答案限制為特定數量的標記。使用預設值可能會導致過多的回應並增加使用費用。因此,顯式配置它是一個好習慣。
我們可以在每個回應後添加有關已使用令牌的資訊:
long usedTokens = result.getUsage().getTotalTokens();
System.out.println("Total tokens used: " + usedTokens);
5.6.代幣化
在前面的範例中,我們顯示了回應中使用的令牌數量。雖然這些資訊很有價值,但我們通常還需要估計請求的大小。為了實現這一點,我們可以使用 OpenAI 提供的分詞器。
為了以更自動化的方式做到這一點,openai-java 為我們提供了TikTokensUtil ,我們可以將模型的名稱和訊息傳遞給它,並最終獲得令牌的數量。
5.7.選項
我們可以用來配置請求的另一個方法神秘地命名為n().它控制我們希望為每個請求獲得多少回應。簡而言之,對於同一個請求,我們可以有兩個不同的答案。預設情況下,我們只有一個。
有時,它對於機器人和網站助手可能很有用。但是,回應是根據所有選項的令牌進行計費的。
5.8.偏差和隨機化
我們可以使用幾個附加選項來控制 ChatGPT 答案的隨機性和偏差。例如, logitBias()可以使看到或看不到特定標記的可能性更大。請注意,我們在這裡討論的是標記而不是特定的單字。但是,這並不意味著該令牌不會 100% 出現。
另外,我們可以使用topP()和temperature() 隨機化響應。雖然它在某些情況下很有用,但我們不會更改學習工具的預設。
6. 課程設定
現在,讓我們檢查一下我們的工具的運作情況。我們將得到以下總體代碼:
public static void main(String[] args) {
String token = System.getenv("OPENAI_TOKEN");
OpenAiService service = new OpenAiService(token);
List<ChatMessage> messages = new ArrayList<>();
ChatMessage systemMessage = new ChatMessage(ChatMessageRole.SYSTEM.value(), PROMPT);
messages.add(systemMessage);
System.out.print(GREETING);
Scanner scanner = new Scanner(System.in);
ChatMessage firstMsg = new ChatMessage(ChatMessageRole.USER.value(), scanner.nextLine());
messages.add(firstMsg);
while (true) {
ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest
.builder()
.model(GPT_3_5_TURBO_0301.getName())
.messages(messages)
.build();
ChatCompletionResult result = service.createChatCompletion(chatCompletionRequest);
long usedTokens = result.getUsage().getTotalTokens();
ChatMessage response = result.getChoices().get(0).getMessage();
messages.add(response);
System.out.println(response.getContent());
System.out.println("Total tokens used: " + usedTokens);
System.out.print("Anything else?\n");
String nextLine = scanner.nextLine();
if (nextLine.equalsIgnoreCase("exit")) {
System.exit(0);
}
messages.add(new ChatMessage(ChatMessageRole.USER.value(), nextLine));
}
}
如果我們運行它,我們可以透過控制台與它互動:
Hello!
What do you want to learn?
作為回應,我們可以寫下我們感興趣的主題:
$ I would like to learn about binary trees.
正如預期的那樣,該工具將為我們提供一些可用於了解這些主題的文章:
Great! Here's a suggested order for Baeldung's articles on binary trees:
1. Introduction to Binary Trees: https://www.baeldung.com/java-binary-tree-intro
2. Implementing a Binary Tree in Java: https://www.baeldung.com/java-binary-tree
3. Depth First Traversal of Binary Tree: https://www.baeldung.com/java-depth-first-binary-tree-traversal
4. Breadth First Traversal of Binary Tree: https://www.baeldung.com/java-breadth-first-binary-tree-traversal
5. Finding the Maximum Element in a Binary Tree: https://www.baeldung.com/java-binary-tree-maximum-element
6. Binary Search Trees in Java: https://www.baeldung.com/java-binary-search-tree
7. Deleting from a Binary Search Tree: https://www.baeldung.com/java-binary-search-tree-delete
I hope this helps you out!
Total tokens used: 288
Anything else?
這樣,我們透過創建課程和學習新事物來解決問題。然而,並不是一切都那麼光明。問題是只有一篇文章是真的。大多數情況下,ChatGPT 都會列出不存在的文章並附有適當的連結。雖然名稱和連結聽起來很合理,但它們不會引導我們到任何地方。
這是任何人工智慧工具的一個重要方面。生成模型很難檢查資訊的有效性。由於它們基於預測和選擇最合適的下一個單詞,因此它們可能很難驗證資訊。我們不能 100% 依賴生成模型的資訊。
7 .結論
人工智慧工具非常棒,可以幫助我們改進應用程式並自動化日常瑣事,從處理電子郵件和建立購物清單到優化教育。 Java 提供了多種與 OpenAI API 互動的方法, openai-java就是這樣的一個函式庫。
然而,重要的是要記住,生成模型儘管非常令人信服,但在驗證資訊是否真實時卻遇到困難。因此,我們有責任重新檢查關鍵資訊或為模型提供足夠的信息,以便它能夠為我們提供有效的答案。
與往常一樣,本教程中的所有程式碼都可以在 GitHub 上取得。