adk deploy agent_engine packages an ADK agent for Agent Runtime. Unlike the previous lesson’s generated application project, this path starts from the agent directory and an ADK deployment configuration. Here we inspect the recorded deployment and use the runtime REST methods to create sessions, stream answers and add a completed conversation to Memory Bank.
Agent Runtime is the managed runtime within Google Cloud Agent Platform. The agent_engine CLI subcommand and reasoningEngines REST resource names are interface identifiers retained in this deployment path. They do not mean that the code is deploying a different application product each time a name changes.
When you need this. A service already has an ADK agent package and its own build conventions. You want the managed runtime without adopting the Agents CLI’s generated application layout. The deployment is only half the interface: the caller also needs to choose the right runtime method and keep user and session IDs associated.
Recorded example. The archived “Deploy an ADK agent to Agent Runtime” demonstration retains the earlier shipctl scenario; the current lab uses docuctl and does not claim a new model result.
# 1. Connection check: the methods this deployment serves
get_session, list_sessions, create_session, delete_session, async_get_session, async_list_sessions, async_create_session, async_delete_session, async_add_session_to_memory, async_search_memory, async_save_artifact, async_load_artifact, async_list_artifact_keys, async_delete_artifact, async_list_versions, async_list_artifact_versions, async_get_artifact_version, stream_query, async_stream_query, streaming_agent_run_with_events
# 2. Conversation 1, in a new session
# 3 s
session 7832467459528458240
> I run shipctl 1.x. How do I undo release r42 in production?
# 5 s
call read_page {"page":"rollback"}
result read_page
answer In shipctl 1.x, use the `shipctl release revert` command:
```bash
shipctl release revert r42 --env production
```
*(Or simply `shipctl release revert r42` if production is the default environment).*
Note that database migrations are not rolled back automatically—if r42 ran a migration, you must deploy a new migration to reverse it.
Lab setup. Download the complete lab ZIP, extract it and open a terminal in adk-agent-runtime/. Keep its code, tests and fixtures together. Create the pinned environment for local checks:
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements-lock.txtThe saved cloud results can be read without provisioning services. The cloud steps require the project access listed in the prerequisites.
1. Package the same docs assistant for the managed runtime
The agent remains the docuctl docs assistant. Its model uses a global endpoint; the runtime and its managed data remain in the selected region. The configuration file sets resource bounds and the Memory Bank topic from the managed-services lesson:
{
"display_name": "docs-helper",
"description": "docuctl docs assistant",
"labels": {"aiml-lesson": "deploy-an-adk-agent-to-agent-runtime"},
"min_instances": 0,
"max_instances": 1,
"resource_limits": {"cpu": "1", "memory": "1Gi"},
"context_spec": {"memory_bank_config": {
"ttl_config": {"default_ttl": "86400s"},
"customization_configs": [{"memory_topics": [
{"managed_memory_topic": {"managed_topic_enum": "USER_PREFERENCES"}},
{"custom_memory_topic": {"label": "tool_versions",
"description": "Which version of a CLI, SDK or service the developer runs, for example 'runs docuctl 1.x'."}}
]}]
}}
}This is a deployment configuration, not a measured capacity result. Check the official service limits and your workload before choosing different values. The lab includes pinned dependencies and the current docuctl agent source; the saved output retains the original shipctl run.
2. Deploy with the native ADK command
Deploy the agent directory with adk deploy agent_engine. ADK copies the source, reads .agent_engine_config.json, generates requirements.txt and creates the Agent Runtime instance:
adk deploy agent_engine --project $GOOGLE_CLOUD_PROJECT \
--region us-central1 --display_name docs-helper docs_helper$ adk deploy agent_engine --project $GOOGLE_CLOUD_PROJECT --region us-central1 --display_name docs-helper docs_helper
Copying agent source code...
Copying agent source code complete.
Resolving files and dependencies...
Reading agent platform config from ./docs_helper/.agent_engine_config.json
Overriding display_name in agent platform config with docs-helper
Creating ./docs_helper_tmp20261021_213335/agents/docs_helper/requirements.txt...
Using google-adk[a2a]==2.12.0 in requirements
Created ./docs_helper_tmp20261021_213335/agents/docs_helper/requirements.txt
Initializing Agent Platform client...
Agent Platform client initialized with project and region.
Deploying to Agent Platform...
Created a new instance: projects/PROJECT_NUMBER/locations/us-central1/reasoningEngines/60776879614853120
Creating Dockerfile...
Dockerfile created at ./docs_helper_tmp20261021_213335/Dockerfile.
Deployed to Agent Platform: projects/PROJECT_NUMBER/locations/us-central1/reasoningEngines/60776879614853120The installed command still uses agent_engine although the product is called Agent Runtime. --agent_engine_id selects an existing instance for update; omitting it creates a new one. The installed help marks --staging_bucket deprecated and unused. These distinctions matter when adapting an older deployment script.
The recording used an existing approved project. Inspect its deployment output before adapting the command to a different project or region.
3. Call session methods and stream an agent turn
query.sh obtains an access token from gcloud, calls ordinary methods through :query, and sends the streaming method to :streamQuery?alt=sse. It prints the available methods before using them, so an SDK or deployment change is visible to the caller.
#!/usr/bin/env bash
# Talk to the deployed docs assistant over the Agent Runtime REST API.
# Usage: PROJECT_ID=... RESOURCE_ID=... ./query.sh
set -euo pipefail
LOCATION_ID=${LOCATION_ID:-us-central1}
URL="https://${LOCATION_ID}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/${LOCATION_ID}/reasoningEngines/${RESOURCE_ID}"
AUTH="Authorization: Bearer $(gcloud auth print-access-token)"
query() { # query CLASS_METHOD INPUT_JSON: one JSON response
curl -s -H "$AUTH" -H "Content-Type: application/json" "$URL:query" \
-d "{\"class_method\": \"$1\", \"input\": $2}"
}
stream() { # stream USER SESSION MESSAGE: one JSON event per line
curl -s -H "$AUTH" -H "Content-Type: application/json" "$URL:streamQuery?alt=sse" \
-d "$(jq -n --arg u "$1" --arg s "$2" --arg m "$3" \
'{class_method: "async_stream_query", input: {user_id: $u, session_id: $s, message: $m}}')"
}
show() { # print tool calls and the final text of a stream
jq -r '.content.parts[]? | if .function_call then "call \(.function_call.name) \(.function_call.args|tojson)"
elif .function_response then "result \(.function_response.name)"
elif (.text and (.thought|not)) then "answer \(.text)" else empty end'
}
timed() { local s=$(date +%s); "$@"; echo "# $(( $(date +%s) - s )) s" >&2; }
echo "# 1. Connection check: the methods this deployment serves"
curl -s -H "$AUTH" "$URL" | jq -r '[.spec.classMethods[].name] | join(", ")'
echo; echo "# 2. Conversation 1, in a new session"
S1=$(timed query async_create_session '{"user_id": "dana"}' | jq -r '.output.id')
echo "session $S1"
echo "> I run docuctl 1.x. How do I undo release r42 in production?"
timed stream dana "$S1" "I run docuctl 1.x. How do I undo release r42 in production?" | show
echo; echo "# 3. Add the finished session to Memory Bank"
SESSION=$(query async_get_session "{\"user_id\": \"dana\", \"session_id\": \"$S1\"}" | jq -c '.output')
query async_add_session_to_memory "{\"session\": $SESSION}" | jq -c '.'
for i in $(seq 1 40); do # memories are extracted in the background
FOUND=$(query async_search_memory '{"user_id": "dana", "query": "docuctl version"}' | jq -c '[.output.memories[]?.content.parts[0].text]')
[ "$FOUND" != "[]" ] && break; sleep 5
done
echo "memories after about $(( i * 5 )) s: $FOUND"
echo; echo "# 4. Conversation 2, in another new session"
S2=$(query async_create_session '{"user_id": "dana"}' | jq -r '.output.id')
echo "> How do I undo the last production release?"
timed stream dana "$S2" "How do I undo the last production release?" | showThe wrapper’s input is {class_method, input}. async_create_session returns an ID; async_stream_query needs that ID, the user ID and a message. A stream is not an ordinary JSON return value, which is why the two surfaces are distinct. Keep request construction and response parsing explicit when diagnosing a failed call.
A streaming method on the ordinary query surface. Select the endpoint from the runtime’s registered streaming operations.
A missing user session. The SDK raises when its session service finds no matching conversation. The local test uses the in-memory service; the saved cloud log is the separate remote observation.
# 1. A streaming method sent to :query
{"code":400,"message":"Reasoning Engine Execution failed.\nPlease refer to our documentation (https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/troubleshooting/use) for checking logs and other troubleshooting tips.\nError Details: Internal Server Error","status":"FAILED_PRECONDITION"}
# 2. Dana's session id sent with another user id
# The service log for both requests
$ gcloud logging read "resource.type=\"aiplatform.googleapis.com/ReasoningEngine\" AND resource.labels.reasoning_engine_id=\"$RESOURCE_ID\" AND severity>=ERROR" --project $PROJECT_ID --freshness=15m
ValueError: Session 1529679781023449088 does not belong to user lee.
ValueError: [TypeError("'async_generator' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
TypeError: vars() argument must have __dict__ attribute
TypeError: 'async_generator' object is not iterable
ValueError: [TypeError("'async_generator' object is not iterable"), TypeError('vars() argument must have __dict__ attribute')]
TypeError: vars() argument must have __dict__ attributeSending async_stream_query to :query produced a failed request and a service log about an async generator. The fix is the streaming endpoint, not a retry of the same JSON request. A request with another user ID and the session ID also failed in the service log.
That user-ID check prevents accidental mixing of sessions; it is not an identity boundary for your application. An authorized runtime caller supplies the user_id field. Your caller must map its authenticated subject to that field rather than accepting an arbitrary value from a client.
test_a_missing_user_session_does_not_return_another_users_history (test_runtime.RuntimeConfiguration.test_a_missing_user_session_does_not_return_another_users_history) ... ok
test_deployment_resource_limits_are_explicit (test_runtime.RuntimeConfiguration.test_deployment_resource_limits_are_explicit) ... ok
test_model_and_regional_services_can_use_different_locations (test_runtime.RuntimeConfiguration.test_model_and_regional_services_can_use_different_locations) ... ok
test_read_page_rejects_a_path_outside_the_docs (test_runtime.RuntimeConfiguration.test_read_page_rejects_a_path_outside_the_docs) ... ok
test_streaming_method_is_registered_on_the_stream_surface (test_runtime.RuntimeConfiguration.test_streaming_method_is_registered_on_the_stream_surface) ... ok
----------------------------------------------------------------------
Ran 5 tests in 0.339s
OK4. Add the completed conversation to Memory Bank
After the first turn, the script retrieves the session and calls async_add_session_to_memory. It polls async_search_memory before opening another session. The second answer used the recalled CLI version, as the saved output shows:
Add the finished session to Memory Bank
{"output":null}
memories after about 25 s: ["I run shipctl 1.x."]
# 4. Conversation 2, in another new session
> How do I undo the last production release?
# 6 s
call load_memory {"query":"shipctl major version"}
call read_page {"page":"rollback"}
result load_memory
result read_page
answer Since you are on **shipctl 1.x**, run:
```bash
shipctl release revert <release-id>
```
Specify the release identifier you want to revert back to (you can find previous releases with `shipctl releases`).
Note that database migrations are not automatically rolled back—if the release ran a migration, you'll need to deploy a reverse migration.This is the same managed-memory mechanism as the earlier lesson; the difference is that the managed runtime exposes it through registered methods. An ingestion response is not a searchable-memory acknowledgement. The polling loop is bounded, and an empty result should be reported to the caller rather than interpreted as remembered data.
Remove only the lesson deployment
The recorded instance was deleted with force=true, which also removed its sessions and memories. For your own run, inspect the project, region, resource ID and lesson label before deletion, and wait for the delete operation to finish. A deployment name is not enough to identify an owned resource. The saved delete output records the original teardown.
What’s next
Deploy an ADK agent to Cloud Run uses the API server’s HTTP interface behind Cloud Run IAM and makes the session-store choice explicit.
Read the unchanged docs assistant
"""The docs assistant on Gemini Enterprise Agent Platform: Gemini through Vertex, recall through Memory Bank."""
import os
from pathlib import Path
from google.adk import Agent
from google.adk.models.google_llm import Gemini
from google.adk.tools import load_memory
from google.genai import types
DOCS = Path(__file__).resolve().parent / "docs"
PAGES = sorted(p.stem for p in DOCS.glob("*.md"))
# The recorded run uses global for this model. Sessions and Memory Bank stay regional
# (GOOGLE_CLOUD_LOCATION), so the model client gets its own location.
MODEL_LOCATION = os.environ.get("DOCS_MODEL_LOCATION", "global")
def read_page(page: str) -> dict:
"""Read one page of the docuctl documentation: config, deploy, rollback or tokens."""
if page not in PAGES:
return {"error": f"No page {page!r}. Pages: {', '.join(PAGES)}."}
return {"page": page, "text": (DOCS / f"{page}.md").read_text(encoding="utf-8")}
INSTRUCTION = """You answer questions from developers about docuctl, the team's deploy CLI.
Read the relevant page with read_page before you answer. Answers depend on the developer's
docuctl major version (1.x or 2.x). If the developer has not said it in this conversation,
call load_memory to look for it in earlier conversations before you ask. Keep the answer
under 80 words."""
root_agent = Agent(
name="docs_helper",
model=Gemini(model="gemini-3.8-flash", client_kwargs={"location": MODEL_LOCATION}),
instruction=INSTRUCTION,
tools=[read_page, load_memory],
generate_content_config=types.GenerateContentConfig(
temperature=0.2, thinking_config=types.ThinkingConfig(thinking_level="low")),
)Check your understanding
Read the explanation
The script routes streaming operations through :streamQuery?alt=sse. An async stream is not an ordinary JSON query result.
References
Official documentation and installed ADK / Google Cloud AI Platform APIs checked October 22, 2026.