Skip to content

Instantly share code, notes, and snippets.

@donbr
Last active April 22, 2024 17:19
Show Gist options
  • Select an option

  • Save donbr/03d5a865c1ef77acb94216b607670aa2 to your computer and use it in GitHub Desktop.

Select an option

Save donbr/03d5a865c1ef77acb94216b607670aa2 to your computer and use it in GitHub Desktop.
yt-email-reply-llama3-crewai-groq.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/donbr/03d5a865c1ef77acb94216b607670aa2/yt-email-reply-llama3-crewai-groq.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"source": [
"!pip -q install crewai langchain-groq duckduckgo-search"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "0FDPgaevdeNQ",
"outputId": "d3b1fc04-7062-466c-d7e3-35b3cc566292"
},
"execution_count": 1,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m61.6/61.6 kB\u001b[0m \u001b[31m871.7 kB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m192.7/192.7 kB\u001b[0m \u001b[31m3.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m817.7/817.7 kB\u001b[0m \u001b[31m6.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m311.2/311.2 kB\u001b[0m \u001b[31m7.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m60.1/60.1 kB\u001b[0m \u001b[31m2.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m106.1/106.1 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.0/75.0 kB\u001b[0m \u001b[31m2.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m291.3/291.3 kB\u001b[0m \u001b[31m13.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.5/5.5 MB\u001b[0m \u001b[31m14.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m141.1/141.1 kB\u001b[0m \u001b[31m2.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m233.4/233.4 kB\u001b[0m \u001b[31m13.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m525.5/525.5 kB\u001b[0m \u001b[31m16.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m131.5/131.5 kB\u001b[0m \u001b[31m9.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m41.3/41.3 kB\u001b[0m \u001b[31m2.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m278.2/278.2 kB\u001b[0m \u001b[31m15.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m71.1/71.1 kB\u001b[0m \u001b[31m6.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.0/2.0 MB\u001b[0m \u001b[31m15.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m75.6/75.6 kB\u001b[0m \u001b[31m5.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.9/1.9 MB\u001b[0m \u001b[31m16.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m115.2/115.2 kB\u001b[0m \u001b[31m8.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m53.0/53.0 kB\u001b[0m \u001b[31m3.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m50.8/50.8 kB\u001b[0m \u001b[31m4.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m78.8/78.8 kB\u001b[0m \u001b[31m7.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.4/2.4 MB\u001b[0m \u001b[31m16.6 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m91.9/91.9 kB\u001b[0m \u001b[31m7.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m60.8/60.8 kB\u001b[0m \u001b[31m5.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m5.4/5.4 MB\u001b[0m \u001b[31m16.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m6.8/6.8 MB\u001b[0m \u001b[31m13.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m67.3/67.3 kB\u001b[0m \u001b[31m6.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h Installing build dependencies ... \u001b[?25l\u001b[?25hdone\n",
" Getting requirements to build wheel ... \u001b[?25l\u001b[?25hdone\n",
" Preparing metadata (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m698.9/698.9 kB\u001b[0m \u001b[31m13.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.6/1.6 MB\u001b[0m \u001b[31m14.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m67.6/67.6 kB\u001b[0m \u001b[31m7.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m49.4/49.4 kB\u001b[0m \u001b[31m5.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m77.9/77.9 kB\u001b[0m \u001b[31m9.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m58.3/58.3 kB\u001b[0m \u001b[31m5.7 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m71.9/71.9 kB\u001b[0m \u001b[31m8.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m46.0/46.0 kB\u001b[0m \u001b[31m4.9 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m341.4/341.4 kB\u001b[0m \u001b[31m15.2 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m3.4/3.4 MB\u001b[0m \u001b[31m14.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.3/1.3 MB\u001b[0m \u001b[31m14.4 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m130.2/130.2 kB\u001b[0m \u001b[31m13.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[2K \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m86.8/86.8 kB\u001b[0m \u001b[31m9.8 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
"\u001b[?25h Building wheel for pypika (pyproject.toml) ... \u001b[?25l\u001b[?25hdone\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!pip show crewai"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "cEIeTK_9uFvL",
"outputId": "bcfe620e-7caa-4827-8f55-cf0e2fad12d4"
},
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Name: crewai\n",
"Version: 0.28.8\n",
"Summary: Cutting-edge framework for orchestrating role-playing, autonomous AI agents. By fostering collaborative intelligence, CrewAI empowers agents to work together seamlessly, tackling complex tasks.\n",
"Home-page: \n",
"Author: Joao Moura\n",
"Author-email: joao@crewai.com\n",
"License: \n",
"Location: /usr/local/lib/python3.10/dist-packages\n",
"Requires: appdirs, click, embedchain, instructor, langchain, openai, opentelemetry-api, opentelemetry-exporter-otlp-proto-http, opentelemetry-sdk, pydantic, python-dotenv, regex\n",
"Required-by: \n"
]
}
]
},
{
"cell_type": "markdown",
"source": [
"# The goal\n",
"\n",
"Reply to a customer email\n",
"1. get the email\n",
"2. categorize this as a \"sales\", \"custom enquiry\", \"off topic\", \"customer complaint\"\n",
"3. use the category to research info needed for the reply\n",
"4. take the info and check if there is enough to answer the email\n",
"5. write a reply\n",
"6. check the reply\n",
"7. save the email\n",
"\n",
"### Agents\n",
"- categorizer\n",
"- researcher (web for now would be internal RAG)\n",
"- email writer\n",
"\n",
"\n",
"### Tasks\n",
"- categorize email\n",
"- research the answer\n",
"- write the email"
],
"metadata": {
"id": "tYH3vJxvjVve"
}
},
{
"cell_type": "code",
"source": [
"import os\n",
"from google.colab import userdata\n",
"\n",
"os.environ[\"GROQ_API_KEY\"] = userdata.get('GROQ_API_KEY')"
],
"metadata": {
"id": "oMAPjXCXcd8a"
},
"execution_count": 4,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Utilities"
],
"metadata": {
"id": "b9kHdE8Na8i4"
}
},
{
"cell_type": "code",
"source": [
"import json # Import the JSON module to parse JSON strings\n",
"from langchain_core.agents import AgentFinish\n",
"\n",
"agent_finishes = []\n",
"\n",
"import json\n",
"from typing import Union, List, Tuple, Dict\n",
"from langchain.schema import AgentFinish\n",
"\n",
"call_number = 0\n",
"\n",
"def print_agent_output(agent_output: Union[str, List[Tuple[Dict, str]], AgentFinish], agent_name: str = 'Generic call'):\n",
" global call_number # Declare call_number as a global variable\n",
" call_number += 1\n",
" with open(\"crew_callback_logs.txt\", \"a\") as log_file:\n",
" # Try to parse the output if it is a JSON string\n",
" if isinstance(agent_output, str):\n",
" try:\n",
" agent_output = json.loads(agent_output) # Attempt to parse the JSON string\n",
" except json.JSONDecodeError:\n",
" pass # If there's an error, leave agent_output as is\n",
"\n",
" # Check if the output is a list of tuples as in the first case\n",
" if isinstance(agent_output, list) and all(isinstance(item, tuple) for item in agent_output):\n",
" print(f\"-{call_number}----Dict------------------------------------------\", file=log_file)\n",
" for action, description in agent_output:\n",
" # Print attributes based on assumed structure\n",
" print(f\"Agent Name: {agent_name}\", file=log_file)\n",
" print(f\"Tool used: {getattr(action, 'tool', 'Unknown')}\", file=log_file)\n",
" print(f\"Tool input: {getattr(action, 'tool_input', 'Unknown')}\", file=log_file)\n",
" print(f\"Action log: {getattr(action, 'log', 'Unknown')}\", file=log_file)\n",
" print(f\"Description: {description}\", file=log_file)\n",
" print(\"--------------------------------------------------\", file=log_file)\n",
"\n",
" # Check if the output is a dictionary as in the second case\n",
" elif isinstance(agent_output, AgentFinish):\n",
" print(f\"-{call_number}----AgentFinish---------------------------------------\", file=log_file)\n",
" print(f\"Agent Name: {agent_name}\", file=log_file)\n",
" agent_finishes.append(agent_output)\n",
" # Extracting 'output' and 'log' from the nested 'return_values' if they exist\n",
" output = agent_output.return_values\n",
" # log = agent_output.get('log', 'No log available')\n",
" print(f\"AgentFinish Output: {output['output']}\", file=log_file)\n",
" # print(f\"Log: {log}\", file=log_file)\n",
" # print(f\"AgentFinish: {agent_output}\", file=log_file)\n",
" print(\"--------------------------------------------------\", file=log_file)\n",
"\n",
" # Handle unexpected formats\n",
" else:\n",
" # If the format is unknown, print out the input directly\n",
" print(f\"-{call_number}-Unknown format of agent_output:\", file=log_file)\n",
" print(type(agent_output), file=log_file)\n",
" print(agent_output, file=log_file)\n"
],
"metadata": {
"id": "DvdcABrTa-rR"
},
"execution_count": 5,
"outputs": []
},
{
"cell_type": "code",
"source": [
"from langchain_groq import ChatGroq\n",
"\n",
"GROQ_LLM = ChatGroq(\n",
" # api_key=os.getenv(\"GROQ_API_KEY\"),\n",
" model=\"llama3-70b-8192\"\n",
" )\n"
],
"metadata": {
"id": "uzWrf9n6adVo"
},
"execution_count": 6,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Tools"
],
"metadata": {
"id": "8G40lprgkCJ2"
}
},
{
"cell_type": "code",
"source": [
"from crewai import Crew, Agent, Task, Process\n",
"from langchain_community.tools import DuckDuckGoSearchRun"
],
"metadata": {
"id": "oYA43C0An6tn"
},
"execution_count": 7,
"outputs": []
},
{
"cell_type": "code",
"source": [
"from datetime import datetime\n",
"from random import randint\n",
"from langchain.tools import tool"
],
"metadata": {
"id": "f3GvksYIkA1m"
},
"execution_count": 8,
"outputs": []
},
{
"cell_type": "code",
"source": [
"import json\n",
"import os\n",
"\n",
"import requests\n",
"from langchain.tools import tool\n",
"\n",
"import os\n",
"from crewai import Agent, Task, Crew, Process\n",
"from langchain_community.tools import DuckDuckGoSearchRun\n",
"from langchain.agents import load_tools\n",
"\n",
"search_tool = DuckDuckGoSearchRun()\n"
],
"metadata": {
"id": "vc8SeVoUn5Q6"
},
"execution_count": 9,
"outputs": []
},
{
"cell_type": "code",
"source": [
"search_tool.run(\"usb key price\")"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 139
},
"id": "Nvbxjcy8jJ56",
"outputId": "6cd7d14f-fd11-4938-91e6-feca0b2186c5"
},
"execution_count": 10,
"outputs": [
{
"output_type": "execute_result",
"data": {
"text/plain": [
"'The Feitian ePass K9 USB Security Key offers all the features expected from a security key at an affordable price. ... The USB-C key is best suited for businesses that want to implement physical ... Best security. 7. Kingston IronKey Keypad 200. View at Amazon. View at Amazon. View at Newegg. The best USB flash drive for security This ultra secure flash drive features USB 3.2 Gen 1 speeds and ... It usually costs about $20-$30 less than the faster Kingston DataTraveler Max, while still delivering 616.9 MB/s sequential read speed and 543.9 MB/s sequential writes in our CrystalDiskMark tests ... YubiKey 5 NFC USB-A. Best Premium Security Key. The YubiKey 5 provides the most comprehensive protocols of any security key out there, as well as some excellent additional features for those who are security conscious. $50 at Amazon. Offered in USB-A or USB-C models, in sizes ranging from 8 GB to 256 GB, this drive comes with a five-year warranty. Connector: USB 3.2 Gen 1 Type-A or Type C $92 at Amazon'"
],
"application/vnd.google.colaboratory.intrinsic+json": {
"type": "string"
}
},
"metadata": {},
"execution_count": 10
}
]
},
{
"cell_type": "markdown",
"source": [
"## Agents"
],
"metadata": {
"id": "1FhZyiMakLNL"
}
},
{
"cell_type": "code",
"source": [
"class EmailAgents():\n",
" def make_categorizer_agent(self):\n",
" return Agent(\n",
" role='Email Categorizer Agent',\n",
" goal=\"\"\"take in a email from a human that has emailed out company email address and categorize it \\\n",
" into one of the following categories: \\\n",
" price_equiry - used when someone is asking for information about pricing \\\n",
" customer_complaint - used when someone is complaining about something \\\n",
" product_enquiry - used when someone is asking for information about a product feature, benefit or service but not about pricing \\\\\n",
" customer_feedback - used when someone is giving feedback about a product \\\n",
" off_topic when it doesnt relate to any other category \"\"\",\n",
" backstory=\"\"\"You are a master at understanding what a customer wants when they write an email and are able to categorize it in a useful way\"\"\",\n",
" llm=GROQ_LLM,\n",
" verbose=True,\n",
" allow_delegation=False,\n",
" max_iter=5,\n",
" memory=True,\n",
" step_callback=lambda x: print_agent_output(x,\"Email Categorizer Agent\"),\n",
" )\n",
"\n",
" def make_researcher_agent(self):\n",
" return Agent(\n",
" role='Info Researcher Agent',\n",
" goal=\"\"\"take in a email from a human that has emailed out company email address and the category \\\n",
" that the categorizer agent gave it and decide what information you need to search for for the email writer to reply to \\\n",
" the email in a thoughtful and helpful way.\n",
" If you DONT think a search will help just reply 'NO SEARCH NEEDED'\n",
" If you dont find any useful info just reply 'NO USEFUL RESESARCH FOUND'\n",
" otherwise reply with the info you found that is useful for the email writer\n",
" \"\"\",\n",
" backstory=\"\"\"You are a master at understanding what information our email writer needs to write a reply that \\\n",
" will help the customer\"\"\",\n",
" llm=GROQ_LLM,\n",
" verbose=True,\n",
" max_iter=5,\n",
" allow_delegation=False,\n",
" memory=True,\n",
" tools=[search_tool],\n",
" step_callback=lambda x: print_agent_output(x,\"Info Researcher Agent\"),\n",
" )\n",
"\n",
" def make_email_writer_agent(self):\n",
" return Agent(\n",
" role='Email Writer Agent',\n",
" goal=\"\"\"take in a email from a human that has emailed out company email address, the category \\\n",
" that the categorizer agent gave it and the research from the research agent and \\\n",
" write a helpful email in a thoughtful and friendly way.\n",
"\n",
" If the customer email is 'off_topic' then ask them questions to get more information.\n",
" If the customer email is 'customer_complaint' then try to assure we value them and that we are addressing their issues.\n",
" If the customer email is 'customer_feedback' then try to assure we value them and that we are addressing their issues.\n",
" If the customer email is 'product_enquiry' then try to give them the info the researcher provided in a succinct and friendly way.\n",
" If the customer email is 'price_equiry' then try to give the pricing info they requested.\n",
"\n",
" You never make up information. that hasn't been provided by the researcher or in the email.\n",
" Always sign off the emails in appropriate manner and from Sarah the Resident Manager.\n",
" \"\"\",\n",
" backstory=\"\"\"You are a master at synthesizing a variety of information and writing a helpful email \\\n",
" that will address the customer's issues and provide them with helpful information\"\"\",\n",
" llm=GROQ_LLM,\n",
" verbose=True,\n",
" allow_delegation=False,\n",
" max_iter=5,\n",
" memory=True,\n",
" step_callback=lambda x: print_agent_output(x,\"Email Writer Agent\"),\n",
" )\n",
"\n"
],
"metadata": {
"id": "Hbe-5rH_nHqf"
},
"execution_count": 11,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Tasks"
],
"metadata": {
"id": "0CxGkAovkUVK"
}
},
{
"cell_type": "code",
"source": [
"class EmailTasks():\n",
" # Define your tasks with descriptions and expected outputs\n",
" def categorize_email(self, email_content):\n",
" return Task(\n",
" description=f\"\"\"Conduct a comprehensive analysis of the email provided and categorize into \\\n",
" one of the following categories:\n",
" price_equiry - used when someone is asking for information about pricing \\\n",
" customer_complaint - used when someone is complaining about something \\\n",
" product_enquiry - used when someone is asking for information about a product feature, benefit or service but not about pricing \\\\\n",
" customer_feedback - used when someone is giving feedback about a product \\\n",
" off_topic when it doesnt relate to any other category \\\n",
"\n",
" EMAIL CONTENT:\\n\\n {email_content} \\n\\n\n",
" Output a single cetgory only\"\"\",\n",
" expected_output=\"\"\"A single categtory for the type of email from the types ('price_equiry', 'customer_complaint', 'product_enquiry', 'customer_feedback', 'off_topic') \\\n",
" eg:\n",
" 'price_enquiry' \\\n",
" \"\"\",\n",
" output_file=f\"email_category.txt\",\n",
" agent=categorizer_agent\n",
" )\n",
"\n",
" def research_info_for_email(self, email_content):\n",
" return Task(\n",
" description=f\"\"\"Conduct a comprehensive analysis of the email provided and the category \\\n",
" provided and search the web to find info needed to respond to the email\n",
"\n",
" EMAIL CONTENT:\\n\\n {email_content} \\n\\n\n",
" Only provide the info needed DONT try to write the email\"\"\",\n",
" expected_output=\"\"\"A set of bullet points of useful info for the email writer \\\n",
" or clear instructions that no useful material was found.\"\"\",\n",
" context = [categorize_email],\n",
" output_file=f\"research_info.txt\",\n",
" agent=researcher_agent\n",
" )\n",
"\n",
" def draft_email(self, email_content):\n",
" return Task(\n",
" description=f\"\"\"Conduct a comprehensive analysis of the email provided, the category provided\\\n",
" and the info provided from the research specialist to write an email. \\\n",
"\n",
" Write a simple, polite and too the point email which will respond to the customer's email. \\\n",
" If useful use the info provided from the research specialist in the email. \\\n",
"\n",
" If no useful info was provided from the research specialist the answer politely but don't make up info. \\\n",
"\n",
" EMAIL CONTENT:\\n\\n {email_content} \\n\\n\n",
" Output a single cetgory only\"\"\",\n",
" expected_output=\"\"\"A well crafted email for the customer that addresses their issues and concerns\"\"\",\n",
" context = [categorize_email, research_info_for_email],\n",
" agent=email_writer_agent,\n",
" output_file=f\"draft_email.txt\",\n",
" )"
],
"metadata": {
"id": "6gYSmym8pdum"
},
"execution_count": 12,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Instantiate Agents and Tasks"
],
"metadata": {
"id": "m4xowf-0qXzf"
}
},
{
"cell_type": "code",
"source": [
"# EMAIL = \"\"\"HI there, \\n\n",
"# I am emailing to find out the current price of Bitcoin. \\n\n",
"\n",
"# Can you please help me/\n",
"\n",
"# Thanks,\n",
"# John\n",
"# \"\"\"\n",
"\n",
"EMAIL = \"\"\"HI there, \\n\n",
"I am emailing to say that I had a wonderful stay at your resort last week. \\n\n",
"\n",
"I really appreaciate what your staff did\n",
"\n",
"Thanks,\n",
"Paul\n",
"\"\"\"\n",
"\n",
"EMAIL = \"\"\"HI there, \\n\n",
"I am emailing to say that the resort weather was way to cloudy and overcast. \\n\n",
"I wanted to write a song called 'Here comes the sun but it never came'\n",
"\n",
"What should be the weather in Arizona in April?\n",
"\n",
"I really hope you fix this next time.\n",
"\n",
"Thanks,\n",
"George\n",
"\"\"\"\n",
"\n",
"EMAIL = \"\"\"HI there, \\n\n",
"Why can't I get to sing?\n",
"\n",
"Thanks,\n",
"Ringo\n",
"\"\"\""
],
"metadata": {
"id": "YrQ-4T5NrlTS"
},
"execution_count": 13,
"outputs": []
},
{
"cell_type": "code",
"source": [
"##\n",
"agents = EmailAgents()\n",
"tasks = EmailTasks()\n",
"\n",
"## Agents\n",
"categorizer_agent = agents.make_categorizer_agent()\n",
"researcher_agent = agents.make_researcher_agent()\n",
"email_writer_agent = agents.make_email_writer_agent()\n",
"\n",
"## Tasks\n",
"categorize_email = tasks.categorize_email(EMAIL)\n",
"research_info_for_email = tasks.research_info_for_email(EMAIL)\n",
"draft_email = tasks.draft_email(EMAIL)\n"
],
"metadata": {
"id": "AQdhwjIrqebG"
},
"execution_count": 14,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"## Crew"
],
"metadata": {
"id": "Xma0GE-Kk9Ef"
}
},
{
"cell_type": "code",
"source": [
"from crewai import Crew, Process\n",
"\n",
"# Instantiate your crew with a sequential process\n",
"crew = Crew(\n",
" agents=[categorizer_agent, researcher_agent, email_writer_agent],\n",
" tasks=[categorize_email, research_info_for_email, draft_email],\n",
" verbose=2,\n",
" process=Process.sequential,\n",
" full_output=True,\n",
" share_crew=False,\n",
" step_callback=lambda x: print_agent_output(x,\"MasterCrew Agent\")\n",
")"
],
"metadata": {
"id": "e3SXzkhhn49N"
},
"execution_count": 15,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Kick off the crew's work\n",
"results = crew.kickoff()\n",
"\n",
"\n",
"# Print the results\n",
"print(\"Crew Work Results:\")\n",
"print(results)\n",
"\n",
"# print(f\"Categorize Email: {categorize_email.output}\")\n",
"print(crew.usage_metrics)"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "HexYqBNBdQ5B",
"outputId": "1ede93c5-cc00-4704-8af6-166bcfc95375"
},
"execution_count": 16,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"\u001b[1m\u001b[95m [DEBUG]: == Working Agent: Email Categorizer Agent\u001b[00m\n",
"\u001b[1m\u001b[95m [INFO]: == Starting Task: Conduct a comprehensive analysis of the email provided and categorize into one of the following categories:\n",
" price_equiry - used when someone is asking for information about pricing customer_complaint - used when someone is complaining about something product_enquiry - used when someone is asking for information about a product feature, benefit or service but not about pricing \\\n",
" customer_feedback - used when someone is giving feedback about a product off_topic when it doesnt relate to any other category \n",
" EMAIL CONTENT:\n",
"\n",
" HI there, \n",
"\n",
"Why can't I get to sing?\n",
"\n",
"Thanks,\n",
"Ringo\n",
" \n",
"\n",
"\n",
" Output a single cetgory only\u001b[00m\n",
"\n",
"\n",
"\u001b[1m> Entering new CrewAgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: I have carefully analyzed the email content and I believe I have a clear understanding of the customer's intent.\n",
"\n",
"Final Answer: off_topic\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[1m\u001b[92m [DEBUG]: == [Email Categorizer Agent] Task output: off_topic\n",
"\n",
"\u001b[00m\n",
"\u001b[1m\u001b[95m [DEBUG]: == Working Agent: Info Researcher Agent\u001b[00m\n",
"\u001b[1m\u001b[95m [INFO]: == Starting Task: Conduct a comprehensive analysis of the email provided and the category provided and search the web to find info needed to respond to the email\n",
"\n",
" EMAIL CONTENT:\n",
"\n",
" HI there, \n",
"\n",
"Why can't I get to sing?\n",
"\n",
"Thanks,\n",
"Ringo\n",
" \n",
"\n",
"\n",
" Only provide the info needed DONT try to write the email\u001b[00m\n",
"\n",
"\n",
"\u001b[1m> Entering new CrewAgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: The email is categorized as \"off_topic\" and the customer, Ringo, is asking why they can't get to sing. I need to understand the context of this question to provide a helpful response.\n",
"\n",
"Action: duckduckgo_search\n",
"Action Input: {\"q\": \"can't sing reasons\"}\u001b[0m\u001b[91m \n",
"\n",
"I encountered an error while trying to use the tool. This was the error: DuckDuckGoSearchRun._run() got an unexpected keyword argument 'q'.\n",
" Tool duckduckgo_search accepts these inputs: A wrapper around DuckDuckGo Search. Useful for when you need to answer questions about current events. Input should be a search query.\n",
"\u001b[00m\n",
"\u001b[32;1m\u001b[1;3mThought: I understand that Ringo is asking why they can't sing, but I need to clarify what they mean by \"can't get to sing\". Are they trying to sing online, or is it a physical limitation?\n",
"\n",
"Action: duckduckgo_search\n",
"Action Input: {\"q\": \"can't sing online\"}\u001b[0m\u001b[91m \n",
"\n",
"I encountered an error while trying to use the tool. This was the error: DuckDuckGoSearchRun._run() got an unexpected keyword argument 'q'.\n",
" Tool duckduckgo_search accepts these inputs: A wrapper around DuckDuckGo Search. Useful for when you need to answer questions about current events. Input should be a search query.\n",
"\u001b[00m\n",
"\u001b[32;1m\u001b[1;3mThought: I understand that Ringo is asking why they can't sing, but I need to clarify what they mean by \"can't get to sing\". Are they trying to sing online, or is it a physical limitation?\n",
"\n",
"Action: duckduckgo_search\n",
"Action Input: \"can't sing online\"\u001b[0m\u001b[32;1m\u001b[1;3mThought: I now can give a great answer\n",
"Final Answer: \n",
"\n",
"* Check if Ringo is referring to a physical limitation, such as a vocal cord issue or a medical condition that prevents them from singing.\n",
"* Consider the possibility that Ringo is trying to sing online, such as participating in an online karaoke or music platform, and is experiencing technical difficulties.\n",
"* If Ringo is experiencing physical limitations, suggest consulting a medical professional for guidance.\n",
"* If Ringo is experiencing technical difficulties, suggest troubleshooting steps or contacting the platform's support team for assistance.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[1m\u001b[92m [DEBUG]: == [Info Researcher Agent] Task output: * Check if Ringo is referring to a physical limitation, such as a vocal cord issue or a medical condition that prevents them from singing.\n",
"* Consider the possibility that Ringo is trying to sing online, such as participating in an online karaoke or music platform, and is experiencing technical difficulties.\n",
"* If Ringo is experiencing physical limitations, suggest consulting a medical professional for guidance.\n",
"* If Ringo is experiencing technical difficulties, suggest troubleshooting steps or contacting the platform's support team for assistance.\n",
"\n",
"\u001b[00m\n",
"\u001b[1m\u001b[95m [DEBUG]: == Working Agent: Email Writer Agent\u001b[00m\n",
"\u001b[1m\u001b[95m [INFO]: == Starting Task: Conduct a comprehensive analysis of the email provided, the category provided and the info provided from the research specialist to write an email. \n",
" Write a simple, polite and too the point email which will respond to the customer's email. If useful use the info provided from the research specialist in the email. \n",
" If no useful info was provided from the research specialist the answer politely but don't make up info. \n",
" EMAIL CONTENT:\n",
"\n",
" HI there, \n",
"\n",
"Why can't I get to sing?\n",
"\n",
"Thanks,\n",
"Ringo\n",
" \n",
"\n",
"\n",
" Output a single cetgory only\u001b[00m\n",
"\n",
"\n",
"\u001b[1m> Entering new CrewAgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: I now can give a great answer\n",
"\n",
"Final Answer:\n",
"\n",
"Dear Ringo,\n",
"\n",
"Thank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\n",
"\n",
"If you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\n",
"\n",
"Please let me know more about your situation, and I'll do my best to help you get back to singing.\n",
"\n",
"Best regards,\n",
"Sarah the Resident Manager\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[1m\u001b[92m [DEBUG]: == [Email Writer Agent] Task output: Dear Ringo,\n",
"\n",
"Thank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\n",
"\n",
"If you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\n",
"\n",
"Please let me know more about your situation, and I'll do my best to help you get back to singing.\n",
"\n",
"Best regards,\n",
"Sarah the Resident Manager\n",
"\n",
"\u001b[00m\n",
"Crew Work Results:\n",
"{'final_output': \"Dear Ringo,\\n\\nThank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\\n\\nIf you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\\n\\nPlease let me know more about your situation, and I'll do my best to help you get back to singing.\\n\\nBest regards,\\nSarah the Resident Manager\", 'tasks_outputs': [TaskOutput(description=\"Conduct a comprehensive analysis of the email provided and categorize into one of the following categories:\\n price_equiry - used when someone is asking for information about pricing customer_complaint - used when someone is complaining about something product_enquiry - used when someone is asking for information about a product feature, benefit or service but not about pricing \\\\\\n customer_feedback - used when someone is giving feedback about a product off_topic when it doesnt relate to any other category \\n EMAIL CONTENT:\\n\\n HI there, \\n\\nWhy can't I get to sing?\\n\\nThanks,\\nRingo\\n \\n\\n\\n Output a single cetgory only\", summary='Conduct a comprehensive analysis of the email provided and categorize...', exported_output='off_topic', raw_output='off_topic'), TaskOutput(description=\"Conduct a comprehensive analysis of the email provided and the category provided and search the web to find info needed to respond to the email\\n\\n EMAIL CONTENT:\\n\\n HI there, \\n\\nWhy can't I get to sing?\\n\\nThanks,\\nRingo\\n \\n\\n\\n Only provide the info needed DONT try to write the email\", summary='Conduct a comprehensive analysis of the email provided and the...', exported_output=\"* Check if Ringo is referring to a physical limitation, such as a vocal cord issue or a medical condition that prevents them from singing.\\n* Consider the possibility that Ringo is trying to sing online, such as participating in an online karaoke or music platform, and is experiencing technical difficulties.\\n* If Ringo is experiencing physical limitations, suggest consulting a medical professional for guidance.\\n* If Ringo is experiencing technical difficulties, suggest troubleshooting steps or contacting the platform's support team for assistance.\", raw_output=\"* Check if Ringo is referring to a physical limitation, such as a vocal cord issue or a medical condition that prevents them from singing.\\n* Consider the possibility that Ringo is trying to sing online, such as participating in an online karaoke or music platform, and is experiencing technical difficulties.\\n* If Ringo is experiencing physical limitations, suggest consulting a medical professional for guidance.\\n* If Ringo is experiencing technical difficulties, suggest troubleshooting steps or contacting the platform's support team for assistance.\"), TaskOutput(description=\"Conduct a comprehensive analysis of the email provided, the category provided and the info provided from the research specialist to write an email. \\n Write a simple, polite and too the point email which will respond to the customer's email. If useful use the info provided from the research specialist in the email. \\n If no useful info was provided from the research specialist the answer politely but don't make up info. \\n EMAIL CONTENT:\\n\\n HI there, \\n\\nWhy can't I get to sing?\\n\\nThanks,\\nRingo\\n \\n\\n\\n Output a single cetgory only\", summary='Conduct a comprehensive analysis of the email provided, the category...', exported_output=\"Dear Ringo,\\n\\nThank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\\n\\nIf you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\\n\\nPlease let me know more about your situation, and I'll do my best to help you get back to singing.\\n\\nBest regards,\\nSarah the Resident Manager\", raw_output=\"Dear Ringo,\\n\\nThank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\\n\\nIf you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\\n\\nPlease let me know more about your situation, and I'll do my best to help you get back to singing.\\n\\nBest regards,\\nSarah the Resident Manager\")]}\n",
"{'total_tokens': 44082, 'prompt_tokens': 39465, 'completion_tokens': 4617, 'successful_requests': 54}\n"
]
}
]
},
{
"cell_type": "code",
"source": [
"!cat /content/draft_email.txt"
],
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"id": "cP7KiMdkbIAh",
"outputId": "b3f393b5-eddf-4606-e019-d8ae36b974d0"
},
"execution_count": 17,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Dear Ringo,\n",
"\n",
"Thank you for reaching out to us about your concern. I'm sorry to hear that you're having trouble singing. Can you please help me better understand the issue you're facing? Are you experiencing a physical limitation, such as a vocal cord issue or a medical condition that prevents you from singing? Or are you trying to sing online, perhaps through an online karaoke or music platform, and encountering technical difficulties?\n",
"\n",
"If you're experiencing physical limitations, I would recommend consulting a medical professional for guidance. If you're experiencing technical difficulties, I'd be happy to help you troubleshoot or direct you to the platform's support team for assistance.\n",
"\n",
"Please let me know more about your situation, and I'll do my best to help you get back to singing.\n",
"\n",
"Best regards,\n",
"Sarah the Resident Manager"
]
}
]
},
{
"cell_type": "code",
"source": [
"!cat /content/email_category.txt"
],
"metadata": {
"id": "uNbw6y96bICK",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "92c418dc-0a15-4b1a-9b86-d47df71aa8d8"
},
"execution_count": 18,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"off_topic"
]
}
]
},
{
"cell_type": "code",
"source": [
"!cat /content/research_info.txt"
],
"metadata": {
"id": "MNx8mRoGbIES",
"colab": {
"base_uri": "https://localhost:8080/"
},
"outputId": "5c7bf23b-770e-4c40-9cfe-1a498fa44172"
},
"execution_count": 19,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"* Check if Ringo is referring to a physical limitation, such as a vocal cord issue or a medical condition that prevents them from singing.\n",
"* Consider the possibility that Ringo is trying to sing online, such as participating in an online karaoke or music platform, and is experiencing technical difficulties.\n",
"* If Ringo is experiencing physical limitations, suggest consulting a medical professional for guidance.\n",
"* If Ringo is experiencing technical difficulties, suggest troubleshooting steps or contacting the platform's support team for assistance."
]
}
]
},
{
"cell_type": "code",
"source": [
"#Task\n",
"\n",
"from crewai import Task\n",
"\n",
"class PersonalizeEmailTask():\n",
" def personalize_email(self, agent, recipient, email_template):\n",
" return Task(\n",
" description=f\"\"\"\n",
" Personalize the template email for recipient using their information.\n",
"\n",
" - Name: {recipient['first_name']} {recipient['last_name']}\n",
" - Email: {recipient['email']}\n",
" - Bio: {recipient['bio']}\n",
" - Last conversation: {recipient['last_conversation']}\n",
"\n",
" Important Info to consider:\n",
" - When personalizing the email, only use one sentence from the bio or last conversation.\n",
" And make sure to incorporate it naturally into the email. without going too much in to detail.\n",
" - Make sure to keep the updated email roughly the same same length as the template email.\n",
"\n",
" The template email is as follows:\n",
"\n",
" ```\n",
" {email_template}\n",
" ```\n",
" \"\"\",\n",
" agent=agent,\n",
" expected_output=f\"Personalized email draft.\",\n",
" async_execution=True,\n",
" )\n",
"\n",
" def ghostwrite_email(self, agent, draft_email, recipient):\n",
" return Task(\n",
" description=f\"\"\"\n",
" Revise the draft email to adopt the following writing style.\n",
"\n",
" Writing Style:\n",
" - Use a more informal, engaging, and slightly sales-oriented tone, mirroring ghost writer's final email communication style.\n",
" - This approach prioritizes clear, direct communication while maintaining a friendly and approachable tone.\n",
" - Use straightforward language, including phrases like \"Hey [Name]!\" to start emails or messages.\n",
" - The tone will be optimistic and encouraging, aiming to build rapport and motivate action, while staying grounded in practical advice.\n",
"\n",
" Important Notes:\n",
" - Do not use emojis.\n",
" \"\"\",\n",
" agent=agent,\n",
" context=[draft_email],\n",
" expected_output=f\"A revised email draft in ghost writer's specified tone and style.\",\n",
" output_file=f\"output/{recipient['first_name']}_{recipient['last_name']}.txt\",\n",
" )"
],
"metadata": {
"id": "xd4Lff70Yaz3"
},
"execution_count": 20,
"outputs": []
},
{
"cell_type": "code",
"source": [
"#agents\n",
"import os\n",
"from crewai import Agent\n",
"from langchain_groq import ChatGroq\n",
"from langchain_openai import ChatOpenAI\n",
"\n",
"\n",
"class EmailPersonalizationAgents():\n",
" def __init__(self):\n",
" self.llm = ChatGroq(\n",
" api_key=os.getenv(\"GROQ_API_KEY\"),\n",
" model=\"llama3-70b-8192\"\n",
" )\n",
"\n",
" # self.llm = ChatOpenAI(\n",
" # model=\"gpt-4-turbo-preview\",\n",
" # )\n",
"\n",
" def personalize_email_agent(self):\n",
" return Agent(\n",
" role=\"Email Personalizer\",\n",
" goal=f\"\"\"\n",
" Personalize template emails for recipients using their information.\n",
"\n",
" Given a template email and recipient information (name, email, bio, last conversation),\n",
" personalize the email by incorporating the recipient's details\n",
" into the email while maintaining the core message and structure of the original email.\n",
" This involves updating the introduction, body, and closing of the email to make\n",
" it more personal and engaging for each recipient.\n",
" \"\"\",\n",
" backstory=\"\"\"\n",
" As an Email Personalizer, you are responsible for customizing template emails for individual recipients based on their information and previous interactions.\n",
" \"\"\",\n",
" verbose=True,\n",
" llm=self.llm,\n",
" max_iter=2,\n",
" )\n",
"\n",
" def ghostwriter_agent(self):\n",
" return Agent(\n",
" role=\"Ghostwriter\",\n",
" goal=f\"\"\"\n",
" Revise draft emails to adopt the Ghostwriter's writing style.\n",
"\n",
" Use an informal, engaging, and slightly sales-oriented tone, mirroring the Ghostwriter's final email communication style.\n",
" \"\"\",\n",
" backstory=\"\"\"\n",
" As a Ghostwriter, you are responsible for revising draft emails to match the Ghostwriter's writing style, focusing on clear, direct communication with a friendly and approachable tone.\n",
" \"\"\",\n",
" verbose=True,\n",
" llm=self.llm,\n",
" max_iter=2,\n",
" )"
],
"metadata": {
"id": "X5d6qtvDX525"
},
"execution_count": 21,
"outputs": []
},
{
"cell_type": "code",
"source": [
"!mkdir output"
],
"metadata": {
"id": "jvg-BgpRfRl_"
},
"execution_count": 22,
"outputs": []
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 211
},
"id": "MiIiOtujUnh0",
"outputId": "154fa783-c4f0-41ef-a2d2-fc3d6ab5c249"
},
"outputs": [
{
"output_type": "error",
"ename": "FileNotFoundError",
"evalue": "[Errno 2] No such file or directory: 'clients_small.csv'",
"traceback": [
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
"\u001b[0;31mFileNotFoundError\u001b[0m Traceback (most recent call last)",
"\u001b[0;32m<ipython-input-23-f011555f8e2e>\u001b[0m in \u001b[0;36m<cell line: 51>\u001b[0;34m()\u001b[0m\n\u001b[1;32m 49\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 50\u001b[0m \u001b[0;31m# Open the CSV file\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 51\u001b[0;31m \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mcsv_file_path\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mmode\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m'r'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnewline\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m''\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mfile\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 52\u001b[0m \u001b[0;31m# Create a CSV reader object\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 53\u001b[0m \u001b[0mcsv_reader\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mcsv\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mDictReader\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfile\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
"\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'clients_small.csv'"
]
}
],
"source": [
"#main\n",
"\n",
"import os\n",
"import time\n",
"import csv\n",
"from crewai import Crew\n",
"from langchain_groq import ChatGroq\n",
"# from agents import EmailPersonalizationAgents\n",
"# from tasks import PersonalizeEmailTask\n",
"\n",
"# 0. Setup environment\n",
"# from dotenv import load_dotenv\n",
"# load_dotenv()\n",
"\n",
"email_template = \"\"\"\n",
"Hey [Name]!\n",
"\n",
"Just a quick reminder that we have a Skool community where you can\n",
"join us for weekly coaching calls every Tuesday at 6 PM Eastern time.\n",
"The community is completely free and we're about to hit the 500\n",
"user milestone. We'd love to have you join us!\n",
"\n",
"If you have any questions or need help with your projects,\n",
"this is a great place to connect with others and get support.\n",
"\n",
"If you're enjoying the AI-related content, make sure to check out\n",
"some of the other videos on my channel. Don't forget to hit that\n",
"like and subscribe button to stay updated with the latest content.\n",
"Looking forward to seeing you in the community!\n",
"\n",
"Best regards,\n",
"Brandon Hancock\n",
"\"\"\"\n",
"\n",
"# 1. Create agents\n",
"agents = EmailPersonalizationAgents()\n",
"\n",
"email_personalizer = agents.personalize_email_agent()\n",
"ghostwriter = agents.ghostwriter_agent()\n",
"\n",
"# 2. Create tasks\n",
"tasks = PersonalizeEmailTask()\n",
"\n",
"personalize_email_tasks = []\n",
"ghostwrite_email_tasks = []\n",
"\n",
"# Path to the CSV file containing client information\n",
"csv_file_path = 'clients_small.csv'\n",
"\n",
"# Open the CSV file\n",
"with open(csv_file_path, mode='r', newline='') as file:\n",
" # Create a CSV reader object\n",
" csv_reader = csv.DictReader(file)\n",
"\n",
" # Iterate over each row in the CSV file\n",
" for row in csv_reader:\n",
" # Access each field in the row\n",
" recipient = {\n",
" 'first_name': row['first_name'],\n",
" 'last_name': row['last_name'],\n",
" 'email': row['email'],\n",
" 'bio': row['bio'],\n",
" 'last_conversation': row['last_conversation']\n",
" }\n",
"\n",
" # Create a personalize_email task for each recipient\n",
" personalize_email_task = tasks.personalize_email(\n",
" agent=email_personalizer,\n",
" recipient=recipient,\n",
" email_template=email_template\n",
" )\n",
"\n",
" # Create a ghostwrite_email task for each recipient\n",
" ghostwrite_email_task = tasks.ghostwrite_email(\n",
" agent=ghostwriter,\n",
" draft_email=personalize_email_task,\n",
" recipient=recipient\n",
" )\n",
"\n",
" # Add the task to the crew\n",
" personalize_email_tasks.append(personalize_email_task)\n",
" ghostwrite_email_tasks.append(ghostwrite_email_task)\n",
"\n",
"\n",
"# Setup Crew\n",
"crew = Crew(\n",
" agents=[\n",
" email_personalizer,\n",
" ghostwriter\n",
" ],\n",
" tasks=[\n",
" *personalize_email_tasks,\n",
" *ghostwrite_email_tasks\n",
" ],\n",
" max_rpm=29\n",
")\n",
"\n",
"# Kick off the crew\n",
"start_time = time.time()\n",
"\n",
"results = crew.kickoff()\n",
"\n",
"end_time = time.time()\n",
"elapsed_time = end_time - start_time\n",
"\n",
"print(f\"Crew kickoff took {elapsed_time} seconds.\")\n",
"print(\"Crew usage\", crew.usage_metrics)"
]
},
{
"cell_type": "code",
"source": [
"results"
],
"metadata": {
"id": "lLJlNd9YzSiP"
},
"execution_count": null,
"outputs": []
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment