Skip to main content
SDK

Go SDK

Qoder Cloud Agents Go SDK のインストールと使用方法。認証、リクエスト型、ストリーミングレスポンス、ページネーション、リトライ、タイムアウトを説明します。

Qoder Cloud Agents Go SDK は型付きの Forward と Managed クライアントを提供し、context によるリクエスト制御、ストリーミングレスポンスの逐次読み取り、認証・リトライ・HTTP 転送の設定に対応します。

インストール

Go 1.23 以降が必要です。既存の Go Modules プロジェクトにインストールします:
go get github.com/QoderAI/qoder-cloud-agents-sdk-go
go.mod と go.sum をバージョン管理に含め、アプリケーションが使用する SDK バージョンを固定してください。

基本的な使い方

個人アクセストークン(PAT)を環境変数に保存します:
export QODER_PAT="替换为你的 PAT"
Forward クライアントでテンプレートと ID を作成し、セッションを開始してメッセージを送信します:
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/QoderAI/qoder-cloud-agents-sdk-go/forward"
)

func main() {
	ctx := context.Background()
	client := forward.NewClient()

	env, err := client.Environments.New(ctx, forward.EnvironmentNewParams{
		Name:   "demo",
		Config: map[string]any{"type": "cloud"},
	})
	if err != nil {
		log.Fatal(err)
	}

	identity, err := client.Identities.New(ctx, forward.IdentityNewParams{
		ExternalID: "demo-user",
		Name:       forward.String("Demo User"),
	})
	if err != nil {
		log.Fatal(err)
	}

	template, err := client.Templates.New(ctx, forward.TemplateNewParams{
		Name:          "assistant",
		Model:         forward.ModelConfigUnionParam{OfString: forward.String("auto")},
		EnvironmentID: env.ID,
	})
	if err != nil {
		log.Fatal(err)
	}

	session, err := client.Sessions.New(ctx, forward.SessionNewParams{
		IdentityID: identity.ID,
		TemplateID: template.ID,
	})
	if err != nil {
		log.Fatal(err)
	}

	message := forward.SessionEventParam{
		Type: "user.message",
		Content: forward.EventContentUnionParam{
			OfBlocks: []forward.ContentBlockParam{
				{Type: "text", Text: forward.String("你好")},
			},
		},
	}
	_, err = client.Sessions.Events.Send(ctx, session.ID, forward.SessionEventSendParams{
		Events: []forward.SessionEventParam{message},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(session.ID)
}
Managed クライアントで Agent と環境を作成し、セッションを開始してメッセージを送信します。次のコードを main.go に保存し、SDK をインストールしたプロジェクトで go run main.go を実行します:
package main

import (
	"context"
	"fmt"
	"log"

	"github.com/QoderAI/qoder-cloud-agents-sdk-go/managed"
)

func main() {
	ctx := context.Background()
	client := managed.NewClient()

	agent, err := client.Agents.New(ctx, managed.AgentNewParams{
		Name:  "assistant",
		Model: managed.ManagedAgentsModelConfigParams{ID: "auto"},
	})
	if err != nil {
		log.Fatal(err)
	}

	env, err := client.Environments.New(ctx, managed.EnvironmentNewParams{
		Name: "demo",
		Config: managed.EnvironmentNewParamsConfigUnion{
			OfCloud: &managed.CloudConfigParams{Type: "cloud"},
		},
	})
	if err != nil {
		log.Fatal(err)
	}

	session, err := client.Sessions.New(ctx, managed.SessionNewParams{
		Agent:         managed.SessionNewParamsAgentUnion{OfString: managed.String(agent.ID)},
		EnvironmentID: env.ID,
	})
	if err != nil {
		log.Fatal(err)
	}

	message := managed.ManagedAgentsEventParamsUnion{
		OfUserMessage: &managed.ManagedAgentsUserMessageEventParams{
			Type: "user.message",
			Content: []managed.ManagedAgentsUserMessageEventParamsContentUnion{
				{OfText: &managed.ManagedAgentsTextBlockParam{Type: "text", Text: "你好"}},
			},
		},
	}
	_, err = client.Sessions.Events.Send(ctx, session.ID, managed.SessionEventSendParams{
		Events: []managed.ManagedAgentsEventParamsUnion{message},
	})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(session.ID)
}
以降の Go コード断片は package と import を省略しています。標準ライブラリ以外の convention、option、param は、それぞれモジュール内の convention、convention/option、convention/param パッケージです。例の client は設定済みの Forward クライアントです。リソース ID は現在の認証情報でアクセスできる実際の ID に置き換えてください。 詳細は Managed クイックスタートと Forward API リファレンスを参照してください。

クライアント設定

option パッケージでコンストラクターまたは個々の呼び出しに設定を渡します。同じ設定は通常、呼び出し単位の値がクライアントのデフォルトを上書きします。追加ヘッダーやミドルウェアなど累積可能なオプションは各ルールに従って結合されます。
設定ForwardManaged
コンストラクターforward.NewClientmanaged.NewClient
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
明示的に渡した設定は環境変数より優先されます。デフォルトの PAT モードは QODER_ACCESS_TOKEN、次に QODER_PAT を読み取ります。クライアントは .env ファイルを自動読み込みしません。 Base URL には完全な API プレフィックスを含め、アカウント、認証情報、リソースのリージョンと一致させてください。複数 goroutine で再利用する前に設定を完了し、共有後は Options を並行変更しないでください。

認証

PAT

option.WithPAT(token) で個人アクセストークンを明示的に設定します。トークンはアプリケーションのサーバー環境に保存してください。

Service Account

QODER_SA_KEY の設定後、認証情報コンポーネントを明示的に作成します。SAT(サービスアカウントトークン)を交換・キャッシュし、後続リクエストで必要になった際に更新します:
func serviceAccountClients() (forward.Client, managed.Client, error) {
	credential, err := convention.ServiceAccountCredentialFromEnv(
		convention.ServiceAccountCredentialConfig{
			Region: "global",
			Scopes: []string{"qca.access", "forward.access"},
		},
	)
	if err != nil {
		return forward.Client{}, managed.Client{}, err
	}
	return forward.NewClient(
		option.WithCredential(credential),
		option.WithBaseURL("https://api.qoder.com/api/v1/forward"),
	), managed.NewClient(
		option.WithCredential(credential),
		option.WithBaseURL("https://api.qoder.com/api/v1/cloud"),
	), nil
}
2 つのクライアントは同じ認証情報インスタンスとトークンキャッシュを共有します。中国サイトでは Region: "cn" と CN Base URL の両方を使用します。Managed のみを利用する場合は qca.access scope を要求できます。権限と Managed ワークスペースの紐付けはサーバー側で設定します。 環境に QODER_SA_KEY が存在するだけでは、通常のクライアントは認証情報交換を自動的に有効にしません。SA Key は SAT の取得専用で、業務 API のアクセストークンとして直接使用できません。SA 認証が失敗しても PAT にフォールバックしません。 アプリケーションが SAT を管理する場合は option.WithAuthMode("sa") と option.WithAuthToken(token) を使用します。静的 SAT の更新はアプリケーションが行います。クライアントごとに明確な認証情報ソースを 1 つ選び、ネイティブ SA 認証情報と静的トークンの併用を避けてください。詳細は認証ドキュメントを参照してください。

リクエストとレスポンスの型

省略可能フィールド

必須フィールドは通常 Go の値を直接使用し、省略可能なスカラーは param.Opt[T] を使用します。未設定とゼロ値の明示的送信は意味が異なります:
記述JSON の動作
未設定の param.Opt[T]フィールドを送信しない
forward.String("")空文字列を送信
forward.Bool(false)false を送信
forward.Int(0)0 を送信
param.Null[string]()null を送信
omitzero 付きの nil map または sliceフィールドを送信しない
nil ではない空の map または slice{} または [] を送信
次の関数はパラメーターのエンコードのみを示し、リクエストは送信しません:
func encodeParameters() ([]byte, error) {
	params := forward.SessionUpdateParams{
		Title: param.Null[string](),
	}
	return json.Marshal(params)
}
フィールドが null を受け付けるか、サーバーがその値をどう処理するかは対応する API 定義に従います。Managed も managed.String、Bool、Int など同様のヘルパーを提供します。

ユニオン型とレスポンスフィールド

リクエストのユニオン型は Of... フィールドで表現を 1 つ選び、1 つの分岐だけを設定します。レスポンスのユニオン型は、その型が提供する As... または AsAny() で具体的な分岐を読み取ります。個々のメソッドは API Reference を参照してください。 レスポンス構造の JSON メタデータで、欠落フィールド、明示的な null、通常の値を区別できます。Go のゼロ値だけでフィールドの存在を判断しないでください。元の JSON にはレスポンスモデルの RawJSON() を使用します。未モデル化フィールドはレスポンスの追加フィールド情報に保持されます。

ストリーミングレスポンス

StreamEvents で単一の SSE 接続を確立し、順にイベントを読み、終了後に Err() を確認します:
func readEvents(ctx context.Context, client forward.Client, sessionID string) error {
	stream := client.Sessions.Events.StreamEvents(
		ctx, sessionID, forward.SessionEventStreamParams{},
	)
	defer stream.Close()

	for stream.Next() {
		event := stream.Current()
		fmt.Println(event.Type)
	}
	return stream.Err()
}
単一ストリームは EOF で終了します。回復可能な切断後も購読するには、明示的に NewResumableStream を使用します:
func readResumableEvents(
	ctx context.Context,
	client forward.Client,
	sessionID string,
	cursor string,
) error {
	params := forward.SessionEventStreamParams{}
	if cursor != "" {
		params.LastEventID = forward.String(cursor)
	}
	stream := client.Sessions.Events.NewResumableStream(ctx, sessionID, params)
	defer stream.Close()

	for stream.Next() {
		event := stream.Current()
		fmt.Println(event.Type)
		// 应用成功处理事件后,再持久化该恢复位置。
		fmt.Println("cursor:", stream.LastEventID())
	}
	return stream.Err()
}
再開可能ストリームは最後に配信されたイベントの SSE カーソルで再接続し、業務リクエストを再送しません。継続実行する場合があるため、context で購読期限を設定するか明示的にキャンセルしてください。HTTP リトライ回数は個々の接続リクエストを制御し、購読全体の再接続回数上限ではありません。 カーソルがなければ現在のイベントストリーム末尾から開始します。履歴を読むには有効な過去の再開位置を先に取得してください。自動再接続は「厳密に 1 回」の処理を保証しません。同じイベントの複数差分が同一 ID を共有する場合があるため、ID だけですべての重複フレームを破棄しないでください。イベントの意味とタスク完了条件は対応する API 定義を参照してください。 ストリームを閉じる、またはローカルの context をキャンセルしても、停止するのは現在の購読のみです。クラウドで開始済みの実行は自動停止しません。

エラー処理と Request ID

HTTP エラーは *convention.Error を使用します。ネットワーク、エンコード、デコード、コンテキストのエラーは個別に処理します:
func retrieveWithErrors(ctx context.Context, client forward.Client, sessionID string) error {
	_, err := client.Sessions.Get(ctx, sessionID)
	if err == nil {
		return nil
	}
	var apiErr *convention.Error
	switch {
	case errors.Is(err, context.Canceled):
		fmt.Println("请求已取消")
	case errors.Is(err, context.DeadlineExceeded):
		fmt.Println("请求超过期限")
	case errors.As(err, &apiErr):
		fmt.Printf("status=%d code=%s request_id=%s\n",
			apiErr.StatusCode, apiErr.Code, apiErr.RequestID)
	default:
		fmt.Printf("请求失败:%v\n", err)
	}
	return err
}
エラーは Message、Type()、Request、Response、RawJSON() も提供します。問題調査用にステータスコード、エラーコード、Request ID を保持し、アクセストークンを含むリクエスト全体をログに出力しないでください。

リトライと冪等性

デフォルトのリトライは最大 2 回、合計で最大 3 回の試行です。本文を再読み取りでき、呼び出し元の context が有効である必要があります。
リクエストデフォルトでリトライする条件
GET、HEAD、または冪等キー付きリクエスト接続エラー、408、429、5xx
冪等キーのないその他のリクエスト429
409 レスポンス自動リトライしない
SDK はこれらの安全ルール内でサーバーのリトライ指示と待機時間も処理します。一部 API にはより厳しい制限があります。option.WithMaxRetries(0) で無効化するか、呼び出し単位で回数を上書きします。 冪等キーはアプリケーションが生成・保存し、リクエストパラメーターの IdempotencyKey または option.WithHeader("Idempotency-Key", key) で渡します。同じ論理操作の全リトライで同じキーを再利用し、新しい操作には新しいキーを使用します。

タイムアウトとキャンセル

デフォルト Transport のレスポンスヘッダー待機タイムアウトは 10 分です。本文読み取りは対象外で、呼び出し全体の総期限でもありません。 context で呼び出し全体を、WithRequestTimeout で各試行を制御します:
func retrieveWithTimeout(parent context.Context, client forward.Client, sessionID string) error {
	ctx, cancel := context.WithTimeout(parent, time.Minute)
	defer cancel()
	_, err := client.Sessions.Get(ctx, sessionID,
		option.WithRequestTimeout(15*time.Second),
		option.WithMaxRetries(1),
	)
	return err
}
例では全体で最大 1 分、各試行で最大 15 秒待機し、先に到達した期限が適用されます。WithRequestTimeout は本文読み取りも対象とするため、長時間 SSE 購読では購読に適した期限を設定してください。

ページネーション

自動ページネーションは必要に応じて後続ページを読み取ります:
func listAll(ctx context.Context, client forward.Client) error {
	pager := client.Sessions.ListAutoPaging(ctx, forward.SessionListParams{
		Limit: forward.Int(20),
	})
	for pager.Next() {
		fmt.Println(pager.Current().ID)
	}
	return pager.Err()
}
ページ単位の処理には List と GetNextPage() を使用します。次のページがない場合は nil を返します:
func listPages(ctx context.Context, client forward.Client) error {
	page, err := client.Sessions.List(ctx, forward.SessionListParams{Limit: forward.Int(20)})
	for err == nil && page != nil {
		fmt.Println(len(page.Data))
		page, err = page.GetNextPage()
	}
	return err
}
ページネーターはクエリ条件とリクエスト設定を保持します。API によりカーソル形式が異なる場合があるため、返されたページが提供するページ送りメソッドを優先してください。

詳細設定

元のレスポンス

WithResponseInto でステータスコードとレスポンスヘッダーを読み取ります:
func inspectResponse(ctx context.Context, client forward.Client, sessionID string) error {
	var response *http.Response
	_, err := client.Sessions.Get(ctx, sessionID, option.WithResponseInto(&response))
	if response != nil {
		fmt.Println(response.StatusCode, response.Header.Get("X-Request-ID"))
	}
	return err
}
本文はすでに SDK が読み取った可能性があります。レスポンス JSON には解析結果の RawJSON() を、デコード先を指定する場合は WithResponseBodyInto を使用します。

HTTP クライアントとリクエスト拡張

WithHTTPClient で接続プール、プロキシ、カスタム Transport を設定できます。カスタムクライアントを渡した場合、そのタイムアウトと接続プール設定はアプリケーションが管理します。
func customHTTPClient(token string) forward.Client {
	transport := http.DefaultTransport.(*http.Transport).Clone()
	transport.ResponseHeaderTimeout = time.Minute
	transport.MaxIdleConnsPerHost = 20
	return forward.NewClient(
		option.WithPAT(token),
		option.WithHTTPClient(&http.Client{Transport: transport}),
	)
}
各リクエスト試行の観測には WithMiddleware を使用します。カスタム Header、Query、JSON フィールドには WithHeader、WithQuery、WithJSONSet を使用します。パラメーター構造の SetExtraFields はサーバーが対応済みで SDK が未モデル化のフィールドを渡すためのものです。認証やルーティング設定を意図せず置き換えないよう、明示的に変更が必要なフィールドだけを上書きしてください。