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 クライアントでテンプレートと ID を作成し、セッションを開始してメッセージを送信します:
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 のデフォルト URLhttps://api.qoder.com/api/v1/forwardhttps://api.qoder.com/api/v1/cloud
CN の URLhttps://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 を業務 API のアクセストークンとして直接使用することもできません。SA 認証が失敗しても PAT にフォールバックしません。 アプリケーションが SAT を管理する場合は .authMode(AuthMode.SA).accessToken(token) を使用します。AuthMode は com.qoder.cloudagents.auth にあります。静的 SAT の更新はアプリケーションが行い、クライアントごとに明確な認証情報ソースを 1 つだけ設定してください。詳細は認証ドキュメントを参照してください。

同期と非同期の呼び出し

同期メソッドは結果を直接返します。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 などでアプリケーションが作成した派生 FutureJava の 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 を受け付けるか、その値をどう処理するかは対応する API 定義に従います。複数の JSON 表現を受け付けるパラメーターには、公開されたオーバーロードやラッパー型を使用し、内部シリアライズ実装に依存しないでください。 レスポンスは不変モデルで、メソッドからフィールドを読み取ります。field(name) と rawJson() で元の JSON にアクセスでき、未知のフィールドも保持されます。汎用 JSON 値は com.qoder.cloudagents.core.JsonValue で表し、アプリケーションは内部の 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() だけでは配信カーソルは進みません。 カーソルがなければ現在のイベントストリーム末尾から開始し、履歴を読むには有効な過去の再開位置が必要です。再開は業務リクエストを再送せず、「厳密に 1 回」の処理も保証しません。同じイベントの複数差分が同一 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 はこれらのルール内でサーバーのリトライ指示と待機時間を処理します。一部 API にはより厳しい制限があります。クライアントの 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

インターセプターはリトライや SSE 再接続を含む各 HTTP 試行を観測できます:
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();
}
インターセプターは速やかに戻り、1 回につき chain.proceed(...) を最大 1 回だけ呼び出してください。完了時刻はヘッダーの受信を表し、SSE や本文の消費完了ではありません。ログ記録のためにストリーミング本文を先読みしないでください。 HTTP ライブラリ、プロキシ、TLS、接続プールを自分で設定するには、公開 HttpTransport インターフェイスを実装し、.transport(...) で注入します。Transport は単一転送とキャンセルを担当し、SDK のリトライと重複しないよう独自の自動リトライを有効にしないでください。明示的に注入した Transport はアプリケーションが所有し、ライフサイクルを管理します。 呼び出し単位の追加パラメーターは RequestOptions.builder() の header(...)、query(...) で設定し、build() で構築します。サーバー対応済みで SDK が未モデル化の JSON フィールドは公開 JSON モデルインターフェイスで渡せます。通常の呼び出しでは型付きメソッドを優先してください。