{ "cells": [ { "cell_type": "markdown", "id": "a0eaff04", "metadata": {}, "source": [ "# Ensure the required libraries are installed i.e.\n", "!pip install sentence-transformers qdrant-client requests IPython\n" ] }, { "cell_type": "markdown", "id": "4a8cdfac", "metadata": {}, "source": [ "# Step 1: Import necessary modules" ] }, { "cell_type": "code", "execution_count": 2, "id": "b3f67c91", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Libraries installed and modules imported successfully.\n" ] } ], "source": [ "import os\n", "import uuid\n", "import re\n", "from pathlib import Path\n", "from sentence_transformers import SentenceTransformer, CrossEncoder\n", "from qdrant_client import QdrantClient, models\n", "from qdrant_client.models import SearchRequest\n", "import requests\n", "from IPython.display import Markdown, display\n", "import json\n", "\n", "print(\"Libraries installed and modules imported successfully.\")" ] }, { "cell_type": "markdown", "id": "58a4962f", "metadata": {}, "source": [ "# Step 2: Define Configuration and Global Variables\n", "This contains all your static configuration, including API keys, URLs, and file paths." ] }, { "cell_type": "code", "execution_count": null, "id": "01e548da", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Configuration variables and collection name set.\n" ] } ], "source": [ "# --- Configuration ---\n", "# API Keys should be loaded from environment variables for security.\n", "# DO NOT commit your .env file or hardcode API keys directly in the code for production.\n", "\n", "\n", "LLAMA_API_KEY = os.getenv(\"LLAMA_API_KEY\")\n", "if not LLAMA_API_KEY:\n", " raise ValueError(\"LLAMA_API_KEY not found. Please set it as an environment variable or in a .env file.\")\n", "\n", "API_URL = \"https://api.llama.com/v1/chat/completions\"\n", "HEADERS = {\n", " \"Content-Type\": \"application/json\",\n", " \"Authorization\": f\"Bearer {LLAMA_API_KEY}\"\n", "}\n", "LLAMA_MODEL = \"Llama-4-Maverick-17B-128E-Instruct-FP8\"\n", "\n", "# Qdrant Configuration (Now using In-Memory Qdrant for offline use)\n", "# No QDRANT_URL or QDRANT_API_KEY needed for in-memory client.\n", "\n", "# The Qdrant collection to be queried. This will be created in-memory.\n", "MAIN_COLLECTION_NAME = \"readme_blogs_latest\"\n", "\n", "print(\"Configuration variables and collection name set.\")" ] }, { "cell_type": "markdown", "id": "d76eccb5", "metadata": {}, "source": [ "# Step 3: Define Helper Functions\n", "It contains all the functions that handle the core logic of the application: markdown_splitter, setup_qdrant, and query_qdrant." ] }, { "cell_type": "code", "execution_count": null, "id": "2b972b21", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Helper functions for querying Qdrant defined.\n" ] } ], "source": [ "def get_qdrant_client():\n", " \"\"\"Returns an in-memory Qdrant client instance.\"\"\"\n", " # For an in-memory client, you don't pass URL or API Key.\n", " return QdrantClient(\":memory:\")\n", "\n", "def get_embedding_model():\n", " \"\"\"Returns the SentenceTransformer embedding model.\"\"\"\n", " return SentenceTransformer('all-MiniLM-L6-v2')\n", "\n", "def create_qdrant_collection(client, collection_name, vector_size):\n", " \"\"\"Creates a Qdrant collection with the specified vector size if it doesn't exist.\"\"\"\n", " try:\n", " # Check if collection exists\n", " client.get_collection(collection_name=collection_name)\n", " print(f\"Collection '{collection_name}' already exists.\")\n", " except Exception: # QdrantClient throws if collection doesn't exist\n", " print(f\"Creating collection '{collection_name}'...\")\n", " client.recreate_collection(\n", " collection_name=collection_name,\n", " vectors_config=models.VectorParams(size=vector_size, distance=models.Distance.COSINE),\n", " )\n", " print(f\"Collection '{collection_name}' created.\")\n", "\n", "def ingest_data_into_qdrant(client, collection_name, embedding_model, data_chunks):\n", " \"\"\"\n", " Ingests data (text chunks) into the Qdrant collection.\n", " You will need to replace this with your actual data loading and chunking logic.\n", " \"\"\"\n", " print(f\"Ingesting data into collection '{collection_name}'...\")\n", " if not data_chunks:\n", " print(\"No data chunks provided for ingestion.\")\n", " return\n", "\n", " points = []\n", " for i, chunk_text in enumerate(data_chunks):\n", " embedding = embedding_model.encode(chunk_text).tolist()\n", " points.append(\n", " models.PointStruct(\n", " id=i, # Unique ID for each point\n", " vector=embedding,\n", " payload={\"text\": chunk_text}\n", " )\n", " )\n", " \n", " # Ensure the collection has been created with the correct vector size\n", " # before attempting to upsert.\n", " # The vector size must match the embedding model output.\n", " embedding_size = len(embedding_model.encode(\"test\").tolist())\n", " create_qdrant_collection(client, collection_name, embedding_size)\n", "\n", " operation_info = client.upsert(\n", " collection_name=collection_name,\n", " wait=True,\n", " points=points,\n", " )\n", " print(f\"Data ingestion complete. Status: {operation_info.status}\")\n", "\n", "\n", "def query_qdrant(query, client, collection_name, top_k=5):\n", " \"\"\"Query Qdrant with hybrid search and reranking on a specified collection.\"\"\"\n", " embedding_model = get_embedding_model()\n", " query_embedding = embedding_model.encode(query).tolist()\n", " \n", " # Initial vector search\n", " try:\n", " results = client.search(\n", " collection_name=collection_name,\n", " query_vector=query_embedding,\n", " limit=top_k*2\n", " )\n", " except Exception as e:\n", " print(f\"Error during Qdrant search on collection '{collection_name}': {e}\")\n", " return []\n", " \n", " if not results:\n", " print(\"No results found in Qdrant for the given query.\")\n", " return []\n", "\n", " # Rerank using cross-encoder\n", " cross_encoder = CrossEncoder('cross-encoder/ms-marco-MiniLM-L6-v2')\n", " pairs = [(query, hit.payload[\"text\"]) for hit in results]\n", " scores = cross_encoder.predict(pairs)\n", " \n", " # Combine scores with results\n", " sorted_results = [x for _, x in sorted(zip(scores, results), key=lambda pair: pair[0], reverse=True)]\n", " return sorted_results[:top_k]\n", "\n", "print(\"Helper functions for querying Qdrant defined.\")" ] }, { "cell_type": "markdown", "id": "092d8cd8", "metadata": {}, "source": [ "# Step 4: Define the Main Blog Generation Function\n", "This function orchestrates the RAG process by calling the helper functions, building the prompt, and making the API call." ] }, { "cell_type": "code", "execution_count": null, "id": "0d682099", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Blog generation function defined.\n" ] } ], "source": [ "def generate_blog(topic):\n", " \"\"\"Generates a technical blog post based on a topic using RAG.\"\"\"\n", " print(\"Getting Qdrant client and querying pre-existing collection...\")\n", " client = get_qdrant_client()\n", " embedding_model = get_embedding_model()\n", "\n", " # IMPORTANT: For in-memory Qdrant, you MUST ingest your data every time\n", " # the script runs or the client is initialized, as it's not persistent.\n", " # Replace this with your actual data loading and chunking.\n", " # Example placeholder data:\n", " example_data_chunks = [\n", " \"Llama 3 is a powerful large language model developed by Meta. It excels at various NLP tasks.\",\n", " \"To build a chatbot with Llama 3, you'll typically use an API to send prompts and receive responses.\",\n", " \"Messenger Platform allows developers to create interactive experiences for Facebook Messenger users.\",\n", " \"Integrating Llama 3 with Messenger involves setting up webhooks and handling message events.\",\n", " \"Key steps include setting up a Facebook App, configuring webhooks, and deploying your bot's backend.\",\n", " \"Best practices for chatbots include clear error handling, concise responses, and user guidance.\",\n", " \"Security is crucial; always protect your API keys and ensure your webhook endpoints are secure.\"\n", " ]\n", " ingest_data_into_qdrant(client, MAIN_COLLECTION_NAME, embedding_model, example_data_chunks)\n", " # End of IMPORTANT section for data ingestion\n", "\n", "\n", " # Query relevant sections from the main collection\n", " relevant_chunks = query_qdrant(topic, client, MAIN_COLLECTION_NAME)\n", " \n", " if not relevant_chunks:\n", " error_message = \"No relevant content found in the knowledge base. Cannot generate blog post.\"\n", " print(error_message)\n", " return error_message\n", "\n", " context = \"\\n\".join([chunk.payload[\"text\"] for chunk in relevant_chunks])\n", " \n", " system_prompt = f\"\"\"\n", " You are a technical writer specializing in creating comprehensive documentation-based blog posts. \n", " Use the following context from technical documentation to write an in-depth blog post about {topic}.\n", " \n", " Requirements:\n", " 1. Structure the blog with clear sections and subsections\n", " 2. Include code examples and configuration details where relevant\n", " 3. Explain architectural components using diagrams (describe in markdown)\n", " 4. Add setup instructions and best practices\n", " 5. Use technical terminology appropriate for developers\n", " \n", " Context:\n", " {context}\n", " \"\"\"\n", " \n", " payload = {\n", " \"model\": LLAMA_MODEL,\n", " \"messages\": [\n", " {\"role\": \"system\", \"content\": system_prompt},\n", " {\"role\": \"user\", \"content\": f\"Write a detailed technical blog post about {topic}\"}\n", " ],\n", " \"temperature\": 0.5,\n", " \"max_tokens\": 4096\n", " }\n", " \n", " print(\"Sending request to Llama API for blog generation...\")\n", " try:\n", " response = requests.post(API_URL, headers=HEADERS, json=payload)\n", " \n", " if response.status_code == 200:\n", " response_json = response.json()\n", " # Adjusting to handle the potentially nested structure as seen in your original code\n", " # where 'completion_message' might be missing or 'content' might be missing.\n", " # Adding .get with default values for safer access.\n", " blog_content = response_json.get('completion_message', {}).get('content', {}).get('text', '')\n", " \n", " if not blog_content:\n", " print(\"Warning: 'completion_message.content.text' was empty or not found in API response.\")\n", " print(f\"Full API response: {response_json}\")\n", " return \"Error: Could not extract blog content from API response.\"\n", "\n", " # Format as markdown\n", " markdown_content = f\"# {topic}\\n\\n{blog_content}\"\n", " \n", " # Save to file\n", " output_path = Path(f\"{topic.replace(' ', '_')}_blog.md\")\n", " with open(output_path, \"w\", encoding=\"utf-8\") as f:\n", " f.write(markdown_content)\n", " \n", " print(f\"Blog post generated and saved to {output_path}.\")\n", " \n", " # Display markdown content directly in the notebook\n", " display(Markdown(markdown_content))\n", " return markdown_content\n", " \n", " else:\n", " error_message = f\"Error: {response.status_code} - {response.text}\"\n", " print(error_message)\n", " return error_message\n", " \n", " except Exception as e:\n", " error_message = f\"An unexpected error occurred: {str(e)}\"\n", " print(error_message)\n", " return error_message\n", "\n", "print(\"Blog generation function defined.\")" ] }, { "cell_type": "markdown", "id": "67497a92", "metadata": {}, "source": [ "# Step 5: Specify the topic for the blog post and execute the Blog Generation Process\n" ] }, { "cell_type": "code", "execution_count": null, "id": "6b3113ef", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Getting Qdrant client and querying pre-existing collection...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/var/folders/f5/lntr7_gx6fd1y_1rtgwf2g9h0000gn/T/ipykernel_89390/3804544503.py:16: DeprecationWarning: `search` method is deprecated and will be removed in the future. Use `query_points` instead.\n", " results = client.search(\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "f0623176afc44a00ac97aa280a104140", "version_major": 2, "version_minor": 0 }, "text/plain": [ "config.json: 0%| | 0.00/794 [00:00 {\n", " // Process webhook event\n", " const event = req.body;\n", " // Generate response using Llama 3\n", " const response = generateResponse(event);\n", " // Send response back to user\n", " sendResponse(response);\n", " res.status(200).send('EVENT_RECEIVED');\n", "});\n", "\n", "app.listen(3000, () => {\n", " console.log('Server listening on port 3000');\n", "});\n", "```\n", "#### Step 3: Integrate Llama 3 with the Web Server\n", "\n", "1. Install the necessary dependencies for Llama 3, such as the Llama 3 Python library.\n", "2. Implement a function to generate responses using Llama 3.\n", "\n", "Here's an example of a Python function that generates a response using Llama 3:\n", "```python\n", "import llama\n", "\n", "def generate_response(event):\n", " # Initialize Llama 3 model\n", " model = llama.Llama3()\n", " # Process event and generate response\n", " response = model.generate(event['message'])\n", " return response\n", "```\n", "#### Step 4: Configure Webhook Events\n", "\n", "1. Go to the Facebook Developer Dashboard and navigate to your app's settings.\n", "2. Configure the webhook events to send incoming messages to your web server.\n", "\n", "Here's an example of a webhook event configuration:\n", "```json\n", "{\n", " \"object\": \"page\",\n", " \"entry\": [\n", " {\n", " \"id\": \"PAGE_ID\",\n", " \"time\": 1643723400,\n", " \"messaging\": [\n", " {\n", " \"sender\": {\n", " \"id\": \"USER_ID\"\n", " },\n", " \"recipient\": {\n", " \"id\": \"PAGE_ID\"\n", " },\n", " \"timestamp\": 1643723400,\n", " \"message\": {\n", " \"text\": \"Hello, how are you?\"\n", " }\n", " }\n", " ]\n", " }\n", " ]\n", "}\n", "```\n", "#### Step 5: Test the Chatbot\n", "\n", "1. Use the Messenger app to send a message to your Facebook business page.\n", "2. Verify that the chatbot responds with a relevant answer generated by Llama 3.\n", "\n", "### Best Practices\n", "\n", "* Ensure that your web server is secure and scalable to handle a large volume of incoming requests.\n", "* Implement logging and monitoring to track chatbot performance and identify areas for improvement.\n", "* Continuously update and fine-tune your Llama 3 model to improve response accuracy and relevance.\n", "\n", "By following these steps and best practices, you can build a Llama 3 enabled Messenger chatbot that provides an engaging and informative customer experience." ], "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# Specify the topic for the blog post\n", "topic = \"Building a Messenger Chatbot with Llama 3\"\n", "\n", "# Generate and display the blog content\n", "blog_content = generate_blog(topic)\n", "\n", "if isinstance(blog_content, str) and \"Error\" in blog_content:\n", " print(blog_content)" ] }, { "cell_type": "code", "execution_count": null, "id": "0930f7de", "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "test_blogs", "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.11" } }, "nbformat": 4, "nbformat_minor": 5 }