部署文心4.5开源模型给Android设备调用

前言

在上一篇文章《文心4.5开源大模型的使用和部署》已经介绍了如何使用fastdeploy部署文心4.5开源大模型的,并且简单调用了接口,本篇文章来介绍Android如何调用这个部署的接口,并实现对话。

部署

  1. 首先还是需要下载要部署的文心模型,之前为了演示我使用了一个比较小的模型,现在部署上我使用更大的模型ERNIE-4.5-21B-A3B-Paddle
aistudio download --model PaddlePaddle/ERNIE-4.5-21B-A3B-Paddle --local_dir ./models/ERNIE-4.5-21B-A3B-Paddle/
  1. 启动fastdeploy服务,要留意这个8180端口号,下面会使用到,注意为了节省显存,这次量化我使用了是wint4方法。
python -m fastdeploy.entrypoints.openai.api_server \
       --model ./models/ERNIE-4.5-21B-A3B-Paddle/ \
       --port 8180 \
       --quantization wint4 \
       --max-model-len 32768 \
       --max-num-seqs 32
  1. 写个Python脚本作为中转和记录对话历史数据,首先写一个LLM类作为整体的工具调用。
class LLM:
    def __init__(self, host, port):
        self.client = openai.Client(base_url=f"http://{host}:{port}/v1", api_key="null")
        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"
  1. 启动自己的服务接口,注意这几个参数,hostport参数值本身服务暴露给Android调用的,fastdeploy_hostfastdeploy_port是fastdeploy部署的接口,也就是第二步设置的端口号及其部署所在的服务器IP。执行这个脚本就可以启动服务了,接下来就等Android调用了。
app = FastAPI()

@app.post("/llm")
async def api_llm(request: Request):
    params = await request.json()

    generator = model.generate_stream(**params)
    background_tasks = BackgroundTasks()
    return StreamingResponse(generator, background=background_tasks)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--host", type=str, default="0.0.0.0")
    parser.add_argument("--port", type=int, default=8000)
    parser.add_argument("--fastdeploy_host", type=str, default="127.0.0.1")
    parser.add_argument("--fastdeploy_port", type=int, default=8180)
    args = parser.parse_args()
    model = LLM(host=args.fastdeploy_host, port=args.fastdeploy_port)
    # 启动服务
    uvicorn.run(app, host=args.host, port=args.port)

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设备调用】即可获取源码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜雨飘零1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值