Artificial Intelligence (AI) agents are transforming the way we interact with technology, acting as autonomous assistants capable of reasoning, planning, and executing tasks. From virtual helpers to complex multi-agent systems, the demand for accessible tools to build these agents is on the rise. Enter Google’s Agent Development Kit (ADK), an open-source Python framework launched on April 9, 2025, designed to simplify the creation of sophisticated AI agents. Integrated tightly with the Google ecosystem—particularly Gemini models and Vertex AI—ADK empowers developers to build, test, and deploy agents with ease, whether for simple tasks or intricate collaborative workflows.
This article will guide you step-by-step through building your very first agent using ADK. We’ll cover setting up your development environment using the fast package installer uv, connecting your agent to a Gemini model via the Google AI Studio API, giving your agent the ability to remember information using Session State, and enhancing it with real-time web search capabilities through the Tavily search tool. By the end, you’ll have a functional agent ready for further exploration!
What is the Google Agent Development Kit (ADK)?
The Google Agent Development Kit (ADK) is an open-source Python framework introduced by Google on April 9, 2025, aimed at simplifying the development of AI agents. Built with integration in mind, ADK leverages Google’s powerful AI infrastructure—such as the Gemini family of models and Vertex AI—to enable developers to create agents that can reason, use tools, and collaborate in multi-agent systems. Whether you’re building a single-purpose assistant or a team of specialized agents, ADK provides the scaffolding to make it happen efficiently.
Key Features of ADK
Tool Integration: Agents can call external APIs, perform web searches, or execute custom functions, making them versatile problem-solvers.
Multi-Agent Collaboration: ADK supports the creation of agent teams that work together, sharing tasks and memory for complex workflows.
Google Ecosystem Compatibility: Seamless integration with Google Cloud, Vertex AI, and Gemini models ensures high performance and scalability.
Open-Source Flexibility: Being open-source, ADK allows customization and community contributions, fostering rapid innovation.
Ease of Use: With pre-built templates and a straightforward API, even beginners can get started quickly.
Why Use ADK for Building Agents?
ADK stands out in a crowded field of AI development tools due to its balance of simplicity and power. Unlike more generic frameworks, its tight integration with Google’s AI offerings—like Gemini’s advanced reasoning capabilities—gives developers access to cutting-edge technology without needing to build everything from scratch. Additionally, its open-source nature means you’re not locked into a proprietary system, offering freedom to adapt your agent to unique use cases. Whether you’re automating a personal task or prototyping a business solution, ADK provides a robust starting point.
In short, ADK is ideal for anyone looking to harness AI agents without getting bogged down in complexity. It’s a bridge between powerful AI models and practical, real-world applications—perfect for your first agent-building adventure.
Build Your First Agent with Google Agent Development Kit (ADK)
Prerequisites
Before we dive into building your first agent with the Google Agent Development Kit (ADK) and uv, let’s ensure you have everything you need to follow along. This section outlines the tools, accounts, and basic knowledge required to complete the project.
Required Tools and Accounts
Python 3.9+
ADK requires Python 3.9 or higher. We’ll use uv to manage the Python version, so you don’t need it pre-installed—uv can handle that for you. However, having a base Python installation (e.g., via your system package manager or python.org) simplifies the initial uv setup.
uv
The star of our project management show! You’ll install uv as a standalone binary (no Python required upfront). We’ll cover the installation steps in the next section.
Google AI Studio API Key
To power our agent with the Gemini 2.0 Flash model, you’ll need an API key from Google AI Studio. Sign up or log in at aistudio.google.com, navigate to the API section, and generate a key. We’ll configure it securely using environment variables.
Tavily API Key
For search functionality, we’ll integrate the Tavily search tool, which provides real-time web search results tailored for AI agents. Get a free API key by signing up at tavily.com. You’ll need this to enable your agent to fetch information from the web.
Text Editor or IDE
Use any editor you prefer—VS Code, PyCharm, or even a simple text editor like Notepad++ will work. You’ll be writing Python code and editing configuration files.
Basic Knowledge Assumptions
This guide assumes you have:
Basic Python Knowledge: Familiarity with Python syntax, functions, and running scripts. You don’t need to be an expert, but you should know how to write and execute a simple Python program.
Understanding of Virtual Environments: A general idea of why virtual environments are useful (isolating project dependencies). If you’ve used venv or pip before, you’re set.
API Basics: Awareness of what an API key is and how it’s used to authenticate requests. No deep API expertise is required—we’ll walk through the setup.
With these in place, you’re ready to set up your project and start building. In the next section, we’ll install uv, initialize your project, and add the necessary dependencies to get ADK up and running.
Setting Up Your Project with uv
Now that you have the prerequisites sorted, let’s set up your Python project using uv. This section walks you through installing uv, initializing a new project, creating a virtual environment, and adding the necessary dependencies for the Google Agent Development Kit (ADK) and Tavily search integration. By using uv instead of pip, you’ll experience a faster, more streamlined workflow.
Installing uv
uv is a standalone tool written in Rust, so you don’t need a pre-existing Python installation to get started. As of April 2025, the latest version is available from the Astral team’s official site. Here’s how to install it:
On macOS/Linux:
Open your terminal and run:
bash
curl -LsSf https://astral.sh/uv/install.sh | sh
This downloads and installs uv into ~/.local/bin. After installation, ensure it’s in your PATH by restarting your terminal or running:
bash
source ~/.local/bin/env
On Windows:
In PowerShell, execute:
powershell
irm https://astral.sh/uv/install.ps1 | iex
The installer adds uv to your PATH automatically, but you might need to restart PowerShell to use it.
Verify the installation:
bash
uv --version
You should see output like uv 0.6.6 (or a later version by April 2025). If it fails, check your PATH or reinstall.
Initializing a New Python Project
With uv installed, let’s create a new project:
Initialize the Project:
Run:
bash
uv init my-adk-agentcd my-adk-agent
This creates a directory my-adk-agent with a basic structure:
Set the Python Version:
The default Python version in .python-version (e.g., 3.12) works for ADK, but let’s ensure we’re using 3.12 explicitly:
bash
uv python pin 3.12
If 3.12 isn’t installed, uv can fetch it:
bash
uv python install 3.12
Setting Up a Virtual Environment
Unlike pip and venv, uv simplifies environment creation:
bash
uv venv
This creates a .venv directory in your project root using Python 3.12. You’ll see:
text
Using Python 3.12.xCreating virtual environment at: .venvActivate with: source .venv/bin/activate
You can activate it manually if needed (source .venv/bin/activate on macOS/Linux, .venv\Scripts\activate on Windows), but uv run will handle this automatically later.
Adding ADK and Dependencies
Now, let’s add the ADK package and Tavily support:
Add Google ADK:
bash
uv add google-adk
This updates pyproject.toml:
toml
[project]dependencies = [ "google-adk>=x.x.x", # Latest version as of April 2025]
Add Tavily Support:
Since we’ll use Tavily via LangChain’s integration, install langchain-community and tavily-python:
Sync the Environment:
Install all dependencies into .venv:
bash
uv sync
You’ll see output listing installed packages (e.g., google-adk, langchain-community, tavily-python, and their dependencies) in mere milliseconds—uv’s speed shines here compared to pip.
Your project is now set up with uv, ready to build an ADK agent. In the next section, we’ll configure the Google AI Studio API to power our agent with the Gemini model.
Okay, here is the next section:
Connecting to Gemini via AI Studio
Your ADK agent needs a way to communicate with the Gemini Large Language Model (LLM) that powers its intelligence. This communication happens via secure API calls, which require authentication credentials, typically an API key. Without a valid key, the LLM service will deny the agent’s requests, preventing it from working.
For development and prototyping, Google AI Studio provides an easy way to get an API key.
If you don’t have one already, create an API key. Copy this key securely – you’ll need it in the next step.
Configure Your Environment
The best way to provide the API key to your ADK application is through environment variables, commonly managed using a .env file within your project directory.
Inside your agent project folder (the one containing your agent.py file), create a new file named .env.
Open the .env file and add the following lines, replacing "PASTE_YOUR_ACTUAL_API_KEY_HERE" with the key you copied from AI Studio:
dotenv
# Use Google AI Studio (not Vertex AI)GOOGLE_GENAI_USE_VERTEXAI="False"# Your API KeyGOOGLE_API_KEY="PASTE_YOUR_ACTUAL_API_KEY_HERE"
The GOOGLE_GENAI_USE_VERTEXAI="False" line explicitly tells ADK to use the Google AI backend (which uses the GOOGLE_API_KEY) instead of the Vertex AI backend. ADK will automatically load these variables when your agent runs, allowing it to authenticate securely with the Gemini API.
Okay, here is the next section:
Building Your First Basic Agent
With the environment set up and the API key configured, let’s create the core of our agent application.
Set Up the Project Structure
A recommended structure helps keep your agent code organized. In your main project directory (where you created the .venv folder), create a structure like this:
Create a folder named agent_module. This will hold your agent’s code.
Inside agent_module, create an empty file named __init__.py. This tells Python to treat the folder as a package. Add the following line to it:
python
# agent_module/__init__.pyfrom . import agent
Inside agent_module, create the main file for your agent’s logic: agent.py.
You should already have the .env file in the your_agent_project directory from the previous step.
Define Your Agent
Now, open agent.py and define your first agent using the Agent class from ADK. This class is the central piece for defining your agent’s identity, model, and instructions.
python
# agent_module/agent.pyfrom google.adk.agents import Agent# Define the root agent for your applicationroot_agent = Agent( # A unique name for your agent [cite: 167, 639] name="my_first_adk_agent", # Specify the Gemini model to use (ensure compatibility with AI Studio key) [cite: 19, 56, 168, 645] model="gemini-2.0-flash-exp", # Or "gemini-1.0-pro" etc. # A brief description of what the agent does [cite: 169, 642] description="A basic agent that can chat.", # Instructions guiding the agent's behavior and persona [cite: 171, 648] instruction="You are a friendly and helpful assistant. Respond concisely to user queries.", # Initially, we won't add specific tools [cite: 172, 659] tools=[])print(f"Agent '{root_agent.name}' defined.")
In this code:
name: Gives your agent a unique ID.
model: Tells the agent which Gemini model to use for thinking and responding. We’re using a recent Flash model suitable for general chat.
description: Briefly summarizes the agent’s purpose, which is especially useful in multi-agent setups.
instruction: Provides the core guidance to the LLM on how it should behave.
tools: An empty list for now, as this basic agent won’t use external tools yet.
Running Your Agent (Briefly)
ADK provides command-line tools to interact with your agent. Navigate your terminal to the your_agent_project directory (the one containing agent_module and .env) and you can try:
adk web: This launches an interactive web UI in your browser, allowing you to chat with your agent.
adk run agent_module: This lets you chat with the agent directly in your terminal.
We won’t delve deep into running the agent just yet, as we first need to add memory and tools, but this gives you an idea of how to interact with it later.
Adding Memory with Session State
Our basic agent can chat, but it has no memory. Each time you interact with it, it starts fresh, forgetting everything from previous turns. To build more engaging and useful agents, we need to give them the ability to remember context and past interactions. ADK achieves this through Session State.
What is Session State?
Session State is essentially a memory bank for each individual conversation session. Technically, it’s a Python dictionary associated with a specific user session. Information stored in this dictionary persists across multiple turns within that single conversation. Both your agent and its tools can read from and write to this state, allowing them to:
Remember user preferences.
Recall information mentioned earlier in the conversation.
Personalize responses based on past interactions.
Using InMemorySessionService
For this tutorial, we’ll use ADK’s InMemorySessionService. As the name suggests, it stores the session history and state directly in your computer’s memory. This is perfect for development and testing, though the memory will be lost when your application stops.
Setting Up the Runner and Session Service
To manage sessions and run your agent, you need two more components: InMemorySessionService and Runner. Let’s add the setup code, typically in a main script or alongside your agent definition:
python
# Add these imports to your agent.py or a main scriptfrom google.adk.sessions import InMemorySessionServicefrom google.adk.runners import Runnerfrom google.adk.agents import Agent # Assuming root_agent is defined as before# Define constants for the app and session identificationAPP_NAME = "my_first_adk_app"USER_ID = "user_test_1" # Identifier for the userSESSION_ID = "session_abc_123" # Identifier for this specific conversation# 1. Create the Session Service instancesession_service = InMemorySessionService()print("Session Service created.")# 2. Create the specific session (can initialize state here if needed)session = session_service.create_session( app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID # state={"initial_key": "initial_value"} # Optional: initial state)print(f"Session '{SESSION_ID}' created.")# 3. Create the Runner instance# The Runner orchestrates the agent's execution and uses the session servicerunner = Runner( agent=root_agent, # Your agent defined earlier app_name=APP_NAME, session_service=session_service)print(f"Runner created for agent '{root_agent.name}'.")# (You would then use 'runner.run()' or 'runner.run_async()' to interact)
Accessing State in Tools
The primary way tools interact with memory is through the ToolContext object. If you define a tool function that accepts tool_context: ToolContext as its last argument, ADK automatically provides this object when the tool is called.
python
# Example tool demonstrating state accessfrom google.adk.tools.tool_context import ToolContextdef remember_something(data_to_remember: str, tool_context: ToolContext) -> dict: """Reads previous data from state and saves new data.""" print("--- Tool: remember_something called ---") # Read from state (use .get() for safety) previous_data = tool_context.state.get("user_data", "nothing") print(f"--- Tool: Found previous data: '{previous_data}' ---") # # Write new data to state tool_context.state["user_data"] = data_to_remember # [cite: 354] print(f"--- Tool: Saved new data: '{data_to_remember}' ---") return {"status": "success", "message": f"I remembered '{data_to_remember}'. Before that, I knew about '{previous_data}'."}# (You would add 'remember_something' to the agent's 'tools' list)
This allows tools to dynamically adapt their behavior based on what’s happened earlier in the conversation.
Quick Note on output_key: ADK also offers a shortcut. By setting output_key="some_name" when defining an Agent, the agent’s final text response for each turn will automatically be saved into session.state["some_name"].
Now that our agent has a way to remember things, let’s give it some tools to interact with the outside world!
Enhancing Your Agent with Tools: Tavily Search
Our agent can now chat and remember things within a session. However, its knowledge is limited to what the underlying Gemini model was trained on. To make it truly powerful, we need to give it access to real-time information and external capabilities. In ADK, this is done using Tools.
Tools are functions or services that allow your agent to interact with the outside world – fetch data from APIs, query databases, perform calculations, or, as we’ll do now, search the web.
Integrating Third-Party Tools: LangChain & Tavily
ADK is designed to be extensible and plays well with other popular AI frameworks. We can easily integrate tools built for libraries like LangChain. We’ll use ADK’s LangchainTool wrapper to incorporate the Tavily search API, a service designed specifically for AI agents to get up-to-date search results.
Setup for Tavily
Install Dependencies: Add the necessary LangChain and Tavily libraries using uv:
bash
# Run this in your activated virtual environmentuv add langchain_community tavily-python
# .env file content (add this line)TAVILY_API_KEY="PASTE_YOUR_TAVILY_API_KEY_HERE"
Make sure your application loads environment variables from this file (ADK often does this automatically, or you can use a library like python-dotenv).
Using the LangchainTool Wrapper
Now, let’s integrate Tavily into our agent code:
Import necessary classes in your agent.py:
python
# Add these importsfrom google.adk.tools.langchain_tool import LangchainToolfrom langchain_community.tools import TavilySearchResultsimport os # To potentially check for the API key
Instantiate and Wrap the Tool: Create an instance of the LangChain tool and wrap it for ADK:
python
# Instantiate the LangChain Tavily tool# You can configure options like max_resultstavily_search_tool_instance = TavilySearchResults( max_results=3, include_answer=True # Ask Tavily to provide a direct answer if possible)# Wrap it with ADK's LangchainTooladk_tavily_tool = LangchainTool(tool=tavily_search_tool_instance)print("Tavily search tool wrapped for ADK.")
Update Your Agent Definition
Finally, modify your agent definition to include the new tool and update its instructions so it knows when to use it:
python
# Modify your existing root_agent definition in agent.pyroot_agent = Agent( name="my_first_adk_agent", # Or maybe rename to "research_agent" model="gemini-2.0-flash-exp", description="A helpful assistant that can search the web for current information.", instruction="""You are a friendly and helpful assistant.If the user asks for information that might require up-to-date details, recent events, or searching the web, use the 'TavilySearchResults' tool.Otherwise, answer directly based on your knowledge.Remember information provided earlier in the conversation using session state.""", # Added mention of memory # Add the wrapped Tavily tool to the list tools=[adk_tavily_tool] # Add any other tools like 'remember_something' here too if desired)print(f"Agent '{root_agent.name}' updated with Tavily tool.")# Ensure your Runner is using this updated 'root_agent' instancerunner = Runner( agent=root_agent, # Make sure this uses the updated agent app_name=APP_NAME, session_service=session_service)
Now, when you run your agent and ask a question requiring current information (e.g., “What’s the latest news about AI agents?” or “What’s the weather in London right now?”), the agent, guided by its instructions, should recognize the need to use the TavilySearchResults tool to fetch and provide a relevant, up-to-date answer.
Complete code:
python
# agent_module/agent.pyimport osimport asynciofrom dotenv import load_dotenvfrom google.adk.sessions import InMemorySessionServicefrom google.adk.runners import Runnerfrom google.adk.agents import Agentfrom google.adk.tools.tool_context import ToolContextfrom google.adk.tools.langchain_tool import LangchainToolfrom langchain_community.tools import TavilySearchResultsfrom google.genai import types # For creating message Content/Parts# --- Load Environment Variables ---# Ensure you have a .env file in the parent directory with your keys# Requires: pip install python-dotenv (or uv install python-dotenv)load_dotenv()# Check if keys are loaded (optional but good practice)if not os.getenv("GOOGLE_API_KEY"): print("Warning: GOOGLE_API_KEY environment variable not set.")if not os.getenv("TAVILY_API_KEY"): print("Warning: TAVILY_API_KEY environment variable not set.")# --- Tool Definitions ---def remember_something(data_to_remember: str, tool_context: ToolContext) -> dict: """ Reads previous data from state ('user_data') and saves new data to the same state key. Used to demonstrate agent memory. """ print("--- Tool: remember_something called ---") # Read from state (use .get() for safety, provide default) previous_data = tool_context.state.get("user_data", "nothing") print(f"--- Tool: Found previous data in state['user_data']: '{previous_data}' ---") # Write new data to state tool_context.state["user_data"] = data_to_remember print(f"--- Tool: Saved new data to state['user_data']: '{data_to_remember}' ---") return {"status": "success", "message": f"Okay, I've noted down '{data_to_remember}'. Before that, I had noted '{previous_data}'."}# --- Tool Setup ---# Instantiate the LangChain Tavily tooltavily_search_tool_instance = TavilySearchResults( max_results=3, # Limit the number of search results include_answer=True # Ask Tavily to provide a direct answer if possible)# Wrap it with ADK's LangchainTool for compatibilityadk_tavily_tool = LangchainTool(tool=tavily_search_tool_instance)print("Tavily search tool wrapped for ADK.")# --- Agent Definition ---root_agent = Agent( name="my_adk_agent_with_tools", # Ensure this model is available with your GOOGLE_API_KEY (AI Studio) model="gemini-2.0-flash-exp", # Or gemini-1.0-pro, etc. description="A helpful assistant that can search the web using Tavily and remember user data.", instruction="""You are a friendly and helpful assistant.1. If the user asks for information that might require up-to-date details, recent events, or specific web searching, use the 'TavilySearchResults' tool.2. If the user asks you to remember something specific, use the 'remember_something' tool to save it. Also mention what was previously remembered if anything.3. Use information remembered earlier (from session state) if relevant to the current query.4. Otherwise, answer directly based on your general knowledge.""", # Include both tools the agent can use tools=[adk_tavily_tool, remember_something])print(f"Agent '{root_agent.name}' defined with tools.")# --- Session and Runner Setup ---APP_NAME = "my_first_adk_app"USER_ID = "user_dev_1" # Identifier for the userSESSION_ID = "session_main_1" # Identifier for this specific conversation# Create the Session Service instance (stores memory)session_service = InMemorySessionService()print("Session Service created.")# Create the specific session for this user and appsession = session_service.create_session( app_name=APP_NAME, user_id=USER_ID, session_id=SESSION_ID # state={"user_data": "initial value"} # Optional: prime the state memory)print(f"Session '{SESSION_ID}' created.")# Create the Runner instance (orchestrates agent execution)runner = Runner( agent=root_agent, app_name=APP_NAME, session_service=session_service)print(f"Runner created for agent '{root_agent.name}'.")# --- Interaction Logic ---async def run_conversation(): """Runs a simple interactive loop to chat with the agent.""" print("\n--- Starting Conversation (type 'quit' to exit) ---") while True: try: user_query = input("You: ") if user_query.lower() == 'quit': print("Exiting conversation.") break # Prepare the user message in ADK format content = types.Content(role='user', parts=[types.Part(text=user_query)]) final_response_text = "Agent did not produce a final response." # Use run_async to process the message and get events async for event in runner.run_async(user_id=USER_ID, session_id=SESSION_ID, new_message=content): # You can uncomment below to see all events (tool calls, etc.) # print(f" [Event] Author: {event.author}, Type: {type(event).__name__}, Final: {event.is_final_response()}") # Look for the final response event if event.is_final_response(): if event.content and event.content.parts: # Assuming text response in the first part final_response_text = event.content.parts[0].text elif event.actions and event.actions.escalate: final_response_text = f"Agent escalated: {event.error_message or 'No specific message.'}" break # Stop processing events for this turn print(f"Agent: {final_response_text}") # Optional: Print current state for debugging # current_session = session_service.get_session(APP_NAME, USER_ID, SESSION_ID) # print(f" (Debug State: {current_session.state})") except Exception as e: print(f"An error occurred: {e}")# --- Run the Application ---if __name__ == "__main__": # Check dependencies are installed try: import langchain_community import tavily except ImportError: print("Error: Missing dependencies. Please run:") print("uv add langchain_community tavily-python python-dotenv") exit() # Run the asynchronous conversation loop try: asyncio.run(run_conversation()) except RuntimeError as e: # Handle specific error when running asyncio.run in an already running loop (like Jupyter/Colab) if "cannot be called from a running event loop" in str(e): print("\nCannot start a new event loop. If in a Jupyter Notebook, run:") print("await run_conversation()") else: raise e # Re-raise other runtime errors
Run in terminal
You can use terminal to run the tool, with:
bash
uv run python -m agent_module.agent
This will provide something like:
bash
Tavily search tool wrapped for ADK.Agent 'my_adk_agent_with_tools' defined with tools.Session Service created.Session 'session_main_1' created.Runner created for agent 'my_adk_agent_with_tools'.<frozen runpy>:128: RuntimeWarning: 'agent_module.agent' found in sys.modules after import of package 'agent_module', but prior to execution of 'agent_module.agent'; this may result in unpredictable behaviourTavily search tool wrapped for ADK.Agent 'my_adk_agent_with_tools' defined with tools.Session Service created.Session 'session_main_1' created.Runner created for agent 'my_adk_agent_with_tools'.--- Starting Conversation (type 'quit' to exit) ---You: what are the latest news on tech in april 2025Warning: there are non-text parts in the response: ['function_call'],returning concatenated text result from text parts,check out the non text parts for full response from model.Agent: In April 2025, some of the biggest tech news included: Apple releasing new software updates, Amazon developing an AI model to enhance customer experience, Google announcing a quantum computing breakthrough, and NVIDIA advancing confidential computing on its Blackwell infrastructure.You:
Conclusion
Congratulations! You’ve successfully built your first AI agent using the Google Agent Development Kit. We’ve walked through the essential steps:
Setting up your development environment using uv for package management.
Connecting your agent to the powerful Gemini models via a Google AI Studio API key.
Defining a basic agent structure with its core identity and instructions.
Adding memory using Session State, enabling contextual conversations.
Integrating a third-party tool (Tavily Search) using ADK’s wrappers, giving your agent access to real-time web information.
This journey demonstrates the flexibility and power of ADK. You’ve seen how quickly you can get a basic agent running and how easily you can enhance it with memory and external tools. ADK provides the building blocks for everything from simple bots to complex, multi-agent systems capable of sophisticated orchestration and leveraging a rich tool ecosystem.
Where to Go Next?
Your first agent is just the beginning! Here are some ideas to continue your exploration of ADK:
Explore the Tutorials: Dive deeper into concepts like multi-agent delegation, advanced state management, and safety callbacks by following the official ADK tutorials.
Integrate Real APIs: Replace mock data or simple tools with real-world APIs for weather, finance, or other services.
Add More Tools: Experiment with other built-in tools like Code Execution or Vertex AI Search, or integrate more tools from LangChain or CrewAi.
Build Agent Teams: Design systems with multiple specialized agents collaborating on tasks .
Persistent Memory: Investigate storing session state more permanently using databases instead of just in memory.