内部ツールレジストリ

Markdown
プロセス境界をまたぐツールセットの発見と呼び出しのために、クラスタ化されたゲートウェイをデプロイします。

内部ツールレジストリ (Internal Tool Registry) は、プロセス境界をまたいでツールセットの発見と呼び出しを可能にするクラスタ化されたゲートウェイサービスです。ツールセットを別サービスで提供し、消費側エージェントとは独立してスケールさせたい場面向けに設計されています。

カタログは native Agent ツール宣言 も保存し、既存の worker と不変のアプリケーション設定を指定します。利用側 runtime が子 workflow を開始します。provider lease、health ping、Pulse 経由の呼び出しはサービスツールだけに適用されます。

ここで説明する動的 Agent API には Goa-AI v0.84.0 以降が必要です。

概要

レジストリは catalog と gateway の両方として動作します:

  • Catalog: エージェントは利用可能なツールセット、スキーマ、ヘルス状態を発見できます
  • Gateway: ツール呼び出しはレジストリから Pulse streams 経由で provider へルーティングされます

これにより、エージェントとツールセット provider が疎結合になり、スケール、デプロイ、ライフサイクル管理を独立して行えます。

Tool Registry と Prompt Registry

両者は責務の異なる別システムです:

  • Internal Tool Registry (このページ): プロセス境界をまたぐ toolset と tool call の発見/呼び出し。
  • Runtime Prompt Registry (runtime.PromptRegistry): プロセス内での prompt spec 登録とレンダリング。任意で prompt override store (runtime.WithPromptStore) を使えます。

tool registry は prompt template を保存せず、prompt override も解決しません。prompt rendering は runtime/planner 層に残り、prompt_rendered 観測イベントを発行します。

Agent-Registry-Provider Topology

マルチノードクラスタリング

複数の registry node は、設定で同じ Name を使い、同じ Redis インスタンスへ接続することで、同じ論理 registry に参加できます。

同じ名前の node は自動的に次を行います:

  • ツールセット登録を共有: Redis の状態を直接読み取ります
  • ヘルスチェック ping を協調: toolset ごとに取得する有効期限付き Redis lease を使います
  • provider health state を共有: すべての node でヘルス状態を共有

これにより水平スケールと高可用性を実現できます。クライアントは任意の node に接続でき、同じ registry state を参照できます。

Registry Cluster Architecture

クイックスタート

ライブラリとして使う

registry node をプログラムから作成して実行します。registry.New は Redis 上の catalog と call record、Pulse stream、health scheduler を初期化します。Run は gRPC server を起動し、shutdown まで待機します。この例はローカル開発用のアドレスを使います。deployment に合わせて Redis と gRPC の認証情報を設定してください。

package main

import (
    "context"
    "log"

    "github.com/redis/go-redis/v9"
    "goa.design/goa-ai/registry"
)

func main() {
    ctx := context.Background()

    // Connect to Redis
    rdb := redis.NewClient(&redis.Options{
        Addr: "localhost:6379",
    })
    defer rdb.Close()

    // Create the registry
    reg, err := registry.New(ctx, registry.Config{
        Redis: rdb,
        Name:  "my-registry",  // Nodes with same name form a cluster
    })
    if err != nil {
        log.Fatal(err)
    }

    // Run the gRPC server (blocks until shutdown)
    log.Println("starting registry on :9090")
    if err := reg.Run(ctx, ":9090"); err != nil {
        log.Fatal(err)
    }
}

サンプルバイナリ

registry package には、素早くデプロイするための example binary が含まれます。同じ Redis instance を指し、同じ REGISTRY_NAME を持つ node は自動的に cluster を形成します。追加設定なしで toolset registrations を共有し、health checks を協調します。

# Single node (development)
REDIS_URL=localhost:6379 go run ./registry/cmd/registry

# Multi-node cluster (production)
REGISTRY_NAME=prod REGISTRY_ADDR=:9090 REDIS_URL=redis:6379 ./registry
REGISTRY_NAME=prod REGISTRY_ADDR=:9091 REDIS_URL=redis:6379 ./registry
REGISTRY_NAME=prod REGISTRY_ADDR=:9092 REDIS_URL=redis:6379 ./registry

環境変数

変数説明デフォルト
REGISTRY_ADDRgRPC listen address:9090
REGISTRY_NAMERegistry cluster nameregistry
REDIS_URLRedis connection URLlocalhost:6379
REDIS_PASSWORDRedis password(なし)
PING_INTERVALHealth check ping interval10s
MISSED_PING_THRESHOLDunhealthy とみなすまでの missed ping 数3

アーキテクチャ

Registry Internal Architecture

コンポーネント

コンポーネント説明
Servicediscovery と invocation のための gRPC handler
CatalogRedis-backed の tool schema、admission token、provider lease、retirement history
Health Trackerping/pong による provider liveness の監視
Stream Managertool call routing 用 Pulse streams の管理
Call Record Store各 call の request identity、provider assignment、deadline、publication state、正規 terminal result を保持

ツール呼び出しフロー

CallTool が呼ばれると、registry は次を順番に実行します:

  1. Identity と schema validation: registry は payload を検証し、run 内で一意な tool_use_id を 1 つ導出します。完全に同じ retry は、保持済みの同じ record へ接続します。
  2. Provider wait: 未 publish の call は active toolset に healthy provider が現れるまで、既存の execution deadline を上限として待ちます。
  3. Atomic publication: 1 回の Redis operation で、選んだ provider が現在も active かつ non-draining であることを確認してから request を正確に 1 回 append します。health check 後に rollout が provider を変更した場合、未 publish の call は同じ deadline 内で replacement を選び直します。
  4. Immutable execution: publish に成功すると provider assignment が固定されます。外部 effect が始まった可能性があるため、その後 call を移動できません。
  5. Result delivery: CallTool は正確な provider token、result-stream identity、execution deadline、retention deadline を返します。executor は provider が terminal result を返すか execution deadline が call を確定するまで、その stream を読みます。

publish 前に execution deadline が切れた場合、registry は call_not_admitted を記録します。これは executor が別の plan を選べることを証明します。publish 済み call の結果が不明な場合は outcome_unknown を返し、replacement へ移せません。

Provider 統合 (サービス側)

registry routing は半分にすぎません。provider は toolset 所有サービスプロセス内で tool execution loop を実行する必要があります。 handler を呼び出す前に、provider は message の execution deadline とは独立した、worker のライフサイクルに紐づく context と既存の claim timeout を使って ClaimToolCall を呼び出します。registry は call が期限切れか、すでに最終結果を持つか、別の配信が実行権限を持つかを判断します。provider はこれらの結果を受けると、handler を呼び出したり execution loop を停止したりせず、message の受信確認を行います。execute の判断を受けた場合に限り、message の元の execution deadline を延長せずに handler へ適用して呼び出します。

service-owned で method-backed な toolset (BindTo(...) で宣言された tool) の場合、code generation は次の provider adapter を出力します:

  • gen/<service>/toolsets/<toolset>/provider.go

生成された provider は次を行います:

  • 受信した tool payload JSON を生成 payload codec でデコード
  • 生成 transform を使って Goa method payload を構築
  • bound service method を呼び出し
  • 生成 result codec を使って、tool result JSON と宣言済み server-data をエンコード

次の例は module example.com/registry-provider、service catalog、その method-backed toolset search を使い、catalog.search として登録します。アプリケーションの 2 つの import path と toolset 名を、自分のプロジェクトで生成された値に置き換えてください。NewProvider、ToolSchemas、SchemaFingerprint は生成 toolset package の関数です。生成 schema はそのまま使用します。登録 callback は、module root に生成される AGENTS_QUICKSTART.md の Service-Side Tool Providers の例に沿っています (クイックスタート)。

service の実装、pulse.New(pulse.Options{Redis: rdb}) で作成した Pulse client、deployment の認証情報を指定して grpc.NewClient で作成した registry への gRPC connection を渡します。この process と toolset に対して安定し、稼働中の replica 間で一意な providerID と、同じ登録の replica が共有する deployment 指定の必須値 admissionRevision を渡してください。Serve が incarnation ID を作成し、callback へ渡します。bound service method は context cancellation に従う必要があります。serveTools を service のライフサイクル内で実行し、両 client を開いたまま終了を待ってください。shutdown 時には新しい処理の受け付けを止め、実行権を取得済みの call、result、受信確認を Options.ShutdownTimeout 内で完了させます。この処理が成功した場合だけ、別の時間枠 Registration.ReleaseTimeout で正確な lease を解放します。完了処理に失敗した場合は lease の期限切れによって実行権が終了します。戻り値の error が context.Canceled にも一致する場合でも、完了処理や lease 解放の error を保持して報告してください。以下では必要な登録 callback をすべて設定しています:

package providers

import (
	"context"
	"encoding/json"
	"time"

	gencatalog "example.com/registry-provider/gen/catalog"
	gensearch "example.com/registry-provider/gen/catalog/toolsets/search"
	"goa.design/goa-ai/features/stream/pulse/clients/pulse"
	genregistrygrpc "goa.design/goa-ai/registry/gen/grpc/registry/client"
	genregistry "goa.design/goa-ai/registry/gen/registry"
	registrywire "goa.design/goa-ai/runtime/toolregistry"
	"goa.design/goa-ai/runtime/toolregistry/provider"
	"google.golang.org/grpc"
)

// serveTools runs the generated catalog provider until shutdown or a provider error.
// The caller owns the clients, service implementation, and deployment identifiers.
func serveTools(ctx context.Context, pulseClient pulse.Client, conn *grpc.ClientConn,
	serviceImpl gencatalog.Service, providerID, admissionRevision string) error {
	const toolsetID = "catalog.search"
	transport := genregistrygrpc.NewClient(conn, grpc.WaitForReady(true))
	registryClient := genregistry.NewClient(
		transport.Register(),
		transport.RenewProvider(),
		transport.ReleaseProvider(),
		transport.DrainProvider(),
		transport.Unregister(),
		transport.Pong(),
		transport.RegisterAgentToolset(),
		transport.ReplaceAgentToolset(),
		transport.ListToolsets(),
		transport.GetToolset(),
		transport.ResolveToolset(),
		transport.CheckAdmission(),
		transport.Search(),
		transport.CallTool(),
		transport.CallResolvedTool(),
		transport.RetryTool(),
		transport.CompleteToolCall(),
		transport.PublishToolOutputDelta(),
		transport.ReportToolCallOverload(),
		transport.ClaimToolCall(),
	)
	toolSchemas := gensearch.ToolSchemas()
	handler := gensearch.NewProvider(serviceImpl)
	return provider.Serve(ctx, pulseClient, toolsetID, handler,
		provider.Registration{
			AdmissionRevision: admissionRevision,
			Register: func(ctx context.Context, toolset, providerID, incarnationID, admissionRevision string) (provider.RegistrationLease, error) {
				schemaFingerprint, err := gensearch.SchemaFingerprint(toolset)
				if err != nil {
					return provider.RegistrationLease{}, err
				}
				result, err := registryClient.Register(ctx, &genregistry.RegisterPayload{
					Name:                  toolset,
					Tools:                 toolSchemas,
					ProviderID:            providerID,
					ProviderIncarnationID: incarnationID,
					AdmissionRevision:     admissionRevision,
					WireProtocolVersion:   registrywire.WireProtocolVersion,
					SchemaFingerprint:     schemaFingerprint,
				})
				if err != nil {
					return provider.RegistrationLease{}, err
				}
				return provider.RegistrationLease{
					RegistrationToken: result.RegistrationToken,
					Duration:          time.Duration(result.LeaseDurationMs) * time.Millisecond,
				}, nil
			},
			Renew: func(ctx context.Context, toolset, providerID, incarnationID, expectedToken string) (time.Duration, error) {
				result, err := registryClient.RenewProvider(ctx, &genregistry.RenewProviderPayload{
					Name: toolset,
					ProviderID: providerID,
					ProviderIncarnationID: incarnationID,
					ExpectedRegistrationToken: expectedToken,
				})
				if err != nil {
					return 0, err
				}
				return time.Duration(result.LeaseDurationMs) * time.Millisecond, nil
			},
			Drain: func(ctx context.Context, toolset, providerID, incarnationID, expectedToken string, settlementDuration time.Duration) error {
				return registryClient.DrainProvider(ctx, &genregistry.DrainProviderPayload{
					Name:                      toolset,
					ProviderID:                providerID,
					ProviderIncarnationID:     incarnationID,
					ExpectedRegistrationToken: expectedToken,
					SettlementDurationMs:      settlementDuration.Milliseconds(),
				})
			},
			Release: func(ctx context.Context, toolset, providerID, incarnationID, expectedToken string) error {
				return registryClient.ReleaseProvider(ctx, &genregistry.ReleaseProviderPayload{
					Name:                      toolset,
					ProviderID:                providerID,
					ProviderIncarnationID:     incarnationID,
					ExpectedRegistrationToken: expectedToken,
				})
			},
			Complete: func(ctx context.Context, toolset, providerID, incarnationID, providerToken, requestEventID string, result registrywire.ToolResultMessage) error {
				resultJSON, err := json.Marshal(result)
				if err != nil {
					return err
				}
				return registryClient.CompleteToolCall(ctx, &genregistry.CompleteToolCallPayload{
					Toolset:                   toolset,
					ProviderID:                providerID,
					ProviderIncarnationID:     incarnationID,
					RegistrationToken:         result.RegistrationToken,
					ToolUseID:                 result.ToolUseID,
					ResultJSON:                resultJSON,
					RequestEventID:            requestEventID,
					ProviderRegistrationToken: providerToken,
				})
			},
			PublishOutputDelta: func(ctx context.Context, toolset, providerID, incarnationID, providerToken, callToken, toolUseID, requestEventID, stream, delta string) error {
				return registryClient.PublishToolOutputDelta(ctx, &genregistry.PublishToolOutputDeltaPayload{
					Toolset:                   toolset,
					ProviderID:                providerID,
					ProviderIncarnationID:     incarnationID,
					ProviderRegistrationToken: providerToken,
					CallRegistrationToken:     callToken,
					ToolUseID:                 toolUseID,
					RequestEventID:            requestEventID,
					Stream:                    stream,
					Delta:                     delta,
				})
			},
			ReportOverload: func(ctx context.Context, toolset, providerID, incarnationID, providerToken, callToken, toolUseID, requestEventID string) error {
				return registryClient.ReportToolCallOverload(ctx, &genregistry.ProviderToolCallClaimPayload{
					Toolset:                   toolset,
					ProviderID:                providerID,
					ProviderIncarnationID:     incarnationID,
					ProviderRegistrationToken: providerToken,
					CallRegistrationToken:     callToken,
					ToolUseID:                 toolUseID,
					RequestEventID:            requestEventID,
				})
			},
			Claim: func(ctx context.Context, claim provider.ClaimRequest) (provider.ClaimDisposition, error) {
				result, err := registryClient.ClaimToolCall(ctx, &genregistry.ClaimToolCallPayload{
					Toolset:                   claim.Toolset,
					ProviderID:                claim.ProviderID,
					ProviderIncarnationID:     claim.ProviderIncarnationID,
					ProviderRegistrationToken: claim.ProviderRegistrationToken,
					CallRegistrationToken:     claim.CallRegistrationToken,
					ToolUseID:                 claim.ToolUseID,
					RequestEventID:            claim.RequestEventID,
					ClaimOperationID:          claim.OperationID,
				})
				if err != nil {
					return "", err
				}
				return provider.ClaimDisposition(result.Disposition), nil
			},
		},
		provider.Options{
			ProviderID: providerID,
			Pong: func(ctx context.Context, providerID, incarnationID, pingID string) error {
				return registryClient.Pong(ctx, &genregistry.PongPayload{
					PingID:                pingID,
					Toolset:               toolsetID,
					ProviderID:            providerID,
					ProviderIncarnationID: incarnationID,
				})
			},
		},
	)
}

stream ID は決定的です:

  • Tool calls: toolset:<toolsetID>:requests
  • Results: result:<toolUseID>

設定

Registry のオプション

ライブラリの例は最小限の設定を示しています。application が所有する Redis client を Redis に渡し、registry cluster で共有する Name を指定します。同じ名前と Redis database を使う node は、catalog、call record、health check の協調状態を共有します。各 node は Redis からコンパクトな catalog 状態を直接読み取り、完全な定義は別に保存します。

全 API と default 値は registry.Config を参照してください。PingInterval と MissedPingThreshold は health check、ExecutionTimeout は新しく受け付けた実行の時間上限、ResultStreamTTL は result の保持、ProviderLeaseDuration は provider 登録の更新を設定します。ExpectedToolsets は必要な catalog 名を telemetry に記録しますが、登録や call を拒否しません。Logger は call の確定処理の失敗を受け取ります。これらのオプションは registry の構築時に指定します。

Redis ストレージ

Redis の catalog は tool schema、admission identity、provider lease、health timestamp、登録撤回の履歴を保持します。call record と Pulse の request/result stream も Redis を使います。registry replica や再起動した process が同じ登録と call の判断を参照できるよう、永続化した Redis を使ってください。application が Redis client を所有し、registry の停止後に閉じます。

provider の更新とストレージの移行

provider は起動時に一度だけ生成済み schema を送信します。必須の Renew callback は、toolset、provider ID、incarnation ID、期待する登録 token を指定して RenewProvider を呼び、許可された lease 期間だけを返します。更新は lease を新規作成せず、token を変更せず、draining を解除しません。lease が消失、期限切れ、置換、撤回された場合は provider_lease_lost を返し、Serve が停止します。一時的な通信障害は既存 lease の制限時間内だけ再試行します。stream、consumer group、ping の協調状態の修復は引き続き利用できます。

registry は admission、lease、health、discovery のコンパクトな状態を、完全な定義と永久に撤回された token から分離して保存します。登録は関連するデータを原子的に更新します。health check と lease 操作はコンパクトな状態だけを転送します。各 process は fingerprint により定義を再利用し、コンパイル済み validator を保持します。Get と Resolve は呼び出し側ごとに独立した完全な値を返します。

以前の一体型 catalog からの移行には、計画的なメンテナンス停止が必要です。新しい作業を止め、受け付け済み call を完了させ、旧 registry が drain と lease 解放を処理できる間に provider を正常終了します。その後、旧 registry の書き込み process をすべて停止します。backup を取得して catalog を offline で変換し、すべての定義、identity、lease、timestamp、撤回済み token を保持します。call、stream、完了処理の index、保持期限は変更しません。新 registry と更新済み provider を起動し、厳密な起動時検証と正確な CheckAdmission が成功してから再開します。tool fingerprint と provider message version は変更されません。Renew callback を追加し、client を再生成してください。旧版と新版の writer を同時に動かしてはいけません。未変更の backup を復元できるのは、全 writer が停止し、新たな書き込みが再開される前だけです。再開後は新しい version で修復します。通常の起動は旧形式を受け付けません。

ヘルス監視

registry は Pulse stream で health ping を送信し、provider は gRPC の Pong メソッドで応答します。

仕組み

  1. health scheduler は共有 catalog から active toolset を読みます。
  2. toolset の ping lease を持つ node は、call を受け付ける有効な provider が存在する間、ping を送信します。
  3. Pong は、応答が現在の登録、provider process、health check identity に一致する場合だけ catalog を更新します。
  4. routing には、新しい call を受け付ける期限内の provider lease と、十分に新しい受理済み pong が必要です。

health は Redis の時刻を使って catalog から計算します。最後に受理した pong の経過時間は (MissedPingThreshold + 1) × PingInterval 以下である必要があります。未 publish の call は、既存の execution deadline までだけ healthy provider を待ちます。

分散協調

各 registry node はローカル scheduler を実行し、toolset ごとに有効期限付き Redis lease の取得を試みます。lease を取得した node がその health check を実行し、期限切れ後は別の node が取得できます。lease 名は registry cluster ごとに分かれています。

provider lease、現在の health check identity、最後に受理した pong は catalog の同じ record に保存されます。各 node はその record から health を計算するため、古い provider の遅れた応答によって現在の登録が healthy になることはありません。

クライアント統合

provider と invocation の API には、生成された registry service client を使います。catalog discovery では runtime/registry.NewClient が同じ生成 client を包み、ListToolsets、GetToolset、Search と、runtime/registry.Manager が使う resource type を提供します。

以下の例は catalog の一覧と、名前で指定した toolset の完全な schema を取得します。grpc.NewClient と deployment の認証情報で作成した connection を渡してください。呼び出し側が connection の所有権を保持します。上の provider の例と同じく、生成 client のすべての endpoint を接続しています。

package discovery

import (
	"context"

	genregistrygrpc "goa.design/goa-ai/registry/gen/grpc/registry/client"
	genregistry "goa.design/goa-ai/registry/gen/registry"
	runtimeregistry "goa.design/goa-ai/runtime/registry"
	"google.golang.org/grpc"
)

// discoverTools lists the catalog and retrieves the schema of the named toolset.
// The caller creates the gRPC connection and keeps it open during discovery.
func discoverTools(ctx context.Context, conn *grpc.ClientConn, toolsetName string) (
	[]*runtimeregistry.ToolsetInfo, *runtimeregistry.ToolsetSchema, error,
) {
	transport := genregistrygrpc.NewClient(conn, grpc.WaitForReady(true))
	generated := genregistry.NewClient(
		transport.Register(),
		transport.RenewProvider(),
		transport.ReleaseProvider(),
		transport.DrainProvider(),
		transport.Unregister(),
		transport.Pong(),
		transport.RegisterAgentToolset(),
		transport.ReplaceAgentToolset(),
		transport.ListToolsets(),
		transport.GetToolset(),
		transport.ResolveToolset(),
		transport.CheckAdmission(),
		transport.Search(),
		transport.CallTool(),
		transport.CallResolvedTool(),
		transport.RetryTool(),
		transport.CompleteToolCall(),
		transport.PublishToolOutputDelta(),
		transport.ReportToolCallOverload(),
		transport.ClaimToolCall(),
	)
	client := runtimeregistry.NewClient(generated)
	toolsets, err := client.ListToolsets(ctx)
	if err != nil {
		return nil, nil, err
	}
	schema, err := client.GetToolset(ctx, toolsetName)
	if err != nil {
		return nil, nil, err
	}
	return toolsets, schema, nil
}

gRPC API

registry は次の gRPC method を公開します:

Provider Operations

Method説明
Register起動時に生成済み定義を送信して provider を登録します。別の contract は古い lease の終了を待ちます。
RenewProvider期限内の正確な lease だけを、定義を送らずに延長します。draining と長い完了期限を維持し、権限を失った場合は provider_lease_lost を返します。
DrainProvider1 つの provider lease を新規 call に使えなくし、すでに所有する call を完了する権限は保ちます。
ReleaseProviderprocess が受理済み work を確定した後、正確な provider lease を削除します。
Unregister正確な現在の登録を discovery から除外します。サービスの token は永久に廃止されますが、native 宣言は ReplaceAgentToolset で再有効化できます。受理済みの native な子呼び出しは選択した宣言を保持します。
Pong正確な current lease と health-check epoch に対して provider health を記録します。
ClaimToolCallpublish 済み request の実行権限を、正確に 1 つの provider lease に付与します。
CompleteToolCallclaim 済み call の正規 terminal result を commit し、result stream へ publish します。
PublishToolOutputDeltaclaim 済み call の bounded な best-effort progress fragment を publish します。
ReportToolCallOverloadprovider が overloaded call を実行する前に、bounded retry control を記録します。

native Agent の操作

Method説明
RegisterAgentToolsetprovider lease なしで native 宣言を登録します。有効な登録と同じ宣言を繰り返しても成功します。
ReplaceAgentToolset現在の token で native 宣言を置換または再有効化します。古い token には admission_conflict を返します。

Discovery Operations

Method説明
ListToolsets登録済み toolset を一覧します (任意で tag filtering)。metadata のみを返し、full schema は返しません。
GetToolset指定 toolset の full schema を取得します。すべての tool input/output schema を含みます。
ResolveToolset有効な定義と正確な登録 token を一緒に読み取ります。provider の健全性は別に確認します。
CheckAdmission正確なサービス登録について、期限内で draining ではない provider lease と最近の health 応答を確認します。
Searchname、description、tags に対する keyword match で toolset を検索します。

Invocation Operations

Method説明
CallToolrun 内で一意な call を検証して publish します。call は既存 deadline 内で provider health を待ち、publish 前だけ replacement に追随し、その後は正確で不変な execution reference を返します。
CallResolvedTool保持した登録 token のサービス呼び出しだけを発行します。発行前に置換されていれば call_not_admitted を記録します。
RetryToolprovider overload が記録された後、元の admission をそのまま再 publish します。replacement provider へ実行を移しません。

ベストプラクティス

デプロイ

  • cluster 内のすべての node で同じ Name を使う: catalog と call の状態を共有し、health check を協調します
  • 同じ Redis instance を指す: state coordination のため
  • load balancer の背後にデプロイする: すべての node が同一 state を返します
  • catalog、call record、Pulse stream には永続化した Redis を使う: registry replica と再起動した process が同じ判断を参照できるようにします

ヘルス監視

  • PingInterval と MissedPingThreshold を設定する: health check の頻度と許容する pong の経過時間を指定します。default 値は registry.Config を参照してください。
  • catalog と health の telemetry を確認する: toolset が存在しない場合と、provider が現在 call を受け付けられない場合を区別します。
  • execution deadline を維持する: 未 publish の call は既存の deadline までだけ provider の回復を待ちます。

スケーリング

  • node を追加する: gRPC connection を増やしても、各 node は任意の request を処理できます
  • node は health check を協調する: toolset ごとの有効期限付き Redis lease を使います
  • sticky session は不要: result stream は Redis により cross-node delivery されるため、ある node で開始した tool call が別 node で完了できます

次のステップ

現在のソース解決、生成済み契約、プロバイダー動作、移行についてはツール検索と動的カタログを参照してください。