Created
November 15, 2024 02:34
-
-
Save wesslen/237368fb01b63f699e2b916f0a65d227 to your computer and use it in GitHub Desktop.
customer_support_agent_langgraph.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "cells": [ | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "view-in-github", | |
| "colab_type": "text" | |
| }, | |
| "source": [ | |
| "<a href=\"https://colab.research.google.com/gist/wesslen/237368fb01b63f699e2b916f0a65d227/customer_support_agent_langgraph.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Ih2BGcyVojiZ" | |
| }, | |
| "source": [ | |
| "# Building an Intelligent Customer Support Agent with LangGraph\n", | |
| "\n", | |
| "## Overview\n", | |
| "This tutorial demonstrates how to create an intelligent customer support agent using LangGraph, a powerful tool for building complex language model workflows. The agent is designed to categorize customer queries, analyze sentiment, and provide appropriate responses or escalate issues when necessary.\n", | |
| "\n", | |
| "## Motivation\n", | |
| "In today's fast-paced business environment, efficient and accurate customer support is crucial. Automating the initial stages of customer interaction can significantly reduce response times and improve overall customer satisfaction. This project aims to showcase how advanced language models and graph-based workflows can be combined to create a sophisticated support system that can handle a variety of customer inquiries.\n", | |
| "\n", | |
| "## Key Components\n", | |
| "1. **State Management**: Using TypedDict to define and manage the state of each customer interaction.\n", | |
| "2. **Query Categorization**: Classifying customer queries into Technical, Billing, or General categories.\n", | |
| "3. **Sentiment Analysis**: Determining the emotional tone of customer queries.\n", | |
| "4. **Response Generation**: Creating appropriate responses based on the query category and sentiment.\n", | |
| "5. **Escalation Mechanism**: Automatically escalating queries with negative sentiment to human agents.\n", | |
| "6. **Workflow Graph**: Utilizing LangGraph to create a flexible and extensible workflow.\n", | |
| "\n", | |
| "## Method Details\n", | |
| "1. **Initialization**: Set up the environment and import necessary libraries.\n", | |
| "2. **State Definition**: Create a structure to hold query information, category, sentiment, and response.\n", | |
| "3. **Node Functions**: Implement separate functions for categorization, sentiment analysis, and response generation.\n", | |
| "4. **Graph Construction**: Use StateGraph to define the workflow, adding nodes and edges to represent the support process.\n", | |
| "5. **Conditional Routing**: Implement logic to route queries based on their category and sentiment.\n", | |
| "6. **Workflow Compilation**: Compile the graph into an executable application.\n", | |
| "7. **Execution**: Process customer queries through the workflow and retrieve results.\n", | |
| "\n", | |
| "## Conclusion\n", | |
| "This tutorial demonstrates the power and flexibility of LangGraph in creating complex, AI-driven workflows. By combining natural language processing capabilities with a structured graph-based approach, we've created a customer support agent that can efficiently handle a wide range of queries. This system can be further extended and customized to meet specific business needs, potentially integrating with existing customer support tools and databases for even more sophisticated interactions.\n", | |
| "\n", | |
| "The approach showcased here has broad applications beyond customer support, illustrating how language models can be effectively orchestrated to solve complex, multi-step problems in various domains." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "source": [ | |
| "!uv pip install --system langgraph langchain_core langchain_openai python-dotenv" | |
| ], | |
| "metadata": { | |
| "colab": { | |
| "base_uri": "https://localhost:8080/" | |
| }, | |
| "id": "T7gXWeDaoyTU", | |
| "outputId": "55db9f70-f1a9-4c4e-b615-607f2175d121" | |
| }, | |
| "execution_count": 3, | |
| "outputs": [ | |
| { | |
| "output_type": "stream", | |
| "name": "stdout", | |
| "text": [ | |
| "\u001b[2mUsing Python 3.10.12 environment at /usr\u001b[0m\n", | |
| "\u001b[2K\u001b[2mResolved \u001b[1m37 packages\u001b[0m \u001b[2min 571ms\u001b[0m\u001b[0m\n", | |
| "\u001b[2K\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2K\u001b[1A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2K\u001b[2A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/22.90 KiB\n", | |
| "\u001b[2K\u001b[3A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2mlanggraph-sdk\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 28.39 KiB/28.39 KiB\n", | |
| "\u001b[2K\u001b[5A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mhttpx-sse \u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 7.64 KiB/7.64 KiB\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 14.91 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[3A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2K\u001b[3A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m----------\u001b[2m--------------------\u001b[0m\u001b[0m 16.00 KiB/49.19 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m----------\u001b[2m--------------------\u001b[0m\u001b[0m 16.00 KiB/49.19 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m----------\u001b[2m--------------------\u001b[0m\u001b[0m 16.00 KiB/49.19 KiB\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m\u001b[2m------------------------------\u001b[0m\u001b[0m 0 B/121.90 KiB\n", | |
| "\u001b[2K\u001b[5A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m----------\u001b[2m--------------------\u001b[0m\u001b[0m 16.00 KiB/49.19 KiB\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m---\u001b[2m---------------------------\u001b[0m\u001b[0m 8.74 KiB/121.90 KiB\n", | |
| "\u001b[2K\u001b[5A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlanggraph-checkpoint\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 22.90 KiB/22.90 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 32.00 KiB/49.19 KiB\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m---\u001b[2m---------------------------\u001b[0m\u001b[0m 8.74 KiB/121.90 KiB\n", | |
| "\u001b[2K\u001b[5A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mpython-dotenv\u001b[0m \u001b[32m-------------------------\u001b[2m-----\u001b[0m\u001b[0m 16.00 KiB/19.40 KiB\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m--------------------\u001b[2m----------\u001b[0m\u001b[0m 32.00 KiB/49.19 KiB\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m---\u001b[2m---------------------------\u001b[0m\u001b[0m 8.74 KiB/121.90 KiB\n", | |
| "\u001b[2K\u001b[4A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mlangchain-openai\u001b[0m \u001b[32m------------------------------\u001b[2m\u001b[0m\u001b[0m 48.00 KiB/49.19 KiB\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m---\u001b[2m---------------------------\u001b[0m\u001b[0m 8.74 KiB/121.90 KiB\n", | |
| "\u001b[2K\u001b[3A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2mlanggraph \u001b[0m \u001b[32m-------\u001b[2m-----------------------\u001b[0m\u001b[0m 24.74 KiB/121.90 KiB\n", | |
| "\u001b[2K\u001b[2A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2K\u001b[1A\u001b[37m⠙\u001b[0m \u001b[2mPreparing packages...\u001b[0m (0/7)\n", | |
| "\u001b[2K\u001b[2mPrepared \u001b[1m7 packages\u001b[0m \u001b[2min 50ms\u001b[0m\u001b[0m\n", | |
| "\u001b[2K\u001b[2mInstalled \u001b[1m7 packages\u001b[0m \u001b[2min 8ms\u001b[0m\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mhttpx-sse\u001b[0m\u001b[2m==0.4.0\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mlangchain-openai\u001b[0m\u001b[2m==0.2.6\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mlanggraph\u001b[0m\u001b[2m==0.2.48\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mlanggraph-checkpoint\u001b[0m\u001b[2m==2.0.4\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mlanggraph-sdk\u001b[0m\u001b[2m==0.1.36\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mpython-dotenv\u001b[0m\u001b[2m==1.0.1\u001b[0m\n", | |
| " \u001b[32m+\u001b[39m \u001b[1mtiktoken\u001b[0m\u001b[2m==0.8.0\u001b[0m\n" | |
| ] | |
| } | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "ATLbvvhpojiZ" | |
| }, | |
| "source": [ | |
| "## Import necessary libraries\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 5, | |
| "metadata": { | |
| "id": "-70I6uZ6ojiZ" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "from typing import Dict, TypedDict\n", | |
| "from langgraph.graph import StateGraph, END\n", | |
| "from langchain_core.prompts import ChatPromptTemplate\n", | |
| "from langchain_openai import ChatOpenAI\n", | |
| "\n", | |
| "from IPython.display import display, Image\n", | |
| "from langchain_core.runnables.graph import MermaidDrawMethod\n", | |
| "from dotenv import load_dotenv\n", | |
| "import os\n", | |
| "\n", | |
| "# Load environment variables and set OpenAI API key\n", | |
| "#load_dotenv()\n", | |
| "\n", | |
| "from google.colab import userdata\n", | |
| "\n", | |
| "os.environ[\"OPENAI_API_KEY\"] = userdata.get('DSBA_LLAMA3_KEY')\n", | |
| "os.environ[\"OPENAI_API_BASE\"] = userdata.get('MODAL_BASE_URL')" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "lx8rfh3dojia" | |
| }, | |
| "source": [ | |
| "## Define State Structure\n", | |
| "\n", | |
| "We define a `State` class to hold the query, category, sentiment, and response for each customer interaction." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 6, | |
| "metadata": { | |
| "id": "VgyWrcjDojia" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "class State(TypedDict):\n", | |
| " query: str\n", | |
| " category: str\n", | |
| " sentiment: str\n", | |
| " response: str" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "O2WkG5x9ojia" | |
| }, | |
| "source": [ | |
| "## Define Node Functions\n", | |
| "\n", | |
| "These functions represent the different stages of processing a customer query." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 12, | |
| "metadata": { | |
| "id": "vtd6oEeMojia" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def categorize(state: State) -> State:\n", | |
| " \"\"\"Categorize the customer query into Technical, Billing, or General.\"\"\"\n", | |
| " prompt = ChatPromptTemplate.from_template(\n", | |
| " \"Categorize the following customer query into one of these categories: \"\n", | |
| " \"Technical, Billing, General. Query: {query}\"\n", | |
| " )\n", | |
| " chain = prompt | ChatOpenAI(temperature=0, model=\"/models/NousResearch/Meta-Llama-3.1-8B-Instruct\")\n", | |
| " category = chain.invoke({\"query\": state[\"query\"]}).content\n", | |
| " return {\"category\": category}\n", | |
| "\n", | |
| "def analyze_sentiment(state: State) -> State:\n", | |
| " \"\"\"Analyze the sentiment of the customer query as Positive, Neutral, or Negative.\"\"\"\n", | |
| " prompt = ChatPromptTemplate.from_template(\n", | |
| " \"Analyze the sentiment of the following customer query. \"\n", | |
| " \"Respond with either 'Positive', 'Neutral', or 'Negative'. Query: {query}\"\n", | |
| " )\n", | |
| " chain = prompt | ChatOpenAI(temperature=0, model=\"/models/NousResearch/Meta-Llama-3.1-8B-Instruct\")\n", | |
| " sentiment = chain.invoke({\"query\": state[\"query\"]}).content\n", | |
| " return {\"sentiment\": sentiment}\n", | |
| "\n", | |
| "def handle_technical(state: State) -> State:\n", | |
| " \"\"\"Provide a technical support response to the query.\"\"\"\n", | |
| " prompt = ChatPromptTemplate.from_template(\n", | |
| " \"Provide a technical support response to the following query: {query}\"\n", | |
| " )\n", | |
| " chain = prompt | ChatOpenAI(temperature=0, model=\"/models/NousResearch/Meta-Llama-3.1-8B-Instruct\")\n", | |
| " response = chain.invoke({\"query\": state[\"query\"]}).content\n", | |
| " return {\"response\": response}\n", | |
| "\n", | |
| "def handle_billing(state: State) -> State:\n", | |
| " \"\"\"Provide a billing support response to the query.\"\"\"\n", | |
| " prompt = ChatPromptTemplate.from_template(\n", | |
| " \"Provide a billing support response to the following query: {query}\"\n", | |
| " )\n", | |
| " chain = prompt | ChatOpenAI(temperature=0, model=\"/models/NousResearch/Meta-Llama-3.1-8B-Instruct\")\n", | |
| " response = chain.invoke({\"query\": state[\"query\"]}).content\n", | |
| " return {\"response\": response}\n", | |
| "\n", | |
| "def handle_general(state: State) -> State:\n", | |
| " \"\"\"Provide a general support response to the query.\"\"\"\n", | |
| " prompt = ChatPromptTemplate.from_template(\n", | |
| " \"Provide a general support response to the following query: {query}\"\n", | |
| " )\n", | |
| " chain = prompt | ChatOpenAI(temperature=0, model=\"/models/NousResearch/Meta-Llama-3.1-8B-Instruct\")\n", | |
| " response = chain.invoke({\"query\": state[\"query\"]}).content\n", | |
| " return {\"response\": response}\n", | |
| "\n", | |
| "def escalate(state: State) -> State:\n", | |
| " \"\"\"Escalate the query to a human agent due to negative sentiment.\"\"\"\n", | |
| " return {\"response\": \"This query has been escalated to a human agent due to its negative sentiment.\"}\n", | |
| "\n", | |
| "def route_query(state: State) -> str:\n", | |
| " \"\"\"Route the query based on its sentiment and category.\"\"\"\n", | |
| " if state[\"sentiment\"] == \"Negative\":\n", | |
| " return \"escalate\"\n", | |
| " elif state[\"category\"] == \"Technical\":\n", | |
| " return \"handle_technical\"\n", | |
| " elif state[\"category\"] == \"Billing\":\n", | |
| " return \"handle_billing\"\n", | |
| " else:\n", | |
| " return \"handle_general\"" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "Af2p0tstojia" | |
| }, | |
| "source": [ | |
| "## Create and Configure the Graph\n", | |
| "\n", | |
| "Here we set up the LangGraph, defining nodes and edges to create our customer support workflow." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 13, | |
| "metadata": { | |
| "id": "rOs1XhMwojia" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "# Create the graph\n", | |
| "workflow = StateGraph(State)\n", | |
| "\n", | |
| "# Add nodes\n", | |
| "workflow.add_node(\"categorize\", categorize)\n", | |
| "workflow.add_node(\"analyze_sentiment\", analyze_sentiment)\n", | |
| "workflow.add_node(\"handle_technical\", handle_technical)\n", | |
| "workflow.add_node(\"handle_billing\", handle_billing)\n", | |
| "workflow.add_node(\"handle_general\", handle_general)\n", | |
| "workflow.add_node(\"escalate\", escalate)\n", | |
| "\n", | |
| "# Add edges\n", | |
| "workflow.add_edge(\"categorize\", \"analyze_sentiment\")\n", | |
| "workflow.add_conditional_edges(\n", | |
| " \"analyze_sentiment\",\n", | |
| " route_query,\n", | |
| " {\n", | |
| " \"handle_technical\": \"handle_technical\",\n", | |
| " \"handle_billing\": \"handle_billing\",\n", | |
| " \"handle_general\": \"handle_general\",\n", | |
| " \"escalate\": \"escalate\"\n", | |
| " }\n", | |
| ")\n", | |
| "workflow.add_edge(\"handle_technical\", END)\n", | |
| "workflow.add_edge(\"handle_billing\", END)\n", | |
| "workflow.add_edge(\"handle_general\", END)\n", | |
| "workflow.add_edge(\"escalate\", END)\n", | |
| "\n", | |
| "# Set entry point\n", | |
| "workflow.set_entry_point(\"categorize\")\n", | |
| "\n", | |
| "# Compile the graph\n", | |
| "app = workflow.compile()" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "yBWeA6ZKojia" | |
| }, | |
| "source": [ | |
| "## Visualize the Graph\n", | |
| "\n", | |
| "This cell generates and displays a visual representation of our LangGraph workflow." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 14, | |
| "metadata": { | |
| "colab": { | |
| "base_uri": "https://localhost:8080/", | |
| "height": 449 | |
| }, | |
| "id": "0_vX33JJojia", | |
| "outputId": "a6f86f9f-6fda-4c36-b326-351911697fc8" | |
| }, | |
| "outputs": [ | |
| { | |
| "output_type": "display_data", | |
| "data": { | |
| "image/jpeg": "/9j/4AAQSkZJRgABAQAAAQABAAD/4gHYSUNDX1BST0ZJTEUAAQEAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADb/2wBDAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRT/2wBDAQMEBAUEBQkFBQkUDQsNFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBT/wAARCAGwAqgDASIAAhEBAxEB/8QAHQABAAICAwEBAAAAAAAAAAAAAAUGBAcCAwgBCf/EAFkQAAEEAQIDAwUKBwwIBAUFAAEAAgMEBQYRBxIhExUxFCJBVpQIFzJRVWFx0dPUFiM3QnWBkzM1NlJUcnSVsrO00iQlJoKDkaGxQ2Jzkgk0U2OkOFdkosH/xAAbAQEBAAMBAQEAAAAAAAAAAAAAAQIDBAUGB//EADYRAQABAgIGCAUDBQEBAAAAAAABAhEDUQQSFDGR0RMhM0FSYnGSBWGhscEiI+EVMkJTgfDx/9oADAMBAAIRAxEAPwD9U0REBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBF1W7UNGrNZsSNhghYZJJHnYNaBuSf1Kttxt3WDRPkJbeNxL9jFjYnOgmlb/Gne08w3/8AptI2Hw9yS1uyijW66ptC2TtvM0KD+W1erVnfxZpmtP8A1Kx/wqwvyxQ9pZ9ax6mh9O0I+SvgsdENtiW1Wbnrv1O2569eqyPwVwvyPQ9mZ9S2fs/P6HUfhVhflih7Sz60/CrC/LFD2ln1p+CuF+R6HszPqT8FcL8j0PZmfUn7Pz+i9R+FWF+WKHtLPrT8KsL8sUPaWfWn4K4X5HoezM+pPwVwvyPQ9mZ9Sfs/P6HUfhVhflih7Sz619bqjDOIDcvRJPoFln1r5+CuF+R6HszPqXw6VwhBBw9Ag9CPJWfUn7Pz+idSSilZPGHxvbIx3g5p3B/WuarUugMTDKbGKjdgLnQ9vi9oQ7bp58YHI8bdNnNP6tgsvC5ey60/F5RjGZOKPtBLE0thtR77dpGCSRsSA5hJLS4dSC1zsZopmL0Tf7/+/wDWLZJpERaUEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQVfVm2SzWn8K7YwWJ33LLDv58UADg39q+En0EAj0q0KsZ0eSa30xddv2ckdvH77bgOkayVu59H/yxH0kD0qzroxP7KI+X5n+FndAiIudFHfxs0YzXx0WMwZNRtkbA+tDUnkjjkcznbG+ZrDEx5b15XOB29Cq/CX3SeB4njVpdWu4kYG5dY99nH22ROq13hvbOkkhYxrzvzGHfnaPEdCVTM53xpv3QcUugcHq2nPls1WbqeG3jicBeq9iBJdjnPSOZjQ1o5XAvczYsPienTF/WeiMJxm0zhdM5ePWM+WzWcwV+XHudjrIn/GQctg/iy/d23ZuO/M3YjZBtbSfugdA63Zl3YjOmV+JqG/bhsUrFaVlcb7zNjlja57Oh85gI32HpCp2uPdd6OwfDW7q3TrrepoIX0mRGHG3GV5PKJA0ETdgWnlAkJaOocwRnle4Bahw2nctc147L1MLxGyEN3QeWxNnJarr2S+S+4QyiJsT/ANxaeR23KxsbnEBnMVsTVmhc5c9w9gdP47CWpM7TwOEldiGxFlgvrvrTTRcjtj2m0TxynqXdPFBvrTOpKOrsHVy+O8p8ishxj8spzVJeji080UzWvb1B+E0bjYjoQVKKD0dquLWmCiysOOymKjkc5orZmjJTsN2O25ieA4D4vjU4gKr8QNqGKr5tmzZ8RYZZ5v8A7RPJM35943P6HpzBp9AVoVY4lDt9GX6TdzLkOShGANyXTPbH/wAhzEk+gAn0Lo0ftaY+f071jes6Ii50EREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQR2ew0WexklSR7onczZIpmfCika4OY8fOHAH9WyxMNqPtrIxeT7Knm2N3MAOzLAHjJCT1c34x4t8D6CZxYWWwtDPVPJcjUiuQcweGSt35XDwcD6HD0EdQt1NUW1K932VR7HucuFdueSebh1piWaRxe+R+JgLnOJ3JJ5epJXGT3N3CmV7nv4caXe9xJc52JgJJ+M+arANCMhHLUzucpx7bCNt4yho+Yyh5/wCq+fgTY9as9+2h+yWWphz/AJ/SS0Zp3E4mlgsZVx2NqQ0KFWJsMFWuwMjiY0bNa1o6AAdAAstVf8CbHrVnv20P2SfgTY9as9+2h+yTo8Px/SS0ZrQi1Xw9x+V1OzURu6pzINDNWqEPYyQj8VG4BvN+LPndev8A2Vs/Amx61Z79tD9knR4fj+klozY2quDmhNdZQZLUWj8HnMgIxF5VkKEU0nIN9m8zmk7Dc9PnUQfc28KC0N97fS3KCSB3TBsD6fzfmCsH4E2PWrPftofskGiZ/TqjPOHxdtEP+0SdHh+P6SWjN2ae0lpPhbhrUeFxWK0tii/t7AqQx1YefYN53bADfYNG5+IL5Qhk1RlauWsQugx1TmdQhla5skj3AtM72n4PmkhrT12c4nqQG9tHQ2KqXIrk7Z8pdiIdHYyVh9h0Th05mBxLWH52gHqfjKsCmtRRH6Ouc+XP6G7cIiLQgiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiDXvBwgxa02O/8AtRkPH+c351sJa94Ob9lrTfb+FGQ8Nv4zfiWwkBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREGvODY2i1p1B/wBqMh4D/wAzVsNa84N7dlrTb1oyHo2/OathoCIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIijc/nIsBRE743zyyPEUFeL4c0h8GjfoPAkk9AASegKyppmqYpp3iSRUqTPauc4lmOwrGn8112ZxH6+yG/8AyXHv3WH8gwftc32a6tlrzjjC2XdFSO/dYfyDB+1zfZp37rD+QYP2ub7NNlrzjjBZd1UOLmuLvDXhtn9U4/Bv1HZxNfyo42OfsHSxtI7Qh/K7blZzP8Dvy7endY/fusP5Bg/a5vs1xmy+rbET4pcbgZIntLXMfamIcD0II7PqE2WvOOMFnmb3HHuwLPGXiJmNL0dCy06123czlzJnJB7acb9uVpYIW85L+Ru+4+ET6Nl7SXmzgBwFte54Op3YCliJ35y+bTnzWZeaCEb9nA09n1a3md19O/XwW3O/dYfyDB+1zfZpsteccYLLuipHfusP5Bg/a5vs0791h/IMH7XN9mmy15xxgsu6Kkd+6w/kGD9rm+zTv3WH8gwftc32abLXnHGCy7oqpjtWZCC9Wq5yjWqttP7KG1TndLH2m24Y8OY0sJ6gHqCRsSCWg2taMTDqw5tUWsIiLUgiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgKna9P+udHDxHech6/wBDsq4qna9/frR36Tl/wdldWjdp/wAn7SsM5ERdKCIsHN5zH6bxVnJ5W7BjsfWbzzWbMgZGwb7dSeniQPpIQZyIiAiIgIonLaqxeCy2Gxt6yYbuYmfXoxdm93avZG6Rw3AIbsxrju4gdNvHopZQERFRAa1PLi6BG24y+M67fHegH/8Aq2Etea3/AHpo/pfGf46BbDWvSOzp9Z/C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ERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERARQmV1vp3BWnVsjnMdRst2LobFpjHt3G43BO439CwffS0d604j22P61vjAxaovFE29JW0rSiq3vpaO9acR7bH9ae+lo71pxHtsf1q7PjeCeErqzktKKre+lo71pxHtsf1p76WjvWnEe2x/Wmz43gnhJqzktKKre+lo71pxHtsf1p76WjvWnEe2x/Wmz43gnhJqzktKwc3ncbprGTZLL5CrisdDy9rbuzthij3cGt5nuIA3cQBufEgKE99LR3rTiPbY/rVa4l5Th3xS0DndJ5fU2Hdj8tVfWe7yyMmMnqyQDm+E1wa4fO0Js+N4J4Sas5IjgZxI0hlr+qMbQ1ThbmRu6kyE1anXyEMk07OjudjA8lw5WuO4G2wJ9C3Kvzr9wBwZxHCvW+qtWa0yuLp5LHTSYjEtmtRgPG+0tlm56tc3ZrXDoQ5691++lo71pxHtsf1ps+N4J4Sas5LSiq3vpaO9acR7bH9ae+lo71pxHtsf1ps+N4J4Sas5LSiq3vpaO9acR7bH9ae+lo71pxHtsf1ps+N4J4Sas5LSiq3vpaO9acR7bH9ae+lo71pxHtsf1ps+N4J4Sas5LSiq3vpaO9acR7bH9a+t4o6Pc4NGqMQSTsALsfX/qmz43gnhKas5LQix6GQq5SpFbpWYbdWUc0c8Egex4+MOHQrIWiYmJtKCIigIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgIiICIiAiIgKF1plZcDo7PZOueWelQnsxnlDtnMjc4dD49QppVjij+TPV36Iuf3L1uwIirFpic4+6xvhjYbFV8Nj4q1dmzWjdzz1dI89XPeT1c5xJJcSSSSSSSs1fGfAb9C+rumZmbygiIoCIiAiIgIiICIiAiIgIiICIiAiIgisJy4ziA6rXAigyFCW1NE0bNdLFJEwP2/jFsmxO25DW7/BCvCo1T8p2N/Q9z++qq8rRpX91M/LmsiIi40EREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQFWO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\n", | |
| "text/plain": [ | |
| "<IPython.core.display.Image object>" | |
| ] | |
| }, | |
| "metadata": {} | |
| } | |
| ], | |
| "source": [ | |
| "display(\n", | |
| " Image(\n", | |
| " app.get_graph().draw_mermaid_png(\n", | |
| " draw_method=MermaidDrawMethod.API,\n", | |
| " )\n", | |
| " )\n", | |
| ")" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "V7ZTmL1Qojia" | |
| }, | |
| "source": [ | |
| "## Run Customer Support Function\n", | |
| "\n", | |
| "This function processes a customer query through our LangGraph workflow." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 15, | |
| "metadata": { | |
| "id": "-mB18Ni7ojia" | |
| }, | |
| "outputs": [], | |
| "source": [ | |
| "def run_customer_support(query: str) -> Dict[str, str]:\n", | |
| " \"\"\"Process a customer query through the LangGraph workflow.\n", | |
| "\n", | |
| " Args:\n", | |
| " query (str): The customer's query\n", | |
| "\n", | |
| " Returns:\n", | |
| " Dict[str, str]: A dictionary containing the query's category, sentiment, and response\n", | |
| " \"\"\"\n", | |
| " results = app.invoke({\"query\": query})\n", | |
| " return {\n", | |
| " \"category\": results[\"category\"],\n", | |
| " \"sentiment\": results[\"sentiment\"],\n", | |
| " \"response\": results[\"response\"]\n", | |
| " }\n" | |
| ] | |
| }, | |
| { | |
| "cell_type": "markdown", | |
| "metadata": { | |
| "id": "FHNOpp_mojib" | |
| }, | |
| "source": [ | |
| "## Test the Customer Support Agent\n", | |
| "\n", | |
| "Let's test our customer support agent with a sample queries for each kind of query type." | |
| ] | |
| }, | |
| { | |
| "cell_type": "code", | |
| "execution_count": 16, | |
| "metadata": { | |
| "colab": { | |
| "base_uri": "https://localhost:8080/" | |
| }, | |
| "id": "xk4Q4mDXojib", | |
| "outputId": "07b7f06a-a174-48c9-c744-5f8440a16002" | |
| }, | |
| "outputs": [ | |
| { | |
| "output_type": "stream", | |
| "name": "stdout", | |
| "text": [ | |
| "Query: My internet connection keeps dropping. Can you help?\n", | |
| "Category: I would categorize the customer query as: **Technical**\n", | |
| "\n", | |
| "The customer is reporting a specific issue with their internet connection, which is a technical problem that requires troubleshooting and possibly technical assistance to resolve.\n", | |
| "Sentiment: The sentiment of this customer query is 'Negative'. The customer is experiencing a problem with their internet connection, which is causing frustration, and is seeking assistance.\n", | |
| "Response: I'd be happy to help you troubleshoot the issue with your internet connection dropping. Here are some general steps we can take to try and resolve the problem:\n", | |
| "\n", | |
| "1. **Restart your router**: This is often the simplest and most effective solution. Try unplugging your router from the power source, waiting for 30 seconds, and then plugging it back in. This will reset your router and may resolve any temporary issues.\n", | |
| "2. **Check your physical connections**: Ensure that all cables are securely connected to your router, modem, and any other devices. Try swapping out any suspect cables to rule out a faulty connection.\n", | |
| "3. **Check for outages in your area**: Visit your internet service provider's (ISP) website or social media page to see if there are any reported outages in your area.\n", | |
| "4. **Check your router's firmware**: Outdated firmware can cause connectivity issues. Check with your router manufacturer to see if there are any firmware updates available.\n", | |
| "5. **Check for interference**: Other devices in your home can cause interference with your internet signal. Try moving your router to a central location and away from other devices that may be causing interference.\n", | |
| "6. **Check your internet plan**: If you've recently upgraded or downgraded your internet plan, it's possible that your current plan is not sufficient for your needs. Contact your ISP to discuss your options.\n", | |
| "7. **Run a speed test**: Use a speed test tool to check your internet speed and see if it's within the expected range for your plan.\n", | |
| "\n", | |
| "If none of these steps resolve the issue, we can try more advanced troubleshooting steps or explore other possible causes, such as:\n", | |
| "\n", | |
| "* **Modem issues**: If you're using a separate modem, try resetting it or replacing it if it's old.\n", | |
| "* **ISP issues**: Contact your ISP to report the issue and see if they can send a technician to investigate.\n", | |
| "* **Hardware issues**: If you're using a wireless router, try replacing it with a wired router to rule out any hardware issues.\n", | |
| "\n", | |
| "Let's start with the first step and see if we can resolve the issue. Do you want to try restarting your router?\n", | |
| "\n", | |
| "\n", | |
| "Query: I need help talking to chatGPT\n", | |
| "Category: I would categorize the customer query as \"General\". The query is asking for assistance with a conversation, which is a non-technical issue.\n", | |
| "Sentiment: The sentiment of this customer query is 'Neutral'. The customer is seeking assistance, which implies a need for help, but the tone is not emotional or confrontational.\n", | |
| "Response: I'm happy to help facilitate a conversation between you and chatGPT. Here are a few suggestions to get you started:\n", | |
| "\n", | |
| "1. **Be clear and specific**: If you have a specific question or topic you'd like to discuss, try to be as clear and concise as possible. This will help chatGPT understand your query and provide a more accurate response.\n", | |
| "2. **Use everyday language**: Don't worry too much about using perfect grammar or jargon. ChatGPT is designed to understand everyday language, so feel free to ask your question in a way that feels natural to you.\n", | |
| "3. **Ask follow-up questions**: If you're not sure about a particular response or want more information, don't hesitate to ask follow-up questions. This will help chatGPT provide more context and clarify any misunderstandings.\n", | |
| "4. **Be patient and open-minded**: ChatGPT is a machine learning model, and it may not always understand the nuances of human language or provide the exact response you're looking for. Be patient and open-minded, and feel free to ask for clarification or rephrase your question if needed.\n", | |
| "\n", | |
| "If you're still having trouble getting started, you can try asking chatGPT a simple question like \"Can you explain how you work?\" or \"What are your limitations?\" This can help you get a better sense of how chatGPT operates and what you can expect from our conversation.\n", | |
| "\n", | |
| "How can I assist you further? Is there a specific topic or question you'd like to discuss with chatGPT?\n", | |
| "\n", | |
| "\n", | |
| "Query: where can i find my receipt?\n", | |
| "Category: I would categorize the customer query as \"General\". The query is asking for information about a specific document (receipt) rather than inquiring about a technical issue or a billing problem.\n", | |
| "Sentiment: I would classify the sentiment of this customer query as 'Neutral'. The customer is simply asking for information and assistance, without expressing any emotion or frustration.\n", | |
| "Response: Here's a general support response:\n", | |
| "\n", | |
| "\"Hello! I'd be happy to help you locate your receipt. There are a few possible places you can check:\n", | |
| "\n", | |
| "* If you made a purchase online, you can check your email inbox for a confirmation email that should include a receipt or a link to access your order details.\n", | |
| "* If you made a purchase in-store, you can check your wallet or purse for a physical receipt.\n", | |
| "* If you're still having trouble finding your receipt, you can also try checking your order history on our website or mobile app (if you have an account with us).\n", | |
| "* If none of the above options work, please let me know and I can try to assist you further. Can you please provide me with more information about your purchase, such as the date and location of the transaction, and I'll do my best to help you locate your receipt?\"\n", | |
| "\n", | |
| "\n", | |
| "Query: What are your business hours?\n", | |
| "Category: I would categorize the customer query as \"General\". The query is asking for information about the company's operating hours, which is a general question about the company's policies and operations, rather than a technical or billing-related question.\n", | |
| "Sentiment: Neutral.\n", | |
| "Response: \"Thank you for reaching out to us. Our business hours are Monday through Friday, 9:00 AM to 5:00 PM EST. We are closed on weekends and major holidays. If you have a question or concern outside of our regular business hours, please feel free to leave a message and we will get back to you as soon as possible. Alternatively, you can also contact us through our website or social media channels for assistance.\"\n" | |
| ] | |
| } | |
| ], | |
| "source": [ | |
| "# escalate\n", | |
| "\n", | |
| "query = \"My internet connection keeps dropping. Can you help?\"\n", | |
| "result = run_customer_support(query)\n", | |
| "print(f\"Query: {query}\")\n", | |
| "print(f\"Category: {result['category']}\")\n", | |
| "print(f\"Sentiment: {result['sentiment']}\")\n", | |
| "print(f\"Response: {result['response']}\")\n", | |
| "print(\"\\n\")\n", | |
| "\n", | |
| "# handle_technical\n", | |
| "\n", | |
| "query = \"I need help talking to chatGPT\"\n", | |
| "result = run_customer_support(query)\n", | |
| "print(f\"Query: {query}\")\n", | |
| "print(f\"Category: {result['category']}\")\n", | |
| "print(f\"Sentiment: {result['sentiment']}\")\n", | |
| "print(f\"Response: {result['response']}\")\n", | |
| "print(\"\\n\")\n", | |
| "\n", | |
| "# handle_billing\n", | |
| "\n", | |
| "query = \"where can i find my receipt?\"\n", | |
| "result = run_customer_support(query)\n", | |
| "print(f\"Query: {query}\")\n", | |
| "print(f\"Category: {result['category']}\")\n", | |
| "print(f\"Sentiment: {result['sentiment']}\")\n", | |
| "print(f\"Response: {result['response']}\")\n", | |
| "print(\"\\n\")\n", | |
| "\n", | |
| "# handle_general\n", | |
| "\n", | |
| "query = \"What are your business hours?\"\n", | |
| "result = run_customer_support(query)\n", | |
| "print(f\"Query: {query}\")\n", | |
| "print(f\"Category: {result['category']}\")\n", | |
| "print(f\"Sentiment: {result['sentiment']}\")\n", | |
| "print(f\"Response: {result['response']}\")\n" | |
| ] | |
| } | |
| ], | |
| "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.12.0" | |
| }, | |
| "colab": { | |
| "provenance": [], | |
| "include_colab_link": true | |
| } | |
| }, | |
| "nbformat": 4, | |
| "nbformat_minor": 0 | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment