我給清華智譜Ai(ChatGLM),寫個Java對接的SDK!
大家好,我是技術(shù)UP主小傅哥。
清華大學(xué)計算機(jī)系的超大規(guī)模訓(xùn)練模型 ChatGLM-130B 使用效果非常牛,所以我也想把這樣的Ai能力接入到自己的應(yīng)用中或者做一些 IntelliJ IDEA Plugin 使用。但經(jīng)過了一晚上的折騰,我決定給它寫個對接的SDK開源出來!—— ?? 智譜Ai不是已經(jīng)有了一個SDK嗎?為啥還要寫呢?那你寫多少了?
在很早之前就關(guān)注了智譜Ai(ChatGLM),也看到官網(wǎng)有一個Java對接的SDK方式。但從前幾天開始正式對接發(fā)現(xiàn),這SDK是8月份提交的,10個commit,而且已經(jīng)2個月沒有更新了。所以真的是不少Bug呀,呀,呀!如果不去修改它的SDK代碼,就沒法對接。如;ConfigV3類中,拆分ApiKey的操作;String[] arrStr = apiSecretKey.split("."); 但這里的.是正則的關(guān)鍵字,所以根本沒法拆分。一起動就報錯 invalid apiSecretKey 這對于初次對接并且沒有看源碼的伙伴來說,是不小的炸雷。
不過,雖然 SDK 有點趕工,不好用。但不影響智譜Ai(ChatGLM)是個好東西。他的官網(wǎng)中有API HTTP 接口對接描述。所以,小傅哥決定跟著按照它的文檔寫一個能簡單對接,代碼有干凈整潔的 SDK 讓大家使用。
圖片
那么,接下來小傅哥就介紹下,如何基于智譜Ai(ChatGLM)的開發(fā)者文檔,開發(fā)一個通用的SDK組件。也讓后續(xù)有想法PR貢獻(xiàn)源碼的伙伴,一起參與進(jìn)來。—— 別看東西不大,寫到簡歷上,也是非常精彩的一筆!
本文不止有智譜Ai-SDK開發(fā),還有如何在項目中運用SDK開發(fā)一個自己的OpenAi服務(wù)。文末有SDK鏈接和OpenAi應(yīng)用工程。
一、對接鑒權(quán)
- 文檔:https://open.bigmodel.cn/dev/api
- ApiKey:https://open.bigmodel.cn/usercenter/apikeys - 申請個人授權(quán),創(chuàng)建ApiKey即可
智譜Ai的Api文檔,與ChatGPT對接有一些差異。如果大家對接過ChatGPT開發(fā),直接獲取一個ApiKey就可以使用了。但在對接智譜Ai的Api時,需要把獲取的ApiKey按照.號分割,并需要進(jìn)行JWT-Token的創(chuàng)建。而這個Token才是實際傳給接口的內(nèi)容。
圖片
- 因為生成Token會比較耗時,所以這里會使用Guava框架進(jìn)行本地緩存29分鐘,有效期30分鐘的Token,確保可以有效的刷新。
- 在工程中提供了 BearerTokenUtils Token 生成工具類,測試的時候可以使用。
二、接口處理
文檔:https://open.bigmodel.cn/dev/api#chatglm_lite - 以Api文檔的chatglm_lite模型舉例對接
傳輸方式 | https |
請求地址 | https://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke |
調(diào)用方式 | SSE |
字符編碼 | UTF-8 |
接口請求頭 | accept: text/event-stream |
接口請求格式 | JSON |
響應(yīng)格式 | 標(biāo)準(zhǔn) Event Stream |
接口請求類型 | POST |
開發(fā)語言 | 任意可發(fā)起 HTTP 請求的開發(fā)語言 |
在正式開發(fā)代碼,要把接口的使用先簡單測試運行出來。之后再去編寫代碼。為此這里小傅哥先根據(jù)官網(wǎng)的文檔和鑒權(quán)使用方式,編寫了 curl http 請求;
curl -X POST \
-H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsInNpZ25fdHlwZSI6IlNJR04ifQ.eyJhcGlfa2V5IjoiNGUwODdlNDEzNTMwNmVmNGE2NzZmMGNjZTNjZWU1NjAiLCJleHAiOjE2OTY5OTM5ODIzMTQsInRpbWVzdGFtcCI6MTY5Njk5MjE4MjMxNH0.9nxhRXTJcP4Q_YTQ8w5y0CZOBOu0epP1J56oDaYewQ8" \
-H "Content-Type: application/json" \
-H "User-Agent: Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)" \
-H "Accept: text/event-stream" \
-d '{
"top_p": 0.7,
"sseFormat": "data",
"temperature": 0.9,
"incremental": true,
"request_id": "xfg-1696992276607",
"prompt": [
{
"role": "user",
"content": "寫個java冒泡排序"
}
]
}' \
http://open.bigmodel.cn/api/paas/v3/model-api/chatglm_lite/sse-invoke
圖片
- 注意:Authorization: Bearer 后面?zhèn)鞯氖?JWT Token 不是一個直接從官網(wǎng)復(fù)制的 ApiKey - 你可以使用工程中的 BearerTokenUtils 創(chuàng)建。
- 之后可以直接運行這段腳本(也可以導(dǎo)入到ApiPost工具中),執(zhí)行后就能獲得到運行效果了?!?速度非???!
三、組件開發(fā)
在??考慮到抽象和設(shè)計原則下,小傅哥這里采用了會話模型結(jié)構(gòu)進(jìn)行工程框架設(shè)計。把程序的調(diào)用抽象為一次會話,而會話的創(chuàng)建則交給工廠??。通過工廠屏蔽使用細(xì)節(jié),在使用上簡化調(diào)用,盡可能讓外部最少知道原則。這樣的設(shè)計實現(xiàn)方式,既可以滿足調(diào)用方開心的使用,也可以讓SDK貢獻(xiàn)者見代碼如見文檔,容易理解和上手。
1. 工程結(jié)構(gòu)
圖片
- 工程非常注重會話的設(shè)計和使用,因為框架的根基搭建好以后,擴(kuò)展各項功能就會有跡可循。大部分代碼就是因為早期沒有考慮好框架,最后功能來了被填充的很亂。
2. 會話流程
圖片
- 會話流程以工廠創(chuàng)建 Session 為入口點進(jìn)行使用,其他的操作都在組件內(nèi)自己處理好。
3. 代碼舉例
@Override
public OpenAiSession openSession() {
// 1. 日志配置
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(configuration.getLevel());
// 2. 開啟 Http 客戶端
OkHttpClient okHttpClient = new OkHttpClient
.Builder()
.addInterceptor(httpLoggingInterceptor)
.addInterceptor(new OpenAiHTTPInterceptor(configuration))
.connectTimeout(configuration.getConnectTimeout(), TimeUnit.SECONDS)
.writeTimeout(configuration.getWriteTimeout(), TimeUnit.SECONDS)
.readTimeout(configuration.getReadTimeout(), TimeUnit.SECONDS)
.build();
configuration.setOkHttpClient(okHttpClient);
// 3. 創(chuàng)建 API 服務(wù)
IOpenAiApi openAiApi = new Retrofit.Builder()
.baseUrl(configuration.getApiHost())
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(JacksonConverterFactory.create())
.build().create(IOpenAiApi.class);
configuration.setOpenAiApi(openAiApi);
return new DefaultOpenAiSession(configuration);
}
- 這是一段 DefaultOpenAiSessionFactory 創(chuàng)建工廠開啟會話的服務(wù)對象。使用方只需要在自己的工程中,創(chuàng)建出一個工廠對象就可以對接使用了。下文有代碼示例
- 其他更多的代碼,直接看小傅哥開發(fā)好的 chatglm-sdk-java
四、組件使用
1. 組件配置
- 申請ApiKey:https://open.bigmodel.cn/usercenter/apikeys - 注冊申請開通,即可獲得 ApiKey
- 運行環(huán)境:JDK 1.8+
- maven pom - 暫時測試階段,未推送到Maven中央倉庫,需要下載代碼本地 install 后使用
<dependency>
<groupId>cn.bugstack</groupId>
<artifactId>chatglm-sdk-java</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
- 源碼(Github):https://github.com/fuzhengwei/chatglm-sdk-java
- 源碼(Gitee):https://gitee.com/fustack/chatglm-sdk-java
- 源碼(Gitcode):https://gitcode.net/KnowledgePlanet/road-map/chatglm-sdk-java
2. 單元測試
@Slf4j
public class ApiTest {
private OpenAiSession openAiSession;
@Before
public void test_OpenAiSessionFactory() {
// 1. 配置文件
Configuration configuration = new Configuration();
configuration.setApiHost("https://open.bigmodel.cn/");
configuration.setApiSecretKey("4e087e4135306ef4a676f0cce3cee560.sgP2*****");
// 2. 會話工廠
OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
// 3. 開啟會話
this.openAiSession = factory.openSession();
}
/**
* 流式對話
*/
@Test
public void test_completions() throws JsonProcessingException, InterruptedException {
// 入?yún)?;模型、請求信? ChatCompletionRequest request = new ChatCompletionRequest();
request.setModel(Model.CHATGLM_LITE); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro
request.setPrompt(new ArrayList<ChatCompletionRequest.Prompt>() {
private static final long serialVersionUID = -7988151926241837899L;
{
add(ChatCompletionRequest.Prompt.builder()
.role(Role.user.getCode())
.content("寫個java冒泡排序")
.build());
}
});
// 請求
openAiSession.completions(request, new EventSourceListener() {
@Override
public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) {
ChatCompletionResponse response = JSON.parseObject(data, ChatCompletionResponse.class);
log.info("測試結(jié)果 onEvent:{}", response.getData());
// type 消息類型,add 增量,finish 結(jié)束,error 錯誤,interrupted 中斷
if (EventType.finish.getCode().equals(type)) {
ChatCompletionResponse.Meta meta = JSON.parseObject(response.getMeta(), ChatCompletionResponse.Meta.class);
log.info("[輸出結(jié)束] Tokens {}", JSON.toJSONString(meta));
}
}
@Override
public void onClosed(EventSource eventSource) {
log.info("對話完成");
}
});
// 等待
new CountDownLatch(1).await();
}
}
- 這是一個單元測試類,也是最常使用的流式對話模式。
五、應(yīng)用接入
1. SpringBoot 配置類
@Configuration
@EnableConfigurationProperties(ChatGLMSDKConfigProperties.class)
public class ChatGLMSDKConfig {
@Bean
@ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false)
public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) {
// 1. 配置文件
cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration();
configuration.setApiHost(properties.getApiHost());
configuration.setApiSecretKey(properties.getApiSecretKey());
// 2. 會話工廠
OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration);
// 3. 開啟會話
return factory.openSession();
}
}
@Data
@ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true)
public class ChatGLMSDKConfigProperties {
/** 狀態(tài);open = 開啟、close 關(guān)閉 */
private boolean enable;
/** 轉(zhuǎn)發(fā)地址 */
private String apiHost;
/** 可以申請 sk-*** */
private String apiSecretKey;
}
@Autowired(required = false)
private OpenAiSession openAiSession;
- 注意:如果你在服務(wù)中配置了關(guān)閉啟動 ChatGLM SDK 那么注入 openAiSession 為 null
2. yml 配置
# ChatGLM SDK Config
chatglm:
sdk:
config:
# 狀態(tài);true = 開啟、false 關(guān)閉
enabled: false
# 官網(wǎng)地址
api-host: https://open.bigmodel.cn/
# 官網(wǎng)申請 https://open.bigmodel.cn/usercenter/apikeys
api-key: 4e087e4135306ef4a676f0cce3cee560.sgP2DUs*****
- 你可以在配置文件中,通過 enabled 參數(shù),啟動和關(guān)閉 ChatGLM SDK
六、應(yīng)用開發(fā)
基于本文開發(fā)的 ChatGLM SDK 就可以對接到 OpenAi 開發(fā)一個自己的應(yīng)用了。
圖片
小傅哥帶著大家寫的項目,從來不是湊數(shù)項目,也從不寫一堆的 CRUD 代碼。而是按照互聯(lián)網(wǎng)企業(yè)級中所開發(fā)項目的模式進(jìn)行架構(gòu)、設(shè)計和實現(xiàn)。所以你跟著小傅哥學(xué)習(xí),學(xué)的是編程的思維和編碼的能力。