Skip to main content
SDK

Java SDK

安装和使用 Qoder Cloud Agents Java SDK,了解 Builder、同步与异步调用、流式响应、资源管理、重试和超时。

Qoder Cloud Agents Java SDK 提供类型化的同步和异步客户端,通过 Builder 构造配置与参数,并统一处理鉴权、分页、重试、超时和 SSE 流式响应。

安装

需要 Java 8 或更高版本。

Maven

在 pom.xml 中添加:
<dependency>
  <groupId>com.qoder.sdk</groupId>
  <artifactId>qoder-cloud-agents-sdk</artifactId>
  <version>0.2.0</version>
</dependency>

Gradle

使用 Maven Central,并添加固定版本依赖:
repositories {
    mavenCentral()
}

dependencies {
    implementation 'com.qoder.sdk:qoder-cloud-agents-sdk:0.2.0'
}

基本用法

将个人访问令牌(PAT)保存到环境变量:
export QODER_PAT="替换为你的 PAT"
使用 Forward 客户端创建模板和身份,发起会话并发送消息:
import com.qoder.cloudagents.ForwardClient;
import com.qoder.cloudagents.forward.*;
import java.util.Collections;

public class QuickStart {
    public static void main(String[] args) {
        try (ForwardClient client = ForwardClient.builder().build()) {
            Environment env = client.environments().create(
                    EnvironmentCreateParams.builder()
                            .name("demo")
                            .config(EnvironmentConfig.builder()
                                    .type("cloud")
                                    .build())
                            .build());

            Identity identity = client.identities().create(
                    IdentityCreateParams.builder()
                            .externalId("demo-user")
                            .name("Demo User")
                            .build());

            Template template = client.templates().create(
                    TemplateCreateParams.builder()
                            .name("assistant")
                            .modelId("auto")
                            .environmentId(env.id())
                            .build());

            Session session = client.sessions().create(
                    SessionCreateParams.builder()
                            .identityId(identity.id())
                            .templateId(template.id())
                            .build());

            SessionEventParam message = SessionEventParam.builder()
                    .type("user.message")
                    .content(EventContent.text("你好"))
                    .build();

            client.sessions().events().send(
                    session.id(),
                    SessionEventSendParams.builder()
                            .events(Collections.singletonList(message))
                            .build());

            System.out.println(session.id());
        }
    }
}
使用 Managed 客户端创建 Agent 和环境,发起会话并发送消息。将以下代码保存为 ManagedQuickStart.java,在已添加 SDK 依赖的项目中运行:
import com.qoder.cloudagents.ManagedClient;
import com.qoder.cloudagents.managed.*;
import java.util.Collections;

public class ManagedQuickStart {
    public static void main(String[] args) {
        try (ManagedClient client = ManagedClient.builder().build()) {
            Agent agent = client.agents().create(
                    AgentCreateParams.builder()
                            .name("assistant")
                            .model("auto")
                            .build());

            Environment env = client.environments().create(
                    EnvironmentCreateParams.builder()
                            .name("demo")
                            .config(EnvironmentConfig.builder()
                                    .type("cloud")
                                    .build())
                            .build());

            Session session = client.sessions().create(
                    SessionCreateParams.builder()
                            .agent(agent.id())
                            .environmentId(env.id())
                            .build());

            SessionEventParam message = SessionEventParam.builder()
                    .type("user.message")
                    .content(EventContent.text("你好"))
                    .build();

            client.sessions().events().send(
                    session.id(),
                    SessionEventSendParams.builder()
                            .events(Collections.singletonList(message))
                            .build());

            System.out.println(session.id());
        }
    }
}
客户端可在线程间复用。应用结束使用时关闭拥有资源的客户端;短程序使用 try-with-resources 自动关闭。更多接入说明见 Managed 快速入门和 Forward API 参考。

客户端配置

配置ForwardManaged
同步客户端ForwardClientManagedClient
异步客户端AsyncForwardClientAsyncManagedClient
Base URL 环境变量QODER_FORWARD_BASE_URLQODER_BASE_URL
Global 默认地址https://api.qoder.com/api/v1/forwardhttps://api.qoder.com/api/v1/cloud
CN 地址https://api.qoder.com.cn/api/v1/forwardhttps://api.qoder.com.cn/api/v1/cloud
显式 Builder 配置优先于环境变量。默认 PAT 模式先读取 QODER_ACCESS_TOKEN,再读取 QODER_PAT。客户端不会自动加载 .env 文件。选择与账号、凭证及资源所在区域一致的完整 Base URL。
import com.qoder.cloudagents.ForwardClient;
import java.time.Duration;

try (ForwardClient client = ForwardClient.builder()
        .baseUrl("https://api.qoder.com.cn/api/v1/forward")
        .timeout(Duration.ofSeconds(30))
        .maxRetries(2)
        .defaultHeader("X-Application", "my-app")
        .build()) {
    System.out.println(client.models().list().data().size());
}
通过 withOptions(...) 创建不同配置的客户端副本,原客户端保持原配置:
import com.qoder.cloudagents.ForwardClient;
import java.time.Duration;

try (ForwardClient slowClient = client.withOptions(options ->
        options.timeout(Duration.ofSeconds(60)).maxRetries(1))) {
    System.out.println(slowClient.models().list().data().size());
}
副本需要独立关闭。显式注入的 Transport 和 Credential 仍由调用方持有,所有使用者结束后再释放这些共享资源。

鉴权

PAT

通过 .pat(token) 显式传入个人访问令牌,或采用环境变量配置。凭证应保存在应用的服务端环境中。

Service Account

设置 QODER_SA_KEY 后,显式创建凭证组件。组件负责置换、缓存 SAT(服务账号令牌),并在后续请求需要时更新:
import com.qoder.cloudagents.ForwardClient;
import com.qoder.cloudagents.auth.Region;
import com.qoder.cloudagents.auth.ServiceAccountCredential;

try (ServiceAccountCredential credential = ServiceAccountCredential.builder()
        .saKey(System.getenv("QODER_SA_KEY"))
        .region(Region.GLOBAL)
        .scopes("qca.access", "forward.access")
        .build();
     ForwardClient client = ForwardClient.builder()
        .credential(credential)
        .baseUrl("https://api.qoder.com/api/v1/forward")
        .build()) {
    System.out.println(client.models().list().data().size());
}
ManagedClient 也接受同一凭证组件。多个客户端可以共享凭证实例;上例会先关闭客户端,再关闭凭证。关闭客户端不会替应用关闭外部传入的凭证。 中国站需要同时使用 Region.CN 和 CN Base URL。仅访问 Managed 时可以申请 qca.access scope;权限及工作空间绑定需在服务端配置。 普通客户端不会因为环境中存在 QODER_SA_KEY 就自动启用置换,SA Key 也不能直接作为业务接口的访问令牌。SA 鉴权失败不会回退到 PAT。 如果应用自行管理 SAT,使用 .authMode(AuthMode.SA).accessToken(token),其中 AuthMode 位于 com.qoder.cloudagents.auth。静态 SAT 的更新由应用负责,同一客户端只配置一个明确的凭证来源。更多说明见认证文档。

同步与异步调用

同步方法直接返回结果。client.async() 提供共享认证与传输资源的异步视图,普通异步请求返回 CompletableFuture<T>:
import com.qoder.cloudagents.forward.Session;
import java.util.concurrent.CompletableFuture;

CompletableFuture<Session> pending = client.async().sessions().retrieve("sess_example");
pending.thenAccept(result -> System.out.println(result.id())).join();
也可以创建独立的异步客户端:
import com.qoder.cloudagents.AsyncForwardClient;
import com.qoder.cloudagents.ForwardClient;

try (AsyncForwardClient asyncClient = ForwardClient.builder()
        .pat(System.getenv("QODER_PAT"))
        .buildAsync()) {
    asyncClient.models().list()
        .thenAccept(result -> System.out.println(result.data().size()))
        .join();
}
示例使用 join() 让短程序等待请求完成。服务端异步链路可以组合 Future;只有需要等待结果的线程才调用 join() 或 get()。异步失败通常包装在 CompletionException 或 ExecutionException 中,可通过 getCause() 检查底层 SDK 异常。
对象关闭与取消行为
client.async() 返回的视图与父客户端共享资源,关闭视图不会关闭父客户端
buildAsync() 返回的客户端独立持有资源,用完后关闭
SDK 返回的 Futurecancel(true) 会取消对应 HTTP 调用及重试等待
应用通过 thenApply 等创建的派生 Future遵循 Java 的 Future 行为;需要停止底层请求时,取消 SDK 原始 Future
请求完成前保持拥有资源的客户端处于打开状态。中断同步调用会取消请求、保留线程中断标志,并抛出 ApiUserAbortException。

请求与响应类型

请求参数通过 Builder 构造,构造完成后为不可变对象。未设置字段与显式设置 null 的编码不同:
import com.qoder.cloudagents.forward.SessionUpdateParams;

SessionUpdateParams omitted = SessionUpdateParams.builder().build();
SessionUpdateParams explicitNull = SessionUpdateParams.builder().title(null).build();
SessionUpdateParams emptyString = SessionUpdateParams.builder().title("").build();
SessionUpdateParams omittedAgain = SessionUpdateParams.builder()
    .title("example")
    .omit("title")
    .build();
Builder 行为JSON 中的行为
不调用字段 setter不发送该字段
为字段设置 null发送 null
设置空字符串或空集合发送 ""、[] 或 {}
omit("字段的 JSON 名称")移除先前设置的字段
字段是否接受 null,以及如何处理该值,以对应接口定义为准。接受多种 JSON 表示的参数,应使用该参数公开的重载方法或包装类型,不依赖内部序列化实现。 响应使用不可变模型,通过方法读取字段。field(name) 和 rawJson() 保留对原始 JSON 的访问,未知字段不会被丢弃。JSON 通用值使用 com.qoder.cloudagents.core.JsonValue 表示;应用无需依赖 SDK 内部的 Jackson 类型。

流式响应

单次 SSE 连接通过 EventStream<T> 消费,并使用 try-with-resources 释放:
import com.qoder.cloudagents.forward.SessionEvent;
import com.qoder.cloudagents.forward.SessionEventStreamParams;
import com.qoder.cloudagents.streaming.EventStream;

try (EventStream<SessionEvent> stream = client.sessions().events().streamEvents(
        "sess_example", SessionEventStreamParams.builder().build())) {
    for (SessionEvent event : stream) {
        System.out.println(event.type());
    }
}
streamEvents 在 EOF 时结束,读取或协议错误会向调用方报告。需要在可恢复的断连后继续订阅时,显式使用 resumableStream:
import com.qoder.cloudagents.core.RequestOptions;
import com.qoder.cloudagents.forward.SessionEvent;
import com.qoder.cloudagents.forward.SessionEventStreamParams;
import com.qoder.cloudagents.streaming.EventStream;
import java.time.Duration;

try (EventStream<SessionEvent> stream = client.sessions().events().resumableStream(
        "sess_example",
        SessionEventStreamParams.builder().lastEventId("evt_checkpoint").build(),
        RequestOptions.builder().totalTimeout(Duration.ofMinutes(5)).build())) {
    for (SessionEvent event : stream) {
        System.out.println(event.type());
        // 应用成功处理事件后,再持久化该恢复位置。
        System.out.println("cursor: " + stream.lastEventId());
    }
}
将 evt_checkpoint 替换为有效恢复位置;首次订阅可省略 lastEventId。恢复流在开始消费时建立连接,后续沿用最近交付事件的 SSE 游标。仅调用迭代器的 hasNext() 不会推进交付游标。 没有游标时从当前事件流尾部开始,补读历史需要有效的历史恢复位置。恢复不会重新发送业务请求,也不保证“恰好一次”处理;同一事件的多个增量可能共用 ID。 maxRetries 控制每次 HTTP 建连的重试次数,totalTimeout 控制整个订阅,包括重连等待和读取。流结束与业务任务完成需要分别判断;具体事件含义见 API 定义。

异步订阅

异步 streamEvents(...) 返回建立连接的 Future;拿到的 EventStream 仍按阻塞方式读取。需要后台读取并顺序处理回调时,使用 subscribe:
import com.qoder.cloudagents.core.RequestOptions;
import com.qoder.cloudagents.forward.SessionEvent;
import com.qoder.cloudagents.forward.SessionEventStreamParams;
import com.qoder.cloudagents.streaming.EventStream;
import com.qoder.cloudagents.streaming.EventSubscription;
import java.time.Duration;

try (EventStream<SessionEvent> stream = client.async().sessions().events().resumableStream(
        "sess_example", SessionEventStreamParams.builder().build(),
        RequestOptions.builder().totalTimeout(Duration.ofMinutes(5)).build());
     EventSubscription subscription = stream.subscribe(event ->
        System.out.println(event.type()))) {
    subscription.completion().join();
}
subscribe 默认使用自己的读取线程,也接受应用提供的 Executor。读取与回调按顺序执行,回调应及时返回;不要将阻塞读取安排到应用的事件循环线程上。 关闭流、关闭订阅,或取消订阅的 completion(),都会结束读取和重连。它们只停止本地订阅,不会自动停止云端已经开始的执行。

错误处理与 Request ID

普通请求失败继承自 ApiException。HTTP 错误通过 ApiStatusException 提供状态码和请求标识:
import com.qoder.cloudagents.errors.ApiException;
import com.qoder.cloudagents.errors.ApiStatusException;
import com.qoder.cloudagents.errors.ApiTimeoutException;
import com.qoder.cloudagents.errors.ApiUserAbortException;

try {
    System.out.println(client.sessions().retrieve("sess_example").requestId());
} catch (ApiUserAbortException error) {
    System.err.println("请求已取消");
} catch (ApiTimeoutException error) {
    System.err.println("请求超时");
} catch (ApiStatusException error) {
    System.err.println("HTTP " + error.statusCode() + ", request ID: " + error.requestId());
} catch (ApiException error) {
    System.err.println("请求失败:" + error.getClass().getSimpleName());
}
连接失败使用 ApiConnectionException;ApiTimeoutException 是其子类。SSE 协议或错误帧可能使用 StreamException,SA 凭证置换失败使用 ServiceAccountExchangeException,不应假设所有失败都有业务 API 的 HTTP 状态码。 正常响应模型提供 requestId()。流通过 response() 提供当前连接的状态码、响应头和 Request ID;惰性恢复流尚未连接时,response() 可能为 null。

重试与幂等性

默认最多重试 2 次,即最多尝试 3 次。SDK 先检查请求方法、幂等键和请求体是否可重放,再判断是否重试:
请求默认可重试情况
GET、HEAD,或携带幂等键的请求连接错误、超时、408、429、5xx
未携带幂等键的其他请求429
409 响应不自动重试
不可重放的请求体不自动重试
SDK 在这些规则内处理服务端重试提示和等待时间;部分接口有更严格的限制。通过客户端 Builder 或单次调用的 RequestOptions.builder().maxRetries(...).build() 修改次数,设置为 0 可关闭重试。 写请求通过 RequestOptions.builder().idempotencyKey(key) 配置幂等键。同一次逻辑操作的所有重试复用同一个键,新的操作使用新的键。

超时与取消

默认连接超时为 5 秒,响应头等待和读写空闲超时为 10 分钟。通过 RequestOptions 区分单次尝试限制和总期限:
import com.qoder.cloudagents.core.RequestOptions;
import java.time.Duration;

client.sessions().retrieve("sess_example", RequestOptions.builder()
    .timeout(Duration.ofSeconds(15))
    .totalTimeout(Duration.ofSeconds(60))
    .maxRetries(1)
    .build());
timeout 调整单次尝试的响应头等待与读写空闲限制;totalTimeout 覆盖凭证获取、所有尝试、重试等待及响应体读取。默认没有整体调用的总期限。 SSE 接受响应头后不启用普通的读取空闲计时;需要限制订阅总时长时设置 totalTimeout。到达期限或取消本地请求,不代表云端已经开始的执行也被取消。

分页

列表响应支持当前页访问和按需跨页迭代:
import com.qoder.cloudagents.forward.Session;
import com.qoder.cloudagents.forward.SessionListParams;
import com.qoder.cloudagents.forward.SessionListResponse;

SessionListResponse page = client.sessions().list(
    SessionListParams.builder().limit(20).build());
System.out.println(page.data().size());

for (Session item : page) {
    System.out.println(item.id());
}
只处理当前页时读取 page.data()。手动翻页先检查 hasNextPage(),再调用 getNextPage();异步翻页使用 getNextPageAsync()。翻页会保留查询条件和请求配置,无需手工拼接游标参数。

高级配置

原始响应信息

使用资源的 withResponse() 同时获取解析结果、HTTP 状态和响应头:
import com.qoder.cloudagents.core.ApiResponse;
import com.qoder.cloudagents.forward.Session;

ApiResponse<Session> response = client.sessions().withResponse().retrieve("sess_example");
System.out.println(response.statusCode());
System.out.println(response.requestId());
System.out.println(response.data().id());
ApiResponse 中的 JSON 数据已经解析。不要把它当成尚未消费的原始 HTTP 响应体。

HTTP 拦截器和 Transport

拦截器可以观察每次 HTTP 尝试,包括重试和 SSE 重连:
import com.qoder.cloudagents.ForwardClient;

try (ForwardClient observed = client.withOptions(options ->
        options.interceptor((request, chain) -> {
            long started = System.nanoTime();
            return chain.proceed(request).whenComplete((response, error) -> {
                long elapsedMs = (System.nanoTime() - started) / 1_000_000;
                System.out.println("http_elapsed_ms=" + elapsedMs);
            });
        }))) {
    observed.models().list();
}
拦截器应及时返回,每次最多调用一次 chain.proceed(...)。完成时间表示收到响应头,不代表 SSE 或响应体已经消费完毕。不要为了记录日志而提前读取流式响应体。 需要自行配置 HTTP 库、代理、TLS 或连接池时,实现公共 HttpTransport 接口并通过 .transport(...) 注入。Transport 负责单次传输及取消,不应额外开启自己的自动重试,以免与 SDK 重试叠加。应用持有显式注入的 Transport,并负责它的生命周期。 单次调用可通过 RequestOptions.builder() 的 header(...)、query(...) 设置额外请求参数,再调用 build() 生成配置。服务端已支持但 SDK 尚未建模的 JSON 字段可通过公开的 JSON 模型接口传入,普通调用优先使用类型化方法。