返回博客

API 与 SDK指南

用 openai-java 在 Java 中发出第一个请求:base URL、key、用量

一个可运行的 Java 8+ 程序:将 openai-java 指向 CheapTokens,一次 chat completion,并打印回答、finish_reason 和 token 用量。

作者:CheapTokens Team发布于 4 分钟阅读

将 openai-java 指向 CheapTokens API 的 Java 代码
本页内容
  1. 添加依赖
  2. 在代码之外设置 key 和 base URL
  3. 程序
  4. 用 builder 显式设置值
  5. 你最先会遇到的三类错误
  6. 一次请求的成本

要从 Java 调用 CheapTokens,保留官方 openai-java SDK,只需改三处:base URL(https://api.cheaptokens.dev/v1)、API key,以及以普通 String 传入的 model id。下面的程序发送一次 chat completion,并打印回答、finish_reason 和 token 用量。它需要 Java 8 或更高版本。

如果你的团队已有对接 OpenAI 的 Spring 服务,这就是全部迁移工作:不需要新客户端,也不需要新的请求类。

添加依赖#

SDK 的 README(github.com/openai/openai-java,查阅于 2026 年 11 月 4 日)显示版本 4.99.0,group 为 com.openai,artifact 为 openai-java。README 没有印出发布日期,所以复制之前请先到仓库查看最新版本。

将 openai-java 添加到你的构建

Maven

xml
<dependency>
  <groupId>com.openai</groupId>
  <artifactId>openai-java</artifactId>
  <version>4.99.0</version>
</dependency>

Gradle

kotlin
implementation("com.openai:openai-java:4.99.0")

与框架无关的 artifact 可在 Java 8+ 上运行,所以较旧的企业 JDK 不是障碍。

在代码之外设置 key 和 base URL#

SDK 读取两个环境变量:OPENAI_API_KEY 和 OPENAI_BASE_URL。OpenAIOkHttpClient.fromEnv() 会同时读取两者,因此 key 永远不会进入你的源文件。(系统属性 openai.apiKey 和 openai.baseUrl 也可用,且优先级高于环境变量。)

bash
export OPENAI_API_KEY="your-cheaptokens-key"
export OPENAI_BASE_URL="https://api.cheaptokens.dev/v1"

SDK 的默认 base URL 是 https://api.openai.com/v1。跳过第二行,请求就会发到那里,并带上一个它不认识的 CheapTokens key。这是首次运行就得到 401 的一种方式。关于 key 应保存在哪里以及如何避免它进入 git,请参阅安全存储 API key。

终端中的两个环境变量和一条运行命令
Key 和 base URL 来自环境,而不是源文件

程序#

创建 FirstRequest.java:

java
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.models.chat.completions.ChatCompletion;
import com.openai.models.chat.completions.ChatCompletionCreateParams;

public class FirstRequest {
  public static void main(String[] args) {
    OpenAIClient client = OpenAIOkHttpClient.fromEnv();

    ChatCompletionCreateParams params = ChatCompletionCreateParams.builder()
        .model("deepseek-ai/DeepSeek-V4-Flash-0731")
        .addUserMessage("Explain in two sentences what a token is.")
        .maxCompletionTokens(300L)
        .build();

    ChatCompletion completion = client.chat().completions().create(params);
    ChatCompletion.Choice choice = completion.choices().get(0);

    System.out.println(choice.message().content().orElse("(no text)"));
    System.out.println("finish_reason: " + choice.finishReason());
    completion.usage().ifPresent(u ->
        System.out.println("input " + u.promptTokens()
            + ", output " + u.completionTokens()));
  }
}

有几行值得再看一眼。

  • .model("deepseek-ai/DeepSeek-V4-Flash-0731")。 SDK 的 README 示例使用 ChatModel.GPT_5_2 之类的类型化常量,但 builder 也有 model(String) 重载,所以任何 id 都可用。请使用 CheapTokens 文档中的完整 id:短名称 deepseek-v4-flash-0731 是网站上标注价格的方式;API 需要带 deepseek-ai/ 前缀的完整 id。不确定 id?用 GET /v1/models 列出模型。
  • .maxCompletionTokens(300L)。 回答长度的上限,类型为 long。DeepSeek-V4-Flash 每次回答最多允许 32,768 个输出 token,所以这里的 300 是为了让演示保持简短的选择,而不是模型的限制。
  • content() 是 Optional。 这就是程序中有 orElse 的原因。仅由工具调用组成的回复没有文本。
  • usage() 也是 Optional。 服务器发送时,ifPresent 会打印它。

按你的构建方式编译并运行(mvn compile exec:java -Dexec.mainClass=FirstRequest,或 Gradle 中的 JavaExec 任务)。你应该会得到对 token 的解释,然后是一行包含 stop 的 finish_reason: …,再是一行输入和输出 token 计数。如果 finish_reason 显示 length,说明回答达到了 300 token 上限并被截断:请调高这个数字(为什么会这样)。

usage() 返回 prompt、completion 和 total token 计数(promptTokens()、completionTokens()、totalTokens())。

用 builder 显式设置值#

测试,或拥有多个客户端的服务,有时需要显式值。同一个客户端,手动构建:

java
OpenAIClient client = OpenAIOkHttpClient.builder()
    .apiKey(System.getenv("OPENAI_API_KEY"))
    .baseUrl("https://api.cheaptokens.dev/v1")
    .build();

这一行之后的所有内容,params 和 create 调用,都保持与上面完全相同。即使在这里也要继续从环境读取 key;.java 文件中的字符串字面量最终会进入你的仓库。

Java 代码、openai-java、CheapTokens 端点和模型
你的代码与 SDK 对话;只有 base URL 决定谁来回答

你最先会遇到的三类错误#

配对

将症状与原因对应起来

  • 401 Unauthorizedkey 为空或错误,或者因为未设置 OPENAI_BASE_URL,请求发到了 api.openai.com
  • 404 Not Foundbase URL 缺少 /v1,或 model id 不存在
  • finish_reason 显示 lengthmaxCompletionTokens 低于回答所需

空的环境变量在 IDE 中很容易被忽略:从工具栏启动的运行配置可能不会继承你在终端中导出的变量。如果 key“已设置”但仍得到 401,请将变量添加到运行配置中。关于这些状态码的更多信息:API 401 或 404:key、base URL、model id。

一次请求的成本#

因为你打印了 usage,所以可以在扩大规模前为请求定价。假设你的服务发送约 200 个输入 token,并得到 300 个输出。按今天的价格,在 DeepSeek-V4-Flash 上该请求花费 $0.000033。输入和输出一起计费,总计比 OpenRouter 便宜。下表按我们的模型重复计算 1,000 次请求:

模型 每次请求 1,000 次请求 OpenRouter,1,000 次请求
GLM-5.3-Flash $0.000016 $0.016 $0.0316
DeepSeek-V4-Flash $0.000033 $0.033 $0.056
MiniMax-M2.7 $0.000055 $0.055 $0.66

每次请求 200 个输入 token 和 300 个输出 token。每 100 万 token 的价格为当前价格,会随我们的资费一同变动;OpenRouter 列显示相同工作负载的参考价格。

现在代入你自己的流量。将滑块拖到你首次打印的 usage 行中的数字:

成本计算器

一个每天 2,000 次请求的支持服务

每次请求的输入 token 数: 200 · 每次请求的输出 token 数: 300 · 每日请求数: 2,000

模型每天每月
GLM-5.3-Flash$0.032$0.96
DeepSeek-V4-Flash$0.066$1.98
MiniMax-M2.7$0.11$3.30

如果需要每次回答更多空间或不同形态的工作负载,DeepSeek-V4-Flash 提供 380K 上下文窗口,且一天中任何时段价格相同;GLM-5.3-Flash(zai-org/GLM-5.3-Flash)可在同一程序中直接替换:只改 id,其他不变。所有模型的当前价格在定价页面,文档展示了其他语言中的相同请求。用 Go 做这个?请参阅openai-go 版本。