Java 对接 OpenAi API 批处理

前提

1、OpenAi API是收费的,但是价格还是比较划算的,而批处理是OpenAi在四月刚出的,价格是单个处理的一半,也就是便宜了50%,下面会讲到价格

2、需要一个api key,申请key也很简单,在OpenAi后台生成一个key即可,还需要绑定银行卡,用于API的扣费

3、批处理的对接也比较简单,不需要高版本的JDK和高版本的SpringBoot版本,直接进行http请求就可以,都不需要依赖任何第三方

3、本文出现的网址,需要科学上网才可访问,同理,在你的项目中如果是国内ip是访问不了的

4、想尝试则可以买一个国外的代理服务,科学上网的方法有很多,这里就不做过多阐述

价格对比

官网给出的价格图:https://openai.com/api/pricing/

gpt-4o-mini也是最近才出的,价格比GPT-3.5还要便宜,建议都用gpt-4o-mini,非常划算

Token计算

官网给出一个在线计算token数的网址,输入文本就可以知道token信息

https://platform.openai.com/tokenizer

这次OpenAi出的批处理,还有个优势就是不需要自己去依赖第三方去计算token数量,批处理每次都会返回用到的token数量

你也可以利用三方的依赖去计算所用到的token,只需要引入以下依赖

<!-- 解析Ai模型词组(token)-->
<dependency>
    <groupId>com.knuddels</groupId>
    <artifactId>jtokkit</artifactId>
    <version>1.1.0</version>
</dependency>

使用方法也很简单

// 计算输入的token
EncodingRegistry registry = Encodings.newDefaultEncodingRegistry();
int requestTokenCount = registry.getEncoding(EncodingType.CL100K_BASE).countTokensOrdinary(使用到的提示词字符串 + 消息字符串);

// 计算输出的token
int resultTokenCount = registry.getEncodingForModel(模型名称,例如gpt-40-mini).countTokensOrdinary(ai返回的结果字符串);

官网已给出1k的token所需要的价格,所以我们如果需要计算价格,只需要计算出我们所用的token然后乘以相应的价格即可

批处理

介绍

官网介绍:https://platform.openai.com/docs/guides/batch

躺平玩家看下面,已经为各位翻译好了

简单来说,以下是对比

优势

  • 更高的成本效益:与同步 API 相比,成本可享受 50% 的折扣
  • 更高的速率限制:与同步 API 相比,具有更大的裕量
  • 快速完成时间:每批在 24 小时内完成(通常更快)

 劣势

  • 非实时

哈哈哈,我觉得优势非常明显哇,经过我的尝试,批处理的速度很快,我差不多500个文章的简介进行判断文章的文章类型,就几分钟,量更大的还没尝试,价格更是便宜了一半,太香了

流程

提前给各位大佬梳理一下使用的流程

1、将我们需要处理的数据组装成一个jsonl文件,jsonl文件就是多个json用换行分割,将这个jsonl文件上传到OpenAi服务器上,会返回你一个输入文件标识(input_file_id

2、创建一个批次任务,这里需要用到第一步的input_file_id,然后会返回一个批次id

3、循环的去根据批次id去检索执行的状态是否完成(status=completed),完成之后拿到输出文件标识(output_file_id

 4、根据文件id去拿到检索文件内容,返回的结果也是一个jsonl,这个jsonl对应着我们输入文件

API介绍

官网:https://platform.openai.com/docs/api-reference/batch

1、上传文件

上传可跨各种端点使用的文件。单个文件的最大大小为 512 MB,一个组织上传的所有文件的大小最大为 100 GB

API:

POST:https://api.openai.com/v1/batches

参数: 

file:仅支持最大 100 MB 的 .jsonl 文件,需要为以下格式

{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4o-mini", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is 2+2?"}]}}



# 字段解释
custom_id:开发人员提供的每个请求 ID,用于将输出与输入匹配。对于批处理中的每个请求必须是唯一的。

method:要用于请求的 HTTP 方法。目前仅POST受支持。

model:ai语言模型

url:要用于请求的 OpenAI API 相对 URL。现在/v1/chat/completions, /v1/embeddings和/v1/completions受到支持。

purpose:上传文件的预期用途。 使用“assistants”表示助手和消息文件,“vision”表示助手图像文件输入,“batch”表示 Batch API,“fine-tune”表示微调.

返回:
{
  "id": "input_file_id",
  "object": "file",
  "bytes": 120000,
  "created_at": 1677610602,
  "filename": "file.jsonl",
  "purpose": "batch",
}

2、创建批处理

从上传的请求文件创建并执行批处理

API:

POST:https://api.openai.com/v1/batches

参数:

input_file_id:上传文件的 ID

endpoint:现在/v1/chat/completions, /v1/embeddings和/v1/completions受到支持

completion_window:应处理批次的时间范围。目前仅24h受支持

返回:
{
  "id": "batch_abc123",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-abc123",
  "completion_window": "24h",
  "status": "validating",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1711471533,
  "in_progress_at": null,
  "expires_at": null,
  "finalizing_at": null,
  "completed_at": null,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 0,
    "completed": 0,
    "failed": 0
  },
  "metadata": {
    "customer_id": "user_123456789",
    "batch_description": "Nightly eval job",
  }
}

3、检索批次

检索批处理的进度和状态

API:

GET:https://api.openai.com/v1/batches/{batch_id}

参数:

batch_id:要检索的批处理的 ID

返回:
{
  "id": "batch_abc123",
  "object": "batch",
  "endpoint": "/v1/completions",
  "errors": null,
  "input_file_id": "file-abc123",
  "completion_window": "24h",
  "status": "completed",
  "output_file_id": "file-cvaTdG",
  "error_file_id": "file-HOWS94",
  "created_at": 1711471533,
  "in_progress_at": 1711471538,
  "expires_at": 1711557933,
  "finalizing_at": 1711493133,
  "completed_at": 1711493163,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 100,
    "completed": 95,
    "failed": 5
  },
  "metadata": {
    "customer_id": "user_123456789",
    "batch_description": "Nightly eval job",
  }
}

4、检索文件

API:

GET:https://api.openai.com/v1/files/{file_id}

参数:

file_id:要用于此请求的文件的 ID。

返回:

多个json的jsonl文件,单个json文件格式如下

{
    "id": "batch_req_wnaDys",
    "custom_id": "request-2",
    "response": {
        "status_code": 200,
        "request_id": "req_c187b3",
        "body": {
            "id": "chatcmpl-9758Iw",
            "object": "chat.completion",
            "created": 1711475054,
            "model": "gpt-4o-mini",
            "choices": [
                {
                    "index": 0,
                    "message": {
                        "role": "assistant",
                        "content": "2 + 2 equals 4."
                    },
                    "finish_reason": "stop"
                }
            ],
            "usage": {
                "prompt_tokens": 24,
                "completion_tokens": 15,
                "total_tokens": 39
            },
            "system_fingerprint": null
        }
    },
    "error": null
}

5、取消批处理

取消正在进行的批处理。批处理将处于cancelling状态长达 10 分钟,然后更改为已cancelled,此时输出文件中将提供部分结果(如果有)

API:

POST:https://api.openai.com/v1/batches/{batch_id}/cancel

参数:

batch_id:要取消的批次的 ID。

返回:
{
  "id": "batch_abc123",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-abc123",
  "completion_window": "24h",
  "status": "cancelling",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1711471533,
  "in_progress_at": 1711471538,
  "expires_at": 1711557933,
  "finalizing_at": null,
  "completed_at": null,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": 1711475133,
  "cancelled_at": null,
  "request_counts": {
    "total": 100,
    "completed": 23,
    "failed": 1
  },
  "metadata": {
    "customer_id": "user_123456789",
    "batch_description": "Nightly eval job",
  }
}

API使用示例代码

上传批次文件

/**
 * 上传批次文件
 */
public String uploadBatchFile(String jsonStr, String fileName) {
    File file = new File(System.getProperty("java.io.tmpdir") + "/" + fileName);
    try (FileWriter writer = new FileWriter(file)) {
        writer.write(jsonStr);
    } catch (IOException e) {
        log.error("openai创建jsonl文件异常,", e);
        return null;
    }
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);
    headers.set("Authorization", "Bearer " + openAiKey);
    headers.set("Connection", "keep-alive");
    MultiValueMap<String, Object> body = new LinkedMultiValueMap<>();
    body.add("purpose", "batch");
    body.add("file", new FileSystemResource(file));
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
    try {
        ResponseEntity<String> response = new RestTemplate().postForEntity(openAiUrl + "/v1/files", requestEntity, String.class);
        log.info("openai上传批次文件返回:fileName:{},result:{}", fileName, response.getBody());
        OpenAiUploadDTO uploadDTO = JSONObject.parseObject(response.getBody(), OpenAiUploadDTO.class);
        if (uploadDTO != null && OpenAiStatusEnum.PROCESSED.getName().equals(uploadDTO.getStatus())) {
            return uploadDTO.getId();
        }
        log.error("openai上传批次文件返回失败,result:{}", response.getBody());
    } catch (Exception e) {
        log.error("openai上传批次文件返回异常,", e);
    }
    return "";
}

创建批次

/**
 * 创建批次
 */
public String createBatch(String fileId) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.set("Authorization", "Bearer " + openAiKey);
    headers.set("Connection", "keep-alive");
    // 创建批次
    Map<String, String> map = new HashMap<>();
    map.put("input_file_id", fileId);
    map.put("endpoint", "/v1/chat/completions");
    map.put("completion_window", "24h");
    HttpEntity<Map<String, String>> entity = new HttpEntity<>(map, headers);
    try {
        ResponseEntity<String> response = new RestTemplate().postForEntity(openAiUrl + "/v1/batches", entity, String.class);
        log.info("open-ai创建批次返回:fileId:{},result:{}", fileId, response.getBody());
        OpenAiBatchDTO openAiBatchDTO = JSONObject.parseObject(response.getBody(), OpenAiBatchDTO.class);
        if (openAiBatchDTO != null) {
            return openAiBatchDTO.getId();
        }
    } catch (Exception e) {
        log.error("open-ai创建批次返回异常,", e);
    }
    return "";
}

检索批次 

/**
 * 检索批次
 */
public OpenAiBatchDTO retrieveBatch(String batchId) {
    HttpHeaders headers = new HttpHeaders();
    headers.set("Authorization", "Bearer " + openAiKey);
    headers.set("Connection", "keep-alive");
    headers.setContentType(MediaType.APPLICATION_JSON);
    HttpEntity<String> entity = new HttpEntity<>(headers);
    try {
        ResponseEntity<String> response = new RestTemplate().exchange(openAiUrl + "/v1/batches/" + batchId, HttpMethod.GET, entity, String.class);
        log.info("openai检索批次返回:batchId:{},result:{}", batchId, response.getBody());
        return JSONObject.parseObject(response.getBody(), OpenAiBatchDTO.class);
    } catch (NullPointerException e) {
        log.error("openai检索批次返回异常,", e);
    }
    return null;
}

 检索文件内容

/**
 * 检索文件内容
 */
public List<OpenAiBatchContentDTO> retrieveFileContent(String outFileId) {
    HttpHeaders headers = new HttpHeaders();
    headers.set("Authorization", "Bearer " + openAiKey);
    headers.set("Connection", "keep-alive");
    HttpEntity<String> entity = new HttpEntity<>(headers);
    List<OpenAiBatchContentDTO> resultList = new ArrayList<>();
    try {
        ResponseEntity<String> response = new RestTemplate().exchange(openAiUrl + "/v1/files/" + outFileId + "/content", HttpMethod.GET, entity, String.class);
        String body = response.getBody();
        log.info("openai检索文件内容返回:outFiledId:{},result:{}", outFileId, body);
        if (!StringUtils.hasText(body)) {
            return null;
        }
        String[] lines = body.split("\n");
        ObjectMapper objectMapper = new ObjectMapper();
        for (String line : lines) {
            OpenAiBatchContentDTO contentDTO = objectMapper.convertValue(objectMapper.readValue(line, Map.class), OpenAiBatchContentDTO.class);
            resultList.add(contentDTO);
        }
    } catch (Exception e) {
        log.error("openai检索文件内容返回异常,", e);
    }
    return resultList;
}

取消批处理 

/**
 * 取消批处理
 */
public String cancelBatch(String batchId) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    headers.set("Authorization", "Bearer " + openAiKey);
    headers.set("Connection", "keep-alive");
    // 创建批次
    HttpEntity<String> entity = new HttpEntity<>(headers);
    try {
        ResponseEntity<String> response = new RestTemplate().postForEntity(openAiUrl + "/v1/batches/" + batchId + "/cancel", entity, String.class);
        log.info("openai取消批处理返回:batchId:{},result:{}", batchId, response.getBody());
        return response.getBody();
    } catch (Exception e) {
        log.error("openai取消批处理返回异常,", e);
    }
    return null;
  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; public class OpenAIJavaExample { public static void main(String[] args) { try { String apiKey = "YOUR_API_KEY"; String prompt = "Hello, world!"; String model = "gpt-3.5-turbo"; int maxTokens = 10; String response = OpenAIJavaExample.sendRequest(apiKey, prompt, model, maxTokens); System.out.println("Response: " + response); } catch (IOException e) { e.printStackTrace(); } } public static String sendRequest(String apiKey, String prompt, String model, int maxTokens) throws IOException { String url = "https://api.openai.com/v1/engines/" + model + "/completions"; URL obj = new URL(url); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); // Set the request method and headers con.setRequestMethod("POST"); con.setRequestProperty("Content-Type", "application/json"); con.setRequestProperty("Authorization", "Bearer " + apiKey); // Build the JSON request body String requestBody = "{\"prompt\": \"" + prompt + "\", \"max_tokens\": " + maxTokens + "}"; con.setDoOutput(true); try (OutputStream os = con.getOutputStream()) { byte[] input = requestBody.getBytes("utf-8"); os.write(input, 0, input.length); } // Send the request and get the response int responseCode = con.getResponseCode(); BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuilder response = new StringBuilder(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); } in.close(); return response.toString(); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值