{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "7aaf216f",
   "metadata": {},
   "source": [
    "# 阶段4：记忆、知识库系统与长期状态 实践\n",
    "\n",
    "本笔记本帮助你实践 Agent 记忆系统、RAG 技术和知识库问答，参考 docs/stage4-memory-rag.md。\n",
    "\n",
    "## 本阶段目标\n",
    "- 理解 Agent 的记忆系统架构\n",
    "- 掌握短期记忆和长期记忆的实现\n",
    "- 学习 RAG（检索增强生成）技术\n",
    "- 实践构建知识库问答 Agent"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "285f9ec4",
   "metadata": {},
   "source": [
    "## 1. 环境配置"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4efa231e",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 导入必要的库\n",
    "import os\n",
    "import json\n",
    "import numpy as np\n",
    "from datetime import datetime\n",
    "from typing import List, Dict, Any\n",
    "from collections import deque\n",
    "from dotenv import load_dotenv\n",
    "from openai import AzureOpenAI\n",
    "\n",
    "# 加载环境变量\n",
    "load_dotenv(override=True)\n",
    "\n",
    "# 创建 Azure OpenAI 客户端\n",
    "client = AzureOpenAI(\n",
    "    azure_endpoint=os.getenv(\"AZURE_OPENAI_ENDPOINT\"),\n",
    "    api_key=os.getenv(\"AZURE_OPENAI_API_KEY\"),\n",
    "    api_version=os.getenv(\"AZURE_OPENAI_API_VERSION\", \"2024-12-01-preview\")\n",
    ")\n",
    "\n",
    "# 获取部署名称\n",
    "deployment = os.getenv(\"AZURE_OPENAI_DEPLOYMENT\", \"gpt-4o\")\n",
    "embedding_deployment = os.getenv(\"AZURE_OPENAI_EMBEDDING_DEPLOYMENT\", \"text-embedding-3-small\")\n",
    "\n",
    "print(f\"✅ 环境配置完成\")\n",
    "print(f\"📌 Chat 部署: {deployment}\")\n",
    "print(f\"📌 Embedding 部署: {embedding_deployment}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e119ddb",
   "metadata": {},
   "source": [
    "## 2. 记忆系统架构\n",
    "\n",
    "Agent 的记忆分为三个层次：\n",
    "\n",
    "```\n",
    "┌─────────────────────────────────────────┐\n",
    "│         工作记忆 (Working Memory)        │\n",
    "│      当前对话上下文，即时可用            │\n",
    "│         Token 限制：4K-128K              │\n",
    "└─────────────────┬───────────────────────┘\n",
    "                  │\n",
    "┌─────────────────┴───────────────────────┐\n",
    "│        短期记忆 (Short-term Memory)      │\n",
    "│      最近的对话历史，会话级别            │\n",
    "│         存储：内存/Redis                 │\n",
    "└─────────────────┬───────────────────────┘\n",
    "                  │\n",
    "┌─────────────────┴───────────────────────┐\n",
    "│        长期记忆 (Long-term Memory)       │\n",
    "│    持久化知识，跨会话可用                │\n",
    "│    存储：向量数据库 + 结构化数据库       │\n",
    "└─────────────────────────────────────────┘\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75501ade",
   "metadata": {},
   "source": [
    "## 3. 短期记忆实现\n",
    "\n",
    "### 3.1 对话历史管理器\n",
    "\n",
    "管理对话历史，支持消息限制和历史摘要。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b7507c5e",
   "metadata": {},
   "outputs": [],
   "source": [
    "class ConversationMemory:\n",
    "    \"\"\"对话记忆管理器\n",
    "    \n",
    "    功能：\n",
    "    - 管理对话历史\n",
    "    - 限制最大消息数\n",
    "    - 支持系统消息\n",
    "    - 可获取最近 N 条消息\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, max_messages: int = 20):\n",
    "        \"\"\"初始化对话记忆\n",
    "        \n",
    "        Args:\n",
    "            max_messages: 最大保留消息数（超出会自动删除最旧的）\n",
    "        \"\"\"\n",
    "        self.messages = deque(maxlen=max_messages)\n",
    "        self.system_message = None\n",
    "    \n",
    "    def set_system_message(self, content: str):\n",
    "        \"\"\"设置系统消息（角色设定）\"\"\"\n",
    "        self.system_message = {\"role\": \"system\", \"content\": content}\n",
    "    \n",
    "    def add_user_message(self, content: str):\n",
    "        \"\"\"添加用户消息\"\"\"\n",
    "        self.messages.append({\"role\": \"user\", \"content\": content})\n",
    "    \n",
    "    def add_assistant_message(self, content: str):\n",
    "        \"\"\"添加助手消息\"\"\"\n",
    "        self.messages.append({\"role\": \"assistant\", \"content\": content})\n",
    "    \n",
    "    def get_messages(self) -> List[Dict]:\n",
    "        \"\"\"获取所有消息（包括系统消息）\"\"\"\n",
    "        messages = []\n",
    "        if self.system_message:\n",
    "            messages.append(self.system_message)\n",
    "        messages.extend(list(self.messages))\n",
    "        return messages\n",
    "    \n",
    "    def get_recent_messages(self, n: int = 10) -> List[Dict]:\n",
    "        \"\"\"获取最近 n 条消息\"\"\"\n",
    "        messages = []\n",
    "        if self.system_message:\n",
    "            messages.append(self.system_message)\n",
    "        \n",
    "        recent = list(self.messages)[-n:]\n",
    "        messages.extend(recent)\n",
    "        return messages\n",
    "    \n",
    "    def clear(self):\n",
    "        \"\"\"清空对话历史（保留系统消息）\"\"\"\n",
    "        self.messages.clear()\n",
    "    \n",
    "    def get_summary(self) -> str:\n",
    "        \"\"\"获取对话摘要（用于调试）\"\"\"\n",
    "        return f\"消息数: {len(self.messages)}, 有系统消息: {self.system_message is not None}\"\n",
    "\n",
    "print(\"✅ ConversationMemory 类定义完成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4db02c9d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试对话记忆\n",
    "memory = ConversationMemory(max_messages=10)\n",
    "memory.set_system_message(\"你是一个有帮助的助手。\")\n",
    "\n",
    "# 模拟对话\n",
    "memory.add_user_message(\"你好！\")\n",
    "memory.add_assistant_message(\"你好！有什么可以帮助你的？\")\n",
    "memory.add_user_message(\"我想学习 Python\")\n",
    "memory.add_assistant_message(\"太好了！Python 是一门非常适合初学者的编程语言...\")\n",
    "memory.add_user_message(\"有什么好的学习资源推荐吗？\")\n",
    "memory.add_assistant_message(\"推荐以下资源：1. 官方文档 2. 廖雪峰教程...\")\n",
    "\n",
    "print(\"📝 对话历史:\")\n",
    "print(\"-\" * 50)\n",
    "for msg in memory.get_messages():\n",
    "    role = msg['role'].upper()\n",
    "    content = msg['content'][:50] + \"...\" if len(msg['content']) > 50 else msg['content']\n",
    "    print(f\"[{role}] {content}\")\n",
    "\n",
    "print(f\"\\n{memory.get_summary()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6248dbed",
   "metadata": {},
   "source": [
    "### 3.2 带记忆的聊天 Agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0fd00a2b",
   "metadata": {},
   "outputs": [],
   "source": [
    "class ChatAgentWithMemory:\n",
    "    \"\"\"带记忆的聊天 Agent\n",
    "    \n",
    "    能够记住对话上下文，实现连贯的多轮对话。\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, system_prompt: str = None, max_messages: int = 20):\n",
    "        self.memory = ConversationMemory(max_messages=max_messages)\n",
    "        if system_prompt:\n",
    "            self.memory.set_system_message(system_prompt)\n",
    "    \n",
    "    def chat(self, user_input: str) -> str:\n",
    "        \"\"\"与 Agent 对话\"\"\"\n",
    "        # 添加用户消息到记忆\n",
    "        self.memory.add_user_message(user_input)\n",
    "        \n",
    "        # 调用 LLM\n",
    "        response = client.chat.completions.create(\n",
    "            model=deployment,\n",
    "            messages=self.memory.get_messages()\n",
    "        )\n",
    "        \n",
    "        assistant_message = response.choices[0].message.content\n",
    "        \n",
    "        # 添加助手回复到记忆\n",
    "        self.memory.add_assistant_message(assistant_message)\n",
    "        \n",
    "        return assistant_message\n",
    "    \n",
    "    def reset(self):\n",
    "        \"\"\"重置对话历史\"\"\"\n",
    "        self.memory.clear()\n",
    "\n",
    "print(\"✅ ChatAgentWithMemory 类定义完成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85a414cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试带记忆的聊天 Agent\n",
    "agent = ChatAgentWithMemory(\n",
    "    system_prompt=\"你是一个友好的 Python 编程导师，用简单易懂的方式解释概念。\"\n",
    ")\n",
    "\n",
    "# 第一轮对话\n",
    "print(\"👤 用户: 什么是变量？\")\n",
    "response1 = agent.chat(\"什么是变量？\")\n",
    "print(f\"🤖 助手: {response1}\\n\")\n",
    "\n",
    "# 第二轮对话（Agent 会记住上下文）\n",
    "print(\"👤 用户: 能给我一个例子吗？\")\n",
    "response2 = agent.chat(\"能给我一个例子吗？\")\n",
    "print(f\"🤖 助手: {response2}\\n\")\n",
    "\n",
    "# 第三轮对话\n",
    "print(\"👤 用户: 它和常量有什么区别？\")\n",
    "response3 = agent.chat(\"它和常量有什么区别？\")\n",
    "print(f\"🤖 助手: {response3}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "df60f365",
   "metadata": {},
   "source": [
    "### 3.3 会话状态管理器"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0685290f",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SessionManager:\n",
    "    \"\"\"会话状态管理器\n",
    "    \n",
    "    用于跟踪和管理用户会话的状态信息，如：\n",
    "    - 用户偏好\n",
    "    - 当前任务进度\n",
    "    - 上下文变量\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, session_id: str):\n",
    "        self.session_id = session_id\n",
    "        self.state = {\n",
    "            \"created_at\": datetime.now().isoformat(),\n",
    "            \"last_active\": datetime.now().isoformat(),\n",
    "            \"context\": {},\n",
    "            \"metadata\": {}\n",
    "        }\n",
    "    \n",
    "    def set_context(self, key: str, value: Any):\n",
    "        \"\"\"设置上下文信息\"\"\"\n",
    "        self.state[\"context\"][key] = value\n",
    "        self._update_activity()\n",
    "    \n",
    "    def get_context(self, key: str, default=None) -> Any:\n",
    "        \"\"\"获取上下文信息\"\"\"\n",
    "        return self.state[\"context\"].get(key, default)\n",
    "    \n",
    "    def set_metadata(self, key: str, value: Any):\n",
    "        \"\"\"设置元数据\"\"\"\n",
    "        self.state[\"metadata\"][key] = value\n",
    "    \n",
    "    def _update_activity(self):\n",
    "        \"\"\"更新活动时间\"\"\"\n",
    "        self.state[\"last_active\"] = datetime.now().isoformat()\n",
    "    \n",
    "    def get_context_summary(self) -> str:\n",
    "        \"\"\"获取上下文摘要（用于 Prompt）\"\"\"\n",
    "        if not self.state[\"context\"]:\n",
    "            return \"\"\n",
    "        \n",
    "        summary = \"当前会话上下文:\\n\"\n",
    "        for key, value in self.state[\"context\"].items():\n",
    "            summary += f\"- {key}: {value}\\n\"\n",
    "        return summary\n",
    "\n",
    "# 测试会话管理\n",
    "session = SessionManager(\"user_123_session_1\")\n",
    "\n",
    "# 设置上下文\n",
    "session.set_context(\"user_name\", \"张三\")\n",
    "session.set_context(\"topic\", \"Python 学习\")\n",
    "session.set_context(\"skill_level\", \"初学者\")\n",
    "\n",
    "# 设置元数据\n",
    "session.set_metadata(\"language\", \"zh-CN\")\n",
    "\n",
    "print(\"📋 会话信息:\")\n",
    "print(f\"会话 ID: {session.session_id}\")\n",
    "print(f\"用户名: {session.get_context('user_name')}\")\n",
    "print(f\"主题: {session.get_context('topic')}\")\n",
    "print(f\"\\n{session.get_context_summary()}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8e0edc8a",
   "metadata": {},
   "source": [
    "## 4. 长期记忆与向量存储\n",
    "\n",
    "### 4.1 简单向量存储实现\n",
    "\n",
    "使用 Azure OpenAI 的 Embedding API 和余弦相似度实现基础向量搜索。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0507d59c",
   "metadata": {},
   "outputs": [],
   "source": [
    "class SimpleVectorStore:\n",
    "    \"\"\"简单的向量存储\n",
    "    \n",
    "    功能：\n",
    "    - 使用 Azure OpenAI Embedding 生成向量\n",
    "    - 余弦相似度搜索\n",
    "    - 支持元数据\n",
    "    \n",
    "    注意：实际生产环境应使用专业向量数据库如 Pinecone, Weaviate, Qdrant 等\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.documents = []\n",
    "        self.embeddings = []\n",
    "        self.metadata = []\n",
    "    \n",
    "    def get_embedding(self, text: str) -> List[float]:\n",
    "        \"\"\"获取文本的向量表示\"\"\"\n",
    "        response = client.embeddings.create(\n",
    "            model=embedding_deployment,\n",
    "            input=text\n",
    "        )\n",
    "        return response.data[0].embedding\n",
    "    \n",
    "    def add_document(self, text: str, metadata: Dict = None):\n",
    "        \"\"\"添加文档到向量存储\"\"\"\n",
    "        embedding = self.get_embedding(text)\n",
    "        \n",
    "        self.documents.append(text)\n",
    "        self.embeddings.append(embedding)\n",
    "        self.metadata.append(metadata or {})\n",
    "        \n",
    "        return len(self.documents) - 1  # 返回文档索引\n",
    "    \n",
    "    def add_documents(self, texts: List[str], metadatas: List[Dict] = None):\n",
    "        \"\"\"批量添加文档\"\"\"\n",
    "        if metadatas is None:\n",
    "            metadatas = [{}] * len(texts)\n",
    "        \n",
    "        for text, meta in zip(texts, metadatas):\n",
    "            self.add_document(text, meta)\n",
    "        \n",
    "        print(f\"✅ 已添加 {len(texts)} 个文档\")\n",
    "    \n",
    "    def cosine_similarity(self, vec1: List[float], vec2: List[float]) -> float:\n",
    "        \"\"\"计算余弦相似度\"\"\"\n",
    "        vec1 = np.array(vec1)\n",
    "        vec2 = np.array(vec2)\n",
    "        \n",
    "        return np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))\n",
    "    \n",
    "    def search(self, query: str, top_k: int = 3) -> List[Dict]:\n",
    "        \"\"\"搜索相关文档\n",
    "        \n",
    "        Args:\n",
    "            query: 查询文本\n",
    "            top_k: 返回最相关的 k 个结果\n",
    "            \n",
    "        Returns:\n",
    "            包含文档、相似度和元数据的结果列表\n",
    "        \"\"\"\n",
    "        if not self.documents:\n",
    "            return []\n",
    "        \n",
    "        query_embedding = self.get_embedding(query)\n",
    "        \n",
    "        # 计算所有文档的相似度\n",
    "        similarities = []\n",
    "        for i, doc_embedding in enumerate(self.embeddings):\n",
    "            similarity = self.cosine_similarity(query_embedding, doc_embedding)\n",
    "            similarities.append({\n",
    "                \"index\": i,\n",
    "                \"similarity\": float(similarity),\n",
    "                \"document\": self.documents[i],\n",
    "                \"metadata\": self.metadata[i]\n",
    "            })\n",
    "        \n",
    "        # 排序并返回 top_k\n",
    "        similarities.sort(key=lambda x: x[\"similarity\"], reverse=True)\n",
    "        return similarities[:top_k]\n",
    "    \n",
    "    def __len__(self):\n",
    "        return len(self.documents)\n",
    "\n",
    "print(\"✅ SimpleVectorStore 类定义完成\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2900118e",
   "metadata": {},
   "source": [
    "### 4.2 测试向量存储"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "98759f5d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建向量存储\n",
    "vector_store = SimpleVectorStore()\n",
    "\n",
    "# 添加知识库文档\n",
    "knowledge_docs = [\n",
    "    \"Python 是一种解释型、面向对象的高级编程语言，由 Guido van Rossum 于 1991 年创建。\",\n",
    "    \"Python 的主要特点包括：简洁易读的语法、丰富的标准库、跨平台兼容性。\",\n",
    "    \"JavaScript 是一种用于网页开发的脚本语言，主要运行在浏览器中。\",\n",
    "    \"Java 是一种面向对象的编程语言，广泛用于企业级应用和 Android 开发。\",\n",
    "    \"机器学习是人工智能的一个分支，使计算机能够从数据中学习。\",\n",
    "    \"深度学习使用多层神经网络处理复杂数据，是机器学习的子领域。\",\n",
    "    \"RAG（检索增强生成）是一种结合检索和生成的 AI 技术，可以让 LLM 访问外部知识。\"\n",
    "]\n",
    "\n",
    "vector_store.add_documents(knowledge_docs)\n",
    "print(f\"📚 向量存储中共有 {len(vector_store)} 个文档\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8756989",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试向量搜索\n",
    "query = \"什么是 Python？它有什么特点？\"\n",
    "\n",
    "print(f\"🔍 查询: {query}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "results = vector_store.search(query, top_k=3)\n",
    "\n",
    "for i, result in enumerate(results, 1):\n",
    "    print(f\"\\n📄 结果 {i} (相似度: {result['similarity']:.4f})\")\n",
    "    print(f\"   {result['document']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "efe2902f",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试另一个查询\n",
    "query2 = \"机器学习和深度学习的关系\"\n",
    "\n",
    "print(f\"🔍 查询: {query2}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "results2 = vector_store.search(query2, top_k=3)\n",
    "\n",
    "for i, result in enumerate(results2, 1):\n",
    "    print(f\"\\n📄 结果 {i} (相似度: {result['similarity']:.4f})\")\n",
    "    print(f\"   {result['document']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1b7694ae",
   "metadata": {},
   "source": [
    "## 5. RAG（检索增强生成）实现\n",
    "\n",
    "RAG 的核心流程：\n",
    "1. **检索 (Retrieve)**: 根据用户问题，从知识库中检索相关文档\n",
    "2. **增强 (Augment)**: 将检索到的文档作为上下文添加到 Prompt\n",
    "3. **生成 (Generate)**: LLM 基于上下文生成答案"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ff1d188c",
   "metadata": {},
   "outputs": [],
   "source": [
    "class RAGAgent:\n",
    "    \"\"\"检索增强生成 Agent\n",
    "    \n",
    "    结合向量检索和 LLM 生成，实现知识库问答。\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, vector_store: SimpleVectorStore):\n",
    "        self.vector_store = vector_store\n",
    "    \n",
    "    def retrieve(self, query: str, top_k: int = 3) -> List[str]:\n",
    "        \"\"\"检索相关文档\"\"\"\n",
    "        results = self.vector_store.search(query, top_k)\n",
    "        return [r[\"document\"] for r in results]\n",
    "    \n",
    "    def generate_answer(self, query: str, context_docs: List[str]) -> str:\n",
    "        \"\"\"基于检索到的文档生成答案\"\"\"\n",
    "        # 构建上下文\n",
    "        context = \"\\n\\n\".join([\n",
    "            f\"文档 {i+1}: {doc}\" \n",
    "            for i, doc in enumerate(context_docs)\n",
    "        ])\n",
    "        \n",
    "        prompt = f\"\"\"基于以下参考文档回答问题。如果文档中没有相关信息，请明确说明。\n",
    "\n",
    "参考文档:\n",
    "{context}\n",
    "\n",
    "问题: {query}\n",
    "\n",
    "请给出准确、简洁的答案，并说明信息来源。\"\"\"\n",
    "        \n",
    "        response = client.chat.completions.create(\n",
    "            model=deployment,\n",
    "            messages=[\n",
    "                {\"role\": \"system\", \"content\": \"你是一个知识问答助手，基于提供的文档准确回答问题。\"},\n",
    "                {\"role\": \"user\", \"content\": prompt}\n",
    "            ]\n",
    "        )\n",
    "        \n",
    "        return response.choices[0].message.content\n",
    "    \n",
    "    def answer(self, query: str, top_k: int = 3) -> Dict:\n",
    "        \"\"\"完整的 RAG 流程\n",
    "        \n",
    "        Returns:\n",
    "            包含查询、检索文档和答案的字典\n",
    "        \"\"\"\n",
    "        # 1. 检索\n",
    "        retrieved_docs = self.retrieve(query, top_k)\n",
    "        \n",
    "        # 2. 生成\n",
    "        answer = self.generate_answer(query, retrieved_docs)\n",
    "        \n",
    "        return {\n",
    "            \"query\": query,\n",
    "            \"retrieved_documents\": retrieved_docs,\n",
    "            \"answer\": answer\n",
    "        }\n",
    "\n",
    "print(\"✅ RAGAgent 类定义完成\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "824089f7",
   "metadata": {},
   "source": [
    "### 5.1 测试 RAG Agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41be9271",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建 RAG Agent（使用之前的向量存储）\n",
    "rag_agent = RAGAgent(vector_store)\n",
    "\n",
    "# 测试问答\n",
    "question = \"Python 是什么时候创建的？创建者是谁？\"\n",
    "\n",
    "print(f\"❓ 问题: {question}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "result = rag_agent.answer(question)\n",
    "\n",
    "print(\"📚 检索到的文档:\")\n",
    "for i, doc in enumerate(result[\"retrieved_documents\"], 1):\n",
    "    print(f\"  {i}. {doc[:80]}...\" if len(doc) > 80 else f\"  {i}. {doc}\")\n",
    "\n",
    "print(f\"\\n💡 答案:\\n{result['answer']}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49fb0f47",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试另一个问题\n",
    "question2 = \"RAG 技术是什么？它有什么用处？\"\n",
    "\n",
    "print(f\"❓ 问题: {question2}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "result2 = rag_agent.answer(question2)\n",
    "\n",
    "print(\"📚 检索到的文档:\")\n",
    "for i, doc in enumerate(result2[\"retrieved_documents\"], 1):\n",
    "    print(f\"  {i}. {doc}\")\n",
    "\n",
    "print(f\"\\n💡 答案:\\n{result2['answer']}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8d31aa82",
   "metadata": {},
   "source": [
    "## 6. 个人笔记问答 Agent\n",
    "\n",
    "结合所有概念，构建一个完整的笔记管理和问答系统。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "43489e27",
   "metadata": {},
   "outputs": [],
   "source": [
    "class NoteAgent:\n",
    "    \"\"\"个人笔记管理和问答 Agent\n",
    "    \n",
    "    功能：\n",
    "    - 添加和管理笔记\n",
    "    - 支持标签分类\n",
    "    - 语义搜索笔记\n",
    "    - 基于笔记回答问题\n",
    "    - 按标签总结笔记\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self):\n",
    "        self.vector_store = SimpleVectorStore()\n",
    "        self.notes = []\n",
    "    \n",
    "    def add_note(self, title: str, content: str, tags: List[str] = None) -> Dict:\n",
    "        \"\"\"添加笔记\"\"\"\n",
    "        note = {\n",
    "            \"id\": len(self.notes) + 1,\n",
    "            \"title\": title,\n",
    "            \"content\": content,\n",
    "            \"tags\": tags or [],\n",
    "            \"created_at\": datetime.now().isoformat()\n",
    "        }\n",
    "        \n",
    "        self.notes.append(note)\n",
    "        \n",
    "        # 添加到向量存储\n",
    "        full_text = f\"{title}\\n{content}\"\n",
    "        self.vector_store.add_document(\n",
    "            full_text,\n",
    "            metadata={\n",
    "                \"note_id\": note[\"id\"],\n",
    "                \"title\": title,\n",
    "                \"tags\": tags\n",
    "            }\n",
    "        )\n",
    "        \n",
    "        print(f\"✅ 笔记已添加: {title}\")\n",
    "        return note\n",
    "    \n",
    "    def search_notes(self, query: str, top_k: int = 3) -> List[Dict]:\n",
    "        \"\"\"搜索笔记\"\"\"\n",
    "        results = self.vector_store.search(query, top_k)\n",
    "        \n",
    "        # 关联完整笔记信息\n",
    "        note_results = []\n",
    "        for result in results:\n",
    "            note_id = result[\"metadata\"].get(\"note_id\")\n",
    "            if note_id and note_id <= len(self.notes):\n",
    "                note = self.notes[note_id - 1]\n",
    "                note_results.append({\n",
    "                    \"note\": note,\n",
    "                    \"similarity\": result[\"similarity\"]\n",
    "                })\n",
    "        \n",
    "        return note_results\n",
    "    \n",
    "    def answer_from_notes(self, question: str) -> Dict:\n",
    "        \"\"\"从笔记中回答问题\"\"\"\n",
    "        # 搜索相关笔记\n",
    "        relevant_notes = self.search_notes(question, top_k=3)\n",
    "        \n",
    "        if not relevant_notes:\n",
    "            return {\n",
    "                \"question\": question,\n",
    "                \"answer\": \"抱歉，我在你的笔记中没有找到相关信息。\",\n",
    "                \"sources\": []\n",
    "            }\n",
    "        \n",
    "        # 构建上下文\n",
    "        context = \"\\n\\n\".join([\n",
    "            f\"笔记: {n['note']['title']}\\n内容: {n['note']['content']}\"\n",
    "            for n in relevant_notes\n",
    "        ])\n",
    "        \n",
    "        # 生成答案\n",
    "        prompt = f\"\"\"基于我的以下笔记内容回答问题：\n",
    "\n",
    "{context}\n",
    "\n",
    "问题: {question}\n",
    "\n",
    "请基于笔记内容给出答案。如果笔记中没有相关信息，请说明。\"\"\"\n",
    "        \n",
    "        response = client.chat.completions.create(\n",
    "            model=deployment,\n",
    "            messages=[\n",
    "                {\"role\": \"system\", \"content\": \"你是一个个人笔记助手，帮助用户从笔记中找到信息。\"},\n",
    "                {\"role\": \"user\", \"content\": prompt}\n",
    "            ]\n",
    "        )\n",
    "        \n",
    "        return {\n",
    "            \"question\": question,\n",
    "            \"answer\": response.choices[0].message.content,\n",
    "            \"sources\": [n[\"note\"][\"title\"] for n in relevant_notes]\n",
    "        }\n",
    "    \n",
    "    def get_notes_by_tag(self, tag: str) -> List[Dict]:\n",
    "        \"\"\"获取某个标签的所有笔记\"\"\"\n",
    "        return [n for n in self.notes if tag in n.get(\"tags\", [])]\n",
    "    \n",
    "    def summarize_by_tag(self, tag: str) -> str:\n",
    "        \"\"\"总结某个标签的所有笔记\"\"\"\n",
    "        tagged_notes = self.get_notes_by_tag(tag)\n",
    "        \n",
    "        if not tagged_notes:\n",
    "            return f\"没有找到标签为 '{tag}' 的笔记。\"\n",
    "        \n",
    "        notes_content = \"\\n\\n\".join([\n",
    "            f\"标题: {n['title']}\\n内容: {n['content']}\"\n",
    "            for n in tagged_notes\n",
    "        ])\n",
    "        \n",
    "        prompt = f\"\"\"请总结以下关于 '{tag}' 的笔记内容：\n",
    "\n",
    "{notes_content}\n",
    "\n",
    "生成一个简洁的总结，提取主要观点和关键信息。\"\"\"\n",
    "        \n",
    "        response = client.chat.completions.create(\n",
    "            model=deployment,\n",
    "            messages=[{\"role\": \"user\", \"content\": prompt}]\n",
    "        )\n",
    "        \n",
    "        return response.choices[0].message.content\n",
    "\n",
    "print(\"✅ NoteAgent 类定义完成\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b5eca75",
   "metadata": {},
   "source": [
    "### 6.1 测试笔记 Agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bab9cbb8",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 创建笔记 Agent\n",
    "note_agent = NoteAgent()\n",
    "\n",
    "# 添加笔记\n",
    "note_agent.add_note(\n",
    "    \"Python 基础语法\",\n",
    "    \"Python 是一种解释型语言，语法简洁。主要特点：动态类型、自动内存管理、缩进表示代码块。\",\n",
    "    tags=[\"编程\", \"Python\"]\n",
    ")\n",
    "\n",
    "note_agent.add_note(\n",
    "    \"Python 常用库\",\n",
    "    \"Python 常用库包括：NumPy（数值计算）、Pandas（数据分析）、Matplotlib（可视化）、Requests（HTTP请求）。\",\n",
    "    tags=[\"编程\", \"Python\"]\n",
    ")\n",
    "\n",
    "note_agent.add_note(\n",
    "    \"机器学习入门\",\n",
    "    \"机器学习是 AI 的子领域。主要分类：监督学习、无监督学习、强化学习。常用库：scikit-learn, TensorFlow, PyTorch。\",\n",
    "    tags=[\"AI\", \"机器学习\"]\n",
    ")\n",
    "\n",
    "note_agent.add_note(\n",
    "    \"Web 开发框架\",\n",
    "    \"Python Web 框架：Flask（轻量级、灵活）、Django（全栈、内置 ORM）、FastAPI（异步、高性能、自动文档）。\",\n",
    "    tags=[\"编程\", \"Python\", \"Web\"]\n",
    ")\n",
    "\n",
    "print(f\"\\n📚 共添加 {len(note_agent.notes)} 条笔记\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "72f68df6",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试笔记问答\n",
    "question = \"Python 有哪些常用的库？\"\n",
    "\n",
    "print(f\"❓ 问题: {question}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "result = note_agent.answer_from_notes(question)\n",
    "\n",
    "print(f\"💡 答案:\\n{result['answer']}\")\n",
    "print(f\"\\n📖 来源: {', '.join(result['sources'])}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a49eb3db",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 测试另一个问题\n",
    "question2 = \"Python 的 Web 框架有哪些？各有什么特点？\"\n",
    "\n",
    "print(f\"❓ 问题: {question2}\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "result2 = note_agent.answer_from_notes(question2)\n",
    "\n",
    "print(f\"💡 答案:\\n{result2['answer']}\")\n",
    "print(f\"\\n📖 来源: {', '.join(result2['sources'])}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "40070879",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 按标签总结笔记\n",
    "tag = \"Python\"\n",
    "\n",
    "print(f\"📑 总结标签 '{tag}' 的所有笔记\\n\")\n",
    "print(\"=\" * 60)\n",
    "\n",
    "summary = note_agent.summarize_by_tag(tag)\n",
    "print(summary)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "da9b48d3",
   "metadata": {},
   "source": [
    "## 7. 文本分块策略\n",
    "\n",
    "处理长文档时，需要将文本分割成合适大小的块。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dcbfaef1",
   "metadata": {},
   "outputs": [],
   "source": [
    "def chunk_text(text: str, chunk_size: int = 500, overlap: int = 50) -> List[str]:\n",
    "    \"\"\"将长文本分块\n",
    "    \n",
    "    Args:\n",
    "        text: 原始文本\n",
    "        chunk_size: 每块的大小（字符数）\n",
    "        overlap: 块之间的重叠大小\n",
    "        \n",
    "    Returns:\n",
    "        文本块列表\n",
    "    \"\"\"\n",
    "    chunks = []\n",
    "    start = 0\n",
    "    \n",
    "    while start < len(text):\n",
    "        end = start + chunk_size\n",
    "        chunk = text[start:end]\n",
    "        chunks.append(chunk)\n",
    "        start = end - overlap  # 重叠部分\n",
    "    \n",
    "    return chunks\n",
    "\n",
    "# 测试分块\n",
    "long_text = \"\"\"人工智能（Artificial Intelligence，AI）是计算机科学的一个重要分支。\n",
    "它的目标是创建能够执行通常需要人类智能的任务的系统。这些任务包括学习、推理、问题解决、\n",
    "感知和语言理解等。人工智能的发展可以追溯到20世纪50年代，当时科学家们开始探索机器是否能够思考。\n",
    "\n",
    "机器学习是人工智能的一个子领域，它使计算机能够从数据中学习，而无需显式编程。\n",
    "深度学习是机器学习的一个分支，它使用多层神经网络来处理复杂的数据。\n",
    "\n",
    "大语言模型（LLM）是最近人工智能领域的重大突破。这些模型通过在大量文本数据上训练，\n",
    "能够理解和生成人类语言。GPT、Claude、LLaMA等都是著名的大语言模型。\n",
    "\n",
    "AI Agent是基于大语言模型构建的智能系统，能够自主完成复杂任务。\n",
    "它们可以使用工具、进行推理、记忆信息，并与用户进行自然交互。\"\"\"\n",
    "\n",
    "chunks = chunk_text(long_text, chunk_size=200, overlap=30)\n",
    "\n",
    "print(f\"原文长度: {len(long_text)} 字符\")\n",
    "print(f\"分块数量: {len(chunks)}\\n\")\n",
    "\n",
    "for i, chunk in enumerate(chunks, 1):\n",
    "    print(f\"--- 块 {i} ({len(chunk)} 字符) ---\")\n",
    "    print(chunk[:100] + \"...\" if len(chunk) > 100 else chunk)\n",
    "    print()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "899774d3",
   "metadata": {},
   "source": [
    "## 8. 总结\n",
    "\n",
    "本阶段我们学习了：\n",
    "\n",
    "### 8.1 核心概念\n",
    "- **记忆层次**: 工作记忆 → 短期记忆 → 长期记忆\n",
    "- **对话管理**: 维护对话历史，支持多轮对话\n",
    "- **向量存储**: 使用 Embedding 进行语义搜索\n",
    "- **RAG**: 检索增强生成，让 LLM 访问外部知识\n",
    "\n",
    "### 8.2 实践项目\n",
    "1. **ConversationMemory**: 对话历史管理\n",
    "2. **ChatAgentWithMemory**: 带记忆的聊天 Agent\n",
    "3. **SimpleVectorStore**: 简单向量存储\n",
    "4. **RAGAgent**: 检索增强生成 Agent\n",
    "5. **NoteAgent**: 个人笔记问答系统\n",
    "\n",
    "### 8.3 生产建议\n",
    "- 使用专业向量数据库: Pinecone, Weaviate, Qdrant, Milvus\n",
    "- 实现更智能的分块策略（按段落、语义分割）\n",
    "- 添加混合检索（向量 + 关键词）\n",
    "- 考虑记忆压缩和摘要\n",
    "\n",
    "### 8.4 下一步\n",
    "进入 [阶段 5：多 Agent 协同与角色体系](../docs/stage5-multi-agent.md)，学习如何构建多 Agent 协作系统。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
