前言

在上一篇文章《部署文心4.5開源模型給Android設備調用》,博主介紹瞭如何在自己的服務器部署文心4.5開源大語言模型,但對於沒有GPU服務器的同學來說,可望而不可即。所以本篇文章就介紹如何白嫖AiStudio上面的算力來部署文心4.5開源大模型給自己使用。

部署文心4.5模型

  1. 註冊AiStudio,關於如何註冊這裏就不再介紹,可以在這個鏈接https://aistudio.baidu.com/overview進行註冊。
  2. 然後來到AiStudio的主頁,可以看到左下方一個部署入口,如下截圖。
    35e26262f7ff4cf387bd27dad84aa454.png

  3. 點擊新建部署,使用FastDeploy進行部署,可以看到有幾個可以選的模型,博主就選擇跟上一篇文件一樣的模型ERNIE-4.5-21B-A3B-Paddle
    4cb7d6430b48403b986abdb6c0babd43.png

  4. 部署完成之後,點擊使用可以查看使用示例。
    2209c7cdcd0d485ba7a81cec8f74873b.png

  5. 使用示例如下,其中最關鍵的是獲取api_keybase_url,從示例中可以看出是支持OpenAI API格式的,這樣就好辦了。
    cbcd118e5def412895a34e0d37cdbd42.png

到這裏就部署完成文心4.5模型,接下來就是部署自己的服務了。

部署自己的中轉服務

更上一篇文章相比,只需要修改openai.Client中的base_urlapi_key,修改成上一步獲取的值。其他的不用修改。

class LLM:
    def __init__(self, host, port):
        self.client = openai.Client(base_url=f"https://api-******************.aistudio-app.com/v1",
                                    api_key="*************************16")
        self.system_prompt = {"role": "system", "content": "You are a helpful assistant."}
        self.histories: Dict[str, list] = {}
        self.base_prompt = {"role": "user", "content": "請扮演一個AI助手角色,你的名字文心4.5。"}
        self.base_prompt_res = {"role": "assistant", "content": "好的,我已經記住了。您有什麼問題想要問我嗎?"}

    # 流式回覆
    def generate_stream(self, prompt, max_length=8192, top_p=0.8, temperature=0.95, session_id=None):
        # 如果session_id存在之前的歷史,則獲取history
        if session_id and session_id in self.histories.keys():
            history = self.histories[session_id]
        else:
            # 否則創建新的session_id
            session_id = str(uuid.uuid4()).replace('-', '')
            history = [self.system_prompt, self.base_prompt, self.base_prompt_res]
        history.append({"role": "user", "content": prompt})
        print(f"歷史紀錄:{history}")
        print("=" * 70)
        print(f"【用戶提問】:{prompt}")
        all_output = ""
        response = self.client.chat.completions.create(model="null",
                                                       messages=history,
                                                       max_tokens=max_length,
                                                       temperature=temperature,
                                                       top_p=top_p,
                                                       stream=True)
        for chunk in response:
            if chunk.choices[0].delta:
                output = chunk.choices[0].delta.content
                if output == "": continue
                ret = {"response": output, "code": 0, "session_id": session_id}
                all_output += output
                # 更新history
                history[-1] = {"role": "assistant", "content": all_output}
                self.histories[session_id] = history
                # 返回json格式的字節
                yield json.dumps(ret).encode() + b"\0"

Android調用

Android中的代碼跟上一篇文章一樣,不需要任何的修改。在Android中,核心代碼如下,其中CHAT_HOST的值爲http://192.168.1.100:8000,其中IP是部署自己的中轉服務的服務器IP,端口是port指定的端口號。

// 發送文本結果到大語言模型接口
private void sendChat(String text) {
    if (text.isEmpty()) {
        return;
    }
    runOnUiThread(() -> sendBtn.setEnabled(false));
    // 請求的參數
    Map<String, String> map = new HashMap<>();
    map.put("prompt", text);
    if (session_id != null) {
        map.put("session_id", session_id);
    }
    JSONObject jsonObject = new JSONObject(map);
    try {
        jsonObject.put("top_p", 0.8);
        jsonObject.put("temperature", 0.95);
    } catch (JSONException e) {
        throw new RuntimeException(e);
    }
    RequestBody requestBodyJson = RequestBody.create(jsonObject.toString(),
            MediaType.parse("application/json; charset=utf-8"));
    Request request = new Request.Builder()
            .url(CHAT_HOST + "/llm")
            .post(requestBodyJson)
            .build();
    OkHttpClient client = new OkHttpClient.Builder()
            .connectTimeout(30, TimeUnit.SECONDS)//設置連接超時時間
            .readTimeout(30, TimeUnit.SECONDS)//設置讀取超時時間
            .build();
    try {
        Response response = client.newCall(request).execute();
        ResponseBody responseBody = response.body();
        // 接收流式結果
        InputStream inputStream = responseBody.byteStream();
        byte[] buffer = new byte[2048];
        int len;
        StringBuilder all_response = new StringBuilder();
        StringBuilder sb = new StringBuilder();
        while ((len = inputStream.read(buffer)) != -1) {
            try {
                // 處理讀取到的數據
                String data = new String(buffer, 0, len - 1, StandardCharsets.UTF_8);
                sb.append(data);
                byte lastBuffer = buffer[len - 2];
                buffer = new byte[2048];
                if (lastBuffer != 0x7d) {
                    continue;
                }
                data = sb.toString();
                sb = new StringBuilder();
                Log.d(TAG, data);
                JSONObject resultJson = new JSONObject(data);
                int code = resultJson.getInt("code");
                String resp = resultJson.getString("response");
                all_response.append(resp);
                session_id = resultJson.getString("session_id");
                runOnUiThread(() -> {
                    Msg lastMsg = mMsgList.get(mMsgList.size() - 1);
                    if (lastMsg.getType() == Msg.TYPE_RECEIVED) {
                        mMsgList.get(mMsgList.size() - 1).setContent(all_response.toString());
                        // 有新消息時,刷新RecyclerView中的顯示
                        mAdapter.notifyItemChanged(mMsgList.size() - 1);
                    } else {
                        mMsgList.add(new Msg(resp, Msg.TYPE_RECEIVED));
                        // 有新消息時,刷新RecyclerView中的顯示
                        mAdapter.notifyItemInserted(mMsgList.size() - 1);
                    }
                    // 將RecyclerView定位到最後一行
                    mRecyclerView.scrollToPosition(mMsgList.size() - 1);
                });
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
        inputStream.close();
        response.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    runOnUiThread(() -> sendBtn.setEnabled(true));
}

效果圖如下:

在這裏插入圖片描述

獲取源碼

在公衆號中回覆【部署文心4.5開源模型給Android設備調用】即可獲取源碼。

小夜