-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Open
Labels
agent engine[Component] This issue is related to Agent Engine deployment[Component] This issue is related to Agent Engine deploymentmcp[Component] Issues about MCP support[Component] Issues about MCP support
Milestone
Description
I'm encountering an error when trying to deploy an agent to agent_engines. The deployment fails with the following traceback:
TypeError: cannot pickle '_contextvars.Context' object
My code is:
from toolbox_core import ToolboxSyncClient
from google.adk.agents import Agent
from typing import Optional
import requests
import json
import os
toolbox = ToolboxSyncClient("https://....")
tools_toolbox = toolbox.load_toolset('megatoy-bigquery-toolset')
INSTRUCTION = """
You are "XXX," the primary AI assistant for MegaToy, a wholesale company specializing in home goods and general merchandise.
Your main objective is to provide excellent customer service, help customers find the right products, assist with their needs related to these items, and coordinate services when necessary.
You must always use the context of the conversation or available tools to gather information. Prefer tools over your own internal knowledge.
You must always respond in Spanish.
**Core Capabilities:**
1. **Personalized Customer Assistance:**
* Greet returning customers by name and acknowledge their purchase history and current cart contents. Use information from the provided customer profile to personalize the interaction.
* Maintain a friendly, empathetic, and helpful tone.
**Tools:**
You have access to the following tools to assist you:
* `get_weather_for_city: When the user asks for the weather in a specific city.
**Constraints:**
* You must use markdown to render any tables.
* **Never mention "tool_code", "tool_outputs", or "print statements" to the user.** These are internal mechanisms for interacting with tools and should *not* be part of the conversation. Focus solely on providing a natural and helpful customer experience. Do not reveal the underlying implementation details.
* Always confirm actions with the user before executing them (e.g., "Would you like me to update your cart?").
* Be proactive in offering help and anticipating customer needs.
* Don't output code even if user asks for it.
**Product Image Display:**
* Include the image along with the product name, price, and other relevant details in the table or list presented to the user.
"""
## --- Functions ---
def get_weather_for_city(city_name: str) -> Optional[dict]:
"""
Obtiene la latitud y longitud de una ciudad usando Google Geocoding API
y luego obtiene el clima usando OpenWeatherMap API.
Las claves API se obtienen de las variables de entorno.
Args:
city_name (str): El nombre de la ciudad.
Returns:
dict: Un diccionario con la información del clima, o None si ocurre un error.
"""
# Obtener claves API de las variables de entorno
google_api_key = os.getenv("MAPS_GOOGLE_API_KEY", "")
weather_api_key = google_api_key
if not google_api_key:
print("Error: La variable de entorno GOOGLE_API_KEY no está configurada.")
return None
if not weather_api_key:
print("Error: La variable de entorno OPENWEATHERMAP_API_KEY no está configurada.")
return None
# Paso 1: Obtener latitud y longitud usando Google Geocoding API
geocode_url = f"https://maps.googleapis.com/maps/api/geocode/json?address={city_name}&key={google_api_key}"
try:
response = requests.get(geocode_url)
response.raise_for_status()
geocode_data = response.json()
if geocode_data['status'] == 'OK':
location = geocode_data['results'][0]['geometry']['location']
latitude = location['lat']
longitude = location['lng']
print(f"Coordenadas para {city_name}: Latitud={latitude}, Longitud={longitude}")
# Paso 2: Obtener el clima usando las coordenadas (ejemplo con OpenWeatherMap)
weather_url = f"https://weather.googleapis.com/v1/currentConditions:lookup?key={weather_api_key}&location.latitude={latitude}&location.longitude={longitude}"
weather_response = requests.get(weather_url)
weather_response.raise_for_status()
weather_data = weather_response.json()
return weather_data
else:
print(f"Error al geocodificar {city_name}: {geocode_data['status']}")
if 'error_message' in geocode_data:
print(f"Mensaje de error de Google: {geocode_data['error_message']}")
return None
except requests.exceptions.HTTPError as http_err:
print(f"Error HTTP: {http_err}")
return None
except requests.exceptions.RequestException as req_err:
print(f"Error en la solicitud: {req_err}")
return None
except KeyError as key_err:
print(f"Error al parsear la respuesta JSON (KeyError): {key_err}")
return None
except IndexError:
print(f"No se encontraron resultados para la ciudad: {city_name}")
return None
root_agent = Agent(
name="megatoy_service_agent",
description="A customer service agent for Megatoy, for demo presentation.",
model="gemini-2.5-pro-preview-03-25",
instruction=INSTRUCTION,
tools=[get_weather_for_city, *tools_toolbox],
)
We followed the documentation from:
- https://google.github.io/adk-docs/deploy/agent-engine/
- https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/deploy?hl=es-419
We ran some local tests such as:
app = reasoning_engines.AdkApp(
agent=root_agent,
enable_tracing=True,
)
session = await app.create_session(user_id="u_123")
for event in app.stream_query(
user_id="u_123",
session_id=session.id,
message="cual es el clima en la ciudad de méxico",
):
print(event)
Everything worked fine locally. However, when running the following code:
dependencies = [
"google-cloud-aiplatform[adk,agent_engines]",
"toolbox_core",
]
remote_app = agent_engines.create(
agent_engine=root_agent,
requirements=dependencies,
gcs_dir_name=gcs_dir_name,
display_name=display_name,
description=description,
env_vars=env_vars,
)
I get the following error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[10], line 1
----> 1 remote_app = agent_engines.create(
2 agent_engine=root_agent,
3 requirements=dependencies,
4 gcs_dir_name=gcs_dir_name,
5 display_name=display_name,
6 description=description,
7 env_vars=env_vars,
8 )
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\site-packages\vertexai\agent_engines\__init__.py:147, in create(agent_engine, requirements, display_name, description, gcs_dir_name, extra_packages, env_vars)
61 def create(
62 agent_engine: Optional[Union[Queryable, OperationRegistrable]] = None,
63 *,
(...) 71 ] = None,
72 ) -> AgentEngine:
73 """Creates a new Agent Engine.
74
75 The Agent Engine will be an instance of the `agent_engine` that
(...) 145 nonexistent file.
146 """
--> 147 return AgentEngine.create(
148 agent_engine=agent_engine,
149 requirements=requirements,
150 display_name=display_name,
151 description=description,
152 gcs_dir_name=gcs_dir_name,
153 extra_packages=extra_packages,
154 env_vars=env_vars,
155 )
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\site-packages\vertexai\agent_engines\_agent_engines.py:449, in AgentEngine.create(cls, agent_engine, requirements, display_name, description, gcs_dir_name, extra_packages, env_vars)
447 staging_bucket = initializer.global_config.staging_bucket
448 if agent_engine is not None:
--> 449 agent_engine = _validate_agent_engine_or_raise(agent_engine)
450 _validate_staging_bucket_or_raise(staging_bucket)
451 if agent_engine is None:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\site-packages\vertexai\agent_engines\_agent_engines.py:874, in _validate_agent_engine_or_raise(agent_engine)
866 raise ValueError(
867 "Invalid register_operations signature. This might be due to a "
868 "missing `self` argument in the "
869 "agent_engine.register_operations method."
870 ) from err
872 if isinstance(agent_engine, Cloneable):
873 # Avoid undeployable states.
--> 874 agent_engine = agent_engine.clone()
875 return agent_engine
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\site-packages\vertexai\preview\reasoning_engines\templates\adk.py:387, in AdkApp.clone(self)
383 """Returns a clone of the ADK application."""
384 import copy
386 return AdkApp(
--> 387 agent=copy.deepcopy(self._tmpl_attrs.get("agent")),
388 enable_tracing=self._tmpl_attrs.get("enable_tracing"),
389 session_service_builder=self._tmpl_attrs.get("session_service_builder"),
390 artifact_service_builder=self._tmpl_attrs.get("artifact_service_builder"),
391 env_vars=self._tmpl_attrs.get("env_vars"),
392 )
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:153, in deepcopy(x, memo, _nil)
151 copier = getattr(x, "__deepcopy__", None)
152 if copier is not None:
--> 153 y = copier(memo)
154 else:
155 reductor = dispatch_table.get(cls)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\site-packages\pydantic\main.py:938, in BaseModel.__deepcopy__(self, memo)
936 cls = type(self)
937 m = cls.__new__(cls)
--> 938 _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
939 _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo))
940 # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],
941 # and attempting a deepcopy would be marginally slower.
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:206, in _deepcopy_list(x, memo, deepcopy)
204 append = y.append
205 for a in x:
--> 206 append(deepcopy(a, memo))
207 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
[... skipping similar frames: deepcopy at line 172 (2 times), _deepcopy_dict at line 231 (1 times), _reconstruct at line 271 (1 times), deepcopy at line 146 (1 times)]
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:297, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
295 for key, value in dictiter:
296 key = deepcopy(key, memo)
--> 297 value = deepcopy(value, memo)
298 y[key] = value
299 else:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:288, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
286 if deep:
287 for item in listiter:
--> 288 item = deepcopy(item, memo)
289 y.append(item)
290 else:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in _deepcopy_tuple(x, memo, deepcopy)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in <listcomp>(.0)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in _deepcopy_tuple(x, memo, deepcopy)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in <listcomp>(.0)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
[... skipping similar frames: deepcopy at line 146 (1 times)]
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
[... skipping similar frames: deepcopy at line 146 (1 times)]
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in _deepcopy_tuple(x, memo, deepcopy)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in <listcomp>(.0)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
[... skipping similar frames: deepcopy at line 146 (1 times)]
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
[... skipping similar frames: deepcopy at line 146 (1 times)]
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:206, in _deepcopy_list(x, memo, deepcopy)
204 append = y.append
205 for a in x:
--> 206 append(deepcopy(a, memo))
207 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:172, in deepcopy(x, memo, _nil)
170 y = x
171 else:
--> 172 y = _reconstruct(x, memo, *rv)
174 # If is its own copy, don't memoize.
175 if y is not x:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:271, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
269 if state is not None:
270 if deep:
--> 271 state = deepcopy(state, memo)
272 if hasattr(y, '__setstate__'):
273 y.__setstate__(state)
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in _deepcopy_tuple(x, memo, deepcopy)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:211, in <listcomp>(.0)
210 def _deepcopy_tuple(x, memo, deepcopy=deepcopy):
--> 211 y = [deepcopy(a, memo) for a in x]
212 # We're not going to put the tuple in the memo, but it's still important we
213 # check for it, in case the tuple contains recursive mutable structures.
214 try:
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:146, in deepcopy(x, memo, _nil)
144 copier = _deepcopy_dispatch.get(cls)
145 if copier is not None:
--> 146 y = copier(x, memo)
147 else:
148 if issubclass(cls, type):
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:231, in _deepcopy_dict(x, memo, deepcopy)
229 memo[id(x)] = y
230 for key, value in x.items():
--> 231 y[deepcopy(key, memo)] = deepcopy(value, memo)
232 return y
File c:\Users\demo\miniconda3\envs\portalup-demos\Lib\copy.py:161, in deepcopy(x, memo, _nil)
159 reductor = getattr(x, "__reduce_ex__", None)
160 if reductor is not None:
--> 161 rv = reductor(4)
162 else:
163 reductor = getattr(x, "__reduce__", None)
TypeError: cannot pickle '_contextvars.Context' object
Thank you!
Metadata
Metadata
Assignees
Labels
agent engine[Component] This issue is related to Agent Engine deployment[Component] This issue is related to Agent Engine deploymentmcp[Component] Issues about MCP support[Component] Issues about MCP support