开源模型应用落地-业务整合篇(一)

一、前言

    经过对qwen-7b-chat的部署以及与vllm的推理加速的整合,我们成功构建了一套高性能、高可靠、高安全的AI服务能力。现在,我们将着手整合具体的业务场景,以实现完整可落地的功能交付。

    作为上游部门,通常会采用最常用的方式来接入下游服务。为了调用我们的AI服务,我们将使用Java语言,并分别使用HttpClient、OkHttp等工具来实现调用。这样可以确保我们能够高效地与AI服务进行交互。


二、术语

2.1.OkHttp

    是一个开源的Java和Kotlin HTTP客户端库,用于进行网络请求。OkHttp支持HTTP/1.1和HTTP/2协议,具有连接池、请求重试、缓存、拦截器等功能。它还提供了异步和同步请求的支持,并且可以与各种平台和框架无缝集成,是Android开发中常用的网络请求库之一。通过使用OkHttp,开发人员可以轻松地发送HTTP请求、处理响应以及管理网络连接,从而加快应用程序的网络通信速度和效率。

2.2.HttpClient

    是一个用于发送HTTP请求和接收HTTP响应的开源库。它提供了一种方便的方式来与Web服务器进行通信,并执行各种HTTP操作,例如发送GET请求、POST请求等。HttpClient库通常用于编写客户端应用程序或服务,这些应用程序需要与Web服务器或Web API进行通信。它提供了许多功能,包括连接管理、身份验证、请求和响应拦截、Cookie管理等。

2.3.HttpURLConnection

    是Java提供的一个用于发送HTTP请求和接收HTTP响应的类。它是Java标准库中的一部分,用于与Web服务器进行通信。HttpURLConnection类提供了一组方法,使您能够创建HTTP连接、设置请求方法(如GET、POST等)、设置请求头、设置请求体和其他参数,并发送请求到指定的URL。它还提供了方法来获取HTTP响应的状态码、响应头和响应体等信息。

    OkHttp和HttpClient提供了更丰富的功能和更好的性能,适用于大多数情况下。HttpURLConnection是Java标准库中的类,提供了基本的HTTP功能


三、前置条件

3.1. 完成Qwen-7b-Chat(Qwen-1_8B-Chat)模型的本地部署或服务端部署

    参见“开源模型应用落地-qwen-7b-chat与vllm实现推理加速的正确姿势”系列文章

3.2. 完成对外服务接口的封装,屏蔽不同模型的调用差异

    参见“开源模型应用落地-qwen-7b-chat与vllm实现推理加速的正确姿势”系列文章


四、技术实现

4.1. HttpURLConnection

import lombok.extern.slf4j.Slf4j;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Objects;@Slf4j
public class QWenCallTest {private static String url = "http://127.0.0.1:9999/api/chat";private static String DEFAULT_TEMPLATE = "{\"prompt\":\"%s\",\"history\":%x,\"top_p\":0.9, \"temperature\":0.45,\"repetition_penalty\":1.1, \"max_new_tokens\":8192}";private static String DEFAULT_USERID = "xxxxx";private static String DEFAULT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";private static int DEFAULT_CONNECTION_TIMEOUT = 3 * 1000;private static int DEFAULT_READ_TIMEOUT = 30 * 1000;public static void main(String[] args) {String question = "我家周边有什么好吃、好玩的地方嘛?";String history = "[{\n" +"\"from\": \"user\",\n" +"\"value\": \"你好\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"你好!有什么我能为你效劳的吗?\"\n" +"},\n" +"{\n" +"\"from\": \"user\",\n" +"\"value\": \"我家在广州,你呢?\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"我是一个人工智能助手,没有具体的家。\"\n" +"}]";String prompt = DEFAULT_TEMPLATE.replace("%s", question).replace("%x", history);log.info("prompt: {}", prompt);HttpURLConnection conn = null;OutputStream os = null;try {//1.设置URLURL urlObject = new URL(url);//2.打开URL连接conn = (HttpURLConnection) urlObject.openConnection();//3.设置请求方式conn.setRequestMethod("POST");conn.setRequestProperty("Content-Type", "application/json;charset=utf-8");conn.setRequestProperty("Accept", "text/event-stream");conn.setRequestProperty("userId", DEFAULT_USERID);conn.setRequestProperty("secret", DEFAULT_SECRET);conn.setDoOutput(true);conn.setDoInput(true);// 设置连接超时时间为60秒conn.setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT);// 设置读取超时时间为60秒conn.setReadTimeout(DEFAULT_READ_TIMEOUT);os = conn.getOutputStream();os.write(prompt.getBytes("utf-8"));} catch (Exception e) {log.error("请求模型接口异常", e);} finally {if(!Objects.isNull(os)){try {os.flush();os.close();} catch (Exception e) {}}}InputStream is = null;try{if(!Objects.isNull(conn)){int responseCode = conn.getResponseCode();log.info("Response Code: " + responseCode);if(responseCode == 200){is = conn.getInputStream();}else{is = conn.getErrorStream();}byte[] bytes = new byte[1024];int len = 0;while ((len = is.read(bytes)) != -1) {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();outputStream.write(bytes, 0, len);String response = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);log.info(response);}}} catch (Exception e) {log.error("请求模型接口异常", e);} finally {if (!Objects.isNull(is)) {try {is.close();} catch (Exception e) {e.printStackTrace();}}}}}

4.2. OkHttp

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;@Slf4j
public class QWenCallTest {private static String url = "http://127.0.0.1:9999/api/chat";private static String DEFAULT_TEMPLATE = "{\"prompt\":\"%s\",\"history\":%x,\"top_p\":0.9, \"temperature\":0.45,\"repetition_penalty\":1.2, \"max_new_tokens\":8192}";private static long DEFAULT_CONNECTION_TIMEOUT = 3 * 1000;private static long DEFAULT_WRITE_TIMEOUT = 15 * 1000;private static long DEFAULT_READ_TIMEOUT = 15 * 1000;private final static Request.Builder buildHeader(Request.Builder builder) {return builder.addHeader("Content-Type", "application/json; charset=utf-8").addHeader("userId", "xxxxx").addHeader("secret", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");}private final static Request buildRequest(String prompt) {//创建一个请求体对象(body)MediaType mediaType = MediaType.parse("application/json");RequestBody requestBody = RequestBody.create(mediaType,prompt);return buildHeader(new Request.Builder().post(requestBody)).url(url).build();}public static void chat(String question,String history,CountDownLatch countDownLatch) {//定义请求的参数String prompt = DEFAULT_TEMPLATE.replace("%s", question).replace("%x", history);log.info("prompt: {}", prompt);//创建一个请求对象Request request = buildRequest(prompt);//发送请求:创建了一个请求工具对象,调用执行request对象OkHttpClient okHttpClient = new OkHttpClient().newBuilder().connectTimeout(DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS).writeTimeout(DEFAULT_WRITE_TIMEOUT, TimeUnit.MILLISECONDS).readTimeout(DEFAULT_READ_TIMEOUT, TimeUnit.MILLISECONDS).build();InputStream is = null;try {Response response = okHttpClient.newCall(request).execute();//正常返回if(response.code() == 200){//打印返回的字符数据is = response.body().byteStream();byte[] bytes = new byte[1024];int len = 0;while ((len = is.read(bytes)) != -1) {ByteArrayOutputStream outputStream = new ByteArrayOutputStream();outputStream.write(bytes, 0, len);outputStream.flush();String result = new String(outputStream.toByteArray(), StandardCharsets.UTF_8);log.info(result);}}else{String result = response.body().string();String jsonStr = JSON.parseObject(result).toJSONString();log.info(jsonStr);}} catch (Throwable e) {log.error("执行异常", e);} finally {if (!Objects.isNull(is)) {try {is.close();} catch (Exception e) {e.printStackTrace();}}countDownLatch.countDown();}}public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(1);String question = "我家周边有什么好吃、好玩的地方嘛?";String history = "[{\n" +"\"from\": \"user\",\n" +"\"value\": \"你好\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"你好!有什么我能为你效劳的吗?\"\n" +"},\n" +"{\n" +"\"from\": \"user\",\n" +"\"value\": \"我家在广州,你呢?\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"我是一个人工智能助手,没有具体的家。\"\n" +"}]";//流式输出long starttime = System.currentTimeMillis();chat(question,history,countDownLatch);long endtime = System.currentTimeMillis();System.err.println((endtime-starttime));try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}}

maven依赖

<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>3.14.9</version>
</dependency>

4.3. HttpClient

import lombok.extern.slf4j.Slf4j;
import org.asynchttpclient.AsyncHttpClient;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.DefaultAsyncHttpClient;
import org.asynchttpclient.DefaultAsyncHttpClientConfig;
import org.asynchttpclient.channel.DefaultKeepAliveStrategy;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;@Slf4j
public class QwenCallTest {private static String url = "http://127.0.0.1:9999/api/chat";private static String DEFAULT_TEMPLATE = "{\"prompt\":\"%s\",\"history\":%x,\"top_p\":0.9, \"temperature\":0.45,\"repetition_penalty\":1.1, \"max_new_tokens\":8192}";private static int DEFAULT_CONNECTION_TIMEOUT = 3 * 1000;private static int DEFAULT_REQUEST_TIMEOUT = 15* 1000;private static int DEFAULT_READ_TIMEOUT = 15* 1000;private static String DEFAULT_USERID = "xxxxx";private static String DEFAULT_SECRET = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";private static AsyncHttpClientConfig asyncHttpClientConfig = new DefaultAsyncHttpClientConfig.Builder().setConnectTimeout(DEFAULT_CONNECTION_TIMEOUT).setReadTimeout(DEFAULT_READ_TIMEOUT).setRequestTimeout(DEFAULT_REQUEST_TIMEOUT).setTcpNoDelay(true).setMaxConnections(1_000_000).setMaxConnectionsPerHost(100_000).setMaxRequestRetry(0).setSoReuseAddress(true).setKeepAlive(true).setKeepAliveStrategy(new DefaultKeepAliveStrategy()).build();public static final AsyncHttpClient ugc_asyncHttpClient = new DefaultAsyncHttpClient(asyncHttpClientConfig);public static void chat(String question,String history, CountDownLatch countDownLatch ) {String prompt = DEFAULT_TEMPLATE.replace("%s", question).replace("%x", history);log.info("prompt: {}", prompt);try {ugc_asyncHttpClient.preparePost(url).addHeader("Content-Type", "application/json; charset=utf-8").addHeader("userId", DEFAULT_USERID).addHeader("secret", DEFAULT_SECRET).addHeader("Accept", "text/event-stream").setBody(prompt).execute(new QwenStreamHandler(countDownLatch)).get(30, TimeUnit.SECONDS);} catch (Exception e) {log.error(prompt + " >> 出现异常");}}public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(1);String question = "我家周边有什么好吃、好玩的地方嘛?";String history = "[{\n" +"\"from\": \"user\",\n" +"\"value\": \"你好\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"你好!有什么我能为你效劳的吗?\"\n" +"},\n" +"{\n" +"\"from\": \"user\",\n" +"\"value\": \"我家在广州,你呢?\"\n" +"},\n" +"{\n" +"\"from\": \"assistant\",\n" +"\"value\": \"我是一个人工智能助手,没有具体的家。\"\n" +"}]";chat(question,history,countDownLatch);try {countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}try {ugc_asyncHttpClient.close();} catch (IOException e) {e.printStackTrace();}}}

import io.netty.handler.codec.http.HttpHeaders;
import lombok.extern.slf4j.Slf4j;
import org.asynchttpclient.HttpResponseBodyPart;
import org.asynchttpclient.HttpResponseStatus;
import org.asynchttpclient.handler.StreamedAsyncHandler;
import org.asynchttpclient.netty.EagerResponseBodyPart;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;import java.util.concurrent.CountDownLatch;@Slf4j
public class QwenStreamHandler implements StreamedAsyncHandler<String> {private CountDownLatch countDownLatch;public QwenStreamHandler(CountDownLatch countDownLatch){this.countDownLatch = countDownLatch;}@Overridepublic State onStream(Publisher publisher) {publisher.subscribe(new Subscriber() {@Overridepublic void onSubscribe(Subscription subscription) {subscription.request(Long.MAX_VALUE);}@Overridepublic void onNext(Object obj) {try{if(obj instanceof EagerResponseBodyPart){EagerResponseBodyPart part = (EagerResponseBodyPart)obj;byte[] bytes = part.getBodyPartBytes();String words = new String(bytes,"UTF-8");log.info(words);}}catch(Throwable e){log.error("系统异常",e);}}@Overridepublic void onError(Throwable throwable) {log.error("系统异常",throwable);}@Overridepublic void onComplete() {countDownLatch.countDown();}});return State.CONTINUE;}@Overridepublic State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {log.info("onStatusReceived: {}",responseStatus.getStatusCode());return responseStatus.getStatusCode() == 200 ? State.CONTINUE : State.ABORT;}@Overridepublic State onHeadersReceived(HttpHeaders headers) throws Exception {return State.CONTINUE;}@Overridepublic State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {return State.CONTINUE;}@Overridepublic void onThrowable(Throwable t) {log.error("onThrowable", t);}@Overridepublic String onCompleted() throws Exception {return State.ABORT.name();}
}

maven依赖

<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.12</version>
</dependency><dependency><groupId>org.asynchttpclient</groupId><artifactId>async-http-client</artifactId><version>2.12.3</version>
</dependency>

五、附带说明

5.1. 需要根据实际情况修改url,userId以及secret的信息

5.2. 如果AI服务已经实现了负载均衡,那么在URL中应该使用SLB(全局负载均衡)的IP地址

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/632954.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python统计分析——样本均值的分布(上)

参考资料&#xff1a;用python动手学统计学 1、导入库 import numpy as np import pandas as pd import scipy as sp from scipy import statsfrom matplotlib import pyplot as plt import seaborn as sns 2、设置总体 本次研究总体是均值为4、标准差为0.8的正态总体。 po…

使用 MinIO 和 PostgreSQL 简化数据事件

本教程将教您如何使用 Docker 和 Docker Compose 在 MinIO 和 PostgreSQL 之间设置和管理数据事件&#xff0c;也称为存储桶或对象事件。 您可能已经在利用 MinIO 事件与外部服务进行通信&#xff0c;现在您将通过使用 PostgreSQL 自动化和简化数据事件管理来增强数据处理能力…

栈(Stack):计算机科学中的基础数据结构

前言 在计算机科学中&#xff0c;栈&#xff08;Stack&#xff09;是一种重要的数据结构&#xff0c;广泛用于各种程序和系统中。本文旨在向初学者介绍栈的概念、为什么需要它以及它的一些常见使用场景。 什么是栈&#xff1f; 栈是一种遵循后进先出&#xff08;Last In, First…

Linux--磁盘与文件系统

目录 1.什么是文件系统 2.磁盘 2.1什么时磁盘 2.2磁盘的物理存储结构 2.3磁盘的逻辑抽象结构 3.磁盘文件系统&#xff08;EXT2&#xff09; inode Table(i结点表) Data Block inode Bitmap(inode位图) Block Bitmap(块位图) 在Linux如何删除文件 Group Descriptor Ta…

30分钟带你深入优化安卓Bitmap大图

30分钟带你源码深入了解Bitmap以及优化安卓大图 一、前言二、Bitmap入门1. 如何创建Bitmap?2. Bitmap的堆内存分布在哪里3. 图片文件越大&#xff0c;Bitmap堆内存会越大吗&#xff1f;4. 如何管理Bitmap的内存&#xff1f;5. 实战修改Bitmap的堆内存&#xff0c;改变图片的图…

24秋招,百度测试开发工程师三面

前言 大家好&#xff0c;我是chowley&#xff0c;今天来回顾一下&#xff0c;我当时参加百度秋招补录&#xff0c;测试开发工程师的第三面-leader面 到面试开始的时间&#xff0c;面试官打电话表示让我等十分钟&#xff0c;随后跳过自我介绍&#xff0c;直接开面 时间&#…

MySQL中锁的概述

按照锁的粒度来分可分为&#xff1a;全局锁&#xff08;锁住当前数据库的所有数据表&#xff09;&#xff0c;表级锁&#xff08;锁住对应的数据表&#xff09;&#xff0c;行级锁&#xff08;每次锁住对应的行数据&#xff09; 加全局锁&#xff1a;flush tables with read lo…

【PyTorch】PyTorch之Tensors操作篇

文章目录 前言一、Tensor创建1、TENSOR2、SPARSE_COO_TENSOR3、SPARSE_CSR_TENSOR4、ASARRAY5、AS_TENSOR6、FROM_NUMPY7、FROMBUFFER8、ZEROS和ZEROS_LIKE9、ONES和ONES_LIKE10、ARANGE11、LINSPACE12、LOGSPACE13、EYE14、EMPTY和EMPTY_LIKE15、FULL和FULL_LIKE 前言 介绍Te…

Docker 安装 PHP

Docker 安装 PHP 安装 PHP 镜像 方法一、docker pull php 查找 Docker Hub 上的 php 镜像: 可以通过 Sort by 查看其他版本的 php&#xff0c;默认是最新版本 php:latest。 此外&#xff0c;我们还可以用 docker search php 命令来查看可用版本&#xff1a; runoobrunoob:…

使用C#操作文件:一个实际案例——替换文件中的IP地址

标题&#xff1a; 使用C#操作文件&#xff1a;一个实际案例——替换文件中的IP地址 介绍&#xff1a; 欢迎阅读我的最新博客&#xff01;今天&#xff0c;我们将探讨如何使用C#来处理一个实际的编程挑战&#xff1a;读取一个配置文件并替换其中的IP地址。这是一个非常常见的…

windows安装mysql5.7

看了如何学习mysql后&#xff0c;就开始本地安装mysql&#xff0c;开始学习了。 1.官网下载 官网地址&#xff1a; https://dev.mysql.com/downloads/mysql/ 选择5.7版本 点击 “No thanks, just start my download”开始下载 下载64位的压缩包版 解压下载好的.zip文件&#xf…

mysql主从报错:Last_IO_Error: Error connecting to source解决方法

目录 报错 处理方法 1.从库停止同步 2.主库修改my.cnf 生效配置default-authentication-pluginmysql_native_password 3.重启服务重新创建复制用户 4.重新同步 5.测试主从 报错 Last_IO_Error: Error connecting to source repl_user192.168.213.15:3306. This was atte…

Adobe全新AI驱动的Premiere Pro功能消除了枯燥的音频编辑任务

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

Github Copilot 的使用方法和快捷键*

GitHub Copilot是一款由GitHub开发的人工智能代码助手&#xff0c;它可以根据上下文和提示生成代码片段和建议。以下是使用GitHub Copilot的基本方法和一些常用的快捷键&#xff1a; 安装和启用&#xff1a;在支持的编辑器&#xff08;如Visual Studio Code&#xff09;中安装G…

(2023版)斯坦福CS231n学习笔记:DL与CV教程 (12) | 视觉模型可视化与可解释性(Visualizing and Understanding)

前言 &#x1f4da; 笔记专栏&#xff1a;斯坦福CS231N&#xff1a;面向视觉识别的卷积神经网络&#xff08;23&#xff09;&#x1f517; 课程链接&#xff1a;https://www.bilibili.com/video/BV1xV411R7i5&#x1f4bb; CS231n: 深度学习计算机视觉&#xff08;2017&#xf…

C# dataGridView 列的勾选框改变事件

dataGridView 增加一列 DataGridViewCheckBoxColumn 然后设置复选框值如下图&#xff1a; dataGridView增加两个事件 private void dataGridView1_CurrentCellDirtyStateChanged(object sender, EventArgs e){//提交改变&#xff0c;触发dataGridView1_CellValueChanged事件&…

Android Launcher3各启动场景源码分析

文章目录 一、概述二、开机启动Launcher2.1、开机启动Launcher流程图2.2、开机启动流程源码分析 三、短压Home键启动Launcher3.1、短压Home键启动Launcher流程图3.2、短压Home键启动Launcher源码分析 四、Launcher异常崩溃后的自启动4.1、Launcher异常崩溃后的自启动流程图4.2、…

【计算机网络】难点、易遗忘点总结

文章目录 1. 单工通信、半双工通信和全双工通信2. TCP的三次握手和四次挥手 1. 单工通信、半双工通信和全双工通信 主要区别在于信息传输的方向和时间安排。单工通信是指信息只能在一个方向上传输的通信方式。半双工通信允许信息在两个方向上传输&#xff0c;但在任何给定的时…

OpenGL DIR

Mesa简介-CSDN博客 Mesa, also called Mesa3D and The Mesa 3D Graphics Library, is an open source software implementation of OpenGL, Vulkan, and other graphics API specifications. Mesa translates these specifications to vendor-specific graphics ha…

Linux 使用PS命令掌握进程管理

在Linux系统中&#xff0c;进程管理是系统管理员和开发人员必备的技能之一。而PS命令作为进程管理的重要工具&#xff0c;可以帮助我们查看和监控系统中运行的进程。本文将详细解析PS命令的使用方法和输出结果&#xff0c;帮助读者全面掌握进程管理的利器。 PS命令概述&#xf…