{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Lesson 13 - Agent Memory with Cognee Knowledge Graphs" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Setup\n", "\n", "This notebook demonstrates how to build an intelligent **coding assistant** with persistent memory using [**Cognee**](https://www.cognee.ai/) knowledge graphs and the **Microsoft Agent Framework** (MAF).\n", "\n", "Cognee transforms unstructured text into a structured, queryable knowledge graph backed by vector embeddings — giving your agent a rich, relationship-aware long-term memory.\n", "\n", "### What You'll Learn\n", "1. **Build Knowledge Graphs**: Transform developer profiles and best practices into structured, queryable knowledge.\n", "2. **Integrate Cognee with MAF**: Use `@tool` functions to let an MAF agent query Cognee's knowledge graph.\n", "3. **Session-Aware Conversations**: Maintain context across multiple questions in the same session.\n", "4. **Long-Term Memory**: Persist important knowledge across sessions and retrieve it in new conversations.\n", "\n", "### Prerequisites\n", "- Python 3.9+\n", "- Redis running locally (`docker run -d -p 6379:6379 redis`) for session management\n", "- An LLM API key (e.g. OpenAI) — set `LLM_API_KEY` in `.env`\n", "- `CACHING=true` in `.env` (required for Cognee sessions)\n", "- An Azure AI Foundry project with a deployed chat model\n", "- `AZURE_AI_PROJECT_ENDPOINT` and `AZURE_AI_MODEL_DEPLOYMENT_NAME` in `.env`\n", "- Azure CLI authenticated (`az login`)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%pip install agent-framework azure-ai-projects azure-identity \"cognee[redis]==0.4.0\" -q" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import os\n", "from pathlib import Path\n", "from typing import Annotated\n", "\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv()\n", "\n", "os.environ[\"LLM_API_KEY\"] = os.getenv(\"LLM_API_KEY\", \"\")\n", "os.environ[\"CACHING\"] = os.getenv(\"CACHING\", \"true\")\n", "\n", "import cognee\n", "from cognee.modules.search.types import SearchType\n", "\n", "from agent_framework import tool\n", "from agent_framework.azure import AzureAIProjectAgentProvider\n", "from azure.identity import AzureCliCredential\n", "\n", "print(f\"Cognee version: {cognee.__version__}\")\n", "print(f\"CACHING: {os.environ.get('CACHING')}\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "provider = AzureAIProjectAgentProvider(credential=AzureCliCredential())\n", "\n", "print(\"✅ AzureAIProjectAgentProvider created\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Types of Agent Memory\n", "\n", "This notebook explores the same three memory types from the main Lesson 13 notebook, but uses Cognee as the long-term memory backend:\n", "\n", "| Memory Type | Mechanism | Lifetime |\n", "|---|---|---|\n", "| **Working** | `agent.create_session()` (MAF) | Single conversation |\n", "| **Short-term** | Cognee session cache (Redis) | Single session |\n", "| **Long-term** | Cognee knowledge graph + vectors | Permanent |\n", "\n", "### Cognee's Memory Architecture\n", "```\n", "┌──────────────────────────┐\n", "│ Raw Data │ (developer profiles, docs, conversations)\n", "└───────────┬──────────────┘\n", " │ cognee.add() + cognee.cognify()\n", " ▼\n", "┌──────────────────────────────────────────┐\n", "│ Knowledge Graph + Vector Embeddings │\n", "└───────────┬──────────────────────────────┘\n", " │ cognee.search()\n", " ▼\n", "┌──────────────────┐ ┌────────────────┐\n", "│ MAF Agent │──────▶│ @tool funcs │\n", "│ (AgentSession) │ │ wrapping │\n", "│ │ │ cognee.search │\n", "└──────────────────┘ └────────────────┘\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Prepare Cognee Storage" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "DATA_ROOT = Path('.data_storage').resolve()\n", "SYSTEM_ROOT = Path('.cognee_system').resolve()\n", "\n", "DATA_ROOT.mkdir(parents=True, exist_ok=True)\n", "SYSTEM_ROOT.mkdir(parents=True, exist_ok=True)\n", "\n", "cognee.config.data_root_directory(str(DATA_ROOT))\n", "cognee.config.system_root_directory(str(SYSTEM_ROOT))\n", "\n", "await cognee.prune.prune_data()\n", "await cognee.prune.prune_system(metadata=True)\n", "print(\"✅ Cognee storage configured and reset\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 1 — Building the Knowledge Base\n", "\n", "We ingest three types of data to create a comprehensive knowledge base for our coding assistant:\n", "\n", "1. **Developer Profile** — personal expertise and technical background\n", "2. **Python Best Practices** — the Zen of Python with practical guidelines\n", "3. **Historical Conversations** — past Q&A sessions between developers and AI assistants" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "developer_intro = (\n", " \"Hi, I'm an AI/Backend engineer. \"\n", " \"I build FastAPI services with Pydantic, heavy asyncio/aiohttp pipelines, \"\n", " \"and production testing via pytest-asyncio. \"\n", " \"I've shipped low-latency APIs on AWS, Azure, and GoogleCloud.\"\n", ")\n", "\n", "python_zen_principles = \"\"\"\n", "# The Zen of Python: Practical Guide\n", "\n", "## Key Principles With Guidance\n", "\n", "### 1. Beautiful is better than ugly\n", "Prefer descriptive names, clear structure, and consistent formatting.\n", "\n", "### 2. Explicit is better than implicit\n", "Be clear about behavior, imports, and types.\n", "\n", "### 3. Simple is better than complex\n", "Choose straightforward solutions first.\n", "\n", "### 4. Flat is better than nested\n", "Use early returns to reduce indentation.\n", "\n", "## Modern Python Tie-ins\n", "- Type hints reinforce explicitness\n", "- Context managers enforce safe resource handling\n", "- Dataclasses improve readability for data containers\n", "\"\"\"\n", "\n", "human_agent_conversations = \"\"\"\n", "\"conversations\": [\n", " {\n", " \"topic\": \"async/await patterns\",\n", " \"user_query\": \"I'm building a web scraper that needs to handle thousands of URLs concurrently. What's the best way to structure this with asyncio?\",\n", " \"assistant_response\": \"Use asyncio with aiohttp, a semaphore to cap concurrency, TCPConnector for connection pooling, and context managers for session lifecycle.\"\n", " },\n", " {\n", " \"topic\": \"dataclass vs pydantic\",\n", " \"user_query\": \"When should I use dataclasses vs Pydantic models?\",\n", " \"assistant_response\": \"For API input/output, prefer Pydantic: runtime validation, type coercion, JSON serialization. Integrates cleanly with FastAPI.\"\n", " },\n", " {\n", " \"topic\": \"testing patterns\",\n", " \"user_query\": \"What's the best approach for pytest with async functions?\",\n", " \"assistant_response\": \"Use pytest-asyncio, async fixtures, and an isolated test database or mocks to reliably test async code.\"\n", " },\n", " {\n", " \"topic\": \"error handling and logging\",\n", " \"user_query\": \"What's the best approach for production-ready error management?\",\n", " \"assistant_response\": \"Centralized error handling with custom exceptions, structured logging, and FastAPI middleware.\"\n", " }\n", " ]\n", "\"\"\"\n", "\n", "print(\"✅ Data sources prepared\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "await cognee.add(developer_intro, node_set=[\"developer_data\"])\n", "await cognee.add(human_agent_conversations, node_set=[\"developer_data\"])\n", "await cognee.add(python_zen_principles, node_set=[\"principles_data\"])\n", "\n", "await cognee.cognify()\n", "print(\"✅ Knowledge graph built\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualize the Knowledge Graph\n", "\n", "Cognee can render an interactive HTML visualization of the entities and relationships it extracted." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "from cognee import visualize_graph\n", "\n", "await visualize_graph('./cognee_graph.html')\n", "print(\"📊 Graph saved to cognee_graph.html — open it in a browser to explore.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Enrich Memory with Memify\n", "\n", "`memify()` analyzes the knowledge graph and generates intelligent rules — identifying patterns, best practices, and relationships between concepts." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "await cognee.memify()\n", "print(\"✅ Memory enriched with memify\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Part 2 — MAF Agent with Cognee Tools\n", "\n", "Now we create an MAF agent that can query Cognee's knowledge graph via `@tool` functions. This lets the agent leverage the full power of graph-aware semantic search while maintaining conversational context through sessions." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "@tool(approval_mode=\"never_require\")\n", "async def search_knowledge(\n", " query: Annotated[str, \"Natural-language question to search the knowledge graph\"],\n", ") -> str:\n", " \"\"\"Search the Cognee knowledge graph for relevant developer knowledge, best practices, and past conversations.\"\"\"\n", " results = await cognee.search(\n", " query_text=query,\n", " query_type=SearchType.GRAPH_COMPLETION,\n", " )\n", " if not results:\n", " return \"No relevant knowledge found.\"\n", " return str(results)\n", "\n", "\n", "@tool(approval_mode=\"never_require\")\n", "async def search_principles(\n", " query: Annotated[str, \"Question about Python principles or best practices\"],\n", ") -> str:\n", " \"\"\"Search only the Python principles subset of the knowledge graph.\"\"\"\n", " from cognee.modules.engine.models.node_set import NodeSet\n", " results = await cognee.search(\n", " query_text=query,\n", " query_type=SearchType.GRAPH_COMPLETION,\n", " node_type=NodeSet,\n", " node_name=[\"principles_data\"],\n", " )\n", " if not results:\n", " return \"No relevant principles found.\"\n", " return str(results)\n", "\n", "\n", "print(\"✅ Cognee tools defined: search_knowledge, search_principles\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "coding_agent = await provider.create_agent(\n", " name=\"CodingAssistant\",\n", " instructions=(\n", " \"You are an expert coding assistant with access to a knowledge graph \"\n", " \"containing developer profiles, Python best practices, and past conversations.\\n\\n\"\n", " \"WORKFLOW:\\n\"\n", " \"1. Use search_knowledge() to find relevant information from the full knowledge graph.\\n\"\n", " \"2. Use search_principles() when the question is specifically about Python best practices.\\n\"\n", " \"3. Combine retrieved knowledge with your own expertise to give comprehensive answers.\\n\"\n", " \"4. Reference the developer's known tech stack (FastAPI, asyncio, Pydantic) when relevant.\"\n", " ),\n", ")\n", "\n", "print(\"✅ CodingAssistant agent created\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Working Memory with Sessions\n", "\n", "The `AgentSession` (created via `agent.create_session()`) provides working memory within a session. The agent can refer back to earlier messages while also querying Cognee's long-term knowledge graph." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "session = coding_agent.create_session()\n", "\n", "response = await coding_agent.run(\n", " \"How does my AsyncWebScraper implementation align with Python's design principles?\",\n", " session=session,\n", ")\n", "print(\"🤖 Agent:\", response)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = await coding_agent.run(\n", " \"Based on what you just said, when should I pick dataclasses versus Pydantic for this work?\",\n", " session=session,\n", ")\n", "print(\"🤖 Agent:\", response)\n", "print(\"\\n💡 The agent combined working memory (previous answer) with Cognee's knowledge graph.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## New Session — Long-Term Memory Persists\n", "\n", "Starting a fresh session clears working memory, but the Cognee knowledge graph is still available. The agent can retrieve the same long-term knowledge in a completely new conversation." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "session_2 = coding_agent.create_session()\n", "\n", "response = await coding_agent.run(\n", " \"What logging guidance should I follow for incident reviews?\",\n", " session=session_2,\n", ")\n", "print(\"🤖 Agent:\", response)\n", "print(\"\\n💡 New session, but the agent still has access to the full Cognee knowledge graph.\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "response = await coding_agent.run(\n", " \"How should variables be named according to Python best practices?\",\n", " session=session_2,\n", ")\n", "print(\"🤖 Agent:\", response)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "In this notebook you built a coding assistant that combines **MAF's working memory** (`agent.create_session()`) with **Cognee's long-term knowledge graph**.\n", "\n", "### What You've Learned\n", "1. **Knowledge graph construction**: Cognee ingests unstructured text and builds a graph + vector memory.\n", "2. **Graph enrichment with memify**: Derived facts and richer relationships on top of your existing graph.\n", "3. **MAF + Cognee integration**: `@tool` functions let an MAF agent query Cognee's graph naturally.\n", "4. **Working memory + long-term memory**: `AgentSession` (via `agent.create_session()`) provides session context while Cognee provides persistent knowledge.\n", "5. **Filtered search with NodeSets**: Target specific subsets of the knowledge graph (e.g. only principles).\n", "\n", "### Key Takeaways\n", "- **Cognee** turns raw text into structured, relationship-aware memory — more powerful than a flat vector store.\n", "- **`@tool` functions** bridge MAF agents and external knowledge systems cleanly.\n", "- **`AgentSession`** (via `agent.create_session()`) keeps per-conversation context separate from long-lived knowledge.\n", "- The same knowledge graph serves multiple sessions and agents.\n", "\n", "### Real-World Applications\n", "- **Developer copilots**: Code review, incident analysis, architecture assistants\n", "- **Customer-facing copilots**: Support agents over product docs, FAQs, and CRM notes\n", "- **Internal expert copilots**: Policy, legal, or security assistants reasoning over guidelines\n", "- **Unified data layers**: Combine structured and unstructured data into one queryable graph\n", "\n", "### Next Steps\n", "- Experiment with temporal awareness in Cognee\n", "- Define an OWL ontology for domain-specific graph quality\n", "- Add user feedback loops to improve retrieval over time\n", "- Scale to multi-agent systems sharing the same Cognee memory layer" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.0" } }, "nbformat": 4, "nbformat_minor": 5 }