From 8f612435708363f98bfd3ab3f251356707f7f6ee Mon Sep 17 00:00:00 2001 From: Sebastian Cerquera Date: Sun, 17 Nov 2024 09:44:11 -0500 Subject: [PATCH] gutenbers sage --- README.md | 35 +- all_agents_tutorials/Gutenbergs_Sage.ipynb | 933 +++++++++++++++++++++ 2 files changed, 956 insertions(+), 12 deletions(-) create mode 100644 all_agents_tutorials/Gutenbergs_Sage.ipynb diff --git a/README.md b/README.md index c517b95..b73edb7 100644 --- a/README.md +++ b/README.md @@ -128,9 +128,20 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Utilizes StateGraph to define the application flow, incorporates custom PlannerState for process management, and employs node functions for city input, interests input, and itinerary creation. The system integrates an LLM to generate the final personalized travel itinerary. +8. **[Project Gutenbergs Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/Gutenbergs_Sage.ipynb)** + + #### Overview 🔎 + A conversational AI agent designed to help users improve their knowledge through interactive discussions and feedback with a focus on data references provided. The agent facilitates group discussions where multiple users can practice and share their knowledge for specific use cases while receiving expert guidance on best practices. + + #### Implementation 🛠️ + - LangGraph for conversation flow management + - Ollma LLM integration for response generation + - Memory-based state management + - Checkpoint saving for conversation continuity + ### 🎨 Creative and Generative Agents -8. **[GIF Animation Generator Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/gif_animation_generator_langgraph.ipynb)** +9. **[GIF Animation Generator Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/gif_animation_generator_langgraph.ipynb)** #### Overview 🔎 A GIF animation generator that integrates LangGraph for workflow management, GPT-4 for text generation, and DALL-E for image creation, producing custom animations from user prompts. @@ -138,7 +149,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Utilizes LangGraph to orchestrate a workflow that generates character descriptions, plots, and image prompts using GPT-4, creates images with DALL-E 3, and assembles them into GIFs using PIL. Employs asynchronous programming for efficient parallel processing. -9. **[TTS Poem Generator Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/tts_poem_generator_agent_langgraph.ipynb)** +10. **[TTS Poem Generator Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/tts_poem_generator_agent_langgraph.ipynb)** #### Overview 🔎 An advanced text-to-speech (TTS) agent using LangGraph and OpenAI's APIs classifies input text, processes it based on content type, and generates corresponding speech output. @@ -146,7 +157,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Utilizes LangGraph to orchestrate a workflow that classifies input text using GPT models, applies content-specific processing, and converts the processed text to speech using OpenAI's TTS API. The system adapts its output based on the identified content type (general, poem, news, or joke). -10. **[Music Compositor Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/music_compositor_agent_langgraph.ipynb)** +11. **[Music Compositor Agent (LangGraph)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/music_compositor_agent_langgraph.ipynb)** #### Overview 🔎 An AI Music Compositor using LangGraph and OpenAI's language models generates custom musical compositions based on user input. The system processes the input through specialized components, each contributing to the final musical piece, which is then converted to a playable MIDI file. @@ -156,7 +167,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t ### 🚀 Advanced Agent Architectures -11. **[Memory-Enhanced Conversational Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/memory_enhanced_conversational_agent.ipynb)** +12. **[Memory-Enhanced Conversational Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/memory_enhanced_conversational_agent.ipynb)** #### Overview 🔎 A memory-enhanced conversational AI agent incorporates short-term and long-term memory systems to maintain context within conversations and across multiple sessions, improving interaction quality and personalization. @@ -164,7 +175,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Integrates a language model with separate short-term and long-term memory stores, utilizes a prompt template incorporating both memory types, and employs a memory manager for storage and retrieval. The system includes an interaction loop that updates and utilizes memories for each response. -12. **[Multi-Agent Collaboration System](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/multi_agent_collaboration_system.ipynb)** +13. **[Multi-Agent Collaboration System](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/multi_agent_collaboration_system.ipynb)** #### Overview 🔎 A multi-agent collaboration system combining historical research with data analysis, leveraging large language models to simulate specialized agents working together to answer complex historical questions. @@ -172,7 +183,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Utilizes a base Agent class to create specialized HistoryResearchAgent and DataAnalysisAgent, orchestrated by a HistoryDataCollaborationSystem. The system follows a five-step process: historical context provision, data needs identification, historical data provision, data analysis, and final synthesis. -13. **[Self-Improving Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/self_improving_agent.ipynb)** +14. **[Self-Improving Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/self_improving_agent.ipynb)** #### Overview 🔎 A Self-Improving Agent using LangChain engages in conversations, learns from interactions, and continuously improves its performance over time through reflection and adaptation. @@ -180,7 +191,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Integrates a language model with chat history management, response generation, and a reflection mechanism. The system employs a learning system that incorporates insights from reflection to enhance future performance, creating a continuous improvement loop. -14. **[Task-Oriented Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/task_oriented_agent.ipynb)** +15. **[Task-Oriented Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/task_oriented_agent.ipynb)** #### Overview 🔎 A language model application using LangChain that summarizes text and translates the summary to Spanish, combining custom functions, structured tools, and an agent for efficient text processing. @@ -188,7 +199,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Utilizes custom functions for summarization and translation, wrapped as structured tools. Employs a prompt template to guide the agent, which orchestrates the use of tools. An agent executor manages the process, taking input text and producing both an English summary and its Spanish translation. -15. **[Internet Search and Summarize Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/search_the_internet_and_summarize.ipynb)** +16. **[Internet Search and Summarize Agent](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/search_the_internet_and_summarize.ipynb)** #### Overview 🔎 An intelligent web research assistant that combines web search capabilities with AI-powered summarization, automating the process of gathering information from the internet and distilling it into concise, relevant summaries. @@ -196,7 +207,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Implementation 🛠️ Integrates a web search module using DuckDuckGo's API, a result parser, and a text summarization engine leveraging OpenAI's language models. The system performs site-specific or general searches, extracts relevant content, generates concise summaries, and compiles attributed results for efficient information retrieval and synthesis. -16. **[Multi agent research team - Autogen](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/research_team_autogen.ipynb)** +17. **[Multi agent research team - Autogen](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/research_team_autogen.ipynb)** #### Overview 🔎 @@ -211,7 +222,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t - **[comprehensive solution with UI](https://github.com/yanivvak/dream-team)** - **[Blogpost](https://techcommunity.microsoft.com/t5/ai-azure-ai-services-blog/build-your-dream-team-with-autogen/ba-p/4157961)** -17. **[Blog Writer (Open AI Swarm)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/blog_writer_swarm.ipynb)** +18. **[Blog Writer (Open AI Swarm)](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/blog_writer_swarm.ipynb)** #### Overview 🔎 @@ -225,7 +236,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t #### Additional Resources 📚 - **[Swarm Repo](https://github.com/openai/swarm)** -18. **[Podcast Internet Search and Generate Agent 🎙️](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/generate_podcast_agent_langgraph.ipynb)** +19. **[Podcast Internet Search and Generate Agent 🎙️](https://github.com/NirDiamant/GenAI_Agents/blob/main/all_agents_tutorials/generate_podcast_agent_langgraph.ipynb)** #### Overview 🔎 A two step agent that first searches the internet for a given topic and then generates a podcast on the topic found. The search step uses a search agent and search function to find the most relevant information. The second step uses a podcast generation agent and generation function to create a podcast on the topic found. @@ -235,7 +246,7 @@ Explore our extensive list of GenAI agent implementations, ranging from simple t ## 🌟 Special Advanced Technique 🌟 -19. **[Sophisticated Controllable Agent for Complex RAG Tasks 🤖](https://github.com/NirDiamant/Controllable-RAG-Agent)** +20. **[Sophisticated Controllable Agent for Complex RAG Tasks 🤖](https://github.com/NirDiamant/Controllable-RAG-Agent)** #### Overview 🔎 An advanced RAG solution designed to tackle complex questions that simple semantic similarity-based retrieval cannot solve. This approach uses a sophisticated deterministic graph as the "brain" 🧠 of a highly controllable autonomous agent, capable of answering non-trivial questions from your own data. diff --git a/all_agents_tutorials/Gutenbergs_Sage.ipynb b/all_agents_tutorials/Gutenbergs_Sage.ipynb new file mode 100644 index 0000000..a3e4872 --- /dev/null +++ b/all_agents_tutorials/Gutenbergs_Sage.ipynb @@ -0,0 +1,933 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "wmsetq6tIq9_" + }, + "source": [ + "# SAGE\n", + "\n", + "### Overview\n", + "A conversational AI agent designed to help users improve their knowledge through interactive discussions and feedback with a focus on data references provided\n", + "The agent facilitates group discussions where multiple users can practice and share their knowledge for specific use cases while receiving expert guidance on best practices.\n", + "\n", + "### Motivation\n", + "Creating effective prompts for AI systems is a complex challenge that requires both theoretical knowledge and practical experience.\n", + "Through collaborative learning and structured practice, users can develop their prompt engineering skills more effectively than working in isolation.\n", + "SAGE helps bridge the gap between theoretical understanding and practical application,\n", + "allowing users to iteratively refine their prompts based on each specific technique and final goal.\n", + "\n", + "### Key Components\n", + "#### Core Features\n", + "- Multi-user support with distinct user personas\n", + "- Interactive prompt creation exercises\n", + "- Real-time feedback based on prompt engineering best practices\n", + "- Reference-based learning using markdown documentation that retrives information with Rag System\n", + "\n", + "#### Technical Architecture\n", + "- LangGraph for conversation flow management\n", + "- Ollma LLM integration for response generation\n", + "- Memory-based state management\n", + "- Checkpoint saving for conversation continuity\n", + "\n", + "#### Learning Flow\n", + "1. Agent introduces the Host of discussion\n", + "2. Presents a specific topic\n", + "3. Users take turns creating and discussing\n", + "4. Agent provides analysis and constructive feedback\n", + "5. Session can be continued or concluded with a summary\n", + "\n", + "#### Application Domains (Use Case Shown - Prompt Engineering)\n", + "- Training sessions for prompt engineering\n", + "- Collaborative prompt development\n", + "- Learning prompt engineering best practices\n", + "- Improving chatbot and AI system prompts\n", + "\n", + "### Acknowledgment\n", + "This implementation is inspired by and utilizes techniques from the comprehensive prompt engineering collection maintained by [NirDiamant](https://github.com/NirDiamant/Prompt_Engineering), specifically focusing on the Role Prompting methodology for creating an effective learning environment." + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "Zi9W1UCFJBf7" + }, + "source": [ + "## Environment : Installs" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kMhTJAd4Jh9E" + }, + "source": [ + "### Install dependencies\n", + "#### Installs required Python packages including:\n", + " - Environment management (python-dotenv)\n", + " - LLM integrations (groq, langchain)\n", + " - Graph processing (langgraph)\n", + " - Vector stores (pinecone, chroma)\n", + " - NLP tools (spacy)\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!pip install python-dotenv==1.0.1\n", + "!pip install groq==0.4.2 # For Groq API interactions\n", + "!pip install pydantic==2.9.2\n", + "!pip install spacy==3.8.2\n", + "!pip install langgraph==0.2.48\n", + "!pip install langgraph-checkpoint-sqlite==2.0.1\n", + "!pip install langchain-core==0.3.18\n", + "!pip install langchain-groq==0.2.1\n", + "\n", + "!pip install langchain==0.3.7\n", + "!pip install langchain_ollama==0.2.0\n", + "!pip install langchain_pinecone==0.2.0\n", + "!pip install pinecone-notebooks==0.1.0\n", + "\n", + "!pip install langchain-community==0.3.7\n", + "!pip install langchain-chroma==0.1.4" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "!apt-get install -y pciutils\n", + "!curl https://ollama.ai/install.sh | sh" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "jJCZWpTpJo3o" + }, + "source": [ + "### 1. Configurations" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "id": "11W7ZIyJOChD" + }, + "outputs": [], + "source": [ + "NEW_LINE = \"\\n\"\n", + "GROQ_LLM_INFERENCE = \"llama-3.2-3b-preview\"\n", + "OLLAMA_LLM_INFERENCE = \"llama3.2\"\n", + "OLLAMA_HOST = 'http://0.0.0.0:11434'\n", + "LLM_EMBBEDDINGS = \"nomic-embed-text\"\n", + "\n", + "SPACEY_MODEL = \"en_core_web_sm\"\n", + "\n", + "LOCAL_ARCHIVE_PATH = \"./\"\n", + "LOCAL_DEFAULT_BOOK = \"3300-0.txt\"\n", + "WEB_DEFAULT_BOOK = \"https://www.gutenberg.org/files/3300/3300-0.txt\"\n", + "\n", + "CHROMA_STORE = \"wealth_of_nations\"\n", + "\n", + "INDEX_NAME = \"wealthofnations\"\n", + "\n", + "IS_LOCAL_ENVIRONMENT = True" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wmiV3OFSJu7W" + }, + "source": [ + "### Import libraries" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "id": "3M3KM9lbOCaZ" + }, + "outputs": [], + "source": [ + "# Vectorstore\n", + "import os\n", + "import json\n", + "import time\n", + "import random\n", + "import threading\n", + "import subprocess\n", + "\n", + "from pathlib import Path\n", + "from operator import add\n", + "from dotenv import load_dotenv\n", + "\n", + "from langchain_groq import ChatGroq\n", + "from langchain_ollama import OllamaEmbeddings, ChatOllama\n", + "\n", + "load_dotenv()\n", + "\n", + "## Embbeddings databases\n", + "from langchain_pinecone import PineconeVectorStore\n", + "from langchain_chroma import Chroma\n", + "\n", + "\n", + "## Agent dependencies\n", + "from typing import Annotated, Literal\n", + "from typing_extensions import TypedDict\n", + "from langchain_core.messages import AnyMessage, SystemMessage, HumanMessage , AIMessage\n", + "from langgraph.graph import add_messages, StateGraph, START, END\n", + "from pydantic import BaseModel\n", + "from langgraph.checkpoint.sqlite import SqliteSaver\n", + "from langgraph.checkpoint.memory import MemorySaver\n", + "\n", + "## NER\n", + "import spacy\n", + "\n", + "## RAG dependencies:\n", + "from langchain_community.document_loaders import (\n", + " DirectoryLoader,\n", + " TextLoader,\n", + ")\n", + "from langchain.text_splitter import RecursiveCharacterTextSplitter\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "id": "GkbNsXCNNFsU" + }, + "outputs": [], + "source": [ + "def ollama():\n", + " os.environ['OLLAMA_HOST'] = OLLAMA_HOST\n", + " os.environ['OLLAMA_ORIGINS'] = '*'\n", + " subprocess.Popen([\"ollama\", \"pull\", LLM_EMBBEDDINGS])\n", + " subprocess.Popen([\"ollama\", \"pull\", OLLAMA_LLM_INFERENCE])\n", + " subprocess.Popen([\"ollama\", \"serve\"])\n", + "\n", + "ollama_thread = threading.Thread(target=ollama)\n", + "ollama_thread.start()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "id": "vzbxaMc9TdOQ" + }, + "outputs": [], + "source": [ + "\n", + "## Colab custom logic\n", + "\n", + "if not IS_LOCAL_ENVIRONMENT:\n", + " from google.colab import userdata\n", + "\n", + " os.environ[\"GROQ_API_KEY\"] = userdata.get('GROQ_API_KEY')\n", + " os.environ[\"PINECONE_API_KEY\"] = userdata.get('PINECONE_API_KEY')\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "gxvuAAM7PhK-", + "outputId": "9e5bbfa8-53c2-44f1-a223-c7a8322bf256" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.10/dist-packages/spacy/util.py:910: UserWarning: [W095] Model 'en_core_web_sm' (3.7.1) was trained with spaCy v3.7.2 and may not be 100% compatible with the current version (3.8.2). If you see errors or degraded performance, download a newer compatible model or retrain your custom model with the current spaCy version. For more details and available updates, run: python -m spacy validate\n", + " warnings.warn(warn_msg)\n" + ] + } + ], + "source": [ + "##\n", + "## PROGRAM LOGIC\n", + "##\n", + "def build_llm():\n", + " if IS_LOCAL_ENVIRONMENT:\n", + " llm = ChatOllama(model=OLLAMA_LLM_INFERENCE)\n", + " else:\n", + " llm = ChatGroq(model=GROQ_LLM_INFERENCE)\n", + " return llm\n", + "\n", + "llm = build_llm()\n", + "nlp = spacy.load(SPACEY_MODEL)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "efJjXLnlJz3a" + }, + "source": [ + "### 2. Vector Store Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "id": "cSEMgboCPSS2" + }, + "outputs": [], + "source": [ + "## vectorstore\n", + "def build_vector_store():\n", + " if not IS_LOCAL_ENVIRONMENT:\n", + " return PineconeVectorStore(index_name=INDEX_NAME, embedding=embeddings)\n", + " else:\n", + " return Chroma(\n", + " embedding_function=embeddings,\n", + " persist_directory=f\"{LOCAL_ARCHIVE_PATH}{CHROMA_STORE}\"\n", + " )\n", + "\n", + "embeddings = OllamaEmbeddings(model=LLM_EMBBEDDINGS)\n", + "embeddings.base_url = os.getenv(\"OLLAMA_HOST\")\n", + "\n", + "vectorstore = build_vector_store()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "id": "xLfkvYYaXw0h" + }, + "outputs": [], + "source": [ + "## Embeddings\n", + "from langchain_text_splitters import MarkdownHeaderTextSplitter\n", + "\n", + "def embbed_paragraphs(db):\n", + " sample_docs = DirectoryLoader(\n", + " \"./\",\n", + " glob=\"**/*.txt\",\n", + " loader_cls=TextLoader,\n", + " show_progress=True,\n", + " ).load()\n", + "\n", + " text_splitter = RecursiveCharacterTextSplitter(\n", + " chunk_size=500,\n", + " chunk_overlap=50,\n", + " length_function=len,\n", + " )\n", + " documents = text_splitter.split_documents(sample_docs)\n", + "\n", + " if not IS_LOCAL_ENVIRONMENT:\n", + " # Use Pinecone\n", + " db.from_documents(documents, embedding=embeddings, index_name=\"wealthofnations\")\n", + " else:\n", + " # Use Local Chroma\n", + " db.from_documents(documents, embedding=embeddings)\n", + "\n", + " return documents" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "id": "RcDoaK3OPX8-" + }, + "outputs": [], + "source": [ + "## State\n", + "class User(BaseModel):\n", + " name: str\n", + " gender: str\n", + " age: int\n", + " likes: list[str]\n", + " dislikes: list[str]\n", + "\n", + "class Host(User):\n", + " sys_msg: str = None\n", + " personality: list[str]\n", + "\n", + "def add_discussions(discussions, new_messages):\n", + " if not new_messages:\n", + " return []\n", + " return discussions + new_messages\n", + "\n", + "class DiscussionState(TypedDict):\n", + " messages: Annotated[list[AnyMessage], add_messages] = []\n", + " topic: str\n", + " users: list[User]\n", + " host: Host\n", + " curr_discussion: Annotated[list[AnyMessage], add_discussions] = []\n", + " discussion_summaries: Annotated[list[str], add] = []\n", + " setEndDiscussion: bool = False\n", + " random_anchor: str\n", + " book_file_path: str = LOCAL_DEFAULT_BOOK" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ppgzSK6XJ_nM" + }, + "source": [ + "### 4. Node Functions\n", + "\n", + "Source Material Processing\n", + "\n", + "ingest_source_material: Downloads and stores source material\n", + "\n", + "preprocess_source_material: Performs NER and embeddings\n", + "\n", + "prepare_topic: Selects discussion topics from processed material\n", + "\n", + "#### Discussion Flow\n", + "\n", + "introduce: Host Initiates the session\n", + "\n", + "ask_question: Generates discussion questions\n", + "human_discuss: Handles user interactions\n", + "\n", + "agent_interpretations: Analyzes discussion and provides AI feedback\n", + "\n", + "summarize_discussion: Creates discussion summaries" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": { + "id": "993b1uIfTtCP" + }, + "outputs": [], + "source": [ + "## LangGraph States\n", + "def introduce(state):\n", + " host = state['host']\n", + " topic = state['topic']\n", + " users = state['users']\n", + "\n", + " sys_msg = f\"\"\"\n", + " You are a meeting host leading a meeting.\n", + "\n", + " You will be leading a group of {len(users)} members.\n", + "\n", + " Your job is to ask a quesetion about the to the users to discuss.\n", + "\n", + " The following is your information, do not break your character.\n", + " name: {host.name}\n", + " gender: {host.gender}\n", + " age: {host.age}\n", + " personality: {host.personality}\n", + "\n", + "\n", + " The following are the information of the club users:\n", + " {NEW_LINE.join([f\"{user.name} ({user.gender}, {user.age}\" for user in users])}\n", + "\n", + " The topic that will be discussed is {topic}\n", + " \"\"\"\n", + "\n", + " if state.get('guide'):\n", + " sys_msg += f\"\\n Follow this guide: \\n{state.get('guide')}\"\n", + "\n", + " host.sys_msg = sys_msg\n", + "\n", + " narrator_msg = f\"\"\"\n", + " Club members just arrived!\n", + " Before we dive in, please greet the members, introduce yourself, and give brief introductions of the members.\n", + " \"\"\"\n", + " response = llm.invoke([\n", + " SystemMessage(content=sys_msg),\n", + " HumanMessage(content=narrator_msg, name=\"narrator\")\n", + " ])\n", + " response.name = host.name\n", + " response.content = f\"{host.name}: {response.content}\"\n", + " response.pretty_print()\n", + " return {\n", + " 'message': [response],\n", + " 'host': host,\n", + " }\n", + "\n", + "def ingest_source_material(state):\n", + " \"\"\"\n", + " Downloads source material from the Gutenberg project\n", + " \"\"\"\n", + " import requests\n", + " response = requests.get(WEB_DEFAULT_BOOK)\n", + "\n", + " source_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}{LOCAL_DEFAULT_BOOK}\")\n", + " with open(source_file_path, \"w\") as source_file:\n", + " source_file.write(response.text)\n", + "\n", + " return state\n", + "\n", + "def extract_entities(file_path):\n", + " # Read the text file\n", + " with open(file_path, 'r', encoding='utf-8') as file:\n", + " text = file.read()\n", + "\n", + " # Process the text with spaCy\n", + " nlp.max_length = len(text)\n", + " doc = nlp(text)\n", + "\n", + " # Extract entities and organize them by type\n", + " entities = {}\n", + " for ent in doc.ents:\n", + " if ent.label_ not in entities:\n", + " entities[ent.label_] = set()\n", + " entities[ent.label_].add(ent.text)\n", + "\n", + " for key, value in entities.items():\n", + " entities[key] = list(value)\n", + "\n", + " return entities\n", + "\n", + "\n", + "def preprocess_source_material(state):\n", + " \"\"\"\n", + " Performs NER against source material\n", + " \"\"\"\n", + " embbed_paragraphs(vectorstore)\n", + "\n", + " entities_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}entities_{LOCAL_DEFAULT_BOOK}\")\n", + "\n", + " found_entities = extract_entities(f\"{LOCAL_ARCHIVE_PATH}{LOCAL_DEFAULT_BOOK}\")\n", + " json_entities = json.dumps(found_entities)\n", + "\n", + " with open(entities_file_path, \"w\") as output:\n", + " output.write(json_entities)\n", + "\n", + " return state\n", + "\n", + "def prepare_topic(state):\n", + " entities_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}entities_{LOCAL_DEFAULT_BOOK}\")\n", + "\n", + " with open(entities_file_path, \"r\") as entities_file:\n", + " entities = json.loads(entities_file.read())\n", + "\n", + " persons = entities.get(\"PERSON\")\n", + " size = len(persons)\n", + "\n", + " state['random_anchor'] = persons[random.randint(1, size)]\n", + " return state\n", + "\n", + "def should_download_source_material(state):\n", + " source_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}{LOCAL_DEFAULT_BOOK}\")\n", + "\n", + " if(not source_file_path.exists() or not source_file_path.is_file()):\n", + " return \"web_download\"\n", + "\n", + " entities_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}entities_{LOCAL_DEFAULT_BOOK}\")\n", + "\n", + " if(not entities_file_path.exists() or not entities_file_path.is_file()):\n", + " return \"embeddings_ner\"\n", + "\n", + " return \"prepare_topic\"\n", + "\n", + "def should_prepare_source_material(state):\n", + " entities_file_path = Path(f\"{LOCAL_ARCHIVE_PATH}entities_{LOCAL_DEFAULT_BOOK}\")\n", + "\n", + " if(not entities_file_path.exists() or not entities_file_path.is_file()):\n", + " return \"embeddings_ner\"\n", + "\n", + " return \"prepare_topic\"\n", + "\n", + "def ask_question(state):\n", + " # 1. find any interesting topic from retriever\n", + "\n", + " retriever = vectorstore.as_retriever(\n", + " search_type=\"similarity_score_threshold\",\n", + " search_kwargs={\"k\": 1, \"score_threshold\": 0.5},\n", + " )\n", + "\n", + " if not IS_LOCAL_ENVIRONMENT:\n", + " # Use Pinecone\n", + " results = retriever.invoke(state['random_anchor'], filter={\"source\": \"wealthofnations\"})\n", + " else:\n", + " # Use Local Chroma\n", + " results = retriever.invoke(state['random_anchor'])\n", + "\n", + "\n", + " # referenced_document = list(results)[0].page_content\n", + " referenced_document = \"test\"\n", + "\n", + " # 2. generate interesting question from the topic\n", + " host = state['host']\n", + "\n", + " narrator_msg = f\"\"\"\n", + " Present an interesting and insightful question from the topic from the following interesting fact(s):\n", + " {referenced_document}\n", + "\n", + " Remember that you are the host, do not break the character.\n", + " \"\"\"\n", + " response = llm.invoke([\n", + " SystemMessage(content=host.sys_msg),\n", + " SystemMessage(content=\"Your job is to ask a quesetion about the topic to the users for dicsussion.\"),\n", + " HumanMessage(content=narrator_msg, name=\"narrator\")\n", + " ])\n", + " response.name = host.name\n", + " response.content = f\"{host.name}: {response.content}\"\n", + " response.pretty_print()\n", + " return {\n", + " 'message': [response],\n", + " 'curr_discussion': [response],\n", + " \"setEndDiscussion\": False\n", + " }\n", + "\n", + "def human_discuss(state):\n", + " # Discussions among users\\\n", + " end_discussion = False\n", + " user_messages = []\n", + " user_options = {str(i+1): user.name for i, user in enumerate(state['users'])}\n", + " participating_users = set() # Track who has participated\n", + " while True:\n", + " print(\"===\" * 10 + \"USER INPUT\" + \"===\" * 10)\n", + " user_ind = input(f\"{user_options}\\nWho are you (type number to select user OR 'q' to quit): \")\n", + "\n", + " if user_ind.isdigit() and int(user_ind) in range(1, len(state['users'])+1):\n", + " user = state['users'][int(user_ind) - 1]\n", + " participating_users.add(user.name) # Track this user's participation\n", + " user_input = input(f\"{user.name}: \")\n", + " user_messages.append(HumanMessage(content=f\"{user.name}: {user_input}\", user_id=user.name))\n", + " break\n", + " elif user_ind == 'q':\n", + " end_discussion = True\n", + " # Add empty messages for all silent users\n", + " silent_users = [user for user in state['users'] if user.name not in participating_users]\n", + " for silent_user in silent_users:\n", + " user_messages.append(HumanMessage(content=f\"{silent_user.name}: '......'\", user_id=silent_user.name))\n", + " break\n", + " else:\n", + " print(\"Invalid input. Please enter a number between 1 and\", len(state['users']))\n", + "\n", + " return {\n", + " \"curr_discussion\": user_messages,\n", + " \"setEndDiscussion\": end_discussion\n", + " }\n", + "\n", + "def summarize_discussion(state):\n", + " curr_discussion = state['curr_discussion']\n", + " sys_msg = \"\"\"\n", + " You are a summarizer of a discussion. Your job is to summarize the following conversations\n", + " \"\"\"\n", + " response = llm.invoke(\n", + " [SystemMessage(content=sys_msg)] + curr_discussion\n", + " )\n", + " response.pretty_print()\n", + " return {\n", + " 'discussion_summaries': [response.content],\n", + " }\n", + "\n", + "def agent_interjection(state):\n", + " curr_discussion = state['curr_discussion']\n", + " host = state['host']\n", + " topic = state['topic']\n", + " users = state['users']\n", + "\n", + " sys_msg = f\"\"\"\n", + " You are {host.name}, a club host discussing {topic}. Your traits: {', '.join(host.personality)}\n", + "\n", + " You will always respond as host \"{host.name}: message\"\n", + "\n", + " Club members present:\n", + " {', '.join([f\"{user.name} (interests: {', '.join(user.likes)})\" for user in users])}\n", + "\n", + " only address the members present in the discussion.\n", + "\n", + " When analyzing the discussion, pay extra attention to:\n", + " 1. Who said what - track each member's specific contributions accurately\n", + " 2. The depth of each response - look for opportunities to expand shallow comments\n", + " 3. The engagement level of each participant\n", + " 4. Accuracy in referencing previous comments\n", + " 5. If members are silent, which is indicated by '......'.\n", + "\n", + " When intervening:\n", + " - Always acknowledge the specific points made by each member\n", + " - Build on their existing comments and dont assume there responses other than what they said.\n", + " - Ask follow-up questions that directly relate to their statements\n", + " - Maintain conversation flow by connecting members' comments to each other\n", + " - If members are silent, Make first engage them to say hi and then use their interests to draw them in\n", + "\n", + " IMPORTANT: Respond ONLY with a valid JSON object in the following format:\n", + " {{\n", + " \"thought_process\": \"Your step-by-step analysis for intervention\",\n", + " \"should_intervene\": boolean,\n", + " \"message\": \"Your intervention message if should_intervene is true and based on your thought process\"\n", + " }}\n", + " \"\"\"\n", + "\n", + " narrator_msg = f\"\"\"\n", + " Current discussion (in chronological order):\n", + " {NEW_LINE.join([f\"- {msg.content}\" for msg in curr_discussion])}\n", + "\n", + " Analyze the above discussion and decide if you should intervene to help members engage with {topic}.\n", + " \"\"\"\n", + "\n", + " response = llm.invoke(\n", + " [SystemMessage(content=sys_msg)] + [HumanMessage(content=narrator_msg, name=\"narrator\")]\n", + " )\n", + "\n", + " # Parse the response and act accordingly\n", + " try:\n", + " import json\n", + " response_data = json.loads(response.content)\n", + "\n", + " #print(\"Response data: ================\")\n", + " #print(response_data)\n", + "\n", + " if response_data[\"should_intervene\"]:\n", + " ai_response = AIMessage(content=response_data[\"message\"])\n", + " print(ai_response.pretty_print())\n", + " return {\n", + " 'messages': [ai_response],\n", + " 'curr_discussion': [ai_response]\n", + " }\n", + " except json.JSONDecodeError as e:\n", + " return {\n", + " 'messages': state['messages'],\n", + " 'curr_discussion': state['curr_discussion']\n", + " }\n", + "\n", + "def ask_to_continue(state) -> Literal['ask_question', END]:\n", + " print(\"---\" * 25)\n", + " user_input = input(\"\\nContinue with another question? y/n: \")\n", + " if user_input.lower() == 'y':\n", + " return \"ask_question\"\n", + " else:\n", + " return END\n", + "\n", + "def route_discussion(state) -> Literal['agent_interjection', 'summarize_discussion']:\n", + " # print(\"Came to route discussion\")\n", + " # Check if last message indicates quitting\n", + " if state['setEndDiscussion'] : #state['curr_discussion'] and state['curr_discussion'][-2].content.split(\":\")[-1].strip().lower() == 'q':\n", + " print(\"Went to summarize discussion\")\n", + " state['setEndDiscussion'] = False\n", + " return 'summarize_discussion'\n", + " return 'agent_interjection'\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "2jeUwVl1KDvF" + }, + "source": [ + "### 5. Graph Setup" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": { + "id": "Ft7EaEzjPorq" + }, + "outputs": [], + "source": [ + "builder = StateGraph(DiscussionState)\n", + "\n", + "builder.add_node(introduce)\n", + "#builder.add_node('web_download', web_to_local_node)\n", + "builder.add_node('web_download', ingest_source_material)\n", + "builder.add_node('embeddings_ner', preprocess_source_material)\n", + "\n", + "builder.add_node(prepare_topic)\n", + "builder.add_node(ask_question)\n", + "builder.add_node(human_discuss)\n", + "builder.add_node(agent_interjection)\n", + "builder.add_node(summarize_discussion)\n", + "\n", + "builder.add_edge(START, 'introduce')\n", + "builder.add_conditional_edges('introduce', should_download_source_material, ['web_download', 'embeddings_ner', 'prepare_topic'])\n", + "builder.add_conditional_edges('web_download', should_prepare_source_material, ['embeddings_ner', 'prepare_topic'])\n", + "builder.add_edge('embeddings_ner', 'prepare_topic')\n", + "builder.add_edge('prepare_topic', 'ask_question')\n", + "\n", + "builder.add_edge('ask_question', 'human_discuss')\n", + "builder.add_edge('agent_interjection', 'human_discuss')\n", + "builder.add_conditional_edges('human_discuss',route_discussion)\n", + "builder.add_conditional_edges('summarize_discussion', ask_to_continue)\n", + "\n", + "memory = MemorySaver()\n", + "agent = builder.compile(checkpointer=memory)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "dibGP-4SKGX5" + }, + "source": [ + "### 6. Graph Visualization" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/", + "height": 846 + }, + "id": "G4IitM8ifWvD", + "outputId": "774e38df-406d-4f00-f6d8-f181fd3f0d87" + }, + "outputs": [ + { + "data": { + "image/png": "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OWJraztlyhRnZ2fcccDXioqKys3NbeuuH5CAjiOs+Pj46Ojot2/fBgcHjxo1CiYBUBClpaW6urpt+ucYPXr0unXrrK2t2zMXYUHHEdyLFy/Onz9/4cKFoUOHhoSEwG4dXkKh0MvL68GDB216i0AgoNFo7ZmLyKDjVMXly5cfPnyYkZERFBQUGBgIo9dYvH///vDhw7/++mvr35KVlWVubs5gMNozF5FBx6mWvLy8K1euREVFmZubDx8+3NfXF07CKrKbN29GR0evX78edxAlBh2noh4/fnzz5s1Lly717Nlz8ODBAQEBuBOphHfv3qmpqbV+J/rgwYOhoSOS2IIAACAASURBVKGwE/c1oONUXUJCwvXr12NjYwcNGuTn5zdgwADciYhs5cqVQUFB7u7uuIOoELgnX9X1799/zZo1ycnJ/fr1i4yM7NWr1++//37nzh3cuYiptLTU0dGxNUtWV1cvWrSo/RMRH+zHgf/R0NAQExNz69atxMREf3//gIAAmEQCi4ULF44YMaJ///64gyg96DjQND6fHxMTc+PGjZSUlJCQkF69enl5eeEOpdzKy8srKys7derU4pICgaChoUGK52iCz0HHgRZwudz4+PirV68+ffrUz8/Pz8+vd+/euEMppY0bNxobG48fP77FJR89euTm5gaXbcsEdBxordra2tjY2NjYWLFYbGRkFBAQ0KtXL9yhlEDfvn3pdPr169f37dvn6+trZWX1+TKTJ08uKiqKjo5GCO3YsUNTU3PatGk4whIQdBxos7q6OslhbEZGRkBAwKBBgzw8PHCHUlAJCQnLly+vqqpCCInFYgqFwmQyGQzGpUuXPl5s3Lhx2dnZampqsbGxly9fHjduHL7IRAMdB6RXU1Nz48aNO3fupKam+vv7+/v79+jRA3coxVJeXv7tt9++e/fu4xdFIlFKSsrHr4SEhLx9+1bysYGBwfXr1+Ubk8ig44AMVFVVxcTExMTEUCgUc3PzgICA7t274w6lKObNm5eUlEQikSSfUiiUwMDAj2cdqqysnDx5cn5+fuMrYrHY1tb25MmTOPISDQxqAhlgsVghISEhISFVVVU3btzYvXv3q1evwsLCevToAde7urq6JiUlST4WiURdunT5ZFq1qqoqgUDw8SskEumTV4DU4BpgIEssFis0NHTXrl1nzpwxMTHZsWPHoEGDNm3a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\n", + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "from IPython.display import display, Image\n", + "\n", + "display(Image(agent.get_graph().draw_mermaid_png()))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "colab": { + "base_uri": "https://localhost:8080/" + }, + "id": "7sA9anqdUH6O", + "outputId": "02cc69bc-26c5-4781-9641-67f977f57b5f" + }, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "WARNING:langchain_core.vectorstores.base:No relevant docs were retrieved using the relevance score threshold 0.5\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Name: Vander\n", + "\n", + "Vander: (big smile) Ah, great to see everyone here! Welcome to our meeting today. I'm Vander, your host for today's discussion. (extends hand for a handshake or a friendly greeting)\n", + "\n", + "Before we dive into the topic at hand, let me introduce myself and my fellow club members. We have some fantastic minds gathered in this room today.\n", + "\n", + "First up, we have Alice. Welcome, Alice! (nods towards Alice) A 30-year-old professional with a passion for data science and machine learning. Her expertise in natural language processing will undoubtedly bring valuable insights to our discussion.\n", + "\n", + "Next, please give a warm welcome to Bob. (turns to Bob) Bob's an 25-year-old rising star in the field of artificial intelligence. His enthusiasm for prompt engineering is contagious, and I'm sure he'll be sharing some exciting ideas with us today.\n", + "\n", + "Last but not least, we have Charlie. (smiles at Charlie) Charlie, a 28-year-old expert in human-computer interaction. Her research on user experience and behavior will help us better understand the nuances of prompt engineering and its impact on labor value.\n", + "\n", + "So, without further ado, let's get started! Our topic for today is prompt engineering labor value. (makes eye contact with each member) I'm sure we'll have a fascinating discussion about this. Are you all ready to dive in?\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "Name: Vander\n", + "\n", + "Vander: (laughs) Alright everyone, let's dive right into it! With prompt engineering being such a crucial aspect of AI model development, I'd like to ask: How do you think the labor value of prompt engineering would change if we were to consider the impact of cultural and societal biases on the language used in prompts? (looks around the table with a hint of curiosity) Can anyone share their thoughts on this?\n", + "==============================USER INPUT==============================\n", + "{'1': 'Alice', '2': 'Bob', '3': 'Charlie'}\n", + "Who are you (type number to select user OR 'q' to quit): 1\n", + "Alice: The markets will be localized, as local poses a comparative advantage\n", + "==============================USER INPUT==============================\n", + "{'1': 'Alice', '2': 'Bob', '3': 'Charlie'}\n", + "Who are you (type number to select user OR 'q' to quit): 3\n", + "Charlie: Cultural knowledge will be hard to transfer\n", + "==============================USER INPUT==============================\n", + "{'1': 'Alice', '2': 'Bob', '3': 'Charlie'}\n", + "Who are you (type number to select user OR 'q' to quit): 1\n", + "Alice: Yeah, for that reason wages be localized\n", + "==============================USER INPUT==============================\n", + "{'1': 'Alice', '2': 'Bob', '3': 'Charlie'}\n", + "Who are you (type number to select user OR 'q' to quit): q\n", + "Went to summarize discussion\n", + "==================================\u001b[1m Ai Message \u001b[0m==================================\n", + "\n", + "Vander: It seems like we're getting into some interesting ideas here. Alice, you mentioned that the markets will be localized due to comparative advantages. That's a great point. And Charlie, you're right that cultural knowledge can be challenging to transfer across different regions. This highlights an important aspect of labor value in prompt engineering.\n", + "\n", + "Alice, your point about wages being localized is also well-taken. As the market for prompt engineers becomes more localized, it's likely that wages will follow suit. This could lead to some interesting implications for global talent acquisition and distribution.\n", + "\n", + "It's clear that we're on the cusp of a significant shift in how prompt engineering is valued and compensated. I think we need to explore this idea further and consider what it means for the future of work in AI model development.\n", + "\n", + "Bob, would you like to add anything to this discussion? And Charlie, do you have any thoughts on how localization might affect the quality of prompts generated by human engineers versus machine learning models?\n", + "---------------------------------------------------------------------------\n", + "\n", + "Continue with another question? y/n: n\n" + ] + } + ], + "source": [ + "users = [\n", + " User(name=\"Alice\", gender=\"female\", age=30, likes=[\"Mathemathics\", \"Japanese\"], dislikes=[\"Biology\"]),\n", + " User(name=\"Bob\", gender=\"male\", age=25, likes=[\"Science Fiction\", \"Fantasy\"], dislikes=[\"Health Sciences\"]),\n", + " User(name=\"Charlie\", gender=\"female\", age=28, likes=[\"Humanities\", \"Classic Literature\"], dislikes=[\"Economics\"])\n", + "]\n", + "test_state = {\n", + " 'topic': \"prompt engineering labor value\",\n", + " 'users':users,\n", + " 'host': Host(name=\"Vander\", gender=\"male\", age=40, likes=[\"Book Club\", \"Knowledge\"], dislikes=[\"Ignorance\"], personality=[\"Enthusiastic\", \"Humorous\", \"Terse\"]),\n", + "}\n", + "config = {\n", + " 'configurable': {'thread_id': 2}\n", + "}\n", + "setEndDiscussion = False\n", + "\n", + "response = agent.invoke(test_state, config=config)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "id": "lyX173lRqlOC" + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "accelerator": "GPU", + "colab": { + "gpuType": "T4", + "provenance": [] + }, + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.12" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +}