Gemini Enterprise Agent Platform の「Managed Agents」に Cloud Run を運用させてみる
はじめに
Google Cloud の Managed Agents API に、Cloud Run のサービスを運用させました。Managed Agent API は Gemini Enterprise Agent Platform の Preview 機能です。今回、ログとメトリクスを読ませ、チューニング案を推測させ、実際に再デプロイまでさせています。
モチベーションは、Antigravity による運用自動化で、どこまでいけるのかを知りたかったというのがあります。こちら検証日は 2026 年 9 月 14 日で、閲覧されたタイミングでは異なる機能になる可能性があります。
先に結論
| 観点 | 結果 |
|---|---|
| 自然言語だけの運用サイクル(観測 → 判断 → 再デプロイ → 比較) | 完走。p95 が 307.9ms から 9.5ms へ |
| MCP 経由での運用 | 不可。mcp_server を宣言すると対話が内部エラーで落ちる |
| Egress の絞り込み | 不可。allowlist は * のみ受理し、API が明示的に拒否する |
対話時の environment 指定 |
base_environment を上書きし、ネットワークが黙って無効になる |
| MCP の資格情報 |
GET agent でも一覧 API でも平文で返る |
| 実行結果の監査 | 保存レコードには残る。SSE ストリームには流れない |
| Google 提供スキルのマウント | 可能。ただし場所を伝えないと読まれない |
| コスト | 6 ターンで約 134 万トークン。暴走した 1 ターンだけで約 70 万 |
- 実用的な運用まで行けそう。要件を渡せば、観測・原因推定・変更・検証まで自分で回す。
- 安全な口と思われる MCP が塞がっているため、Google Cloud を触らせるには egress 全開放と資格情報の持ち込みしか残らない。
- 事故はセキュリティよりコストの形で出やすい。暴走した対話を止める API が無い。
1. Managed Agents API とは
単一の API で、自律的にコード実行・ファイル操作・外部通信・外部ツール接続をするエージェントを立ち上げる仕組みです。実行環境は Google 管理の Linux サンドボックス(Antigravity ハーネス)です。
-
2 つの API:
Agents API(設定)とInteractions API(実行) -
サンドボックス: bash とコード実行、
gcloud/git/python/nodeのプリインストール、7 日 TTL、ネットワークは既定で無効 -
組込ツール:
code_execution/filesystem/google_search/url_context - 外部連携: MCP(Model Context Protocol)と Skill Registry
参照: Managed Agents API on Agent Platform sandbox environment
Agent Runtime(ADK などのコードファースト)との違いは、認証の作り込みにあります。Agent Identity と auth manager の対応サービスは、Agent Runtime・Gemini Enterprise・Cloud Run の 3 つです。Managed Agents は列挙に入っていません。
2. Cloud Run のサービスを運用させる
用意したもの
検証対象は、外部サービスの呼び出しを 300ms の待ち時間で模した小さな Web サービスです。環境変数 CACHE_ENABLED を true にすると結果をメモ化し、待ち時間を省きます。
def fetch_upstream(seed):
"""外部サービスの呼び出しを模す。待ち時間が主で、CPU は少しだけ使う。"""
time.sleep(UPSTREAM_MS / 1000.0)
digest = hashlib.sha256(str(seed).encode()).digest()
for _ in range(WORK_UNITS):
digest = hashlib.sha256(digest).digest()
return digest.hex()[:16]
def handle(seed):
if CACHE_ENABLED:
with _lock:
if seed in _cache:
return _cache[seed], True
value = fetch_upstream(seed)
if CACHE_ENABLED:
with _lock:
_cache[seed] = value
return value, False
Cloud Run 側は既定値から外した設定でデプロイしました。並行数 4、CPU 1、メモリ 512Mi、最大インスタンス 3 です。負荷は 20 並列・600 リクエスト・8 種類のシードで、変更前と変更後に同じ条件で流します。
エージェントの定義はこうです。ツールには MCP を入れていません。 宣言すると対話そのものが失敗するためです。切り分けは 4 章に書きます。
{
"id": "cloudrun-ops-gcloud",
"base_agent": "antigravity-preview-05-2026",
"description": "Cloud Run ops via gcloud in sandbox",
"system_instruction": "あなたは Cloud Run のサービスを運用する SRE です。渡された資格情報で gcloud と curl を使います。観測した生の出力を要約せずに示してから結論を述べます。指示されたコマンド以外は実行せず、サンドボックス自体の調査もしません。",
"tools": [{ "type": "code_execution" }, { "type": "filesystem" }],
"base_environment": {
"type": "remote",
"network": { "allowlist": [{ "domain": "*" }] }
}
}
資格情報は、最小権限のサービスアカウントから発行した短命トークンです。付与したロールは run.admin / monitoring.viewer / logging.viewer / artifactregistry.reader の 4 つだけです。これをプロンプトに書いて渡し、サンドボックス内で CLOUDSDK_AUTH_ACCESS_TOKEN として使わせます。
これらを使って検証します。
Turn 1: 観測
投げたプロンプトです。資格情報はプロンプトの冒頭に置き、以降のターンでは環境変数として引き継がせます。
次のアクセストークンで Google Cloud を操作してください(最小権限の SA・1 時間で失効)。
export CLOUDSDK_AUTH_ACCESS_TOKEN=<トークン>
export CLOUDSDK_CORE_PROJECT=<プロジェクト ID>
Cloud Run のサービス ops-target(リージョン asia-northeast1)の状態を観測してください。
次の 4 つだけを実行し、生の出力をそのまま貼ってください。
1) gcloud run services describe ops-target --region=asia-northeast1 --format=yaml(spec.template)
2) 直近 30 分のリクエスト数とレイテンシ分布:
curl -s -H "Authorization: Bearer $CLOUDSDK_AUTH_ACCESS_TOKEN" \
'https://monitoring.googleapis.com/v3/projects/<プロジェクト ID>/timeSeries?filter=metric.type%3D%22run.googleapis.com%2Frequest_latencies%22%20AND%20resource.label.service_name%3D%22ops-target%22&interval.endTime='$(date -u +%Y-%m-%dT%H:%M:%SZ)'&interval.startTime='$(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30M +%Y-%m-%dT%H:%M:%SZ)
3) 同じ期間の run.googleapis.com/container/instance_count
4) gcloud logging read 'resource.labels.service_name="ops-target" severity>=WARNING' --limit=10 --freshness=1h
結論は書かず、数値だけを表にしてください。
返ってきたのは生の数値です。
"points": [
{
"interval": { "startTime": "2026-09-14T15:00:00.001Z", "endTime": "2026-09-14T15:01:00Z" },
"value": { "distributionValue": {
"count": "269",
"mean": 305.6301261391298,
"sumOfSquaredDeviation": 56260.497680588975
} }
}
]
Turn 2: 判断
原因の推定とチューニング案だけを出させます。UPSTREAM_MS を「外部要因なので変更してはいけない」と縛ったのは、外部サービスの遅さを直接消す逃げ道を塞ぐためです。
前のターンの数値をもとに、レイテンシが悪い原因を推定し、チューニング案を出してください。
このアプリは次の環境変数を読みます。
- CACHE_ENABLED (true/false, 既定 false): 外部サービスの呼び出し結果をメモ化する
- WORK_UNITS (整数, 既定 2000): 1 リクエストあたりの計算量
- UPSTREAM_MS (整数, 既定 300): 外部サービス依存の応答時間。外部要因なので変更してはいけません
Cloud Run 側の設定(CPU・メモリ・並行数)も変更できます。
変更前と変更後を並べた表を出し、p95 がどう動くと予想するかを 1 行で書いてください。
このターンではコマンドを実行しないでください。
返ってきた診断がこちらです。
平均レイテンシが約 298〜306 ms であるのに対し、外部依存の応答時間 `UPSTREAM_MS` が 300 ms に設定されています。現在 `CACHE_ENABLED` が `false` であるため、すべてのリクエストがこの 300 ms の上流待ち時間に直接影響を受けています。
キャッシュを有効化して I/O 待ちを解消した場合、1 リクエストあたり `WORK_UNITS=2000` の計算量が発生するため、1 CPU(最大並行数 4)の設定では CPU の枯渇や割り当て待ちが新たなボトルネック(テールレイテンシの悪化)になる可能性があります。
注目すべき提案は、CACHE_ENABLED を true、CPU を 2、メモリを 1Gi、並行数を 80 にするというものでした。待ち時間を消すと CPU 律速に変わるという二段目まで読んでいます。禁止した UPSTREAM_MS には手を付けていません。
Turn 3: 適用
手段だけを指定し、値は前のターンの案に任せます。
前のターンの案を適用してください。gcloud run services update を使い、
変更は環境変数と Cloud Run の設定だけにとどめます。
完了したら gcloud run services describe で新しいリビジョン名と設定を確認し、
変更前後を報告してください。
1 回目は失敗しました。
ERROR: (gcloud.run.services.update) PERMISSION_DENIED:
Permission 'artifactregistry.repositories.downloadArtifacts' denied on resource
'//artifactregistry.googleapis.com/.../repositories/cloud-run-source-deploy'
注目したのはこの後の挙動です。エージェントは gcloud run services replace でも試し、どちらも失敗すると、describe で変更が入っていないことを確認してから報告しました。
デプロイ失敗(PERMISSION_DENIED)により、新しいリビジョンは作成されず、設定値は変更されていません。
Cloud Run が新しいリビジョンをデプロイする際に要求される Artifact Registry のダウンロード権限(`artifactregistry.repositories.downloadArtifacts`)が不足しているため、チューニング案の変更を適用できませんでした。
成功を装わず、不足している権限まで特定しています。roles/run.admin だけでは Cloud Run を更新できない、という実務的な発見でもあります。
権限を足し、同じ会話の続きとして投げ直しました。
Artifact Registry の読み取り権限を追加しました。前のターンで失敗した変更をもう一度適用してください。
gcloud run services update を 1 回だけ実行し、成功したら gcloud run services describe で
新しいリビジョン名と env・cpu・memory・concurrency を確認して報告してください。
失敗したらエラーをそのまま貼って止めてください。
新しいリビジョンが出ました。
| 設定項目 | 変更前 | 変更後 |
|---|---|---|
| リビジョン名 | ops-target-00001-hbj |
ops-target-00002-5zd |
CACHE_ENABLED |
false |
true |
| CPU | 1 | 2 |
| メモリ | 512Mi | 1Gi |
| 並行数 | 4 | 80 |
Turn 4: 比較
同じ条件で負荷をかけ直し、リビジョン別の p95 を取らせました。
2026-09-14T15:11:33Z ops-target-00001-hbj 308.0
2026-09-14T15:10:33Z ops-target-00001-hbj 307.9
2026-09-14T15:26:33Z ops-target-00002-5zd 9.6
2026-09-14T15:25:33Z ops-target-00002-5zd 9.5
手元から測った値も並べます。こちらは日本のクライアントから asia-northeast1 を叩いた往復時間を含みます。
| 計測 | 変更前 | 変更後 |
|---|---|---|
| p50 | 520.6ms | 54.2ms |
| p95 | 786.1ms | 148.2ms |
| スループット | 35.1 rps | 292.7 rps |
| 600 リクエストの所要 | 17.1 秒 | 2.1 秒 |
ここまでで分かったこと
観測から変更・検証まで、指示は自然言語だけで通りました。判断の質も実用に足ります。外部サービスの待ち時間が支配的だと見抜き、それを消した後に CPU 律速へ移ることまで予測し、実測でも裏付けが取れました。
ただし、2 つの意外な点が有りました。どちらも詳細は後に書きます。
1 つは、MCP でつなぐつもりが動かなかったことです。そのため egress を全開放し、サンドボックスの gcloud に資格情報を渡す形になりました。安全な口を使えないので、広い口を開けています。
もう 1 つは、対話時に environment を指定するとネットワークが黙って無効になることです。最初はこれに気づかず、エージェントが原因を探して 1 ターンで 70 万トークンを使いました。
3. Google 提供のスキルを入れる
Google は Cloud 向けのスキルを github.com/google/skills で公開しています。これの中には Google Cloud の Well-Architected Framework の SKILL が入っています。中身は SKILL.md(YAML フロントマターに name と description)と参照ドキュメントで、Skill Registry に登録すればサンドボックスへマウントできます。
cloud-run-basics と Monitoring 関連の 2 本を登録し、運用エージェントにマウントして比べました。
エージェントとスキルはロケーションが揃わない
Managed Agents は global にしか作れません。 一方 Skill Registry の対応リージョンは 3 つで、global は含まれません。us-central1 / europe-west4 / us-east5 です。どちらもドキュメントに書かれています。
問題は、global を指定したときの落ち方です。リクエストは 200 で受理され、スキルのリソースも作られます。そのうえで非同期の取り込みが失敗し、state: FAILED になります。操作の結果は INTERNAL としか返らず、リージョンが対象外であることは読み取れません。Google が管理するビルトインの gcp-skill-registry まで global では FAILED のままでした。
cloud-monitoring-list-time-series-request state=FAILED
cloud-monitoring-metric-selection state=FAILED
cloud-run-basics state=FAILED
gcp-skill-registry state=FAILED ← Google 管理のビルトイン
us-central1 に入れ直すと、同じ payload が ACTIVE になります。
cloud-monitoring-list-time-series-request state=ACTIVE source=USER
cloud-monitoring-metric-selection state=ACTIVE source=USER
ops-cloud-run-basics state=ACTIVE source=USER
gcp-skill-registry state=ACTIVE source=SYSTEM
プロジェクトで最初の登録は失敗する
us-central1 でも、最初の 1 本は INTERNAL で失敗しました。このとき、ビルトインの gcp-skill-registry も FAILED でした。しばらくして同じ内容を削除・再登録すると ACTIVE になり、以降の登録も通ります。
マウント先は target のとおりにならない
global のエージェントから us-central1 のスキルを指定できました。ロケーションが違っても参照できます。
"base_environment": {
"type": "remote",
"sources": [
{ "type": "skill_registry",
"source": "projects/<プロジェクト番号>/locations/us-central1/skills",
"target": "/.agent/skills" }
],
"network": { "allowlist": [{ "domain": "*" }] }
}
ただし実際の配置は指定と違います。サンドボックスの中を見ると、絶対パスではなくワークスペース相対に置かれていました。
$ ls -la /.agent/skills
ls: cannot access '/.agent/skills': No such file or directory
$ find / -maxdepth 6 -name SKILL.md
/workspace/.agent/skills/cloud-monitoring-metric-selection/SKILL.md
/workspace/.agent/skills/gcp-skill-registry/SKILL.md
/workspace/.agent/skills/ops-cloud-run-basics/SKILL.md
/workspace/.agent/skills/cloud-monitoring-list-time-series-request/SKILL.md
マウントしただけでは使われない
同じ指示を 3 通りの条件で投げました。手順は書かず、「直近 30 分のレイテンシと稼働状況を調べてください。使うコマンドや API は自分で決めてください」とだけ伝えています。
条件を変えて 2 回まわしました。1 回目は直近 30 分にリクエストが無い時間帯(最後のアクセスは 17 時間前)、2 回目は 600 リクエストの負荷をかけた直後です。
| 条件 | 1 回目のトークン | 2 回目のトークン | スキルを読んだか |
|---|---|---|---|
| スキル無し | 268,839 | 357,172 | 読まない |
| スキル有り・場所を伝えない | 291,932 | 207,593 | 読まない |
| スキル有り・場所を伝える | 200,690 | 390,258 | 読む(3 本) |
一貫していたのは 1 点だけです。場所を伝えない限り、スキルは読まれません。 マウントしてあっても、2 回とも一度も参照していません。
もしかすると、現状 agy cli のような skill を自動で読み取るかたちになっていない可能性があります。
1. provision_sandbox
2. list_dir
3. view_file /workspace/.agent/skills/ops-cloud-run-basics/SKILL.md
4. list_dir
5. view_file /workspace/.agent/skills/cloud-monitoring-metric-selection/SKILL.md
6. list_dir
7. view_file /workspace/.agent/skills/cloud-monitoring-list-time-series-request/SKILL.md
トークンの増減は、この回数では判断できない
同じ条件でも 1 回目と 2 回目で 1.5 倍以上ぶれています。スキルを読ませると速くなるとも、遅くなるとも言えません。1 回ずつの比較で語れる差ではない、というのが正直なところです。
答えの中身は次のとおりでした。負荷をかけた 2 回目は、実測と突き合わせられます。
| 条件 | 2 回目の結論 |
|---|---|
| スキル無し | 613 件・エラー 0%・平均 11ms・p99 304ms(正しい) |
| スキル有り・場所を伝えない | 615 件・成功率 100%・平均 11ms・p99 304ms(正しい) |
| スキル有り・場所を伝える |
結論に到達せず。describe の生 JSON を貼っている途中で出力が切れた |
手元の負荷生成側の記録は 600 リクエストで、ヘルスチェックを含めて 613〜615 件です。数字は合っています。
3 本目が失敗した形は、コストの節で触れる話と同じです。生の JSON を貼らせると出力が膨らみ、途中で切れます。
ERROR: (gcloud.monitoring) Invalid choice: 'time-series'.
Maybe you meant: gcloud monitoring dashboards list / gcloud monitoring policies list
入れるなら CLI 前提のスキルを選ぶ
リポジトリの skills/cloud/ 119 本を調べると、MCP に言及しないものが 72 本あります。言及する 47 本のうち 29 本は、MCP ツールの呼び出しを前提に書かれています。
Managed Agents では MCP が動かないため、選ぶ基準は gcloud や REST だけで完結するか になります。今回のシナリオなら次が候補です。
| スキル | MCP 必須の記述 |
gcloud への言及 |
|---|---|---|
gcloud |
0 | 56 |
cloud-logging-configuration-basics |
0 | 26 |
cloud-run-basics |
0 | 19 |
cloud-run-alert-configuration |
0 | 13 |
cloud-logging-query-generation |
0 | 0(クエリ言語の知識のみ) |
cloud-monitoring-metric-selection |
3 | 0 |
cloud-monitoring-list-time-series-request |
4 | 1 |
上 5 本はサンドボックスの gcloud だけで完結します。下 2 本が、今回そのまま入れてしまったものです。
grep -L 'MCP tool' skills/cloud/*/SKILL.md # MCP ツール前提でないものを列挙
4. 詰まった点
MCP は Preview では使えない
mcp_server を宣言したエージェントは、対話そのものが失敗します。 サンドボックスの provisioning 直後に内部エラーで落ちます。
{
"status": "failed",
"steps": [ { "type": "function_call", "name": "provision_sandbox", "arguments": { ... } } ],
"errors": [ { "message": "Internal error encountered.", "code": "api_error" } ]
}
条件を変えて切り分けました。
| 条件 | 結果 |
|---|---|
mcp_server 無し(code_execution のみ) |
成功 |
エージェント設定に直書き / 実行時に tools で注入 |
どちらも失敗 |
network 無効 / allowlist: *
|
どちらも失敗 |
| Cloud Run MCP / BigQuery MCP(ドキュメントの例)/ 外部の公開 MCP | すべて失敗 |
| 認証ヘッダーあり / なし | どちらも失敗 |
| ストリーミング / 非ストリーミング | どちらも失敗 |
MCP サーバーを直接 initialize と tools/call で叩く |
成功 |
最後の行が効いています。https://run.googleapis.com/mcp は Streamable HTTP で正しく応答し、同じトークンで list_services も通ります。落ちているのは Managed Agents 側です。
Cloud Run MCP で何ができるはずだったか
認証なしの tools/list で、公開されているツールを確認できます。
| エンドポイント | ツール |
|---|---|
logging.googleapis.com/mcp |
list_log_entries ほか 6 件(すべて読み取り) |
monitoring.googleapis.com/mcp |
list_timeseries / query_range(PromQL)ほか 9 件(すべて読み取り) |
run.googleapis.com/mcp |
get_service / list_services と、deploy_service_from_image などの書き込み 3 件 |
ただし deploy_service_from_image が受け取る Service は image / env / args / command / ports しか持ちません。CPU・メモリ・並行数を指定する場所が無いため、MCP が動いたとしても今回のチューニングは environment 変数の変更までしかできませんでした。
対話時の environment がネットワークを黙って消す
同じエージェント(allowlist: *)に、ほぼ同じ指示を出した結果です。
| 対話リクエスト | 結果 | 消費トークン |
|---|---|---|
"environment": {"type":"remote"} を指定 |
DNS 解決に失敗。gcloud がクラッシュ |
698,991 |
environment を省略 |
名前解決も API 呼び出しも成功 | 29,092 |
ERROR: gcloud crashed (ConnectionError): HTTPSConnectionPool(
host='asia-northeast1-run.googleapis.com', port=443): Max retries exceeded
... Failed to resolve 'asia-northeast1-run.googleapis.com'
([Errno -3] Temporary failure in name resolution)
ドキュメントは「作成時に環境を定義していない場合は対話側で定義する」と書いています。定義してある場合に指定すると base_environment が置き換わり、network が失われます。エラーは出ず、外向き通信だけが止まります。
これのヤバいところが、エージェントが原因を探しに行ったことです。それ自体はまだいいのですが。/etc/passwd の確認、su appuser、setuid バイナリの列挙、sudoers の探索まで進み、1 ターンで 698,991 トークンを使っています。
暴走した対話を止める API が無い
上の約 70 万トークンのターンは、途中で止められませんでした。
POST /v1beta1/.../interactions/{id}:cancel → 404
DELETE /v1beta1/.../interactions/{id} → 400
"Interaction `...` is not finished yet. Please wait until it is completed or cancelled."
background: true は必須です。false にすると Agent interactions must set background to true. で弾かれます。いったん走り出した対話は、終わるまで待つしかありません。
サンドボックスにはコマンドの検閲がある
権限昇格を試みた過程で、拒否の挙動が観測できました。
Rejected: Invalid or unapproved sudo/apt pattern.
Only 'sudo apt update' or 'sudo apt install <pkg>' without flags are permitted.
Rejected: Metacharacters are not allowed in privileged commands (sudo/apt).
sudo は存在しますが、パターンから外れる使い方は弾かれます。ドキュメントには見当たらない挙動です。
5. コスト
課金はモデルのトークン従量のみで、サンドボックスの計算に対する別課金は観測されませんでした。今回の運用デモ 6 ターンの実測がこちらです。
| ターン | 内容 | トークン | うちキャッシュ |
|---|---|---|---|
| Turn 1 | 観測 | 95,105 | 57,154 |
| Turn 2 | 判断 | 28,821 | 0 |
| Turn 3 | 適用(権限不足で失敗) | 248,416 | 153,913 |
| Turn 4 | 比較(生 JSON をそのまま扱わせた) | 607,391 | 389,850 |
| 再適用 | 権限を足して適用 | 210,496 | 127,667 |
| 再比較 | 集計クエリに変更 | 149,638 | 71,860 |
| 合計 | 1,339,867 | 800,444 |
Turn 4 と再比較は同じ作業です。Monitoring の応答をそのまま読ませると 607,391 トークン、ALIGN_PERCENTILE_95 で集計させ値だけを出させると 149,638 トークンでした。出力の絞り方がそのまま課金に効きます。
6. 運用で気を付けること
運用させると、資格情報がどこに残り、実行結果がどこから取れるかが効いてきます。実測した観測と、そこから引く対策を並べます。
観測
| # | 観測 |
|---|---|
| 1 | egress 許可は * のみ受理。ドメイン単位の最小化はできない |
| 2 | 宣言ツールを絞っても bash 実行は残る |
| 3 | 設定に書いた MCP の資格情報は GET agent でも一覧 API でも平文で返る |
| 4 | 同じ env_id を知る別エージェントが同一サンドボックスに到達する(仕様) |
| 5 | プロンプトで渡した資格情報が保存レコードに平文で残る |
| 6 | 実行結果は保存レコードに残るが、SSE ストリームには流れない |
#1 はエラーメッセージまで確認しています。
{
"error": {
"code": 400,
"message": "Only domain: '*' is supported now. Found: `monitoring.googleapis.com`.",
"status": "INVALID_ARGUMENT"
}
}
#3 は、エージェント ID を知らなくても回収できます。GET /agents の一覧応答に、登録済みエージェントの headers がそのまま載ります。検証では実トークンではなく sentinel 文字列を入れて確認しました。
"headers": {
"Authorization": "Bearer SENTINEL-TOKEN-VALUE",
"X-Goog-User-Project": "<プロジェクト ID>"
}
#6 は監査の材料がどこにあるかの話です。SSE ストリームには、ツール呼び出しの引数しか流れません。一方 GET interaction の保存レコードには、function_result として stdout・stderr・終了コードが残ります。実行結果を追うなら保存レコードを取ります。 裏返しとして、#5 のとおり渡した資格情報もそこに残ります。
ここまで踏まえて気を付けること
外部通信と資格情報
- 外部通信は本当に必要なときだけ有効にする。現状有効化は全開放を意味する
- egress を絞れないため、ちゃんとやるなら VPC Service Controls・出口プロキシ・専用プロジェクトで隔離する
- 渡す資格情報は専用・最小権限・短命にする。保存レコードに平文で残ることを前提に選ぶ
- 設定に MCP の資格情報を直書きしない。一覧 API で誰にでも読まれる
権限境界とデータ
- 「read-only エージェント」を宣言ツールだけで作ったつもりにならない
- 付ける権限は用途ぴったりに切る。Cloud Run の更新なら
run.adminとartifactregistry.reader -
env_idを信頼境界の越しに共有しない。使い捨てにする - マウントするデータとスキルは出所を限定したうえで内容を検証する
監査と事故
- 監査は
GET interactionのfunction_resultを取る。SSE ストリームには結果が流れない - 予算上限とアラートを先に設定する。暴走した対話を止める API は無い
- Preview 規約を守る。本番データ・専有データ・機微データを載せない
おわりに
環境を Google Cloud に統一しつつ、Skill など管理ができれば有用に使えそうだと思いました。とはいえまだ Preview で使えない微妙な機能が多い印象です。今回書いてないですが、たとえば scm などのコード管理をどうするか、Secret Managerに置いて取得するか?
- Mamaged Agent API運用させる能力は実用段階にある。観測から原因推定、変更、検証までを自然言語の指示だけで回せた。失敗したときに成功を装わない点も含めて、任せられる水準。
- cloud assist もある。Gemini Enterprise から Agent Runtime を通して起動できると( https://adk.dev/agents/managed-agents/ )、できることの幅はさらに広がる可能性がある。こちらちょっと次のネタにしておきます。
- ただし今は、安全に渡す口が無い。MCP が動かないため、egress 全開放と資格情報の持ち込みが前提になる。
- 事故はセキュリティよりコストの形で出る。設定ミス 1 つで 1 ターン 70 万トークンが飛び、止める手段が無い。
Discussion