
Agent Memory
- 31 installs
- 179 repo stars
- Updated July 28, 2026
- databricks/app-templates
agent-memory configures Lakebase persistent memory for Databricks agents.
About
The agent-memory skill guides setting up persistent agent memory using Databricks Lakebase in app-templates agents. It covers provisioning Lakebase databases, wiring memory backends in agent configuration, and databricks.yml resource declarations with permission grants required at deploy time. Works with the broader app-templates quickstart and add-tools skills for bundle deploy and runtime permissions. Use when agents need durable conversation or state memory on Databricks infrastructure.
- Lakebase-backed persistent memory for Databricks agents.
- databricks.yml resource and permission configuration.
- Integrates with app-templates agent LangGraph setup.
- Requires bundle deploy for permission grants.
- Part of Databricks app-templates agent toolkit.
Agent Memory by the numbers
- 31 all-time installs (skills.sh)
- Ranked #9,164 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
agent-memory capabilities & compatibility
- Capabilities
- agent memory on lakebase
- Works with
- databricks
- Use cases
- memory · orchestration
What agent-memory says it does
agent-memory
npx skills add https://github.com/databricks/app-templates --skill agent-memoryAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 31 |
|---|---|
| repo stars | ★ 179 |
| Last updated | July 28, 2026 |
| Repository | databricks/app-templates ↗ |
How do I add persistent memory to a Databricks agent?
Configure agent memory storage with Databricks Lakebase for LangGraph agents.
Who is it for?
Teams building Databricks LangGraph agents with memory.
Skip if: Skip for stateless agents without persistence needs.
When should I use this skill?
User configures agent memory on Databricks app-templates.
What you get
Agent with Lakebase memory resources and deploy-time permissions.
Files
Adding Memory to Your Agent
Note: This template does not include memory by default. Use this skill to add memory capabilities. For a pre-configured memory template, see:
- agent-langgraph-advanced - Short-term and long-term memory with long-running background tasks
Memory Types
| Type | Use Case | Storage | Identifier |
|---|---|---|---|
| Short-term | Conversation history within a session | AsyncCheckpointSaver | thread_id |
| Long-term | User facts that persist across sessions | AsyncDatabricksStore | user_id |
Prerequisites
1. Add memory dependency to pyproject.toml:
dependencies = [
"databricks-langchain[memory]",
]Then run uv sync
2. Configure Lakebase - See lakebase-setup skill for:
- Creating/configuring Lakebase instance
- Initializing tables (CRITICAL first-time step)
---
Quick Setup Summary
Adding memory requires changes to 4 files:
| File | What to Add |
|---|---|
pyproject.toml | Memory dependency |
.env | Lakebase env vars (for local dev) |
databricks.yml | Lakebase database resource + env vars in config block |
agent_server/agent.py | Memory tools and AsyncDatabricksStore |
---
Key Principles
Before implementing memory, understand these patterns from the production implementation.
1. Factory Function Pattern
Memory tools should be returned from a factory function, not defined as standalone functions:
def memory_tools():
@tool
async def get_user_memory(query: str, config: RunnableConfig) -> str:
...
@tool
async def save_user_memory(memory_key: str, memory_data_json: str, config: RunnableConfig) -> str:
...
@tool
async def delete_user_memory(memory_key: str, config: RunnableConfig) -> str:
...
return [get_user_memory, save_user_memory, delete_user_memory]2. User ID Extraction
Extract user_id from the request, checking custom_inputs first. Return None (not a default) to let the caller decide:
def get_user_id(request: ResponsesAgentRequest) -> Optional[str]:
custom_inputs = dict(request.custom_inputs or {})
if "user_id" in custom_inputs:
return custom_inputs["user_id"]
if request.context and getattr(request.context, "user_id", None):
return request.context.user_id
return None3. Separate Error Handling
Check user_id and store separately with distinct error messages:
user_id = config.get("configurable", {}).get("user_id")
if not user_id:
return "Memory not available - no user_id provided."
store: Optional[BaseStore] = config.get("configurable", {}).get("store")
if not store:
return "Memory not available - store not configured."4. JSON Validation for Save
Validate JSON input before storing - the LLM may pass invalid JSON:
try:
memory_data = json.loads(memory_data_json)
if not isinstance(memory_data, dict):
return f"Failed: memory_data must be a JSON object, not {type(memory_data).__name__}"
await store.aput(namespace, memory_key, memory_data)
except json.JSONDecodeError as e:
return f"Failed to save memory: Invalid JSON - {e}"5. Pass Store via RunnableConfig
Pass the store through config, not as a function parameter:
config = {"configurable": {"user_id": user_id, "store": store}}
# Tools access via: config.get("configurable", {}).get("store")---
Complete Example
A full implementation is available in this skill's examples folder:
# Copy to your project
cp .claude/skills/agent-memory/examples/memory_tools.py agent_server/See examples/memory_tools.py for production-ready code including all helper functions.
Production Reference
For implementations in the pre-built templates:
| File | Description |
|---|---|
| `agent-langgraph-advanced/agent_server/utils_memory.py` | Memory tools factory, helpers, error handling |
| `agent-langgraph-advanced/agent_server/agent.py` | Integration with agent, store initialization |
Key functions:
memory_tools()- Factory returning get/save/delete toolsget_user_id()- Extract user_id from requestresolve_lakebase_instance_name()- Handle hostname vs instance nameget_lakebase_access_error_message()- Helpful error messages
---
Configuration Files
Step 1: databricks.yml (Lakebase Resource)
Add the Lakebase database resource to your app:
resources:
apps:
agent_langgraph:
name: "your-app-name"
source_code_path: ./
resources:
# ... other resources (experiment, UC functions, etc.) ...
# Lakebase instance for long-term memory
- name: 'database'
database:
instance_name: '<your-lakebase-instance-name>'
database_name: 'databricks_postgres'
permission: 'CAN_CONNECT_AND_CREATE'Important: The name: 'database' must match the value_from reference in the databricks.yml config.env block.
Step 2: databricks.yml config block (Environment Variables)
Add the Lakebase environment variables to your app's config.env in databricks.yml:
config:
command: ["uv", "run", "start-app"]
env:
# ... other env vars ...
# Lakebase instance name (resolved from database resource)
- name: LAKEBASE_INSTANCE_NAME
value_from: "database"
# Embedding configuration
- name: EMBEDDING_ENDPOINT
value: "databricks-gte-large-en"
- name: EMBEDDING_DIMS
value: "1024"Important: LAKEBASE_INSTANCE_NAME uses value_from: "database" to resolve from the database resource at deploy time.
Step 3: .env (Local Development)
# Lakebase configuration for long-term memory
LAKEBASE_INSTANCE_NAME=<your-instance-name>
EMBEDDING_ENDPOINT=databricks-gte-large-en
EMBEDDING_DIMS=1024---
Integration Example
Minimal example showing how to integrate memory into your streaming function:
from agent_server.utils_memory import memory_tools, get_user_id
@stream()
async def streaming(request: ResponsesAgentRequest):
user_id = get_user_id(request)
async with AsyncDatabricksStore(
instance_name=LAKEBASE_INSTANCE_NAME,
embedding_endpoint=EMBEDDING_ENDPOINT,
embedding_dims=EMBEDDING_DIMS,
) as store:
await store.setup() # Creates tables if needed
tools = await mcp_client.get_tools() + memory_tools()
config = {"configurable": {"user_id": user_id, "store": store}}
agent = create_react_agent(model=model, tools=tools)
async for event in agent.astream(messages, config):
yield event---
Initialize Tables and Deploy
Initialize Lakebase Tables (First Time Only)
Before deploying, initialize the tables locally:
uv run python -c "$(cat <<'EOF'
import asyncio
from databricks_langchain import AsyncDatabricksStore
async def setup():
async with AsyncDatabricksStore(
instance_name="<your-instance-name>",
embedding_endpoint="databricks-gte-large-en",
embedding_dims=1024,
) as store:
await store.setup()
print("Tables created!")
asyncio.run(setup())
EOF
)"Deploy
After initializing tables, deploy your agent. See deploy skill for full instructions.
---
Short-Term Memory
For conversation history within a session, use AsyncCheckpointSaver:
from databricks_langchain import AsyncCheckpointSaver
async with AsyncCheckpointSaver(instance_name=LAKEBASE_INSTANCE_NAME) as checkpointer:
agent = create_react_agent(
model=model,
tools=tools,
checkpointer=checkpointer,
)
config = {"configurable": {"thread_id": thread_id}}
async for event in agent.astream(messages, config):
yield eventSee the agent-langgraph-advanced template for a complete implementation.
---
Testing Memory
Test Locally
# Start the server
uv run start-app
# Save a memory
curl -X POST http://localhost:8000/invocations \
-H "Content-Type: application/json" \
-d '{
"input": [{"role": "user", "content": "Remember that I am on the shipping team"}],
"custom_inputs": {"user_id": "alice@example.com"}
}'
# Recall the memory
curl -X POST http://localhost:8000/invocations \
-H "Content-Type: application/json" \
-d '{
"input": [{"role": "user", "content": "What team am I on?"}],
"custom_inputs": {"user_id": "alice@example.com"}
}'
# Delete a memory
curl -X POST http://localhost:8000/invocations \
-H "Content-Type: application/json" \
-d '{
"input": [{"role": "user", "content": "Forget what team I am on"}],
"custom_inputs": {"user_id": "alice@example.com"}
}'Test Deployed App
# Get OAuth token (PATs don't work for apps)
TOKEN=$(databricks auth token --host <workspace-url> | jq -r '.access_token')
# Test memory save
curl -X POST https://<app-url>/invocations \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": [{"role": "user", "content": "Remember I prefer detailed explanations"}],
"custom_inputs": {"user_id": "alice@example.com"}
}'---
First-Time Setup Checklist
- [ ] Added
databricks-langchain[memory]topyproject.toml - [ ] Run
uv syncto install dependencies - [ ] Created or identified Lakebase instance
- [ ] Added Lakebase env vars to
.env(for local dev) - [ ] Added
databaseresource todatabricks.yml - [ ] Added
LAKEBASE_INSTANCE_NAMEtodatabricks.ymlconfig.env - [ ] Initialized tables locally by running
await store.setup() - [ ] Deployed with
databricks bundle deploy && databricks bundle run
---
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| "embedding_dims is required" | Missing parameter | Add embedding_dims=1024 to AsyncDatabricksStore |
| "relation 'store' does not exist" | Tables not created | Run await store.setup() locally first |
| "Unable to resolve Lakebase instance 'None'" | Missing env var | Check LAKEBASE_INSTANCE_NAME in databricks.yml config.env |
| "permission denied for table store" | Missing grants | Add database resource to databricks.yml |
| "Memory not available - no user_id" | Missing user_id | Pass custom_inputs.user_id in request |
| Memory not persisting | Different user_ids | Use consistent user_id across requests |
| App not updated after deploy | Forgot to run bundle | Run databricks bundle run agent_langgraph after deploy |
---
Pre-Built Memory Templates
For fully configured implementations without manual setup:
| Template | Memory Type | Key Features |
|---|---|---|
| agent-langgraph-advanced | Short-term + Long-term | AsyncCheckpointSaver, AsyncDatabricksStore, memory tools |
---
Next Steps
- Configure Lakebase: see lakebase-setup skill
- Test locally: see run-locally skill
- Deploy: see deploy skill
"""Memory tools for LangGraph agents.
This module provides tools for managing user long-term memory using
Databricks Lakebase. Copy this file to your agent_server/ directory.
Usage:
from agent_server.memory_tools import memory_tools, get_user_id
# In your streaming function:
user_id = get_user_id(request)
tools = await mcp_client.get_tools() + memory_tools()
config = {"configurable": {"user_id": user_id, "store": store}}
"""
import json
import logging
import os
from typing import Optional
from databricks.sdk import WorkspaceClient
from langchain_core.runnables import RunnableConfig
from langchain_core.tools import tool
from langgraph.store.base import BaseStore
from mlflow.types.responses import ResponsesAgentRequest
# -----------------------------------------------------------------------------
# Helper Functions
# -----------------------------------------------------------------------------
def get_user_id(request: ResponsesAgentRequest) -> Optional[str]:
"""Extract user_id from request context or custom inputs.
Checks custom_inputs first (for API calls), then request.context
(for Databricks Apps with OBO authentication).
Returns None if no user_id found - let the caller decide the fallback.
"""
custom_inputs = dict(request.custom_inputs or {})
if "user_id" in custom_inputs:
return custom_inputs["user_id"]
if request.context and getattr(request.context, "user_id", None):
return request.context.user_id
return None
def _is_lakebase_hostname(value: str) -> bool:
"""Check if the value looks like a Lakebase hostname rather than an instance name."""
return ".database." in value and value.endswith(".com")
def resolve_lakebase_instance_name(
instance_name: str, workspace_client: Optional[WorkspaceClient] = None
) -> str:
"""Resolve a Lakebase instance name from a hostname if needed.
If the input is a hostname (e.g., from Databricks Apps value_from resolution),
this will resolve it to the actual instance name by listing database instances.
Args:
instance_name: Either an instance name or a hostname
workspace_client: Optional WorkspaceClient to use for resolution
Returns:
The resolved instance name
Raises:
ValueError: If the hostname cannot be resolved to an instance name
"""
if not _is_lakebase_hostname(instance_name):
return instance_name
client = workspace_client or WorkspaceClient()
hostname = instance_name
try:
instances = list(client.database.list_database_instances())
except Exception as exc:
raise ValueError(
f"Unable to list database instances to resolve hostname '{hostname}'. "
"Ensure you have access to database instances."
) from exc
for instance in instances:
rw_dns = getattr(instance, "read_write_dns", None)
ro_dns = getattr(instance, "read_only_dns", None)
if hostname in (rw_dns, ro_dns):
resolved_name = getattr(instance, "name", None)
if not resolved_name:
raise ValueError(
f"Found matching instance for hostname '{hostname}' "
"but instance name is not available."
)
logging.info(f"Resolved Lakebase hostname '{hostname}' to instance name '{resolved_name}'")
return resolved_name
raise ValueError(
f"Unable to find database instance matching hostname '{hostname}'. "
"Ensure the hostname is correct and the instance exists."
)
def _is_databricks_app_env() -> bool:
"""Check if running in a Databricks App environment."""
return bool(os.getenv("DATABRICKS_APP_NAME"))
def get_lakebase_access_error_message(lakebase_instance_name: str) -> str:
"""Generate a helpful error message for Lakebase access issues."""
if _is_databricks_app_env():
app_name = os.getenv("DATABRICKS_APP_NAME")
return (
f"Failed to connect to Lakebase instance '{lakebase_instance_name}'. "
f"The App Service Principal for '{app_name}' may not have access.\n\n"
"To fix this:\n"
"1. Go to the Databricks UI and navigate to your app\n"
"2. Click 'Edit' → 'App resources' → 'Add resource'\n"
"3. Add your Lakebase instance as a resource\n"
"4. Grant the necessary permissions on your Lakebase instance."
)
else:
return (
f"Failed to connect to Lakebase instance '{lakebase_instance_name}'. "
"Please verify:\n"
"1. The instance name is correct\n"
"2. You have the necessary permissions to access the instance\n"
"3. Your Databricks authentication is configured correctly"
)
# -----------------------------------------------------------------------------
# Memory Tools Factory
# -----------------------------------------------------------------------------
def memory_tools():
"""Factory function returning memory tools for the agent.
Returns a list of tools that can be added to your agent:
- get_user_memory: Search for relevant information from long-term memory
- save_user_memory: Save information to long-term memory
- delete_user_memory: Delete a specific memory
Usage:
tools = await mcp_client.get_tools() + memory_tools()
config = {"configurable": {"user_id": user_id, "store": store}}
"""
@tool
async def get_user_memory(query: str, config: RunnableConfig) -> str:
"""Search for relevant information about the user from long-term memory.
Use this to recall preferences, past interactions, or other saved information.
Args:
query: What to search for in the user's memories
"""
user_id = config.get("configurable", {}).get("user_id")
if not user_id:
return "Memory not available - no user_id provided."
store: Optional[BaseStore] = config.get("configurable", {}).get("store")
if not store:
return "Memory not available - store not configured."
namespace = ("user_memories", user_id.replace(".", "-"))
results = await store.asearch(namespace, query=query, limit=5)
if not results:
return "No memories found for this user."
memory_items = [f"- [{item.key}]: {json.dumps(item.value)}" for item in results]
return f"Found {len(results)} relevant memories:\n" + "\n".join(memory_items)
@tool
async def save_user_memory(memory_key: str, memory_data_json: str, config: RunnableConfig) -> str:
"""Save information about the user to long-term memory.
Use this to remember user preferences, important details, or other
information that should persist across conversations.
Args:
memory_key: A short descriptive key (e.g., "preferred_name", "team", "interests")
memory_data_json: JSON object to save (e.g., '{"value": "engineering"}')
"""
user_id = config.get("configurable", {}).get("user_id")
if not user_id:
return "Cannot save memory - no user_id provided."
store: Optional[BaseStore] = config.get("configurable", {}).get("store")
if not store:
return "Cannot save memory - store not configured."
namespace = ("user_memories", user_id.replace(".", "-"))
try:
memory_data = json.loads(memory_data_json)
if not isinstance(memory_data, dict):
return f"Failed: memory_data must be a JSON object, not {type(memory_data).__name__}"
await store.aput(namespace, memory_key, memory_data)
return f"Successfully saved memory '{memory_key}' for user."
except json.JSONDecodeError as e:
return f"Failed to save memory: Invalid JSON - {e}"
@tool
async def delete_user_memory(memory_key: str, config: RunnableConfig) -> str:
"""Delete a specific memory from the user's long-term memory.
Use this when the user asks to forget something or correct stored information.
Args:
memory_key: The key of the memory to delete (e.g., "preferred_name", "team")
"""
user_id = config.get("configurable", {}).get("user_id")
if not user_id:
return "Cannot delete memory - no user_id provided."
store: Optional[BaseStore] = config.get("configurable", {}).get("store")
if not store:
return "Cannot delete memory - store not configured."
namespace = ("user_memories", user_id.replace(".", "-"))
await store.adelete(namespace, memory_key)
return f"Successfully deleted memory '{memory_key}' for user."
return [get_user_memory, save_user_memory, delete_user_memory]
Related skills
FAQ
What does agent-memory do?
agent-memory configures Lakebase persistent memory for Databricks agents.
When should I use agent-memory?
User configures agent memory on Databricks app-templates.
Is this skill safe to install?
Review the Security Audits panel on this page before installing in production.