From 1a3141e2c783f1b5a3f8b5621f6a84bc47ad7ade Mon Sep 17 00:00:00 2001 From: Richmond Alake Date: Mon, 8 Dec 2025 21:55:05 +0000 Subject: [PATCH 1/2] Fix pre-commit prettier config and update memory context engineering notebook --- .pre-commit-config.yaml | 1 + .../memory_context_engineering_agents.ipynb | 464 ++++++++++++++---- 2 files changed, 381 insertions(+), 84 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 60062d1f..7ca8bd42 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -22,6 +22,7 @@ repos: (?x)^( .*\.min\.js| .*\.min\.css| + .*\.ipynb| node_modules/.*| .*\.lock )$ diff --git a/notebooks/memory_context_engineering_agents.ipynb b/notebooks/memory_context_engineering_agents.ipynb index 681f5369..cafeaf52 100644 --- a/notebooks/memory_context_engineering_agents.ipynb +++ b/notebooks/memory_context_engineering_agents.ipynb @@ -18,7 +18,7 @@ "[![Open in Colab](https://img.shields.io/badge/Open%20in-Colab-F9AB00?style=flat-square&logo=googlecolab)](https://colab.research.google.com/github/oracle-devrel/oracle-ai-developer-hub/blob/main/notebooks/memory_context_engineering_agents.ipynb)\n", "\n", "In this notebook, you'll learn how to engineer memory systems that give AI agents the ability to remember, learn, and adapt across conversations. \n", - "Moving beyond simple RAG, we implement a complete **Memory Layer** with six distinct memory typesβ€”each serving a specific cognitive function." + "Moving beyond simple RAG, we implement a complete **Memory Manager** with six distinct memory typesβ€”each serving a specific cognitive function." ] }, { @@ -75,12 +75,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "b243d181", "metadata": {}, "outputs": [], "source": [ - "! pip install -qU langchain-oracledb sentence-transformers langchain-openai langchain langchain-anthropic tavily-python" + "! pip install -qU langchain-oracledb sentence-transformers langchain-openai langchain tavily-python" ] }, { @@ -88,7 +88,9 @@ "id": "393bf345", "metadata": {}, "source": [ - "# Local Installation of Oracle AI Database via Docker [Memory Core]" + "# Local Installation of Oracle AI Database via Docker [Memory Core]\n", + "\n", + "--------" ] }, { @@ -155,11 +157,231 @@ "> 🧩 **Fix:** \n", "> - Remove the existing container: \n", "> ```bash\n", - "> docker rm oracle-full\n", + "> docker rm oracle-free\n", "> ``` \n", "> - Then re-run your Docker command from **Step 3** to start a new container.\n" ] }, + { + "cell_type": "markdown", + "id": "88463dc3", + "metadata": {}, + "source": [ + "### πŸš€ One-Click Database Setup\n", + "\n", + "The cell below handles **everything automatically**:\n", + "- βœ… Checks if Docker is running\n", + "- βœ… Checks if Oracle container exists and is healthy\n", + "- βœ… Waits for database to be ready (with progress indicator)\n", + "- βœ… Fixes the listener for ARM Macs (Apple Silicon)\n", + "- βœ… Creates the VECTOR user with proper privileges\n", + "- βœ… Tests the connection\n", + "\n", + "**Just run the cell below and wait for the βœ… success message!**\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fb7ae8e6", + "metadata": {}, + "outputs": [], + "source": [ + "import subprocess\n", + "import time\n", + "import sys\n", + "\n", + "def setup_oracle_database(container_name=\"oracle-free\", vector_password=\"VectorPwd_2025\"):\n", + " \"\"\"\n", + " Complete Oracle Database setup - handles everything in one call.\n", + " \n", + " This function:\n", + " 1. Checks Docker is running\n", + " 2. Verifies container exists and is healthy\n", + " 3. Waits for database to be ready\n", + " 4. Fixes listener for ARM Macs\n", + " 5. Creates VECTOR user\n", + " 6. Tests connection\n", + " \"\"\"\n", + " print(\"=\" * 60)\n", + " print(\"πŸš€ ORACLE DATABASE SETUP\")\n", + " print(\"=\" * 60)\n", + " \n", + " # Step 1: Check Docker\n", + " print(\"\\n[1/6] Checking Docker...\")\n", + " try:\n", + " result = subprocess.run(['docker', 'info'], capture_output=True, text=True, timeout=10)\n", + " if result.returncode != 0:\n", + " print(\" ❌ Docker is not running!\")\n", + " print(\" πŸ’‘ Start Docker Desktop and try again.\")\n", + " return False\n", + " print(\" βœ… Docker is running\")\n", + " except FileNotFoundError:\n", + " print(\" ❌ Docker not found! Please install Docker.\")\n", + " return False\n", + " except subprocess.TimeoutExpired:\n", + " print(\" ❌ Docker is not responding. Please restart Docker.\")\n", + " return False\n", + " \n", + " # Step 2: Check container\n", + " print(f\"\\n[2/6] Checking container '{container_name}'...\")\n", + " result = subprocess.run(\n", + " ['docker', 'ps', '-a', '--filter', f'name={container_name}', '--format', '{{.Status}}'],\n", + " capture_output=True, text=True\n", + " )\n", + " status = result.stdout.strip()\n", + " \n", + " if not status:\n", + " print(f\" ❌ Container '{container_name}' not found!\")\n", + " print(\" πŸ’‘ Run the docker run command from the previous cell first.\")\n", + " return False\n", + " elif \"Up\" not in status:\n", + " print(f\" ⚠️ Container exists but not running. Starting...\")\n", + " subprocess.run(['docker', 'start', container_name], capture_output=True)\n", + " time.sleep(5)\n", + " \n", + " print(f\" βœ… Container is running\")\n", + " \n", + " # Step 3: Wait for database ready\n", + " print(\"\\n[3/6] Waiting for database to be ready...\")\n", + " print(\" (This can take 2-5 minutes on Apple Silicon Macs)\")\n", + " \n", + " max_wait = 300 # 5 minutes\n", + " check_interval = 10\n", + " elapsed = 0\n", + " \n", + " while elapsed < max_wait:\n", + " # Check container health\n", + " result = subprocess.run(\n", + " ['docker', 'ps', '--filter', f'name={container_name}', '--format', '{{.Status}}'],\n", + " capture_output=True, text=True\n", + " )\n", + " if \"healthy\" in result.stdout.lower():\n", + " print(f\"\\n βœ… Database is healthy!\")\n", + " break\n", + " \n", + " # Also check logs for ready message\n", + " logs = subprocess.run(\n", + " ['docker', 'logs', '--tail', '20', container_name],\n", + " capture_output=True, text=True\n", + " )\n", + " if \"DATABASE IS READY TO USE\" in logs.stdout:\n", + " print(f\"\\n βœ… Database is ready!\")\n", + " break\n", + " \n", + " # Progress indicator\n", + " dots = \".\" * ((elapsed // check_interval) % 4 + 1)\n", + " print(f\"\\r ⏳ Waiting{dots.ljust(5)} ({elapsed}s elapsed)\", end=\"\", flush=True)\n", + " time.sleep(check_interval)\n", + " elapsed += check_interval\n", + " else:\n", + " print(f\"\\n ❌ Timeout waiting for database. Check 'docker logs {container_name}'\")\n", + " return False\n", + " \n", + " # Step 4: Fix listener (for ARM Macs)\n", + " print(\"\\n[4/6] Configuring listener...\")\n", + " \n", + " # Fix listener.ora\n", + " subprocess.run(\n", + " ['docker', 'exec', container_name, 'bash', '-c',\n", + " \"sed -i 's/HOST = [^)]*)/HOST = 0.0.0.0)/g' /opt/oracle/product/26ai/dbhomeFree/network/admin/listener.ora\"],\n", + " capture_output=True\n", + " )\n", + " \n", + " # Restart listener\n", + " subprocess.run(['docker', 'exec', container_name, 'lsnrctl', 'stop'], capture_output=True)\n", + " start_result = subprocess.run(\n", + " ['docker', 'exec', container_name, 'lsnrctl', 'start'],\n", + " capture_output=True, text=True\n", + " )\n", + " \n", + " if \"Listening on\" not in start_result.stdout:\n", + " print(\" ❌ Failed to start listener\")\n", + " return False\n", + " \n", + " # Register services\n", + " subprocess.run(\n", + " ['docker', 'exec', container_name, 'bash', '-c',\n", + " \"export ORACLE_SID=FREE && sqlplus -s / as sysdba <<< 'ALTER SYSTEM REGISTER;'\"],\n", + " capture_output=True\n", + " )\n", + " print(\" βœ… Listener configured and running\")\n", + " \n", + " # Step 5: Create VECTOR user\n", + " print(\"\\n[5/6] Creating VECTOR user...\")\n", + " \n", + " create_user_sql = f'''\n", + " DECLARE\n", + " user_count NUMBER;\n", + " BEGIN\n", + " SELECT COUNT(*) INTO user_count FROM all_users WHERE username = 'VECTOR';\n", + " IF user_count = 0 THEN\n", + " EXECUTE IMMEDIATE 'CREATE USER VECTOR IDENTIFIED BY {vector_password}';\n", + " EXECUTE IMMEDIATE 'GRANT CONNECT, RESOURCE, CREATE SESSION TO VECTOR';\n", + " EXECUTE IMMEDIATE 'GRANT UNLIMITED TABLESPACE TO VECTOR';\n", + " EXECUTE IMMEDIATE 'GRANT CREATE TABLE, CREATE SEQUENCE, CREATE VIEW TO VECTOR';\n", + " DBMS_OUTPUT.PUT_LINE('CREATED');\n", + " ELSE\n", + " DBMS_OUTPUT.PUT_LINE('EXISTS');\n", + " END IF;\n", + " END;\n", + " /\n", + " '''\n", + " \n", + " result = subprocess.run(\n", + " ['docker', 'exec', container_name, 'bash', '-c',\n", + " f\"export ORACLE_SID=FREE && sqlplus -s / as sysdba <<< \\\"ALTER SESSION SET CONTAINER = FREEPDB1; {create_user_sql}\\\"\"],\n", + " capture_output=True, text=True\n", + " )\n", + " \n", + " if \"ORA-\" in result.stdout:\n", + " print(f\" ⚠️ Warning: {result.stdout}\")\n", + " else:\n", + " print(\" βœ… VECTOR user ready\")\n", + " \n", + " # Step 6: Test connection\n", + " print(\"\\n[6/6] Testing connection...\")\n", + " try:\n", + " import oracledb\n", + " conn = oracledb.connect(\n", + " user=\"VECTOR\",\n", + " password=vector_password,\n", + " dsn=\"127.0.0.1:1521/FREEPDB1\"\n", + " )\n", + " with conn.cursor() as cur:\n", + " cur.execute(\"SELECT 1 FROM dual\")\n", + " cur.fetchone()\n", + " conn.close()\n", + " print(\" βœ… Connection successful!\")\n", + " except Exception as e:\n", + " print(f\" ❌ Connection failed: {e}\")\n", + " return False\n", + " \n", + " # Success!\n", + " print(\"\\n\" + \"=\" * 60)\n", + " print(\"πŸŽ‰ SETUP COMPLETE!\")\n", + " print(\"=\" * 60)\n", + " print(f\"\"\"\n", + "You can now connect to Oracle:\n", + " User: VECTOR\n", + " Password: {vector_password}\n", + " DSN: 127.0.0.1:1521/FREEPDB1\n", + "\"\"\")\n", + " return True\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bacd1640", + "metadata": {}, + "outputs": [], + "source": [ + "# Run this cell after starting your Docker container\n", + "# It handles everything: waits for ready, fixes listener, creates user, tests connection\n", + "setup_oracle_database()" + ] + }, { "cell_type": "markdown", "id": "63b1afd2", @@ -222,16 +444,15 @@ " if \"DPY-4011\" in error_msg or \"Connection reset by peer\" in error_msg:\n", " print(\" β†’ This usually means:\")\n", " print(\" 1. Database is still starting up (wait 2-3 minutes)\")\n", - " print(\" 2. Listener is not bound to 0.0.0.0 (run fix_oracle_listener())\")\n", - " print(\" 3. Container is not running (check with check_docker_container())\")\n", + " print(\" 2. Listener configuration issue\")\n", + " print(\" 3. Container is not running\")\n", " \n", " if attempt < max_retries:\n", " print(f\"\\n Waiting {retry_delay} seconds before retry...\")\n", " time.sleep(retry_delay)\n", " else:\n", - " print(\"\\n πŸ’‘ Try running:\")\n", - " print(\" 1. check_docker_container() - verify container is running\")\n", - " print(\" 2. fix_oracle_listener() - fix listener binding\")\n", + " print(\"\\n πŸ’‘ Try running: setup_oracle_database()\")\n", + " print(\" This will fix the listener and verify the connection.\")\n", " raise\n", " else:\n", " raise\n", @@ -242,6 +463,14 @@ " raise ConnectionError(\"Failed to connect after all retries\")" ] }, + { + "cell_type": "markdown", + "id": "1f8bacbe", + "metadata": {}, + "source": [ + "Ensure you have your Docker Engine running before going through the next steps" + ] + }, { "cell_type": "markdown", "id": "c03562c6", @@ -282,7 +511,9 @@ "id": "47f8ccd5", "metadata": {}, "source": [ - "# Vector Search With Langchain and Oracle AI Database" + "# Vector Search With Langchain and Oracle AI Database\n", + "\n", + "--------" ] }, { @@ -439,6 +670,7 @@ { "cell_type": "code", "execution_count": null, + "id": "3196205a", "metadata": {}, "outputs": [], "source": [ @@ -463,6 +695,7 @@ { "cell_type": "code", "execution_count": null, + "id": "56e523e0", "metadata": {}, "outputs": [], "source": [ @@ -471,7 +704,7 @@ "for doc, score in results:\n", " print(\"Score:\", score)\n", " print(\"Text :\", doc.page_content)\n", - " print(\"Meta :\", doc.metadatas)\n", + " print(\"Meta :\", doc.metadata)\n", " print(\"------\")" ] }, @@ -533,7 +766,8 @@ "id": "82623c94", "metadata": {}, "source": [ - "# Memory Engineering and Agent Memory\n" + "# Memory Engineering and Agent Memory\n", + "--------\n" ] }, { @@ -547,7 +781,7 @@ "\n", "**`Memory Engineering`** is the scaffolding and control harness that we design to move information optimally and efficiently into, through, and across all components of an AI system(databases, LLMs, applications etc). It ensures that data is captured, transformed, organized, and retrieved in the right way at the right timeβ€”so agents can behave reliably, believably, and capabaly.\n", "\n", - "This is the core section of the notebook where we build a complete **`Memory Layer`** for AI agents. \n", + "This is the core section of the notebook where we build a complete **`Memory Manager`** for AI agents. \n", "\n", "Just like humans have different types of memory (short-term, long-term, procedural), AI agents benefit from specialized memory systems.\n", "\n", @@ -583,7 +817,7 @@ "3. **Create vector stores** for semantic memories\n", "4. **Build indexes** for fast similarity search\n", "5. **Implement MemoryLayer class** with read/write methods for each memory type\n", - "6. **Initialize the memory layer** with all storage backends" + "6. **Initialize the memory manager** with all storage backends" ] }, { @@ -878,7 +1112,7 @@ "id": "76f3f57d", "metadata": {}, "source": [ - "## Memory Layer Implementation" + "## Memory Manager Implementation" ] }, { @@ -886,7 +1120,7 @@ "id": "a85d87a8", "metadata": {}, "source": [ - "The `MemoryLayer` class is the central abstraction that unifies all memory operations. It provides a clean interface for reading and writing to different memory types, hiding the complexity of SQL queries and vector store operations.\n", + "The `MemoryManager` class is the central abstraction that unifies all memory operations. It provides a clean interface for reading and writing to different memory types, hiding the complexity of SQL queries and vector store operations.\n", "\n", "### What We're Building\n", "\n", @@ -909,7 +1143,7 @@ "- **LLM-powered entity extraction** β€” Automatically extracts people, places, and systems from text\n", "- **Formatted context output** β€” Each read method returns formatted text ready for the LLM context\n", "\n", - "### Alternative: Memory Layer Frameworks\n", + "### Alternative: Memory Manager Frameworks\n", "\n", "There are existing frameworks that abstract memory management for AI agents:\n", "\n", @@ -933,7 +1167,7 @@ "id": "69bb7a2f", "metadata": {}, "source": [ - "> **For learning purposes**, building your own memory layer (as we do here) gives you a deep understanding of how memory engineering works. \n", + "> **For learning purposes**, building your own memory manager (as we do here) gives you a deep understanding of how memory engineering works. \n", "> \n", "> **For production**, you might consider using or extending an existing framework. \n", ">\n", @@ -950,9 +1184,9 @@ "import json as json_lib\n", "from datetime import datetime\n", "\n", - "class MemoryLayer:\n", + "class MemoryManager:\n", " \"\"\"\n", - " A simplified memory layer for AI agents using Oracle AI Database.\n", + " A simplified memory manager for AI agents using Oracle AI Database.\n", " \n", " Manages 5 types of memory:\n", " - Conversational: Chat history per thread (SQL table)\n", @@ -960,6 +1194,7 @@ " - Workflow: Execution patterns (Vector store)\n", " - Toolbox: Available tools (Vector store)\n", " - Entity: People, places, systems (Vector store)\n", + " - Summary: Storing compressed context window\n", " \"\"\"\n", " \n", " def __init__(self, conn, conversation_table: str, knowledge_base_vs, workflow_vs, toolbox_vs, entity_vs, summary_vs):\n", @@ -1000,9 +1235,11 @@ " results = cur.fetchall()\n", " \n", " messages = [f\"[{ts.strftime('%H:%M:%S')}] [{role}] {content}\" for role, content, ts in results]\n", - " return f\"\"\"## Conversation Memory: This is the conversation history for the current thread \n", - " ### How to use: Use the conversation history to answer the question\\n\n", - " {messages}\"\"\"\n", + " messages_formatted = '\\n'.join(messages)\n", + " return f\"\"\"## Conversation Memory: This is the conversation history for the current thread\n", + "### How to use: Use the conversation history to answer the question\n", + "\n", + "{messages_formatted}\"\"\"\n", " \n", " def mark_as_summarized(self, thread_id: str, summary_id: str):\n", " \"\"\"Mark all unsummarized messages in a thread as summarized.\"\"\"\n", @@ -1026,9 +1263,10 @@ " \"\"\"Search knowledge base for relevant content.\"\"\"\n", " results = self.knowledge_base_vs.similarity_search(query, k=k)\n", " content = \"\\n\".join([doc.page_content for doc in results])\n", - " return f\"\"\"## Knowledge Base Memory: This are general information that is relevant to the question\\n\n", - " ### How to use: Use the knowledge base as background information that can help answer the question\\n\n", - " {content}\"\"\"\n", + " return f\"\"\"## Knowledge Base Memory: This are general information that is relevant to the question\n", + "### How to use: Use the knowledge base as background information that can help answer the question\n", + "\n", + "{content}\"\"\"\n", " \n", " \n", " # ==================== WORKFLOW (Vector Store) ====================\n", @@ -1058,9 +1296,10 @@ " if not results:\n", " return \"## Workflow Memory\\nNo relevant workflows found.\"\n", " content = \"\\n---\\n\".join([doc.page_content for doc in results])\n", - " return f\"\"\"## Workflow Memory: This are the past workflows that are relevant to the question \\n\n", - " ### How to use: Use the steps and use them to answer the question, especially when using tools and external sources \\n\n", - " {content}\"\"\"\n", + " return f\"\"\"## Workflow Memory: This are the past workflows that are relevant to the question\n", + "### How to use: Use the steps and use them to answer the question, especially when using tools and external sources\n", + "\n", + "{content}\"\"\"\n", " \n", " # ==================== TOOLBOX (Vector Store) ====================\n", " \n", @@ -1074,13 +1313,38 @@ " tools = []\n", " for doc in results:\n", " meta = doc.metadata\n", - " # Build simple OpenAI tool schema\n", + " # Extract parameters from metadata and convert to OpenAI format\n", + " stored_params = meta.get(\"parameters\", {})\n", + " properties = {}\n", + " required = []\n", + " \n", + " for param_name, param_info in stored_params.items():\n", + " # Convert stored param info to OpenAI schema format\n", + " param_type = param_info.get(\"type\", \"string\")\n", + " # Map Python types to JSON schema types\n", + " type_mapping = {\n", + " \"\": \"string\",\n", + " \"\": \"integer\", \n", + " \"\": \"number\",\n", + " \"\": \"boolean\",\n", + " \"str\": \"string\",\n", + " \"int\": \"integer\",\n", + " \"float\": \"number\",\n", + " \"bool\": \"boolean\"\n", + " }\n", + " json_type = type_mapping.get(param_type, \"string\")\n", + " properties[param_name] = {\"type\": json_type}\n", + " \n", + " # If no default, it's required\n", + " if \"default\" not in param_info:\n", + " required.append(param_name)\n", + " \n", " tools.append({\n", " \"type\": \"function\",\n", " \"function\": {\n", " \"name\": meta.get(\"name\", \"tool\"),\n", " \"description\": meta.get(\"description\", \"\"),\n", - " \"parameters\": {\"type\": \"object\", \"properties\": {}, \"required\": []}\n", + " \"parameters\": {\"type\": \"object\", \"properties\": properties, \"required\": required}\n", " }\n", " })\n", " return tools\n", @@ -1142,9 +1406,11 @@ " \n", " entities = [f\"β€’ {doc.metadata.get('name', '?')}: {doc.metadata.get('description', '')}\" \n", " for doc in results if hasattr(doc, 'metadata')]\n", - " return f\"\"\"## Entity Memory: This are the entities that are relevant to the question\\n\n", - " ### How to use: Use the entities to answer the question, especially when having long conversations\\n\n", - " {entities}\"\"\"\n", + " entities_formatted = '\\n'.join(entities)\n", + " return f\"\"\"## Entity Memory: This are the entities that are relevant to the question\n", + "### How to use: Use the entities to answer the question, especially when having long conversations\n", + "\n", + "{entities_formatted}\"\"\"\n", " \n", " # ==================== SUMMARY (Vector Store) ====================\n", " \n", @@ -1191,7 +1457,7 @@ "source": [ "# Initialize the MemoryLayer instance\n", "# Note: Uses SQL table for conversational memory, vector stores for others\n", - "memory_layer = MemoryLayer(\n", + "memory_manager = MemoryManager(\n", " conn=vector_conn,\n", " conversation_table=CONVERSATION_HISTORY_TABLE, \n", " knowledge_base_vs=knowledge_base_vs,\n", @@ -1254,7 +1520,7 @@ "| `_generate_queries()` | Creates synthetic queries that would trigger this tool |\n", "| `register_tool()` | Decorator that stores tool with its embedding in the toolbox |\n", "\n", - "When you call `memory_layer.read_toolbox(query)`, it performs a similarity search to find tools whose docstrings are semantically similar to the query.\n", + "When you call `memory_manager.read_toolbox(query)`, it performs a similarity search to find tools whose docstrings are semantically similar to the query.\n", "\n", "### The Intersection of Three Engineering Disciplines\n", "\n", @@ -1315,16 +1581,16 @@ " find relevant tools based on natural language queries.\n", " \"\"\"\n", " \n", - " def __init__(self, memory_layer, llm_client, model: str = \"gpt-4o-mini\"):\n", + " def __init__(self, memory_manager, llm_client, model: str = \"gpt-4o-mini\"):\n", " \"\"\"\n", " Initialize the Toolbox.\n", " \n", " Args:\n", - " memory_layer: MemoryLayer instance for storing tools\n", + " memory_manager: MemoryManager instance for storing tools\n", " llm_client: OpenAI client for LLM augmentation\n", " model: Model to use for augmentation (default: gpt-4o-mini)\n", " \"\"\"\n", - " self.memory_layer = memory_layer\n", + " self.memory_manager = memory_manager\n", " self.llm_client = llm_client\n", " self.model = model\n", " self._tools: dict[str, Callable] = {} # Maps tool_id -> callable\n", @@ -1519,7 +1785,7 @@ "\n", " # Store the tool in the toolbox memory for retrieval\n", " # The embedding enables semantic search to find relevant tools\n", - " self.memory_layer.write_toolbox(\n", + " self.memory_manager.write_toolbox(\n", " f\"{f.__name__} {docstring} {signature}\", \n", " tool_dict\n", " )\n", @@ -1537,13 +1803,27 @@ { "cell_type": "code", "execution_count": null, - "id": "7eb6bacd", + "id": "b6de020f", "metadata": {}, "outputs": [], "source": [ "import os\n", + "import getpass\n", "\n", - "os.environ[\"OPENAI_API_KEY\"] = \"\"" + "# Function to securely get and set environment variables\n", + "def set_env_securely(var_name, prompt):\n", + " value = getpass.getpass(prompt)\n", + " os.environ[var_name] = value\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "7eb6bacd", + "metadata": {}, + "outputs": [], + "source": [ + "set_env_securely(\"OPENAI_API_KEY\", \"OpenAI API Key: \")" ] }, { @@ -1558,7 +1838,7 @@ "client = OpenAI()\n", "\n", "# Initialize the Toolbox\n", - "toolbox = Toolbox(memory_layer=memory_layer, llm_client=client)" + "toolbox = Toolbox(memory_manager=memory_manager, llm_client=client)" ] }, { @@ -1566,7 +1846,9 @@ "id": "305bc1bd", "metadata": {}, "source": [ - "# Context Engineering Techniques\n" + "# Context Engineering Techniques\n", + "\n", + "--------\n" ] }, { @@ -1628,7 +1910,7 @@ "# Context summariser - calls LLM and stores summary\n", "import uuid\n", "\n", - "def summarise_context_window(content: str, memory_layer, llm_client, model: str = \"gpt-4o-mini\") -> dict:\n", + "def summarise_context_window(content: str, memory_manager, llm_client, model: str = \"gpt-4o-mini\") -> dict:\n", " \"\"\"Summarise content using LLM and store in summary memory.\"\"\"\n", " # Call LLM to summarise\n", " response = llm_client.chat.completions.create(\n", @@ -1648,7 +1930,7 @@ " \n", " # Store in memory\n", " summary_id = str(uuid.uuid4())[:8]\n", - " memory_layer.write_summary(summary_id, content, summary, description)\n", + " memory_manager.write_summary(summary_id, content, summary, description)\n", " \n", " return {\"id\": summary_id, \"description\": description, \"summary\": summary}\n" ] @@ -1661,7 +1943,7 @@ "outputs": [], "source": [ "# Context offloader - replaces content with summary reference\n", - "def offload_to_summary(context: str, memory_layer, llm_client, threshold_percent: float = 80.0) -> tuple:\n", + "def offload_to_summary(context: str, memory_manager, llm_client, threshold_percent: float = 80.0) -> tuple:\n", " \"\"\"If context exceeds threshold, summarise and return compacted version.\"\"\"\n", " usage = calculate_context_usage(context)\n", " \n", @@ -1669,7 +1951,7 @@ " return context, [] # No offload needed\n", " \n", " # Summarise the context\n", - " result = summarise_context_window(context, memory_layer, llm_client)\n", + " result = summarise_context_window(context, memory_manager, llm_client)\n", " \n", " # Return compact reference instead of full content\n", " compact = f\"[Summary ID: {result['id']}] {result['description']}\"\n", @@ -1727,15 +2009,15 @@ "@toolbox.register_tool(augment=True)\n", "def expand_summary(summary_id: str) -> str:\n", " \"\"\"Expand a summary reference to full content. Use when you need more details from a [Summary ID: xxx] reference.\"\"\"\n", - " return memory_layer.read_summary_memory(summary_id)\n", + " return memory_manager.read_summary_memory(summary_id)\n", "\n", "@toolbox.register_tool(augment=True)\n", "def summarize_and_store(text: str, thread_id: str = None) -> str:\n", " \"\"\"Summarize long text and store in memory. Returns a summary ID for later retrieval with expand_summary.\"\"\"\n", - " result = summarise_context_window(text, memory_layer, client)\n", + " result = summarise_context_window(text, memory_manager, client)\n", " # If thread_id provided, mark conversation messages as summarized\n", " if thread_id:\n", - " memory_layer.mark_as_summarized(thread_id, result['id'])\n", + " memory_manager.mark_as_summarized(thread_id, result['id'])\n", " return f\"Stored as [Summary ID: {result['id']}] {result['description']}\"\n", "\n", "def summarize_conversation(thread_id: str) -> dict:\n", @@ -1744,16 +2026,16 @@ " Call this to compact a thread's conversation history.\n", " \"\"\"\n", " # Read current unsummarized messages\n", - " conv_memory = memory_layer.read_conversational_memory(thread_id, limit=100)\n", + " conv_memory = memory_manager.read_conversational_memory(thread_id, limit=100)\n", " \n", " if not conv_memory or \"[]\" in conv_memory:\n", " return {\"status\": \"nothing_to_summarize\"}\n", " \n", " # Summarize the conversation\n", - " result = summarise_context_window(conv_memory, memory_layer, client)\n", + " result = summarise_context_window(conv_memory, memory_manager, client)\n", " \n", " # Mark messages as summarized\n", - " memory_layer.mark_as_summarized(thread_id, result['id'])\n", + " memory_manager.mark_as_summarized(thread_id, result['id'])\n", " \n", " print(f\"βœ… Conversation summarized: [Summary ID: {result['id']}]\")\n", " return result\n", @@ -1765,7 +2047,9 @@ "id": "d05404f5", "metadata": {}, "source": [ - "# Web Access with Tavily\n" + "# Web Access with Tavily\n", + "\n", + "--------" ] }, { @@ -1802,6 +2086,16 @@ "This pattern means the agent **learns** from its searches. Information discovered once becomes part of the agent's long-term memory, available for future conversations without additional API calls." ] }, + { + "cell_type": "code", + "execution_count": null, + "id": "500d7836", + "metadata": {}, + "outputs": [], + "source": [ + "set_env_securely(\"TAVILY_API_KEY\", \"Tavily API Key: \")" + ] + }, { "cell_type": "code", "execution_count": null, @@ -1812,7 +2106,8 @@ "from tavily import TavilyClient\n", "from datetime import datetime\n", "\n", - "tavily_client = TavilyClient(api_key=\"\")\n", + "# Don't forget to set your API key!\n", + "tavily_client = TavilyClient(api_key=os.environ[\"TAVILY_API_KEY\"])\n", "\n", "@toolbox.register_tool(augment=True)\n", "def search_tavily(query: str, max_results: int = 5):\n", @@ -1838,7 +2133,7 @@ " }\n", " \n", " # Write to knowledge base\n", - " memory_layer.write_knowledge_base(text, metadata)\n", + " memory_manager.write_knowledge_base(text, metadata)\n", "\n", " return results" ] @@ -1851,7 +2146,7 @@ "outputs": [], "source": [ "import pprint\n", - "retreived_tools = memory_layer.read_toolbox(\"Search the internet\")\n", + "retreived_tools = memory_manager.read_toolbox(\"Search the internet\")\n", "pprint.pprint(retreived_tools)" ] }, @@ -1860,7 +2155,9 @@ "id": "9e993b26", "metadata": {}, "source": [ - "# Agent Execution\n" + "# Agent Execution\n", + "\n", + "--------\n" ] }, { @@ -1892,6 +2189,10 @@ "client = OpenAI()\n", "\n", "# ==================== SYSTEM PROMPT ====================\n", + "# Below is an example of prompt engineering technique called role description.\n", + "# It is a technique that is used to improve the quality of the LLM's output.\n", + "# Although there are research that suggest that role description doesn't realy affect the quality of the LLM's output, it is still a useful technique\n", + "#Β and it is a good [prompt engineering] technique to know.\n", "AGENT_SYSTEM_PROMPT = \"\"\"\n", "# System Instructions\n", "You are an intelligent assistant with access to memory and tools.\n", @@ -1991,11 +2292,11 @@ " print(\"🧠 BUILDING CONTEXT...\")\n", " \n", " context = f\"# Question\\n{query}\\n\\n\"\n", - " context += memory_layer.read_conversational_memory(thread_id) + \"\\n\\n\"\n", - " context += memory_layer.read_knowledge_base(query) + \"\\n\\n\"\n", - " context += memory_layer.read_workflow(query) + \"\\n\\n\"\n", - " context += memory_layer.read_entity(query) + \"\\n\\n\"\n", - " context += memory_layer.read_summary_context(query) + \"\\n\\n\" # Shows IDs + descriptions\n", + " context += memory_manager.read_conversational_memory(thread_id) + \"\\n\\n\"\n", + " context += memory_manager.read_knowledge_base(query) + \"\\n\\n\"\n", + " context += memory_manager.read_workflow(query) + \"\\n\\n\"\n", + " context += memory_manager.read_entity(query) + \"\\n\\n\"\n", + " context += memory_manager.read_summary_context(query) + \"\\n\\n\" # Shows IDs + descriptions\n", "\n", " print(\"====CONTEXT WINDOW=====\\n\")\n", " print(context)\n", @@ -2006,7 +2307,7 @@ " \n", " if usage['percent'] > 80:\n", " print(\"⚠️ Context >80% - summarizing...\")\n", - " context, summaries = offload_to_summary(context, memory_layer, client)\n", + " context, summaries = offload_to_summary(context, memory_manager, client)\n", " # Add summary references to context\n", " if summaries:\n", " summary_section = \"\\n## Summary Memory\\n\"\n", @@ -2017,13 +2318,13 @@ " print(f\"πŸ“Š After summarization: {usage['percent']}%\")\n", " \n", " # 3. Get tools\n", - " dynamic_tools = memory_layer.read_toolbox(query, k=5)\n", + " dynamic_tools = memory_manager.read_toolbox(query, k=5)\n", " print(f\"πŸ”§ Tools: {[t['function']['name'] for t in dynamic_tools]}\")\n", " \n", " # 4. Store user message & extract entities\n", - " memory_layer.write_conversational_memory(query, \"user\", thread_id)\n", + " memory_manager.write_conversational_memory(query, \"user\", thread_id)\n", " try:\n", - " memory_layer.write_entity(\"\", \"\", \"\", llm_client=client, text=query)\n", + " memory_manager.write_entity(\"\", \"\", \"\", llm_client=client, text=query)\n", " except: pass\n", " \n", " # 5. Agent loop\n", @@ -2046,14 +2347,17 @@ " for tc in msg.tool_calls:\n", " tool_name = tc.function.name\n", " tool_args = json_lib.loads(tc.function.arguments)\n", - " print(f\"πŸ› οΈ {tool_name}({tool_args})\")\n", + " # Format args for display (truncate long values)\n", + " args_display = {k: (v[:50] + '...' if isinstance(v, str) and len(v) > 50 else v) \n", + " for k, v in tool_args.items()}\n", + " print(f\"πŸ› οΈ {tool_name}({args_display})\")\n", " \n", " try:\n", " result = execute_tool(tool_name, tool_args)\n", - " steps.append(f\"{tool_name} β†’ success\")\n", + " steps.append(f\"{tool_name}({args_display}) β†’ success\")\n", " except Exception as e:\n", " result = f\"Error: {e}\"\n", - " steps.append(f\"{tool_name} β†’ failed\")\n", + " steps.append(f\"{tool_name}({args_display}) β†’ failed\")\n", " \n", " print(f\" β†’ {result[:200]}...\")\n", " messages.append({\"role\": \"tool\", \"tool_call_id\": tc.id, \"content\": result})\n", @@ -2064,11 +2368,11 @@ " \n", " # 6. Save workflow & entities\n", " if steps:\n", - " memory_layer.write_workflow(query, steps, final_answer)\n", + " memory_manager.write_workflow(query, steps, final_answer)\n", " try:\n", - " memory_layer.write_entity(\"\", \"\", \"\", llm_client=client, text=final_answer)\n", + " memory_manager.write_entity(\"\", \"\", \"\", llm_client=client, text=final_answer)\n", " except: pass\n", - " memory_layer.write_conversational_memory(final_answer, \"assistant\", thread_id)\n", + " memory_manager.write_conversational_memory(final_answer, \"assistant\", thread_id)\n", " \n", " print(\"\\n\" + \"=\"*50 + f\"\\nπŸ’¬ ANSWER:\\n{final_answer}\\n\" + \"=\"*50)\n", " return final_answer\n" @@ -2081,16 +2385,8 @@ "metadata": {}, "outputs": [], "source": [ - "call_agent(\"Check one of my summary memory and expand and tell me the details\", thread_id=\"0\")" + "call_agent(\"These results are good, can you double click into the most relevant one and tell me more about it?\", thread_id=\"0\")" ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "02d6d663", - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { From 17bef6ea578f07c3086bb6499a66d9d8b95eecae Mon Sep 17 00:00:00 2001 From: RichmondAlake Date: Thu, 11 Dec 2025 17:27:37 +0000 Subject: [PATCH 2/2] updated readme --- README.md | 14 ++++----- .../memory_context_engineering_agents.ipynb | 30 +++++++++++++++++-- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e9a41833..c87240ff 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,8 @@ This repository is organized into several key areas: Applications and reference implementations demonstrating how to build AI-powered solutions with Oracle technologies. These complete, working examples showcase end-to-end implementations of AI applications, agents, and systems that leverage Oracle AI Database and OCI services. Each application includes source code, deployment configurations, and documentation to help developers understand architectural patterns, integration approaches, and best practices for building production-grade AI solutions. -| Name | Description | Link | -| ------------------------ | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| Name | Description | Link | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | oci-generative-ai-jet-ui | Full-stack AI application with Oracle JET UI, OCI Generative AI integration, Kubernetes deployment, and Terraform infrastructure | [![View App](https://img.shields.io/badge/View%20App-blue?style=flat-square)](./apps/oci-generative-ai-jet-ui) | ### πŸ““ **Notebooks** (`/notebooks`) @@ -24,11 +24,11 @@ Jupyter notebooks and interactive tutorials covering: - Data preparation and analysis workflows - Agent development and orchestration examples -| Name | Description | Stack | Link | -| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -| memory_context_engineering_agents | Build AI agents with 6 types of persistent memory. Covers memory engineering, context window management, and just-in-time retrieval patterns. | LangChain, Oracle AI Database, OpenAI, Tavily | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/memory_context_engineering_agents.ipynb) | -| oracle_rag_agents_zero_to_hero | Learn to build RAG agents from scratch using Oracle AI Database. | Oracle AI Database, OpenAI, OpenAI Agents SDK | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/oracle_rag_agents_zero_to_hero.ipynb) | -| oracle_rag_with_evals | Build RAG systems with comprehensive evaluation metrics | Oracle AI Database, OpenAI, BEIR, Galileo | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/oracle_rag_with_evals.ipynb) | +| Name | Description | Stack | Link | +| --------------------------------- | ---------------------------------------------------------------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | +| memory_context_engineering_agents | Build AI agents with 6 types of persistent memory. | LangChain, Oracle AI Database, OpenAI, Tavily | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/memory_context_engineering_agents.ipynb) | +| oracle_rag_agents_zero_to_hero | Learn to build RAG agents from scratch using Oracle AI Database. | Oracle AI Database, OpenAI, OpenAI Agents SDK | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/oracle_rag_agents_zero_to_hero.ipynb) | +| oracle_rag_with_evals | Build RAG systems with comprehensive evaluation metrics | Oracle AI Database, OpenAI, BEIR, Galileo | [![Open Notebook](https://img.shields.io/badge/Open%20Notebook-orange?style=flat-square)](./notebooks/oracle_rag_with_evals.ipynb) | ### πŸŽ“ **Workshops** (`/workshops`) diff --git a/notebooks/memory_context_engineering_agents.ipynb b/notebooks/memory_context_engineering_agents.ipynb index cafeaf52..88c10b98 100644 --- a/notebooks/memory_context_engineering_agents.ipynb +++ b/notebooks/memory_context_engineering_agents.ipynb @@ -182,7 +182,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "fb7ae8e6", "metadata": {}, "outputs": [], @@ -372,10 +372,34 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "bacd1640", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "============================================================\n", + "πŸš€ ORACLE DATABASE SETUP\n", + "============================================================\n", + "\n", + "[1/6] Checking Docker...\n", + " ❌ Docker is not running!\n", + " πŸ’‘ Start Docker Desktop and try again.\n" + ] + }, + { + "data": { + "text/plain": [ + "False" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Run this cell after starting your Docker container\n", "# It handles everything: waits for ready, fixes listener, creates user, tests connection\n",