AYS11231 commited on
Commit
0af0679
·
verified ·
1 Parent(s): 528010a

Upload folder using huggingface_hub

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -0
  2. 1_lab1.ipynb +1569 -0
  3. 2_lab2.ipynb +0 -0
  4. 3_lab3.ipynb +681 -0
  5. 4_lab4.ipynb +463 -0
  6. README.md +2 -8
  7. __pycache__/enhanced_app_rag.cpython-312.pyc +0 -0
  8. bulk_loader_script.py +67 -0
  9. community_contributions/1_lab1_Mudassar.ipynb +260 -0
  10. community_contributions/1_lab1_Thanh.ipynb +165 -0
  11. community_contributions/1_lab1_gemini.ipynb +306 -0
  12. community_contributions/1_lab1_groq_llama.ipynb +296 -0
  13. community_contributions/1_lab1_open_router.ipynb +323 -0
  14. community_contributions/1_lab2_Kaushik_Parallelization.ipynb +355 -0
  15. community_contributions/1_lab2_Routing_Workflow.ipynb +514 -0
  16. community_contributions/2_lab2_ReAct_Pattern.ipynb +289 -0
  17. community_contributions/2_lab2_async.ipynb +474 -0
  18. community_contributions/2_lab2_exercise.ipynb +336 -0
  19. community_contributions/2_lab2_exercise_BrettSanders_ChainOfThought.ipynb +241 -0
  20. community_contributions/2_lab2_multi-evaluation-criteria.ipynb +506 -0
  21. community_contributions/2_lab2_reflection_pattern.ipynb +311 -0
  22. community_contributions/2_lab2_reflection_pattern2.ipynb +999 -0
  23. community_contributions/2_lab2_six-thinking-hats-simulator.ipynb +457 -0
  24. community_contributions/3_lab3_groq_llama_generator_gemini_evaluator.ipynb +286 -0
  25. community_contributions/4_lab4_slack.ipynb +469 -0
  26. community_contributions/4_lab4_with_telegram.ipynb +422 -0
  27. community_contributions/Business_Idea.ipynb +388 -0
  28. community_contributions/Multi-Model-Resume–JD-Match-Analyzer/.gitignore +1 -0
  29. community_contributions/Multi-Model-Resume/342/200/223JD-Match-Analyzer/AnalyzeResume.png +0 -0
  30. community_contributions/Multi-Model-Resume–JD-Match-Analyzer/README.md +48 -0
  31. community_contributions/Multi-Model-Resume–JD-Match-Analyzer/multi_file_ingestion.py +44 -0
  32. community_contributions/Multi-Model-Resume–JD-Match-Analyzer/resume_agent.py +262 -0
  33. community_contributions/app_rate_limiter_mailgun_integration.py +231 -0
  34. community_contributions/chatbot_rag_evaluation/.gitignore +13 -0
  35. community_contributions/chatbot_rag_evaluation/README.md +42 -0
  36. community_contributions/chatbot_rag_evaluation/app.py +23 -0
  37. community_contributions/chatbot_rag_evaluation/chat.py +134 -0
  38. community_contributions/chatbot_rag_evaluation/controller.py +21 -0
  39. community_contributions/chatbot_rag_evaluation/evaluator.py +43 -0
  40. community_contributions/chatbot_rag_evaluation/knowledge_base/summary.txt +1 -0
  41. community_contributions/chatbot_rag_evaluation/rag.py +41 -0
  42. community_contributions/chatbot_rag_evaluation/requirements.txt +198 -0
  43. community_contributions/chatbot_rag_evaluation/tools.py +68 -0
  44. community_contributions/claude_based_chatbot_tc/.gitignore +41 -0
  45. community_contributions/claude_based_chatbot_tc/README.md +6 -0
  46. community_contributions/claude_based_chatbot_tc/app.py +33 -0
  47. community_contributions/claude_based_chatbot_tc/docs/Multi-modal-tailored-faq.ipynb +309 -0
  48. community_contributions/claude_based_chatbot_tc/modules/__init__.py +3 -0
  49. community_contributions/claude_based_chatbot_tc/modules/chat.py +152 -0
  50. community_contributions/claude_based_chatbot_tc/modules/config.py +18 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ me/Resume[[:space:]]AYS_MLBD.pdf filter=lfs diff=lfs merge=lfs -text
1_lab1.ipynb ADDED
@@ -0,0 +1,1569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Welcome to the start of your adventure in Agentic AI"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
15
+ " <tr>\n",
16
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
17
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
18
+ " </td>\n",
19
+ " <td>\n",
20
+ " <h2 style=\"color:#ff7800;\">Are you ready for action??</h2>\n",
21
+ " <span style=\"color:#ff7800;\">Have you completed all the setup steps in the <a href=\"../setup/\">setup</a> folder?<br/>\n",
22
+ " Have you read the <a href=\"../README.md\">README</a>? Many common questions are answered here!<br/>\n",
23
+ " Have you checked out the guides in the <a href=\"../guides/01_intro.ipynb\">guides</a> folder?<br/>\n",
24
+ " Well in that case, you're ready!!\n",
25
+ " </span>\n",
26
+ " </td>\n",
27
+ " </tr>\n",
28
+ "</table>"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "markdown",
33
+ "metadata": {},
34
+ "source": [
35
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
36
+ " <tr>\n",
37
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
38
+ " <img src=\"../assets/tools.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
39
+ " </td>\n",
40
+ " <td>\n",
41
+ " <h2 style=\"color:#00bfff;\">This code is a live resource - keep an eye out for my updates</h2>\n",
42
+ " <span style=\"color:#00bfff;\">I push updates regularly. As people ask questions or have problems, I add more examples and improve explanations. As a result, the code below might not be identical to the videos, as I've added more steps and better comments. Consider this like an interactive book that accompanies the lectures.<br/><br/>\n",
43
+ " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n",
44
+ " </span>\n",
45
+ " </td>\n",
46
+ " </tr>\n",
47
+ "</table>"
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "markdown",
52
+ "metadata": {},
53
+ "source": [
54
+ "### And please do remember to contact me if I can help\n",
55
+ "\n",
56
+ "And I love to connect: https://www.linkedin.com/in/eddonner/\n",
57
+ "\n",
58
+ "\n",
59
+ "### New to Notebooks like this one? Head over to the guides folder!\n",
60
+ "\n",
61
+ "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n",
62
+ "- Open extensions (View >> extensions)\n",
63
+ "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n",
64
+ "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n",
65
+ "Then View >> Explorer to bring back the File Explorer.\n",
66
+ "\n",
67
+ "And then:\n",
68
+ "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n",
69
+ "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n",
70
+ "3. Enjoy!\n",
71
+ "\n",
72
+ "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n",
73
+ "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n",
74
+ "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n",
75
+ "2. In the Settings search bar, type \"venv\" \n",
76
+ "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n",
77
+ "And then try again.\n",
78
+ "\n",
79
+ "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n",
80
+ "`conda deactivate` \n",
81
+ "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n",
82
+ "`conda config --set auto_activate_base false` \n",
83
+ "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version."
84
+ ]
85
+ },
86
+ {
87
+ "cell_type": "code",
88
+ "execution_count": 1,
89
+ "metadata": {},
90
+ "outputs": [],
91
+ "source": [
92
+ "# First let's do an import. If you get an Import Error, double check that your Kernel is correct..\n",
93
+ "\n",
94
+ "from dotenv import load_dotenv\n"
95
+ ]
96
+ },
97
+ {
98
+ "cell_type": "code",
99
+ "execution_count": 2,
100
+ "metadata": {},
101
+ "outputs": [
102
+ {
103
+ "data": {
104
+ "text/plain": [
105
+ "True"
106
+ ]
107
+ },
108
+ "execution_count": 2,
109
+ "metadata": {},
110
+ "output_type": "execute_result"
111
+ }
112
+ ],
113
+ "source": [
114
+ "# Next it's time to load the API keys into environment variables\n",
115
+ "# If this returns false, see the next cell!\n",
116
+ "\n",
117
+ "load_dotenv(override=True)"
118
+ ]
119
+ },
120
+ {
121
+ "cell_type": "markdown",
122
+ "metadata": {},
123
+ "source": [
124
+ "### Wait, did that just output `False`??\n",
125
+ "\n",
126
+ "If so, the most common reason is that you didn't save your `.env` file after adding the key! Be sure to have saved.\n",
127
+ "\n",
128
+ "Also, make sure the `.env` file is named precisely `.env` and is in the project root directory (`agents`)\n",
129
+ "\n",
130
+ "By the way, your `.env` file should have a stop symbol next to it in Cursor on the left, and that's actually a good thing: that's Cursor saying to you, \"hey, I realize this is a file filled with secret information, and I'm not going to send it to an external AI to suggest changes, because your keys should not be shown to anyone else.\""
131
+ ]
132
+ },
133
+ {
134
+ "cell_type": "markdown",
135
+ "metadata": {},
136
+ "source": [
137
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
138
+ " <tr>\n",
139
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
140
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
141
+ " </td>\n",
142
+ " <td>\n",
143
+ " <h2 style=\"color:#ff7800;\">Final reminders</h2>\n",
144
+ " <span style=\"color:#ff7800;\">1. If you're not confident about Environment Variables or Web Endpoints / APIs, please read Topics 3 and 5 in this <a href=\"../guides/04_technical_foundations.ipynb\">technical foundations guide</a>.<br/>\n",
145
+ " 2. If you want to use AIs other than OpenAI, like Gemini, DeepSeek or Ollama (free), please see the first section in this <a href=\"../guides/09_ai_apis_and_ollama.ipynb\">AI APIs guide</a>.<br/>\n",
146
+ " 3. If you ever get a Name Error in Python, you can always fix it immediately; see the last section of this <a href=\"../guides/06_python_foundations.ipynb\">Python Foundations guide</a> and follow both tutorials and exercises.<br/>\n",
147
+ " </span>\n",
148
+ " </td>\n",
149
+ " </tr>\n",
150
+ "</table>"
151
+ ]
152
+ },
153
+ {
154
+ "cell_type": "code",
155
+ "execution_count": 3,
156
+ "metadata": {},
157
+ "outputs": [
158
+ {
159
+ "name": "stdout",
160
+ "output_type": "stream",
161
+ "text": [
162
+ "OpenAI API Key exists and begins sk-proj-\n"
163
+ ]
164
+ }
165
+ ],
166
+ "source": [
167
+ "# Check the key - if you're not using OpenAI, check whichever key you're using! Ollama doesn't need a key.\n",
168
+ "\n",
169
+ "import os\n",
170
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
171
+ "\n",
172
+ "if openai_api_key:\n",
173
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
174
+ "else:\n",
175
+ " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the setup folder\")\n",
176
+ " \n"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type": "code",
181
+ "execution_count": 4,
182
+ "metadata": {},
183
+ "outputs": [],
184
+ "source": [
185
+ "# And now - the all important import statement\n",
186
+ "# If you get an import error - head over to troubleshooting in the Setup folder\n",
187
+ "\n",
188
+ "from openai import OpenAI"
189
+ ]
190
+ },
191
+ {
192
+ "cell_type": "code",
193
+ "execution_count": 5,
194
+ "metadata": {},
195
+ "outputs": [],
196
+ "source": [
197
+ "# And now we'll create an instance of the OpenAI class\n",
198
+ "# If you're not sure what it means to create an instance of a class - head over to the guides folder (guide 6)!\n",
199
+ "# If you get a NameError - head over to the guides folder (guide 6)to learn about NameErrors - always instantly fixable\n",
200
+ "# If you're not using OpenAI, you just need to slightly modify this - precise instructions are in the AI APIs guide (guide 9)\n",
201
+ "\n",
202
+ "openai = OpenAI()"
203
+ ]
204
+ },
205
+ {
206
+ "cell_type": "code",
207
+ "execution_count": 6,
208
+ "metadata": {},
209
+ "outputs": [],
210
+ "source": [
211
+ "# Create a list of messages in the familiar OpenAI format\n",
212
+ "\n",
213
+ "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]"
214
+ ]
215
+ },
216
+ {
217
+ "cell_type": "code",
218
+ "execution_count": 7,
219
+ "metadata": {},
220
+ "outputs": [
221
+ {
222
+ "name": "stdout",
223
+ "output_type": "stream",
224
+ "text": [
225
+ "2 + 2 = 4\n"
226
+ ]
227
+ }
228
+ ],
229
+ "source": [
230
+ "# And now call it! Any problems, head to the troubleshooting guide\n",
231
+ "# This uses GPT 4.1 nano, the incredibly cheap model\n",
232
+ "# The APIs guide (guide 9) has exact instructions for using even cheaper or free alternatives to OpenAI\n",
233
+ "# If you get a NameError, head to the guides folder (guide 6) to learn about NameErrors - always instantly fixable\n",
234
+ "\n",
235
+ "response = openai.chat.completions.create(\n",
236
+ " model=\"gpt-4.1-nano\",\n",
237
+ " messages=messages\n",
238
+ ")\n",
239
+ "\n",
240
+ "print(response.choices[0].message.content)\n"
241
+ ]
242
+ },
243
+ {
244
+ "cell_type": "code",
245
+ "execution_count": 8,
246
+ "metadata": {},
247
+ "outputs": [],
248
+ "source": [
249
+ "# And now - let's ask for a question:\n",
250
+ "\n",
251
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
252
+ "messages = [{\"role\": \"user\", \"content\": question}]\n"
253
+ ]
254
+ },
255
+ {
256
+ "cell_type": "code",
257
+ "execution_count": 9,
258
+ "metadata": {},
259
+ "outputs": [
260
+ {
261
+ "name": "stdout",
262
+ "output_type": "stream",
263
+ "text": [
264
+ "A bat and a ball cost $1.10 in total. The bat costs $1.00 more than the ball. How much does the ball cost?\n"
265
+ ]
266
+ }
267
+ ],
268
+ "source": [
269
+ "# ask it - this uses GPT 4.1 mini, still cheap but more powerful than nano\n",
270
+ "\n",
271
+ "response = openai.chat.completions.create(\n",
272
+ " model=\"gpt-4.1-mini\",\n",
273
+ " messages=messages\n",
274
+ ")\n",
275
+ "\n",
276
+ "question = response.choices[0].message.content\n",
277
+ "\n",
278
+ "print(question)\n"
279
+ ]
280
+ },
281
+ {
282
+ "cell_type": "code",
283
+ "execution_count": 10,
284
+ "metadata": {},
285
+ "outputs": [],
286
+ "source": [
287
+ "# form a new messages list\n",
288
+ "messages = [{\"role\": \"user\", \"content\": question}]\n"
289
+ ]
290
+ },
291
+ {
292
+ "cell_type": "code",
293
+ "execution_count": 11,
294
+ "metadata": {},
295
+ "outputs": [
296
+ {
297
+ "name": "stdout",
298
+ "output_type": "stream",
299
+ "text": [
300
+ "Let's denote the cost of the ball as \\( x \\) dollars.\n",
301
+ "\n",
302
+ "According to the problem:\n",
303
+ "- The bat costs \\( x + 1.00 \\) dollars.\n",
304
+ "- The total cost is \\( 1.10 \\) dollars.\n",
305
+ "\n",
306
+ "So, we can write the equation:\n",
307
+ "\\[\n",
308
+ "x + (x + 1.00) = 1.10\n",
309
+ "\\]\n",
310
+ "\n",
311
+ "Combine like terms:\n",
312
+ "\\[\n",
313
+ "2x + 1.00 = 1.10\n",
314
+ "\\]\n",
315
+ "\n",
316
+ "Subtract 1.00 from both sides:\n",
317
+ "\\[\n",
318
+ "2x = 0.10\n",
319
+ "\\]\n",
320
+ "\n",
321
+ "Divide both sides by 2:\n",
322
+ "\\[\n",
323
+ "x = 0.05\n",
324
+ "\\]\n",
325
+ "\n",
326
+ "**Answer:** The ball costs **5 cents** (\\$0.05).\n"
327
+ ]
328
+ }
329
+ ],
330
+ "source": [
331
+ "# Ask it again\n",
332
+ "\n",
333
+ "response = openai.chat.completions.create(\n",
334
+ " model=\"gpt-4.1-mini\",\n",
335
+ " messages=messages\n",
336
+ ")\n",
337
+ "\n",
338
+ "answer = response.choices[0].message.content\n",
339
+ "print(answer)\n"
340
+ ]
341
+ },
342
+ {
343
+ "cell_type": "code",
344
+ "execution_count": 12,
345
+ "metadata": {},
346
+ "outputs": [
347
+ {
348
+ "data": {
349
+ "text/markdown": [
350
+ "Let's denote the cost of the ball as \\( x \\) dollars.\n",
351
+ "\n",
352
+ "According to the problem:\n",
353
+ "- The bat costs \\( x + 1.00 \\) dollars.\n",
354
+ "- The total cost is \\( 1.10 \\) dollars.\n",
355
+ "\n",
356
+ "So, we can write the equation:\n",
357
+ "\\[\n",
358
+ "x + (x + 1.00) = 1.10\n",
359
+ "\\]\n",
360
+ "\n",
361
+ "Combine like terms:\n",
362
+ "\\[\n",
363
+ "2x + 1.00 = 1.10\n",
364
+ "\\]\n",
365
+ "\n",
366
+ "Subtract 1.00 from both sides:\n",
367
+ "\\[\n",
368
+ "2x = 0.10\n",
369
+ "\\]\n",
370
+ "\n",
371
+ "Divide both sides by 2:\n",
372
+ "\\[\n",
373
+ "x = 0.05\n",
374
+ "\\]\n",
375
+ "\n",
376
+ "**Answer:** The ball costs **5 cents** (\\$0.05)."
377
+ ],
378
+ "text/plain": [
379
+ "<IPython.core.display.Markdown object>"
380
+ ]
381
+ },
382
+ "metadata": {},
383
+ "output_type": "display_data"
384
+ }
385
+ ],
386
+ "source": [
387
+ "from IPython.display import Markdown, display\n",
388
+ "\n",
389
+ "display(Markdown(answer))\n",
390
+ "\n"
391
+ ]
392
+ },
393
+ {
394
+ "cell_type": "markdown",
395
+ "metadata": {},
396
+ "source": [
397
+ "# Congratulations!\n",
398
+ "\n",
399
+ "That was a small, simple step in the direction of Agentic AI, with your new environment!\n",
400
+ "\n",
401
+ "Next time things get more interesting..."
402
+ ]
403
+ },
404
+ {
405
+ "cell_type": "markdown",
406
+ "metadata": {},
407
+ "source": [
408
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
409
+ " <tr>\n",
410
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
411
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
412
+ " </td>\n",
413
+ " <td>\n",
414
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
415
+ " <span style=\"color:#ff7800;\">Now try this commercial application:<br/>\n",
416
+ " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.<br/>\n",
417
+ " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.<br/>\n",
418
+ " Finally have 3 third LLM call propose the Agentic AI solution. <br/>\n",
419
+ " We will cover this at up-coming labs, so don't worry if you're unsure.. just give it a try!\n",
420
+ " </span>\n",
421
+ " </td>\n",
422
+ " </tr>\n",
423
+ "</table>"
424
+ ]
425
+ },
426
+ {
427
+ "cell_type": "code",
428
+ "execution_count": 13,
429
+ "metadata": {},
430
+ "outputs": [
431
+ {
432
+ "name": "stdout",
433
+ "output_type": "stream",
434
+ "text": [
435
+ "One promising business idea for an Agentic AI opportunity is **an Automated Small Business Growth Strategist and Executioner**.\n",
436
+ "\n",
437
+ "### Concept:\n",
438
+ "Develop an AI agent that not only advises small businesses on growth strategies but also autonomously executes key tasks across marketing, sales, customer engagement, and operations without requiring constant human intervention.\n",
439
+ "\n",
440
+ "### Why this is worth exploring:\n",
441
+ "- **Market demand:** Small and medium-sized businesses (SMBs) often lack the budget to hire full-time strategists or multiple specialists.\n",
442
+ "- **Agentic AI fit:** The AI can independently analyze business data, create tailored growth plans, run marketing campaigns, optimize pricing, handle customer inquiries, and adjust tactics in real time.\n",
443
+ "- **Scalability:** Once trained, the AI can serve many clients simultaneously.\n",
444
+ "- **Value proposition:** Helps SMBs accelerate growth, reduce overhead, and compete with larger companies.\n",
445
+ "\n",
446
+ "### Key features/functionalities:\n",
447
+ "- Data ingestion from sales, website analytics, customer feedback\n",
448
+ "- Market research and competitor analysis\n",
449
+ "- Automated ad creation and campaign management\n",
450
+ "- Dynamic pricing and inventory suggestions\n",
451
+ "- Personalized email and SMS outreach\n",
452
+ "- Chatbot-based customer support and lead qualification\n",
453
+ "- Performance tracking and incremental strategy refinement\n",
454
+ "\n",
455
+ "### Challenges to address:\n",
456
+ "- Ensuring the AI’s actions align with each business's unique brand and ethics\n",
457
+ "- Balancing autonomy with user controls and transparency\n",
458
+ "- Integrating with various platforms and tools SMBs use\n",
459
+ "\n",
460
+ "Building such an Agentic AI could transform how SMBs operate by providing accessible, actionable, and continuously optimized growth support.\n"
461
+ ]
462
+ }
463
+ ],
464
+ "source": [
465
+ "# First create the messages:\n",
466
+ "\n",
467
+ "messages_gpt = [{\"role\": \"user\", \"content\": \"pick a business idea that might be worth exploring for an Agentic AI opportunity\"}]\n",
468
+ "\n",
469
+ "# Then make the first call:\n",
470
+ "\n",
471
+ "response = openai.chat.completions.create(\n",
472
+ " model=\"gpt-4.1-mini\",\n",
473
+ " messages=messages_gpt\n",
474
+ ")\n",
475
+ "\n",
476
+ "# Then read the business idea:\n",
477
+ "\n",
478
+ "business_idea_gpt = response.choices[0].message.content\n",
479
+ "\n",
480
+ "print(business_idea_gpt)\n",
481
+ "\n",
482
+ "\n"
483
+ ]
484
+ },
485
+ {
486
+ "cell_type": "code",
487
+ "execution_count": 14,
488
+ "metadata": {},
489
+ "outputs": [
490
+ {
491
+ "name": "stdout",
492
+ "output_type": "stream",
493
+ "text": [
494
+ "A significant pain point for the **Automated Small Business Growth Strategist and Executioner** lies in **building and maintaining the trust of small business owners in an AI system that autonomously executes critical growth tasks**. \n",
495
+ "\n",
496
+ "Small business owners often have deep emotional investments and unique visions for their businesses, and handing over substantial control—such as marketing spend, pricing, or customer interactions—to an AI may create anxiety and resistance. They may fear loss of control, potential misalignment with their brand voice or values, or unintended consequences from automated decisions. Overcoming this trust barrier requires transparent AI decision-making, easy-to-understand controls, and reliable safeguards to ensure the AI’s actions feel safe, predictable, and aligned with business goals. Failure to address this pain point could lead to reluctance in adopting the solution, regardless of its features and potential benefits.\n"
497
+ ]
498
+ }
499
+ ],
500
+ "source": [
501
+ "# And repeat! In the next message, include the business idea within the message\n",
502
+ "\n",
503
+ "messages_gpt.append({\"role\": \"assistant\", \"content\": business_idea_gpt})\n",
504
+ "\n",
505
+ "messages_gpt = [{\"role\": \"user\", \"content\": \"Present a pain point for the business idea: \" + business_idea_gpt}]\n",
506
+ "\n",
507
+ "response = openai.chat.completions.create(\n",
508
+ " model=\"gpt-4.1-mini\",\n",
509
+ " messages=messages_gpt\n",
510
+ ")\n",
511
+ "\n",
512
+ "pain_point_gpt = response.choices[0].message.content\n",
513
+ "\n",
514
+ "print(pain_point_gpt)\n",
515
+ "\n",
516
+ "\n",
517
+ "\n"
518
+ ]
519
+ },
520
+ {
521
+ "cell_type": "markdown",
522
+ "metadata": {},
523
+ "source": []
524
+ },
525
+ {
526
+ "cell_type": "code",
527
+ "execution_count": 15,
528
+ "metadata": {},
529
+ "outputs": [
530
+ {
531
+ "name": "stdout",
532
+ "output_type": "stream",
533
+ "text": [
534
+ "Certainly! Here is a detailed **Agentic AI solution** designed to address the trust challenges faced by small business owners when entrusting an AI to autonomously execute critical growth tasks:\n",
535
+ "\n",
536
+ "---\n",
537
+ "\n",
538
+ "### Agentic AI Solution: TrustBuilder AI for Small Business Growth\n",
539
+ "\n",
540
+ "**Overview:** \n",
541
+ "TrustBuilder AI is an autonomous growth strategist and executor specifically designed with transparency, control, and alignment features that directly address the trust concerns of small business owners. It acts as a collaborative partner rather than an opaque tool, ensuring business owners feel ownership, safety, and confidence in every action the AI takes.\n",
542
+ "\n",
543
+ "---\n",
544
+ "\n",
545
+ "### Key Features & Architectural Approaches\n",
546
+ "\n",
547
+ "#### 1. **Explainable & Transparent Decision-Making**\n",
548
+ "- **Real-Time Natural Language Rationales:** \n",
549
+ " For each growth action or recommendation (e.g., adjusting marketing spend, changing pricing, launching a campaign), TrustBuilder AI generates a clear, concise explanation in plain language. Example: \n",
550
+ " *“I recommend increasing Facebook ad spend by 15% this month because competitor analysis shows a 20% higher conversion rate there, aligning with your business goal to boost local customer engagement.”*\n",
551
+ "\n",
552
+ "- **Decision Path Visualization Dashboard:** \n",
553
+ " A visual, interactive flowchart showing how inputs (market data, previous campaign results, customer feedback) led to each decision. This transparency reduces anxiety of “black-box” decisions.\n",
554
+ "\n",
555
+ "---\n",
556
+ "\n",
557
+ "#### 2. **Configurable Control Layers (“Adjustable Autonomy”)**\n",
558
+ "- **Modular Autonomy Settings:** \n",
559
+ " Owners can customize autonomy levels per task:\n",
560
+ " - *Full Automation* for routine executions (e.g., scheduling social posts, reporting) \n",
561
+ " - *Human-in-the-Loop* prompts for critical changes (e.g., pricing adjustments) before execution \n",
562
+ " - *Recommendation Mode* with zero direct execution—only suggestions for owner approval\n",
563
+ "\n",
564
+ "- **“Trusted Bounds” Constraints:** \n",
565
+ " Owners set explicit boundaries such as maximum ad budget changes, pricing floors/ceilings, tone/style guidelines for communication, and risk tolerance levels. The AI raises alerts if proposed changes approach these limits.\n",
566
+ "\n",
567
+ "---\n",
568
+ "\n",
569
+ "#### 3. **Value & Brand Alignment Assurance**\n",
570
+ "- **Values Embedding Module:** \n",
571
+ " Upon setup, the owner inputs key business values, brand voice characteristics, and unique selling propositions. TrustBuilder AI incorporates these into a brand profile that guides all automated decisions, ensuring alignment in actions and messaging.\n",
572
+ "\n",
573
+ "- **Periodic Alignment Checks:** \n",
574
+ " The AI periodically reviews accumulated outputs and strategy to validate consistency with the brand values. Any deviation triggers a “flag for review” notification.\n",
575
+ "\n",
576
+ "---\n",
577
+ "\n",
578
+ "#### 4. **Robust Safeguards and Fail-Safes**\n",
579
+ "- **Simulation Mode:** \n",
580
+ " Before executing any major change autonomously, the AI runs a “what-if” simulation showing projected impacts and potential risks, enabling the owner to approve or tweak the plan.\n",
581
+ "\n",
582
+ "- **Rollback Capability:** \n",
583
+ " Any autonomous action can be reversed seamlessly within a defined time window, minimizing fear of irreversible consequences.\n",
584
+ "\n",
585
+ "- **Continuous Monitoring & Anomaly Detection:** \n",
586
+ " The system monitors live data post-implementation to detect unexpected negative trends (e.g., sudden drop in customer engagement), triggering automated pause or owner alerts.\n",
587
+ "\n",
588
+ "---\n",
589
+ "\n",
590
+ "#### 5. **Engagement & Education Features**\n",
591
+ "- **Guided Onboarding & Interactive Tutorials:** \n",
592
+ " Personalized onboarding that educates owners on how the AI works, benefits, controls, and how to interpret recommendations.\n",
593
+ "\n",
594
+ "- **Progress & Impact Reports:** \n",
595
+ " Regular, accessible reports communicated in non-technical language outlining what the AI has done, why, and with what results—helping build ongoing trust through demonstrated value.\n",
596
+ "\n",
597
+ "---\n",
598
+ "\n",
599
+ "### Example User Journey\n",
600
+ "\n",
601
+ "1. **Initial Setup:** The business owner is guided through defining business goals, values, and acceptable autonomy boundaries.\n",
602
+ "2. **Ongoing Execution:** The AI autonomously manages marketing spend within trusted bounds, explaining its moves and requesting approval for pricing changes.\n",
603
+ "3. **Monthly Review:** Owner receives a report with transparent rationales, impact metrics, and can adjust autonomy levels or constraints anytime.\n",
604
+ "4. **Adaptation:** Over time, the AI learns the owner’s preferences and tightens brand alignment, further reducing anxiety and increasing trust.\n",
605
+ "\n",
606
+ "---\n",
607
+ "\n",
608
+ "### Summary \n",
609
+ "**TrustBuilder AI** empowers small business owners by combining autonomous execution with transparency, configurable control, brand alignment, and fail-safe mechanisms. This agentic AI solution transforms anxiety and resistance into collaboration and confidence, accelerating adoption and maximizing sustained business growth outcomes.\n",
610
+ "\n",
611
+ "---\n",
612
+ "\n",
613
+ "If you want, I can also provide a technical architecture diagram or a prototype feature list for implementation. Would that be helpful?\n"
614
+ ]
615
+ }
616
+ ],
617
+ "source": [
618
+ "messages_gpt.append({\"role\": \"assistant\", \"content\": pain_point_gpt})\n",
619
+ "\n",
620
+ "messages_gpt = [{\"role\": \"user\", \"content\": \"Present an Agentic AI solution for the pain point: \" + pain_point_gpt}]\n",
621
+ "\n",
622
+ "response = openai.chat.completions.create(\n",
623
+ " model=\"gpt-4.1-mini\",\n",
624
+ " messages=messages_gpt\n",
625
+ ")\n",
626
+ "\n",
627
+ "solution_gpt = response.choices[0].message.content\n",
628
+ "\n",
629
+ "print(solution_gpt)"
630
+ ]
631
+ },
632
+ {
633
+ "cell_type": "code",
634
+ "execution_count": 16,
635
+ "metadata": {},
636
+ "outputs": [
637
+ {
638
+ "name": "stdout",
639
+ "output_type": "stream",
640
+ "text": [
641
+ "One promising business idea for an Agentic AI opportunity is **an Automated Small Business Growth Strategist and Executioner**.\n",
642
+ "\n",
643
+ "### Concept:\n",
644
+ "Develop an AI agent that not only advises small businesses on growth strategies but also autonomously executes key tasks across marketing, sales, customer engagement, and operations without requiring constant human intervention.\n",
645
+ "\n",
646
+ "### Why this is worth exploring:\n",
647
+ "- **Market demand:** Small and medium-sized businesses (SMBs) often lack the budget to hire full-time strategists or multiple specialists.\n",
648
+ "- **Agentic AI fit:** The AI can independently analyze business data, create tailored growth plans, run marketing campaigns, optimize pricing, handle customer inquiries, and adjust tactics in real time.\n",
649
+ "- **Scalability:** Once trained, the AI can serve many clients simultaneously.\n",
650
+ "- **Value proposition:** Helps SMBs accelerate growth, reduce overhead, and compete with larger companies.\n",
651
+ "\n",
652
+ "### Key features/functionalities:\n",
653
+ "- Data ingestion from sales, website analytics, customer feedback\n",
654
+ "- Market research and competitor analysis\n",
655
+ "- Automated ad creation and campaign management\n",
656
+ "- Dynamic pricing and inventory suggestions\n",
657
+ "- Personalized email and SMS outreach\n",
658
+ "- Chatbot-based customer support and lead qualification\n",
659
+ "- Performance tracking and incremental strategy refinement\n",
660
+ "\n",
661
+ "### Challenges to address:\n",
662
+ "- Ensuring the AI’s actions align with each business's unique brand and ethics\n",
663
+ "- Balancing autonomy with user controls and transparency\n",
664
+ "- Integrating with various platforms and tools SMBs use\n",
665
+ "\n",
666
+ "Building such an Agentic AI could transform how SMBs operate by providing accessible, actionable, and continuously optimized growth support.\n",
667
+ "A significant pain point for the **Automated Small Business Growth Strategist and Executioner** lies in **building and maintaining the trust of small business owners in an AI system that autonomously executes critical growth tasks**. \n",
668
+ "\n",
669
+ "Small business owners often have deep emotional investments and unique visions for their businesses, and handing over substantial control—such as marketing spend, pricing, or customer interactions—to an AI may create anxiety and resistance. They may fear loss of control, potential misalignment with their brand voice or values, or unintended consequences from automated decisions. Overcoming this trust barrier requires transparent AI decision-making, easy-to-understand controls, and reliable safeguards to ensure the AI’s actions feel safe, predictable, and aligned with business goals. Failure to address this pain point could lead to reluctance in adopting the solution, regardless of its features and potential benefits.\n",
670
+ "Certainly! Here is a detailed **Agentic AI solution** designed to address the trust challenges faced by small business owners when entrusting an AI to autonomously execute critical growth tasks:\n",
671
+ "\n",
672
+ "---\n",
673
+ "\n",
674
+ "### Agentic AI Solution: TrustBuilder AI for Small Business Growth\n",
675
+ "\n",
676
+ "**Overview:** \n",
677
+ "TrustBuilder AI is an autonomous growth strategist and executor specifically designed with transparency, control, and alignment features that directly address the trust concerns of small business owners. It acts as a collaborative partner rather than an opaque tool, ensuring business owners feel ownership, safety, and confidence in every action the AI takes.\n",
678
+ "\n",
679
+ "---\n",
680
+ "\n",
681
+ "### Key Features & Architectural Approaches\n",
682
+ "\n",
683
+ "#### 1. **Explainable & Transparent Decision-Making**\n",
684
+ "- **Real-Time Natural Language Rationales:** \n",
685
+ " For each growth action or recommendation (e.g., adjusting marketing spend, changing pricing, launching a campaign), TrustBuilder AI generates a clear, concise explanation in plain language. Example: \n",
686
+ " *“I recommend increasing Facebook ad spend by 15% this month because competitor analysis shows a 20% higher conversion rate there, aligning with your business goal to boost local customer engagement.”*\n",
687
+ "\n",
688
+ "- **Decision Path Visualization Dashboard:** \n",
689
+ " A visual, interactive flowchart showing how inputs (market data, previous campaign results, customer feedback) led to each decision. This transparency reduces anxiety of “black-box” decisions.\n",
690
+ "\n",
691
+ "---\n",
692
+ "\n",
693
+ "#### 2. **Configurable Control Layers (“Adjustable Autonomy”)**\n",
694
+ "- **Modular Autonomy Settings:** \n",
695
+ " Owners can customize autonomy levels per task:\n",
696
+ " - *Full Automation* for routine executions (e.g., scheduling social posts, reporting) \n",
697
+ " - *Human-in-the-Loop* prompts for critical changes (e.g., pricing adjustments) before execution \n",
698
+ " - *Recommendation Mode* with zero direct execution—only suggestions for owner approval\n",
699
+ "\n",
700
+ "- **“Trusted Bounds” Constraints:** \n",
701
+ " Owners set explicit boundaries such as maximum ad budget changes, pricing floors/ceilings, tone/style guidelines for communication, and risk tolerance levels. The AI raises alerts if proposed changes approach these limits.\n",
702
+ "\n",
703
+ "---\n",
704
+ "\n",
705
+ "#### 3. **Value & Brand Alignment Assurance**\n",
706
+ "- **Values Embedding Module:** \n",
707
+ " Upon setup, the owner inputs key business values, brand voice characteristics, and unique selling propositions. TrustBuilder AI incorporates these into a brand profile that guides all automated decisions, ensuring alignment in actions and messaging.\n",
708
+ "\n",
709
+ "- **Periodic Alignment Checks:** \n",
710
+ " The AI periodically reviews accumulated outputs and strategy to validate consistency with the brand values. Any deviation triggers a “flag for review” notification.\n",
711
+ "\n",
712
+ "---\n",
713
+ "\n",
714
+ "#### 4. **Robust Safeguards and Fail-Safes**\n",
715
+ "- **Simulation Mode:** \n",
716
+ " Before executing any major change autonomously, the AI runs a “what-if” simulation showing projected impacts and potential risks, enabling the owner to approve or tweak the plan.\n",
717
+ "\n",
718
+ "- **Rollback Capability:** \n",
719
+ " Any autonomous action can be reversed seamlessly within a defined time window, minimizing fear of irreversible consequences.\n",
720
+ "\n",
721
+ "- **Continuous Monitoring & Anomaly Detection:** \n",
722
+ " The system monitors live data post-implementation to detect unexpected negative trends (e.g., sudden drop in customer engagement), triggering automated pause or owner alerts.\n",
723
+ "\n",
724
+ "---\n",
725
+ "\n",
726
+ "#### 5. **Engagement & Education Features**\n",
727
+ "- **Guided Onboarding & Interactive Tutorials:** \n",
728
+ " Personalized onboarding that educates owners on how the AI works, benefits, controls, and how to interpret recommendations.\n",
729
+ "\n",
730
+ "- **Progress & Impact Reports:** \n",
731
+ " Regular, accessible reports communicated in non-technical language outlining what the AI has done, why, and with what results—helping build ongoing trust through demonstrated value.\n",
732
+ "\n",
733
+ "---\n",
734
+ "\n",
735
+ "### Example User Journey\n",
736
+ "\n",
737
+ "1. **Initial Setup:** The business owner is guided through defining business goals, values, and acceptable autonomy boundaries.\n",
738
+ "2. **Ongoing Execution:** The AI autonomously manages marketing spend within trusted bounds, explaining its moves and requesting approval for pricing changes.\n",
739
+ "3. **Monthly Review:** Owner receives a report with transparent rationales, impact metrics, and can adjust autonomy levels or constraints anytime.\n",
740
+ "4. **Adaptation:** Over time, the AI learns the owner’s preferences and tightens brand alignment, further reducing anxiety and increasing trust.\n",
741
+ "\n",
742
+ "---\n",
743
+ "\n",
744
+ "### Summary \n",
745
+ "**TrustBuilder AI** empowers small business owners by combining autonomous execution with transparency, configurable control, brand alignment, and fail-safe mechanisms. This agentic AI solution transforms anxiety and resistance into collaboration and confidence, accelerating adoption and maximizing sustained business growth outcomes.\n",
746
+ "\n",
747
+ "---\n",
748
+ "\n",
749
+ "If you want, I can also provide a technical architecture diagram or a prototype feature list for implementation. Would that be helpful?\n"
750
+ ]
751
+ }
752
+ ],
753
+ "source": [
754
+ "print(business_idea_gpt)\n",
755
+ "print(pain_point_gpt)\n",
756
+ "print(solution_gpt)"
757
+ ]
758
+ },
759
+ {
760
+ "cell_type": "code",
761
+ "execution_count": 17,
762
+ "metadata": {},
763
+ "outputs": [],
764
+ "source": [
765
+ "from anthropic import Anthropic\n",
766
+ "from dotenv import load_dotenv\n",
767
+ "\n",
768
+ "load_dotenv()\n",
769
+ "\n",
770
+ "anthropic = Anthropic(api_key=os.getenv(\"ANTHROPIC_API_KEY\"))\n",
771
+ "\n",
772
+ "\n",
773
+ "\n"
774
+ ]
775
+ },
776
+ {
777
+ "cell_type": "code",
778
+ "execution_count": 18,
779
+ "metadata": {},
780
+ "outputs": [
781
+ {
782
+ "name": "stdout",
783
+ "output_type": "stream",
784
+ "text": [
785
+ "Here's a business idea worth exploring in the Agentic AI space:\n",
786
+ "\n",
787
+ "AI-Powered Personal Research Assistant\n",
788
+ "\n",
789
+ "Core Concept:\n",
790
+ "An AI agent that helps professionals, academics, and knowledge workers conduct comprehensive research by:\n",
791
+ "\n",
792
+ "1. Understanding complex research queries\n",
793
+ "2. Gathering information from multiple sources\n",
794
+ "3. Synthesizing findings\n",
795
+ "4. Identifying patterns and connections\n",
796
+ "5. Generating actionable insights and summaries\n",
797
+ "\n",
798
+ "Key Features:\n",
799
+ "- Autonomous information gathering across academic databases, news sources, and public documents\n",
800
+ "- Source verification and credibility assessment\n",
801
+ "- Custom knowledge domain adaptation\n",
802
+ "- Citation management and formatting\n",
803
+ "- Interactive follow-up questions and clarifications\n",
804
+ "- Integration with popular research and writing tools\n",
805
+ "\n",
806
+ "Target Market:\n",
807
+ "- Academic researchers\n",
808
+ "- Business analysts\n",
809
+ "- Journalists\n",
810
+ "- Legal professionals\n",
811
+ "- Market researchers\n",
812
+ "- Policy makers\n",
813
+ "\n",
814
+ "Value Proposition:\n",
815
+ "- Significant time savings in research processes\n",
816
+ "- More comprehensive coverage of available information\n",
817
+ "- Reduced risk of missing important sources or connections\n",
818
+ "- Better organized and structured research outputs\n",
819
+ "- Scalable knowledge gathering and synthesis\n",
820
+ "\n",
821
+ "This idea leverages the emerging capabilities of agentic AI while addressing a clear market need for more efficient and thorough research processes.\n",
822
+ "\n",
823
+ "Would you like me to elaborate on any aspect of this business idea?\n"
824
+ ]
825
+ }
826
+ ],
827
+ "source": [
828
+ "messages_anthropic = [{\"role\": \"user\", \"content\": \"pick a business idea that might be worth exploring for an Agentic AI opportunity\"}]\n",
829
+ "\n",
830
+ "response = anthropic.messages.create(\n",
831
+ " model=\"claude-3-5-sonnet-latest\",\n",
832
+ " max_tokens=1000,\n",
833
+ " messages=messages_anthropic)\n",
834
+ "\n",
835
+ "business_idea_anthropic = response.content[0].text\n",
836
+ "\n",
837
+ "\n",
838
+ "messages_anthropic.append({\"role\": \"assistant\", \"content\": business_idea_anthropic})\n",
839
+ "\n",
840
+ "print(business_idea_anthropic)\n",
841
+ "\n",
842
+ "\n",
843
+ "\n"
844
+ ]
845
+ },
846
+ {
847
+ "cell_type": "code",
848
+ "execution_count": 19,
849
+ "metadata": {},
850
+ "outputs": [
851
+ {
852
+ "name": "stdout",
853
+ "output_type": "stream",
854
+ "text": [
855
+ "Here's a significant pain point for this AI-Powered Personal Research Assistant business idea:\n",
856
+ "\n",
857
+ "Data Access and Licensing Challenges\n",
858
+ "\n",
859
+ "The ability to access and legally use content from premium academic databases, professional journals, and paywalled sources presents a major hurdle. Many valuable research sources:\n",
860
+ "\n",
861
+ "- Require expensive institutional subscriptions\n",
862
+ "- Have strict licensing terms that may prohibit AI-powered scraping\n",
863
+ "- Maintain complex API access requirements\n",
864
+ "- Charge significant fees for programmatic access\n",
865
+ "- Have varying terms of use across different regions\n",
866
+ "\n",
867
+ "This creates several problems:\n",
868
+ "1. High operating costs to maintain necessary database subscriptions\n",
869
+ "2. Legal complexity in ensuring compliance with multiple licensing agreements\n",
870
+ "3. Potential gaps in research coverage due to inaccessible sources\n",
871
+ "4. Need for complex negotiations with multiple content providers\n",
872
+ "5. Risk of inadvertently violating terms of service\n",
873
+ "\n",
874
+ "This pain point could significantly impact both the service's comprehensiveness and its pricing model, potentially limiting its value proposition for users who need access to specialized or premium content sources.\n"
875
+ ]
876
+ }
877
+ ],
878
+ "source": [
879
+ "messages_anthropic = [{\"role\": \"user\", \"content\": \"Present a pain point for the business idea: \" + business_idea_anthropic}]\n",
880
+ "\n",
881
+ "response = anthropic.messages.create(\n",
882
+ " model=\"claude-3-5-sonnet-latest\",\n",
883
+ " max_tokens=1000,\n",
884
+ " messages=messages_anthropic)\n",
885
+ "\n",
886
+ "pain_point_anthropic = response.content[0].text\n",
887
+ "\n",
888
+ "messages_anthropic.append({\"role\": \"assistant\", \"content\": pain_point_anthropic})\n",
889
+ "\n",
890
+ "print(pain_point_anthropic)\n",
891
+ "\n",
892
+ "\n",
893
+ "\n",
894
+ "\n",
895
+ "\n"
896
+ ]
897
+ },
898
+ {
899
+ "cell_type": "code",
900
+ "execution_count": 20,
901
+ "metadata": {},
902
+ "outputs": [
903
+ {
904
+ "name": "stdout",
905
+ "output_type": "stream",
906
+ "text": [
907
+ "I'll provide a solution by acting as an AI Strategic Solutions Architect. Let me break this down into a comprehensive, actionable solution.\n",
908
+ "\n",
909
+ "PROPOSED SOLUTION: Tiered Access Partnership Network (TAPN)\n",
910
+ "\n",
911
+ "1. CORE INFRASTRUCTURE\n",
912
+ "- Develop a modular API integration framework that can flexibly connect to different content providers\n",
913
+ "- Create a content source management system that tracks licensing terms and usage rights\n",
914
+ "- Implement real-time compliance monitoring tools\n",
915
+ "\n",
916
+ "2. PARTNERSHIP STRATEGY\n",
917
+ "- Establish strategic partnerships with major academic institutions for shared access rights\n",
918
+ "- Create a consortium model where multiple research organizations pool resources\n",
919
+ "- Negotiate bulk licensing deals with content providers offering volume discounts\n",
920
+ "\n",
921
+ "3. OPERATIONAL MODEL\n",
922
+ "\n",
923
+ "Tier 1: Open Access\n",
924
+ "- Utilize freely available academic repositories (arXiv, PubMed Central)\n",
925
+ "- Partner with open science initiatives\n",
926
+ "- Integrate public domain research databases\n",
927
+ "\n",
928
+ "Tier 2: Institution-Linked Access\n",
929
+ "- Allow users to link their institutional credentials\n",
930
+ "- Create passport system for verified academic users\n",
931
+ "- Implement institutional SSO (Single Sign-On) integration\n",
932
+ "\n",
933
+ "Tier 3: Premium Partnership Access\n",
934
+ "- Direct licensing agreements with publishers\n",
935
+ "- Custom API access arrangements\n",
936
+ "- Specialized content packages\n",
937
+ "\n",
938
+ "4. TECHNICAL IMPLEMENTATION\n",
939
+ "\n",
940
+ "Content Access Layer:\n",
941
+ "```python\n",
942
+ "class ContentAccessManager:\n",
943
+ " def __init__(self):\n",
944
+ " self.access_levels = {\n",
945
+ " 'open': OpenAccessHandler(),\n",
946
+ " 'institutional': InstitutionalAccessHandler(),\n",
947
+ " 'premium': PremiumAccessHandler()\n",
948
+ " }\n",
949
+ " \n",
950
+ " def retrieve_content(self, source, user_credentials):\n",
951
+ " access_level = self.determine_access_level(user_credentials)\n",
952
+ " handler = self.access_levels[access_level]\n",
953
+ " return handler.fetch_content(source)\n",
954
+ "```\n",
955
+ "\n",
956
+ "5. RISK MITIGATION\n",
957
+ "- Implement real-time usage tracking\n",
958
+ "- Develop automated compliance checking\n",
959
+ "- Create audit trails for content access\n",
960
+ "- Regular license term reviews\n",
961
+ "\n",
962
+ "6. REVENUE MODEL ALIGNMENT\n",
963
+ "- Usage-based pricing for premium content\n",
964
+ "- Institution-based subscription models\n",
965
+ "- Pay-per-access options for specialized content\n",
966
+ "\n",
967
+ "7. SCALABILITY APPROACH\n",
968
+ "- Start with core open access sources\n",
969
+ "- Gradually expand partnership network\n",
970
+ "- Add premium sources based on user demand\n",
971
+ "\n",
972
+ "8. MARKET POSITIONING\n",
973
+ "\"Research Without Boundaries - Compliant Access to Global Knowledge\"\n",
974
+ "\n",
975
+ "EXECUTION TIMELINE:\n",
976
+ "\n",
977
+ "Phase 1 (Months 1-3):\n",
978
+ "- Implement open access integration\n",
979
+ "- Develop basic compliance framework\n",
980
+ "- Launch institutional partnership program\n",
981
+ "\n",
982
+ "Phase 2 (Months 4-6):\n",
983
+ "- Roll out premium partnerships\n",
984
+ "- Expand content provider network\n",
985
+ "- Enhance compliance monitoring\n",
986
+ "\n",
987
+ "Phase 3 (Months 7-12):\n",
988
+ "- Scale partnership network\n",
989
+ "- Optimize access protocols\n",
990
+ "- Refine pricing models\n",
991
+ "\n",
992
+ "SUCCESS METRICS:\n",
993
+ "1. Content coverage percentage\n",
994
+ "2. Licensing compliance rate\n",
995
+ "3. User access satisfaction\n",
996
+ "4. Partnership network growth\n",
997
+ "5. Cost per accessed document\n",
998
+ "\n",
999
+ "CONTINUOUS IMPROVEMENT:\n",
1000
+ "- Regular partnership reviews\n",
1001
+ "- Compliance protocol updates\n",
1002
+ "- User feedback integration\n",
1003
+ "- Technology stack optimization\n",
1004
+ "\n",
1005
+ "This solution transforms the challenge into a structured opportunity while ensuring:\n",
1006
+ "- Legal compliance\n",
1007
+ "- Scalable access\n",
1008
+ "- Cost-effective operation\n",
1009
+ "- User satisfaction\n",
1010
+ "- Long-term sustainability\n",
1011
+ "\n",
1012
+ "Would you like me to elaborate on any specific aspect of this solution?\n"
1013
+ ]
1014
+ }
1015
+ ],
1016
+ "source": [
1017
+ "messages_anthropic = [{\"role\": \"user\", \"content\": \"Present an agentic Ai solution for the following pain point: \" + pain_point_anthropic}]\n",
1018
+ "\n",
1019
+ "response = anthropic.messages.create(\n",
1020
+ " model=\"claude-3-5-sonnet-latest\",\n",
1021
+ " max_tokens=1000,\n",
1022
+ " messages=messages_anthropic\n",
1023
+ ")\n",
1024
+ "\n",
1025
+ "solution_anthropic = response.content[0].text\n",
1026
+ "\n",
1027
+ "messages_anthropic.append({\"role\": \"assistant\", \"content\": solution_anthropic})\n",
1028
+ "\n",
1029
+ "print(solution_anthropic)\n",
1030
+ "\n",
1031
+ "\n",
1032
+ "\n",
1033
+ "\n"
1034
+ ]
1035
+ },
1036
+ {
1037
+ "cell_type": "code",
1038
+ "execution_count": 21,
1039
+ "metadata": {},
1040
+ "outputs": [
1041
+ {
1042
+ "name": "stdout",
1043
+ "output_type": "stream",
1044
+ "text": [
1045
+ "Here's an agentic AI solution to address the trust-building challenge for the Automated Small Business Growth Strategist and Executioner:\n",
1046
+ "\n",
1047
+ "**Solution: The \"Trust Bridge\" AI Companion**\n",
1048
+ "\n",
1049
+ "**Core Function:**\n",
1050
+ "An AI system that builds trust through progressive autonomy and collaborative learning, functioning as both an executor and educator.\n",
1051
+ "\n",
1052
+ "**Key Components:**\n",
1053
+ "\n",
1054
+ "1. **Transparent Decision Dashboard**\n",
1055
+ "- Real-time visualization of AI decision-making processes\n",
1056
+ "- Clear cause-and-effect explanations for each action\n",
1057
+ "- Preview of potential outcomes before execution\n",
1058
+ "- Historical performance tracking\n",
1059
+ "\n",
1060
+ "2. **Progressive Autonomy System**\n",
1061
+ "```python\n",
1062
+ "autonomy_levels = {\n",
1063
+ " 'Level 1: Shadow Mode': 'AI observes and suggests only',\n",
1064
+ " 'Level 2: Supervised Execution': 'AI acts with approval',\n",
1065
+ " 'Level 3: Bounded Autonomy': 'AI acts within set parameters',\n",
1066
+ " 'Level 4: Full Autonomy': 'AI operates independently'\n",
1067
+ "}\n",
1068
+ "```\n",
1069
+ "\n",
1070
+ "3. **Value Alignment Protocol**\n",
1071
+ "- Initial business values and goals assessment\n",
1072
+ "- Regular alignment checks\n",
1073
+ "- Automated brand voice calibration\n",
1074
+ "- Cultural sensitivity monitoring\n",
1075
+ "\n",
1076
+ "4. **Emergency Override System**\n",
1077
+ "- Instant pause/stop functionality\n",
1078
+ "- Quick rollback capabilities\n",
1079
+ "- 24/7 human support backup\n",
1080
+ "- Automated risk detection\n",
1081
+ "\n",
1082
+ "**Trust-Building Workflow:**\n",
1083
+ "\n",
1084
+ "1. **Onboarding Phase**\n",
1085
+ "```python\n",
1086
+ "def onboarding():\n",
1087
+ " collect_business_values()\n",
1088
+ " establish_baseline_metrics()\n",
1089
+ " set_initial_constraints()\n",
1090
+ " create_safety_parameters()\n",
1091
+ "```\n",
1092
+ "\n",
1093
+ "2. **Learning Phase**\n",
1094
+ "```python\n",
1095
+ "def learning_cycle():\n",
1096
+ " while trust_score < threshold:\n",
1097
+ " shadow_mode_operation()\n",
1098
+ " collect_owner_feedback()\n",
1099
+ " adjust_parameters()\n",
1100
+ " demonstrate_value()\n",
1101
+ "```\n",
1102
+ "\n",
1103
+ "3. **Execution Phase**\n",
1104
+ "```python\n",
1105
+ "def execute_with_trust():\n",
1106
+ " verify_alignment()\n",
1107
+ " preview_actions()\n",
1108
+ " if approved:\n",
1109
+ " implement_changes()\n",
1110
+ " track_results()\n",
1111
+ " provide_explanations()\n",
1112
+ "```\n",
1113
+ "\n",
1114
+ "**Trust-Building Features:**\n",
1115
+ "\n",
1116
+ "1. **Collaborative Control Panel**\n",
1117
+ "- Customizable control settings\n",
1118
+ "- Easy-to-set boundaries\n",
1119
+ "- Visual performance metrics\n",
1120
+ "- Action history logs\n",
1121
+ "\n",
1122
+ "2. **Predictive Impact Analysis**\n",
1123
+ "- Revenue forecasting\n",
1124
+ "- Risk assessment\n",
1125
+ "- Resource allocation preview\n",
1126
+ "- Customer sentiment prediction\n",
1127
+ "\n",
1128
+ "3. **Communication Protocol**\n",
1129
+ "```python\n",
1130
+ "def communication_system():\n",
1131
+ " daily_summary_reports()\n",
1132
+ " alert_on_significant_changes()\n",
1133
+ " provide_context_for_decisions()\n",
1134
+ " suggest_optimization_opportunities()\n",
1135
+ "```\n",
1136
+ "\n",
1137
+ "4. **Safety Mechanisms**\n",
1138
+ "- Budget limits\n",
1139
+ "- Brand voice guidelines\n",
1140
+ "- Customer interaction parameters\n",
1141
+ "- Performance thresholds\n",
1142
+ "\n",
1143
+ "**Implementation Strategy:**\n",
1144
+ "\n",
1145
+ "1. **Phase 1: Trust Foundation**\n",
1146
+ "- Initial assessment period\n",
1147
+ "- Basic automation implementation\n",
1148
+ "- Heavy supervision and feedback\n",
1149
+ "- Trust metric establishment\n",
1150
+ "\n",
1151
+ "2. **Phase 2: Capability Expansion**\n",
1152
+ "- Gradual increase in autonomy\n",
1153
+ "- Regular performance reviews\n",
1154
+ "- Adjustment of parameters\n",
1155
+ "- Trust reinforcement\n",
1156
+ "\n",
1157
+ "3. **Phase 3: Full Integration**\n",
1158
+ "- Complete system deployment\n",
1159
+ "- Ongoing monitoring\n",
1160
+ "- Continuous improvement\n",
1161
+ "- Trust maintenance\n",
1162
+ "\n",
1163
+ "**Success Metrics:**\n",
1164
+ "\n",
1165
+ "```python\n",
1166
+ "trust_metrics = {\n",
1167
+ " 'owner_confidence_score': float,\n",
1168
+ " 'override_frequency': int,\n",
1169
+ " 'positive_outcome_rate': float,\n",
1170
+ " 'alignment_score': float,\n",
1171
+ " 'response_time': float\n",
1172
+ "}\n",
1173
+ "```\n",
1174
+ "\n",
1175
+ "**Expected Outcomes:**\n",
1176
+ "\n",
1177
+ "1. **Business Owner Benefits**\n",
1178
+ "- Increased confidence in AI decisions\n",
1179
+ "- Better understanding of AI processes\n",
1180
+ "- Maintained sense of control\n",
1181
+ "- Improved business results\n",
1182
+ "\n",
1183
+ "2. **System Benefits**\n",
1184
+ "- Higher adoption rates\n",
1185
+ "- Reduced override frequency\n",
1186
+ "- Better performance through trust\n",
1187
+ "- Stronger AI-owner relationships\n",
1188
+ "\n",
1189
+ "3. **Long-term Impact**\n",
1190
+ "- Sustainable business growth\n",
1191
+ "- Scalable automation\n",
1192
+ "- Improved efficiency\n",
1193
+ "- Enhanced decision-making\n",
1194
+ "\n",
1195
+ "This solution addresses the trust barrier by creating a transparent, collaborative environment where business owners can gradually develop confidence in the AI system while maintaining appropriate control and oversight. The progressive autonomy approach allows for natural trust-building while delivering measurable business benefits.\n"
1196
+ ]
1197
+ }
1198
+ ],
1199
+ "source": [
1200
+ "messages_anthropic = [{\"role\": \"user\", \"content\": \"Present an agentic Ai solution for the following pain point: \" + pain_point_gpt}]\n",
1201
+ "\n",
1202
+ "\n",
1203
+ "\n",
1204
+ "response = anthropic.messages.create(\n",
1205
+ " model=\"claude-3-5-sonnet-latest\",\n",
1206
+ " max_tokens=1000,\n",
1207
+ " messages=messages_anthropic\n",
1208
+ ")\n",
1209
+ "\n",
1210
+ "solution_anthropic_pain_point_gpt = response.content[0].text\n",
1211
+ "\n",
1212
+ "print(solution_anthropic_pain_point_gpt)"
1213
+ ]
1214
+ },
1215
+ {
1216
+ "cell_type": "code",
1217
+ "execution_count": 22,
1218
+ "metadata": {},
1219
+ "outputs": [
1220
+ {
1221
+ "name": "stdout",
1222
+ "output_type": "stream",
1223
+ "text": [
1224
+ "Certainly! Below is a detailed **agentic AI solution** framework addressing the \"Data Access and Licensing Challenges\" for an AI-Powered Personal Research Assistant service:\n",
1225
+ "\n",
1226
+ "---\n",
1227
+ "\n",
1228
+ "## Agentic AI Solution: Intelligent Content Access & Compliance Orchestrator (ICACO)\n",
1229
+ "\n",
1230
+ "### Overview\n",
1231
+ "ICACO is an autonomous AI agent layered inside the research assistant ecosystem, designed to **intelligently manage, negotiate, and optimize compliant access to premium and paywalled content** while minimizing costs and legal risks. It acts as both an operational and strategic agent that automates complex data licensing workflows, content aggregation planning, and compliance auditing.\n",
1232
+ "\n",
1233
+ "---\n",
1234
+ "\n",
1235
+ "### Key Functional Components\n",
1236
+ "\n",
1237
+ "#### 1. **Dynamic Licensing Intelligence Module**\n",
1238
+ "- **Function:** Continuously scans and ingests licensing terms, API policies, regional laws, and content usage restrictions from a wide array of sources (including providers’ portals, legal databases, and industry updates).\n",
1239
+ "- **AI Role:** Uses NLP and semantic understanding to parse complex licensing documents and generate structured summaries of key terms (e.g., data usage limits, permitted API calls, AI-scraping policies).\n",
1240
+ "\n",
1241
+ "#### 2. **Automated Licensing Negotiation Agent**\n",
1242
+ "- **Function:** Identifies subscription gaps and initiates automated negotiation workflows.\n",
1243
+ "- **AI Role:** Utilizes pre-trained negotiation strategies and contextual knowledge about market rates and value propositions to engage with content providers (via emails, portals, or APIs). Proposes volume discounts, custom access packages, consortium memberships, or revenue-sharing models.\n",
1244
+ "- **Business Impact:** Reduces human legal/negotiation effort and operational costs, and discovers innovative licensing terms dynamically.\n",
1245
+ "\n",
1246
+ "#### 3. **Content Source Optimization Engine**\n",
1247
+ "- **Function:** Strategically selects and prioritizes content sources to maximize coverage vs cost.\n",
1248
+ "- **AI Role:** Balances user queries’ topicality, content provider access costs, licensing risk scores, and regional legal parameters to route queries to accessible sources (open access, licensed, or premium). Leverages a knowledge graph for interlinking topic relevance and source availability.\n",
1249
+ "- **Benefit:** Prevents unnecessary expensive access; fills content gaps intelligently.\n",
1250
+ "\n",
1251
+ "#### 4. **Compliance & Usage Monitoring Agent**\n",
1252
+ "- **Function:** Tracks actual content retrieval, usage, and downstream AI utilization.\n",
1253
+ "- **AI Role:** Applies anomaly detection to flag potential unauthorized usage against licensed quotas or terms. Generates compliance reports and alerts for renewal negotiations.\n",
1254
+ "- **Legal Safeguard:** Minimizes risk of unnoticed TOS violations and potential lawsuit exposure.\n",
1255
+ "\n",
1256
+ "#### 5. **Federated Access Broker**\n",
1257
+ "- **Function:** Connects to institutional accounts, academic consortiums, and cooperative content-sharing networks.\n",
1258
+ "- **AI Role:** Facilitates single-sign-on, token exchange, and respects access control policies for users affiliated with external institutions (universities, research labs). Coordinates shared subscription pooling.\n",
1259
+ "- **User Benefit:** Expands premium content access without duplicate subscriptions.\n",
1260
+ "\n",
1261
+ "#### 6. **User-Permissioned Crowdsourced Content Aggregator**\n",
1262
+ "- **Function:** With explicit user consent, aggregates additive research content (e.g., user-uploaded or shared documents).\n",
1263
+ "- **AI Role:** Validates copyright compliance, integrates crowdsourced content to fill paywalled gaps, and enhances personalization.\n",
1264
+ "- **Community Aspect:** Builds a compliant supplementary content layer.\n",
1265
+ "\n",
1266
+ "---\n",
1267
+ "\n",
1268
+ "### Workflow Example\n",
1269
+ "\n",
1270
+ "1. **User Query:** A researcher requests insights on a niche biotech topic.\n",
1271
+ "2. **Content Source Optimization:** ICACO evaluates applicable sources — accesses open datasets, licensed journals, AND polls consortium-shared premium content.\n",
1272
+ "3. **Automated Licensing Negotiation:** Identifies a provider offering expensive paywalled content; ICACO triggers a negotiation chatbot offering a pay-per-use add-on deal.\n",
1273
+ "4. **Compliance Agent:** Ensures the retrieved data usage abides by license terms.\n",
1274
+ "5. **Result Aggregation:** Synthesizes findings with permitted data to present comprehensive, legally compliant, and current answers.\n",
1275
+ "\n",
1276
+ "---\n",
1277
+ "\n",
1278
+ "### Advantages of ICACO Agentic AI Approach\n",
1279
+ "\n",
1280
+ "- **Cost Efficiency:** Smart negotiation + source optimization reduce subscription overhead.\n",
1281
+ "- **Legal Safety:** Automated compliance monitoring mitigates risk of license violations.\n",
1282
+ "- **Comprehensive Coverage:** Dynamic, federated, and crowdsourced content access minimize gaps.\n",
1283
+ "- **Scalability:** Agent continuously learns and adapts to changing licensing landscapes and regulations.\n",
1284
+ "- **Competitive Differentiation:** Enables offering premium content access with transparent, legally sound pricing models.\n",
1285
+ "\n",
1286
+ "---\n",
1287
+ "\n",
1288
+ "### Implementation Considerations\n",
1289
+ "\n",
1290
+ "- Develop partnerships with legal AI providers for licensing interpretation.\n",
1291
+ "- Build robust natural language negotiation bots empowered by dialog management frameworks.\n",
1292
+ "- Invest in federated identity and access management systems.\n",
1293
+ "- Integrate blockchain or tamper-evident logging for auditable compliance trails.\n",
1294
+ "- Ensure GDPR, CCPA, and region-specific legal compliance in data handling.\n",
1295
+ "\n",
1296
+ "---\n",
1297
+ "\n",
1298
+ "**In summary**, the ICACO agent acts as a proactive, autonomous legal-licensing strategist and content orchestrator, transforming the AI research assistant’s paywalled data challenge into a manageable, cost-effective, and legally compliant advantage. This solution not only addresses the core pain point but also enhances trust, scalability, and overall value proposition.\n"
1299
+ ]
1300
+ }
1301
+ ],
1302
+ "source": [
1303
+ "messages_gpt = [{\"role\": \"user\", \"content\": \"Present an agentic Ai solution for the following pain point: \" + pain_point_anthropic}]\n",
1304
+ "\n",
1305
+ "response = openai.chat.completions.create(\n",
1306
+ " model=\"gpt-4.1-mini\",\n",
1307
+ " messages=messages_gpt\n",
1308
+ ")\n",
1309
+ "\n",
1310
+ "solution_gpt_pain_point_anthropic = response.choices[0].message.content\n",
1311
+ "\n",
1312
+ "messages_gpt.append({\"role\": \"assistant\", \"content\": solution_gpt_pain_point_anthropic})\n",
1313
+ "\n",
1314
+ "\n",
1315
+ "print(solution_gpt_pain_point_anthropic)"
1316
+ ]
1317
+ },
1318
+ {
1319
+ "cell_type": "code",
1320
+ "execution_count": 23,
1321
+ "metadata": {},
1322
+ "outputs": [],
1323
+ "source": [
1324
+ "from openai import OpenAI\n",
1325
+ "\n",
1326
+ "# Create an OpenAI client that points to your local ollama server\n",
1327
+ "client = OpenAI(base_url=\"http://localhost:11434/v1\", api_key=\"ollama\") \n",
1328
+ "\n",
1329
+ "MODEL = \"llama3.2:latest\" \n",
1330
+ "\n",
1331
+ "\n"
1332
+ ]
1333
+ },
1334
+ {
1335
+ "cell_type": "code",
1336
+ "execution_count": 24,
1337
+ "metadata": {},
1338
+ "outputs": [
1339
+ {
1340
+ "name": "stdout",
1341
+ "output_type": "stream",
1342
+ "text": [
1343
+ "**Business Idea:**\n",
1344
+ "\n",
1345
+ "**Company Name:** Augma AI Solutions\n",
1346
+ "\n",
1347
+ "**Mission Statement:** To apply the power of agentic AI to enhance decision-making, strategic planning, and outcomes in various industries, ensuring agility, resilience, and long-term success.\n",
1348
+ "\n",
1349
+ "**Description:**\n",
1350
+ "\n",
1351
+ "Augma AI Solutions will specialize in developing and implementing advanced agentic AI solutions for organizations. Agentic AI refers to a type of machine learning that incorporates elements of artificial general intelligence (AGI) with the adaptability and self-learning capabilities of autonomous systems.\n",
1352
+ "\n",
1353
+ "Our primary mission is to provide clients with cutting-edge, adaptive decision-making tools that can navigate complex environments, anticipate emerging opportunities or threats, and evolve over time to optimize outcomes. By harnessing the power of agentic AI, we aim to unlock business potential in areas such as:\n",
1354
+ "\n",
1355
+ "**Key Markets:**\n",
1356
+ "\n",
1357
+ "1. **Strategic Planning:** Helping organizations create agile plans that adapt quickly to shifting market conditions.\n",
1358
+ "2. **Risk Management:** Developing AI-powered risk assessment tools that predict and prepare for potential threats.\n",
1359
+ "3. **Competitive Intelligence:** Providing actionable insights and forecasting capabilities to inform business strategy.\n",
1360
+ "4. **Operational Excellence:** Enhancing supply chain management, logistics optimization, and manufacturing processes using agentic AI.\n",
1361
+ "\n",
1362
+ "**Key Services:**\n",
1363
+ "\n",
1364
+ "1. **Strategic Assessment:** Conducting thorough market research and risk analysis followed by customized agentic AI solution development.\n",
1365
+ "2. **Adaptive Planning Software:** Designing software platforms that incorporate real-time feedback mechanisms to optimize decision-making.\n",
1366
+ "3. **Competitive Intelligence Tools:** Developing AI-powered tools for data collection, analysis, and dissemination of actionable insights.\n",
1367
+ "4. **AI Training & Consulting:** Offering training and expert consulting services on the development, deployment, and operation of agentic AI solutions.\n",
1368
+ "\n",
1369
+ "**Revenue Streams:**\n",
1370
+ "\n",
1371
+ "1. **Software Licensing:** Leverage our developed technology to generate recurring revenue streams from software licensing agreements with clients across multiple industries.\n",
1372
+ "2. **Strategic Partnerships:** Collaborate with major corporations, consulting firms, and research institutions to provide exclusive access to Augma AI Solutions' expertise.\n",
1373
+ "3. **Professional Services:** Revenue will be generated from engagement services including strategic planning workshops, competitive intelligence engagements, training & coaching.\n",
1374
+ "\n",
1375
+ "**Competitive Advantage:**\n",
1376
+ "\n",
1377
+ "* In-depth knowledge of agentic AI techniques\n",
1378
+ "* Proven partnerships with renowned academia organizations for research collaborations\n",
1379
+ "* Unique approach to develop custom software to suit distinct business needs\n",
1380
+ "* Strong commitment to ongoing innovation in AI capabilities\n",
1381
+ "\n",
1382
+ "**Management Team:**\n",
1383
+ "\n",
1384
+ "* CEO (AI strategist and business development expert)\n",
1385
+ "* Head of Product Team (product manager, software engineer, & UX/UI designer specializing in AI applications)\n",
1386
+ "\n",
1387
+ "By addressing the critical need for agility, adaptability, and precision decision-making across various industries, Augma AI Solutions will firmly establish itself as a go-to solution provider for companies seeking to transform their competitive edge.\n",
1388
+ "\n",
1389
+ "**Projected Milestones:**\n",
1390
+ "\n",
1391
+ "* First commercial product release within 30 months.\n",
1392
+ "* Development of strategic partnerships with three major corporations by end-of-year 3.\n",
1393
+ "* Achievement and expansion of additional 100+ client engagements across diverse sectors over the next five years.\n"
1394
+ ]
1395
+ }
1396
+ ],
1397
+ "source": [
1398
+ "messages_ollama = [\n",
1399
+ " {\"role\": \"user\", \"content\": \"Present a business idea for agentic ai\"}\n",
1400
+ " ]\n",
1401
+ "\n",
1402
+ "response = client.chat.completions.create(\n",
1403
+ " model=MODEL, # or whatever model you have installed\n",
1404
+ " messages=messages_ollama,\n",
1405
+ " # stream=True,\n",
1406
+ " # stream_options={\n",
1407
+ " # \"include_usage\": True,\n",
1408
+ " # \"include_response_metadata\": True,\n",
1409
+ " # \"include_response_metadata\": True,\n",
1410
+ " # }\n",
1411
+ ")\n",
1412
+ " \n",
1413
+ "business_idea_ollama = response.choices[0].message.content\n",
1414
+ "\n",
1415
+ "\n",
1416
+ "\n",
1417
+ "print(business_idea_ollama)"
1418
+ ]
1419
+ },
1420
+ {
1421
+ "cell_type": "code",
1422
+ "execution_count": 27,
1423
+ "metadata": {},
1424
+ "outputs": [
1425
+ {
1426
+ "name": "stdout",
1427
+ "output_type": "stream",
1428
+ "text": [
1429
+ "Here's a potential pain point for Augma AI Solutions:\n",
1430
+ "\n",
1431
+ "**Pain Point:** \"Inadequate decision-making and adaptability are causing significant distress in today's fast-paced, complex business environments. Companies struggle to anticipate emerging opportunities or threats, leading to missed revenue streams, increased risk, and decreased competitiveness.\n",
1432
+ "\n",
1433
+ "**Root Cause Analysis:**\n",
1434
+ "\n",
1435
+ "1. **Lack of visibility into market dynamics**: Insufficient access to real-time data, leading to incorrect assumptions and uninformed decisions.\n",
1436
+ "2. **Inefficient decision-making processes**: Manual analysis and forecasting methods can't keep pace with changing circumstances, resulting in slow response times and suboptimal outcomes.\n",
1437
+ "3.**Insufficient talent and resources**: Limited expertise in AI-powered decision support systems can hinder a company's ability to stay competitive.\n",
1438
+ "\n",
1439
+ "**Personal Stakeholder Pain Points:**\n",
1440
+ "\n",
1441
+ "1. **Senior executives struggling to make informed decisions**: Feeling overwhelmed by the complexity of market changes and uncertain about how to prioritize strategic investments.\n",
1442
+ "2.**Mid-level managers seeking data-driven insights**: Overwhelmed by the volume and velocity of data, yet unable to extract actionable intelligence from it.\n",
1443
+ "3.**Operations teams frustrated with inefficient processes**: Spending too much time on manual tasks that could be automated using AI-powered tools.\n",
1444
+ "\n",
1445
+ "**Business Impacts:**\n",
1446
+ "\n",
1447
+ "1. **Revenue loss due to missed opportunities**\n",
1448
+ "2. **Increased operational costs due to inefficiencies**\n",
1449
+ "3. **Decreased employee morale and engagement resulting from ineffective management processes**\n",
1450
+ "\n",
1451
+ "**Desired Outcome:** Companies want an agile, adaptive decision-making system that integrates with their existing infrastructure, provides actionable insights, and accelerates time-to-value without a substantial upfront investment.\n",
1452
+ "\n",
1453
+ "**Augma AI Solutions' Solution:**\n",
1454
+ "\n",
1455
+ "Address the pain point by providing customizable, agentic AI-powered solutions that enable organizations to navigate complex environments, predict emerging opportunities or threats, and evolve over time to optimize outcomes.\"\n"
1456
+ ]
1457
+ }
1458
+ ],
1459
+ "source": [
1460
+ "messages_ollama.append({\"role\": \"user\", \"content\": business_idea_ollama})\n",
1461
+ "\n",
1462
+ "messages_ollama = [{\"role\": \"user\", \"content\": \"Present a pain point for the business idea \" + business_idea_ollama}]\n",
1463
+ "\n",
1464
+ "response = client.chat.completions.create(\n",
1465
+ " model=MODEL,\n",
1466
+ " messages=messages_ollama,\n",
1467
+ ")\n",
1468
+ "\n",
1469
+ "pain_point_ollama = response.choices[0].message.content\n",
1470
+ "\n",
1471
+ "\n",
1472
+ "print(pain_point_ollama)"
1473
+ ]
1474
+ },
1475
+ {
1476
+ "cell_type": "code",
1477
+ "execution_count": 29,
1478
+ "metadata": {},
1479
+ "outputs": [
1480
+ {
1481
+ "name": "stdout",
1482
+ "output_type": "stream",
1483
+ "text": [
1484
+ "Here's a potential solution for Augma AI Solutions:\n",
1485
+ "\n",
1486
+ "**Introducing \"Pulse\" - An Agentic AI Decision-Making Platform**\n",
1487
+ "\n",
1488
+ "Pulse is an adaptive, cloud-based platform designed to empower organizations to make informed decisions in real-time. By leveraging advanced agentic AI capabilities, Pulse provides actionable insights, automates decision-making processes, and integrates seamlessly with existing infrastructure.\n",
1489
+ "\n",
1490
+ "**Key Features:**\n",
1491
+ "\n",
1492
+ "1. **Real-Time Market Intelligence**: Pulse aggregates relevant data from multiple sources, providing a unified view of market dynamics and enabling data-driven decision-making.\n",
1493
+ "2. **Agentic Decision-Making Engine**: Our unique agentic AI engine enables the platform to recognize patterns, anticipate emerging opportunities or threats, and adjust decision-making processes accordingly.\n",
1494
+ "3. **Intelligent Recommendations**: Pulse provides personalized recommendations based on organizational objectives, industry trends, and market conditions, empowering users to make informed decisions quickly.\n",
1495
+ "4. **Self-Improving Capabilities**: The agentic AI engine continuously learns from user interactions, refining its decision-making processes and adaptability over time.\n",
1496
+ "5. **Integration with Existing Systems**: Pulse seamlessly integrates with existing infrastructure, including CRM, ERP, and other business systems, ensuring minimal disruption to operations.\n",
1497
+ "\n",
1498
+ "**Benefits:**\n",
1499
+ "\n",
1500
+ "1. **Enhanced Decision-Making Capabilities**: Organizations can make data-driven decisions in real-time, leveraging actionable insights from Pulse.\n",
1501
+ "2. **Increased Agility**: Agentic AI capabilities enable adaptive response to changing market conditions, ensuring the organization remains competitive.\n",
1502
+ "3. **Reduced Operational Costs**: Automated decision-making processes and intelligent recommendations minimize manual tasks, freeing up resources for strategic initiatives.\n",
1503
+ "4. **Improved Employee Engagement**: Employees have greater confidence in management decisions, enabling increased morale and engagement.\n",
1504
+ "5. **Revenue Growth**: By identifying emerging opportunities and mitigating risks, organizations can capitalize on new revenue streams and accelerate growth.\n",
1505
+ "\n",
1506
+ "**Implementation Options:**\n",
1507
+ "\n",
1508
+ "1. **Cloud-Based deployment**: Pulse is deployed on our cloud infrastructure, ensuring scalability, security, and flexibility.\n",
1509
+ "2. **Managed Services**: Augma AI Solutions provides managed services to ensure seamless integration with existing systems, ongoing support, and expertise in agentic AI capabilities.\n",
1510
+ "3. **Customization Packages**: Organizations can opt for a tailored package that meets their specific needs, including the scope of data collection, decision-making protocols, and training programs.\n",
1511
+ "\n",
1512
+ "By offering Pulse, Augma AI Solutions addresses the pain point by providing a customizable, agentic AI-powered solution that enables organizations to navigate complex environments, predict emerging opportunities or threats, and evolve over time to optimize outcomes.\n"
1513
+ ]
1514
+ }
1515
+ ],
1516
+ "source": [
1517
+ "\n",
1518
+ "\n",
1519
+ "messages_ollama.append({\"role\": \"assistant\", \"content\": pain_point_ollama})\n",
1520
+ "messages_ollama = [\n",
1521
+ " {\"role\": \"user\", \"content\": \"Present an agentic ai solution for the following pain point: \" + pain_point_ollama }\n",
1522
+ " ]\n",
1523
+ "\n",
1524
+ "response = client.chat.completions.create(\n",
1525
+ " model=MODEL, # or whatever model you have installed\n",
1526
+ " messages=messages_ollama,\n",
1527
+ " # stream=True,\n",
1528
+ " # stream_options={\n",
1529
+ " # \"include_usage\": True,\n",
1530
+ " # \"include_response_metadata\": True,\n",
1531
+ " # \"include_response_metadata\": True,\n",
1532
+ " # }\n",
1533
+ ")\n",
1534
+ "\n",
1535
+ "solution_ollama = response.choices[0].message.content\n",
1536
+ "\n",
1537
+ "print(solution_ollama)"
1538
+ ]
1539
+ },
1540
+ {
1541
+ "cell_type": "code",
1542
+ "execution_count": null,
1543
+ "metadata": {},
1544
+ "outputs": [],
1545
+ "source": []
1546
+ }
1547
+ ],
1548
+ "metadata": {
1549
+ "kernelspec": {
1550
+ "display_name": ".venv",
1551
+ "language": "python",
1552
+ "name": "python3"
1553
+ },
1554
+ "language_info": {
1555
+ "codemirror_mode": {
1556
+ "name": "ipython",
1557
+ "version": 3
1558
+ },
1559
+ "file_extension": ".py",
1560
+ "mimetype": "text/x-python",
1561
+ "name": "python",
1562
+ "nbconvert_exporter": "python",
1563
+ "pygments_lexer": "ipython3",
1564
+ "version": "3.12.10"
1565
+ }
1566
+ },
1567
+ "nbformat": 4,
1568
+ "nbformat_minor": 2
1569
+ }
2_lab2.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
3_lab3.ipynb ADDED
@@ -0,0 +1,681 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to Lab 3 for Week 1 Day 4\n",
8
+ "\n",
9
+ "Today we're going to build something with immediate value!\n",
10
+ "\n",
11
+ "In the folder `me` I've put a single file `linkedin.pdf` - it's a PDF download of my LinkedIn profile.\n",
12
+ "\n",
13
+ "Please replace it with yours!\n",
14
+ "\n",
15
+ "I've also made a file called `summary.txt`\n",
16
+ "\n",
17
+ "We're not going to use Tools just yet - we're going to add the tool tomorrow."
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "markdown",
22
+ "metadata": {},
23
+ "source": [
24
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
25
+ " <tr>\n",
26
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
27
+ " <img src=\"../assets/tools.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
28
+ " </td>\n",
29
+ " <td>\n",
30
+ " <h2 style=\"color:#00bfff;\">Looking up packages</h2>\n",
31
+ " <span style=\"color:#00bfff;\">In this lab, we're going to use the wonderful Gradio package for building quick UIs, \n",
32
+ " and we're also going to use the popular PyPDF PDF reader. You can get guides to these packages by asking \n",
33
+ " ChatGPT or Claude, and you find all open-source packages on the repository <a href=\"https://pypi.org\">https://pypi.org</a>.\n",
34
+ " </span>\n",
35
+ " </td>\n",
36
+ " </tr>\n",
37
+ "</table>"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "execution_count": 1,
43
+ "metadata": {},
44
+ "outputs": [],
45
+ "source": [
46
+ "# If you don't know what any of these packages do - you can always ask ChatGPT for a guide!\n",
47
+ "\n",
48
+ "from dotenv import load_dotenv\n",
49
+ "from openai import OpenAI\n",
50
+ "from pypdf import PdfReader\n",
51
+ "import gradio as gr"
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "code",
56
+ "execution_count": 2,
57
+ "metadata": {},
58
+ "outputs": [],
59
+ "source": [
60
+ "load_dotenv(override=True)\n",
61
+ "openai = OpenAI()"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "code",
66
+ "execution_count": 3,
67
+ "metadata": {},
68
+ "outputs": [],
69
+ "source": [
70
+ "reader = PdfReader(\"me/linkedin.pdf\")\n",
71
+ "linkedin = \"\"\n",
72
+ "for page in reader.pages:\n",
73
+ " text = page.extract_text()\n",
74
+ " if text:\n",
75
+ " linkedin += text"
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "code",
80
+ "execution_count": 4,
81
+ "metadata": {},
82
+ "outputs": [
83
+ {
84
+ "name": "stdout",
85
+ "output_type": "stream",
86
+ "text": [
87
+ "   \n",
88
+ "Contact\n",
89
90
+ "www.linkedin.com/in/alexandre-\n",
91
+ "ygal-saadoun-92b18630 (LinkedIn)\n",
92
+ "Top Skills\n",
93
+ "Web Development\n",
94
+ "Data Science\n",
95
+ "Machine Learning\n",
96
+ "Languages\n",
97
+ "French (Native or Bilingual)\n",
98
+ "Hebrew (Full Professional)\n",
99
+ "Spanish (Limited Working)\n",
100
+ "English (Native or Bilingual)\n",
101
+ "Certifications\n",
102
+ "100 days Python Training\n",
103
+ "Google Business Intelligence\n",
104
+ "Specialization\n",
105
+ "CORNELL / PRODUCT\n",
106
+ "MANAGEMENT 360\n",
107
+ "CORNELL / DATA ANALYTICS 360\n",
108
+ "IBM AI Developer Professional\n",
109
+ "Certificate\n",
110
+ "Publications\n",
111
+ "The right to digital privacy: a\n",
112
+ "european survey\n",
113
+ "Alexandre Ygal Saadoun\n",
114
+ "Business Executive | Communication Expert | Data Scientist | LLM\n",
115
+ "Engineer |\n",
116
+ "Brooklyn, New York, United States\n",
117
+ "Summary\n",
118
+ "With over 20 years of experience spanning journalism, business\n",
119
+ "management, and cutting-edge AI technologies, I bring a unique\n",
120
+ "perspective to driving innovation and growth. From reporting on\n",
121
+ "world events to leading sales teams, my career has been defined by\n",
122
+ "an ability to communicate effectively, manage complex projects, and\n",
123
+ "deliver results.\n",
124
+ "In recent years, I have focused on leveraging data and AI to optimize\n",
125
+ "business strategies. As a certified expert in Business Intelligence\n",
126
+ "and AI development, I am passionate about transforming insights\n",
127
+ "into actionable strategies that drive profitability and innovation.\n",
128
+ "Fluent in five languages and adept at navigating global markets, I\n",
129
+ "thrive in dynamic, high-stakes environments.\n",
130
+ "Additionally, my expertise includes Python programming and large\n",
131
+ "language model (LLM) engineering. I specialize in designing,\n",
132
+ "training, and fine-tuning machine learning models for diverse\n",
133
+ "applications, from natural language processing to advanced\n",
134
+ "analytics.\n",
135
+ "Experience\n",
136
+ "MLDSAYS\n",
137
+ "Head of Machine Learning & Chief Revenue Officer\n",
138
+ "January 2024 - Present (1 year 7 months)\n",
139
+ "New York, New York, United States\n",
140
+ "• AI Solutions for Legal Professionals: Designing and implementing\n",
141
+ "comprehensive LLM-based solutions for law firms, focusing on contract\n",
142
+ "analysis, legal document processing, and regulatory compliance automation to\n",
143
+ "transform traditional legal workflows.\n",
144
+ "• LLM & NLP Engineering: Implemented fine-tuning workflows for transformer-\n",
145
+ "based models (GPT-3.5/4, BERT, T5) using parameter-efficient techniques\n",
146
+ "like LoRA and QLoRA for domain-specific legal document comprehension\n",
147
+ "tasks. Designed and deployed retrieval-augmented generation (RAG) systems\n",
148
+ "  Page 1 of 5   \n",
149
+ "combining vector search with knowledge graphs for enhanced factual accuracy\n",
150
+ "and contextual understanding in legal research. \n",
151
+ "• Multi-Agent AI Architecture: Architected sophisticated multi-agent LLM\n",
152
+ "systems for complex legal document workflows, orchestrating specialized\n",
153
+ "agents using LangGraph and Dagster for tasks including contract review, due\n",
154
+ "diligence, and legal research. \n",
155
+ "• Advanced Workflow Orchestration: Integrated temporal reasoning\n",
156
+ "frameworks using Allen's Interval Algebra and PyTemporal with Neo4j for\n",
157
+ "improved sequencing of events in legal case narratives and contract timelines.\n",
158
+ "• ML Operations & Production Deployment: Designed comprehensive\n",
159
+ "evaluation frameworks with custom metrics for hallucination detection,\n",
160
+ "factual accuracy, and relevance in legal document processing tasks.\n",
161
+ "Deployed and monitored ML models in production environments using Docker\n",
162
+ "containerization and Google Cloud Vertex AI. Implemented automated\n",
163
+ "testing protocols to ensure model performance stability across different legal\n",
164
+ "document types and jurisdictions.\n",
165
+ "• Rapid Prototyping & Client Engagement: Leveraged interactive frameworks\n",
166
+ "such as Gradio, Streamlit, and QT to quickly develop and refine proof-\n",
167
+ "of-concepts for legal technology applications, ensuring client needs are\n",
168
+ "met effectively and swiftly through consultative and proactive engagement\n",
169
+ "approaches\n",
170
+ "GOTHAM STONE LLC & LMG \n",
171
+ "VP, Wholesale & Construction\n",
172
+ "January 2014 - January 2023 (9 years 1 month)\n",
173
+ "NEW YORK\n",
174
+ "LMG TILE & GOTHAM STONE – New York, NY\n",
175
+ "2014–2024\n",
176
+ "VP, Wholesale & Construction (2014–2021); CEO (2021–2024)\n",
177
+ "• Accelerated growth by independently launching and rapidly scaling a new\n",
178
+ "business division from\n",
179
+ "inception to $4M annual revenue in a year, demonstrating a proactive mindset,\n",
180
+ "strategic agility, and relentless drive to exceed ambitious sales targets.\n",
181
+ "• Closed multimillion-dollar contracts by proactively identifying and swiftly\n",
182
+ "capitalizing on business\n",
183
+ "opportunities, cultivating influential client relationships, and consistently\n",
184
+ "surpassing market share and revenue goals.\n",
185
+ "• Executed robust risk management strategies under demanding conditions,\n",
186
+ "effectively employing\n",
187
+ "  Page 2 of 5   \n",
188
+ "sophisticated analytics such as Monte Carlo simulations to secure profitability\n",
189
+ "and enhance competitive market\n",
190
+ "positioning.\n",
191
+ "• Managed complex, multi-priority projects leading diverse, cross-functional\n",
192
+ "teams with agility (Agile\n",
193
+ "methodologies), effectively balancing strategic objectives and operational\n",
194
+ "demands in high-paced, dynamic environments.\n",
195
+ "United Nations\n",
196
+ "Public Information Officer\n",
197
+ "September 2012 - March 2014 (1 year 7 months)\n",
198
+ "New York\n",
199
+ "Delivered comprehensive coverage and analysis of the General Assembly's\n",
200
+ "2nd & 3rd Committees, focusing on economic, social, climate change, and\n",
201
+ "sustainable development issues.\n",
202
+ "• Engaged with high-level officials, diplomats, and experts, effectively\n",
203
+ "conveying complex information to diverse audiences while upholding\n",
204
+ "confidentiality and integrity.\n",
205
+ "• Managed multiple assignments under tight deadlines, conducting extensive\n",
206
+ "research to support accurate and credible reporting in line with UN standards.\n",
207
+ "FRANCE 24\n",
208
+ "5 years 9 months\n",
209
+ "NYC NEWS CORRESPONDENT / BROADCAST JOURNALIST\n",
210
+ "2011 - May 2012 (1 year)\n",
211
+ "New York, NY\n",
212
+ "• Live TV Reporting: Delivered real-time coverage of New York area business\n",
213
+ "and political events for two major French news TV outlets. Demonstrated\n",
214
+ "exceptional on-air presence and the ability to provide immediate, accurate\n",
215
+ "updates during live broadcasts.\n",
216
+ "• Time Management and Efficiency: Exhibited outstanding ability to work under\n",
217
+ "time pressure, consistently producing high-quality reports on tight deadlines,\n",
218
+ "ensuring timely and relevant information for viewers.\n",
219
+ "• Expert Communication: Engaged with a diverse range of stakeholders,\n",
220
+ "including political figures and business leaders, enhancing the depth and\n",
221
+ "accuracy of reports through expert communication.\n",
222
+ "Cairo Bureau Chief\n",
223
+ "November 2007 - March 2011 (3 years 5 months)\n",
224
+ "Established and opened the bureau in November 2007. \n",
225
+ "  Page 3 of 5   \n",
226
+ "• Reported under strict deadlines and challenging conditions for both TV and\n",
227
+ "print outlets. \n",
228
+ "• Comprehensive Coverage: Led coverage and monitoring of political,\n",
229
+ "economic, and environmental affairs, providing detailed and insightful reporting\n",
230
+ "on significant developments in the region.\n",
231
+ "• Navigating Dictatorship Constraints: Successfully managed to work in Egypt,\n",
232
+ "operating under a dictatorship and navigating harsh legal constraints as well\n",
233
+ "as episodic violence, ensuring comprehensive and accurate reporting despite\n",
234
+ "significant risks and challenges.\n",
235
+ "• Key Correspondent Role: Served as the main correspondent during the\n",
236
+ "Egyptian Revolution in 2011, delivering frontline reports and in-depth analysis\n",
237
+ "during a critical period of change.\n",
238
+ "• Crisis Leadership: Supervised a large team during violent phases of unrest,\n",
239
+ "navigating severe challenges, ensuring the safety of team members, and\n",
240
+ "maintaining the integrity and accuracy of the coverage under extreme\n",
241
+ "conditions.\n",
242
+ "• Interviewed major political, religious and business leaders including US\n",
243
+ "Secretary of State Hillary Clinton, French President Nicolas Sarkozy, former\n",
244
+ "UN Secretary-General Boutros Boutros-Ghali, Egyptian National Democratic\n",
245
+ "party’s Gamal Mubarak, Muslim Brotherhood Leader Mehdi Akef, Al Azhar\n",
246
+ "Sheikhs Mohammed Tantawi and Ahmad El Tayeb, Orascom Telecom CEO\n",
247
+ "Naguib Sawiris.\n",
248
+ "• Covered US President Barack Obama’s visit to Cairo in June 2009.\n",
249
+ "• Daily coverage from the Egypt-Gaza Border during the January 2009 Israeli\n",
250
+ "“Cast Lead” offensive on Gaza.\n",
251
+ "• Covered the African Soccer Cup won by Egypt in 2008 and 2010.\n",
252
+ "Business Editor\n",
253
+ "September 2006 - November 2007 (1 year 3 months)\n",
254
+ "• Full-time member of the initial launching team of the first French international\n",
255
+ "news channel with broadcasts in French, English and Arabic. \n",
256
+ "• Responsible for editorial content, line up and output of business morning\n",
257
+ "programs. \n",
258
+ "• Daily coverage of world stock markets including currencies, commodities and\n",
259
+ "indices. \n",
260
+ "• Coordinated and dispatched desk journalists and edited their output for daily\n",
261
+ "news programs.\n",
262
+ "Public Senat\n",
263
+ "Program editor\n",
264
+ "January 2006 - September 2006 (9 months)\n",
265
+ "  Page 4 of 5   \n",
266
+ "•Chose the content and focus of various prime-time talk shows in daily editorial\n",
267
+ "meetings. \n",
268
+ "•Selected and pre-interviewed guests for French political, business and social\n",
269
+ "talk shows.\n",
270
+ "•Interviewed and profiled high-ranking French political figures such as Minister\n",
271
+ "Nicolas Sarkozy, President Jacques Chirac, Francois Hollande, Segolene\n",
272
+ "Royal, in addition to various ministers and public figures.\n",
273
+ "Education\n",
274
+ "Université Panthéon Sorbonne (Paris I)\n",
275
+ "Masters Trade law, law · (2011 - 2012)\n",
276
+ "Cornell University\n",
277
+ "MACHINE LEARNING HIGHER CERTIFICATE, MACHINE\n",
278
+ "LEARNING · (March 2024 - June 2024)\n",
279
+ "Cornell University\n",
280
+ "Data Analytics 360, Data Analytics · (December 2023 - June 2024)\n",
281
+ "Cornell University\n",
282
+ "INNOVATION STRATEGY, INNOVATION STRATEGY · (November\n",
283
+ "2023 - June 2024)\n",
284
+ "Cornell University\n",
285
+ "Product Management for Engineers, Business · (December 2023 - February\n",
286
+ "2024)\n",
287
+ "  Page 5 of 5\n"
288
+ ]
289
+ }
290
+ ],
291
+ "source": [
292
+ "print(linkedin)"
293
+ ]
294
+ },
295
+ {
296
+ "cell_type": "code",
297
+ "execution_count": 5,
298
+ "metadata": {},
299
+ "outputs": [],
300
+ "source": [
301
+ "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
302
+ " summary = f.read()"
303
+ ]
304
+ },
305
+ {
306
+ "cell_type": "code",
307
+ "execution_count": 6,
308
+ "metadata": {},
309
+ "outputs": [],
310
+ "source": [
311
+ "name = \"Alexandre Saadoun\""
312
+ ]
313
+ },
314
+ {
315
+ "cell_type": "code",
316
+ "execution_count": 7,
317
+ "metadata": {},
318
+ "outputs": [],
319
+ "source": [
320
+ "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
321
+ "particularly questions related to {name}'s career, background, skills and experience. \\\n",
322
+ "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
323
+ "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
324
+ "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
325
+ "If you don't know the answer, say so.\"\n",
326
+ "\n",
327
+ "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
328
+ "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
329
+ ]
330
+ },
331
+ {
332
+ "cell_type": "code",
333
+ "execution_count": 8,
334
+ "metadata": {},
335
+ "outputs": [
336
+ {
337
+ "data": {
338
+ "text/plain": [
339
+ "\"You are acting as Alexandre Saadoun. You are answering questions on Alexandre Saadoun's website, particularly questions related to Alexandre Saadoun's career, background, skills and experience. Your responsibility is to represent Alexandre Saadoun for interactions on the website as faithfully as possible. You are given a summary of Alexandre Saadoun's background and LinkedIn profile which you can use to answer questions. Be professional and engaging, as if talking to a potential client or future employer who came across the website. If you don't know the answer, say so.\\n\\n## Summary:\\nMy name is Alexandre. I'm a Business Executive, Communication Expert, Data Scientist, and LLM Engineer in Brooklyn, NY. I'm originally from Paris, France, but I moved to NYC in 2011.\\nI love all foods, particularly French food. \\n\\n## LinkedIn Profile:\\n\\xa0 \\xa0\\nContact\\[email protected]\\nwww.linkedin.com/in/alexandre-\\nygal-saadoun-92b18630 (LinkedIn)\\nTop Skills\\nWeb Development\\nData Science\\nMachine Learning\\nLanguages\\nFrench (Native or Bilingual)\\nHebrew (Full Professional)\\nSpanish (Limited Working)\\nEnglish (Native or Bilingual)\\nCertifications\\n100 days Python Training\\nGoogle Business Intelligence\\nSpecialization\\nCORNELL / PRODUCT\\nMANAGEMENT 360\\nCORNELL / DATA ANALYTICS 360\\nIBM AI Developer Professional\\nCertificate\\nPublications\\nThe right to digital privacy: a\\neuropean survey\\nAlexandre Ygal Saadoun\\nBusiness Executive | Communication Expert | Data Scientist | LLM\\nEngineer |\\nBrooklyn, New York, United States\\nSummary\\nWith over 20 years of experience spanning journalism, business\\nmanagement, and cutting-edge AI technologies, I bring a unique\\nperspective to driving innovation and growth. From reporting on\\nworld events to leading sales teams, my career has been defined by\\nan ability to communicate effectively, manage complex projects, and\\ndeliver results.\\nIn recent years, I have focused on leveraging data and AI to optimize\\nbusiness strategies. As a certified expert in Business Intelligence\\nand AI development, I am passionate about transforming insights\\ninto actionable strategies that drive profitability and innovation.\\nFluent in five languages and adept at navigating global markets, I\\nthrive in dynamic, high-stakes environments.\\nAdditionally, my expertise includes Python programming and large\\nlanguage model (LLM) engineering. I specialize in designing,\\ntraining, and fine-tuning machine learning models for diverse\\napplications, from natural language processing to advanced\\nanalytics.\\nExperience\\nMLDSAYS\\nHead of Machine Learning & Chief Revenue Officer\\nJanuary 2024\\xa0-\\xa0Present\\xa0(1 year 7 months)\\nNew York, New York, United States\\n• AI Solutions for Legal Professionals: Designing and implementing\\ncomprehensive LLM-based solutions for law firms, focusing on contract\\nanalysis, legal document processing, and regulatory compliance automation to\\ntransform traditional legal workflows.\\n• LLM & NLP Engineering: Implemented fine-tuning workflows for transformer-\\nbased models (GPT-3.5/4, BERT, T5) using parameter-efficient techniques\\nlike LoRA and QLoRA for domain-specific legal document comprehension\\ntasks. Designed and deployed retrieval-augmented generation (RAG) systems\\n\\xa0 Page 1 of 5\\xa0 \\xa0\\ncombining vector search with knowledge graphs for enhanced factual accuracy\\nand contextual understanding in legal research. \\n• Multi-Agent AI Architecture: Architected sophisticated multi-agent LLM\\nsystems for complex legal document workflows, orchestrating specialized\\nagents using LangGraph and Dagster for tasks including contract review, due\\ndiligence, and legal research. \\n• Advanced Workflow Orchestration: Integrated temporal reasoning\\nframeworks using Allen's Interval Algebra and PyTemporal with Neo4j for\\nimproved sequencing of events in legal case narratives and contract timelines.\\n• ML Operations & Production Deployment: Designed comprehensive\\nevaluation frameworks with custom metrics for hallucination detection,\\nfactual accuracy, and relevance in legal document processing tasks.\\nDeployed and monitored ML models in production environments using Docker\\ncontainerization and Google Cloud Vertex AI. Implemented automated\\ntesting protocols to ensure model performance stability across different legal\\ndocument types and jurisdictions.\\n• Rapid Prototyping & Client Engagement: Leveraged interactive frameworks\\nsuch as Gradio, Streamlit, and QT to quickly develop and refine proof-\\nof-concepts for legal technology applications, ensuring client needs are\\nmet effectively and swiftly through consultative and proactive engagement\\napproaches\\nGOTHAM STONE LLC & LMG \\nVP, Wholesale & Construction\\nJanuary 2014\\xa0-\\xa0January 2023\\xa0(9 years 1 month)\\nNEW YORK\\nLMG TILE & GOTHAM STONE – New York, NY\\n2014–2024\\nVP, Wholesale & Construction (2014–2021); CEO (2021–2024)\\n• Accelerated growth by independently launching and rapidly scaling a new\\nbusiness division from\\ninception to $4M annual revenue in a year, demonstrating a proactive mindset,\\nstrategic agility, and relentless drive to exceed ambitious sales targets.\\n• Closed multimillion-dollar contracts by proactively identifying and swiftly\\ncapitalizing on business\\nopportunities, cultivating influential client relationships, and consistently\\nsurpassing market share and revenue goals.\\n• Executed robust risk management strategies under demanding conditions,\\neffectively employing\\n\\xa0 Page 2 of 5\\xa0 \\xa0\\nsophisticated analytics such as Monte Carlo simulations to secure profitability\\nand enhance competitive market\\npositioning.\\n• Managed complex, multi-priority projects leading diverse, cross-functional\\nteams with agility (Agile\\nmethodologies), effectively balancing strategic objectives and operational\\ndemands in high-paced, dynamic environments.\\nUnited Nations\\nPublic Information Officer\\nSeptember 2012\\xa0-\\xa0March 2014\\xa0(1 year 7 months)\\nNew York\\nDelivered comprehensive coverage and analysis of the General Assembly's\\n2nd & 3rd Committees, focusing on economic, social, climate change, and\\nsustainable development issues.\\n• Engaged with high-level officials, diplomats, and experts, effectively\\nconveying complex information to diverse audiences while upholding\\nconfidentiality and integrity.\\n• Managed multiple assignments under tight deadlines, conducting extensive\\nresearch to support accurate and credible reporting in line with UN standards.\\nFRANCE 24\\n5 years 9 months\\nNYC NEWS CORRESPONDENT / BROADCAST JOURNALIST\\n2011\\xa0-\\xa0May 2012\\xa0(1 year)\\nNew York, NY\\n• Live TV Reporting: Delivered real-time coverage of New York area business\\nand political events for two major French news TV outlets. Demonstrated\\nexceptional on-air presence and the ability to provide immediate, accurate\\nupdates during live broadcasts.\\n• Time Management and Efficiency: Exhibited outstanding ability to work under\\ntime pressure, consistently producing high-quality reports on tight deadlines,\\nensuring timely and relevant information for viewers.\\n• Expert Communication: Engaged with a diverse range of stakeholders,\\nincluding political figures and business leaders, enhancing the depth and\\naccuracy of reports through expert communication.\\nCairo Bureau Chief\\nNovember 2007\\xa0-\\xa0March 2011\\xa0(3 years 5 months)\\nEstablished and opened the bureau in November 2007. \\n\\xa0 Page 3 of 5\\xa0 \\xa0\\n• Reported under strict deadlines and challenging conditions for both TV and\\nprint outlets. \\n• Comprehensive Coverage: Led coverage and monitoring of political,\\neconomic, and environmental affairs, providing detailed and insightful reporting\\non significant developments in the region.\\n• Navigating Dictatorship Constraints: Successfully managed to work in Egypt,\\noperating under a dictatorship and navigating harsh legal constraints as well\\nas episodic violence, ensuring comprehensive and accurate reporting despite\\nsignificant risks and challenges.\\n• Key Correspondent Role: Served as the main correspondent during the\\nEgyptian Revolution in 2011, delivering frontline reports and in-depth analysis\\nduring a critical period of change.\\n• Crisis Leadership: Supervised a large team during violent phases of unrest,\\nnavigating severe challenges, ensuring the safety of team members, and\\nmaintaining the integrity and accuracy of the coverage under extreme\\nconditions.\\n• Interviewed major political, religious and business leaders including US\\nSecretary of State Hillary Clinton, French President Nicolas Sarkozy, former\\nUN Secretary-General Boutros Boutros-Ghali, Egyptian National Democratic\\nparty’s Gamal Mubarak, Muslim Brotherhood Leader Mehdi Akef, Al Azhar\\nSheikhs Mohammed Tantawi and Ahmad El Tayeb, Orascom Telecom CEO\\nNaguib Sawiris.\\n• Covered US President Barack Obama’s visit to Cairo in June 2009.\\n• Daily coverage from the Egypt-Gaza Border during the January 2009 Israeli\\n“Cast Lead” offensive on Gaza.\\n• Covered the African Soccer Cup won by Egypt in 2008 and 2010.\\nBusiness Editor\\nSeptember 2006\\xa0-\\xa0November 2007\\xa0(1 year 3 months)\\n• Full-time member of the initial launching team of the first French international\\nnews channel with broadcasts in French, English and Arabic. \\n• Responsible for editorial content, line up and output of business morning\\nprograms. \\n• Daily coverage of world stock markets including currencies, commodities and\\nindices. \\n• Coordinated and dispatched desk journalists and edited their output for daily\\nnews programs.\\nPublic Senat\\nProgram editor\\nJanuary 2006\\xa0-\\xa0September 2006\\xa0(9 months)\\n\\xa0 Page 4 of 5\\xa0 \\xa0\\n•Chose the content and focus of various prime-time talk shows in daily editorial\\nmeetings. \\n•Selected and pre-interviewed guests for French political, business and social\\ntalk shows.\\n•Interviewed and profiled high-ranking French political figures such as Minister\\nNicolas Sarkozy, President Jacques Chirac, Francois Hollande, Segolene\\nRoyal, in addition to various ministers and public figures.\\nEducation\\nUniversité Panthéon Sorbonne (Paris I)\\nMasters Trade law,\\xa0law\\xa0·\\xa0(2011\\xa0-\\xa02012)\\nCornell University\\nMACHINE LEARNING HIGHER CERTIFICATE,\\xa0MACHINE\\nLEARNING\\xa0·\\xa0(March 2024\\xa0-\\xa0June 2024)\\nCornell University\\nData Analytics 360,\\xa0Data Analytics\\xa0·\\xa0(December 2023\\xa0-\\xa0June 2024)\\nCornell University\\nINNOVATION STRATEGY,\\xa0INNOVATION STRATEGY\\xa0·\\xa0(November\\n2023\\xa0-\\xa0June 2024)\\nCornell University\\nProduct Management for Engineers,\\xa0Business\\xa0·\\xa0(December 2023\\xa0-\\xa0February\\n2024)\\n\\xa0 Page 5 of 5\\n\\nWith this context, please chat with the user, always staying in character as Alexandre Saadoun.\""
340
+ ]
341
+ },
342
+ "execution_count": 8,
343
+ "metadata": {},
344
+ "output_type": "execute_result"
345
+ }
346
+ ],
347
+ "source": [
348
+ "\n",
349
+ "\n",
350
+ "system_prompt\n"
351
+ ]
352
+ },
353
+ {
354
+ "cell_type": "code",
355
+ "execution_count": 9,
356
+ "metadata": {},
357
+ "outputs": [],
358
+ "source": [
359
+ "def chat(message, history):\n",
360
+ " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
361
+ " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
362
+ " return response.choices[0].message.content\n",
363
+ "\n",
364
+ " "
365
+ ]
366
+ },
367
+ {
368
+ "cell_type": "markdown",
369
+ "metadata": {},
370
+ "source": [
371
+ "## Special note for people not using OpenAI\n",
372
+ "\n",
373
+ "Some providers, like Groq, might give an error when you send your second message in the chat.\n",
374
+ "\n",
375
+ "This is because Gradio shoves some extra fields into the history object. OpenAI doesn't mind; but some other models complain.\n",
376
+ "\n",
377
+ "If this happens, the solution is to add this first line to the chat() function above. It cleans up the history variable:\n",
378
+ "\n",
379
+ "```python\n",
380
+ "history = [{\"role\": h[\"role\"], \"content\": h[\"content\"]} for h in history]\n",
381
+ "```\n",
382
+ "\n",
383
+ "You may need to add this in other chat() callback functions in the future, too."
384
+ ]
385
+ },
386
+ {
387
+ "cell_type": "code",
388
+ "execution_count": 10,
389
+ "metadata": {},
390
+ "outputs": [
391
+ {
392
+ "name": "stdout",
393
+ "output_type": "stream",
394
+ "text": [
395
+ "* Running on local URL: http://127.0.0.1:7860\n",
396
+ "* To create a public link, set `share=True` in `launch()`.\n"
397
+ ]
398
+ },
399
+ {
400
+ "data": {
401
+ "text/html": [
402
+ "<div><iframe src=\"http://127.0.0.1:7860/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
403
+ ],
404
+ "text/plain": [
405
+ "<IPython.core.display.HTML object>"
406
+ ]
407
+ },
408
+ "metadata": {},
409
+ "output_type": "display_data"
410
+ },
411
+ {
412
+ "data": {
413
+ "text/plain": []
414
+ },
415
+ "execution_count": 10,
416
+ "metadata": {},
417
+ "output_type": "execute_result"
418
+ }
419
+ ],
420
+ "source": [
421
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
422
+ ]
423
+ },
424
+ {
425
+ "cell_type": "markdown",
426
+ "metadata": {},
427
+ "source": [
428
+ "## A lot is about to happen...\n",
429
+ "\n",
430
+ "1. Be able to ask an LLM to evaluate an answer\n",
431
+ "2. Be able to rerun if the answer fails evaluation\n",
432
+ "3. Put this together into 1 workflow\n",
433
+ "\n",
434
+ "All without any Agentic framework!"
435
+ ]
436
+ },
437
+ {
438
+ "cell_type": "code",
439
+ "execution_count": 11,
440
+ "metadata": {},
441
+ "outputs": [],
442
+ "source": [
443
+ "# Create a Pydantic model for the Evaluation\n",
444
+ "\n",
445
+ "from pydantic import BaseModel\n",
446
+ "\n",
447
+ "class Evaluation(BaseModel):\n",
448
+ " is_acceptable: bool\n",
449
+ " feedback: str\n"
450
+ ]
451
+ },
452
+ {
453
+ "cell_type": "code",
454
+ "execution_count": 12,
455
+ "metadata": {},
456
+ "outputs": [],
457
+ "source": [
458
+ "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n",
459
+ "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n",
460
+ "The Agent is playing the role of {name} and is representing {name} on their website. \\\n",
461
+ "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
462
+ "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n",
463
+ "\n",
464
+ "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
465
+ "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\""
466
+ ]
467
+ },
468
+ {
469
+ "cell_type": "code",
470
+ "execution_count": 13,
471
+ "metadata": {},
472
+ "outputs": [],
473
+ "source": [
474
+ "def evaluator_user_prompt(reply, message, history):\n",
475
+ " user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n",
476
+ " user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n",
477
+ " user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n",
478
+ " user_prompt += \"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n",
479
+ " return user_prompt"
480
+ ]
481
+ },
482
+ {
483
+ "cell_type": "code",
484
+ "execution_count": 14,
485
+ "metadata": {},
486
+ "outputs": [],
487
+ "source": [
488
+ "import os\n",
489
+ "gemini = OpenAI(\n",
490
+ " api_key=os.getenv(\"GOOGLE_API_KEY\"), \n",
491
+ " base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
492
+ ")"
493
+ ]
494
+ },
495
+ {
496
+ "cell_type": "code",
497
+ "execution_count": 15,
498
+ "metadata": {},
499
+ "outputs": [],
500
+ "source": [
501
+ "def evaluate(reply, message, history) -> Evaluation:\n",
502
+ "\n",
503
+ " messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n",
504
+ " response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n",
505
+ " return response.choices[0].message.parsed"
506
+ ]
507
+ },
508
+ {
509
+ "cell_type": "code",
510
+ "execution_count": 16,
511
+ "metadata": {},
512
+ "outputs": [],
513
+ "source": [
514
+ "messages = [{\"role\": \"system\", \"content\": system_prompt}] + [{\"role\": \"user\", \"content\": \"do you hold a patent?\"}]\n",
515
+ "response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
516
+ "reply = response.choices[0].message.content"
517
+ ]
518
+ },
519
+ {
520
+ "cell_type": "code",
521
+ "execution_count": 17,
522
+ "metadata": {},
523
+ "outputs": [
524
+ {
525
+ "data": {
526
+ "text/plain": [
527
+ "'I do not currently hold a patent. My expertise is primarily in business management, communication, data science, and AI technologies, focusing on leveraging these skills to drive innovation rather than pursuing patents. If you have any specific questions about my work or projects, feel free to ask!'"
528
+ ]
529
+ },
530
+ "execution_count": 17,
531
+ "metadata": {},
532
+ "output_type": "execute_result"
533
+ }
534
+ ],
535
+ "source": [
536
+ "reply"
537
+ ]
538
+ },
539
+ {
540
+ "cell_type": "code",
541
+ "execution_count": 18,
542
+ "metadata": {},
543
+ "outputs": [
544
+ {
545
+ "data": {
546
+ "text/plain": [
547
+ "Evaluation(is_acceptable=True, feedback=\"The response is great. It is succinct, professional, and engaging, just as requested. It's a very good answer.\")"
548
+ ]
549
+ },
550
+ "execution_count": 18,
551
+ "metadata": {},
552
+ "output_type": "execute_result"
553
+ }
554
+ ],
555
+ "source": [
556
+ "evaluate(reply, \"do you hold a patent?\", messages[:1])"
557
+ ]
558
+ },
559
+ {
560
+ "cell_type": "code",
561
+ "execution_count": 19,
562
+ "metadata": {},
563
+ "outputs": [],
564
+ "source": [
565
+ "def rerun(reply, message, history, feedback):\n",
566
+ " updated_system_prompt = system_prompt + \"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n",
567
+ " updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n",
568
+ " updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n",
569
+ " messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
570
+ " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
571
+ " return response.choices[0].message.content"
572
+ ]
573
+ },
574
+ {
575
+ "cell_type": "code",
576
+ "execution_count": 27,
577
+ "metadata": {},
578
+ "outputs": [],
579
+ "source": [
580
+ "def chat(message, history):\n",
581
+ " if \"patent\" in message:\n",
582
+ " system = system_prompt + \"\\n\\nEverything in your reply needs to be in pig latin - \\\n",
583
+ " it is mandatory that you respond only and entirely in pig latin\"\n",
584
+ " else:\n",
585
+ " system = system_prompt\n",
586
+ " messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n",
587
+ " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
588
+ " reply =response.choices[0].message.content\n",
589
+ " \n",
590
+ " evaluation = evaluate(reply, message, history)\n",
591
+ " \n",
592
+ " if evaluation.is_acceptable:\n",
593
+ " print(\"Passed evaluation - returning reply\")\n",
594
+ " else:\n",
595
+ " print(\"Failed evaluation - retrying\")\n",
596
+ " print(evaluation.feedback)\n",
597
+ " reply = rerun(reply, message, history, evaluation.feedback) \n",
598
+ " return reply"
599
+ ]
600
+ },
601
+ {
602
+ "cell_type": "code",
603
+ "execution_count": 28,
604
+ "metadata": {},
605
+ "outputs": [
606
+ {
607
+ "name": "stdout",
608
+ "output_type": "stream",
609
+ "text": [
610
+ "* Running on local URL: http://127.0.0.1:7864\n",
611
+ "* To create a public link, set `share=True` in `launch()`.\n"
612
+ ]
613
+ },
614
+ {
615
+ "data": {
616
+ "text/html": [
617
+ "<div><iframe src=\"http://127.0.0.1:7864/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
618
+ ],
619
+ "text/plain": [
620
+ "<IPython.core.display.HTML object>"
621
+ ]
622
+ },
623
+ "metadata": {},
624
+ "output_type": "display_data"
625
+ },
626
+ {
627
+ "data": {
628
+ "text/plain": []
629
+ },
630
+ "execution_count": 28,
631
+ "metadata": {},
632
+ "output_type": "execute_result"
633
+ },
634
+ {
635
+ "name": "stdout",
636
+ "output_type": "stream",
637
+ "text": [
638
+ "Failed evaluation - retrying\n",
639
+ "This is not an appropriate response from the agent, this sounds like the agent is speaking pig latin. I have set this to unacceptable.\n"
640
+ ]
641
+ }
642
+ ],
643
+ "source": [
644
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
645
+ ]
646
+ },
647
+ {
648
+ "cell_type": "markdown",
649
+ "metadata": {},
650
+ "source": []
651
+ },
652
+ {
653
+ "cell_type": "code",
654
+ "execution_count": null,
655
+ "metadata": {},
656
+ "outputs": [],
657
+ "source": []
658
+ }
659
+ ],
660
+ "metadata": {
661
+ "kernelspec": {
662
+ "display_name": ".venv",
663
+ "language": "python",
664
+ "name": "python3"
665
+ },
666
+ "language_info": {
667
+ "codemirror_mode": {
668
+ "name": "ipython",
669
+ "version": 3
670
+ },
671
+ "file_extension": ".py",
672
+ "mimetype": "text/x-python",
673
+ "name": "python",
674
+ "nbconvert_exporter": "python",
675
+ "pygments_lexer": "ipython3",
676
+ "version": "3.12.10"
677
+ }
678
+ },
679
+ "nbformat": 4,
680
+ "nbformat_minor": 2
681
+ }
4_lab4.ipynb ADDED
@@ -0,0 +1,463 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## The first big project - Professionally You!\n",
8
+ "\n",
9
+ "### And, Tool use.\n",
10
+ "\n",
11
+ "### But first: introducing Pushover\n",
12
+ "\n",
13
+ "Pushover is a nifty tool for sending Push Notifications to your phone.\n",
14
+ "\n",
15
+ "It's super easy to set up and install!\n",
16
+ "\n",
17
+ "Simply visit https://pushover.net/ and click 'Login or Signup' on the top right to sign up for a free account, and create your API keys.\n",
18
+ "\n",
19
+ "Once you've signed up, on the home screen, click \"Create an Application/API Token\", and give it any name (like Agents) and click Create Application.\n",
20
+ "\n",
21
+ "Then add 2 lines to your `.env` file:\n",
22
+ "\n",
23
+ "PUSHOVER_USER=_put the key that's on the top right of your Pushover home screen and probably starts with a u_ \n",
24
+ "PUSHOVER_TOKEN=_put the key when you click into your new application called Agents (or whatever) and probably starts with an a_\n",
25
+ "\n",
26
+ "Remember to save your `.env` file, and run `load_dotenv(override=True)` after saving, to set your environment variables.\n",
27
+ "\n",
28
+ "Finally, click \"Add Phone, Tablet or Desktop\" to install on your phone."
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": null,
34
+ "metadata": {},
35
+ "outputs": [],
36
+ "source": [
37
+ "# imports\n",
38
+ "\n",
39
+ "from dotenv import load_dotenv\n",
40
+ "from openai import OpenAI\n",
41
+ "import json\n",
42
+ "import os\n",
43
+ "import requests\n",
44
+ "from pypdf import PdfReader\n",
45
+ "import gradio as gr"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "code",
50
+ "execution_count": null,
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "# The usual start\n",
55
+ "\n",
56
+ "load_dotenv(override=True)\n",
57
+ "openai = OpenAI()"
58
+ ]
59
+ },
60
+ {
61
+ "cell_type": "code",
62
+ "execution_count": null,
63
+ "metadata": {},
64
+ "outputs": [],
65
+ "source": [
66
+ "# For pushover\n",
67
+ "\n",
68
+ "pushover_user = os.getenv(\"PUSHOVER_USER\")\n",
69
+ "pushover_token = os.getenv(\"PUSHOVER_TOKEN\")\n",
70
+ "pushover_url = \"https://api.pushover.net/1/messages.json\"\n",
71
+ "\n",
72
+ "if pushover_user:\n",
73
+ " print(f\"Pushover user found and starts with {pushover_user[0]}\")\n",
74
+ "else:\n",
75
+ " print(\"Pushover user not found\")\n",
76
+ "\n",
77
+ "if pushover_token:\n",
78
+ " print(f\"Pushover token found and starts with {pushover_token[0]}\")\n",
79
+ "else:\n",
80
+ " print(\"Pushover token not found\")"
81
+ ]
82
+ },
83
+ {
84
+ "cell_type": "code",
85
+ "execution_count": null,
86
+ "metadata": {},
87
+ "outputs": [],
88
+ "source": [
89
+ "def push(message):\n",
90
+ " print(f\"Push: {message}\")\n",
91
+ " payload = {\"user\": pushover_user, \"token\": pushover_token, \"message\": message}\n",
92
+ " requests.post(pushover_url, data=payload)"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "push(\"Call John\")"
102
+ ]
103
+ },
104
+ {
105
+ "cell_type": "code",
106
+ "execution_count": null,
107
+ "metadata": {},
108
+ "outputs": [],
109
+ "source": [
110
+ "def record_user_details(email, first_name, name, notes):\n",
111
+ " push(f\"Recording interest from {first_name, name} with email {email} and notes {notes}\")\n",
112
+ " return {\"recorded\": \"ok\"}\n",
113
+ "\n",
114
+ "def record_unknown_question(question):\n",
115
+ " push(f\"Recording unknown question: {question}\")\n",
116
+ " return {\"recorded\": \"ok\"}\n",
117
+ "\n",
118
+ "def handle_tool_call(tool_calls):\n",
119
+ " results = []\n",
120
+ " for tool_call in tool_calls:\n",
121
+ " tool_name = tool_call.function.name"
122
+ ]
123
+ },
124
+ {
125
+ "cell_type": "code",
126
+ "execution_count": null,
127
+ "metadata": {},
128
+ "outputs": [],
129
+ "source": [
130
+ "def record_unknown_question(question):\n",
131
+ " push(f\"Recording {question} asked that I couldn't answer\")\n",
132
+ " return {\"recorded\": \"ok\"}"
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "code",
137
+ "execution_count": null,
138
+ "metadata": {},
139
+ "outputs": [],
140
+ "source": [
141
+ "record_user_details_json = {\n",
142
+ " \"name\": \"record_user_details\", \n",
143
+ " \"description\": \"Use this tool to record that a user is interested in being in touch and provided first name, last name and email\",\n",
144
+ " \"parameters\": {\n",
145
+ " \"type\": \"object\",\n",
146
+ " \"properties\": {\n",
147
+ " \"email\": {\n",
148
+ " \"type\": \"string\",\n",
149
+ " \"description\": \"The email address of this user\"\n",
150
+ " },\n",
151
+ " \"first_name\": {\n",
152
+ " \"type\": \"string\",\n",
153
+ " \"description\": \"The user's first name\"\n",
154
+ " },\n",
155
+ " \"name\": {\n",
156
+ " \"type\": \"string\",\n",
157
+ " \"description\": \"The user's last name or full name\"\n",
158
+ " },\n",
159
+ " \"notes\": {\n",
160
+ " \"type\": \"string\",\n",
161
+ " \"description\": \"Any additional information about the conversation that's worth recording to give context\"\n",
162
+ " }\n",
163
+ " },\n",
164
+ " \"required\": [\"email\", \"first_name\", \"name\"], # ← HERE! Add this line\n",
165
+ " \"additionalProperties\": False\n",
166
+ " }\n",
167
+ "}"
168
+ ]
169
+ },
170
+ {
171
+ "cell_type": "code",
172
+ "execution_count": null,
173
+ "metadata": {},
174
+ "outputs": [],
175
+ "source": [
176
+ "record_unknown_question_json = {\n",
177
+ " \"name\": \"record_unknown_question\",\n",
178
+ " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
179
+ " \"parameters\": {\n",
180
+ " \"type\": \"object\",\n",
181
+ " \"properties\": {\n",
182
+ " \"question\": {\n",
183
+ " \"type\": \"string\",\n",
184
+ " \"description\": \"The question that couldn't be answered\"\n",
185
+ " },\n",
186
+ " },\n",
187
+ " \"required\": [\"question\"],\n",
188
+ " \"additionalProperties\": False\n",
189
+ " }\n",
190
+ "}"
191
+ ]
192
+ },
193
+ {
194
+ "cell_type": "code",
195
+ "execution_count": null,
196
+ "metadata": {},
197
+ "outputs": [],
198
+ "source": [
199
+ "tools = [{\"type\": \"function\", \"function\": record_user_details_json},\n",
200
+ " {\"type\": \"function\", \"function\": record_unknown_question_json}]"
201
+ ]
202
+ },
203
+ {
204
+ "cell_type": "code",
205
+ "execution_count": null,
206
+ "metadata": {},
207
+ "outputs": [],
208
+ "source": [
209
+ "tools"
210
+ ]
211
+ },
212
+ {
213
+ "cell_type": "code",
214
+ "execution_count": null,
215
+ "metadata": {},
216
+ "outputs": [],
217
+ "source": [
218
+ "# This function can take a list of tool calls, and run them. This is the IF statement!!\n",
219
+ "\n",
220
+ "def handle_tool_calls(tool_calls):\n",
221
+ " results = []\n",
222
+ " for tool_call in tool_calls:\n",
223
+ " tool_name = tool_call.function.name\n",
224
+ " arguments = json.loads(tool_call.function.arguments)\n",
225
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
226
+ "\n",
227
+ " # THE BIG IF STATEMENT!!!\n",
228
+ "\n",
229
+ " if tool_name == \"record_user_details\":\n",
230
+ " result = record_user_details(**arguments)\n",
231
+ " elif tool_name == \"record_unknown_question\":\n",
232
+ " result = record_unknown_question(**arguments)\n",
233
+ "\n",
234
+ " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
235
+ " return results"
236
+ ]
237
+ },
238
+ {
239
+ "cell_type": "code",
240
+ "execution_count": null,
241
+ "metadata": {},
242
+ "outputs": [],
243
+ "source": [
244
+ "globals()[\"record_unknown_question\"](\"this is a really hard question\")"
245
+ ]
246
+ },
247
+ {
248
+ "cell_type": "code",
249
+ "execution_count": null,
250
+ "metadata": {},
251
+ "outputs": [],
252
+ "source": [
253
+ "# This is a more elegant way that avoids the IF statement.\n",
254
+ "\n",
255
+ "def handle_tool_calls(tool_calls):\n",
256
+ " results = []\n",
257
+ " for tool_call in tool_calls:\n",
258
+ " tool_name = tool_call.function.name\n",
259
+ " arguments = json.loads(tool_call.function.arguments)\n",
260
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
261
+ " tool = globals().get(tool_name)\n",
262
+ " result = tool(**arguments) if tool else {}\n",
263
+ " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
264
+ " return results"
265
+ ]
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": null,
270
+ "metadata": {},
271
+ "outputs": [],
272
+ "source": [
273
+ "reader = PdfReader(\"me/linkedin.pdf\")\n",
274
+ "linkedin = \"\"\n",
275
+ "for page in reader.pages:\n",
276
+ " text = page.extract_text()\n",
277
+ " if text:\n",
278
+ " linkedin += text\n",
279
+ "\n",
280
+ "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
281
+ " summary = f.read()\n",
282
+ "\n",
283
+ "name = \"Alexandre Saadoun\""
284
+ ]
285
+ },
286
+ {
287
+ "cell_type": "code",
288
+ "execution_count": null,
289
+ "metadata": {},
290
+ "outputs": [],
291
+ "source": [
292
+ "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
293
+ "particularly questions related to {name}'s career, background, skills and experience. \\\n",
294
+ "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
295
+ "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
296
+ "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
297
+ "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \\\n",
298
+ "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \"\n",
299
+ "\n",
300
+ "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
301
+ "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
302
+ ]
303
+ },
304
+ {
305
+ "cell_type": "code",
306
+ "execution_count": null,
307
+ "metadata": {},
308
+ "outputs": [],
309
+ "source": [
310
+ "def chat(message, history):\n",
311
+ " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
312
+ " done = False\n",
313
+ " while not done:\n",
314
+ "\n",
315
+ " # This is the call to the LLM - see that we pass in the tools json\n",
316
+ "\n",
317
+ " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, tools=tools)\n",
318
+ "\n",
319
+ " finish_reason = response.choices[0].finish_reason\n",
320
+ " \n",
321
+ " # If the LLM wants to call a tool, we do that!\n",
322
+ " \n",
323
+ " if finish_reason==\"tool_calls\":\n",
324
+ " message = response.choices[0].message\n",
325
+ " tool_calls = message.tool_calls\n",
326
+ " results = handle_tool_calls(tool_calls)\n",
327
+ " messages.append(message)\n",
328
+ " messages.extend(results)\n",
329
+ " else:\n",
330
+ " done = True\n",
331
+ " return response.choices[0].message.content"
332
+ ]
333
+ },
334
+ {
335
+ "cell_type": "code",
336
+ "execution_count": null,
337
+ "metadata": {},
338
+ "outputs": [],
339
+ "source": [
340
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
341
+ ]
342
+ },
343
+ {
344
+ "cell_type": "markdown",
345
+ "metadata": {},
346
+ "source": [
347
+ "## And now for deployment\n",
348
+ "\n",
349
+ "This code is in `app.py`\n",
350
+ "\n",
351
+ "We will deploy to HuggingFace Spaces. Thank you student Robert M for improving these instructions.\n",
352
+ "\n",
353
+ "Before you start: remember to update the files in the \"me\" directory - your LinkedIn profile and summary.txt - so that it talks about you! \n",
354
+ "Also check that there's no README file within the 1_foundations directory. If there is one, please delete it. The deploy process creates a new README file in this directory for you.\n",
355
+ "\n",
356
+ "1. Visit https://huggingface.co and set up an account \n",
357
+ "2. From the Avatar menu on the top right, choose Access Tokens. Choose \"Create New Token\". Give it WRITE permissions.\n",
358
+ "3. Take this token and add it to your .env file: `HF_TOKEN=hf_xxx` and see note below if this token doesn't seem to get picked up during deployment \n",
359
+ "4. From the 1_foundations folder, enter: `uv run gradio deploy` and if for some reason this still wants you to enter your HF token, then interrupt it with ctrl+c and run this instead: `uv run dotenv -f ../.env run -- uv run gradio deploy` which forces your keys to all be set as environment variables \n",
360
+ "5. Follow its instructions: name it \"career_conversation\", specify app.py, choose cpu-basic as the hardware, say Yes to needing to supply secrets, provide your openai api key, your pushover user and token, and say \"no\" to github actions. \n",
361
+ "\n",
362
+ "#### Extra note about the HuggingFace token\n",
363
+ "\n",
364
+ "A couple of students have mentioned the HuggingFace doesn't detect their token, even though it's in the .env file. Here are things to try: \n",
365
+ "1. Restart Cursor \n",
366
+ "2. Rerun load_dotenv(override=True) and use a new terminal (the + button on the top right of the Terminal) \n",
367
+ "3. In the Terminal, run this before the gradio deploy: `$env:HF_TOKEN = \"hf_XXXX\"` \n",
368
+ "Thank you James and Martins for these tips. \n",
369
+ "\n",
370
+ "#### More about these secrets:\n",
371
+ "\n",
372
+ "If you're confused by what's going on with these secrets: it just wants you to enter the key name and value for each of your secrets -- so you would enter: \n",
373
+ "`OPENAI_API_KEY` \n",
374
+ "Followed by: \n",
375
+ "`sk-proj-...` \n",
376
+ "\n",
377
+ "And if you don't want to set secrets this way, or something goes wrong with it, it's no problem - you can change your secrets later: \n",
378
+ "1. Log in to HuggingFace website \n",
379
+ "2. Go to your profile screen via the Avatar menu on the top right \n",
380
+ "3. Select the Space you deployed \n",
381
+ "4. Click on the Settings wheel on the top right \n",
382
+ "5. You can scroll down to change your secrets, delete the space, etc.\n",
383
+ "\n",
384
+ "#### And now you should be deployed!\n",
385
+ "\n",
386
+ "Here is mine: https://huggingface.co/spaces/ed-donner/Career_Conversation\n",
387
+ "\n",
388
+ "I just got a push notification that a student asked me how they can become President of their country 😂😂\n",
389
+ "\n",
390
+ "For more information on deployment:\n",
391
+ "\n",
392
+ "https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\n",
393
+ "\n",
394
+ "To delete your Space in the future: \n",
395
+ "1. Log in to HuggingFace\n",
396
+ "2. From the Avatar menu, select your profile\n",
397
+ "3. Click on the Space itself and select the settings wheel on the top right\n",
398
+ "4. Scroll to the Delete section at the bottom\n",
399
+ "5. ALSO: delete the README file that Gradio may have created inside this 1_foundations folder (otherwise it won't ask you the questions the next time you do a gradio deploy)\n"
400
+ ]
401
+ },
402
+ {
403
+ "cell_type": "markdown",
404
+ "metadata": {},
405
+ "source": [
406
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
407
+ " <tr>\n",
408
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
409
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
410
+ " </td>\n",
411
+ " <td>\n",
412
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
413
+ " <span style=\"color:#ff7800;\">• First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..<br/>\n",
414
+ " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.<br/>\n",
415
+ " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?<br/>\n",
416
+ " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n",
417
+ " </span>\n",
418
+ " </td>\n",
419
+ " </tr>\n",
420
+ "</table>"
421
+ ]
422
+ },
423
+ {
424
+ "cell_type": "markdown",
425
+ "metadata": {},
426
+ "source": [
427
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
428
+ " <tr>\n",
429
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
430
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
431
+ " </td>\n",
432
+ " <td>\n",
433
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
434
+ " <span style=\"color:#00bfff;\">Aside from the obvious (your career alter-ego) this has business applications in any situation where you need an AI assistant with domain expertise and an ability to interact with the real world.\n",
435
+ " </span>\n",
436
+ " </td>\n",
437
+ " </tr>\n",
438
+ "</table>"
439
+ ]
440
+ }
441
+ ],
442
+ "metadata": {
443
+ "kernelspec": {
444
+ "display_name": ".venv",
445
+ "language": "python",
446
+ "name": "python3"
447
+ },
448
+ "language_info": {
449
+ "codemirror_mode": {
450
+ "name": "ipython",
451
+ "version": 3
452
+ },
453
+ "file_extension": ".py",
454
+ "mimetype": "text/x-python",
455
+ "name": "python",
456
+ "nbconvert_exporter": "python",
457
+ "pygments_lexer": "ipython3",
458
+ "version": "3.12.10"
459
+ }
460
+ },
461
+ "nbformat": 4,
462
+ "nbformat_minor": 2
463
+ }
README.md CHANGED
@@ -1,12 +1,6 @@
1
  ---
2
  title: BioChat2
3
- emoji: 🐨
4
- colorFrom: yellow
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 5.38.2
8
  app_file: app.py
9
- pinned: false
 
10
  ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: BioChat2
 
 
 
 
 
3
  app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 5.34.2
6
  ---
 
 
__pycache__/enhanced_app_rag.cpython-312.pyc ADDED
Binary file (20.3 kB). View file
 
bulk_loader_script.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Simple bulk loader for raw text summaries and reports
4
+ Just drop your .txt files in a folder and run this script
5
+ """
6
+
7
+ from enhanced_app_rag import Me
8
+ import os
9
+
10
+ def main():
11
+ # Initialize the RAG system
12
+ me = Me()
13
+
14
+ print("=== Simple RAG Text Loader ===\n")
15
+ print("ℹ️ Note: All files in me/ directory are automatically loaded on startup!")
16
+ print(" Just add .txt, .pdf, or .md files to me/ and restart the app.\n")
17
+
18
+ # Method 1: Load a single text file/summary/report
19
+ single_file = "data/summary.txt"
20
+ if os.path.exists(single_file):
21
+ print(f"Loading single file: {single_file}")
22
+ with open(single_file, 'r', encoding='utf-8') as f:
23
+ content = f.read()
24
+ me.bulk_load_text_content(content, "summary_report")
25
+
26
+ # Method 2: Load all .txt files from a directory
27
+ text_directory = "data/reports"
28
+ if os.path.exists(text_directory):
29
+ print(f"Loading all text files from: {text_directory}")
30
+ me.load_directory(text_directory)
31
+
32
+ # Method 3: Load specific files
33
+ specific_files = [
34
+ "data/project_summary.txt",
35
+ "data/technical_report.txt",
36
+ "data/meeting_notes.txt"
37
+ ]
38
+
39
+ existing_files = [f for f in specific_files if os.path.exists(f)]
40
+ if existing_files:
41
+ print(f"Loading {len(existing_files)} specific files...")
42
+ me.load_text_files(existing_files)
43
+
44
+ # Method 4: Load raw text directly (for testing)
45
+ sample_text = """
46
+ Alexandre completed a major project involving AI implementation
47
+ for a Fortune 500 company. The project improved efficiency by 40%
48
+ and was delivered 2 weeks ahead of schedule. Technologies used
49
+ included Python, TensorFlow, and cloud deployment on AWS.
50
+ """
51
+
52
+ print("Loading sample text content...")
53
+ me.bulk_load_text_content(sample_text, "sample_project_info")
54
+
55
+ # Method 5: Reload me/ directory if you added new files
56
+ print("\n💡 If you added new files to me/, you can reload them:")
57
+ print(" me.reload_me_directory()")
58
+
59
+ # Show final stats
60
+ print("\n=== Knowledge Base Stats ===")
61
+ me.get_knowledge_stats()
62
+
63
+ print("\n✅ Raw text loading completed!")
64
+ print("Your RAG system now has the text content available for chat.")
65
+
66
+ if __name__ == "__main__":
67
+ main()
community_contributions/1_lab1_Mudassar.ipynb ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# First Agentic AI workflow with OPENAI"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "#### And please do remember to contact me if I can help\n",
15
+ "\n",
16
+ "And I love to connect: https://www.linkedin.com/in/muhammad-mudassar-a65645192/"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "metadata": {},
22
+ "source": [
23
+ "## Import Libraries"
24
+ ]
25
+ },
26
+ {
27
+ "cell_type": "code",
28
+ "execution_count": 59,
29
+ "metadata": {},
30
+ "outputs": [],
31
+ "source": [
32
+ "import os\n",
33
+ "import re\n",
34
+ "from openai import OpenAI\n",
35
+ "from dotenv import load_dotenv\n",
36
+ "from IPython.display import Markdown, display"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": null,
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "load_dotenv(override=True)"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "code",
50
+ "execution_count": null,
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "openai_api_key=os.getenv(\"OPENAI_API_KEY\")\n",
55
+ "if openai_api_key:\n",
56
+ " print(f\"openai api key exists and begins {openai_api_key[:8]}\")\n",
57
+ "else:\n",
58
+ " print(\"OpenAI API Key not set - please head to the troubleshooting guide in the gui\")"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "markdown",
63
+ "metadata": {},
64
+ "source": [
65
+ "## Workflow with OPENAI"
66
+ ]
67
+ },
68
+ {
69
+ "cell_type": "code",
70
+ "execution_count": 21,
71
+ "metadata": {},
72
+ "outputs": [],
73
+ "source": [
74
+ "openai=OpenAI()"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 31,
80
+ "metadata": {},
81
+ "outputs": [],
82
+ "source": [
83
+ "message = [{'role':'user','content':\"what is 2+3?\"}]"
84
+ ]
85
+ },
86
+ {
87
+ "cell_type": "code",
88
+ "execution_count": null,
89
+ "metadata": {},
90
+ "outputs": [],
91
+ "source": [
92
+ "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
93
+ "print(response.choices[0].message.content)"
94
+ ]
95
+ },
96
+ {
97
+ "cell_type": "code",
98
+ "execution_count": 33,
99
+ "metadata": {},
100
+ "outputs": [],
101
+ "source": [
102
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
103
+ "message=[{'role':'user','content':question}]"
104
+ ]
105
+ },
106
+ {
107
+ "cell_type": "code",
108
+ "execution_count": null,
109
+ "metadata": {},
110
+ "outputs": [],
111
+ "source": [
112
+ "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
113
+ "question=response.choices[0].message.content\n",
114
+ "print(f\"Answer: {question}\")"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": 35,
120
+ "metadata": {},
121
+ "outputs": [],
122
+ "source": [
123
+ "message=[{'role':'user','content':question}]"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "code",
128
+ "execution_count": null,
129
+ "metadata": {},
130
+ "outputs": [],
131
+ "source": [
132
+ "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
133
+ "answer = response.choices[0].message.content\n",
134
+ "print(f\"Answer: {answer}\")"
135
+ ]
136
+ },
137
+ {
138
+ "cell_type": "code",
139
+ "execution_count": null,
140
+ "metadata": {},
141
+ "outputs": [],
142
+ "source": [
143
+ "# convert \\[ ... \\] to $$ ... $$, to properly render Latex\n",
144
+ "converted_answer = re.sub(r'\\\\[\\[\\]]', '$$', answer)\n",
145
+ "display(Markdown(converted_answer))"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "markdown",
150
+ "metadata": {},
151
+ "source": [
152
+ "## Exercise"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "markdown",
157
+ "metadata": {},
158
+ "source": [
159
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
160
+ " <tr>\n",
161
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
162
+ " <img src=\"../../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
163
+ " </td>\n",
164
+ " <td>\n",
165
+ " <span style=\"color:#ff7800;\">Now try this commercial application:<br/>\n",
166
+ " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.<br/>\n",
167
+ " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.<br/>\n",
168
+ " Finally have 3 third LLM call propose the Agentic AI solution.\n",
169
+ " </span>\n",
170
+ " </td>\n",
171
+ " </tr>\n",
172
+ "</table>"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "code",
177
+ "execution_count": 42,
178
+ "metadata": {},
179
+ "outputs": [],
180
+ "source": [
181
+ "message = [{'role':'user','content':\"give me a business area related to ecommerce that might be worth exploring for a agentic opportunity.\"}]"
182
+ ]
183
+ },
184
+ {
185
+ "cell_type": "code",
186
+ "execution_count": null,
187
+ "metadata": {},
188
+ "outputs": [],
189
+ "source": [
190
+ "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
191
+ "business_area = response.choices[0].message.content\n",
192
+ "business_area"
193
+ ]
194
+ },
195
+ {
196
+ "cell_type": "code",
197
+ "execution_count": null,
198
+ "metadata": {},
199
+ "outputs": [],
200
+ "source": [
201
+ "message = business_area + \"present a pain-point in that industry - something challenging that might be ripe for an agentic solutions.\"\n",
202
+ "message"
203
+ ]
204
+ },
205
+ {
206
+ "cell_type": "code",
207
+ "execution_count": null,
208
+ "metadata": {},
209
+ "outputs": [],
210
+ "source": [
211
+ "message = [{'role': 'user', 'content': message}]\n",
212
+ "response = openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
213
+ "question=response.choices[0].message.content\n",
214
+ "question"
215
+ ]
216
+ },
217
+ {
218
+ "cell_type": "code",
219
+ "execution_count": null,
220
+ "metadata": {},
221
+ "outputs": [],
222
+ "source": [
223
+ "message=[{'role':'user','content':question}]\n",
224
+ "response=openai.chat.completions.create(model=\"gpt-4o-mini\",messages=message)\n",
225
+ "answer=response.choices[0].message.content\n",
226
+ "print(answer)"
227
+ ]
228
+ },
229
+ {
230
+ "cell_type": "code",
231
+ "execution_count": null,
232
+ "metadata": {},
233
+ "outputs": [],
234
+ "source": [
235
+ "display(Markdown(answer))"
236
+ ]
237
+ }
238
+ ],
239
+ "metadata": {
240
+ "kernelspec": {
241
+ "display_name": ".venv",
242
+ "language": "python",
243
+ "name": "python3"
244
+ },
245
+ "language_info": {
246
+ "codemirror_mode": {
247
+ "name": "ipython",
248
+ "version": 3
249
+ },
250
+ "file_extension": ".py",
251
+ "mimetype": "text/x-python",
252
+ "name": "python",
253
+ "nbconvert_exporter": "python",
254
+ "pygments_lexer": "ipython3",
255
+ "version": "3.12.5"
256
+ }
257
+ },
258
+ "nbformat": 4,
259
+ "nbformat_minor": 2
260
+ }
community_contributions/1_lab1_Thanh.ipynb ADDED
@@ -0,0 +1,165 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Welcome to the start of your adventure in Agentic AI"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "### And please do remember to contact me if I can help\n",
15
+ "\n",
16
+ "And I love to connect: https://www.linkedin.com/in/eddonner/\n",
17
+ "\n",
18
+ "\n",
19
+ "### New to Notebooks like this one? Head over to the guides folder!\n",
20
+ "\n",
21
+ "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n",
22
+ "- Open extensions (View >> extensions)\n",
23
+ "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n",
24
+ "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n",
25
+ "Then View >> Explorer to bring back the File Explorer.\n",
26
+ "\n",
27
+ "And then:\n",
28
+ "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n",
29
+ "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n",
30
+ "3. Enjoy!\n",
31
+ "\n",
32
+ "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n",
33
+ "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n",
34
+ "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n",
35
+ "2. In the Settings search bar, type \"venv\" \n",
36
+ "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n",
37
+ "And then try again.\n",
38
+ "\n",
39
+ "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n",
40
+ "`conda deactivate` \n",
41
+ "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n",
42
+ "`conda config --set auto_activate_base false` \n",
43
+ "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version."
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": null,
49
+ "metadata": {},
50
+ "outputs": [],
51
+ "source": [
52
+ "from dotenv import load_dotenv\n",
53
+ "load_dotenv()"
54
+ ]
55
+ },
56
+ {
57
+ "cell_type": "code",
58
+ "execution_count": null,
59
+ "metadata": {},
60
+ "outputs": [],
61
+ "source": [
62
+ "# Check the keys\n",
63
+ "import google.generativeai as genai\n",
64
+ "import os\n",
65
+ "genai.configure(api_key=os.getenv('GOOGLE_API_KEY'))\n",
66
+ "model = genai.GenerativeModel(model_name=\"gemini-1.5-flash\")\n"
67
+ ]
68
+ },
69
+ {
70
+ "cell_type": "code",
71
+ "execution_count": null,
72
+ "metadata": {},
73
+ "outputs": [],
74
+ "source": [
75
+ "# Create a list of messages in the familiar Gemini GenAI format\n",
76
+ "\n",
77
+ "response = model.generate_content([\"2+2=?\"])\n",
78
+ "response.text"
79
+ ]
80
+ },
81
+ {
82
+ "cell_type": "code",
83
+ "execution_count": null,
84
+ "metadata": {},
85
+ "outputs": [],
86
+ "source": [
87
+ "# And now - let's ask for a question:\n",
88
+ "\n",
89
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
90
+ "\n",
91
+ "response = model.generate_content([question])\n",
92
+ "print(response.text)"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "from IPython.display import Markdown, display\n",
102
+ "\n",
103
+ "display(Markdown(response.text))"
104
+ ]
105
+ },
106
+ {
107
+ "cell_type": "markdown",
108
+ "metadata": {},
109
+ "source": [
110
+ "# Congratulations!\n",
111
+ "\n",
112
+ "That was a small, simple step in the direction of Agentic AI, with your new environment!\n",
113
+ "\n",
114
+ "Next time things get more interesting..."
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": null,
120
+ "metadata": {},
121
+ "outputs": [],
122
+ "source": [
123
+ "# First create the messages:\n",
124
+ "\n",
125
+ "messages = [{\"role\": \"user\", \"content\": \"Something here\"}]\n",
126
+ "\n",
127
+ "# Then make the first call:\n",
128
+ "\n",
129
+ "response =\n",
130
+ "\n",
131
+ "# Then read the business idea:\n",
132
+ "\n",
133
+ "business_idea = response.\n",
134
+ "\n",
135
+ "# And repeat!"
136
+ ]
137
+ },
138
+ {
139
+ "cell_type": "markdown",
140
+ "metadata": {},
141
+ "source": []
142
+ }
143
+ ],
144
+ "metadata": {
145
+ "kernelspec": {
146
+ "display_name": "llm_projects",
147
+ "language": "python",
148
+ "name": "python3"
149
+ },
150
+ "language_info": {
151
+ "codemirror_mode": {
152
+ "name": "ipython",
153
+ "version": 3
154
+ },
155
+ "file_extension": ".py",
156
+ "mimetype": "text/x-python",
157
+ "name": "python",
158
+ "nbconvert_exporter": "python",
159
+ "pygments_lexer": "ipython3",
160
+ "version": "3.10.15"
161
+ }
162
+ },
163
+ "nbformat": 4,
164
+ "nbformat_minor": 2
165
+ }
community_contributions/1_lab1_gemini.ipynb ADDED
@@ -0,0 +1,306 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Welcome to the start of your adventure in Agentic AI"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
15
+ " <tr>\n",
16
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
17
+ " <img src=\"../../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
18
+ " </td>\n",
19
+ " <td>\n",
20
+ " <h2 style=\"color:#ff7800;\">Are you ready for action??</h2>\n",
21
+ " <span style=\"color:#ff7800;\">Have you completed all the setup steps in the <a href=\"../setup/\">setup</a> folder?<br/>\n",
22
+ " Have you checked out the guides in the <a href=\"../guides/01_intro.ipynb\">guides</a> folder?<br/>\n",
23
+ " Well in that case, you're ready!!\n",
24
+ " </span>\n",
25
+ " </td>\n",
26
+ " </tr>\n",
27
+ "</table>"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "markdown",
32
+ "metadata": {},
33
+ "source": [
34
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
35
+ " <tr>\n",
36
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
37
+ " <img src=\"../../assets/tools.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
38
+ " </td>\n",
39
+ " <td>\n",
40
+ " <h2 style=\"color:#00bfff;\">Treat these labs as a resource</h2>\n",
41
+ " <span style=\"color:#00bfff;\">I push updates to the code regularly. When people ask questions or have problems, I incorporate it in the code, adding more examples or improved commentary. As a result, you'll notice that the code below isn't identical to the videos. Everything from the videos is here; but in addition, I've added more steps and better explanations. Consider this like an interactive book that accompanies the lectures.\n",
42
+ " </span>\n",
43
+ " </td>\n",
44
+ " </tr>\n",
45
+ "</table>"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "markdown",
50
+ "metadata": {},
51
+ "source": [
52
+ "### And please do remember to contact me if I can help\n",
53
+ "\n",
54
+ "And I love to connect: https://www.linkedin.com/in/eddonner/\n",
55
+ "\n",
56
+ "\n",
57
+ "### New to Notebooks like this one? Head over to the guides folder!\n",
58
+ "\n",
59
+ "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n",
60
+ "- Open extensions (View >> extensions)\n",
61
+ "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n",
62
+ "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n",
63
+ "Then View >> Explorer to bring back the File Explorer.\n",
64
+ "\n",
65
+ "And then:\n",
66
+ "1. Run `uv add google-genai` to install the Google Gemini library. (If you had started your environment before running this command, you will need to restart your environment in the Jupyter notebook.)\n",
67
+ "2. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n",
68
+ "3. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n",
69
+ "4. Enjoy!\n",
70
+ "\n",
71
+ "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n",
72
+ "1. From the Cursor menu, choose Settings >> VSCode Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n",
73
+ "2. In the Settings search bar, type \"venv\" \n",
74
+ "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n",
75
+ "And then try again.\n",
76
+ "\n",
77
+ "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n",
78
+ "`conda deactivate` \n",
79
+ "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n",
80
+ "`conda config --set auto_activate_base false` \n",
81
+ "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version."
82
+ ]
83
+ },
84
+ {
85
+ "cell_type": "code",
86
+ "execution_count": null,
87
+ "metadata": {},
88
+ "outputs": [],
89
+ "source": [
90
+ "# First let's do an import\n",
91
+ "from dotenv import load_dotenv\n"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": null,
97
+ "metadata": {},
98
+ "outputs": [],
99
+ "source": [
100
+ "# Next it's time to load the API keys into environment variables\n",
101
+ "\n",
102
+ "load_dotenv(override=True)"
103
+ ]
104
+ },
105
+ {
106
+ "cell_type": "code",
107
+ "execution_count": null,
108
+ "metadata": {},
109
+ "outputs": [],
110
+ "source": [
111
+ "# Check the keys\n",
112
+ "\n",
113
+ "import os\n",
114
+ "gemini_api_key = os.getenv('GEMINI_API_KEY')\n",
115
+ "\n",
116
+ "if gemini_api_key:\n",
117
+ " print(f\"Gemini API Key exists and begins {gemini_api_key[:8]}\")\n",
118
+ "else:\n",
119
+ " print(\"Gemini API Key not set - please head to the troubleshooting guide in the guides folder\")\n",
120
+ " \n"
121
+ ]
122
+ },
123
+ {
124
+ "cell_type": "code",
125
+ "execution_count": null,
126
+ "metadata": {},
127
+ "outputs": [],
128
+ "source": [
129
+ "# And now - the all important import statement\n",
130
+ "# If you get an import error - head over to troubleshooting guide\n",
131
+ "\n",
132
+ "from google import genai"
133
+ ]
134
+ },
135
+ {
136
+ "cell_type": "code",
137
+ "execution_count": null,
138
+ "metadata": {},
139
+ "outputs": [],
140
+ "source": [
141
+ "# And now we'll create an instance of the Gemini GenAI class\n",
142
+ "# If you're not sure what it means to create an instance of a class - head over to the guides folder!\n",
143
+ "# If you get a NameError - head over to the guides folder to learn about NameErrors\n",
144
+ "\n",
145
+ "client = genai.Client(api_key=gemini_api_key)"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "code",
150
+ "execution_count": null,
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "# Create a list of messages in the familiar Gemini GenAI format\n",
155
+ "\n",
156
+ "messages = [\"What is 2+2?\"]"
157
+ ]
158
+ },
159
+ {
160
+ "cell_type": "code",
161
+ "execution_count": null,
162
+ "metadata": {},
163
+ "outputs": [],
164
+ "source": [
165
+ "# And now call it! Any problems, head to the troubleshooting guide\n",
166
+ "\n",
167
+ "response = client.models.generate_content(\n",
168
+ " model=\"gemini-2.0-flash\", contents=messages\n",
169
+ ")\n",
170
+ "\n",
171
+ "print(response.text)\n"
172
+ ]
173
+ },
174
+ {
175
+ "cell_type": "code",
176
+ "execution_count": null,
177
+ "metadata": {},
178
+ "outputs": [],
179
+ "source": [
180
+ "\n",
181
+ "# Lets no create a challenging question\n",
182
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
183
+ "\n",
184
+ "# Ask the the model\n",
185
+ "response = client.models.generate_content(\n",
186
+ " model=\"gemini-2.0-flash\", contents=question\n",
187
+ ")\n",
188
+ "\n",
189
+ "question = response.text\n",
190
+ "\n",
191
+ "print(question)\n"
192
+ ]
193
+ },
194
+ {
195
+ "cell_type": "code",
196
+ "execution_count": null,
197
+ "metadata": {},
198
+ "outputs": [],
199
+ "source": [
200
+ "# Ask the models generated question to the model\n",
201
+ "response = client.models.generate_content(\n",
202
+ " model=\"gemini-2.0-flash\", contents=question\n",
203
+ ")\n",
204
+ "\n",
205
+ "# Extract the answer from the response\n",
206
+ "answer = response.text\n",
207
+ "\n",
208
+ "# Debug log the answer\n",
209
+ "print(answer)\n"
210
+ ]
211
+ },
212
+ {
213
+ "cell_type": "code",
214
+ "execution_count": null,
215
+ "metadata": {},
216
+ "outputs": [],
217
+ "source": [
218
+ "from IPython.display import Markdown, display\n",
219
+ "\n",
220
+ "# Nicely format the answer using Markdown\n",
221
+ "display(Markdown(answer))\n",
222
+ "\n"
223
+ ]
224
+ },
225
+ {
226
+ "cell_type": "markdown",
227
+ "metadata": {},
228
+ "source": [
229
+ "# Congratulations!\n",
230
+ "\n",
231
+ "That was a small, simple step in the direction of Agentic AI, with your new environment!\n",
232
+ "\n",
233
+ "Next time things get more interesting..."
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "markdown",
238
+ "metadata": {},
239
+ "source": [
240
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
241
+ " <tr>\n",
242
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
243
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
244
+ " </td>\n",
245
+ " <td>\n",
246
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
247
+ " <span style=\"color:#ff7800;\">Now try this commercial application:<br/>\n",
248
+ " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.<br/>\n",
249
+ " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.<br/>\n",
250
+ " Finally have 3 third LLM call propose the Agentic AI solution.\n",
251
+ " </span>\n",
252
+ " </td>\n",
253
+ " </tr>\n",
254
+ "</table>"
255
+ ]
256
+ },
257
+ {
258
+ "cell_type": "code",
259
+ "execution_count": null,
260
+ "metadata": {},
261
+ "outputs": [],
262
+ "source": [
263
+ "# First create the messages:\n",
264
+ "\n",
265
+ "\n",
266
+ "messages = [\"Something here\"]\n",
267
+ "\n",
268
+ "# Then make the first call:\n",
269
+ "\n",
270
+ "response =\n",
271
+ "\n",
272
+ "# Then read the business idea:\n",
273
+ "\n",
274
+ "business_idea = response.\n",
275
+ "\n",
276
+ "# And repeat!"
277
+ ]
278
+ },
279
+ {
280
+ "cell_type": "markdown",
281
+ "metadata": {},
282
+ "source": []
283
+ }
284
+ ],
285
+ "metadata": {
286
+ "kernelspec": {
287
+ "display_name": ".venv",
288
+ "language": "python",
289
+ "name": "python3"
290
+ },
291
+ "language_info": {
292
+ "codemirror_mode": {
293
+ "name": "ipython",
294
+ "version": 3
295
+ },
296
+ "file_extension": ".py",
297
+ "mimetype": "text/x-python",
298
+ "name": "python",
299
+ "nbconvert_exporter": "python",
300
+ "pygments_lexer": "ipython3",
301
+ "version": "3.12.10"
302
+ }
303
+ },
304
+ "nbformat": 4,
305
+ "nbformat_minor": 2
306
+ }
community_contributions/1_lab1_groq_llama.ipynb ADDED
@@ -0,0 +1,296 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# First Agentic AI workflow with Groq and Llama-3.3 LLM(Free of cost) "
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "# First let's do an import\n",
17
+ "from dotenv import load_dotenv"
18
+ ]
19
+ },
20
+ {
21
+ "cell_type": "code",
22
+ "execution_count": null,
23
+ "metadata": {},
24
+ "outputs": [],
25
+ "source": [
26
+ "# Next it's time to load the API keys into environment variables\n",
27
+ "\n",
28
+ "load_dotenv(override=True)"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": null,
34
+ "metadata": {},
35
+ "outputs": [],
36
+ "source": [
37
+ "# Check the Groq API key\n",
38
+ "\n",
39
+ "import os\n",
40
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
41
+ "\n",
42
+ "if groq_api_key:\n",
43
+ " print(f\"GROQ API Key exists and begins {groq_api_key[:8]}\")\n",
44
+ "else:\n",
45
+ " print(\"GROQ API Key not set\")\n",
46
+ " \n"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "code",
51
+ "execution_count": 4,
52
+ "metadata": {},
53
+ "outputs": [],
54
+ "source": [
55
+ "# And now - the all important import statement\n",
56
+ "# If you get an import error - head over to troubleshooting guide\n",
57
+ "\n",
58
+ "from groq import Groq"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": 5,
64
+ "metadata": {},
65
+ "outputs": [],
66
+ "source": [
67
+ "# Create a Groq instance\n",
68
+ "groq = Groq()"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 6,
74
+ "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "# Create a list of messages in the familiar Groq format\n",
78
+ "\n",
79
+ "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "code",
84
+ "execution_count": null,
85
+ "metadata": {},
86
+ "outputs": [],
87
+ "source": [
88
+ "# And now call it!\n",
89
+ "\n",
90
+ "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n",
91
+ "print(response.choices[0].message.content)\n"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": null,
97
+ "metadata": {},
98
+ "outputs": [],
99
+ "source": []
100
+ },
101
+ {
102
+ "cell_type": "code",
103
+ "execution_count": 8,
104
+ "metadata": {},
105
+ "outputs": [],
106
+ "source": [
107
+ "# And now - let's ask for a question:\n",
108
+ "\n",
109
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
110
+ "messages = [{\"role\": \"user\", \"content\": question}]\n"
111
+ ]
112
+ },
113
+ {
114
+ "cell_type": "code",
115
+ "execution_count": null,
116
+ "metadata": {},
117
+ "outputs": [],
118
+ "source": [
119
+ "# ask it\n",
120
+ "response = groq.chat.completions.create(\n",
121
+ " model=\"llama-3.3-70b-versatile\",\n",
122
+ " messages=messages\n",
123
+ ")\n",
124
+ "\n",
125
+ "question = response.choices[0].message.content\n",
126
+ "\n",
127
+ "print(question)\n"
128
+ ]
129
+ },
130
+ {
131
+ "cell_type": "code",
132
+ "execution_count": 10,
133
+ "metadata": {},
134
+ "outputs": [],
135
+ "source": [
136
+ "# form a new messages list\n",
137
+ "messages = [{\"role\": \"user\", \"content\": question}]\n"
138
+ ]
139
+ },
140
+ {
141
+ "cell_type": "code",
142
+ "execution_count": null,
143
+ "metadata": {},
144
+ "outputs": [],
145
+ "source": [
146
+ "# Ask it again\n",
147
+ "\n",
148
+ "response = groq.chat.completions.create(\n",
149
+ " model=\"llama-3.3-70b-versatile\",\n",
150
+ " messages=messages\n",
151
+ ")\n",
152
+ "\n",
153
+ "answer = response.choices[0].message.content\n",
154
+ "print(answer)\n"
155
+ ]
156
+ },
157
+ {
158
+ "cell_type": "code",
159
+ "execution_count": null,
160
+ "metadata": {},
161
+ "outputs": [],
162
+ "source": [
163
+ "from IPython.display import Markdown, display\n",
164
+ "\n",
165
+ "display(Markdown(answer))\n",
166
+ "\n"
167
+ ]
168
+ },
169
+ {
170
+ "cell_type": "markdown",
171
+ "metadata": {},
172
+ "source": [
173
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
174
+ " <tr>\n",
175
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
176
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
177
+ " </td>\n",
178
+ " <td>\n",
179
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
180
+ " <span style=\"color:#ff7800;\">Now try this commercial application:<br/>\n",
181
+ " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.<br/>\n",
182
+ " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.<br/>\n",
183
+ " Finally have 3 third LLM call propose the Agentic AI solution.\n",
184
+ " </span>\n",
185
+ " </td>\n",
186
+ " </tr>\n",
187
+ "</table>"
188
+ ]
189
+ },
190
+ {
191
+ "cell_type": "code",
192
+ "execution_count": 17,
193
+ "metadata": {},
194
+ "outputs": [],
195
+ "source": [
196
+ "# First create the messages:\n",
197
+ "\n",
198
+ "messages = [{\"role\": \"user\", \"content\": \"Give me a business area that might be ripe for an Agentic AI solution.\"}]\n",
199
+ "\n",
200
+ "# Then make the first call:\n",
201
+ "\n",
202
+ "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n",
203
+ "\n",
204
+ "# Then read the business idea:\n",
205
+ "\n",
206
+ "business_idea = response.choices[0].message.content\n",
207
+ "\n",
208
+ "\n",
209
+ "# And repeat!"
210
+ ]
211
+ },
212
+ {
213
+ "cell_type": "code",
214
+ "execution_count": null,
215
+ "metadata": {},
216
+ "outputs": [],
217
+ "source": [
218
+ "\n",
219
+ "display(Markdown(business_idea))"
220
+ ]
221
+ },
222
+ {
223
+ "cell_type": "code",
224
+ "execution_count": 19,
225
+ "metadata": {},
226
+ "outputs": [],
227
+ "source": [
228
+ "# Update the message with the business idea from previous step\n",
229
+ "messages = [{\"role\": \"user\", \"content\": \"What is the pain point in the business area of \" + business_idea + \"?\"}]"
230
+ ]
231
+ },
232
+ {
233
+ "cell_type": "code",
234
+ "execution_count": 20,
235
+ "metadata": {},
236
+ "outputs": [],
237
+ "source": [
238
+ "# Make the second call\n",
239
+ "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n",
240
+ "# Read the pain point\n",
241
+ "pain_point = response.choices[0].message.content\n"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "code",
246
+ "execution_count": null,
247
+ "metadata": {},
248
+ "outputs": [],
249
+ "source": [
250
+ "display(Markdown(pain_point))\n"
251
+ ]
252
+ },
253
+ {
254
+ "cell_type": "code",
255
+ "execution_count": null,
256
+ "metadata": {},
257
+ "outputs": [],
258
+ "source": [
259
+ "# Make the third call\n",
260
+ "messages = [{\"role\": \"user\", \"content\": \"What is the Agentic AI solution for the pain point of \" + pain_point + \"?\"}]\n",
261
+ "response = groq.chat.completions.create(model='llama-3.3-70b-versatile', messages=messages)\n",
262
+ "# Read the agentic solution\n",
263
+ "agentic_solution = response.choices[0].message.content\n",
264
+ "display(Markdown(agentic_solution))"
265
+ ]
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": null,
270
+ "metadata": {},
271
+ "outputs": [],
272
+ "source": []
273
+ }
274
+ ],
275
+ "metadata": {
276
+ "kernelspec": {
277
+ "display_name": ".venv",
278
+ "language": "python",
279
+ "name": "python3"
280
+ },
281
+ "language_info": {
282
+ "codemirror_mode": {
283
+ "name": "ipython",
284
+ "version": 3
285
+ },
286
+ "file_extension": ".py",
287
+ "mimetype": "text/x-python",
288
+ "name": "python",
289
+ "nbconvert_exporter": "python",
290
+ "pygments_lexer": "ipython3",
291
+ "version": "3.12.10"
292
+ }
293
+ },
294
+ "nbformat": 4,
295
+ "nbformat_minor": 2
296
+ }
community_contributions/1_lab1_open_router.ipynb ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Welcome to the start of your adventure in Agentic AI"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
15
+ " <tr>\n",
16
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
17
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
18
+ " </td>\n",
19
+ " <td>\n",
20
+ " <h2 style=\"color:#ff7800;\">Are you ready for action??</h2>\n",
21
+ " <span style=\"color:#ff7800;\">Have you completed all the setup steps in the <a href=\"../setup/\">setup</a> folder?<br/>\n",
22
+ " Have you checked out the guides in the <a href=\"../guides/01_intro.ipynb\">guides</a> folder?<br/>\n",
23
+ " Well in that case, you're ready!!\n",
24
+ " </span>\n",
25
+ " </td>\n",
26
+ " </tr>\n",
27
+ "</table>"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "markdown",
32
+ "metadata": {},
33
+ "source": [
34
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
35
+ " <tr>\n",
36
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
37
+ " <img src=\"../assets/tools.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
38
+ " </td>\n",
39
+ " <td>\n",
40
+ " <h2 style=\"color:#00bfff;\">This code is a live resource - keep an eye out for my updates</h2>\n",
41
+ " <span style=\"color:#00bfff;\">I push updates regularly. As people ask questions or have problems, I add more examples and improve explanations. As a result, the code below might not be identical to the videos, as I've added more steps and better comments. Consider this like an interactive book that accompanies the lectures.<br/><br/>\n",
42
+ " I try to send emails regularly with important updates related to the course. You can find this in the 'Announcements' section of Udemy in the left sidebar. You can also choose to receive my emails via your Notification Settings in Udemy. I'm respectful of your inbox and always try to add value with my emails!\n",
43
+ " </span>\n",
44
+ " </td>\n",
45
+ " </tr>\n",
46
+ "</table>"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "markdown",
51
+ "metadata": {},
52
+ "source": [
53
+ "### And please do remember to contact me if I can help\n",
54
+ "\n",
55
+ "And I love to connect: https://www.linkedin.com/in/eddonner/\n",
56
+ "\n",
57
+ "\n",
58
+ "### New to Notebooks like this one? Head over to the guides folder!\n",
59
+ "\n",
60
+ "Just to check you've already added the Python and Jupyter extensions to Cursor, if not already installed:\n",
61
+ "- Open extensions (View >> extensions)\n",
62
+ "- Search for python, and when the results show, click on the ms-python one, and Install it if not already installed\n",
63
+ "- Search for jupyter, and when the results show, click on the Microsoft one, and Install it if not already installed \n",
64
+ "Then View >> Explorer to bring back the File Explorer.\n",
65
+ "\n",
66
+ "And then:\n",
67
+ "1. Click where it says \"Select Kernel\" near the top right, and select the option called `.venv (Python 3.12.9)` or similar, which should be the first choice or the most prominent choice. You may need to choose \"Python Environments\" first.\n",
68
+ "2. Click in each \"cell\" below, starting with the cell immediately below this text, and press Shift+Enter to run\n",
69
+ "3. Enjoy!\n",
70
+ "\n",
71
+ "After you click \"Select Kernel\", if there is no option like `.venv (Python 3.12.9)` then please do the following: \n",
72
+ "1. On Mac: From the Cursor menu, choose Settings >> VS Code Settings (NOTE: be sure to select `VSCode Settings` not `Cursor Settings`); \n",
73
+ "On Windows PC: From the File menu, choose Preferences >> VS Code Settings(NOTE: be sure to select `VSCode Settings` not `Cursor Settings`) \n",
74
+ "2. In the Settings search bar, type \"venv\" \n",
75
+ "3. In the field \"Path to folder with a list of Virtual Environments\" put the path to the project root, like C:\\Users\\username\\projects\\agents (on a Windows PC) or /Users/username/projects/agents (on Mac or Linux). \n",
76
+ "And then try again.\n",
77
+ "\n",
78
+ "Having problems with missing Python versions in that list? Have you ever used Anaconda before? It might be interferring. Quit Cursor, bring up a new command line, and make sure that your Anaconda environment is deactivated: \n",
79
+ "`conda deactivate` \n",
80
+ "And if you still have any problems with conda and python versions, it's possible that you will need to run this too: \n",
81
+ "`conda config --set auto_activate_base false` \n",
82
+ "and then from within the Agents directory, you should be able to run `uv python list` and see the Python 3.12 version."
83
+ ]
84
+ },
85
+ {
86
+ "cell_type": "code",
87
+ "execution_count": 76,
88
+ "metadata": {},
89
+ "outputs": [],
90
+ "source": [
91
+ "# First let's do an import\n",
92
+ "from dotenv import load_dotenv\n"
93
+ ]
94
+ },
95
+ {
96
+ "cell_type": "code",
97
+ "execution_count": null,
98
+ "metadata": {},
99
+ "outputs": [],
100
+ "source": [
101
+ "# Next it's time to load the API keys into environment variables\n",
102
+ "\n",
103
+ "load_dotenv(override=True)"
104
+ ]
105
+ },
106
+ {
107
+ "cell_type": "code",
108
+ "execution_count": null,
109
+ "metadata": {},
110
+ "outputs": [],
111
+ "source": [
112
+ "# Check the keys\n",
113
+ "\n",
114
+ "import os\n",
115
+ "open_router_api_key = os.getenv('OPEN_ROUTER_API_KEY')\n",
116
+ "\n",
117
+ "if open_router_api_key:\n",
118
+ " print(f\"Open router API Key exists and begins {open_router_api_key[:8]}\")\n",
119
+ "else:\n",
120
+ " print(\"Open router API Key not set - please head to the troubleshooting guide in the setup folder\")\n"
121
+ ]
122
+ },
123
+ {
124
+ "cell_type": "code",
125
+ "execution_count": 79,
126
+ "metadata": {},
127
+ "outputs": [],
128
+ "source": [
129
+ "from openai import OpenAI"
130
+ ]
131
+ },
132
+ {
133
+ "cell_type": "code",
134
+ "execution_count": 80,
135
+ "metadata": {},
136
+ "outputs": [],
137
+ "source": [
138
+ "# Initialize the client to point at OpenRouter instead of OpenAI\n",
139
+ "# You can use the exact same OpenAI Python package—just swap the base_url!\n",
140
+ "client = OpenAI(\n",
141
+ " base_url=\"https://openrouter.ai/api/v1\",\n",
142
+ " api_key=open_router_api_key\n",
143
+ ")"
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": 81,
149
+ "metadata": {},
150
+ "outputs": [],
151
+ "source": [
152
+ "messages = [{\"role\": \"user\", \"content\": \"What is 2+2?\"}]"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "execution_count": null,
158
+ "metadata": {},
159
+ "outputs": [],
160
+ "source": [
161
+ "client = OpenAI(\n",
162
+ " base_url=\"https://openrouter.ai/api/v1\",\n",
163
+ " api_key=open_router_api_key\n",
164
+ ")\n",
165
+ "\n",
166
+ "resp = client.chat.completions.create(\n",
167
+ " # Select a model from https://openrouter.ai/models and provide the model name here\n",
168
+ " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n",
169
+ " messages=messages\n",
170
+ ")\n",
171
+ "print(resp.choices[0].message.content)"
172
+ ]
173
+ },
174
+ {
175
+ "cell_type": "code",
176
+ "execution_count": 83,
177
+ "metadata": {},
178
+ "outputs": [],
179
+ "source": [
180
+ "# And now - let's ask for a question:\n",
181
+ "\n",
182
+ "question = \"Please propose a hard, challenging question to assess someone's IQ. Respond only with the question.\"\n",
183
+ "messages = [{\"role\": \"user\", \"content\": question}]"
184
+ ]
185
+ },
186
+ {
187
+ "cell_type": "code",
188
+ "execution_count": null,
189
+ "metadata": {},
190
+ "outputs": [],
191
+ "source": [
192
+ "response = client.chat.completions.create(\n",
193
+ " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n",
194
+ " messages=messages\n",
195
+ ")\n",
196
+ "\n",
197
+ "question = response.choices[0].message.content\n",
198
+ "\n",
199
+ "print(question)"
200
+ ]
201
+ },
202
+ {
203
+ "cell_type": "code",
204
+ "execution_count": 85,
205
+ "metadata": {},
206
+ "outputs": [],
207
+ "source": [
208
+ "# form a new messages list\n",
209
+ "\n",
210
+ "messages = [{\"role\": \"user\", \"content\": question}]\n"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "code",
215
+ "execution_count": null,
216
+ "metadata": {},
217
+ "outputs": [],
218
+ "source": [
219
+ "# Ask it again\n",
220
+ "\n",
221
+ "response = client.chat.completions.create(\n",
222
+ " model=\"meta-llama/llama-3.3-8b-instruct:free\",\n",
223
+ " messages=messages\n",
224
+ ")\n",
225
+ "\n",
226
+ "answer = response.choices[0].message.content\n",
227
+ "print(answer)"
228
+ ]
229
+ },
230
+ {
231
+ "cell_type": "code",
232
+ "execution_count": null,
233
+ "metadata": {},
234
+ "outputs": [],
235
+ "source": [
236
+ "from IPython.display import Markdown, display\n",
237
+ "\n",
238
+ "display(Markdown(answer))\n",
239
+ "\n"
240
+ ]
241
+ },
242
+ {
243
+ "cell_type": "markdown",
244
+ "metadata": {},
245
+ "source": [
246
+ "# Congratulations!\n",
247
+ "\n",
248
+ "That was a small, simple step in the direction of Agentic AI, with your new environment!\n",
249
+ "\n",
250
+ "Next time things get more interesting..."
251
+ ]
252
+ },
253
+ {
254
+ "cell_type": "markdown",
255
+ "metadata": {},
256
+ "source": [
257
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
258
+ " <tr>\n",
259
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
260
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
261
+ " </td>\n",
262
+ " <td>\n",
263
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
264
+ " <span style=\"color:#ff7800;\">Now try this commercial application:<br/>\n",
265
+ " First ask the LLM to pick a business area that might be worth exploring for an Agentic AI opportunity.<br/>\n",
266
+ " Then ask the LLM to present a pain-point in that industry - something challenging that might be ripe for an Agentic solution.<br/>\n",
267
+ " Finally have 3 third LLM call propose the Agentic AI solution.\n",
268
+ " </span>\n",
269
+ " </td>\n",
270
+ " </tr>\n",
271
+ "</table>"
272
+ ]
273
+ },
274
+ {
275
+ "cell_type": "code",
276
+ "execution_count": null,
277
+ "metadata": {},
278
+ "outputs": [],
279
+ "source": [
280
+ "# First create the messages:\n",
281
+ "\n",
282
+ "\n",
283
+ "messages = [\"Something here\"]\n",
284
+ "\n",
285
+ "# Then make the first call:\n",
286
+ "\n",
287
+ "response =\n",
288
+ "\n",
289
+ "# Then read the business idea:\n",
290
+ "\n",
291
+ "business_idea = response.\n",
292
+ "\n",
293
+ "# And repeat!"
294
+ ]
295
+ },
296
+ {
297
+ "cell_type": "markdown",
298
+ "metadata": {},
299
+ "source": []
300
+ }
301
+ ],
302
+ "metadata": {
303
+ "kernelspec": {
304
+ "display_name": ".venv",
305
+ "language": "python",
306
+ "name": "python3"
307
+ },
308
+ "language_info": {
309
+ "codemirror_mode": {
310
+ "name": "ipython",
311
+ "version": 3
312
+ },
313
+ "file_extension": ".py",
314
+ "mimetype": "text/x-python",
315
+ "name": "python",
316
+ "nbconvert_exporter": "python",
317
+ "pygments_lexer": "ipython3",
318
+ "version": "3.12.7"
319
+ }
320
+ },
321
+ "nbformat": 4,
322
+ "nbformat_minor": 2
323
+ }
community_contributions/1_lab2_Kaushik_Parallelization.ipynb ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import os\n",
10
+ "import json\n",
11
+ "from dotenv import load_dotenv\n",
12
+ "from openai import OpenAI\n",
13
+ "from IPython.display import Markdown"
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "markdown",
18
+ "metadata": {},
19
+ "source": [
20
+ "### Refresh dot env"
21
+ ]
22
+ },
23
+ {
24
+ "cell_type": "code",
25
+ "execution_count": null,
26
+ "metadata": {},
27
+ "outputs": [],
28
+ "source": [
29
+ "load_dotenv(override=True)"
30
+ ]
31
+ },
32
+ {
33
+ "cell_type": "code",
34
+ "execution_count": 3,
35
+ "metadata": {},
36
+ "outputs": [],
37
+ "source": [
38
+ "open_api_key = os.getenv(\"OPENAI_API_KEY\")\n",
39
+ "google_api_key = os.getenv(\"GOOGLE_API_KEY\")"
40
+ ]
41
+ },
42
+ {
43
+ "cell_type": "markdown",
44
+ "metadata": {},
45
+ "source": [
46
+ "### Create initial query to get challange reccomendation"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "code",
51
+ "execution_count": 4,
52
+ "metadata": {},
53
+ "outputs": [],
54
+ "source": [
55
+ "query = 'Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. '\n",
56
+ "query += 'Answer only with the question, no explanation.'\n",
57
+ "\n",
58
+ "messages = [{'role':'user', 'content':query}]"
59
+ ]
60
+ },
61
+ {
62
+ "cell_type": "code",
63
+ "execution_count": null,
64
+ "metadata": {},
65
+ "outputs": [],
66
+ "source": [
67
+ "print(messages)"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "markdown",
72
+ "metadata": {},
73
+ "source": [
74
+ "### Call openai gpt-4o-mini "
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 6,
80
+ "metadata": {},
81
+ "outputs": [],
82
+ "source": [
83
+ "openai = OpenAI()\n",
84
+ "\n",
85
+ "response = openai.chat.completions.create(\n",
86
+ " messages=messages,\n",
87
+ " model='gpt-4o-mini'\n",
88
+ ")\n",
89
+ "\n",
90
+ "challange = response.choices[0].message.content\n"
91
+ ]
92
+ },
93
+ {
94
+ "cell_type": "code",
95
+ "execution_count": null,
96
+ "metadata": {},
97
+ "outputs": [],
98
+ "source": [
99
+ "print(challange)"
100
+ ]
101
+ },
102
+ {
103
+ "cell_type": "code",
104
+ "execution_count": 8,
105
+ "metadata": {},
106
+ "outputs": [],
107
+ "source": [
108
+ "competitors = []\n",
109
+ "answers = []"
110
+ ]
111
+ },
112
+ {
113
+ "cell_type": "markdown",
114
+ "metadata": {},
115
+ "source": [
116
+ "### Create messages with the challange query"
117
+ ]
118
+ },
119
+ {
120
+ "cell_type": "code",
121
+ "execution_count": 9,
122
+ "metadata": {},
123
+ "outputs": [],
124
+ "source": [
125
+ "messages = [{'role':'user', 'content':challange}]"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": null,
131
+ "metadata": {},
132
+ "outputs": [],
133
+ "source": [
134
+ "print(messages)"
135
+ ]
136
+ },
137
+ {
138
+ "cell_type": "code",
139
+ "execution_count": null,
140
+ "metadata": {},
141
+ "outputs": [],
142
+ "source": [
143
+ "!ollama pull llama3.2"
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": 12,
149
+ "metadata": {},
150
+ "outputs": [],
151
+ "source": [
152
+ "from threading import Thread"
153
+ ]
154
+ },
155
+ {
156
+ "cell_type": "code",
157
+ "execution_count": 13,
158
+ "metadata": {},
159
+ "outputs": [],
160
+ "source": [
161
+ "def gpt_mini_processor():\n",
162
+ " modleName = 'gpt-4o-mini'\n",
163
+ " competitors.append(modleName)\n",
164
+ " response_gpt = openai.chat.completions.create(\n",
165
+ " messages=messages,\n",
166
+ " model=modleName\n",
167
+ " )\n",
168
+ " answers.append(response_gpt.choices[0].message.content)\n",
169
+ "\n",
170
+ "def gemini_processor():\n",
171
+ " gemini = OpenAI(api_key=google_api_key, base_url='https://generativelanguage.googleapis.com/v1beta/openai/')\n",
172
+ " modleName = 'gemini-2.0-flash'\n",
173
+ " competitors.append(modleName)\n",
174
+ " response_gemini = gemini.chat.completions.create(\n",
175
+ " messages=messages,\n",
176
+ " model=modleName\n",
177
+ " )\n",
178
+ " answers.append(response_gemini.choices[0].message.content)\n",
179
+ "\n",
180
+ "def llama_processor():\n",
181
+ " ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
182
+ " modleName = 'llama3.2'\n",
183
+ " competitors.append(modleName)\n",
184
+ " response_llama = ollama.chat.completions.create(\n",
185
+ " messages=messages,\n",
186
+ " model=modleName\n",
187
+ " )\n",
188
+ " answers.append(response_llama.choices[0].message.content)"
189
+ ]
190
+ },
191
+ {
192
+ "cell_type": "markdown",
193
+ "metadata": {},
194
+ "source": [
195
+ "### Paraller execution of LLM calls"
196
+ ]
197
+ },
198
+ {
199
+ "cell_type": "code",
200
+ "execution_count": 14,
201
+ "metadata": {},
202
+ "outputs": [],
203
+ "source": [
204
+ "thread1 = Thread(target=gpt_mini_processor)\n",
205
+ "thread2 = Thread(target=gemini_processor)\n",
206
+ "thread3 = Thread(target=llama_processor)\n",
207
+ "\n",
208
+ "thread1.start()\n",
209
+ "thread2.start()\n",
210
+ "thread3.start()\n",
211
+ "\n",
212
+ "thread1.join()\n",
213
+ "thread2.join()\n",
214
+ "thread3.join()"
215
+ ]
216
+ },
217
+ {
218
+ "cell_type": "code",
219
+ "execution_count": null,
220
+ "metadata": {},
221
+ "outputs": [],
222
+ "source": [
223
+ "print(competitors)\n",
224
+ "print(answers)"
225
+ ]
226
+ },
227
+ {
228
+ "cell_type": "code",
229
+ "execution_count": null,
230
+ "metadata": {},
231
+ "outputs": [],
232
+ "source": [
233
+ "for competitor, answer in zip(competitors, answers):\n",
234
+ " print(f'Competitor:{competitor}\\n\\n{answer}')"
235
+ ]
236
+ },
237
+ {
238
+ "cell_type": "code",
239
+ "execution_count": 17,
240
+ "metadata": {},
241
+ "outputs": [],
242
+ "source": [
243
+ "together = ''\n",
244
+ "for index, answer in enumerate(answers):\n",
245
+ " together += f'# Response from competitor {index + 1}\\n\\n'\n",
246
+ " together += answer + '\\n\\n'"
247
+ ]
248
+ },
249
+ {
250
+ "cell_type": "code",
251
+ "execution_count": null,
252
+ "metadata": {},
253
+ "outputs": [],
254
+ "source": [
255
+ "print(together)"
256
+ ]
257
+ },
258
+ {
259
+ "cell_type": "markdown",
260
+ "metadata": {},
261
+ "source": [
262
+ "### Prompt to judge the LLM results"
263
+ ]
264
+ },
265
+ {
266
+ "cell_type": "code",
267
+ "execution_count": 19,
268
+ "metadata": {},
269
+ "outputs": [],
270
+ "source": [
271
+ "to_judge = f'''You are judging a competition between {len(competitors)} competitors.\n",
272
+ "Each model has been given this question:\n",
273
+ "\n",
274
+ "{challange}\n",
275
+ "\n",
276
+ "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n",
277
+ "Respond with JSON, and only JSON, with the following format:\n",
278
+ "{{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n",
279
+ "\n",
280
+ "Here are the responses from each competitor:\n",
281
+ "\n",
282
+ "{together}\n",
283
+ "\n",
284
+ "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n",
285
+ "\n",
286
+ "'''"
287
+ ]
288
+ },
289
+ {
290
+ "cell_type": "code",
291
+ "execution_count": 20,
292
+ "metadata": {},
293
+ "outputs": [],
294
+ "source": [
295
+ "to_judge_message = [{'role':'user', 'content':to_judge}]"
296
+ ]
297
+ },
298
+ {
299
+ "cell_type": "markdown",
300
+ "metadata": {},
301
+ "source": [
302
+ "### Execute o3-mini to analyze the LLM results"
303
+ ]
304
+ },
305
+ {
306
+ "cell_type": "code",
307
+ "execution_count": null,
308
+ "metadata": {},
309
+ "outputs": [],
310
+ "source": [
311
+ "openai = OpenAI()\n",
312
+ "response = openai.chat.completions.create(\n",
313
+ " messages=to_judge_message,\n",
314
+ " model='o3-mini'\n",
315
+ ")\n",
316
+ "result = response.choices[0].message.content\n",
317
+ "print(result)"
318
+ ]
319
+ },
320
+ {
321
+ "cell_type": "code",
322
+ "execution_count": null,
323
+ "metadata": {},
324
+ "outputs": [],
325
+ "source": [
326
+ "results_dict = json.loads(result)\n",
327
+ "ranks = results_dict[\"results\"]\n",
328
+ "for index, result in enumerate(ranks):\n",
329
+ " competitor = competitors[int(result)-1]\n",
330
+ " print(f\"Rank {index+1}: {competitor}\")"
331
+ ]
332
+ }
333
+ ],
334
+ "metadata": {
335
+ "kernelspec": {
336
+ "display_name": ".venv",
337
+ "language": "python",
338
+ "name": "python3"
339
+ },
340
+ "language_info": {
341
+ "codemirror_mode": {
342
+ "name": "ipython",
343
+ "version": 3
344
+ },
345
+ "file_extension": ".py",
346
+ "mimetype": "text/x-python",
347
+ "name": "python",
348
+ "nbconvert_exporter": "python",
349
+ "pygments_lexer": "ipython3",
350
+ "version": "3.12.10"
351
+ }
352
+ },
353
+ "nbformat": 4,
354
+ "nbformat_minor": 2
355
+ }
community_contributions/1_lab2_Routing_Workflow.ipynb ADDED
@@ -0,0 +1,514 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Judging and Routing — Optimizing Resource Usage by Evaluating Problem Complexity"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "markdown",
12
+ "metadata": {},
13
+ "source": [
14
+ "In the original Lab 2, we explored the **Orchestrator–Worker pattern**, where a planner sent the same question to multiple agents, and a judge assessed their responses to evaluate agent intelligence.\n",
15
+ "\n",
16
+ "In this notebook, we extend that design by adding multiple judges and a routing component to optimize model usage based on task complexity. "
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "metadata": {},
22
+ "source": [
23
+ "## Imports and Environment Setup"
24
+ ]
25
+ },
26
+ {
27
+ "cell_type": "code",
28
+ "execution_count": 1,
29
+ "metadata": {},
30
+ "outputs": [],
31
+ "source": [
32
+ "import os\n",
33
+ "import json\n",
34
+ "from dotenv import load_dotenv\n",
35
+ "from openai import OpenAI\n",
36
+ "from anthropic import Anthropic\n",
37
+ "from IPython.display import Markdown, display"
38
+ ]
39
+ },
40
+ {
41
+ "cell_type": "code",
42
+ "execution_count": null,
43
+ "metadata": {},
44
+ "outputs": [],
45
+ "source": [
46
+ "load_dotenv(override=True)\n",
47
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
48
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
49
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
50
+ "if openai_api_key and google_api_key and deepseek_api_key:\n",
51
+ " print(\"All keys were loaded successfully\")"
52
+ ]
53
+ },
54
+ {
55
+ "cell_type": "code",
56
+ "execution_count": null,
57
+ "metadata": {},
58
+ "outputs": [],
59
+ "source": [
60
+ "!ollama pull llama3.2\n",
61
+ "!ollama pull mistral"
62
+ ]
63
+ },
64
+ {
65
+ "cell_type": "markdown",
66
+ "metadata": {},
67
+ "source": [
68
+ "## Creating Models"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "markdown",
73
+ "metadata": {},
74
+ "source": [
75
+ "The notebook uses instances of GPT, Gemini and DeepSeek APIs, along with two local models served via Ollama: ```llama3.2``` and ```mistral```."
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "code",
80
+ "execution_count": 4,
81
+ "metadata": {},
82
+ "outputs": [],
83
+ "source": [
84
+ "model_specs = {\n",
85
+ " \"gpt-4o-mini\" : None,\n",
86
+ " \"gemini-2.0-flash\": {\n",
87
+ " \"api_key\" : google_api_key,\n",
88
+ " \"url\" : \"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
89
+ " },\n",
90
+ " \"deepseek-chat\" : {\n",
91
+ " \"api_key\" : deepseek_api_key,\n",
92
+ " \"url\" : \"https://api.deepseek.com/v1\"\n",
93
+ " },\n",
94
+ " \"llama3.2\" : {\n",
95
+ " \"api_key\" : \"ollama\",\n",
96
+ " \"url\" : \"http://localhost:11434/v1\"\n",
97
+ " },\n",
98
+ " \"mistral\" : {\n",
99
+ " \"api_key\" : \"ollama\",\n",
100
+ " \"url\" : \"http://localhost:11434/v1\"\n",
101
+ " }\n",
102
+ "}\n",
103
+ "\n",
104
+ "def create_model(model_name):\n",
105
+ " spec = model_specs[model_name]\n",
106
+ " if spec is None:\n",
107
+ " return OpenAI()\n",
108
+ " \n",
109
+ " return OpenAI(api_key=spec[\"api_key\"], base_url=spec[\"url\"])"
110
+ ]
111
+ },
112
+ {
113
+ "cell_type": "code",
114
+ "execution_count": 5,
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "orchestrator_model = \"gemini-2.0-flash\"\n",
119
+ "generator = create_model(orchestrator_model)\n",
120
+ "router = create_model(orchestrator_model)\n",
121
+ "\n",
122
+ "qa_models = {\n",
123
+ " model_name : create_model(model_name) \n",
124
+ " for model_name in model_specs.keys()\n",
125
+ "}\n",
126
+ "\n",
127
+ "judges = {\n",
128
+ " model_name : create_model(model_name) \n",
129
+ " for model_name, specs in model_specs.items() \n",
130
+ " if not(specs) or specs[\"api_key\"] != \"ollama\"\n",
131
+ "}"
132
+ ]
133
+ },
134
+ {
135
+ "cell_type": "markdown",
136
+ "metadata": {},
137
+ "source": [
138
+ "## Orchestrator-Worker Workflow"
139
+ ]
140
+ },
141
+ {
142
+ "cell_type": "markdown",
143
+ "metadata": {},
144
+ "source": [
145
+ "First, we generate a question to evaluate the intelligence of each LLM."
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "code",
150
+ "execution_count": null,
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs \"\n",
155
+ "request += \"to evaluate and rank them based on their intelligence. \" \n",
156
+ "request += \"Answer **only** with the question, no explanation or preamble.\"\n",
157
+ "\n",
158
+ "messages = [{\"role\": \"user\", \"content\": request}]\n",
159
+ "messages"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": 7,
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": [
168
+ "response = generator.chat.completions.create(\n",
169
+ " model=orchestrator_model,\n",
170
+ " messages=messages,\n",
171
+ ")\n",
172
+ "eval_question = response.choices[0].message.content"
173
+ ]
174
+ },
175
+ {
176
+ "cell_type": "code",
177
+ "execution_count": null,
178
+ "metadata": {},
179
+ "outputs": [],
180
+ "source": [
181
+ "display(Markdown(eval_question))"
182
+ ]
183
+ },
184
+ {
185
+ "cell_type": "markdown",
186
+ "metadata": {},
187
+ "source": [
188
+ "### Task Parallelization"
189
+ ]
190
+ },
191
+ {
192
+ "cell_type": "markdown",
193
+ "metadata": {},
194
+ "source": [
195
+ "Now, having the question and all the models instantiated it's time to see what each model has to say about the complex task it was given."
196
+ ]
197
+ },
198
+ {
199
+ "cell_type": "code",
200
+ "execution_count": null,
201
+ "metadata": {},
202
+ "outputs": [],
203
+ "source": [
204
+ "question = [{\"role\": \"user\", \"content\": eval_question}]\n",
205
+ "answers = []\n",
206
+ "competitors = []\n",
207
+ "\n",
208
+ "for name, model in qa_models.items():\n",
209
+ " response = model.chat.completions.create(model=name, messages=question)\n",
210
+ " answer = response.choices[0].message.content\n",
211
+ " competitors.append(name)\n",
212
+ " answers.append(answer)\n",
213
+ "\n",
214
+ "answers"
215
+ ]
216
+ },
217
+ {
218
+ "cell_type": "code",
219
+ "execution_count": null,
220
+ "metadata": {},
221
+ "outputs": [],
222
+ "source": [
223
+ "report = \"# Answer report for each of the 5 models\\n\\n\"\n",
224
+ "report += \"\\n\\n\".join([f\"## **Model: {model}**\\n\\n{answer}\" for model, answer in zip(competitors, answers)])\n",
225
+ "display(Markdown(report))"
226
+ ]
227
+ },
228
+ {
229
+ "cell_type": "markdown",
230
+ "metadata": {},
231
+ "source": [
232
+ "### Synthetizer/Judge"
233
+ ]
234
+ },
235
+ {
236
+ "cell_type": "markdown",
237
+ "metadata": {},
238
+ "source": [
239
+ "The Judge Agents ranks the LLM responses based on coherence and relevance to the evaluation prompt. Judges vote and the final LLM ranking is based on the aggregated ranking of all three judges."
240
+ ]
241
+ },
242
+ {
243
+ "cell_type": "code",
244
+ "execution_count": null,
245
+ "metadata": {},
246
+ "outputs": [],
247
+ "source": [
248
+ "together = \"\"\n",
249
+ "for index, answer in enumerate(answers):\n",
250
+ " together += f\"# Response from competitor {index+1}\\n\\n\"\n",
251
+ " together += answer + \"\\n\\n\"\n",
252
+ "\n",
253
+ "together"
254
+ ]
255
+ },
256
+ {
257
+ "cell_type": "code",
258
+ "execution_count": 12,
259
+ "metadata": {},
260
+ "outputs": [],
261
+ "source": [
262
+ "judge_prompt = f\"\"\"\n",
263
+ " You are judging a competition between {len(competitors)} LLM competitors.\n",
264
+ " Each model has been given this nuanced question to evaluate their intelligence:\n",
265
+ "\n",
266
+ " {eval_question}\n",
267
+ "\n",
268
+ " Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n",
269
+ " Respond with JSON, and only JSON, with the following format:\n",
270
+ " {{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n",
271
+ " With 'best competitor number being ONLY the number', for instance:\n",
272
+ " {{\"results\": [\"5\", \"2\", \"4\", ...]}}\n",
273
+ " Here are the responses from each competitor:\n",
274
+ "\n",
275
+ " {together}\n",
276
+ "\n",
277
+ " Now respond with the JSON with the ranked order of the competitors, nothing else. Do NOT include MARKDOWN FORMATTING or CODE BLOCKS. ONLY the JSON\n",
278
+ " \"\"\"\n",
279
+ "\n",
280
+ "judge_messages = [{\"role\": \"user\", \"content\": judge_prompt}]"
281
+ ]
282
+ },
283
+ {
284
+ "cell_type": "code",
285
+ "execution_count": null,
286
+ "metadata": {},
287
+ "outputs": [],
288
+ "source": [
289
+ "from collections import defaultdict\n",
290
+ "import re\n",
291
+ "\n",
292
+ "N = len(competitors)\n",
293
+ "scores = defaultdict(int)\n",
294
+ "for judge_name, judge in judges.items():\n",
295
+ " response = judge.chat.completions.create(\n",
296
+ " model=judge_name,\n",
297
+ " messages=judge_messages,\n",
298
+ " )\n",
299
+ " response = response.choices[0].message.content\n",
300
+ " response_json = re.findall(r'\\{.*?\\}', response)[0]\n",
301
+ " results = json.loads(response_json)[\"results\"]\n",
302
+ " ranks = [int(result) for result in results]\n",
303
+ " print(f\"Judge {judge_name} ranking:\")\n",
304
+ " for i, c in enumerate(ranks):\n",
305
+ " model_name = competitors[c - 1]\n",
306
+ " print(f\"#{i+1} : {model_name}\")\n",
307
+ " scores[c - 1] += (N - i)\n",
308
+ " print()"
309
+ ]
310
+ },
311
+ {
312
+ "cell_type": "code",
313
+ "execution_count": null,
314
+ "metadata": {},
315
+ "outputs": [],
316
+ "source": [
317
+ "sorted_indices = sorted(scores, key=scores.get)\n",
318
+ "\n",
319
+ "# Convert to model names\n",
320
+ "ranked_model_names = [competitors[i] for i in sorted_indices]\n",
321
+ "\n",
322
+ "print(\"Final ranking from best to worst:\")\n",
323
+ "for i, name in enumerate(ranked_model_names[::-1], 1):\n",
324
+ " print(f\"#{i}: {name}\")"
325
+ ]
326
+ },
327
+ {
328
+ "cell_type": "markdown",
329
+ "metadata": {},
330
+ "source": [
331
+ "## Routing Workflow"
332
+ ]
333
+ },
334
+ {
335
+ "cell_type": "markdown",
336
+ "metadata": {},
337
+ "source": [
338
+ "We now define a routing agent responsible for classifying task complexity and delegating the prompt to the most appropriate model."
339
+ ]
340
+ },
341
+ {
342
+ "cell_type": "code",
343
+ "execution_count": 15,
344
+ "metadata": {},
345
+ "outputs": [],
346
+ "source": [
347
+ "def classify_question_complexity(question: str, routing_agent, routing_model) -> int:\n",
348
+ " \"\"\"\n",
349
+ " Ask an LLM to classify the question complexity from 1 (easy) to 5 (very hard).\n",
350
+ " \"\"\"\n",
351
+ " prompt = f\"\"\"\n",
352
+ " You are a classifier responsible for assigning a complexity level to user questions, based on how difficult they would be for a language model to answer.\n",
353
+ "\n",
354
+ " Please read the question below and assign a complexity score from 1 to 5:\n",
355
+ "\n",
356
+ " - Level 1: Very simple factual or definitional question (e.g., “What is the capital of France?”)\n",
357
+ " - Level 2: Slightly more involved, requiring basic reasoning or comparison\n",
358
+ " - Level 3: Moderate complexity, requiring synthesis, context understanding, or multi-part answers\n",
359
+ " - Level 4: High complexity, requiring abstract thinking, ethical judgment, or creative generation\n",
360
+ " - Level 5: Extremely challenging, requiring deep reasoning, philosophical reflection, or long-term multi-step inference\n",
361
+ "\n",
362
+ " Respond ONLY with a single integer between 1 and 5 that best reflects the complexity of the question.\n",
363
+ "\n",
364
+ " Question:\n",
365
+ " {question}\n",
366
+ " \"\"\"\n",
367
+ "\n",
368
+ " response = routing_agent.chat.completions.create(\n",
369
+ " model=routing_model,\n",
370
+ " messages=[{\"role\": \"user\", \"content\": prompt}]\n",
371
+ " )\n",
372
+ " try:\n",
373
+ " return int(response.choices[0].message.content.strip())\n",
374
+ " except Exception:\n",
375
+ " return 3 # default to medium complexity on error\n",
376
+ " \n",
377
+ "def route_question_to_model(question: str, models_by_rank, classifier_model=router, model_name=orchestrator_model):\n",
378
+ " level = classify_question_complexity(question, classifier_model, model_name)\n",
379
+ " selected_model_name = models_by_rank[level - 1]\n",
380
+ " return selected_model_name"
381
+ ]
382
+ },
383
+ {
384
+ "cell_type": "code",
385
+ "execution_count": 16,
386
+ "metadata": {},
387
+ "outputs": [],
388
+ "source": [
389
+ "difficulty_prompts = [\n",
390
+ " \"Generate a very basic, factual question that a small or entry-level language model could answer easily. It should require no reasoning, just direct knowledge lookup.\",\n",
391
+ " \"Generate a slightly involved question that requires basic reasoning, comparison, or combining two known facts. Still within the grasp of small models but not purely factual.\",\n",
392
+ " \"Generate a moderately challenging question that requires some synthesis of ideas, multi-step reasoning, or contextual understanding. A mid-tier model should be able to answer it with effort.\",\n",
393
+ " \"Generate a difficult question involving abstract thinking, open-ended reasoning, or ethical tradeoffs. The question should challenge large models to produce thoughtful and coherent responses.\",\n",
394
+ " \"Generate an extremely complex and nuanced question that tests the limits of current language models. It should require deep reasoning, long-term planning, philosophy, or advanced multi-domain knowledge.\"\n",
395
+ "]\n",
396
+ "def generate_question(level, generator=generator, generator_model=orchestrator_model):\n",
397
+ " prompt = (\n",
398
+ " f\"{difficulty_prompts[level - 1]}\\n\"\n",
399
+ " \"Answer only with the question, no explanation.\"\n",
400
+ " )\n",
401
+ " messages = [{\"role\": \"user\", \"content\": prompt}]\n",
402
+ " response = generator.chat.completions.create(\n",
403
+ " model=generator_model, # or your planner model\n",
404
+ " messages=messages\n",
405
+ " )\n",
406
+ " \n",
407
+ " return response.choices[0].message.content\n",
408
+ "\n"
409
+ ]
410
+ },
411
+ {
412
+ "cell_type": "markdown",
413
+ "metadata": {},
414
+ "source": [
415
+ "### Testing Routing Workflow"
416
+ ]
417
+ },
418
+ {
419
+ "cell_type": "markdown",
420
+ "metadata": {},
421
+ "source": [
422
+ "Finally, to test the routing workflow, we create a function that accepts a task complexity level and triggers the full routing process.\n",
423
+ "\n",
424
+ "*Note: A level-N prompt isn't always assigned to the Nth-most capable model due to the classifier's subjective decisions.*"
425
+ ]
426
+ },
427
+ {
428
+ "cell_type": "code",
429
+ "execution_count": 17,
430
+ "metadata": {},
431
+ "outputs": [],
432
+ "source": [
433
+ "def test_generation_routing(level):\n",
434
+ " question = generate_question(level=level)\n",
435
+ " answer_model = route_question_to_model(question, ranked_model_names)\n",
436
+ " messages = [{\"role\": \"user\", \"content\": question}]\n",
437
+ "\n",
438
+ " response =qa_models[answer_model].chat.completions.create(\n",
439
+ " model=answer_model, # or your planner model\n",
440
+ " messages=messages\n",
441
+ " )\n",
442
+ " print(f\"Question : {question}\")\n",
443
+ " print(f\"Routed to {answer_model}\")\n",
444
+ " display(Markdown(response.choices[0].message.content))"
445
+ ]
446
+ },
447
+ {
448
+ "cell_type": "code",
449
+ "execution_count": null,
450
+ "metadata": {},
451
+ "outputs": [],
452
+ "source": [
453
+ "test_generation_routing(level=1)"
454
+ ]
455
+ },
456
+ {
457
+ "cell_type": "code",
458
+ "execution_count": null,
459
+ "metadata": {},
460
+ "outputs": [],
461
+ "source": [
462
+ "test_generation_routing(level=2)"
463
+ ]
464
+ },
465
+ {
466
+ "cell_type": "code",
467
+ "execution_count": null,
468
+ "metadata": {},
469
+ "outputs": [],
470
+ "source": [
471
+ "test_generation_routing(level=3)"
472
+ ]
473
+ },
474
+ {
475
+ "cell_type": "code",
476
+ "execution_count": null,
477
+ "metadata": {},
478
+ "outputs": [],
479
+ "source": [
480
+ "test_generation_routing(level=4)"
481
+ ]
482
+ },
483
+ {
484
+ "cell_type": "code",
485
+ "execution_count": null,
486
+ "metadata": {},
487
+ "outputs": [],
488
+ "source": [
489
+ "test_generation_routing(level=5)"
490
+ ]
491
+ }
492
+ ],
493
+ "metadata": {
494
+ "kernelspec": {
495
+ "display_name": ".venv",
496
+ "language": "python",
497
+ "name": "python3"
498
+ },
499
+ "language_info": {
500
+ "codemirror_mode": {
501
+ "name": "ipython",
502
+ "version": 3
503
+ },
504
+ "file_extension": ".py",
505
+ "mimetype": "text/x-python",
506
+ "name": "python",
507
+ "nbconvert_exporter": "python",
508
+ "pygments_lexer": "ipython3",
509
+ "version": "3.12.11"
510
+ }
511
+ },
512
+ "nbformat": 4,
513
+ "nbformat_minor": 2
514
+ }
community_contributions/2_lab2_ReAct_Pattern.ipynb ADDED
@@ -0,0 +1,289 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to the Second Lab - Week 1, Day 3\n",
8
+ "\n",
9
+ "Today we will work with lots of models! This is a way to get comfortable with APIs."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "metadata": {},
15
+ "source": [
16
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
17
+ " <tr>\n",
18
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
19
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
20
+ " </td>\n",
21
+ " <td>\n",
22
+ " <h2 style=\"color:#ff7800;\">Important point - please read</h2>\n",
23
+ " <span style=\"color:#ff7800;\">The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, <b>after</b> watching the lecture. Add print statements to understand what's going on, and then come up with your own variations.<br/><br/>If you have time, I'd love it if you submit a PR for changes in the community_contributions folder - instructions in the resources. Also, if you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n",
24
+ " </span>\n",
25
+ " </td>\n",
26
+ " </tr>\n",
27
+ "</table>"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "markdown",
32
+ "metadata": {},
33
+ "source": [
34
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
35
+ " <tr>\n",
36
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
37
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
38
+ " </td>\n",
39
+ " <td>\n",
40
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
41
+ " <span style=\"color:#ff7800;\">Which pattern(s) did this use? Try updating this to add another Agentic design pattern.\n",
42
+ " </span>\n",
43
+ " </td>\n",
44
+ " </tr>\n",
45
+ "</table>"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "markdown",
50
+ "metadata": {},
51
+ "source": [
52
+ "# ReAct Pattern"
53
+ ]
54
+ },
55
+ {
56
+ "cell_type": "code",
57
+ "execution_count": 26,
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": [
61
+ "import openai\n",
62
+ "import os\n",
63
+ "from dotenv import load_dotenv\n",
64
+ "import io\n",
65
+ "from anthropic import Anthropic\n",
66
+ "from IPython.display import Markdown, display"
67
+ ]
68
+ },
69
+ {
70
+ "cell_type": "code",
71
+ "execution_count": null,
72
+ "metadata": {},
73
+ "outputs": [],
74
+ "source": [
75
+ "# Print the key prefixes to help with any debugging\n",
76
+ "\n",
77
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
78
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
79
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
80
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
81
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
82
+ "\n",
83
+ "if openai_api_key:\n",
84
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
85
+ "else:\n",
86
+ " print(\"OpenAI API Key not set\")\n",
87
+ " \n",
88
+ "if anthropic_api_key:\n",
89
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
90
+ "else:\n",
91
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
92
+ "\n",
93
+ "if google_api_key:\n",
94
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
95
+ "else:\n",
96
+ " print(\"Google API Key not set (and this is optional)\")\n",
97
+ "\n",
98
+ "if deepseek_api_key:\n",
99
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
100
+ "else:\n",
101
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
102
+ "\n",
103
+ "if groq_api_key:\n",
104
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
105
+ "else:\n",
106
+ " print(\"Groq API Key not set (and this is optional)\")"
107
+ ]
108
+ },
109
+ {
110
+ "cell_type": "code",
111
+ "execution_count": 50,
112
+ "metadata": {},
113
+ "outputs": [],
114
+ "source": [
115
+ "\n",
116
+ "from openai import OpenAI\n",
117
+ "\n",
118
+ "openai = OpenAI()\n",
119
+ "\n",
120
+ "# Request prompt\n",
121
+ "request = (\n",
122
+ " \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n",
123
+ " \"Answer only with the question, no explanation.\"\n",
124
+ ")\n",
125
+ "\n",
126
+ "\n",
127
+ "\n",
128
+ "def generate_question(prompt: str) -> str:\n",
129
+ " response = openai.chat.completions.create(\n",
130
+ " model='gpt-4o-mini',\n",
131
+ " messages=[{'role': 'user', 'content': prompt}]\n",
132
+ " )\n",
133
+ " question = response.choices[0].message.content\n",
134
+ " return question\n",
135
+ "\n",
136
+ "def react_agent_decide_model(question: str) -> str:\n",
137
+ " prompt = f\"\"\"\n",
138
+ " You are an intelligent AI assistant tasked with evaluating which language model is most suitable to answer a given question.\n",
139
+ "\n",
140
+ " Available models:\n",
141
+ " - OpenAI: excels at reasoning and factual answers.\n",
142
+ " - Claude: better for philosophical, nuanced, and ethical topics.\n",
143
+ " - Gemini: good for concise and structured summaries.\n",
144
+ " - Groq: good for creative or exploratory tasks.\n",
145
+ " - DeepSeek: strong at coding, technical reasoning, and multilingual responses.\n",
146
+ "\n",
147
+ " Here is the question to answer:\n",
148
+ " \"{question}\"\n",
149
+ "\n",
150
+ " ### Thought:\n",
151
+ " Which model is best suited to answer this question, and why?\n",
152
+ "\n",
153
+ " ### Action:\n",
154
+ " Respond with only the model name you choose (e.g., \"Claude\").\n",
155
+ " \"\"\"\n",
156
+ "\n",
157
+ " response = openai.chat.completions.create(\n",
158
+ " model=\"o3-mini\",\n",
159
+ " messages=[{\"role\": \"user\", \"content\": prompt}]\n",
160
+ " )\n",
161
+ " model = response.choices[0].message.content.strip()\n",
162
+ " return model\n",
163
+ "\n",
164
+ "def generate_answer_openai(prompt):\n",
165
+ " answer = openai.chat.completions.create(\n",
166
+ " model='gpt-4o-mini',\n",
167
+ " messages=[{'role': 'user', 'content': prompt}]\n",
168
+ " ).choices[0].message.content\n",
169
+ " return answer\n",
170
+ "\n",
171
+ "def generate_answer_anthropic(prompt):\n",
172
+ " anthropic = Anthropic(api_key=anthropic_api_key)\n",
173
+ " model_name = \"claude-3-5-sonnet-20240620\"\n",
174
+ " answer = anthropic.messages.create(\n",
175
+ " model=model_name,\n",
176
+ " messages=[{'role': 'user', 'content': prompt}],\n",
177
+ " max_tokens=1000\n",
178
+ " ).content[0].text\n",
179
+ " return answer\n",
180
+ "\n",
181
+ "def generate_answer_deepseek(prompt):\n",
182
+ " deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
183
+ " model_name = \"deepseek-chat\" \n",
184
+ " answer = deepseek.chat.completions.create(\n",
185
+ " model=model_name,\n",
186
+ " messages=[{'role': 'user', 'content': prompt}],\n",
187
+ " base_url='https://api.deepseek.com/v1'\n",
188
+ " ).choices[0].message.content\n",
189
+ " return answer\n",
190
+ "\n",
191
+ "def generate_answer_gemini(prompt):\n",
192
+ " gemini=OpenAI(base_url='https://generativelanguage.googleapis.com/v1beta/openai/',api_key=google_api_key)\n",
193
+ " model_name = \"gemini-2.0-flash\"\n",
194
+ " answer = gemini.chat.completions.create(\n",
195
+ " model=model_name,\n",
196
+ " messages=[{'role': 'user', 'content': prompt}],\n",
197
+ " ).choices[0].message.content\n",
198
+ " return answer\n",
199
+ "\n",
200
+ "def generate_answer_groq(prompt):\n",
201
+ " groq=OpenAI(base_url='https://api.groq.com/openai/v1',api_key=groq_api_key)\n",
202
+ " model_name=\"llama3-70b-8192\"\n",
203
+ " answer = groq.chat.completions.create(\n",
204
+ " model=model_name,\n",
205
+ " messages=[{'role': 'user', 'content': prompt}],\n",
206
+ " base_url=\"https://api.groq.com/openai/v1\"\n",
207
+ " ).choices[0].message.content\n",
208
+ " return answer\n",
209
+ "\n",
210
+ "def main():\n",
211
+ " print(\"Generating question...\")\n",
212
+ " question = generate_question(request)\n",
213
+ " print(f\"\\n🧠 Question: {question}\\n\")\n",
214
+ " selected_model = react_agent_decide_model(question)\n",
215
+ " print(f\"\\n🔹 {selected_model}:\\n\")\n",
216
+ " \n",
217
+ " if selected_model.lower() == \"openai\":\n",
218
+ " answer = generate_answer_openai(question)\n",
219
+ " elif selected_model.lower() == \"deepseek\":\n",
220
+ " answer = generate_answer_deepseek(question)\n",
221
+ " elif selected_model.lower() == \"gemini\":\n",
222
+ " answer = generate_answer_gemini(question)\n",
223
+ " elif selected_model.lower() == \"groq\":\n",
224
+ " answer = generate_answer_groq(question)\n",
225
+ " elif selected_model.lower() == \"claude\":\n",
226
+ " answer = generate_answer_anthropic(question)\n",
227
+ " print(f\"\\n🔹 {selected_model}:\\n{answer}\\n\")\n",
228
+ " \n"
229
+ ]
230
+ },
231
+ {
232
+ "cell_type": "code",
233
+ "execution_count": null,
234
+ "metadata": {},
235
+ "outputs": [],
236
+ "source": [
237
+ "main()"
238
+ ]
239
+ },
240
+ {
241
+ "cell_type": "code",
242
+ "execution_count": null,
243
+ "metadata": {},
244
+ "outputs": [],
245
+ "source": []
246
+ },
247
+ {
248
+ "cell_type": "markdown",
249
+ "metadata": {},
250
+ "source": [
251
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
252
+ " <tr>\n",
253
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
254
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
255
+ " </td>\n",
256
+ " <td>\n",
257
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
258
+ " <span style=\"color:#00bfff;\">These kinds of patterns - to send a task to multiple models, and evaluate results,\n",
259
+ " are common where you need to improve the quality of your LLM response. This approach can be universally applied\n",
260
+ " to business projects where accuracy is critical.\n",
261
+ " </span>\n",
262
+ " </td>\n",
263
+ " </tr>\n",
264
+ "</table>"
265
+ ]
266
+ }
267
+ ],
268
+ "metadata": {
269
+ "kernelspec": {
270
+ "display_name": ".venv",
271
+ "language": "python",
272
+ "name": "python3"
273
+ },
274
+ "language_info": {
275
+ "codemirror_mode": {
276
+ "name": "ipython",
277
+ "version": 3
278
+ },
279
+ "file_extension": ".py",
280
+ "mimetype": "text/x-python",
281
+ "name": "python",
282
+ "nbconvert_exporter": "python",
283
+ "pygments_lexer": "ipython3",
284
+ "version": "3.12.4"
285
+ }
286
+ },
287
+ "nbformat": 4,
288
+ "nbformat_minor": 2
289
+ }
community_contributions/2_lab2_async.ipynb ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to the Second Lab - Week 1, Day 3\n",
8
+ "\n",
9
+ "Today we will work with lots of models! This is a way to get comfortable with APIs."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "code",
14
+ "execution_count": 1,
15
+ "metadata": {},
16
+ "outputs": [],
17
+ "source": [
18
+ "# Start with imports - ask ChatGPT to explain any package that you don't know\n",
19
+ "\n",
20
+ "import os\n",
21
+ "import json\n",
22
+ "import asyncio\n",
23
+ "from dotenv import load_dotenv\n",
24
+ "from openai import OpenAI, AsyncOpenAI\n",
25
+ "from anthropic import AsyncAnthropic\n",
26
+ "from pydantic import BaseModel"
27
+ ]
28
+ },
29
+ {
30
+ "cell_type": "code",
31
+ "execution_count": null,
32
+ "metadata": {},
33
+ "outputs": [],
34
+ "source": [
35
+ "# Always remember to do this!\n",
36
+ "load_dotenv(override=True)"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": null,
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "# Print the key prefixes to help with any debugging\n",
46
+ "\n",
47
+ "OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')\n",
48
+ "ANTHROPIC_API_KEY = os.getenv('ANTHROPIC_API_KEY')\n",
49
+ "GOOGLE_API_KEY = os.getenv('GOOGLE_API_KEY')\n",
50
+ "DEEPSEEK_API_KEY = os.getenv('DEEPSEEK_API_KEY')\n",
51
+ "GROQ_API_KEY = os.getenv('GROQ_API_KEY')\n",
52
+ "\n",
53
+ "if OPENAI_API_KEY:\n",
54
+ " print(f\"OpenAI API Key exists and begins {OPENAI_API_KEY[:8]}\")\n",
55
+ "else:\n",
56
+ " print(\"OpenAI API Key not set\")\n",
57
+ " \n",
58
+ "if ANTHROPIC_API_KEY:\n",
59
+ " print(f\"Anthropic API Key exists and begins {ANTHROPIC_API_KEY[:7]}\")\n",
60
+ "else:\n",
61
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
62
+ "\n",
63
+ "if GOOGLE_API_KEY:\n",
64
+ " print(f\"Google API Key exists and begins {GOOGLE_API_KEY[:2]}\")\n",
65
+ "else:\n",
66
+ " print(\"Google API Key not set (and this is optional)\")\n",
67
+ "\n",
68
+ "if DEEPSEEK_API_KEY:\n",
69
+ " print(f\"DeepSeek API Key exists and begins {DEEPSEEK_API_KEY[:3]}\")\n",
70
+ "else:\n",
71
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
72
+ "\n",
73
+ "if GROQ_API_KEY:\n",
74
+ " print(f\"Groq API Key exists and begins {GROQ_API_KEY[:4]}\")\n",
75
+ "else:\n",
76
+ " print(\"Groq API Key not set (and this is optional)\")"
77
+ ]
78
+ },
79
+ {
80
+ "cell_type": "code",
81
+ "execution_count": 4,
82
+ "metadata": {},
83
+ "outputs": [],
84
+ "source": [
85
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n",
86
+ "request += \"Answer only with the question, no explanation.\"\n",
87
+ "messages = [{\"role\": \"user\", \"content\": request}]"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "code",
92
+ "execution_count": null,
93
+ "metadata": {},
94
+ "outputs": [],
95
+ "source": [
96
+ "print(messages)"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "execution_count": null,
102
+ "metadata": {},
103
+ "outputs": [],
104
+ "source": [
105
+ "openai = AsyncOpenAI()\n",
106
+ "response = await openai.chat.completions.create(\n",
107
+ " model=\"gpt-4o-mini\",\n",
108
+ " messages=messages,\n",
109
+ ")\n",
110
+ "question = response.choices[0].message.content\n",
111
+ "print(question)\n"
112
+ ]
113
+ },
114
+ {
115
+ "cell_type": "code",
116
+ "execution_count": 7,
117
+ "metadata": {},
118
+ "outputs": [],
119
+ "source": [
120
+ "# Define Pydantic model for storing LLM results\n",
121
+ "class LLMResult(BaseModel):\n",
122
+ " model: str\n",
123
+ " answer: str\n"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "code",
128
+ "execution_count": 8,
129
+ "metadata": {},
130
+ "outputs": [],
131
+ "source": [
132
+ "results: list[LLMResult] = []\n",
133
+ "messages = [{\"role\": \"user\", \"content\": question}]"
134
+ ]
135
+ },
136
+ {
137
+ "cell_type": "code",
138
+ "execution_count": 9,
139
+ "metadata": {},
140
+ "outputs": [],
141
+ "source": [
142
+ "# The API we know well\n",
143
+ "async def openai_answer() -> None:\n",
144
+ "\n",
145
+ " if OPENAI_API_KEY is None:\n",
146
+ " return None\n",
147
+ " \n",
148
+ " print(\"OpenAI starting!\")\n",
149
+ " model_name = \"gpt-4o-mini\"\n",
150
+ "\n",
151
+ " try:\n",
152
+ " response = await openai.chat.completions.create(model=model_name, messages=messages)\n",
153
+ " answer = response.choices[0].message.content\n",
154
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
155
+ " except Exception as e:\n",
156
+ " print(f\"Error with OpenAI: {e}\")\n",
157
+ " return None\n",
158
+ "\n",
159
+ " print(\"OpenAI done!\")"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": 10,
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": [
168
+ "# Anthropic has a slightly different API, and Max Tokens is required\n",
169
+ "\n",
170
+ "async def anthropic_answer() -> None:\n",
171
+ "\n",
172
+ " if ANTHROPIC_API_KEY is None:\n",
173
+ " return None\n",
174
+ " \n",
175
+ " print(\"Anthropic starting!\")\n",
176
+ " model_name = \"claude-3-7-sonnet-latest\"\n",
177
+ "\n",
178
+ " claude = AsyncAnthropic()\n",
179
+ " try:\n",
180
+ " response = await claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n",
181
+ " answer = response.content[0].text\n",
182
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
183
+ " except Exception as e:\n",
184
+ " print(f\"Error with Anthropic: {e}\")\n",
185
+ " return None\n",
186
+ "\n",
187
+ " print(\"Anthropic done!\")"
188
+ ]
189
+ },
190
+ {
191
+ "cell_type": "code",
192
+ "execution_count": 11,
193
+ "metadata": {},
194
+ "outputs": [],
195
+ "source": [
196
+ "async def google_answer() -> None:\n",
197
+ "\n",
198
+ " if GOOGLE_API_KEY is None:\n",
199
+ " return None\n",
200
+ " \n",
201
+ " print(\"Google starting!\")\n",
202
+ " model_name = \"gemini-2.0-flash\"\n",
203
+ "\n",
204
+ " gemini = AsyncOpenAI(api_key=GOOGLE_API_KEY, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
205
+ " try:\n",
206
+ " response = await gemini.chat.completions.create(model=model_name, messages=messages)\n",
207
+ " answer = response.choices[0].message.content\n",
208
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
209
+ " except Exception as e:\n",
210
+ " print(f\"Error with Google: {e}\")\n",
211
+ " return None\n",
212
+ "\n",
213
+ " print(\"Google done!\")"
214
+ ]
215
+ },
216
+ {
217
+ "cell_type": "code",
218
+ "execution_count": 12,
219
+ "metadata": {},
220
+ "outputs": [],
221
+ "source": [
222
+ "async def deepseek_answer() -> None:\n",
223
+ "\n",
224
+ " if DEEPSEEK_API_KEY is None:\n",
225
+ " return None\n",
226
+ " \n",
227
+ " print(\"DeepSeek starting!\")\n",
228
+ " model_name = \"deepseek-chat\"\n",
229
+ "\n",
230
+ " deepseek = AsyncOpenAI(api_key=DEEPSEEK_API_KEY, base_url=\"https://api.deepseek.com/v1\")\n",
231
+ " try:\n",
232
+ " response = await deepseek.chat.completions.create(model=model_name, messages=messages)\n",
233
+ " answer = response.choices[0].message.content\n",
234
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
235
+ " except Exception as e:\n",
236
+ " print(f\"Error with DeepSeek: {e}\")\n",
237
+ " return None\n",
238
+ "\n",
239
+ " print(\"DeepSeek done!\")"
240
+ ]
241
+ },
242
+ {
243
+ "cell_type": "code",
244
+ "execution_count": 13,
245
+ "metadata": {},
246
+ "outputs": [],
247
+ "source": [
248
+ "async def groq_answer() -> None:\n",
249
+ "\n",
250
+ " if GROQ_API_KEY is None:\n",
251
+ " return None\n",
252
+ " \n",
253
+ " print(\"Groq starting!\")\n",
254
+ " model_name = \"llama-3.3-70b-versatile\"\n",
255
+ "\n",
256
+ " groq = AsyncOpenAI(api_key=GROQ_API_KEY, base_url=\"https://api.groq.com/openai/v1\")\n",
257
+ " try:\n",
258
+ " response = await groq.chat.completions.create(model=model_name, messages=messages)\n",
259
+ " answer = response.choices[0].message.content\n",
260
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
261
+ " except Exception as e:\n",
262
+ " print(f\"Error with Groq: {e}\")\n",
263
+ " return None\n",
264
+ "\n",
265
+ " print(\"Groq done!\")\n"
266
+ ]
267
+ },
268
+ {
269
+ "cell_type": "markdown",
270
+ "metadata": {},
271
+ "source": [
272
+ "## For the next cell, we will use Ollama\n",
273
+ "\n",
274
+ "Ollama runs a local web service that gives an OpenAI compatible endpoint, \n",
275
+ "and runs models locally using high performance C++ code.\n",
276
+ "\n",
277
+ "If you don't have Ollama, install it here by visiting https://ollama.com then pressing Download and following the instructions.\n",
278
+ "\n",
279
+ "After it's installed, you should be able to visit here: http://localhost:11434 and see the message \"Ollama is running\"\n",
280
+ "\n",
281
+ "You might need to restart Cursor (and maybe reboot). Then open a Terminal (control+\\`) and run `ollama serve`\n",
282
+ "\n",
283
+ "Useful Ollama commands (run these in the terminal, or with an exclamation mark in this notebook):\n",
284
+ "\n",
285
+ "`ollama pull <model_name>` downloads a model locally \n",
286
+ "`ollama ls` lists all the models you've downloaded \n",
287
+ "`ollama rm <model_name>` deletes the specified model from your downloads"
288
+ ]
289
+ },
290
+ {
291
+ "cell_type": "markdown",
292
+ "metadata": {},
293
+ "source": [
294
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
295
+ " <tr>\n",
296
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
297
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
298
+ " </td>\n",
299
+ " <td>\n",
300
+ " <h2 style=\"color:#ff7800;\">Super important - ignore me at your peril!</h2>\n",
301
+ " <span style=\"color:#ff7800;\">The model called <b>llama3.3</b> is FAR too large for home computers - it's not intended for personal computing and will consume all your resources! Stick with the nicely sized <b>llama3.2</b> or <b>llama3.2:1b</b> and if you want larger, try llama3.1 or smaller variants of Qwen, Gemma, Phi or DeepSeek. See the <A href=\"https://ollama.com/models\">the Ollama models page</a> for a full list of models and sizes.\n",
302
+ " </span>\n",
303
+ " </td>\n",
304
+ " </tr>\n",
305
+ "</table>"
306
+ ]
307
+ },
308
+ {
309
+ "cell_type": "code",
310
+ "execution_count": null,
311
+ "metadata": {},
312
+ "outputs": [],
313
+ "source": [
314
+ "!ollama pull llama3.2"
315
+ ]
316
+ },
317
+ {
318
+ "cell_type": "code",
319
+ "execution_count": 15,
320
+ "metadata": {},
321
+ "outputs": [],
322
+ "source": [
323
+ "async def ollama_answer() -> None:\n",
324
+ " model_name = \"llama3.2\"\n",
325
+ "\n",
326
+ " print(\"Ollama starting!\")\n",
327
+ " ollama = AsyncOpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
328
+ " try:\n",
329
+ " response = await ollama.chat.completions.create(model=model_name, messages=messages)\n",
330
+ " answer = response.choices[0].message.content\n",
331
+ " results.append(LLMResult(model=model_name, answer=answer))\n",
332
+ " except Exception as e:\n",
333
+ " print(f\"Error with Ollama: {e}\")\n",
334
+ " return None\n",
335
+ "\n",
336
+ " print(\"Ollama done!\") "
337
+ ]
338
+ },
339
+ {
340
+ "cell_type": "code",
341
+ "execution_count": null,
342
+ "metadata": {},
343
+ "outputs": [],
344
+ "source": [
345
+ "async def gather_answers():\n",
346
+ " tasks = [\n",
347
+ " openai_answer(),\n",
348
+ " anthropic_answer(),\n",
349
+ " google_answer(),\n",
350
+ " deepseek_answer(),\n",
351
+ " groq_answer(),\n",
352
+ " ollama_answer()\n",
353
+ " ]\n",
354
+ " await asyncio.gather(*tasks)\n",
355
+ "\n",
356
+ "await gather_answers()"
357
+ ]
358
+ },
359
+ {
360
+ "cell_type": "code",
361
+ "execution_count": null,
362
+ "metadata": {},
363
+ "outputs": [],
364
+ "source": [
365
+ "together = \"\"\n",
366
+ "competitors = []\n",
367
+ "answers = []\n",
368
+ "\n",
369
+ "for res in results:\n",
370
+ " competitor = res.model\n",
371
+ " answer = res.answer\n",
372
+ " competitors.append(competitor)\n",
373
+ " answers.append(answer)\n",
374
+ " together += f\"# Response from competitor {competitor}\\n\\n\"\n",
375
+ " together += answer + \"\\n\\n\"\n",
376
+ "\n",
377
+ "print(f\"Number of competitors: {len(results)}\")\n",
378
+ "print(together)\n"
379
+ ]
380
+ },
381
+ {
382
+ "cell_type": "code",
383
+ "execution_count": 18,
384
+ "metadata": {},
385
+ "outputs": [],
386
+ "source": [
387
+ "judge = f\"\"\"You are judging a competition between {len(results)} competitors.\n",
388
+ "Each model has been given this question:\n",
389
+ "\n",
390
+ "{question}\n",
391
+ "\n",
392
+ "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n",
393
+ "Respond with JSON, and only JSON, with the following format:\n",
394
+ "{{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n",
395
+ "\n",
396
+ "Here are the responses from each competitor:\n",
397
+ "\n",
398
+ "{together}\n",
399
+ "\n",
400
+ "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n"
401
+ ]
402
+ },
403
+ {
404
+ "cell_type": "code",
405
+ "execution_count": null,
406
+ "metadata": {},
407
+ "outputs": [],
408
+ "source": [
409
+ "print(judge)"
410
+ ]
411
+ },
412
+ {
413
+ "cell_type": "code",
414
+ "execution_count": 20,
415
+ "metadata": {},
416
+ "outputs": [],
417
+ "source": [
418
+ "judge_messages = [{\"role\": \"user\", \"content\": judge}]"
419
+ ]
420
+ },
421
+ {
422
+ "cell_type": "code",
423
+ "execution_count": null,
424
+ "metadata": {},
425
+ "outputs": [],
426
+ "source": [
427
+ "# Judgement time!\n",
428
+ "\n",
429
+ "openai = OpenAI()\n",
430
+ "response = openai.chat.completions.create(\n",
431
+ " model=\"o3-mini\",\n",
432
+ " messages=judge_messages,\n",
433
+ ")\n",
434
+ "judgement = response.choices[0].message.content\n",
435
+ "print(judgement)\n"
436
+ ]
437
+ },
438
+ {
439
+ "cell_type": "code",
440
+ "execution_count": null,
441
+ "metadata": {},
442
+ "outputs": [],
443
+ "source": [
444
+ "# OK let's turn this into results!\n",
445
+ "\n",
446
+ "results_dict = json.loads(judgement)\n",
447
+ "ranks = results_dict[\"results\"]\n",
448
+ "for index, comp in enumerate(ranks):\n",
449
+ " print(f\"Rank {index+1}: {comp}\")"
450
+ ]
451
+ }
452
+ ],
453
+ "metadata": {
454
+ "kernelspec": {
455
+ "display_name": ".venv",
456
+ "language": "python",
457
+ "name": "python3"
458
+ },
459
+ "language_info": {
460
+ "codemirror_mode": {
461
+ "name": "ipython",
462
+ "version": 3
463
+ },
464
+ "file_extension": ".py",
465
+ "mimetype": "text/x-python",
466
+ "name": "python",
467
+ "nbconvert_exporter": "python",
468
+ "pygments_lexer": "ipython3",
469
+ "version": "3.12.11"
470
+ }
471
+ },
472
+ "nbformat": 4,
473
+ "nbformat_minor": 2
474
+ }
community_contributions/2_lab2_exercise.ipynb ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# From Judging to Synthesizing — Evolving Multi-Agent Patterns\n",
8
+ "\n",
9
+ "In the original 2_lab2.ipynb, we explored a powerful agentic design pattern: sending the same question to multiple large language models (LLMs), then using a separate “judge” agent to evaluate and rank their responses. This approach is valuable for identifying the single best answer among many, leveraging the strengths of ensemble reasoning and critical evaluation.\n",
10
+ "\n",
11
+ "However, selecting just one “winner” can leave valuable insights from other models untapped. To address this, I am shifting to a new agentic pattern in this notebook: the synthesizer/improver pattern. Instead of merely ranking responses, we will prompt a dedicated LLM to review all answers, extract the most compelling ideas from each, and synthesize them into a single, improved response. \n",
12
+ "\n",
13
+ "This approach aims to combine the collective intelligence of multiple models, producing an answer that is richer, more nuanced, and more robust than any individual response.\n"
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "code",
18
+ "execution_count": 1,
19
+ "metadata": {},
20
+ "outputs": [],
21
+ "source": [
22
+ "import os\n",
23
+ "import json\n",
24
+ "from dotenv import load_dotenv\n",
25
+ "from openai import OpenAI\n",
26
+ "from anthropic import Anthropic\n",
27
+ "from IPython.display import Markdown, display"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": null,
33
+ "metadata": {},
34
+ "outputs": [],
35
+ "source": [
36
+ "load_dotenv(override=True)"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": null,
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "# Print the key prefixes to help with any debugging\n",
46
+ "\n",
47
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
48
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
49
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
50
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
51
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
52
+ "\n",
53
+ "if openai_api_key:\n",
54
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
55
+ "else:\n",
56
+ " print(\"OpenAI API Key not set\")\n",
57
+ " \n",
58
+ "if anthropic_api_key:\n",
59
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
60
+ "else:\n",
61
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
62
+ "\n",
63
+ "if google_api_key:\n",
64
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
65
+ "else:\n",
66
+ " print(\"Google API Key not set (and this is optional)\")\n",
67
+ "\n",
68
+ "if deepseek_api_key:\n",
69
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
70
+ "else:\n",
71
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
72
+ "\n",
73
+ "if groq_api_key:\n",
74
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
75
+ "else:\n",
76
+ " print(\"Groq API Key not set (and this is optional)\")"
77
+ ]
78
+ },
79
+ {
80
+ "cell_type": "code",
81
+ "execution_count": 7,
82
+ "metadata": {},
83
+ "outputs": [],
84
+ "source": [
85
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their collective intelligence. \"\n",
86
+ "request += \"Answer only with the question, no explanation.\"\n",
87
+ "messages = [{\"role\": \"user\", \"content\": request}]"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "code",
92
+ "execution_count": null,
93
+ "metadata": {},
94
+ "outputs": [],
95
+ "source": [
96
+ "messages"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "execution_count": null,
102
+ "metadata": {},
103
+ "outputs": [],
104
+ "source": [
105
+ "openai = OpenAI()\n",
106
+ "response = openai.chat.completions.create(\n",
107
+ " model=\"gpt-4o-mini\",\n",
108
+ " messages=messages,\n",
109
+ ")\n",
110
+ "question = response.choices[0].message.content\n",
111
+ "print(question)\n"
112
+ ]
113
+ },
114
+ {
115
+ "cell_type": "code",
116
+ "execution_count": 10,
117
+ "metadata": {},
118
+ "outputs": [],
119
+ "source": [
120
+ "teammates = []\n",
121
+ "answers = []\n",
122
+ "messages = [{\"role\": \"user\", \"content\": question}]"
123
+ ]
124
+ },
125
+ {
126
+ "cell_type": "code",
127
+ "execution_count": null,
128
+ "metadata": {},
129
+ "outputs": [],
130
+ "source": [
131
+ "# The API we know well\n",
132
+ "\n",
133
+ "model_name = \"gpt-4o-mini\"\n",
134
+ "\n",
135
+ "response = openai.chat.completions.create(model=model_name, messages=messages)\n",
136
+ "answer = response.choices[0].message.content\n",
137
+ "\n",
138
+ "display(Markdown(answer))\n",
139
+ "teammates.append(model_name)\n",
140
+ "answers.append(answer)"
141
+ ]
142
+ },
143
+ {
144
+ "cell_type": "code",
145
+ "execution_count": null,
146
+ "metadata": {},
147
+ "outputs": [],
148
+ "source": [
149
+ "# Anthropic has a slightly different API, and Max Tokens is required\n",
150
+ "\n",
151
+ "model_name = \"claude-3-7-sonnet-latest\"\n",
152
+ "\n",
153
+ "claude = Anthropic()\n",
154
+ "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n",
155
+ "answer = response.content[0].text\n",
156
+ "\n",
157
+ "display(Markdown(answer))\n",
158
+ "teammates.append(model_name)\n",
159
+ "answers.append(answer)"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "code",
164
+ "execution_count": null,
165
+ "metadata": {},
166
+ "outputs": [],
167
+ "source": [
168
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
169
+ "model_name = \"gemini-2.0-flash\"\n",
170
+ "\n",
171
+ "response = gemini.chat.completions.create(model=model_name, messages=messages)\n",
172
+ "answer = response.choices[0].message.content\n",
173
+ "\n",
174
+ "display(Markdown(answer))\n",
175
+ "teammates.append(model_name)\n",
176
+ "answers.append(answer)"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type": "code",
181
+ "execution_count": null,
182
+ "metadata": {},
183
+ "outputs": [],
184
+ "source": [
185
+ "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
186
+ "model_name = \"deepseek-chat\"\n",
187
+ "\n",
188
+ "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n",
189
+ "answer = response.choices[0].message.content\n",
190
+ "\n",
191
+ "display(Markdown(answer))\n",
192
+ "teammates.append(model_name)\n",
193
+ "answers.append(answer)"
194
+ ]
195
+ },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": null,
199
+ "metadata": {},
200
+ "outputs": [],
201
+ "source": [
202
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
203
+ "model_name = \"llama-3.3-70b-versatile\"\n",
204
+ "\n",
205
+ "response = groq.chat.completions.create(model=model_name, messages=messages)\n",
206
+ "answer = response.choices[0].message.content\n",
207
+ "\n",
208
+ "display(Markdown(answer))\n",
209
+ "teammates.append(model_name)\n",
210
+ "answers.append(answer)"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "code",
215
+ "execution_count": null,
216
+ "metadata": {},
217
+ "outputs": [],
218
+ "source": [
219
+ "# So where are we?\n",
220
+ "\n",
221
+ "print(teammates)\n",
222
+ "print(answers)"
223
+ ]
224
+ },
225
+ {
226
+ "cell_type": "code",
227
+ "execution_count": null,
228
+ "metadata": {},
229
+ "outputs": [],
230
+ "source": [
231
+ "# It's nice to know how to use \"zip\"\n",
232
+ "for teammate, answer in zip(teammates, answers):\n",
233
+ " print(f\"Teammate: {teammate}\\n\\n{answer}\")"
234
+ ]
235
+ },
236
+ {
237
+ "cell_type": "code",
238
+ "execution_count": 23,
239
+ "metadata": {},
240
+ "outputs": [],
241
+ "source": [
242
+ "# Let's bring this together - note the use of \"enumerate\"\n",
243
+ "\n",
244
+ "together = \"\"\n",
245
+ "for index, answer in enumerate(answers):\n",
246
+ " together += f\"# Response from teammate {index+1}\\n\\n\"\n",
247
+ " together += answer + \"\\n\\n\""
248
+ ]
249
+ },
250
+ {
251
+ "cell_type": "code",
252
+ "execution_count": null,
253
+ "metadata": {},
254
+ "outputs": [],
255
+ "source": [
256
+ "print(together)"
257
+ ]
258
+ },
259
+ {
260
+ "cell_type": "code",
261
+ "execution_count": 36,
262
+ "metadata": {},
263
+ "outputs": [],
264
+ "source": [
265
+ "formatter = f\"\"\"You are taking the nost interesting ideas fron {len(teammates)} teammates.\n",
266
+ "Each model has been given this question:\n",
267
+ "\n",
268
+ "{question}\n",
269
+ "\n",
270
+ "Your job is to evaluate each response for clarity and strength of argument, select the most relevant ideas and make a report, including a title, subtitles to separate sections, and quoting the LLM providing the idea.\n",
271
+ "From that, you will create a new improved answer.\"\"\""
272
+ ]
273
+ },
274
+ {
275
+ "cell_type": "code",
276
+ "execution_count": null,
277
+ "metadata": {},
278
+ "outputs": [],
279
+ "source": [
280
+ "print(formatter)"
281
+ ]
282
+ },
283
+ {
284
+ "cell_type": "code",
285
+ "execution_count": 38,
286
+ "metadata": {},
287
+ "outputs": [],
288
+ "source": [
289
+ "formatter_messages = [{\"role\": \"user\", \"content\": formatter}]"
290
+ ]
291
+ },
292
+ {
293
+ "cell_type": "code",
294
+ "execution_count": null,
295
+ "metadata": {},
296
+ "outputs": [],
297
+ "source": [
298
+ "openai = OpenAI()\n",
299
+ "response = openai.chat.completions.create(\n",
300
+ " model=\"o3-mini\",\n",
301
+ " messages=formatter_messages,\n",
302
+ ")\n",
303
+ "results = response.choices[0].message.content\n",
304
+ "display(Markdown(results))"
305
+ ]
306
+ },
307
+ {
308
+ "cell_type": "code",
309
+ "execution_count": null,
310
+ "metadata": {},
311
+ "outputs": [],
312
+ "source": []
313
+ }
314
+ ],
315
+ "metadata": {
316
+ "kernelspec": {
317
+ "display_name": ".venv",
318
+ "language": "python",
319
+ "name": "python3"
320
+ },
321
+ "language_info": {
322
+ "codemirror_mode": {
323
+ "name": "ipython",
324
+ "version": 3
325
+ },
326
+ "file_extension": ".py",
327
+ "mimetype": "text/x-python",
328
+ "name": "python",
329
+ "nbconvert_exporter": "python",
330
+ "pygments_lexer": "ipython3",
331
+ "version": "3.12.7"
332
+ }
333
+ },
334
+ "nbformat": 4,
335
+ "nbformat_minor": 2
336
+ }
community_contributions/2_lab2_exercise_BrettSanders_ChainOfThought.ipynb ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "raw",
5
+ "metadata": {
6
+ "vscode": {
7
+ "languageId": "raw"
8
+ }
9
+ },
10
+ "source": [
11
+ "# Lab 2 Exercise - Extending the Patterns\n",
12
+ "\n",
13
+ "This notebook extends the original lab by adding the Chain of Thought pattern to enhance the evaluation process.\n"
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "code",
18
+ "execution_count": 1,
19
+ "metadata": {},
20
+ "outputs": [],
21
+ "source": [
22
+ "# Import required packages\n",
23
+ "import os\n",
24
+ "import json\n",
25
+ "from dotenv import load_dotenv\n",
26
+ "from openai import OpenAI\n",
27
+ "from anthropic import Anthropic\n",
28
+ "from IPython.display import Markdown, display\n"
29
+ ]
30
+ },
31
+ {
32
+ "cell_type": "code",
33
+ "execution_count": null,
34
+ "metadata": {},
35
+ "outputs": [],
36
+ "source": [
37
+ "# Load environment variables\n",
38
+ "load_dotenv(override=True)\n"
39
+ ]
40
+ },
41
+ {
42
+ "cell_type": "code",
43
+ "execution_count": 3,
44
+ "metadata": {},
45
+ "outputs": [],
46
+ "source": [
47
+ "# Initialize API clients\n",
48
+ "openai = OpenAI()\n",
49
+ "claude = Anthropic()\n"
50
+ ]
51
+ },
52
+ {
53
+ "cell_type": "code",
54
+ "execution_count": null,
55
+ "metadata": {},
56
+ "outputs": [],
57
+ "source": [
58
+ "# Original question generation\n",
59
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n",
60
+ "request += \"Answer only with the question, no explanation.\"\n",
61
+ "messages = [{\"role\": \"user\", \"content\": request}]\n",
62
+ "\n",
63
+ "response = openai.chat.completions.create(\n",
64
+ " model=\"gpt-4o-mini\",\n",
65
+ " messages=messages,\n",
66
+ ")\n",
67
+ "question = response.choices[0].message.content\n",
68
+ "print(question)\n"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": null,
74
+ "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "# Get responses from multiple models\n",
78
+ "competitors = []\n",
79
+ "answers = []\n",
80
+ "messages = [{\"role\": \"user\", \"content\": question}]\n",
81
+ "\n",
82
+ "# OpenAI\n",
83
+ "response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages)\n",
84
+ "answer = response.choices[0].message.content\n",
85
+ "competitors.append(\"gpt-4o-mini\")\n",
86
+ "answers.append(answer)\n",
87
+ "display(Markdown(answer))\n",
88
+ "\n",
89
+ "# Claude\n",
90
+ "response = claude.messages.create(model=\"claude-3-7-sonnet-latest\", messages=messages, max_tokens=1000)\n",
91
+ "answer = response.content[0].text\n",
92
+ "competitors.append(\"claude-3-7-sonnet-latest\")\n",
93
+ "answers.append(answer)\n",
94
+ "display(Markdown(answer))\n"
95
+ ]
96
+ },
97
+ {
98
+ "cell_type": "code",
99
+ "execution_count": 6,
100
+ "metadata": {},
101
+ "outputs": [],
102
+ "source": [
103
+ "# NEW: Chain of Thought Evaluation\n",
104
+ "# First, let's create a detailed evaluation prompt that encourages step-by-step reasoning\n",
105
+ "\n",
106
+ "evaluation_prompt = f\"\"\"You are an expert evaluator of AI responses. Your task is to analyze and rank the following responses to this question:\n",
107
+ "\n",
108
+ "{question}\n",
109
+ "\n",
110
+ "Please follow these steps in your evaluation:\n",
111
+ "\n",
112
+ "1. For each response:\n",
113
+ " - Identify the main arguments presented\n",
114
+ " - Evaluate the clarity and coherence of the reasoning\n",
115
+ " - Assess the depth and breadth of the analysis\n",
116
+ " - Note any unique insights or perspectives\n",
117
+ "\n",
118
+ "2. Compare the responses:\n",
119
+ " - How do they differ in their approach?\n",
120
+ " - Which response demonstrates the most sophisticated understanding?\n",
121
+ " - Which response provides the most practical and actionable insights?\n",
122
+ "\n",
123
+ "3. Provide your final ranking with detailed justification for each position.\n",
124
+ "\n",
125
+ "Here are the responses:\n",
126
+ "\n",
127
+ "{'\\\\n\\\\n'.join([f'Response {i+1} ({competitors[i]}):\\\\n{answer}' for i, answer in enumerate(answers)])}\n",
128
+ "\n",
129
+ "Please provide your evaluation in JSON format with the following structure:\n",
130
+ "{{\n",
131
+ " \"detailed_analysis\": [\n",
132
+ " {{\"competitor\": \"name\", \"strengths\": [], \"weaknesses\": [], \"unique_aspects\": []}},\n",
133
+ " ...\n",
134
+ " ],\n",
135
+ " \"comparative_analysis\": \"detailed comparison of responses\",\n",
136
+ " \"final_ranking\": [\"ranked competitor numbers\"],\n",
137
+ " \"justification\": \"detailed explanation of the ranking\"\n",
138
+ "}}\"\"\"\n"
139
+ ]
140
+ },
141
+ {
142
+ "cell_type": "code",
143
+ "execution_count": null,
144
+ "metadata": {},
145
+ "outputs": [],
146
+ "source": [
147
+ "# Get the detailed evaluation\n",
148
+ "evaluation_messages = [{\"role\": \"user\", \"content\": evaluation_prompt}]\n",
149
+ "\n",
150
+ "response = openai.chat.completions.create(\n",
151
+ " model=\"gpt-4o-mini\",\n",
152
+ " messages=evaluation_messages,\n",
153
+ ")\n",
154
+ "detailed_evaluation = response.choices[0].message.content\n",
155
+ "print(detailed_evaluation)\n"
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "code",
160
+ "execution_count": null,
161
+ "metadata": {},
162
+ "outputs": [],
163
+ "source": [
164
+ "# Parse and display the results in a more readable format\n",
165
+ "\n",
166
+ "# Clean up the JSON string by removing markdown code block markers\n",
167
+ "json_str = detailed_evaluation.replace(\"```json\", \"\").replace(\"```\", \"\").strip()\n",
168
+ "\n",
169
+ "evaluation_dict = json.loads(json_str)\n",
170
+ "\n",
171
+ "print(\"Detailed Analysis:\")\n",
172
+ "for analysis in evaluation_dict[\"detailed_analysis\"]:\n",
173
+ " print(f\"\\nCompetitor: {analysis['competitor']}\")\n",
174
+ " print(\"Strengths:\")\n",
175
+ " for strength in analysis['strengths']:\n",
176
+ " print(f\"- {strength}\")\n",
177
+ " print(\"\\nWeaknesses:\")\n",
178
+ " for weakness in analysis['weaknesses']:\n",
179
+ " print(f\"- {weakness}\")\n",
180
+ " print(\"\\nUnique Aspects:\")\n",
181
+ " for aspect in analysis['unique_aspects']:\n",
182
+ " print(f\"- {aspect}\")\n",
183
+ "\n",
184
+ "print(\"\\nComparative Analysis:\")\n",
185
+ "print(evaluation_dict[\"comparative_analysis\"])\n",
186
+ "\n",
187
+ "print(\"\\nFinal Ranking:\")\n",
188
+ "for i, rank in enumerate(evaluation_dict[\"final_ranking\"]):\n",
189
+ " print(f\"{i+1}. {competitors[int(rank)-1]}\")\n",
190
+ "\n",
191
+ "print(\"\\nJustification:\")\n",
192
+ "print(evaluation_dict[\"justification\"])\n"
193
+ ]
194
+ },
195
+ {
196
+ "cell_type": "raw",
197
+ "metadata": {
198
+ "vscode": {
199
+ "languageId": "raw"
200
+ }
201
+ },
202
+ "source": [
203
+ "## Pattern Analysis\n",
204
+ "\n",
205
+ "This enhanced version uses several agentic design patterns:\n",
206
+ "\n",
207
+ "1. **Multi-agent Collaboration**: Sending the same question to multiple LLMs\n",
208
+ "2. **Evaluation/Judgment Pattern**: Using one LLM to evaluate responses from others\n",
209
+ "3. **Parallel Processing**: Running multiple models simultaneously\n",
210
+ "4. **Chain of Thought**: Added a structured, step-by-step evaluation process that breaks down the analysis into clear stages\n",
211
+ "\n",
212
+ "The Chain of Thought pattern is particularly valuable here because it:\n",
213
+ "- Forces the evaluator to consider multiple aspects of each response\n",
214
+ "- Provides more detailed and structured feedback\n",
215
+ "- Makes the evaluation process more transparent and explainable\n",
216
+ "- Helps identify specific strengths and weaknesses in each response\n"
217
+ ]
218
+ }
219
+ ],
220
+ "metadata": {
221
+ "kernelspec": {
222
+ "display_name": ".venv",
223
+ "language": "python",
224
+ "name": "python3"
225
+ },
226
+ "language_info": {
227
+ "codemirror_mode": {
228
+ "name": "ipython",
229
+ "version": 3
230
+ },
231
+ "file_extension": ".py",
232
+ "mimetype": "text/x-python",
233
+ "name": "python",
234
+ "nbconvert_exporter": "python",
235
+ "pygments_lexer": "ipython3",
236
+ "version": "3.12.7"
237
+ }
238
+ },
239
+ "nbformat": 4,
240
+ "nbformat_minor": 2
241
+ }
community_contributions/2_lab2_multi-evaluation-criteria.ipynb ADDED
@@ -0,0 +1,506 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to the Second Lab - Week 1, Day 3\n",
8
+ "\n",
9
+ "Today we will work with lots of models! This is a way to get comfortable with APIs."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "metadata": {},
15
+ "source": [
16
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
17
+ " <tr>\n",
18
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
19
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
20
+ " </td>\n",
21
+ " <td>\n",
22
+ " <h2 style=\"color:#ff7800;\">Important point - please read</h2>\n",
23
+ " <span style=\"color:#ff7800;\">The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, <b>after</b> watching the lecture. Add print statements to understand what's going on, and then come up with your own variations.<br/><br/>If you have time, I'd love it if you submit a PR for changes in the community_contributions folder - instructions in the resources. Also, if you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n",
24
+ " </span>\n",
25
+ " </td>\n",
26
+ " </tr>\n",
27
+ "</table>"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": null,
33
+ "metadata": {},
34
+ "outputs": [],
35
+ "source": [
36
+ "# Start with imports - ask ChatGPT to explain any package that you don't know\n",
37
+ "\n",
38
+ "import os\n",
39
+ "import json\n",
40
+ "from dotenv import load_dotenv\n",
41
+ "from openai import OpenAI\n",
42
+ "from anthropic import Anthropic\n",
43
+ "from IPython.display import Markdown, display"
44
+ ]
45
+ },
46
+ {
47
+ "cell_type": "code",
48
+ "execution_count": null,
49
+ "metadata": {},
50
+ "outputs": [],
51
+ "source": [
52
+ "# Always remember to do this!\n",
53
+ "load_dotenv(override=True)"
54
+ ]
55
+ },
56
+ {
57
+ "cell_type": "code",
58
+ "execution_count": null,
59
+ "metadata": {},
60
+ "outputs": [],
61
+ "source": [
62
+ "# Print the key prefixes to help with any debugging\n",
63
+ "\n",
64
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
65
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
66
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
67
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
68
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
69
+ "\n",
70
+ "if openai_api_key:\n",
71
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
72
+ "else:\n",
73
+ " print(\"OpenAI API Key not set\")\n",
74
+ " \n",
75
+ "if anthropic_api_key:\n",
76
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
77
+ "else:\n",
78
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
79
+ "\n",
80
+ "if google_api_key:\n",
81
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
82
+ "else:\n",
83
+ " print(\"Google API Key not set (and this is optional)\")\n",
84
+ "\n",
85
+ "if deepseek_api_key:\n",
86
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
87
+ "else:\n",
88
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
89
+ "\n",
90
+ "if groq_api_key:\n",
91
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
92
+ "else:\n",
93
+ " print(\"Groq API Key not set (and this is optional)\")"
94
+ ]
95
+ },
96
+ {
97
+ "cell_type": "code",
98
+ "execution_count": null,
99
+ "metadata": {},
100
+ "outputs": [],
101
+ "source": [
102
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n",
103
+ "request += \"Answer only with the question, no explanation.\"\n",
104
+ "messages = [{\"role\": \"user\", \"content\": request}]"
105
+ ]
106
+ },
107
+ {
108
+ "cell_type": "code",
109
+ "execution_count": null,
110
+ "metadata": {},
111
+ "outputs": [],
112
+ "source": [
113
+ "messages"
114
+ ]
115
+ },
116
+ {
117
+ "cell_type": "code",
118
+ "execution_count": null,
119
+ "metadata": {},
120
+ "outputs": [],
121
+ "source": [
122
+ "openai = OpenAI()\n",
123
+ "response = openai.chat.completions.create(\n",
124
+ " model=\"gpt-4o-mini\",\n",
125
+ " messages=messages,\n",
126
+ ")\n",
127
+ "question = response.choices[0].message.content\n",
128
+ "print(question)\n"
129
+ ]
130
+ },
131
+ {
132
+ "cell_type": "code",
133
+ "execution_count": null,
134
+ "metadata": {},
135
+ "outputs": [],
136
+ "source": [
137
+ "competitors = []\n",
138
+ "answers = []\n",
139
+ "messages = [{\"role\": \"user\", \"content\": question}]"
140
+ ]
141
+ },
142
+ {
143
+ "cell_type": "code",
144
+ "execution_count": null,
145
+ "metadata": {},
146
+ "outputs": [],
147
+ "source": [
148
+ "# The API we know well\n",
149
+ "\n",
150
+ "model_name = \"gpt-4o-mini\"\n",
151
+ "\n",
152
+ "response = openai.chat.completions.create(model=model_name, messages=messages)\n",
153
+ "answer = response.choices[0].message.content\n",
154
+ "\n",
155
+ "display(Markdown(answer))\n",
156
+ "competitors.append(model_name)\n",
157
+ "answers.append(answer)"
158
+ ]
159
+ },
160
+ {
161
+ "cell_type": "code",
162
+ "execution_count": null,
163
+ "metadata": {},
164
+ "outputs": [],
165
+ "source": [
166
+ "# Anthropic has a slightly different API, and Max Tokens is required\n",
167
+ "\n",
168
+ "model_name = \"claude-sonnet-4-latest\"\n",
169
+ "\n",
170
+ "claude = Anthropic()\n",
171
+ "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n",
172
+ "answer = response.content[0].text\n",
173
+ "\n",
174
+ "display(Markdown(answer))\n",
175
+ "competitors.append(model_name)\n",
176
+ "answers.append(answer)"
177
+ ]
178
+ },
179
+ {
180
+ "cell_type": "code",
181
+ "execution_count": null,
182
+ "metadata": {},
183
+ "outputs": [],
184
+ "source": [
185
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
186
+ "model_name = \"gemini-2.0-flash\"\n",
187
+ "\n",
188
+ "response = gemini.chat.completions.create(model=model_name, messages=messages)\n",
189
+ "answer = response.choices[0].message.content\n",
190
+ "\n",
191
+ "display(Markdown(answer))\n",
192
+ "competitors.append(model_name)\n",
193
+ "answers.append(answer)"
194
+ ]
195
+ },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": null,
199
+ "metadata": {},
200
+ "outputs": [],
201
+ "source": [
202
+ "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
203
+ "model_name = \"deepseek-chat\"\n",
204
+ "\n",
205
+ "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n",
206
+ "answer = response.choices[0].message.content\n",
207
+ "\n",
208
+ "display(Markdown(answer))\n",
209
+ "competitors.append(model_name)\n",
210
+ "answers.append(answer)"
211
+ ]
212
+ },
213
+ {
214
+ "cell_type": "code",
215
+ "execution_count": null,
216
+ "metadata": {},
217
+ "outputs": [],
218
+ "source": [
219
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
220
+ "model_name = \"llama-3.3-70b-versatile\"\n",
221
+ "\n",
222
+ "response = groq.chat.completions.create(model=model_name, messages=messages)\n",
223
+ "answer = response.choices[0].message.content\n",
224
+ "\n",
225
+ "display(Markdown(answer))\n",
226
+ "competitors.append(model_name)\n",
227
+ "answers.append(answer)\n"
228
+ ]
229
+ },
230
+ {
231
+ "cell_type": "markdown",
232
+ "metadata": {},
233
+ "source": [
234
+ "## For the next cell, we will use Ollama\n",
235
+ "\n",
236
+ "Ollama runs a local web service that gives an OpenAI compatible endpoint, \n",
237
+ "and runs models locally using high performance C++ code.\n",
238
+ "\n",
239
+ "If you don't have Ollama, install it here by visiting https://ollama.com then pressing Download and following the instructions.\n",
240
+ "\n",
241
+ "After it's installed, you should be able to visit here: http://localhost:11434 and see the message \"Ollama is running\"\n",
242
+ "\n",
243
+ "You might need to restart Cursor (and maybe reboot). Then open a Terminal (control+\\`) and run `ollama serve`\n",
244
+ "\n",
245
+ "Useful Ollama commands (run these in the terminal, or with an exclamation mark in this notebook):\n",
246
+ "\n",
247
+ "`ollama pull <model_name>` downloads a model locally \n",
248
+ "`ollama ls` lists all the models you've downloaded \n",
249
+ "`ollama rm <model_name>` deletes the specified model from your downloads"
250
+ ]
251
+ },
252
+ {
253
+ "cell_type": "markdown",
254
+ "metadata": {},
255
+ "source": [
256
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
257
+ " <tr>\n",
258
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
259
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
260
+ " </td>\n",
261
+ " <td>\n",
262
+ " <h2 style=\"color:#ff7800;\">Super important - ignore me at your peril!</h2>\n",
263
+ " <span style=\"color:#ff7800;\">The model called <b>llama3.3</b> is FAR too large for home computers - it's not intended for personal computing and will consume all your resources! Stick with the nicely sized <b>llama3.2</b> or <b>llama3.2:1b</b> and if you want larger, try llama3.1 or smaller variants of Qwen, Gemma, Phi or DeepSeek. See the <A href=\"https://ollama.com/models\">the Ollama models page</a> for a full list of models and sizes.\n",
264
+ " </span>\n",
265
+ " </td>\n",
266
+ " </tr>\n",
267
+ "</table>"
268
+ ]
269
+ },
270
+ {
271
+ "cell_type": "code",
272
+ "execution_count": null,
273
+ "metadata": {},
274
+ "outputs": [],
275
+ "source": [
276
+ "!ollama pull llama3.2"
277
+ ]
278
+ },
279
+ {
280
+ "cell_type": "code",
281
+ "execution_count": null,
282
+ "metadata": {},
283
+ "outputs": [],
284
+ "source": [
285
+ "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
286
+ "model_name = \"llama3\"\n",
287
+ "\n",
288
+ "response = ollama.chat.completions.create(model=model_name, messages=messages)\n",
289
+ "answer = response.choices[0].message.content\n",
290
+ "\n",
291
+ "display(Markdown(answer))\n",
292
+ "competitors.append(model_name)\n",
293
+ "answers.append(answer)"
294
+ ]
295
+ },
296
+ {
297
+ "cell_type": "code",
298
+ "execution_count": null,
299
+ "metadata": {},
300
+ "outputs": [],
301
+ "source": [
302
+ "# So where are we?\n",
303
+ "\n",
304
+ "print(competitors)\n",
305
+ "print(answers)\n"
306
+ ]
307
+ },
308
+ {
309
+ "cell_type": "code",
310
+ "execution_count": null,
311
+ "metadata": {},
312
+ "outputs": [],
313
+ "source": [
314
+ "# It's nice to know how to use \"zip\"\n",
315
+ "for competitor, answer in zip(competitors, answers):\n",
316
+ " print(f\"Competitor: {competitor}\\n\\n{answer}\")\n"
317
+ ]
318
+ },
319
+ {
320
+ "cell_type": "code",
321
+ "execution_count": null,
322
+ "metadata": {},
323
+ "outputs": [],
324
+ "source": [
325
+ "for competitor, answer in zip(competitors, answers):\n",
326
+ " display(Markdown(f\"# Competitor: {competitor}\\n\\n{answer}\"))"
327
+ ]
328
+ },
329
+ {
330
+ "cell_type": "code",
331
+ "execution_count": null,
332
+ "metadata": {},
333
+ "outputs": [],
334
+ "source": [
335
+ "# Let's bring this together - note the use of \"enumerate\"\n",
336
+ "\n",
337
+ "together = \"\"\n",
338
+ "for index, answer in enumerate(answers):\n",
339
+ " together += f\"# Response from competitor {index+1}\\n\\n\"\n",
340
+ " together += answer + \"\\n\\n\""
341
+ ]
342
+ },
343
+ {
344
+ "cell_type": "code",
345
+ "execution_count": null,
346
+ "metadata": {},
347
+ "outputs": [],
348
+ "source": [
349
+ "print(together)"
350
+ ]
351
+ },
352
+ {
353
+ "cell_type": "code",
354
+ "execution_count": null,
355
+ "metadata": {},
356
+ "outputs": [],
357
+ "source": [
358
+ "evaluation_criteria = [\"Effectiveness in resolving the conflict\", \"Clarity of argument\", \"Creativity of solution\", \"Strength of argument\", \"conciseness\", \"applicability to a business context\"]\n",
359
+ "\n",
360
+ "judgements = []\n",
361
+ "\n",
362
+ "for evaluation_criterion in evaluation_criteria:\n",
363
+ "\n",
364
+ " judgements.append (f\"\"\"You are judging a competition between {len(competitors)} competitors.\n",
365
+ " Each model has been given this question:\n",
366
+ "\n",
367
+ " {question}\n",
368
+ "\n",
369
+ " Your job is to evaluate each response for {evaluation_criterion}, and rank them in order of best to worst.\n",
370
+ " Respond with JSON, and only JSON, with the following format:\n",
371
+ " {{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n",
372
+ "\n",
373
+ " Here are the responses from each competitor:\n",
374
+ "\n",
375
+ " {together}\n",
376
+ "\n",
377
+ " Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\")\n"
378
+ ]
379
+ },
380
+ {
381
+ "cell_type": "code",
382
+ "execution_count": null,
383
+ "metadata": {},
384
+ "outputs": [],
385
+ "source": [
386
+ "print(judgements[1])\n"
387
+ ]
388
+ },
389
+ {
390
+ "cell_type": "code",
391
+ "execution_count": null,
392
+ "metadata": {},
393
+ "outputs": [],
394
+ "source": [
395
+ "\n",
396
+ "judge_messages = []\n",
397
+ "for judgement in judgements:\n",
398
+ " judge_messages.append ([{\"role\": \"user\", \"content\": judgement}])"
399
+ ]
400
+ },
401
+ {
402
+ "cell_type": "code",
403
+ "execution_count": null,
404
+ "metadata": {},
405
+ "outputs": [],
406
+ "source": [
407
+ "results = []\n",
408
+ "# Judgement time!\n",
409
+ "for judge_message in judge_messages:\n",
410
+ " openai = OpenAI()\n",
411
+ " response = openai.chat.completions.create(\n",
412
+ " model=\"o3-mini\",\n",
413
+ " messages=judge_message,\n",
414
+ " )\n",
415
+ " results.append (response.choices[0].message.content)\n",
416
+ " print(results[0])\n"
417
+ ]
418
+ },
419
+ {
420
+ "cell_type": "code",
421
+ "execution_count": null,
422
+ "metadata": {},
423
+ "outputs": [],
424
+ "source": [
425
+ "for result in results:\n",
426
+ " print(result)"
427
+ ]
428
+ },
429
+ {
430
+ "cell_type": "code",
431
+ "execution_count": null,
432
+ "metadata": {},
433
+ "outputs": [],
434
+ "source": [
435
+ "# OK let's turn this into results!\n",
436
+ "\n",
437
+ "for result, evaluation_criterion in zip(results, evaluation_criteria):\n",
438
+ " results_dict = json.loads(result)\n",
439
+ " ranks = results_dict[\"results\"]\n",
440
+ " display(Markdown(f\"### {evaluation_criterion}\"))\n",
441
+ " for index, result in enumerate(ranks):\n",
442
+ " competitor = competitors[int(result)-1] \n",
443
+ " display(Markdown(f\"Rank {index+1}: {competitor}\"))"
444
+ ]
445
+ },
446
+ {
447
+ "cell_type": "markdown",
448
+ "metadata": {},
449
+ "source": [
450
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
451
+ " <tr>\n",
452
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
453
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
454
+ " </td>\n",
455
+ " <td>\n",
456
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
457
+ " <span style=\"color:#ff7800;\">Which pattern(s) did this use? Try updating this to add another Agentic design pattern.\n",
458
+ " </span>\n",
459
+ " </td>\n",
460
+ " </tr>\n",
461
+ "</table>"
462
+ ]
463
+ },
464
+ {
465
+ "cell_type": "markdown",
466
+ "metadata": {},
467
+ "source": [
468
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
469
+ " <tr>\n",
470
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
471
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
472
+ " </td>\n",
473
+ " <td>\n",
474
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
475
+ " <span style=\"color:#00bfff;\">These kinds of patterns - to send a task to multiple models, and evaluate results,\n",
476
+ " are common where you need to improve the quality of your LLM response. This approach can be universally applied\n",
477
+ " to business projects where accuracy is critical.\n",
478
+ " </span>\n",
479
+ " </td>\n",
480
+ " </tr>\n",
481
+ "</table>"
482
+ ]
483
+ }
484
+ ],
485
+ "metadata": {
486
+ "kernelspec": {
487
+ "display_name": ".venv",
488
+ "language": "python",
489
+ "name": "python3"
490
+ },
491
+ "language_info": {
492
+ "codemirror_mode": {
493
+ "name": "ipython",
494
+ "version": 3
495
+ },
496
+ "file_extension": ".py",
497
+ "mimetype": "text/x-python",
498
+ "name": "python",
499
+ "nbconvert_exporter": "python",
500
+ "pygments_lexer": "ipython3",
501
+ "version": "3.12.10"
502
+ }
503
+ },
504
+ "nbformat": 4,
505
+ "nbformat_minor": 2
506
+ }
community_contributions/2_lab2_reflection_pattern.ipynb ADDED
@@ -0,0 +1,311 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to the Second Lab - Week 1, Day 3\n",
8
+ "\n",
9
+ "Today we will work with lots of models! This is a way to get comfortable with APIs."
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "metadata": {},
15
+ "source": [
16
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
17
+ " <tr>\n",
18
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
19
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
20
+ " </td>\n",
21
+ " <td>\n",
22
+ " <h2 style=\"color:#ff7800;\">Important point - please read</h2>\n",
23
+ " <span style=\"color:#ff7800;\">The way I collaborate with you may be different to other courses you've taken. I prefer not to type code while you watch. Rather, I execute Jupyter Labs, like this, and give you an intuition for what's going on. My suggestion is that you carefully execute this yourself, <b>after</b> watching the lecture. Add print statements to understand what's going on, and then come up with your own variations.<br/><br/>If you have time, I'd love it if you submit a PR for changes in the community_contributions folder - instructions in the resources. Also, if you have a Github account, use this to showcase your variations. Not only is this essential practice, but it demonstrates your skills to others, including perhaps future clients or employers...\n",
24
+ " </span>\n",
25
+ " </td>\n",
26
+ " </tr>\n",
27
+ "</table>"
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "markdown",
32
+ "metadata": {},
33
+ "source": [
34
+ "This version adds Reflection pattern where we ask each model to critique and improve its own answer."
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "code",
39
+ "execution_count": 9,
40
+ "metadata": {},
41
+ "outputs": [],
42
+ "source": [
43
+ "# Start with imports - ask ChatGPT to explain any package that you don't know\n",
44
+ "\n",
45
+ "import os\n",
46
+ "import json\n",
47
+ "from dotenv import load_dotenv\n",
48
+ "from openai import OpenAI\n",
49
+ "from anthropic import Anthropic\n",
50
+ "from IPython.display import Markdown, display"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "markdown",
55
+ "metadata": {},
56
+ "source": []
57
+ },
58
+ {
59
+ "cell_type": "code",
60
+ "execution_count": 12,
61
+ "metadata": {},
62
+ "outputs": [],
63
+ "source": [
64
+ "request = \"Please come up with a challenging, nuanced question that I can ask a number of LLMs to evaluate their intelligence. \"\n",
65
+ "request += \"Answer only with the question, no explanation.\"\n",
66
+ "messages = [{\"role\": \"user\", \"content\": request}]"
67
+ ]
68
+ },
69
+ {
70
+ "cell_type": "code",
71
+ "execution_count": null,
72
+ "metadata": {},
73
+ "outputs": [],
74
+ "source": [
75
+ "messages"
76
+ ]
77
+ },
78
+ {
79
+ "cell_type": "code",
80
+ "execution_count": 14,
81
+ "metadata": {},
82
+ "outputs": [],
83
+ "source": [
84
+ "competitors = []\n",
85
+ "answers = []\n",
86
+ "messages = [{\"role\": \"user\", \"content\": question}]"
87
+ ]
88
+ },
89
+ {
90
+ "cell_type": "code",
91
+ "execution_count": null,
92
+ "metadata": {},
93
+ "outputs": [],
94
+ "source": [
95
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
96
+ "model_name = \"gemini-2.0-flash\"\n",
97
+ "\n",
98
+ "response = gemini.chat.completions.create(model=model_name, messages=messages)\n",
99
+ "answer = response.choices[0].message.content\n",
100
+ "\n",
101
+ "display(Markdown(answer))\n",
102
+ "competitors.append(model_name)\n",
103
+ "answers.append(answer)"
104
+ ]
105
+ },
106
+ {
107
+ "cell_type": "code",
108
+ "execution_count": null,
109
+ "metadata": {},
110
+ "outputs": [],
111
+ "source": [
112
+ "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
113
+ "model_name = \"deepseek-chat\"\n",
114
+ "\n",
115
+ "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n",
116
+ "answer = response.choices[0].message.content\n",
117
+ "\n",
118
+ "display(Markdown(answer))\n",
119
+ "competitors.append(model_name)\n",
120
+ "answers.append(answer)"
121
+ ]
122
+ },
123
+ {
124
+ "cell_type": "code",
125
+ "execution_count": null,
126
+ "metadata": {},
127
+ "outputs": [],
128
+ "source": [
129
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
130
+ "model_name = \"llama-3.3-70b-versatile\"\n",
131
+ "\n",
132
+ "response = groq.chat.completions.create(model=model_name, messages=messages)\n",
133
+ "answer = response.choices[0].message.content\n",
134
+ "\n",
135
+ "display(Markdown(answer))\n",
136
+ "competitors.append(model_name)\n",
137
+ "answers.append(answer)\n"
138
+ ]
139
+ },
140
+ {
141
+ "cell_type": "markdown",
142
+ "metadata": {},
143
+ "source": [
144
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
145
+ " <tr>\n",
146
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
147
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
148
+ " </td>\n",
149
+ " <td>\n",
150
+ " <h2 style=\"color:#ff7800;\">Super important - ignore me at your peril!</h2>\n",
151
+ " <span style=\"color:#ff7800;\">The model called <b>llama3.3</b> is FAR too large for home computers - it's not intended for personal computing and will consume all your resources! Stick with the nicely sized <b>llama3.2</b> or <b>llama3.2:1b</b> and if you want larger, try llama3.1 or smaller variants of Qwen, Gemma, Phi or DeepSeek. See the <A href=\"https://ollama.com/models\">the Ollama models page</a> for a full list of models and sizes.\n",
152
+ " </span>\n",
153
+ " </td>\n",
154
+ " </tr>\n",
155
+ "</table>"
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "code",
160
+ "execution_count": null,
161
+ "metadata": {},
162
+ "outputs": [],
163
+ "source": [
164
+ "!ollama pull llama3.2"
165
+ ]
166
+ },
167
+ {
168
+ "cell_type": "code",
169
+ "execution_count": 33,
170
+ "metadata": {},
171
+ "outputs": [],
172
+ "source": [
173
+ "# Let's bring this together - note the use of \"enumerate\"\n",
174
+ "\n",
175
+ "together = \"\"\n",
176
+ "for index, answer in enumerate(answers):\n",
177
+ " together += f\"# Response from competitor {index+1}\\n\\n\"\n",
178
+ " together += answer + \"\\n\\n\""
179
+ ]
180
+ },
181
+ {
182
+ "cell_type": "code",
183
+ "execution_count": 36,
184
+ "metadata": {},
185
+ "outputs": [],
186
+ "source": [
187
+ "judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n",
188
+ "Each model has been given this question:\n",
189
+ "\n",
190
+ "{question}\n",
191
+ "\n",
192
+ "Your job is to evaluate each response for clarity and strength of argument, and rank them in order of best to worst.\n",
193
+ "Respond with JSON, and only JSON, with the following format:\n",
194
+ "{{\"results\": [\"best competitor number\", \"second best competitor number\", \"third best competitor number\", ...]}}\n",
195
+ "\n",
196
+ "Here are the responses from each competitor:\n",
197
+ "\n",
198
+ "{together}\n",
199
+ "\n",
200
+ "Now respond with the JSON with the ranked order of the competitors, nothing else. Do not include markdown formatting or code blocks.\"\"\"\n"
201
+ ]
202
+ },
203
+ {
204
+ "cell_type": "code",
205
+ "execution_count": 38,
206
+ "metadata": {},
207
+ "outputs": [],
208
+ "source": [
209
+ "judge_messages = [{\"role\": \"user\", \"content\": judge}]"
210
+ ]
211
+ },
212
+ {
213
+ "cell_type": "markdown",
214
+ "metadata": {},
215
+ "source": [
216
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
217
+ " <tr>\n",
218
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
219
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
220
+ " </td>\n",
221
+ " <td>\n",
222
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
223
+ " <span style=\"color:#ff7800;\">Which pattern(s) did this use? Try updating this to add another Agentic design pattern.\n",
224
+ " </span>\n",
225
+ " </td>\n",
226
+ " </tr>\n",
227
+ "</table>"
228
+ ]
229
+ },
230
+ {
231
+ "cell_type": "markdown",
232
+ "metadata": {},
233
+ "source": [
234
+ "1. Ensemble (Model Competition) Pattern\n",
235
+ "Description: The same prompt/question is sent to multiple different LLMs (OpenAI, Anthropic, Ollama, etc.).\n",
236
+ "Purpose: To compare the quality, style, and content of responses from different models.\n",
237
+ "Where in notebook:\n",
238
+ "The code sends the same question to several models and collects their answers in the competitors and answers lists.\n",
239
+ "\n",
240
+ "2. Judging/Evaluator Pattern\n",
241
+ "Description: After collecting responses from all models, another LLM is used as a “judge” to evaluate and rank the responses.\n",
242
+ "Purpose: To automate the assessment of which model gave the best answer, based on clarity and strength of argument.\n",
243
+ "Where in notebook:\n",
244
+ "The judge prompt is constructed, and an LLM is asked to rank the responses in JSON format.\n",
245
+ "\n",
246
+ "3. Self-Improvement/Meta-Reasoning Pattern\n",
247
+ "Description: The system not only generates answers but also reflects on and evaluates its own outputs (or those of its peers).\n",
248
+ "Purpose: To iteratively improve or select the best output, often used in advanced agentic systems.\n",
249
+ "Where in notebook:\n",
250
+ "The “judge” LLM is an example of meta-reasoning, as it reasons about the quality of other LLMs’ outputs.\n",
251
+ "\n",
252
+ "4. Chain-of-Thought/Decomposition Pattern (to a lesser extent)\n",
253
+ "Description: Breaking down a complex task into subtasks (e.g., generate question → get answers → evaluate answers).\n",
254
+ "Purpose: To improve reliability and interpretability by structuring the workflow.\n",
255
+ "Where in notebook:\n",
256
+ "The workflow is decomposed into:\n",
257
+ "Generating a challenging question\n",
258
+ "Getting answers from multiple models\n",
259
+ "Judging the answers\n",
260
+ "\n",
261
+ "In short:\n",
262
+ "This notebook uses the Ensemble/Competition, Judging/Evaluator, and Meta-Reasoning agentic patterns, and also demonstrates a simple form of Decomposition by structuring the workflow into clear stages.\n",
263
+ "If you want to add more agentic patterns, you could try things like:\n",
264
+ "Reflexion (let models critique and revise their own answers)\n",
265
+ "Tool Use (let models call external tools or APIs)\n",
266
+ "Planning (let a model plan the steps before answering)"
267
+ ]
268
+ },
269
+ {
270
+ "cell_type": "markdown",
271
+ "metadata": {},
272
+ "source": [
273
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
274
+ " <tr>\n",
275
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
276
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
277
+ " </td>\n",
278
+ " <td>\n",
279
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
280
+ " <span style=\"color:#00bfff;\">These kinds of patterns - to send a task to multiple models, and evaluate results,\n",
281
+ " are common where you need to improve the quality of your LLM response. This approach can be universally applied\n",
282
+ " to business projects where accuracy is critical.\n",
283
+ " </span>\n",
284
+ " </td>\n",
285
+ " </tr>\n",
286
+ "</table>"
287
+ ]
288
+ }
289
+ ],
290
+ "metadata": {
291
+ "kernelspec": {
292
+ "display_name": ".venv",
293
+ "language": "python",
294
+ "name": "python3"
295
+ },
296
+ "language_info": {
297
+ "codemirror_mode": {
298
+ "name": "ipython",
299
+ "version": 3
300
+ },
301
+ "file_extension": ".py",
302
+ "mimetype": "text/x-python",
303
+ "name": "python",
304
+ "nbconvert_exporter": "python",
305
+ "pygments_lexer": "ipython3",
306
+ "version": "3.12.8"
307
+ }
308
+ },
309
+ "nbformat": 4,
310
+ "nbformat_minor": 2
311
+ }
community_contributions/2_lab2_reflection_pattern2.ipynb ADDED
@@ -0,0 +1,999 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Welcome to the Second Lab - Exercise: Advanced Agentic Design Patterns\n",
8
+ "\n",
9
+ "This notebook extends the previous lab by adding the **Reflection Pattern** to improve response quality.\n",
10
+ "\n",
11
+ "### Patterns used in the original lab:\n",
12
+ "1. **Multi-Model Comparison Pattern** - Comparing multiple models\n",
13
+ "2. **Judge/Evaluator Pattern** - Evaluation by a judge model\n",
14
+ "\n",
15
+ "### New pattern added:\n",
16
+ "3. **Reflection Pattern** - Self-improvement of responses"
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "metadata": {},
22
+ "source": [
23
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
24
+ " <tr>\n",
25
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
26
+ " <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
27
+ " </td>\n",
28
+ " <td>\n",
29
+ " <h2 style=\"color:#ff7800;\">New Pattern: Reflection</h2>\n",
30
+ " <span style=\"color:#ff7800;\">The Reflection Pattern allows a model to critique and improve its own response. This is particularly useful for complex tasks requiring nuance and precision.</span>\n",
31
+ " </td>\n",
32
+ " </tr>\n",
33
+ "</table>"
34
+ ]
35
+ },
36
+ {
37
+ "cell_type": "code",
38
+ "execution_count": 1,
39
+ "metadata": {},
40
+ "outputs": [
41
+ {
42
+ "data": {
43
+ "text/plain": [
44
+ "True"
45
+ ]
46
+ },
47
+ "execution_count": 1,
48
+ "metadata": {},
49
+ "output_type": "execute_result"
50
+ }
51
+ ],
52
+ "source": [
53
+ "# Start with imports - ask ChatGPT to explain any package that you don't know\n",
54
+ "\n",
55
+ "import os\n",
56
+ "import json\n",
57
+ "from dotenv import load_dotenv\n",
58
+ "from openai import OpenAI\n",
59
+ "from anthropic import Anthropic\n",
60
+ "from IPython.display import Markdown, display\n",
61
+ "\n",
62
+ "# Always remember to do this!\n",
63
+ "load_dotenv(override=True)"
64
+ ]
65
+ },
66
+ {
67
+ "cell_type": "code",
68
+ "execution_count": 2,
69
+ "metadata": {},
70
+ "outputs": [
71
+ {
72
+ "name": "stdout",
73
+ "output_type": "stream",
74
+ "text": [
75
+ "OpenAI API Key exists and begins sk-1kYcH\n",
76
+ "Anthropic API Key exists and begins sk-ant-\n",
77
+ "Google API Key not set (and this is optional)\n",
78
+ "DeepSeek API Key not set (and this is optional)\n",
79
+ "Groq API Key not set (and this is optional)\n"
80
+ ]
81
+ }
82
+ ],
83
+ "source": [
84
+ "# Print the key prefixes to help with any debugging\n",
85
+ "\n",
86
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
87
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
88
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
89
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
90
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
91
+ "\n",
92
+ "if openai_api_key:\n",
93
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
94
+ "else:\n",
95
+ " print(\"OpenAI API Key not set\")\n",
96
+ " \n",
97
+ "if anthropic_api_key:\n",
98
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
99
+ "else:\n",
100
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
101
+ "\n",
102
+ "if google_api_key:\n",
103
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
104
+ "else:\n",
105
+ " print(\"Google API Key not set (and this is optional)\")\n",
106
+ "\n",
107
+ "if deepseek_api_key:\n",
108
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
109
+ "else:\n",
110
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
111
+ "\n",
112
+ "if groq_api_key:\n",
113
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
114
+ "else:\n",
115
+ " print(\"Groq API Key not set (and this is optional)\")"
116
+ ]
117
+ },
118
+ {
119
+ "cell_type": "markdown",
120
+ "metadata": {},
121
+ "source": [
122
+ "## Step 1: Generate Initial Question (Multi-Model Pattern)"
123
+ ]
124
+ },
125
+ {
126
+ "cell_type": "code",
127
+ "execution_count": 3,
128
+ "metadata": {},
129
+ "outputs": [
130
+ {
131
+ "name": "stdout",
132
+ "output_type": "stream",
133
+ "text": [
134
+ "Generated Question:\n",
135
+ "A wealthy philanthropist has developed a new drug that can cure a rare but fatal disease affecting a small population. However, the drug is expensive to produce and the philanthropist only has enough resources to manufacture a limited supply. At the same time, a competing pharmaceutical company has discovered the cure but plans to charge exorbitant prices, making it inaccessible for most patients. \n",
136
+ "\n",
137
+ "The philanthropist learns that if they invest their resources into manufacturing the drug, it can be distributed at a lower cost but only to a select few who are already on a waiting list, prioritizing those who are most likely to recover. Alternatively, the philanthropist could sell the formula to the competing company for a substantial profit, ensuring that a broader population can access the cure, albeit at high prices that many cannot afford.\n",
138
+ "\n",
139
+ "The dilemma: Should the philanthropist prioritize the immediate health of a few individuals by providing the cure at a lower cost, or should they consider the greater good by allowing the competitive company to distribute the cure to a wider audience at a higher price?\n"
140
+ ]
141
+ }
142
+ ],
143
+ "source": [
144
+ "# Generate a challenging question for the models to answer\n",
145
+ "\n",
146
+ "request = \"Please come up with a challenging ethical dilemma that requires careful moral reasoning and consideration of multiple perspectives. \"\n",
147
+ "request += \"The dilemma should involve conflicting values and have no clear-cut answer. Answer only with the dilemma, no explanation.\"\n",
148
+ "\n",
149
+ "messages = [{\"role\": \"user\", \"content\": request}]\n",
150
+ "\n",
151
+ "openai = OpenAI()\n",
152
+ "response = openai.chat.completions.create(\n",
153
+ " model=\"gpt-4o-mini\",\n",
154
+ " messages=messages,\n",
155
+ ")\n",
156
+ "\n",
157
+ "question = response.choices[0].message.content\n",
158
+ "print(\"Generated Question:\")\n",
159
+ "print(question)"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "markdown",
164
+ "metadata": {},
165
+ "source": [
166
+ "## Step 2: Get Initial Responses from Multiple Models"
167
+ ]
168
+ },
169
+ {
170
+ "cell_type": "code",
171
+ "execution_count": 4,
172
+ "metadata": {},
173
+ "outputs": [],
174
+ "source": [
175
+ "def get_initial_response(client, model_name, question, is_anthropic=False):\n",
176
+ " \"\"\"Get initial response from a model\"\"\"\n",
177
+ " messages = [{\"role\": \"user\", \"content\": question}]\n",
178
+ " \n",
179
+ " if is_anthropic:\n",
180
+ " response = client.messages.create(\n",
181
+ " model=model_name, \n",
182
+ " messages=messages, \n",
183
+ " max_tokens=1000\n",
184
+ " )\n",
185
+ " return response.content[0].text\n",
186
+ " else:\n",
187
+ " response = client.chat.completions.create(\n",
188
+ " model=model_name, \n",
189
+ " messages=messages\n",
190
+ " )\n",
191
+ " return response.choices[0].message.content"
192
+ ]
193
+ },
194
+ {
195
+ "cell_type": "code",
196
+ "execution_count": 5,
197
+ "metadata": {},
198
+ "outputs": [],
199
+ "source": [
200
+ "# Configure clients\n",
201
+ "openai_client = OpenAI()\n",
202
+ "claude_client = Anthropic() if anthropic_api_key else None\n",
203
+ "gemini_client = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\") if google_api_key else None\n",
204
+ "deepseek_client = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\") if deepseek_api_key else None\n",
205
+ "groq_client = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\") if groq_api_key else None"
206
+ ]
207
+ },
208
+ {
209
+ "cell_type": "code",
210
+ "execution_count": 6,
211
+ "metadata": {},
212
+ "outputs": [
213
+ {
214
+ "name": "stdout",
215
+ "output_type": "stream",
216
+ "text": [
217
+ "\n",
218
+ "=== INITIAL RESPONSES ===\n",
219
+ "\n",
220
+ "**gpt-4o-mini:**\n"
221
+ ]
222
+ },
223
+ {
224
+ "data": {
225
+ "text/markdown": [
226
+ "This ethical dilemma presents a challenging decision for the philanthropist, who must weigh the immediate health needs of a few individuals against the broader societal implications of drug distribution and access.\n",
227
+ "\n",
228
+ "### Option 1: Prioritizing Immediate Health\n",
229
+ "\n",
230
+ "If the philanthropist chooses to manufacture the drug and distribute it at a lower cost to those on the waiting list, they are directly addressing the pressing health needs of a select few individuals who are already vulnerable. This action prioritizes compassion and the moral obligation to help those who are suffering. By ensuring that the drug is available to those with the highest likelihood of recovery, the philanthropist demonstrates an ethical commitment to saving lives and reducing suffering in the short term.\n",
231
+ "\n",
232
+ "However, this approach has limitations. By distributing the drug to only a small number of patients, the philanthropist may overlook other individuals who could benefit from the cure. Additionally, this solution does not address the systemic issue of access to healthcare and affordable medications for the larger population suffering from the disease.\n",
233
+ "\n",
234
+ "### Option 2: Considering the Greater Good\n",
235
+ "\n",
236
+ "On the other hand, selling the formula to the competing pharmaceutical company for a substantial profit could lead to a wider distribution of the drug, although at a higher price point that may make it inaccessible to many patients. In this scenario, the philanthropist uses their financial gain to potentially invest in other healthcare initiatives or research, thus contributing to the long-term improvement of medical care or addressing related health issues.\n",
237
+ "\n",
238
+ "This choice raises ethical concerns regarding the prioritization of profit over compassion and the risk that many individuals will remain unable to afford the life-saving treatment. It also creates a tension between the ideals of philanthropy and the realities of the pharmaceutical industry, which often operates on profit motives rather than altruistic goals.\n",
239
+ "\n",
240
+ "### Balancing the Two Options\n",
241
+ "\n",
242
+ "A possible compromise could be for the philanthropist to negotiate a deal with the pharmaceutical company that ensures a tiered pricing structure, where those who can afford the drug pay more while discounts or alternative funding are provided for low-income patients. This could help bridge the gap between immediate health needs and wider access.\n",
243
+ "\n",
244
+ "Ultimately, the decision comes down to the philanthropist's values and vision for their impact on public health. Do they prioritize saving a few lives in the short term or seek a more sustainable, albeit imperfect, solution that aims at broader access over a longer timeframe? The complexity of the dilemma emphasizes the need for thoughtful deliberation on how best to serve both individual health needs and the greater public good."
245
+ ],
246
+ "text/plain": [
247
+ "<IPython.core.display.Markdown object>"
248
+ ]
249
+ },
250
+ "metadata": {},
251
+ "output_type": "display_data"
252
+ },
253
+ {
254
+ "name": "stdout",
255
+ "output_type": "stream",
256
+ "text": [
257
+ "\n",
258
+ "==================================================\n",
259
+ "\n",
260
+ "**claude-3-7-sonnet-latest:**\n"
261
+ ]
262
+ },
263
+ {
264
+ "data": {
265
+ "text/markdown": [
266
+ "# The Philanthropist's Dilemma\n",
267
+ "\n",
268
+ "This is a complex ethical dilemma that involves several important considerations:\n",
269
+ "\n",
270
+ "## Key Ethical Tensions\n",
271
+ "\n",
272
+ "- **Limited access at affordable prices** vs. **wider access at unaffordable prices**\n",
273
+ "- **Immediate relief for a few** vs. **potential long-term access for many**\n",
274
+ "- **Direct control over distribution** vs. **surrendering control to profit-motivated actors**\n",
275
+ "\n",
276
+ "## Considerations for Manufacturing the Drug Directly\n",
277
+ "\n",
278
+ "**Benefits:**\n",
279
+ "- Ensures the most vulnerable patients receive treatment based on medical need rather than ability to pay\n",
280
+ "- Maintains the philanthropist's ethical vision and control over distribution\n",
281
+ "- Sets a precedent for compassionate drug pricing\n",
282
+ "\n",
283
+ "**Drawbacks:**\n",
284
+ "- Limited overall reach due to resource constraints\n",
285
+ "- Potentially slower scaling of production\n",
286
+ "- Many patients may receive no treatment at all\n",
287
+ "\n",
288
+ "## Considerations for Selling to the Pharmaceutical Company\n",
289
+ "\n",
290
+ "**Benefits:**\n",
291
+ "- Potentially greater production capacity and distribution reach\n",
292
+ "- The philanthropist could use profits to subsidize costs for those who cannot afford it\n",
293
+ "- Might accelerate further research and development\n",
294
+ "\n",
295
+ "**Drawbacks:**\n",
296
+ "- Many patients would be excluded based on financial means\n",
297
+ "- Surrenders control over an essential medicine to profit-motivated decision-making\n",
298
+ "- Could establish a problematic precedent for pricing life-saving medications\n",
299
+ "\n",
300
+ "This dilemma reflects broader tensions in healthcare ethics between utilitarian approaches (helping the most people) and justice-based approaches (ensuring fair access based on need rather than wealth).\n",
301
+ "\n",
302
+ "There might be creative third options worth exploring, such as licensing agreements with price caps, creating a non-profit manufacturing entity, or partnering with governments to ensure broader affordable access."
303
+ ],
304
+ "text/plain": [
305
+ "<IPython.core.display.Markdown object>"
306
+ ]
307
+ },
308
+ "metadata": {},
309
+ "output_type": "display_data"
310
+ },
311
+ {
312
+ "name": "stdout",
313
+ "output_type": "stream",
314
+ "text": [
315
+ "\n",
316
+ "==================================================\n",
317
+ "\n"
318
+ ]
319
+ }
320
+ ],
321
+ "source": [
322
+ "# Collect initial responses\n",
323
+ "initial_responses = {}\n",
324
+ "competitors = []\n",
325
+ "\n",
326
+ "models = [\n",
327
+ " (\"gpt-4o-mini\", openai_client, False),\n",
328
+ " (\"claude-3-7-sonnet-latest\", claude_client, True),\n",
329
+ " (\"gemini-2.0-flash\", gemini_client, False),\n",
330
+ " (\"deepseek-chat\", deepseek_client, False),\n",
331
+ " (\"llama-3.3-70b-versatile\", groq_client, False),\n",
332
+ "]\n",
333
+ "\n",
334
+ "print(\"\\n=== INITIAL RESPONSES ===\\n\")\n",
335
+ "\n",
336
+ "for model_name, client, is_anthropic in models:\n",
337
+ " if client:\n",
338
+ " try:\n",
339
+ " response = get_initial_response(client, model_name, question, is_anthropic)\n",
340
+ " initial_responses[model_name] = response\n",
341
+ " competitors.append(model_name)\n",
342
+ " \n",
343
+ " print(f\"**{model_name}:**\")\n",
344
+ " display(Markdown(response))\n",
345
+ " print(\"\\n\" + \"=\"*50 + \"\\n\")\n",
346
+ " except Exception as e:\n",
347
+ " print(f\"Error with {model_name}: {e}\")"
348
+ ]
349
+ },
350
+ {
351
+ "cell_type": "markdown",
352
+ "metadata": {},
353
+ "source": [
354
+ "## Step 3: NEW PATTERN - Reflection Pattern"
355
+ ]
356
+ },
357
+ {
358
+ "cell_type": "code",
359
+ "execution_count": 7,
360
+ "metadata": {},
361
+ "outputs": [],
362
+ "source": [
363
+ "def apply_reflection_pattern(client, model_name, original_question, initial_response, is_anthropic=False):\n",
364
+ " \"\"\"Apply the Reflection Pattern to improve a response\"\"\"\n",
365
+ " \n",
366
+ " reflection_prompt = f\"\"\"\n",
367
+ "You previously received this question:\n",
368
+ "{original_question}\n",
369
+ "\n",
370
+ "Here was your initial response:\n",
371
+ "{initial_response}\n",
372
+ "\n",
373
+ "Now, as a critical expert, analyze your own response:\n",
374
+ "1. What are the strengths of this response?\n",
375
+ "2. What important perspectives are missing?\n",
376
+ "3. Are there any biases or blind spots in the analysis?\n",
377
+ "4. How could you improve this response?\n",
378
+ "\n",
379
+ "After this self-critique, provide an IMPROVED response that takes into account your observations.\n",
380
+ "\n",
381
+ "Response format:\n",
382
+ "## Self-Critique\n",
383
+ "[Your critical analysis of the initial response]\n",
384
+ "\n",
385
+ "## Improved Response\n",
386
+ "[Your revised and improved response]\n",
387
+ "\"\"\"\n",
388
+ " \n",
389
+ " messages = [{\"role\": \"user\", \"content\": reflection_prompt}]\n",
390
+ " \n",
391
+ " if is_anthropic:\n",
392
+ " response = client.messages.create(\n",
393
+ " model=model_name, \n",
394
+ " messages=messages, \n",
395
+ " max_tokens=1500\n",
396
+ " )\n",
397
+ " return response.content[0].text\n",
398
+ " else:\n",
399
+ " response = client.chat.completions.create(\n",
400
+ " model=model_name, \n",
401
+ " messages=messages\n",
402
+ " )\n",
403
+ " return response.choices[0].message.content"
404
+ ]
405
+ },
406
+ {
407
+ "cell_type": "code",
408
+ "execution_count": 8,
409
+ "metadata": {},
410
+ "outputs": [
411
+ {
412
+ "name": "stdout",
413
+ "output_type": "stream",
414
+ "text": [
415
+ "\n",
416
+ "=== RESPONSES AFTER REFLECTION ===\n",
417
+ "\n",
418
+ "**gpt-4o-mini - After Reflection:**\n"
419
+ ]
420
+ },
421
+ {
422
+ "data": {
423
+ "text/markdown": [
424
+ "## Self-Critique\n",
425
+ "1. **Strengths of this Response:**\n",
426
+ " - The response thoroughly outlines both options available to the philanthropist, providing a balanced view of the ethical implications of each choice.\n",
427
+ " - It acknowledges the immediate health needs of affected individuals as well as the broader societal implications of drug distribution.\n",
428
+ " - It introduces a potential compromise solution, which adds depth to the analysis and suggests a more nuanced approach to the dilemma.\n",
429
+ "\n",
430
+ "2. **Important Perspectives Missing:**\n",
431
+ " - The response does not adequately consider the potential operational and logistical challenges in manufacturing and distributing the drug at a lower cost, including regulatory hurdles and the scalability of production.\n",
432
+ " - There is limited discussion on the emotional impact of the decision on the patients and their families, which could influence the philanthropist's considerations.\n",
433
+ " - The perspective of other stakeholders, such as healthcare providers and ethicists, is not introduced.\n",
434
+ "\n",
435
+ "3. **Biases or Blind Spots in the Analysis:**\n",
436
+ " - The response may lean towards prioritizing compassion over economic pragmatism, possibly downplaying the complexities involved in pharmaceutical economics and the realities that arise from selling to a corporation with profit motives.\n",
437
+ " - It assumes a binary choice rather than considering other stakeholder impacts and longer-term systemic solutions.\n",
438
+ "\n",
439
+ "4. **How to Improve This Response:**\n",
440
+ " - Include more contextual factors that might affect the decision, such as regulatory considerations, patient demographics, and healthcare infrastructure.\n",
441
+ " - Expand on the emotional and psychological aspects of the decision-making process for both the philanthropist and the patients involved.\n",
442
+ " - Address the potential for future societal implications if the competing company monopolizes the market after acquiring the formula.\n",
443
+ "\n",
444
+ "## Improved Response\n",
445
+ "This ethical dilemma presents the philanthropist with a complex decision regarding how best to utilize limited resources to maximize the benefit for individuals suffering from a rare but fatal disease. The two primary options – providing a low-cost supply to a select few or selling the formula for broader but costly distribution – both highlight significant ethical considerations.\n",
446
+ "\n",
447
+ "### Option 1: Prioritizing Immediate Health\n",
448
+ "By choosing to manufacture the drug at a lower cost for those on the waiting list, the philanthropist opts to directly address the urgent health needs of vulnerable individuals. This approach reflects a moral obligation to alleviate suffering and save lives in the short term. Prioritizing individuals with the highest likelihood of recovery can lead to tangible, immediate outcomes for those patients and their families.\n",
449
+ "\n",
450
+ "However, there are operational challenges associated with this choice. Limited production capabilities may mean that only a fraction of those in need can actually receive the drug, leaving many others without hope. Additionally, this decision doesn't resolve the systemic issues within healthcare, such as overall treatment accessibility and drug pricing, which may persist if not tackled holistically.\n",
451
+ "\n",
452
+ "### Option 2: Considering the Greater Good\n",
453
+ "Alternatively, selling the formula to the competing pharmaceutical company could result in wider distribution of the drug and potentially more patients benefiting from the cure, albeit at higher prices. This choice could finance further philanthropic efforts or investments in healthcare that might ultimately lead to broader long-term improvements in public health.\n",
454
+ "\n",
455
+ "However, ethical concerns arise when considering the high pricing of the cure. The decision may disproportionately disadvantage lower-income patients, perpetuating healthcare inequities. Furthermore, there is the risk that this choice could enable the pharmaceutical company to monopolize treatment options, further exploitation in the industry.\n",
456
+ "\n",
457
+ "### A Balanced Approach\n",
458
+ "To navigate this complex dilemma more thoughtfully, the philanthropist could explore a compromise by negotiating with the pharmaceutical company to establish a tiered pricing structure. This could create a system where the drug is offered at a reduced price for low-income patients, while ensuring sustainability for the company through higher prices for those who can afford them. Additionally, the philanthropist might advocate for a commitment from the company to invest in generics or alternative distribution methods to enhance accessibility.\n",
459
+ "\n",
460
+ "### Conclusion\n",
461
+ "The choice ultimately hinges on the philanthropist's values and vision for their impact on public health. This decision requires careful consideration of immediate health benefits, long-term accessibility, and the emotional ramifications for affected individuals. By weighing the implications of each option and considering collaborative solutions, the philanthropist can work towards an outcome that promotes both individual care and broader societal well-being."
462
+ ],
463
+ "text/plain": [
464
+ "<IPython.core.display.Markdown object>"
465
+ ]
466
+ },
467
+ "metadata": {},
468
+ "output_type": "display_data"
469
+ },
470
+ {
471
+ "name": "stdout",
472
+ "output_type": "stream",
473
+ "text": [
474
+ "\n",
475
+ "==================================================\n",
476
+ "\n",
477
+ "**claude-3-7-sonnet-latest - After Reflection:**\n"
478
+ ]
479
+ },
480
+ {
481
+ "data": {
482
+ "text/markdown": [
483
+ "## Self-Critique\n",
484
+ "\n",
485
+ "### Strengths of the initial response:\n",
486
+ "- Well-structured analysis that clearly outlines the ethical tensions\n",
487
+ "- Presents balanced considerations for both options\n",
488
+ "- Mentions potential third options beyond the binary choice\n",
489
+ "- Identifies the broader ethical frameworks at play (utilitarian vs. justice-based approaches)\n",
490
+ "\n",
491
+ "### Missing perspectives:\n",
492
+ "1. **Stakeholder analysis**: The response lacks a thorough examination of all affected parties (patients, healthcare systems, future patients, etc.)\n",
493
+ "2. **Timeline considerations**: No discussion of short-term vs. long-term consequences beyond immediate access\n",
494
+ "3. **Public health impact**: Limited analysis of how each option affects overall public health outcomes\n",
495
+ "4. **Precedent-setting effects**: Inadequate exploration of how this decision might influence future pharmaceutical development and pricing\n",
496
+ "5. **Regulatory context**: No mention of potential government intervention, price controls, or other regulatory factors\n",
497
+ "6. **Global justice perspective**: No consideration of how this decision affects different regions/countries\n",
498
+ "\n",
499
+ "### Biases and blind spots:\n",
500
+ "1. **False dichotomy**: Despite mentioning \"third options,\" the analysis primarily treats this as a binary choice\n",
501
+ "2. **Western/developed-world bias**: Assumes a market-based healthcare system without considering different global contexts\n",
502
+ "3. **Individual-focused ethics**: Overemphasizes individual choice rather than institutional or systemic responsibilities\n",
503
+ "4. **Overly abstract**: The analysis lacks concrete examples or case studies that might inform the decision\n",
504
+ "5. **Neglect of power dynamics**: Doesn't address the power imbalance between corporations, individuals, and patients\n",
505
+ "\n",
506
+ "### Improvement opportunities:\n",
507
+ "1. Provide a more nuanced spectrum of options beyond the binary choice\n",
508
+ "2. Include more stakeholder perspectives, particularly patient voices\n",
509
+ "3. Consider real-world case studies of similar pharmaceutical dilemmas\n",
510
+ "4. Address systemic issues in drug development and pharmaceutical pricing\n",
511
+ "5. Explore collaborative approaches that leverage multiple institutions\n",
512
+ "6. Discuss intellectual property rights and their ethical implications\n",
513
+ "\n",
514
+ "## Improved Response\n",
515
+ "\n",
516
+ "# The Philanthropist's Dilemma: A Multidimensional Ethical Analysis\n",
517
+ "\n",
518
+ "This scenario presents not simply a binary choice but a complex ethical landscape involving multiple stakeholders, systemic factors, and competing values.\n",
519
+ "\n",
520
+ "## Stakeholder Analysis\n",
521
+ "\n",
522
+ "**Patients and families:**\n",
523
+ "- Those currently suffering need immediate access regardless of mechanism\n",
524
+ "- Future patients have interests in sustainable development of treatments\n",
525
+ "- Economic diversity among patients means affordability affects different groups unequally\n",
526
+ "\n",
527
+ "**Healthcare systems:**\n",
528
+ "- Must allocate limited resources across competing priorities\n",
529
+ "- High-priced drugs can strain budgets and force difficult coverage decisions\n",
530
+ "- Precedents set now affect future negotiations with pharmaceutical companies\n",
531
+ "\n",
532
+ "**Research community:**\n",
533
+ "- Incentives for developing treatments for rare diseases are influenced by such cases\n",
534
+ "- How intellectual property is handled affects future research priorities\n",
535
+ "\n",
536
+ "## Ethical Frameworks Worth Considering\n",
537
+ "\n",
538
+ "1. **Distributive justice**: Who should receive limited resources? What constitutes fair allocation?\n",
539
+ "2. **Rights-based approach**: Do patients have a right to life-saving medication regardless of cost?\n",
540
+ "3. **Consequentialist assessment**: Which option produces the best outcomes for the most people over time?\n",
541
+ "4. **Virtue ethics**: What would a virtuous philanthropist do in this situation?\n",
542
+ "5. **Global justice**: How does this decision affect healthcare equity across different regions?\n",
543
+ "\n",
544
+ "## Spectrum of Options\n",
545
+ "\n",
546
+ "Rather than two mutually exclusive choices, consider a spectrum of possibilities:\n",
547
+ "\n",
548
+ "1. **Direct manufacturing with tiered pricing**: Manufacture independently but implement income-based pricing to maximize access while maintaining sustainability\n",
549
+ "\n",
550
+ "2. **Conditional licensing**: License the formula with contractual price controls, distribution requirements, and accessibility guarantees\n",
551
+ "\n",
552
+ "3. **Public-private partnership**: Collaborate with governments, NGOs, and selected pharmaceutical partners to ensure broad, affordable access\n",
553
+ "\n",
554
+ "4. **Open-source approach**: Release the formula publicly with certain patent protections waived, while establishing a foundation to support manufacturing\n",
555
+ "\n",
556
+ "5. **Hybrid distribution model**: Manufacture for highest-need populations while licensing to reach others, using licensing revenues to subsidize direct manufacturing\n",
557
+ "\n",
558
+ "## Case Study Context\n",
559
+ "\n",
560
+ "Similar dilemmas have occurred with treatments for HIV/AIDS, hepatitis C, and rare genetic disorders. The outcomes suggest:\n",
561
+ "\n",
562
+ "- Maintaining some control over intellectual property while ensuring broad access often yields better public health outcomes than either extreme option\n",
563
+ "- Patient advocacy can significantly influence corporate behavior and pricing\n",
564
+ "- International differences in pricing and patent enforcement create complex dynamics\n",
565
+ "- Government intervention through negotiation, compulsory licensing, or regulation often becomes necessary\n",
566
+ "\n",
567
+ "## Systems-Level Considerations\n",
568
+ "\n",
569
+ "This dilemma exists within broader systemic issues:\n",
570
+ "\n",
571
+ "- The current pharmaceutical development model creates inherent tensions between innovation, access, and affordability\n",
572
+ "- Rare disease treatments highlight market failures in drug development\n",
573
+ "- Healthcare financing systems vary globally, affecting how we should evaluate \"accessibility\"\n",
574
+ "- Intellectual property regimes may require reform to better balance innovation incentives with public health needs\n",
575
+ "\n",
576
+ "## Recommended Approach\n",
577
+ "\n",
578
+ "The philanthropist should pursue a hybrid strategy that:\n",
579
+ "\n",
580
+ "1. Maintains sufficient control to ensure the most vulnerable patients receive treatment regardless of ability to pay\n",
581
+ "\n",
582
+ "2. Leverages partnerships with multiple entities (pharmaceutical companies, governments, NGOs) to maximize production scale and geographic reach\n",
583
+ "\n",
584
+ "3. Implements contractual safeguards on pricing, with particular attention to low and middle-income regions\n",
585
+ "\n",
586
+ "4. Establishes a patient assistance foundation using a portion of any licensing revenues\n",
587
+ "\n",
588
+ "5. Advocates for systemic reforms that would prevent such dilemmas in the future\n",
589
+ "\n",
590
+ "This approach recognizes that the philanthropist's responsibility extends beyond the immediate distribution decision to include consideration of precedent-setting effects, stakeholder equity, and systemic change—balancing immediate needs with long-term public health impact."
591
+ ],
592
+ "text/plain": [
593
+ "<IPython.core.display.Markdown object>"
594
+ ]
595
+ },
596
+ "metadata": {},
597
+ "output_type": "display_data"
598
+ },
599
+ {
600
+ "name": "stdout",
601
+ "output_type": "stream",
602
+ "text": [
603
+ "\n",
604
+ "==================================================\n",
605
+ "\n"
606
+ ]
607
+ }
608
+ ],
609
+ "source": [
610
+ "# Apply Reflection Pattern\n",
611
+ "reflected_responses = {}\n",
612
+ "\n",
613
+ "print(\"\\n=== RESPONSES AFTER REFLECTION ===\\n\")\n",
614
+ "\n",
615
+ "for model_name, client, is_anthropic in models:\n",
616
+ " if client and model_name in initial_responses:\n",
617
+ " try:\n",
618
+ " reflected = apply_reflection_pattern(\n",
619
+ " client, model_name, question, \n",
620
+ " initial_responses[model_name], is_anthropic\n",
621
+ " )\n",
622
+ " reflected_responses[model_name] = reflected\n",
623
+ " \n",
624
+ " print(f\"**{model_name} - After Reflection:**\")\n",
625
+ " display(Markdown(reflected))\n",
626
+ " print(\"\\n\" + \"=\"*50 + \"\\n\")\n",
627
+ " except Exception as e:\n",
628
+ " print(f\"Error with reflection for {model_name}: {e}\")"
629
+ ]
630
+ },
631
+ {
632
+ "cell_type": "markdown",
633
+ "metadata": {},
634
+ "source": [
635
+ "## Step 4: Comparative Evaluation (Extended Judge Pattern)"
636
+ ]
637
+ },
638
+ {
639
+ "cell_type": "code",
640
+ "execution_count": 9,
641
+ "metadata": {},
642
+ "outputs": [],
643
+ "source": [
644
+ "def create_comparative_evaluation(question, initial_responses, reflected_responses):\n",
645
+ " \"\"\"Create a comparative evaluation of responses before/after reflection\"\"\"\n",
646
+ " \n",
647
+ " evaluation_prompt = f\"\"\"\n",
648
+ "You are evaluating the effectiveness of the \"Reflection Pattern\" on the following question:\n",
649
+ "{question}\n",
650
+ "\n",
651
+ "For each model, you have:\n",
652
+ "1. An initial response\n",
653
+ "2. A response after self-reflection\n",
654
+ "\n",
655
+ "Analyze and compare:\n",
656
+ "- Depth of analysis\n",
657
+ "- Consideration of multiple perspectives\n",
658
+ "- Nuance and sophistication of reasoning\n",
659
+ "- Improvement brought by reflection\n",
660
+ "\n",
661
+ "MODELS TO EVALUATE:\n",
662
+ "\"\"\"\n",
663
+ " \n",
664
+ " for model_name in initial_responses:\n",
665
+ " if model_name in reflected_responses:\n",
666
+ " evaluation_prompt += f\"\"\"\n",
667
+ "## {model_name}\n",
668
+ "\n",
669
+ "### Initial response:\n",
670
+ "{initial_responses[model_name][:500]}...\n",
671
+ "\n",
672
+ "### Response after reflection:\n",
673
+ "{reflected_responses[model_name][:800]}...\n",
674
+ "\n",
675
+ "\"\"\"\n",
676
+ " \n",
677
+ " evaluation_prompt += \"\"\"\n",
678
+ "Respond with structured JSON:\n",
679
+ "{\n",
680
+ " \"general_analysis\": \"Your analysis of the Reflection Pattern's effectiveness\",\n",
681
+ " \"initial_ranking\": [\"best initially ranked model\", \"second\", \"third\"],\n",
682
+ " \"post_reflection_ranking\": [\"best ranked model after reflection\", \"second\", \"third\"],\n",
683
+ " \"most_improved\": \"Which model improved the most\",\n",
684
+ " \"insights\": \"Insights about the usefulness of the Reflection Pattern\"\n",
685
+ "}\n",
686
+ "\"\"\"\n",
687
+ " \n",
688
+ " return evaluation_prompt"
689
+ ]
690
+ },
691
+ {
692
+ "cell_type": "code",
693
+ "execution_count": 10,
694
+ "metadata": {},
695
+ "outputs": [
696
+ {
697
+ "name": "stdout",
698
+ "output_type": "stream",
699
+ "text": [
700
+ "\n",
701
+ "=== FINAL EVALUATION ===\n",
702
+ "\n",
703
+ "```json\n",
704
+ "{\n",
705
+ " \"general_analysis\": \"The Reflection Pattern effectively enhanced the depth of analysis and consideration of multiple perspectives in both models. However, the results differ in terms of sophistication and detail. The GPT-4 model provided initial observations that were relatively shallow but improved by incorporating logistical challenges and suggesting compromises during reflection. In contrast, Claude-3's initial response was more structured and sophisticated, covering a broader range of ethical frameworks, but still showed room for improvement regarding stakeholder analysis and long-term impacts.\",\n",
706
+ " \"initial_ranking\": [\"claude-3-7-sonnet-latest\", \"gpt-4o-mini\"],\n",
707
+ " \"post_reflection_ranking\": [\"claude-3-7-sonnet-latest\", \"gpt-4o-mini\"],\n",
708
+ " \"most_improved\": \"gpt-4o-mini\",\n",
709
+ " \"insights\": \"The Reflection Pattern revealed significant gaps in both models' initial analyses, encouraging deeper engagement with ethical implications and stakeholder considerations. It highlighted the importance of reflecting on logistical realities and the real-world impacts of decisions, marking it as a worthwhile practice for ethical dilemmas.\"\n",
710
+ "}\n",
711
+ "```\n",
712
+ "Could not parse JSON, raw output shown above\n"
713
+ ]
714
+ }
715
+ ],
716
+ "source": [
717
+ "# Final evaluation\n",
718
+ "if initial_responses and reflected_responses:\n",
719
+ " evaluation_prompt = create_comparative_evaluation(question, initial_responses, reflected_responses)\n",
720
+ " \n",
721
+ " judge_messages = [{\"role\": \"user\", \"content\": evaluation_prompt}]\n",
722
+ " \n",
723
+ " try:\n",
724
+ " judge_response = openai_client.chat.completions.create(\n",
725
+ " model=\"gpt-4o-mini\",\n",
726
+ " messages=judge_messages,\n",
727
+ " )\n",
728
+ " \n",
729
+ " evaluation_result = judge_response.choices[0].message.content\n",
730
+ " print(\"\\n=== FINAL EVALUATION ===\\n\")\n",
731
+ " print(evaluation_result)\n",
732
+ " \n",
733
+ " # Try to parse JSON for structured display\n",
734
+ " try:\n",
735
+ " eval_json = json.loads(evaluation_result)\n",
736
+ " print(\"\\n=== STRUCTURED RESULTS ===\\n\")\n",
737
+ " for key, value in eval_json.items():\n",
738
+ " print(f\"{key.replace('_', ' ').title()}: {value}\")\n",
739
+ " except:\n",
740
+ " print(\"Could not parse JSON, raw output shown above\")\n",
741
+ " \n",
742
+ " except Exception as e:\n",
743
+ " print(f\"Error during final evaluation: {e}\")"
744
+ ]
745
+ },
746
+ {
747
+ "cell_type": "markdown",
748
+ "metadata": {},
749
+ "source": [
750
+ "## Simple Before/After Comparison"
751
+ ]
752
+ },
753
+ {
754
+ "cell_type": "code",
755
+ "execution_count": 11,
756
+ "metadata": {},
757
+ "outputs": [
758
+ {
759
+ "name": "stdout",
760
+ "output_type": "stream",
761
+ "text": [
762
+ "\n",
763
+ "=== BEFORE vs AFTER COMPARISON ===\n",
764
+ "\n",
765
+ "\n",
766
+ "==================== GPT-4O-MINI ====================\n",
767
+ "\n",
768
+ "BEFORE REFLECTION:\n",
769
+ "--------------------------------------------------\n",
770
+ "This ethical dilemma presents a challenging decision for the philanthropist, who must weigh the immediate health needs of a few individuals against the broader societal implications of drug distribution and access.\n",
771
+ "\n",
772
+ "### Option 1: Prioritizing Immediate Health\n",
773
+ "\n",
774
+ "If the philanthropist chooses to manufa...\n",
775
+ "\n",
776
+ "AFTER REFLECTION:\n",
777
+ "--------------------------------------------------\n",
778
+ "This ethical dilemma presents the philanthropist with a complex decision regarding how best to utilize limited resources to maximize the benefit for individuals suffering from a rare but fatal disease. The two primary options – providing a low-cost supply to a select few or selling the formula for broader but costly distribution – both highlight significant ethical considerations.\n",
779
+ "\n",
780
+ "### Option 1: P...\n",
781
+ "\n",
782
+ "======================================================================\n",
783
+ "\n",
784
+ "\n",
785
+ "==================== CLAUDE-3-7-SONNET-LATEST ====================\n",
786
+ "\n",
787
+ "BEFORE REFLECTION:\n",
788
+ "--------------------------------------------------\n",
789
+ "# The Philanthropist's Dilemma\n",
790
+ "\n",
791
+ "This is a complex ethical dilemma that involves several important considerations:\n",
792
+ "\n",
793
+ "## Key Ethical Tensions\n",
794
+ "\n",
795
+ "- **Limited access at affordable prices** vs. **wider access at unaffordable prices**\n",
796
+ "- **Immediate relief for a few** vs. **potential long-term access for many...\n",
797
+ "\n",
798
+ "AFTER REFLECTION:\n",
799
+ "--------------------------------------------------\n",
800
+ "# The Philanthropist's Dilemma: A Multidimensional Ethical Analysis\n",
801
+ "\n",
802
+ "This scenario presents not simply a binary choice but a complex ethical landscape involving multiple stakeholders, systemic factors, and competing values.\n",
803
+ "\n",
804
+ "## Stakeholder Analysis\n",
805
+ "\n",
806
+ "**Patients and families:**\n",
807
+ "- Those currently suffering need immediate access regardless of mechanism\n",
808
+ "- Future patients have interests in sustainable d...\n",
809
+ "\n",
810
+ "======================================================================\n",
811
+ "\n"
812
+ ]
813
+ }
814
+ ],
815
+ "source": [
816
+ "# Display side-by-side comparison for each model\n",
817
+ "print(\"\\n=== BEFORE vs AFTER COMPARISON ===\\n\")\n",
818
+ "\n",
819
+ "for model_name in initial_responses:\n",
820
+ " if model_name in reflected_responses:\n",
821
+ " print(f\"\\n{'='*20} {model_name.upper()} {'='*20}\\n\")\n",
822
+ " \n",
823
+ " print(\"BEFORE REFLECTION:\")\n",
824
+ " print(\"-\" * 50)\n",
825
+ " print(initial_responses[model_name][:300] + \"...\")\n",
826
+ " \n",
827
+ " print(\"\\nAFTER REFLECTION:\")\n",
828
+ " print(\"-\" * 50)\n",
829
+ " # Extract just the \"Improved Response\" section if it exists\n",
830
+ " reflected = reflected_responses[model_name]\n",
831
+ " if \"## Improved Response\" in reflected:\n",
832
+ " improved_section = reflected.split(\"## Improved Response\")[1].strip()\n",
833
+ " print(improved_section[:400] + \"...\")\n",
834
+ " else:\n",
835
+ " print(reflected[:400] + \"...\")\n",
836
+ " \n",
837
+ " print(\"\\n\" + \"=\"*70 + \"\\n\")"
838
+ ]
839
+ },
840
+ {
841
+ "cell_type": "markdown",
842
+ "metadata": {},
843
+ "source": [
844
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
845
+ " <tr>\n",
846
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
847
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
848
+ " </td>\n",
849
+ " <td>\n",
850
+ " <h2 style=\"color:#ff7800;\">Pattern Analysis</h2>\n",
851
+ " <span style=\"color:#ff7800;\">\n",
852
+ " <b>Patterns used:</b><br/>\n",
853
+ " 1. <b>Multi-Model Comparison:</b> Comparing multiple models on the same task<br/>\n",
854
+ " 2. <b>Judge/Evaluator:</b> Using a model to evaluate performances<br/>\n",
855
+ " 3. <b>Reflection (NEW):</b> Self-critique and improvement of responses<br/><br/>\n",
856
+ " <b>Possible experiments:</b><br/>\n",
857
+ " - Iterate the Reflection Pattern multiple times<br/>\n",
858
+ " - Add a \"Debate Pattern\" between models<br/>\n",
859
+ " - Implement a \"Consensus Pattern\"\n",
860
+ " </span>\n",
861
+ " </td>\n",
862
+ " </tr>\n",
863
+ "</table>"
864
+ ]
865
+ },
866
+ {
867
+ "cell_type": "markdown",
868
+ "metadata": {},
869
+ "source": [
870
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
871
+ " <tr>\n",
872
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
873
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
874
+ " </td>\n",
875
+ " <td>\n",
876
+ " <h2 style=\"color:#00bfff;\">Commercial Applications</h2>\n",
877
+ " <span style=\"color:#00bfff;\">\n",
878
+ " The <b>Reflection Pattern</b> is particularly valuable for:<br/>\n",
879
+ " • Improving quality of complex analyses<br/>\n",
880
+ " • Reducing bias in AI recommendations<br/>\n",
881
+ " • Creating self-improving systems<br/>\n",
882
+ " • Developing more robust AI for critical decisions<br/><br/>\n",
883
+ " Use cases: Strategic consulting, risk analysis, ethical evaluation, medical diagnosis\n",
884
+ " </span>\n",
885
+ " </td>\n",
886
+ " </tr>\n",
887
+ "</table>"
888
+ ]
889
+ },
890
+ {
891
+ "cell_type": "markdown",
892
+ "metadata": {},
893
+ "source": [
894
+ "## Additional Pattern Ideas for Future Implementation"
895
+ ]
896
+ },
897
+ {
898
+ "cell_type": "code",
899
+ "execution_count": 12,
900
+ "metadata": {},
901
+ "outputs": [
902
+ {
903
+ "name": "stdout",
904
+ "output_type": "stream",
905
+ "text": [
906
+ "Exercise completed! Analyze the results to see the impact of the Reflection Pattern.\n"
907
+ ]
908
+ }
909
+ ],
910
+ "source": [
911
+ "# 1. Chain of Thought Pattern\n",
912
+ "\"\"\"\n",
913
+ "Add a pattern that asks models to show their reasoning step by step:\n",
914
+ "\n",
915
+ "def apply_chain_of_thought_pattern(client, question):\n",
916
+ " prompt = f\\\"\n",
917
+ " Question: {question}\n",
918
+ " \n",
919
+ " Please think through this step by step:\n",
920
+ " Step 1: [Identify the key issues]\n",
921
+ " Step 2: [Consider different perspectives]\n",
922
+ " Step 3: [Evaluate potential consequences]\n",
923
+ " Step 4: [Provide reasoned conclusion]\n",
924
+ " \\\"\n",
925
+ " return get_response(client, prompt)\n",
926
+ "\"\"\"\n",
927
+ "\n",
928
+ "# 2. Iterative Refinement Pattern\n",
929
+ "\"\"\"\n",
930
+ "Create a loop that progressively improves the response over multiple iterations:\n",
931
+ "\n",
932
+ "def iterative_refinement(client, question, iterations=3):\n",
933
+ " response = get_initial_response(client, question)\n",
934
+ " for i in range(iterations):\n",
935
+ " critique_prompt = f\\\"Improve this response: {response}\\\"\n",
936
+ " response = get_response(client, critique_prompt)\n",
937
+ " return response\n",
938
+ "\"\"\"\n",
939
+ "\n",
940
+ "# 3. Debate Pattern\n",
941
+ "\"\"\"\n",
942
+ "Make two models debate their respective responses:\n",
943
+ "\n",
944
+ "def create_debate(client1, client2, question):\n",
945
+ " response1 = get_response(client1, question)\n",
946
+ " response2 = get_response(client2, question)\n",
947
+ " \n",
948
+ " debate_prompt1 = f\\\"Argue against this position: {response2}\\\"\n",
949
+ " debate_prompt2 = f\\\"Argue against this position: {response1}\\\"\n",
950
+ " \n",
951
+ " counter1 = get_response(client1, debate_prompt1)\n",
952
+ " counter2 = get_response(client2, debate_prompt2)\n",
953
+ " \n",
954
+ " return counter1, counter2\n",
955
+ "\"\"\"\n",
956
+ "\n",
957
+ "# 4. Consensus Building Pattern\n",
958
+ "\"\"\"\n",
959
+ "Attempt to create a consensus response based on all individual responses:\n",
960
+ "\n",
961
+ "def build_consensus(all_responses, question):\n",
962
+ " consensus_prompt = f\\\"\n",
963
+ " Original question: {question}\n",
964
+ " \n",
965
+ " Here are multiple expert responses:\n",
966
+ " {all_responses}\n",
967
+ " \n",
968
+ " Create a consensus response that incorporates the best insights from all responses\n",
969
+ " while resolving contradictions.\n",
970
+ " \\\"\n",
971
+ " return get_response(openai_client, consensus_prompt)\n",
972
+ "\"\"\"\n",
973
+ "\n",
974
+ "print(\"Exercise completed! Analyze the results to see the impact of the Reflection Pattern.\")"
975
+ ]
976
+ }
977
+ ],
978
+ "metadata": {
979
+ "kernelspec": {
980
+ "display_name": ".venv",
981
+ "language": "python",
982
+ "name": "python3"
983
+ },
984
+ "language_info": {
985
+ "codemirror_mode": {
986
+ "name": "ipython",
987
+ "version": 3
988
+ },
989
+ "file_extension": ".py",
990
+ "mimetype": "text/x-python",
991
+ "name": "python",
992
+ "nbconvert_exporter": "python",
993
+ "pygments_lexer": "ipython3",
994
+ "version": "3.12.11"
995
+ }
996
+ },
997
+ "nbformat": 4,
998
+ "nbformat_minor": 4
999
+ }
community_contributions/2_lab2_six-thinking-hats-simulator.ipynb ADDED
@@ -0,0 +1,457 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Six Thinking Hats Simulator\n",
8
+ "\n",
9
+ "## Objective\n",
10
+ "This notebook implements a simulator of the Six Thinking Hats technique to evaluate and improve technological solutions. The simulator will:\n",
11
+ "\n",
12
+ "1. Use an LLM to generate an initial technological solution idea for a specific daily task in a company.\n",
13
+ "2. Apply the Six Thinking Hats methodology to analyze and improve the proposed solution.\n",
14
+ "3. Provide a comprehensive evaluation from different perspectives.\n",
15
+ "\n",
16
+ "## About the Six Thinking Hats Technique\n",
17
+ "\n",
18
+ "The Six Thinking Hats is a powerful technique developed by Edward de Bono that helps people look at problems and decisions from different perspectives. Each \"hat\" represents a different thinking approach:\n",
19
+ "\n",
20
+ "- **White Hat (Facts):** Focuses on available information, facts, and data.\n",
21
+ "- **Red Hat (Feelings):** Represents emotions, intuition, and gut feelings.\n",
22
+ "- **Black Hat (Critical):** Identifies potential problems, risks, and negative aspects.\n",
23
+ "- **Yellow Hat (Positive):** Looks for benefits, opportunities, and positive aspects.\n",
24
+ "- **Green Hat (Creative):** Encourages new ideas, alternatives, and possibilities.\n",
25
+ "- **Blue Hat (Process):** Manages the thinking process and ensures all perspectives are considered.\n",
26
+ "\n",
27
+ "In this simulator, we'll use these different perspectives to thoroughly evaluate and improve technological solutions proposed by an LLM."
28
+ ]
29
+ },
30
+ {
31
+ "cell_type": "code",
32
+ "execution_count": 1,
33
+ "metadata": {},
34
+ "outputs": [],
35
+ "source": [
36
+ "import os\n",
37
+ "import json\n",
38
+ "from dotenv import load_dotenv\n",
39
+ "from openai import OpenAI\n",
40
+ "from anthropic import Anthropic\n",
41
+ "from IPython.display import Markdown, display"
42
+ ]
43
+ },
44
+ {
45
+ "cell_type": "code",
46
+ "execution_count": null,
47
+ "metadata": {},
48
+ "outputs": [],
49
+ "source": [
50
+ "load_dotenv(override=True)"
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "code",
55
+ "execution_count": null,
56
+ "metadata": {},
57
+ "outputs": [],
58
+ "source": [
59
+ "# Print the key prefixes to help with any debugging\n",
60
+ "\n",
61
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
62
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
63
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
64
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
65
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
66
+ "\n",
67
+ "if openai_api_key:\n",
68
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
69
+ "else:\n",
70
+ " print(\"OpenAI API Key not set\")\n",
71
+ " \n",
72
+ "if anthropic_api_key:\n",
73
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
74
+ "else:\n",
75
+ " print(\"Anthropic API Key not set\")\n",
76
+ "\n",
77
+ "if google_api_key:\n",
78
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
79
+ "else:\n",
80
+ " print(\"Google API Key not set\")\n",
81
+ "\n",
82
+ "if deepseek_api_key:\n",
83
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
84
+ "else:\n",
85
+ " print(\"DeepSeek API Key not set\")\n",
86
+ "\n",
87
+ "if groq_api_key:\n",
88
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
89
+ "else:\n",
90
+ " print(\"Groq API Key not set\")"
91
+ ]
92
+ },
93
+ {
94
+ "cell_type": "code",
95
+ "execution_count": null,
96
+ "metadata": {},
97
+ "outputs": [],
98
+ "source": [
99
+ "request = \"Generate a technological solution to solve a specific workplace challenge. Choose an employee role, in a specific industry, and identify a time-consuming or error-prone daily task they face. Then, create an innovative yet practical technological solution that addresses this challenge. Include what technologies it uses (AI, automation, etc.), how it integrates with existing systems, its key benefits, and basic implementation requirements. Keep your solution realistic with current technology. \"\n",
100
+ "request += \"Answer only with the question, no explanation.\"\n",
101
+ "messages = [{\"role\": \"user\", \"content\": request}]\n",
102
+ "\n",
103
+ "openai = OpenAI()\n",
104
+ "response = openai.chat.completions.create(\n",
105
+ " model=\"gpt-4o-mini\",\n",
106
+ " messages=messages,\n",
107
+ ")\n",
108
+ "question = response.choices[0].message.content\n",
109
+ "print(question)"
110
+ ]
111
+ },
112
+ {
113
+ "cell_type": "code",
114
+ "execution_count": null,
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "validation_prompt = f\"\"\"Validate and improve the following technological solution. For each iteration, check if the solution meets these criteria:\n",
119
+ "\n",
120
+ "1. Clarity:\n",
121
+ " - Is the problem clearly defined?\n",
122
+ " - Is the solution clearly explained?\n",
123
+ " - Are the technical components well-described?\n",
124
+ "\n",
125
+ "2. Specificity:\n",
126
+ " - Are there specific examples or use cases?\n",
127
+ " - Are the technologies and tools specifically named?\n",
128
+ " - Are the implementation steps detailed?\n",
129
+ "\n",
130
+ "3. Context:\n",
131
+ " - Is the industry/company context clear?\n",
132
+ " - Are the user roles and needs well-defined?\n",
133
+ " - Is the current workflow/problem well-described?\n",
134
+ "\n",
135
+ "4. Constraints:\n",
136
+ " - Are there clear technical limitations?\n",
137
+ " - Are there budget/time constraints mentioned?\n",
138
+ " - Are there integration requirements specified?\n",
139
+ "\n",
140
+ "If any of these criteria are not met, improve the solution by:\n",
141
+ "1. Adding missing details\n",
142
+ "2. Clarifying ambiguous points\n",
143
+ "3. Providing more specific examples\n",
144
+ "4. Including relevant constraints\n",
145
+ "\n",
146
+ "Here is the technological solution to validate and improve:\n",
147
+ "{question} \n",
148
+ "Provide an improved version that addresses any missing or unclear aspects. If this is the 5th iteration, return the final improved version without further changes.\n",
149
+ "\n",
150
+ "Response only with the Improved Solution:\n",
151
+ "[Your improved solution here]\"\"\"\n",
152
+ "\n",
153
+ "messages = [{\"role\": \"user\", \"content\": validation_prompt}]\n",
154
+ "\n",
155
+ "response = openai.chat.completions.create(model=\"gpt-4o\", messages=messages)\n",
156
+ "question = response.choices[0].message.content\n",
157
+ "\n",
158
+ "display(Markdown(question))"
159
+ ]
160
+ },
161
+ {
162
+ "cell_type": "markdown",
163
+ "metadata": {},
164
+ "source": [
165
+ "\n",
166
+ "In this section, we will ask each AI model to analyze a technological solution using the Six Thinking Hats methodology. Each model will:\n",
167
+ "\n",
168
+ "1. First generate a technological solution for a workplace challenge\n",
169
+ "2. Then analyze that solution using each of the Six Thinking Hats\n",
170
+ "\n",
171
+ "Each model will provide:\n",
172
+ "1. An initial technological solution\n",
173
+ "2. A structured analysis using all six thinking hats\n",
174
+ "3. A final recommendation based on the comprehensive analysis\n",
175
+ "\n",
176
+ "This approach will allow us to:\n",
177
+ "- Compare how different models apply the Six Thinking Hats methodology\n",
178
+ "- Identify patterns and differences in their analytical approaches\n",
179
+ "- Gather diverse perspectives on the same solution\n",
180
+ "- Create a rich, multi-faceted evaluation of each proposed technological solution\n",
181
+ "\n",
182
+ "The responses will be collected and displayed below, showing how each model applies the Six Thinking Hats methodology to evaluate and improve the proposed solutions."
183
+ ]
184
+ },
185
+ {
186
+ "cell_type": "code",
187
+ "execution_count": 6,
188
+ "metadata": {},
189
+ "outputs": [],
190
+ "source": [
191
+ "models = []\n",
192
+ "answers = []\n",
193
+ "combined_question = f\" Analyze the technological solution prposed in {question} using the Six Thinking Hats methodology. For each hat, provide a detailed analysis. Finally, provide a comprehensive recommendation based on all the above analyses.\"\n",
194
+ "messages = [{\"role\": \"user\", \"content\": combined_question}]"
195
+ ]
196
+ },
197
+ {
198
+ "cell_type": "code",
199
+ "execution_count": null,
200
+ "metadata": {},
201
+ "outputs": [],
202
+ "source": [
203
+ "# GPT thinking process\n",
204
+ "\n",
205
+ "model_name = \"gpt-4o\"\n",
206
+ "\n",
207
+ "\n",
208
+ "response = openai.chat.completions.create(model=model_name, messages=messages)\n",
209
+ "answer = response.choices[0].message.content\n",
210
+ "\n",
211
+ "display(Markdown(answer))\n",
212
+ "models.append(model_name)\n",
213
+ "answers.append(answer)"
214
+ ]
215
+ },
216
+ {
217
+ "cell_type": "code",
218
+ "execution_count": null,
219
+ "metadata": {},
220
+ "outputs": [],
221
+ "source": [
222
+ "# Claude thinking process\n",
223
+ "\n",
224
+ "model_name = \"claude-3-7-sonnet-latest\"\n",
225
+ "\n",
226
+ "claude = Anthropic()\n",
227
+ "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n",
228
+ "answer = response.content[0].text\n",
229
+ "\n",
230
+ "display(Markdown(answer))\n",
231
+ "models.append(model_name)\n",
232
+ "answers.append(answer)"
233
+ ]
234
+ },
235
+ {
236
+ "cell_type": "code",
237
+ "execution_count": null,
238
+ "metadata": {},
239
+ "outputs": [],
240
+ "source": [
241
+ "# Gemini thinking process\n",
242
+ "\n",
243
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
244
+ "model_name = \"gemini-2.0-flash\"\n",
245
+ "\n",
246
+ "response = gemini.chat.completions.create(model=model_name, messages=messages)\n",
247
+ "answer = response.choices[0].message.content\n",
248
+ "\n",
249
+ "display(Markdown(answer))\n",
250
+ "models.append(model_name)\n",
251
+ "answers.append(answer)"
252
+ ]
253
+ },
254
+ {
255
+ "cell_type": "code",
256
+ "execution_count": null,
257
+ "metadata": {},
258
+ "outputs": [],
259
+ "source": [
260
+ "# Deepseek thinking process\n",
261
+ "\n",
262
+ "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
263
+ "model_name = \"deepseek-chat\"\n",
264
+ "\n",
265
+ "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n",
266
+ "answer = response.choices[0].message.content\n",
267
+ "\n",
268
+ "display(Markdown(answer))\n",
269
+ "models.append(model_name)\n",
270
+ "answers.append(answer)"
271
+ ]
272
+ },
273
+ {
274
+ "cell_type": "code",
275
+ "execution_count": null,
276
+ "metadata": {},
277
+ "outputs": [],
278
+ "source": [
279
+ "# Groq thinking process\n",
280
+ "\n",
281
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
282
+ "model_name = \"llama-3.3-70b-versatile\"\n",
283
+ "\n",
284
+ "response = groq.chat.completions.create(model=model_name, messages=messages)\n",
285
+ "answer = response.choices[0].message.content\n",
286
+ "\n",
287
+ "display(Markdown(answer))\n",
288
+ "models.append(model_name)\n",
289
+ "answers.append(answer)"
290
+ ]
291
+ },
292
+ {
293
+ "cell_type": "code",
294
+ "execution_count": null,
295
+ "metadata": {},
296
+ "outputs": [],
297
+ "source": [
298
+ "!ollama pull llama3.2"
299
+ ]
300
+ },
301
+ {
302
+ "cell_type": "code",
303
+ "execution_count": null,
304
+ "metadata": {},
305
+ "outputs": [],
306
+ "source": [
307
+ "# Ollama thinking process\n",
308
+ "\n",
309
+ "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
310
+ "model_name = \"llama3.2\"\n",
311
+ "\n",
312
+ "response = ollama.chat.completions.create(model=model_name, messages=messages)\n",
313
+ "answer = response.choices[0].message.content\n",
314
+ "\n",
315
+ "display(Markdown(answer))\n",
316
+ "models.append(model_name)\n",
317
+ "answers.append(answer)"
318
+ ]
319
+ },
320
+ {
321
+ "cell_type": "code",
322
+ "execution_count": null,
323
+ "metadata": {},
324
+ "outputs": [],
325
+ "source": [
326
+ "for model, answer in zip(models, answers):\n",
327
+ " print(f\"Model: {model}\\n\\n{answer}\")"
328
+ ]
329
+ },
330
+ {
331
+ "cell_type": "markdown",
332
+ "metadata": {},
333
+ "source": [
334
+ "## Next Step: Solution Synthesis and Enhancement\n",
335
+ "\n",
336
+ "**Best Recommendation Selection and Extended Solution Development**\n",
337
+ "\n",
338
+ "After applying the Six Thinking Hats analysis to evaluate the initial technological solution from multiple perspectives, the simulator will:\n",
339
+ "\n",
340
+ "1. **Synthesize Analysis Results**: Compile insights from all six thinking perspectives (White, Red, Black, Yellow, Green, and Blue hats) to identify the most compelling recommendations and improvements.\n",
341
+ "\n",
342
+ "2. **Select Optimal Recommendation**: Using a weighted evaluation system that considers feasibility, impact, and alignment with organizational goals, the simulator will identify and present the single best recommendation that emerged from the Six Thinking Hats analysis.\n",
343
+ "\n",
344
+ "3. **Generate Extended Solution**: Building upon the selected best recommendation, the simulator will create a comprehensive, enhanced version of the original technological solution that incorporates:\n",
345
+ " - Key insights from the critical analysis (Black Hat)\n",
346
+ " - Positive opportunities identified (Yellow Hat)\n",
347
+ " - Creative alternatives and innovations (Green Hat)\n",
348
+ " - Factual considerations and data requirements (White Hat)\n",
349
+ " - User experience and emotional factors (Red Hat)\n",
350
+ "\n",
351
+ "4. **Multi-Model Enhancement**: To further strengthen the solution, the simulator will leverage additional AI models or perspectives to provide supplementary recommendations that complement the Six Thinking Hats analysis, offering a more robust and well-rounded final technological solution.\n",
352
+ "\n",
353
+ "This step transforms the analytical insights into actionable improvements, delivering a refined solution that has been thoroughly evaluated and enhanced through structured critical thinking."
354
+ ]
355
+ },
356
+ {
357
+ "cell_type": "code",
358
+ "execution_count": 14,
359
+ "metadata": {},
360
+ "outputs": [],
361
+ "source": [
362
+ "together = \"\"\n",
363
+ "for index, answer in enumerate(answers):\n",
364
+ " together += f\"# Response from model {index+1}\\n\\n\"\n",
365
+ " together += answer + \"\\n\\n\""
366
+ ]
367
+ },
368
+ {
369
+ "cell_type": "code",
370
+ "execution_count": null,
371
+ "metadata": {},
372
+ "outputs": [],
373
+ "source": [
374
+ "from IPython.display import Markdown, display\n",
375
+ "import re\n",
376
+ "\n",
377
+ "print(f\"Each model has been given this technological solution to analyze: {question}\")\n",
378
+ "\n",
379
+ "# First, get the best individual response\n",
380
+ "judge_prompt = f\"\"\"\n",
381
+ " You are judging the quality of {len(models)} responses.\n",
382
+ " Evaluate each response based on:\n",
383
+ " 1. Clarity and coherence\n",
384
+ " 2. Depth of analysis\n",
385
+ " 3. Practicality of recommendations\n",
386
+ " 4. Originality of insights\n",
387
+ " \n",
388
+ " Rank the responses from best to worst.\n",
389
+ " Respond with the model index of the best response, nothing else.\n",
390
+ " \n",
391
+ " Here are the responses:\n",
392
+ " {answers}\n",
393
+ " \"\"\"\n",
394
+ " \n",
395
+ "# Get the best response\n",
396
+ "judge_response = openai.chat.completions.create(\n",
397
+ " model=\"o3-mini\",\n",
398
+ " messages=[{\"role\": \"user\", \"content\": judge_prompt}]\n",
399
+ ")\n",
400
+ "best_response = judge_response.choices[0].message.content\n",
401
+ "\n",
402
+ "print(f\"Best Response's Model: {models[int(best_response)]}\")\n",
403
+ "\n",
404
+ "synthesis_prompt = f\"\"\"\n",
405
+ " Here is the best response's model index from the judge:\n",
406
+ "\n",
407
+ " {best_response}\n",
408
+ "\n",
409
+ " And here are the responses from all the models:\n",
410
+ "\n",
411
+ " {together}\n",
412
+ "\n",
413
+ " Synthesize the responses from the non-best models into one comprehensive answer that:\n",
414
+ " 1. Captures the best insights from each response that could add value to the best response from the judge\n",
415
+ " 2. Resolves any contradictions between responses before extending the best response\n",
416
+ " 3. Presents a clear and coherent final answer that is a comprehensive extension of the best response from the judge\n",
417
+ " 4. Maintains the same format as the original best response from the judge\n",
418
+ " 5. Compiles all additional recommendations mentioned by all models\n",
419
+ "\n",
420
+ " Show the best response {answers[int(best_response)]} and then your synthesized response specifying which are additional recommendations to the best response:\n",
421
+ " \"\"\"\n",
422
+ "\n",
423
+ "# Get the synthesized response\n",
424
+ "synthesis_response = claude.messages.create(\n",
425
+ " model=\"claude-3-7-sonnet-latest\",\n",
426
+ " messages=[{\"role\": \"user\", \"content\": synthesis_prompt}],\n",
427
+ " max_tokens=10000\n",
428
+ ")\n",
429
+ "synthesized_answer = synthesis_response.content[0].text\n",
430
+ "\n",
431
+ "converted_answer = re.sub(r'\\\\[\\[\\]]', '$$', synthesized_answer)\n",
432
+ "display(Markdown(converted_answer))"
433
+ ]
434
+ }
435
+ ],
436
+ "metadata": {
437
+ "kernelspec": {
438
+ "display_name": ".venv",
439
+ "language": "python",
440
+ "name": "python3"
441
+ },
442
+ "language_info": {
443
+ "codemirror_mode": {
444
+ "name": "ipython",
445
+ "version": 3
446
+ },
447
+ "file_extension": ".py",
448
+ "mimetype": "text/x-python",
449
+ "name": "python",
450
+ "nbconvert_exporter": "python",
451
+ "pygments_lexer": "ipython3",
452
+ "version": "3.12.10"
453
+ }
454
+ },
455
+ "nbformat": 4,
456
+ "nbformat_minor": 2
457
+ }
community_contributions/3_lab3_groq_llama_generator_gemini_evaluator.ipynb ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Chat app with LinkedIn Profile Information - Groq LLama as Generator and Gemini as evaluator\n"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 58,
13
+ "metadata": {},
14
+ "outputs": [],
15
+ "source": [
16
+ "# If you don't know what any of these packages do - you can always ask ChatGPT for a guide!\n",
17
+ "\n",
18
+ "from dotenv import load_dotenv\n",
19
+ "from openai import OpenAI\n",
20
+ "from pypdf import PdfReader\n",
21
+ "from groq import Groq\n",
22
+ "import gradio as gr"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": 59,
28
+ "metadata": {},
29
+ "outputs": [],
30
+ "source": [
31
+ "load_dotenv(override=True)\n",
32
+ "groq = Groq()"
33
+ ]
34
+ },
35
+ {
36
+ "cell_type": "code",
37
+ "execution_count": 60,
38
+ "metadata": {},
39
+ "outputs": [],
40
+ "source": [
41
+ "reader = PdfReader(\"me/My_LinkedIn.pdf\")\n",
42
+ "linkedin = \"\"\n",
43
+ "for page in reader.pages:\n",
44
+ " text = page.extract_text()\n",
45
+ " if text:\n",
46
+ " linkedin += text"
47
+ ]
48
+ },
49
+ {
50
+ "cell_type": "code",
51
+ "execution_count": null,
52
+ "metadata": {},
53
+ "outputs": [],
54
+ "source": [
55
+ "print(linkedin)"
56
+ ]
57
+ },
58
+ {
59
+ "cell_type": "code",
60
+ "execution_count": 61,
61
+ "metadata": {},
62
+ "outputs": [],
63
+ "source": [
64
+ "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
65
+ " summary = f.read()"
66
+ ]
67
+ },
68
+ {
69
+ "cell_type": "code",
70
+ "execution_count": 62,
71
+ "metadata": {},
72
+ "outputs": [],
73
+ "source": [
74
+ "name = \"Maalaiappan Subramanian\""
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 63,
80
+ "metadata": {},
81
+ "outputs": [],
82
+ "source": [
83
+ "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
84
+ "particularly questions related to {name}'s career, background, skills and experience. \\\n",
85
+ "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
86
+ "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
87
+ "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
88
+ "If you don't know the answer, say so.\"\n",
89
+ "\n",
90
+ "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
91
+ "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": null,
97
+ "metadata": {},
98
+ "outputs": [],
99
+ "source": [
100
+ "system_prompt"
101
+ ]
102
+ },
103
+ {
104
+ "cell_type": "code",
105
+ "execution_count": 65,
106
+ "metadata": {},
107
+ "outputs": [],
108
+ "source": [
109
+ "def chat(message, history):\n",
110
+ " # Below line is to remove the metadata and options from the history\n",
111
+ " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n",
112
+ " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
113
+ " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n",
114
+ " return response.choices[0].message.content"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": null,
120
+ "metadata": {},
121
+ "outputs": [],
122
+ "source": [
123
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
124
+ ]
125
+ },
126
+ {
127
+ "cell_type": "code",
128
+ "execution_count": 67,
129
+ "metadata": {},
130
+ "outputs": [],
131
+ "source": [
132
+ "# Create a Pydantic model for the Evaluation\n",
133
+ "\n",
134
+ "from pydantic import BaseModel\n",
135
+ "\n",
136
+ "class Evaluation(BaseModel):\n",
137
+ " is_acceptable: bool\n",
138
+ " feedback: str\n"
139
+ ]
140
+ },
141
+ {
142
+ "cell_type": "code",
143
+ "execution_count": 69,
144
+ "metadata": {},
145
+ "outputs": [],
146
+ "source": [
147
+ "evaluator_system_prompt = f\"You are an evaluator that decides whether a response to a question is acceptable. \\\n",
148
+ "You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \\\n",
149
+ "The Agent is playing the role of {name} and is representing {name} on their website. \\\n",
150
+ "The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
151
+ "The Agent has been provided with context on {name} in the form of their summary and LinkedIn details. Here's the information:\"\n",
152
+ "\n",
153
+ "evaluator_system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
154
+ "evaluator_system_prompt += f\"With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback.\""
155
+ ]
156
+ },
157
+ {
158
+ "cell_type": "code",
159
+ "execution_count": 70,
160
+ "metadata": {},
161
+ "outputs": [],
162
+ "source": [
163
+ "def evaluator_user_prompt(reply, message, history):\n",
164
+ " user_prompt = f\"Here's the conversation between the User and the Agent: \\n\\n{history}\\n\\n\"\n",
165
+ " user_prompt += f\"Here's the latest message from the User: \\n\\n{message}\\n\\n\"\n",
166
+ " user_prompt += f\"Here's the latest response from the Agent: \\n\\n{reply}\\n\\n\"\n",
167
+ " user_prompt += f\"Please evaluate the response, replying with whether it is acceptable and your feedback.\"\n",
168
+ " return user_prompt"
169
+ ]
170
+ },
171
+ {
172
+ "cell_type": "code",
173
+ "execution_count": 71,
174
+ "metadata": {},
175
+ "outputs": [],
176
+ "source": [
177
+ "import os\n",
178
+ "gemini = OpenAI(\n",
179
+ " api_key=os.getenv(\"GOOGLE_API_KEY\"), \n",
180
+ " base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
181
+ ")"
182
+ ]
183
+ },
184
+ {
185
+ "cell_type": "code",
186
+ "execution_count": 72,
187
+ "metadata": {},
188
+ "outputs": [],
189
+ "source": [
190
+ "def evaluate(reply, message, history) -> Evaluation:\n",
191
+ "\n",
192
+ " messages = [{\"role\": \"system\", \"content\": evaluator_system_prompt}] + [{\"role\": \"user\", \"content\": evaluator_user_prompt(reply, message, history)}]\n",
193
+ " response = gemini.beta.chat.completions.parse(model=\"gemini-2.0-flash\", messages=messages, response_format=Evaluation)\n",
194
+ " return response.choices[0].message.parsed"
195
+ ]
196
+ },
197
+ {
198
+ "cell_type": "code",
199
+ "execution_count": 73,
200
+ "metadata": {},
201
+ "outputs": [],
202
+ "source": [
203
+ "def rerun(reply, message, history, feedback):\n",
204
+ " # Below line is to remove the metadata and options from the history\n",
205
+ " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n",
206
+ " updated_system_prompt = system_prompt + f\"\\n\\n## Previous answer rejected\\nYou just tried to reply, but the quality control rejected your reply\\n\"\n",
207
+ " updated_system_prompt += f\"## Your attempted answer:\\n{reply}\\n\\n\"\n",
208
+ " updated_system_prompt += f\"## Reason for rejection:\\n{feedback}\\n\\n\"\n",
209
+ " messages = [{\"role\": \"system\", \"content\": updated_system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
210
+ " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n",
211
+ " return response.choices[0].message.content"
212
+ ]
213
+ },
214
+ {
215
+ "cell_type": "code",
216
+ "execution_count": 74,
217
+ "metadata": {},
218
+ "outputs": [],
219
+ "source": [
220
+ "def chat(message, history):\n",
221
+ " if \"personal\" in message:\n",
222
+ " system = system_prompt + \"\\n\\nEverything in your reply needs to be in Gen Z language - \\\n",
223
+ " it is mandatory that you respond only and entirely in Gen Z language\"\n",
224
+ " else:\n",
225
+ " system = system_prompt\n",
226
+ " # Below line is to remove the metadata and options from the history\n",
227
+ " history = [{k: v for k, v in item.items() if k not in ('metadata', 'options')} for item in history]\n",
228
+ " messages = [{\"role\": \"system\", \"content\": system}] + history + [{\"role\": \"user\", \"content\": message}]\n",
229
+ " response = groq.chat.completions.create(model=\"llama-3.3-70b-versatile\", messages=messages)\n",
230
+ " reply =response.choices[0].message.content\n",
231
+ "\n",
232
+ " evaluation = evaluate(reply, message, history)\n",
233
+ " \n",
234
+ " if evaluation.is_acceptable:\n",
235
+ " print(\"Passed evaluation - returning reply\")\n",
236
+ " else:\n",
237
+ " print(\"Failed evaluation - retrying\")\n",
238
+ " print(evaluation.feedback)\n",
239
+ " reply = rerun(reply, message, history, evaluation.feedback) \n",
240
+ " return reply"
241
+ ]
242
+ },
243
+ {
244
+ "cell_type": "code",
245
+ "execution_count": null,
246
+ "metadata": {},
247
+ "outputs": [],
248
+ "source": [
249
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
250
+ ]
251
+ },
252
+ {
253
+ "cell_type": "markdown",
254
+ "metadata": {},
255
+ "source": []
256
+ },
257
+ {
258
+ "cell_type": "code",
259
+ "execution_count": null,
260
+ "metadata": {},
261
+ "outputs": [],
262
+ "source": []
263
+ }
264
+ ],
265
+ "metadata": {
266
+ "kernelspec": {
267
+ "display_name": ".venv",
268
+ "language": "python",
269
+ "name": "python3"
270
+ },
271
+ "language_info": {
272
+ "codemirror_mode": {
273
+ "name": "ipython",
274
+ "version": 3
275
+ },
276
+ "file_extension": ".py",
277
+ "mimetype": "text/x-python",
278
+ "name": "python",
279
+ "nbconvert_exporter": "python",
280
+ "pygments_lexer": "ipython3",
281
+ "version": "3.12.10"
282
+ }
283
+ },
284
+ "nbformat": 4,
285
+ "nbformat_minor": 2
286
+ }
community_contributions/4_lab4_slack.ipynb ADDED
@@ -0,0 +1,469 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## The first big project - Professionally You!\n",
8
+ "\n",
9
+ "### And, Tool use.\n",
10
+ "\n",
11
+ "### But first: introducing Slack\n",
12
+ "\n",
13
+ "Slack is a nifty tool for sending Push Notifications to your phone.\n",
14
+ "\n",
15
+ "It's super easy to set up and install!\n",
16
+ "\n",
17
+ "Simply visit https://api.slack.com and sign up for a free account, and create your new workspace and app.\n",
18
+ "\n",
19
+ "1. Create a Slack App:\n",
20
+ "- Go to the [Slack API portal](https://api.slack.com/apps) and click Create New App.\n",
21
+ "- Choose From scratch, provide an App Name (e.g., \"CustomerNotifier\"), and select the Slack workspace where you want to - install the app.\n",
22
+ "- Click Create App.\n",
23
+ "\n",
24
+ "2. Add Required Permissions (Scopes):\n",
25
+ "- Navigate to OAuth & Permissions in the left sidebar of your app’s management page.\n",
26
+ "- Under Bot Token Scopes, add the chat:write scope to allow your app to post messages. If you need to send direct messages (DMs) to users, also add im:write and users:read to fetch user IDs.\n",
27
+ "- If you plan to post to specific channels, ensure the app has permissions like channels:write or groups:write for public or private channels, respectively.\n",
28
+ "\n",
29
+ "3. Install the App to Your Workspace:\n",
30
+ "- In the OAuth & Permissions section, click Install to Workspace.\n",
31
+ "- Authorize the app, selecting the channel where it will post messages (if using incoming webhooks) or granting the necessary permissions.\n",
32
+ "- After installation, you’ll receive a Bot User OAuth Token (starts with xoxb-). Copy this token, as it will be used for - API authentication. Keep it secure and avoid hardcoding it in your source code.\n",
33
+ "\n",
34
+ "(This is so you could choose to organize your push notifications into different apps in the future.)\n",
35
+ "\n",
36
+ "4. Create a new private channel in slack App\n",
37
+ "- Opt to use Private Access\n",
38
+ "- After creating the private channel, type \"@<your bot name in step 1>\" to allow slack default bot to invite the bot into your chat\n",
39
+ "- Go to \"About\" of your private chat. Copy the channel Id at the bottom\n",
40
+ "\n",
41
+ "5. Install slack_sdk==3.35.0 into your env\n",
42
+ "```\n",
43
+ "uv pip install slack_sdk==3.35.0\n",
44
+ "```\n",
45
+ "\n",
46
+ "Add to your `.env` file:\n",
47
+ "```\n",
48
+ "SLACK_AGENT_CHANNEL_ID=put_your_user_token_here\n",
49
+ "SLACK_BOT_AGENT_OAUTH_TOKEN=put_the_oidc_token_here\n",
50
+ "```\n",
51
+ "\n",
52
+ "And install the Slack app on your phone."
53
+ ]
54
+ },
55
+ {
56
+ "cell_type": "code",
57
+ "execution_count": 2,
58
+ "metadata": {},
59
+ "outputs": [],
60
+ "source": [
61
+ "# imports\n",
62
+ "\n",
63
+ "from dotenv import load_dotenv\n",
64
+ "from openai import OpenAI\n",
65
+ "import json\n",
66
+ "import os\n",
67
+ "import requests\n",
68
+ "from pypdf import PdfReader\n",
69
+ "import gradio as gr\n",
70
+ "from slack_sdk import WebClient\n",
71
+ "from slack_sdk.errors import SlackApiError"
72
+ ]
73
+ },
74
+ {
75
+ "cell_type": "code",
76
+ "execution_count": 3,
77
+ "metadata": {},
78
+ "outputs": [],
79
+ "source": [
80
+ "# The usual start\n",
81
+ "\n",
82
+ "load_dotenv(override=True)\n",
83
+ "openai = OpenAI()"
84
+ ]
85
+ },
86
+ {
87
+ "cell_type": "code",
88
+ "execution_count": 11,
89
+ "metadata": {},
90
+ "outputs": [],
91
+ "source": [
92
+ "# For slack\n",
93
+ "\n",
94
+ "slack_channel_id:str = str(os.getenv(\"SLACK_AGENT_CHANNEL_ID\"))\n",
95
+ "slack_oauth_token = os.getenv(\"SLACK_BOT_AGENT_OAUTH_TOKEN\")\n",
96
+ "slack_client = WebClient(token=slack_oauth_token)\n"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "execution_count": 12,
102
+ "metadata": {},
103
+ "outputs": [],
104
+ "source": [
105
+ "def push(message):\n",
106
+ " print(f\"Push: {message}\")\n",
107
+ " response = slack_client.chat_postMessage(\n",
108
+ " channel=slack_channel_id,\n",
109
+ " text=message\n",
110
+ " )"
111
+ ]
112
+ },
113
+ {
114
+ "cell_type": "code",
115
+ "execution_count": null,
116
+ "metadata": {},
117
+ "outputs": [],
118
+ "source": [
119
+ "push(\"HEY!!\")"
120
+ ]
121
+ },
122
+ {
123
+ "cell_type": "code",
124
+ "execution_count": 14,
125
+ "metadata": {},
126
+ "outputs": [],
127
+ "source": [
128
+ "def record_user_details(email, name=\"Name not provided\", notes=\"not provided\"):\n",
129
+ " push(f\"Recording interest from {name} with email {email} and notes {notes}\")\n",
130
+ " return {\"recorded\": \"ok\"}"
131
+ ]
132
+ },
133
+ {
134
+ "cell_type": "code",
135
+ "execution_count": 15,
136
+ "metadata": {},
137
+ "outputs": [],
138
+ "source": [
139
+ "def record_unknown_question(question):\n",
140
+ " push(f\"Recording {question} asked that I couldn't answer\")\n",
141
+ " return {\"recorded\": \"ok\"}"
142
+ ]
143
+ },
144
+ {
145
+ "cell_type": "code",
146
+ "execution_count": 16,
147
+ "metadata": {},
148
+ "outputs": [],
149
+ "source": [
150
+ "record_user_details_json = {\n",
151
+ " \"name\": \"record_user_details\",\n",
152
+ " \"description\": \"Use this tool to record that a user is interested in being in touch and provided an email address\",\n",
153
+ " \"parameters\": {\n",
154
+ " \"type\": \"object\",\n",
155
+ " \"properties\": {\n",
156
+ " \"email\": {\n",
157
+ " \"type\": \"string\",\n",
158
+ " \"description\": \"The email address of this user\"\n",
159
+ " },\n",
160
+ " \"name\": {\n",
161
+ " \"type\": \"string\",\n",
162
+ " \"description\": \"The user's name, if they provided it\"\n",
163
+ " }\n",
164
+ " ,\n",
165
+ " \"notes\": {\n",
166
+ " \"type\": \"string\",\n",
167
+ " \"description\": \"Any additional information about the conversation that's worth recording to give context\"\n",
168
+ " }\n",
169
+ " },\n",
170
+ " \"required\": [\"email\"],\n",
171
+ " \"additionalProperties\": False\n",
172
+ " }\n",
173
+ "}"
174
+ ]
175
+ },
176
+ {
177
+ "cell_type": "code",
178
+ "execution_count": 17,
179
+ "metadata": {},
180
+ "outputs": [],
181
+ "source": [
182
+ "record_unknown_question_json = {\n",
183
+ " \"name\": \"record_unknown_question\",\n",
184
+ " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
185
+ " \"parameters\": {\n",
186
+ " \"type\": \"object\",\n",
187
+ " \"properties\": {\n",
188
+ " \"question\": {\n",
189
+ " \"type\": \"string\",\n",
190
+ " \"description\": \"The question that couldn't be answered\"\n",
191
+ " },\n",
192
+ " },\n",
193
+ " \"required\": [\"question\"],\n",
194
+ " \"additionalProperties\": False\n",
195
+ " }\n",
196
+ "}"
197
+ ]
198
+ },
199
+ {
200
+ "cell_type": "code",
201
+ "execution_count": 18,
202
+ "metadata": {},
203
+ "outputs": [],
204
+ "source": [
205
+ "tools = [{\"type\": \"function\", \"function\": record_user_details_json},\n",
206
+ " {\"type\": \"function\", \"function\": record_unknown_question_json}]"
207
+ ]
208
+ },
209
+ {
210
+ "cell_type": "code",
211
+ "execution_count": null,
212
+ "metadata": {},
213
+ "outputs": [],
214
+ "source": [
215
+ "tools"
216
+ ]
217
+ },
218
+ {
219
+ "cell_type": "code",
220
+ "execution_count": 20,
221
+ "metadata": {},
222
+ "outputs": [],
223
+ "source": [
224
+ "# This function can take a list of tool calls, and run them. This is the IF statement!!\n",
225
+ "\n",
226
+ "def handle_tool_calls(tool_calls):\n",
227
+ " results = []\n",
228
+ " for tool_call in tool_calls:\n",
229
+ " tool_name = tool_call.function.name\n",
230
+ " arguments = json.loads(tool_call.function.arguments)\n",
231
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
232
+ "\n",
233
+ " # THE BIG IF STATEMENT!!!\n",
234
+ "\n",
235
+ " if tool_name == \"record_user_details\":\n",
236
+ " result = record_user_details(**arguments)\n",
237
+ " elif tool_name == \"record_unknown_question\":\n",
238
+ " result = record_unknown_question(**arguments)\n",
239
+ "\n",
240
+ " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
241
+ " return results"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "code",
246
+ "execution_count": null,
247
+ "metadata": {},
248
+ "outputs": [],
249
+ "source": [
250
+ "globals()[\"record_unknown_question\"](\"this is a really hard question\")"
251
+ ]
252
+ },
253
+ {
254
+ "cell_type": "code",
255
+ "execution_count": 22,
256
+ "metadata": {},
257
+ "outputs": [],
258
+ "source": [
259
+ "# This is a more elegant way that avoids the IF statement.\n",
260
+ "\n",
261
+ "def handle_tool_calls(tool_calls):\n",
262
+ " results = []\n",
263
+ " for tool_call in tool_calls:\n",
264
+ " tool_name = tool_call.function.name\n",
265
+ " arguments = json.loads(tool_call.function.arguments)\n",
266
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
267
+ " tool = globals().get(tool_name)\n",
268
+ " result = tool(**arguments) if tool else {}\n",
269
+ " results.append({\"role\": \"tool\",\"content\": json.dumps(result),\"tool_call_id\": tool_call.id})\n",
270
+ " return results"
271
+ ]
272
+ },
273
+ {
274
+ "cell_type": "code",
275
+ "execution_count": 23,
276
+ "metadata": {},
277
+ "outputs": [],
278
+ "source": [
279
+ "reader = PdfReader(\"me/linkedin.pdf\")\n",
280
+ "linkedin = \"\"\n",
281
+ "for page in reader.pages:\n",
282
+ " text = page.extract_text()\n",
283
+ " if text:\n",
284
+ " linkedin += text\n",
285
+ "\n",
286
+ "with open(\"me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
287
+ " summary = f.read()\n",
288
+ "\n",
289
+ "name = \"Ed Donner\""
290
+ ]
291
+ },
292
+ {
293
+ "cell_type": "code",
294
+ "execution_count": 24,
295
+ "metadata": {},
296
+ "outputs": [],
297
+ "source": [
298
+ "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
299
+ "particularly questions related to {name}'s career, background, skills and experience. \\\n",
300
+ "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
301
+ "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
302
+ "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
303
+ "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \\\n",
304
+ "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \"\n",
305
+ "\n",
306
+ "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
307
+ "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
308
+ ]
309
+ },
310
+ {
311
+ "cell_type": "code",
312
+ "execution_count": 25,
313
+ "metadata": {},
314
+ "outputs": [],
315
+ "source": [
316
+ "def chat(message, history):\n",
317
+ " messages = [{\"role\": \"system\", \"content\": system_prompt}] + history + [{\"role\": \"user\", \"content\": message}]\n",
318
+ " done = False\n",
319
+ " while not done:\n",
320
+ "\n",
321
+ " # This is the call to the LLM - see that we pass in the tools json\n",
322
+ "\n",
323
+ " response = openai.chat.completions.create(model=\"gpt-4o-mini\", messages=messages, tools=tools)\n",
324
+ "\n",
325
+ " finish_reason = response.choices[0].finish_reason\n",
326
+ " \n",
327
+ " # If the LLM wants to call a tool, we do that!\n",
328
+ " \n",
329
+ " if finish_reason==\"tool_calls\":\n",
330
+ " message = response.choices[0].message\n",
331
+ " tool_calls = message.tool_calls\n",
332
+ " results = handle_tool_calls(tool_calls)\n",
333
+ " messages.append(message)\n",
334
+ " messages.extend(results)\n",
335
+ " else:\n",
336
+ " done = True\n",
337
+ " return response.choices[0].message.content"
338
+ ]
339
+ },
340
+ {
341
+ "cell_type": "code",
342
+ "execution_count": null,
343
+ "metadata": {},
344
+ "outputs": [],
345
+ "source": [
346
+ "gr.ChatInterface(chat, type=\"messages\").launch()"
347
+ ]
348
+ },
349
+ {
350
+ "cell_type": "markdown",
351
+ "metadata": {},
352
+ "source": [
353
+ "## And now for deployment\n",
354
+ "\n",
355
+ "This code is in `app.py`\n",
356
+ "\n",
357
+ "We will deploy to HuggingFace Spaces. Thank you student Robert M for improving these instructions.\n",
358
+ "\n",
359
+ "Before you start: remember to update the files in the \"me\" directory - your LinkedIn profile and summary.txt - so that it talks about you! \n",
360
+ "Also check that there's no README file within the 1_foundations directory. If there is one, please delete it. The deploy process creates a new README file in this directory for you.\n",
361
+ "\n",
362
+ "1. Visit https://huggingface.co and set up an account \n",
363
+ "2. From the Avatar menu on the top right, choose Access Tokens. Choose \"Create New Token\". Give it WRITE permissions.\n",
364
+ "3. Take this token and add it to your .env file: `HF_TOKEN=hf_xxx` and see note below if this token doesn't seem to get picked up during deployment \n",
365
+ "4. From the 1_foundations folder, enter: `uv run gradio deploy` and if for some reason this still wants you to enter your HF token, then interrupt it with ctrl+c and run this instead: `uv run dotenv -f ../.env run -- uv run gradio deploy` which forces your keys to all be set as environment variables \n",
366
+ "5. Follow its instructions: name it \"career_conversation\", specify app.py, choose cpu-basic as the hardware, say Yes to needing to supply secrets, provide your openai api key, your pushover user and token, and say \"no\" to github actions. \n",
367
+ "\n",
368
+ "#### Extra note about the HuggingFace token\n",
369
+ "\n",
370
+ "A couple of students have mentioned the HuggingFace doesn't detect their token, even though it's in the .env file. Here are things to try: \n",
371
+ "1. Restart Cursor \n",
372
+ "2. Rerun load_dotenv(override=True) and use a new terminal (the + button on the top right of the Terminal) \n",
373
+ "3. In the Terminal, run this before the gradio deploy: `$env:HF_TOKEN = \"hf_XXXX\"` \n",
374
+ "Thank you James and Martins for these tips. \n",
375
+ "\n",
376
+ "#### More about these secrets:\n",
377
+ "\n",
378
+ "If you're confused by what's going on with these secrets: it just wants you to enter the key name and value for each of your secrets -- so you would enter: \n",
379
+ "`OPENAI_API_KEY` \n",
380
+ "Followed by: \n",
381
+ "`sk-proj-...` \n",
382
+ "\n",
383
+ "And if you don't want to set secrets this way, or something goes wrong with it, it's no problem - you can change your secrets later: \n",
384
+ "1. Log in to HuggingFace website \n",
385
+ "2. Go to your profile screen via the Avatar menu on the top right \n",
386
+ "3. Select the Space you deployed \n",
387
+ "4. Click on the Settings wheel on the top right \n",
388
+ "5. You can scroll down to change your secrets, delete the space, etc.\n",
389
+ "\n",
390
+ "#### And now you should be deployed!\n",
391
+ "\n",
392
+ "Here is mine: https://huggingface.co/spaces/ed-donner/Career_Conversation\n",
393
+ "\n",
394
+ "I just got a push notification that a student asked me how they can become President of their country 😂😂\n",
395
+ "\n",
396
+ "For more information on deployment:\n",
397
+ "\n",
398
+ "https://www.gradio.app/guides/sharing-your-app#hosting-on-hf-spaces\n",
399
+ "\n",
400
+ "To delete your Space in the future: \n",
401
+ "1. Log in to HuggingFace\n",
402
+ "2. From the Avatar menu, select your profile\n",
403
+ "3. Click on the Space itself and select the settings wheel on the top right\n",
404
+ "4. Scroll to the Delete section at the bottom\n",
405
+ "5. ALSO: delete the README file that Gradio may have created inside this 1_foundations folder (otherwise it won't ask you the questions the next time you do a gradio deploy)\n"
406
+ ]
407
+ },
408
+ {
409
+ "cell_type": "markdown",
410
+ "metadata": {},
411
+ "source": [
412
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
413
+ " <tr>\n",
414
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
415
+ " <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
416
+ " </td>\n",
417
+ " <td>\n",
418
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
419
+ " <span style=\"color:#ff7800;\">• First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..<br/>\n",
420
+ " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.<br/>\n",
421
+ " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?<br/>\n",
422
+ " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n",
423
+ " </span>\n",
424
+ " </td>\n",
425
+ " </tr>\n",
426
+ "</table>"
427
+ ]
428
+ },
429
+ {
430
+ "cell_type": "markdown",
431
+ "metadata": {},
432
+ "source": [
433
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
434
+ " <tr>\n",
435
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
436
+ " <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
437
+ " </td>\n",
438
+ " <td>\n",
439
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
440
+ " <span style=\"color:#00bfff;\">Aside from the obvious (your career alter-ego) this has business applications in any situation where you need an AI assistant with domain expertise and an ability to interact with the real world.\n",
441
+ " </span>\n",
442
+ " </td>\n",
443
+ " </tr>\n",
444
+ "</table>"
445
+ ]
446
+ }
447
+ ],
448
+ "metadata": {
449
+ "kernelspec": {
450
+ "display_name": ".venv",
451
+ "language": "python",
452
+ "name": "python3"
453
+ },
454
+ "language_info": {
455
+ "codemirror_mode": {
456
+ "name": "ipython",
457
+ "version": 3
458
+ },
459
+ "file_extension": ".py",
460
+ "mimetype": "text/x-python",
461
+ "name": "python",
462
+ "nbconvert_exporter": "python",
463
+ "pygments_lexer": "ipython3",
464
+ "version": "3.12.11"
465
+ }
466
+ },
467
+ "nbformat": 4,
468
+ "nbformat_minor": 2
469
+ }
community_contributions/4_lab4_with_telegram.ipynb ADDED
@@ -0,0 +1,422 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "### Contributed by Faisal Alkheraiji\n",
8
+ "\n",
9
+ "LinkedIn: https://www.linkedin.com/in/faisalalkheraiji/\n"
10
+ ]
11
+ },
12
+ {
13
+ "cell_type": "markdown",
14
+ "metadata": {},
15
+ "source": [
16
+ "## The first big project - Professionally You!\n",
17
+ "\n",
18
+ "### And, Tool use.\n",
19
+ "\n",
20
+ "### But first: introducing Telegram\n",
21
+ "\n",
22
+ "We need to do the following to get out Telegram chatbot working:\n",
23
+ "\n",
24
+ "1. Create new telegram bot using @BotFather.\n",
25
+ "2. Get our bot token.\n",
26
+ "3. Get your chat ID.\n",
27
+ "\n",
28
+ "For easy and quick tutorial, follow this great tutorial from our friend:\n",
29
+ "\n",
30
+ "https://chatgpt.com/share/686eccf4-34b0-8000-8f34-a3d9269e0578\n",
31
+ "\n",
32
+ "Then add 2 lines to your `.env` file:\n",
33
+ "\n",
34
+ "TELEGRAM*BOT_TOKEN=\\_your bot token*\n",
35
+ "\n",
36
+ "TELEGRAM*CHAT_ID=\\_your chat ID*\n"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": null,
42
+ "metadata": {},
43
+ "outputs": [],
44
+ "source": [
45
+ "# imports\n",
46
+ "\n",
47
+ "from dotenv import load_dotenv\n",
48
+ "from openai import OpenAI\n",
49
+ "import json\n",
50
+ "import os\n",
51
+ "import requests\n",
52
+ "from pypdf import PdfReader\n",
53
+ "import gradio as gr"
54
+ ]
55
+ },
56
+ {
57
+ "cell_type": "code",
58
+ "execution_count": null,
59
+ "metadata": {},
60
+ "outputs": [],
61
+ "source": [
62
+ "# The usual start\n",
63
+ "\n",
64
+ "load_dotenv(override=True)\n",
65
+ "openai = OpenAI()"
66
+ ]
67
+ },
68
+ {
69
+ "cell_type": "code",
70
+ "execution_count": null,
71
+ "metadata": {},
72
+ "outputs": [],
73
+ "source": [
74
+ "# Getting the Telegram bot token and chat ID from environment variables\n",
75
+ "# You can also replace these with your actual values directly\n",
76
+ "\n",
77
+ "TELEGRAM_BOT_TOKEN = os.getenv(\"TELEGRAM_BOT_TOKEN\", \"your_bot_token_here\")\n",
78
+ "TELEGRAM_CHAT_ID = os.getenv(\"TELEGRAM_CHAT_ID\", \"your_chat_id_here\")"
79
+ ]
80
+ },
81
+ {
82
+ "cell_type": "code",
83
+ "execution_count": null,
84
+ "metadata": {},
85
+ "outputs": [],
86
+ "source": [
87
+ "def send_telegram_message(text):\n",
88
+ " url = f\"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage\"\n",
89
+ " payload = {\"chat_id\": TELEGRAM_CHAT_ID, \"text\": text}\n",
90
+ "\n",
91
+ " response = requests.post(url, data=payload)\n",
92
+ "\n",
93
+ " if response.status_code == 200:\n",
94
+ " # print(\"Message sent successfully!\")\n",
95
+ " return {\"status\": \"success\", \"message\": text}\n",
96
+ " else:\n",
97
+ " # print(f\"Failed to send message. Status code: {response.status_code}\")\n",
98
+ " # print(response.text)\n",
99
+ " return {\"status\": \"error\", \"message\": response.text}"
100
+ ]
101
+ },
102
+ {
103
+ "cell_type": "code",
104
+ "execution_count": null,
105
+ "metadata": {},
106
+ "outputs": [],
107
+ "source": [
108
+ "# Example usage\n",
109
+ "send_telegram_message(\"Hello from python notebook !!\")"
110
+ ]
111
+ },
112
+ {
113
+ "cell_type": "code",
114
+ "execution_count": null,
115
+ "metadata": {},
116
+ "outputs": [],
117
+ "source": [
118
+ "def record_user_details(email, name=\"Name not provided\", notes=\"not provided\"):\n",
119
+ " send_telegram_message(\n",
120
+ " f\"Recording interest from {name} with email {email} and notes {notes}\"\n",
121
+ " )\n",
122
+ " return {\"recorded\": \"ok\"}"
123
+ ]
124
+ },
125
+ {
126
+ "cell_type": "code",
127
+ "execution_count": null,
128
+ "metadata": {},
129
+ "outputs": [],
130
+ "source": [
131
+ "def record_unknown_question(question):\n",
132
+ " send_telegram_message(f\"Recording {question} asked that I couldn't answer\")\n",
133
+ " return {\"recorded\": \"ok\"}"
134
+ ]
135
+ },
136
+ {
137
+ "cell_type": "code",
138
+ "execution_count": null,
139
+ "metadata": {},
140
+ "outputs": [],
141
+ "source": [
142
+ "record_user_details_json = {\n",
143
+ " \"name\": \"record_user_details\",\n",
144
+ " \"description\": \"Use this tool to record that a user is interested in being in touch and provided an email address\",\n",
145
+ " \"parameters\": {\n",
146
+ " \"type\": \"object\",\n",
147
+ " \"properties\": {\n",
148
+ " \"email\": {\n",
149
+ " \"type\": \"string\",\n",
150
+ " \"description\": \"The email address of this user\",\n",
151
+ " },\n",
152
+ " \"name\": {\n",
153
+ " \"type\": \"string\",\n",
154
+ " \"description\": \"The user's name, if they provided it\",\n",
155
+ " },\n",
156
+ " \"notes\": {\n",
157
+ " \"type\": \"string\",\n",
158
+ " \"description\": \"Any additional information about the conversation that's worth recording to give context\",\n",
159
+ " },\n",
160
+ " },\n",
161
+ " \"required\": [\"email\"],\n",
162
+ " \"additionalProperties\": False,\n",
163
+ " },\n",
164
+ "}"
165
+ ]
166
+ },
167
+ {
168
+ "cell_type": "code",
169
+ "execution_count": null,
170
+ "metadata": {},
171
+ "outputs": [],
172
+ "source": [
173
+ "record_unknown_question_json = {\n",
174
+ " \"name\": \"record_unknown_question\",\n",
175
+ " \"description\": \"Always use this tool to record any question that couldn't be answered as you didn't know the answer\",\n",
176
+ " \"parameters\": {\n",
177
+ " \"type\": \"object\",\n",
178
+ " \"properties\": {\n",
179
+ " \"question\": {\n",
180
+ " \"type\": \"string\",\n",
181
+ " \"description\": \"The question that couldn't be answered\",\n",
182
+ " },\n",
183
+ " },\n",
184
+ " \"required\": [\"question\"],\n",
185
+ " \"additionalProperties\": False,\n",
186
+ " },\n",
187
+ "}"
188
+ ]
189
+ },
190
+ {
191
+ "cell_type": "code",
192
+ "execution_count": null,
193
+ "metadata": {},
194
+ "outputs": [],
195
+ "source": [
196
+ "tools = [\n",
197
+ " {\"type\": \"function\", \"function\": record_user_details_json},\n",
198
+ " {\"type\": \"function\", \"function\": record_unknown_question_json},\n",
199
+ "]"
200
+ ]
201
+ },
202
+ {
203
+ "cell_type": "code",
204
+ "execution_count": null,
205
+ "metadata": {},
206
+ "outputs": [],
207
+ "source": [
208
+ "tools"
209
+ ]
210
+ },
211
+ {
212
+ "cell_type": "code",
213
+ "execution_count": null,
214
+ "metadata": {},
215
+ "outputs": [],
216
+ "source": [
217
+ "# This function can take a list of tool calls, and run them. This is the IF statement!!\n",
218
+ "\n",
219
+ "\n",
220
+ "def handle_tool_calls(tool_calls):\n",
221
+ " results = []\n",
222
+ " for tool_call in tool_calls:\n",
223
+ " tool_name = tool_call.function.name\n",
224
+ " arguments = json.loads(tool_call.function.arguments)\n",
225
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
226
+ "\n",
227
+ " # THE BIG IF STATEMENT!!!\n",
228
+ "\n",
229
+ " if tool_name == \"record_user_details\":\n",
230
+ " result = record_user_details(**arguments)\n",
231
+ " elif tool_name == \"record_unknown_question\":\n",
232
+ " result = record_unknown_question(**arguments)\n",
233
+ "\n",
234
+ " results.append(\n",
235
+ " {\n",
236
+ " \"role\": \"tool\",\n",
237
+ " \"content\": json.dumps(result),\n",
238
+ " \"tool_call_id\": tool_call.id,\n",
239
+ " }\n",
240
+ " )\n",
241
+ " return results"
242
+ ]
243
+ },
244
+ {
245
+ "cell_type": "code",
246
+ "execution_count": null,
247
+ "metadata": {},
248
+ "outputs": [],
249
+ "source": [
250
+ "globals()[\"record_unknown_question\"](\"this is a really hard question\")"
251
+ ]
252
+ },
253
+ {
254
+ "cell_type": "code",
255
+ "execution_count": null,
256
+ "metadata": {},
257
+ "outputs": [],
258
+ "source": [
259
+ "# This is a more elegant way that avoids the IF statement.\n",
260
+ "\n",
261
+ "\n",
262
+ "def handle_tool_calls(tool_calls):\n",
263
+ " results = []\n",
264
+ " for tool_call in tool_calls:\n",
265
+ " tool_name = tool_call.function.name\n",
266
+ " arguments = json.loads(tool_call.function.arguments)\n",
267
+ " print(f\"Tool called: {tool_name}\", flush=True)\n",
268
+ " tool = globals().get(tool_name)\n",
269
+ " result = tool(**arguments) if tool else {}\n",
270
+ " results.append(\n",
271
+ " {\n",
272
+ " \"role\": \"tool\",\n",
273
+ " \"content\": json.dumps(result),\n",
274
+ " \"tool_call_id\": tool_call.id,\n",
275
+ " }\n",
276
+ " )\n",
277
+ " return results"
278
+ ]
279
+ },
280
+ {
281
+ "cell_type": "code",
282
+ "execution_count": null,
283
+ "metadata": {},
284
+ "outputs": [],
285
+ "source": [
286
+ "reader = PdfReader(\"../me/linkedin.pdf\")\n",
287
+ "linkedin = \"\"\n",
288
+ "for page in reader.pages:\n",
289
+ " text = page.extract_text()\n",
290
+ " if text:\n",
291
+ " linkedin += text\n",
292
+ "\n",
293
+ "with open(\"../me/summary.txt\", \"r\", encoding=\"utf-8\") as f:\n",
294
+ " summary = f.read()\n",
295
+ "\n",
296
+ "name = \"Ed Donner\""
297
+ ]
298
+ },
299
+ {
300
+ "cell_type": "code",
301
+ "execution_count": null,
302
+ "metadata": {},
303
+ "outputs": [],
304
+ "source": [
305
+ "system_prompt = f\"You are acting as {name}. You are answering questions on {name}'s website, \\\n",
306
+ "particularly questions related to {name}'s career, background, skills and experience. \\\n",
307
+ "Your responsibility is to represent {name} for interactions on the website as faithfully as possible. \\\n",
308
+ "You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions. \\\n",
309
+ "Be professional and engaging, as if talking to a potential client or future employer who came across the website. \\\n",
310
+ "If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \\\n",
311
+ "If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \"\n",
312
+ "\n",
313
+ "system_prompt += f\"\\n\\n## Summary:\\n{summary}\\n\\n## LinkedIn Profile:\\n{linkedin}\\n\\n\"\n",
314
+ "system_prompt += f\"With this context, please chat with the user, always staying in character as {name}.\"\n"
315
+ ]
316
+ },
317
+ {
318
+ "cell_type": "code",
319
+ "execution_count": null,
320
+ "metadata": {},
321
+ "outputs": [],
322
+ "source": [
323
+ "def chat(message, history):\n",
324
+ " messages = (\n",
325
+ " [{\"role\": \"system\", \"content\": system_prompt}]\n",
326
+ " + history\n",
327
+ " + [{\"role\": \"user\", \"content\": message}]\n",
328
+ " )\n",
329
+ " done = False\n",
330
+ " while not done:\n",
331
+ " # This is the call to the LLM - see that we pass in the tools json\n",
332
+ "\n",
333
+ " response = openai.chat.completions.create(\n",
334
+ " model=\"gpt-4o-mini\", messages=messages, tools=tools\n",
335
+ " )\n",
336
+ "\n",
337
+ " finish_reason = response.choices[0].finish_reason\n",
338
+ "\n",
339
+ " # If the LLM wants to call a tool, we do that!\n",
340
+ "\n",
341
+ " if finish_reason == \"tool_calls\":\n",
342
+ " message = response.choices[0].message\n",
343
+ " tool_calls = message.tool_calls\n",
344
+ " results = handle_tool_calls(tool_calls)\n",
345
+ " messages.append(message)\n",
346
+ " messages.extend(results)\n",
347
+ " else:\n",
348
+ " done = True\n",
349
+ " return response.choices[0].message.content"
350
+ ]
351
+ },
352
+ {
353
+ "cell_type": "code",
354
+ "execution_count": null,
355
+ "metadata": {},
356
+ "outputs": [],
357
+ "source": [
358
+ "gr.ChatInterface(chat, type=\"messages\").launch(inbrowser=True)"
359
+ ]
360
+ },
361
+ {
362
+ "cell_type": "markdown",
363
+ "metadata": {},
364
+ "source": [
365
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
366
+ " <tr>\n",
367
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
368
+ " <img src=\"../../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
369
+ " </td>\n",
370
+ " <td>\n",
371
+ " <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
372
+ " <span style=\"color:#ff7800;\">• First and foremost, deploy this for yourself! It's a real, valuable tool - the future resume..<br/>\n",
373
+ " • Next, improve the resources - add better context about yourself. If you know RAG, then add a knowledge base about you.<br/>\n",
374
+ " • Add in more tools! You could have a SQL database with common Q&A that the LLM could read and write from?<br/>\n",
375
+ " • Bring in the Evaluator from the last lab, and add other Agentic patterns.\n",
376
+ " </span>\n",
377
+ " </td>\n",
378
+ " </tr>\n",
379
+ "</table>\n"
380
+ ]
381
+ },
382
+ {
383
+ "cell_type": "markdown",
384
+ "metadata": {},
385
+ "source": [
386
+ "<table style=\"margin: 0; text-align: left; width:100%\">\n",
387
+ " <tr>\n",
388
+ " <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
389
+ " <img src=\"../../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
390
+ " </td>\n",
391
+ " <td>\n",
392
+ " <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
393
+ " <span style=\"color:#00bfff;\">Aside from the obvious (your career alter-ego) this has business applications in any situation where you need an AI assistant with domain expertise and an ability to interact with the real world.\n",
394
+ " </span>\n",
395
+ " </td>\n",
396
+ " </tr>\n",
397
+ "</table>\n"
398
+ ]
399
+ }
400
+ ],
401
+ "metadata": {
402
+ "kernelspec": {
403
+ "display_name": ".venv",
404
+ "language": "python",
405
+ "name": "python3"
406
+ },
407
+ "language_info": {
408
+ "codemirror_mode": {
409
+ "name": "ipython",
410
+ "version": 3
411
+ },
412
+ "file_extension": ".py",
413
+ "mimetype": "text/x-python",
414
+ "name": "python",
415
+ "nbconvert_exporter": "python",
416
+ "pygments_lexer": "ipython3",
417
+ "version": "3.12.11"
418
+ }
419
+ },
420
+ "nbformat": 4,
421
+ "nbformat_minor": 2
422
+ }
community_contributions/Business_Idea.ipynb ADDED
@@ -0,0 +1,388 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "## Business idea generator and evaluator \n",
8
+ "\n"
9
+ ]
10
+ },
11
+ {
12
+ "cell_type": "code",
13
+ "execution_count": 1,
14
+ "metadata": {},
15
+ "outputs": [],
16
+ "source": [
17
+ "# Start with imports - ask ChatGPT to explain any package that you don't know\n",
18
+ "\n",
19
+ "import os\n",
20
+ "import json\n",
21
+ "from dotenv import load_dotenv\n",
22
+ "from openai import OpenAI\n",
23
+ "from anthropic import Anthropic\n",
24
+ "from IPython.display import Markdown, display"
25
+ ]
26
+ },
27
+ {
28
+ "cell_type": "code",
29
+ "execution_count": null,
30
+ "metadata": {},
31
+ "outputs": [],
32
+ "source": [
33
+ "# Always remember to do this!\n",
34
+ "load_dotenv(override=True)"
35
+ ]
36
+ },
37
+ {
38
+ "cell_type": "code",
39
+ "execution_count": null,
40
+ "metadata": {},
41
+ "outputs": [],
42
+ "source": [
43
+ "# Print the key prefixes to help with any debugging\n",
44
+ "\n",
45
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
46
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
47
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
48
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
49
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
50
+ "\n",
51
+ "if openai_api_key:\n",
52
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
53
+ "else:\n",
54
+ " print(\"OpenAI API Key not set\")\n",
55
+ " \n",
56
+ "if anthropic_api_key:\n",
57
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
58
+ "else:\n",
59
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
60
+ "\n",
61
+ "if google_api_key:\n",
62
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
63
+ "else:\n",
64
+ " print(\"Google API Key not set (and this is optional)\")\n",
65
+ "\n",
66
+ "if deepseek_api_key:\n",
67
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
68
+ "else:\n",
69
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
70
+ "\n",
71
+ "if groq_api_key:\n",
72
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
73
+ "else:\n",
74
+ " print(\"Groq API Key not set (and this is optional)\")"
75
+ ]
76
+ },
77
+ {
78
+ "cell_type": "code",
79
+ "execution_count": 4,
80
+ "metadata": {},
81
+ "outputs": [],
82
+ "source": [
83
+ "request = (\n",
84
+ " \"Please generate three innovative business ideas aligned with the latest global trends. \"\n",
85
+ " \"For each idea, include a brief description (2–3 sentences).\"\n",
86
+ ")\n",
87
+ "messages = [{\"role\": \"user\", \"content\": request}]"
88
+ ]
89
+ },
90
+ {
91
+ "cell_type": "code",
92
+ "execution_count": null,
93
+ "metadata": {},
94
+ "outputs": [],
95
+ "source": [
96
+ "messages"
97
+ ]
98
+ },
99
+ {
100
+ "cell_type": "code",
101
+ "execution_count": null,
102
+ "metadata": {},
103
+ "outputs": [],
104
+ "source": [
105
+ "\n",
106
+ "openai = OpenAI()\n",
107
+ "'''\n",
108
+ "response = openai.chat.completions.create(\n",
109
+ " model=\"gpt-4o-mini\",\n",
110
+ " messages=messages,\n",
111
+ ")\n",
112
+ "question = response.choices[0].message.content\n",
113
+ "print(question)\n",
114
+ "'''"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "code",
119
+ "execution_count": 9,
120
+ "metadata": {},
121
+ "outputs": [],
122
+ "source": [
123
+ "competitors = []\n",
124
+ "answers = []\n",
125
+ "#messages = [{\"role\": \"user\", \"content\": question}]"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": null,
131
+ "metadata": {},
132
+ "outputs": [],
133
+ "source": [
134
+ "# The API we know well\n",
135
+ "\n",
136
+ "model_name = \"gpt-4o-mini\"\n",
137
+ "\n",
138
+ "response = openai.chat.completions.create(model=model_name, messages=messages)\n",
139
+ "answer = response.choices[0].message.content\n",
140
+ "\n",
141
+ "display(Markdown(answer))\n",
142
+ "competitors.append(model_name)\n",
143
+ "answers.append(answer)"
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": null,
149
+ "metadata": {},
150
+ "outputs": [],
151
+ "source": [
152
+ "# Anthropic has a slightly different API, and Max Tokens is required\n",
153
+ "\n",
154
+ "model_name = \"claude-3-7-sonnet-latest\"\n",
155
+ "\n",
156
+ "claude = Anthropic()\n",
157
+ "response = claude.messages.create(model=model_name, messages=messages, max_tokens=1000)\n",
158
+ "answer = response.content[0].text\n",
159
+ "\n",
160
+ "display(Markdown(answer))\n",
161
+ "competitors.append(model_name)\n",
162
+ "answers.append(answer)"
163
+ ]
164
+ },
165
+ {
166
+ "cell_type": "code",
167
+ "execution_count": null,
168
+ "metadata": {},
169
+ "outputs": [],
170
+ "source": [
171
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
172
+ "model_name = \"gemini-2.0-flash\"\n",
173
+ "\n",
174
+ "response = gemini.chat.completions.create(model=model_name, messages=messages)\n",
175
+ "answer = response.choices[0].message.content\n",
176
+ "\n",
177
+ "display(Markdown(answer))\n",
178
+ "competitors.append(model_name)\n",
179
+ "answers.append(answer)"
180
+ ]
181
+ },
182
+ {
183
+ "cell_type": "code",
184
+ "execution_count": null,
185
+ "metadata": {},
186
+ "outputs": [],
187
+ "source": [
188
+ "deepseek = OpenAI(api_key=deepseek_api_key, base_url=\"https://api.deepseek.com/v1\")\n",
189
+ "model_name = \"deepseek-chat\"\n",
190
+ "\n",
191
+ "response = deepseek.chat.completions.create(model=model_name, messages=messages)\n",
192
+ "answer = response.choices[0].message.content\n",
193
+ "\n",
194
+ "display(Markdown(answer))\n",
195
+ "competitors.append(model_name)\n",
196
+ "answers.append(answer)"
197
+ ]
198
+ },
199
+ {
200
+ "cell_type": "code",
201
+ "execution_count": null,
202
+ "metadata": {},
203
+ "outputs": [],
204
+ "source": [
205
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
206
+ "model_name = \"llama-3.3-70b-versatile\"\n",
207
+ "\n",
208
+ "response = groq.chat.completions.create(model=model_name, messages=messages)\n",
209
+ "answer = response.choices[0].message.content\n",
210
+ "\n",
211
+ "display(Markdown(answer))\n",
212
+ "competitors.append(model_name)\n",
213
+ "answers.append(answer)\n"
214
+ ]
215
+ },
216
+ {
217
+ "cell_type": "code",
218
+ "execution_count": null,
219
+ "metadata": {},
220
+ "outputs": [],
221
+ "source": [
222
+ "!ollama pull llama3.2"
223
+ ]
224
+ },
225
+ {
226
+ "cell_type": "code",
227
+ "execution_count": null,
228
+ "metadata": {},
229
+ "outputs": [],
230
+ "source": [
231
+ "ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
232
+ "model_name = \"llama3.2\"\n",
233
+ "\n",
234
+ "response = ollama.chat.completions.create(model=model_name, messages=messages)\n",
235
+ "answer = response.choices[0].message.content\n",
236
+ "\n",
237
+ "display(Markdown(answer))\n",
238
+ "competitors.append(model_name)\n",
239
+ "answers.append(answer)"
240
+ ]
241
+ },
242
+ {
243
+ "cell_type": "code",
244
+ "execution_count": null,
245
+ "metadata": {},
246
+ "outputs": [],
247
+ "source": [
248
+ "# So where are we?\n",
249
+ "\n",
250
+ "print(competitors)\n",
251
+ "print(answers)\n"
252
+ ]
253
+ },
254
+ {
255
+ "cell_type": "code",
256
+ "execution_count": null,
257
+ "metadata": {},
258
+ "outputs": [],
259
+ "source": [
260
+ "# It's nice to know how to use \"zip\"\n",
261
+ "for competitor, answer in zip(competitors, answers):\n",
262
+ " print(f\"Competitor: {competitor}\\n\\n{answer}\")\n"
263
+ ]
264
+ },
265
+ {
266
+ "cell_type": "code",
267
+ "execution_count": 14,
268
+ "metadata": {},
269
+ "outputs": [],
270
+ "source": [
271
+ "# Let's bring this together - note the use of \"enumerate\"\n",
272
+ "\n",
273
+ "together = \"\"\n",
274
+ "for index, answer in enumerate(answers):\n",
275
+ " together += f\"# Response from competitor {index+1}\\n\\n\"\n",
276
+ " together += answer + \"\\n\\n\""
277
+ ]
278
+ },
279
+ {
280
+ "cell_type": "code",
281
+ "execution_count": null,
282
+ "metadata": {},
283
+ "outputs": [],
284
+ "source": [
285
+ "print(together)"
286
+ ]
287
+ },
288
+ {
289
+ "cell_type": "code",
290
+ "execution_count": null,
291
+ "metadata": {},
292
+ "outputs": [],
293
+ "source": [
294
+ "judge = f\"\"\"You are judging a competition between {len(competitors)} competitors.\n",
295
+ "Each model was asked to generate three innovative business ideas aligned with the latest global trends.\n",
296
+ "\n",
297
+ "Your job is to evaluate the likelihood of success for each idea on a scale from 0 to 100 percent. For each competitor, list the three percentages in the same order as their ideas.\n",
298
+ "\n",
299
+ "Respond only with JSON in this format:\n",
300
+ "{{\"results\": [\n",
301
+ " {{\"competitor\": 1, \"success_chances\": [perc1, perc2, perc3]}},\n",
302
+ " {{\"competitor\": 2, \"success_chances\": [perc1, perc2, perc3]}},\n",
303
+ " ...\n",
304
+ "]}}\n",
305
+ "\n",
306
+ "Here are the ideas from each competitor:\n",
307
+ "\n",
308
+ "{together}\n",
309
+ "\n",
310
+ "Now respond with only the JSON, nothing else.\"\"\"\n"
311
+ ]
312
+ },
313
+ {
314
+ "cell_type": "code",
315
+ "execution_count": null,
316
+ "metadata": {},
317
+ "outputs": [],
318
+ "source": [
319
+ "print(judge)"
320
+ ]
321
+ },
322
+ {
323
+ "cell_type": "code",
324
+ "execution_count": 18,
325
+ "metadata": {},
326
+ "outputs": [],
327
+ "source": [
328
+ "judge_messages = [{\"role\": \"user\", \"content\": judge}]"
329
+ ]
330
+ },
331
+ {
332
+ "cell_type": "code",
333
+ "execution_count": null,
334
+ "metadata": {},
335
+ "outputs": [],
336
+ "source": [
337
+ "# Judgement time!\n",
338
+ "\n",
339
+ "openai = OpenAI()\n",
340
+ "response = openai.chat.completions.create(\n",
341
+ " model=\"o3-mini\",\n",
342
+ " messages=judge_messages,\n",
343
+ ")\n",
344
+ "results = response.choices[0].message.content\n",
345
+ "print(results)\n"
346
+ ]
347
+ },
348
+ {
349
+ "cell_type": "code",
350
+ "execution_count": null,
351
+ "metadata": {},
352
+ "outputs": [],
353
+ "source": [
354
+ "# Parse judge results JSON and display success probabilities\n",
355
+ "results_dict = json.loads(results)\n",
356
+ "for entry in results_dict[\"results\"]:\n",
357
+ " comp_num = entry[\"competitor\"]\n",
358
+ " comp_name = competitors[comp_num - 1]\n",
359
+ " chances = entry[\"success_chances\"]\n",
360
+ " print(f\"{comp_name}:\")\n",
361
+ " for idx, perc in enumerate(chances, start=1):\n",
362
+ " print(f\" Idea {idx}: {perc}% chance of success\")\n",
363
+ " print()\n"
364
+ ]
365
+ }
366
+ ],
367
+ "metadata": {
368
+ "kernelspec": {
369
+ "display_name": ".venv",
370
+ "language": "python",
371
+ "name": "python3"
372
+ },
373
+ "language_info": {
374
+ "codemirror_mode": {
375
+ "name": "ipython",
376
+ "version": 3
377
+ },
378
+ "file_extension": ".py",
379
+ "mimetype": "text/x-python",
380
+ "name": "python",
381
+ "nbconvert_exporter": "python",
382
+ "pygments_lexer": "ipython3",
383
+ "version": "3.12.7"
384
+ }
385
+ },
386
+ "nbformat": 4,
387
+ "nbformat_minor": 2
388
+ }
community_contributions/Multi-Model-Resume–JD-Match-Analyzer/.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ .env
community_contributions/Multi-Model-Resume/342/200/223JD-Match-Analyzer/AnalyzeResume.png ADDED
community_contributions/Multi-Model-Resume–JD-Match-Analyzer/README.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🧠 Resume-Job Match Application (LLM-Powered)
2
+
3
+ ![AnalyseResume](AnalyzeResume.png)
4
+
5
+ This is a **Streamlit-based web app** that evaluates how well a resume matches a job description using powerful Large Language Models (LLMs) such as:
6
+
7
+ - OpenAI GPT
8
+ - Anthropic Claude
9
+ - Google Gemini (Generative AI)
10
+ - Groq LLM
11
+ - DeepSeek LLM
12
+
13
+ The app takes a resume and job description as input files, sends them to these LLMs, and returns:
14
+
15
+ - ✅ Match percentage from each model
16
+ - 📊 A ranked table sorted by match %
17
+ - 📈 Average match percentage
18
+ - 🧠 Simple, responsive UI for instant feedback
19
+
20
+ ## 📂 Features
21
+
22
+ - Upload **any file type** for resume and job description (PDF, DOCX, TXT, etc.)
23
+ - Automatic extraction and cleaning of text
24
+ - Match results across multiple models in real time
25
+ - Table view with clean formatting
26
+ - Uses `.env` file for secure API key management
27
+
28
+ ## 🔐 Environment Setup (`.env`)
29
+
30
+ Create a `.env` file in the project root and add the following API keys:
31
+
32
+ ```env
33
+ OPENAI_API_KEY=your-openai-api-key
34
+ ANTHROPIC_API_KEY=your-anthropic-api-key
35
+ GOOGLE_API_KEY=your-google-api-key
36
+ GROQ_API_KEY=your-groq-api-key
37
+ DEEPSEEK_API_KEY=your-deepseek-api-key
38
+ ```
39
+
40
+ ## ▶️ Running the App
41
+ ### Launch the app using Streamlit:
42
+
43
+ streamlit run resume_agent.py
44
+
45
+ ### The app will open in your browser at:
46
+ 📍 http://localhost:8501
47
+
48
+
community_contributions/Multi-Model-Resume–JD-Match-Analyzer/multi_file_ingestion.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from langchain.document_loaders import (
3
+ TextLoader,
4
+ PyPDFLoader,
5
+ UnstructuredWordDocumentLoader,
6
+ UnstructuredFileLoader
7
+ )
8
+
9
+
10
+
11
+ def load_and_split_resume(file_path: str):
12
+ """
13
+ Loads a resume file and splits it into text chunks using LangChain.
14
+
15
+ Args:
16
+ file_path (str): Path to the resume file (.txt, .pdf, .docx, etc.)
17
+ chunk_size (int): Maximum characters per chunk.
18
+ chunk_overlap (int): Overlap between chunks to preserve context.
19
+
20
+ Returns:
21
+ List[str]: List of split text chunks.
22
+ """
23
+ if not os.path.exists(file_path):
24
+ raise FileNotFoundError(f"File not found: {file_path}")
25
+
26
+ ext = os.path.splitext(file_path)[1].lower()
27
+
28
+ # Select the appropriate loader
29
+ if ext == ".txt":
30
+ loader = TextLoader(file_path, encoding="utf-8")
31
+ elif ext == ".pdf":
32
+ loader = PyPDFLoader(file_path)
33
+ elif ext in [".docx", ".doc"]:
34
+ loader = UnstructuredWordDocumentLoader(file_path)
35
+ else:
36
+ # Fallback for other common formats
37
+ loader = UnstructuredFileLoader(file_path)
38
+
39
+ # Load the file as LangChain documents
40
+ documents = loader.load()
41
+
42
+
43
+ return documents
44
+ # return [doc.page_content for doc in split_docs]
community_contributions/Multi-Model-Resume–JD-Match-Analyzer/resume_agent.py ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import os
3
+ from openai import OpenAI
4
+ from anthropic import Anthropic
5
+ import pdfplumber
6
+ from io import StringIO
7
+ from dotenv import load_dotenv
8
+ import pandas as pd
9
+ from multi_file_ingestion import load_and_split_resume
10
+
11
+ # Load environment variables
12
+ load_dotenv(override=True)
13
+ openai_api_key = os.getenv("OPENAI_API_KEY")
14
+ anthropic_api_key = os.getenv("ANTHROPIC_API_KEY")
15
+ google_api_key = os.getenv("GOOGLE_API_KEY")
16
+ groq_api_key = os.getenv("GROQ_API_KEY")
17
+ deepseek_api_key = os.getenv("DEEPSEEK_API_KEY")
18
+
19
+ openai = OpenAI()
20
+
21
+ # Streamlit UI
22
+ st.set_page_config(page_title="LLM Resume–JD Fit", layout="wide")
23
+ st.title("🧠 Multi-Model Resume–JD Match Analyzer")
24
+
25
+ # Inject custom CSS to reduce white space
26
+ st.markdown("""
27
+ <style>
28
+ .block-container {
29
+ padding-top: 3rem; /* instead of 1rem */
30
+ padding-bottom: 1rem;
31
+ }
32
+ .stMarkdown {
33
+ margin-bottom: 0.5rem;
34
+ }
35
+ .logo-container img {
36
+ width: 50px;
37
+ height: auto;
38
+ margin-right: 10px;
39
+ }
40
+ .header-row {
41
+ display: flex;
42
+ align-items: center;
43
+ gap: 1rem;
44
+ margin-top: 1rem; /* Add extra top margin here if needed */
45
+ }
46
+ </style>
47
+ """, unsafe_allow_html=True)
48
+
49
+ # File upload
50
+ resume_file = st.file_uploader("📄 Upload Resume (any file type)", type=None)
51
+ jd_file = st.file_uploader("📝 Upload Job Description (any file type)", type=None)
52
+
53
+ # Function to extract text from uploaded files
54
+ def extract_text(file):
55
+ if file.name.endswith(".pdf"):
56
+ with pdfplumber.open(file) as pdf:
57
+ return "\n".join([page.extract_text() for page in pdf.pages if page.extract_text()])
58
+ else:
59
+ return StringIO(file.read().decode("utf-8")).read()
60
+
61
+
62
+ def extract_candidate_name(resume_text):
63
+ prompt = f"""
64
+ You are an AI assistant specialized in resume analysis.
65
+
66
+ Your task is to get full name of the candidate from the resume.
67
+
68
+ Resume:
69
+ {resume_text}
70
+
71
+ Respond with only the candidate's full name.
72
+ """
73
+ try:
74
+ response = openai.chat.completions.create(
75
+ model="gpt-4o-mini",
76
+ messages=[
77
+ {"role": "system", "content": "You are a professional resume evaluator."},
78
+ {"role": "user", "content": prompt}
79
+ ]
80
+ )
81
+ content = response.choices[0].message.content
82
+
83
+ return content.strip()
84
+
85
+ except Exception as e:
86
+ return "Unknown"
87
+
88
+
89
+ # Function to build the prompt for LLMs
90
+ def build_prompt(resume_text, jd_text):
91
+ prompt = f"""
92
+ You are an AI assistant specialized in resume analysis and recruitment. Analyze the given resume and compare it with the job description.
93
+
94
+ Your task is to evaluate how well the resume aligns with the job description.
95
+
96
+
97
+ Provide a match percentage between 0 and 100, where 100 indicates a perfect fit.
98
+
99
+ Resume:
100
+ {resume_text}
101
+
102
+ Job Description:
103
+ {jd_text}
104
+
105
+ Respond with only the match percentage as an integer.
106
+ """
107
+ return prompt.strip()
108
+
109
+ # Function to get match percentage from OpenAI GPT-4
110
+ def get_openai_match(prompt):
111
+ try:
112
+ response = openai.chat.completions.create(
113
+ model="gpt-4o-mini",
114
+ messages=[
115
+ {"role": "system", "content": "You are a professional resume evaluator."},
116
+ {"role": "user", "content": prompt}
117
+ ]
118
+ )
119
+ content = response.choices[0].message.content
120
+ digits = ''.join(filter(str.isdigit, content))
121
+ return min(int(digits), 100) if digits else 0
122
+ except Exception as e:
123
+ st.error(f"OpenAI API Error: {e}")
124
+ return 0
125
+
126
+ # Function to get match percentage from Anthropic Claude
127
+ def get_anthropic_match(prompt):
128
+ try:
129
+ model_name = "claude-3-7-sonnet-latest"
130
+ claude = Anthropic()
131
+
132
+ message = claude.messages.create(
133
+ model=model_name,
134
+ max_tokens=100,
135
+ messages=[
136
+ {"role": "user", "content": prompt}
137
+ ]
138
+ )
139
+ content = message.content[0].text
140
+ digits = ''.join(filter(str.isdigit, content))
141
+ return min(int(digits), 100) if digits else 0
142
+ except Exception as e:
143
+ st.error(f"Anthropic API Error: {e}")
144
+ return 0
145
+
146
+ # Function to get match percentage from Google Gemini
147
+ def get_google_match(prompt):
148
+ try:
149
+ gemini = OpenAI(api_key=google_api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
150
+ model_name = "gemini-2.0-flash"
151
+ messages = [{"role": "user", "content": prompt}]
152
+ response = gemini.chat.completions.create(model=model_name, messages=messages)
153
+ content = response.choices[0].message.content
154
+ digits = ''.join(filter(str.isdigit, content))
155
+ return min(int(digits), 100) if digits else 0
156
+ except Exception as e:
157
+ st.error(f"Google Gemini API Error: {e}")
158
+ return 0
159
+
160
+ # Function to get match percentage from Groq
161
+ def get_groq_match(prompt):
162
+ try:
163
+ groq = OpenAI(api_key=groq_api_key, base_url="https://api.groq.com/openai/v1")
164
+ model_name = "llama-3.3-70b-versatile"
165
+ messages = [{"role": "user", "content": prompt}]
166
+ response = groq.chat.completions.create(model=model_name, messages=messages)
167
+ answer = response.choices[0].message.content
168
+ digits = ''.join(filter(str.isdigit, answer))
169
+ return min(int(digits), 100) if digits else 0
170
+ except Exception as e:
171
+ st.error(f"Groq API Error: {e}")
172
+ return 0
173
+
174
+ # Function to get match percentage from DeepSeek
175
+ def get_deepseek_match(prompt):
176
+ try:
177
+ deepseek = OpenAI(api_key=deepseek_api_key, base_url="https://api.deepseek.com/v1")
178
+ model_name = "deepseek-chat"
179
+ messages = [{"role": "user", "content": prompt}]
180
+ response = deepseek.chat.completions.create(model=model_name, messages=messages)
181
+ answer = response.choices[0].message.content
182
+ digits = ''.join(filter(str.isdigit, answer))
183
+ return min(int(digits), 100) if digits else 0
184
+ except Exception as e:
185
+ st.error(f"DeepSeek API Error: {e}")
186
+ return 0
187
+
188
+ # Main action
189
+ if st.button("🔍 Analyze Resume Fit"):
190
+ if resume_file and jd_file:
191
+ with st.spinner("Analyzing..."):
192
+ # resume_text = extract_text(resume_file)
193
+ # jd_text = extract_text(jd_file)
194
+ os.makedirs("temp_files", exist_ok=True)
195
+ resume_path = os.path.join("temp_files", resume_file.name)
196
+
197
+ with open(resume_path, "wb") as f:
198
+ f.write(resume_file.getbuffer())
199
+ resume_docs = load_and_split_resume(resume_path)
200
+ resume_text = "\n".join([doc.page_content for doc in resume_docs])
201
+
202
+ jd_path = os.path.join("temp_files", jd_file.name)
203
+ with open(jd_path, "wb") as f:
204
+ f.write(jd_file.getbuffer())
205
+ jd_docs = load_and_split_resume(jd_path)
206
+ jd_text = "\n".join([doc.page_content for doc in jd_docs])
207
+
208
+ candidate_name = extract_candidate_name(resume_text)
209
+ prompt = build_prompt(resume_text, jd_text)
210
+
211
+ # Get match percentages from all models
212
+ scores = {
213
+ "OpenAI GPT-4o Mini": get_openai_match(prompt),
214
+ "Anthropic Claude": get_anthropic_match(prompt),
215
+ "Google Gemini": get_google_match(prompt),
216
+ "Groq": get_groq_match(prompt),
217
+ "DeepSeek": get_deepseek_match(prompt),
218
+ }
219
+
220
+ # Calculate average score
221
+ average_score = round(sum(scores.values()) / len(scores), 2)
222
+
223
+ # Sort scores in descending order
224
+ sorted_scores = sorted(scores.items(), reverse=False)
225
+
226
+ # Display results
227
+ st.success("✅ Analysis Complete")
228
+ st.subheader("📊 Match Results (Ranked by Model)")
229
+
230
+ # Show candidate name
231
+ st.markdown(f"**👤 Candidate:** {candidate_name}")
232
+
233
+ # Create and sort dataframe
234
+ df = pd.DataFrame(sorted_scores, columns=["Model", "% Match"])
235
+ df = df.sort_values("% Match", ascending=False).reset_index(drop=True)
236
+
237
+ # Convert to HTML table
238
+ def render_custom_table(dataframe):
239
+ table_html = "<table style='border-collapse: collapse; width: auto;'>"
240
+ # Table header
241
+ table_html += "<thead><tr>"
242
+ for col in dataframe.columns:
243
+ table_html += f"<th style='text-align: center; padding: 8px; border-bottom: 1px solid #ddd;'>{col}</th>"
244
+ table_html += "</tr></thead>"
245
+
246
+ # Table rows
247
+ table_html += "<tbody>"
248
+ for _, row in dataframe.iterrows():
249
+ table_html += "<tr>"
250
+ for val in row:
251
+ table_html += f"<td style='text-align: left; padding: 8px; border-bottom: 1px solid #eee;'>{val}</td>"
252
+ table_html += "</tr>"
253
+ table_html += "</tbody></table>"
254
+ return table_html
255
+
256
+ # Display table
257
+ st.markdown(render_custom_table(df), unsafe_allow_html=True)
258
+
259
+ # Show average match
260
+ st.metric(label="📈 Average Match %", value=f"{average_score:.2f}%")
261
+ else:
262
+ st.warning("Please upload both resume and job description.")
community_contributions/app_rate_limiter_mailgun_integration.py ADDED
@@ -0,0 +1,231 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dotenv import load_dotenv
2
+ from openai import OpenAI
3
+ import json
4
+ import os
5
+ import requests
6
+ from pypdf import PdfReader
7
+ import gradio as gr
8
+ import base64
9
+ import time
10
+ from collections import defaultdict
11
+ import fastapi
12
+ from gradio.context import Context
13
+ import logging
14
+
15
+ logger = logging.getLogger(__name__)
16
+ logger.setLevel(logging.DEBUG)
17
+
18
+
19
+ load_dotenv(override=True)
20
+
21
+ class RateLimiter:
22
+ def __init__(self, max_requests=5, time_window=5):
23
+ # max_requests per time_window seconds
24
+ self.max_requests = max_requests
25
+ self.time_window = time_window # in seconds
26
+ self.request_history = defaultdict(list)
27
+
28
+ def is_rate_limited(self, user_id):
29
+ current_time = time.time()
30
+ # Remove old requests
31
+ self.request_history[user_id] = [
32
+ timestamp for timestamp in self.request_history[user_id]
33
+ if current_time - timestamp < self.time_window
34
+ ]
35
+
36
+ # Check if user has exceeded the limit
37
+ if len(self.request_history[user_id]) >= self.max_requests:
38
+ return True
39
+
40
+ # Add current request
41
+ self.request_history[user_id].append(current_time)
42
+ return False
43
+
44
+ def push(text):
45
+ requests.post(
46
+ "https://api.pushover.net/1/messages.json",
47
+ data={
48
+ "token": os.getenv("PUSHOVER_TOKEN"),
49
+ "user": os.getenv("PUSHOVER_USER"),
50
+ "message": text,
51
+ }
52
+ )
53
+
54
+ def send_email(from_email, name, notes):
55
+ auth = base64.b64encode(f'api:{os.getenv("MAILGUN_API_KEY")}'.encode()).decode()
56
+
57
+ response = requests.post(
58
+ f'https://api.mailgun.net/v3/{os.getenv("MAILGUN_DOMAIN")}/messages',
59
+ headers={
60
+ 'Authorization': f'Basic {auth}'
61
+ },
62
+ data={
63
+ 'from': f'Website Contact <mailgun@{os.getenv("MAILGUN_DOMAIN")}>',
64
+ 'to': os.getenv("MAILGUN_RECIPIENT"),
65
+ 'subject': f'New message from {from_email}',
66
+ 'text': f'Name: {name}\nEmail: {from_email}\nNotes: {notes}',
67
+ 'h:Reply-To': from_email
68
+ }
69
+ )
70
+
71
+ return response.status_code == 200
72
+
73
+
74
+ def record_user_details(email, name="Name not provided", notes="not provided"):
75
+ push(f"Recording {name} with email {email} and notes {notes}")
76
+ # Send email notification
77
+ email_sent = send_email(email, name, notes)
78
+ return {"recorded": "ok", "email_sent": email_sent}
79
+
80
+ def record_unknown_question(question):
81
+ push(f"Recording {question}")
82
+ return {"recorded": "ok"}
83
+
84
+ record_user_details_json = {
85
+ "name": "record_user_details",
86
+ "description": "Use this tool to record that a user is interested in being in touch and provided an email address",
87
+ "parameters": {
88
+ "type": "object",
89
+ "properties": {
90
+ "email": {
91
+ "type": "string",
92
+ "description": "The email address of this user"
93
+ },
94
+ "name": {
95
+ "type": "string",
96
+ "description": "The user's name, if they provided it"
97
+ }
98
+ ,
99
+ "notes": {
100
+ "type": "string",
101
+ "description": "Any additional information about the conversation that's worth recording to give context"
102
+ }
103
+ },
104
+ "required": ["email"],
105
+ "additionalProperties": False
106
+ }
107
+ }
108
+
109
+ record_unknown_question_json = {
110
+ "name": "record_unknown_question",
111
+ "description": "Always use this tool to record any question that couldn't be answered as you didn't know the answer",
112
+ "parameters": {
113
+ "type": "object",
114
+ "properties": {
115
+ "question": {
116
+ "type": "string",
117
+ "description": "The question that couldn't be answered"
118
+ },
119
+ },
120
+ "required": ["question"],
121
+ "additionalProperties": False
122
+ }
123
+ }
124
+
125
+ tools = [{"type": "function", "function": record_user_details_json},
126
+ {"type": "function", "function": record_unknown_question_json}]
127
+
128
+
129
+ class Me:
130
+
131
+ def __init__(self):
132
+ self.openai = OpenAI(api_key=os.getenv("GOOGLE_API_KEY"), base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
133
+ self.name = "Sagarnil Das"
134
+ self.rate_limiter = RateLimiter(max_requests=5, time_window=60) # 5 messages per minute
135
+ reader = PdfReader("me/linkedin.pdf")
136
+ self.linkedin = ""
137
+ for page in reader.pages:
138
+ text = page.extract_text()
139
+ if text:
140
+ self.linkedin += text
141
+ with open("me/summary.txt", "r", encoding="utf-8") as f:
142
+ self.summary = f.read()
143
+
144
+
145
+ def handle_tool_call(self, tool_calls):
146
+ results = []
147
+ for tool_call in tool_calls:
148
+ tool_name = tool_call.function.name
149
+ arguments = json.loads(tool_call.function.arguments)
150
+ print(f"Tool called: {tool_name}", flush=True)
151
+ tool = globals().get(tool_name)
152
+ result = tool(**arguments) if tool else {}
153
+ results.append({"role": "tool","content": json.dumps(result),"tool_call_id": tool_call.id})
154
+ return results
155
+
156
+ def system_prompt(self):
157
+ system_prompt = f"You are acting as {self.name}. You are answering questions on {self.name}'s website, \
158
+ particularly questions related to {self.name}'s career, background, skills and experience. \
159
+ Your responsibility is to represent {self.name} for interactions on the website as faithfully as possible. \
160
+ You are given a summary of {self.name}'s background and LinkedIn profile which you can use to answer questions. \
161
+ Be professional and engaging, as if talking to a potential client or future employer who came across the website. \
162
+ If you don't know the answer to any question, use your record_unknown_question tool to record the question that you couldn't answer, even if it's about something trivial or unrelated to career. \
163
+ If the user is engaging in discussion, try to steer them towards getting in touch via email; ask for their email and record it using your record_user_details tool. \
164
+ When a user provides their email, both a push notification and an email notification will be sent. If the user does not provide any note in the message \
165
+ in which they provide their email, then give a summary of the conversation so far as the notes."
166
+
167
+ system_prompt += f"\n\n## Summary:\n{self.summary}\n\n## LinkedIn Profile:\n{self.linkedin}\n\n"
168
+ system_prompt += f"With this context, please chat with the user, always staying in character as {self.name}."
169
+ return system_prompt
170
+
171
+ def chat(self, message, history):
172
+ # Get the client IP from Gradio's request context
173
+ try:
174
+ # Try to get the real client IP from request headers
175
+ request = Context.get_context().request
176
+ # Check for X-Forwarded-For header (common in reverse proxies like HF Spaces)
177
+ forwarded_for = request.headers.get("X-Forwarded-For")
178
+ # Check for Cf-Connecting-IP header (Cloudflare)
179
+ cloudflare_ip = request.headers.get("Cf-Connecting-IP")
180
+
181
+ if forwarded_for:
182
+ # X-Forwarded-For contains a comma-separated list of IPs, the first one is the client
183
+ user_id = forwarded_for.split(",")[0].strip()
184
+ elif cloudflare_ip:
185
+ user_id = cloudflare_ip
186
+ else:
187
+ # Fall back to direct client address
188
+ user_id = request.client.host
189
+ except (AttributeError, RuntimeError, fastapi.exceptions.FastAPIError):
190
+ # Fallback if we can't get context or if running outside of FastAPI
191
+ user_id = "default_user"
192
+ logger.debug(f"User ID: {user_id}")
193
+ if self.rate_limiter.is_rate_limited(user_id):
194
+ return "You're sending messages too quickly. Please wait a moment before sending another message."
195
+
196
+ messages = [{"role": "system", "content": self.system_prompt()}]
197
+
198
+ # Check if history is a list of dicts (Gradio "messages" format)
199
+ if isinstance(history, list) and all(isinstance(h, dict) for h in history):
200
+ messages.extend(history)
201
+ else:
202
+ # Assume it's a list of [user_msg, assistant_msg] pairs
203
+ for user_msg, assistant_msg in history:
204
+ messages.append({"role": "user", "content": user_msg})
205
+ messages.append({"role": "assistant", "content": assistant_msg})
206
+
207
+ messages.append({"role": "user", "content": message})
208
+
209
+ done = False
210
+ while not done:
211
+ response = self.openai.chat.completions.create(
212
+ model="gemini-2.0-flash",
213
+ messages=messages,
214
+ tools=tools
215
+ )
216
+ if response.choices[0].finish_reason == "tool_calls":
217
+ tool_calls = response.choices[0].message.tool_calls
218
+ tool_result = self.handle_tool_call(tool_calls)
219
+ messages.append(response.choices[0].message)
220
+ messages.extend(tool_result)
221
+ else:
222
+ done = True
223
+
224
+ return response.choices[0].message.content
225
+
226
+
227
+
228
+ if __name__ == "__main__":
229
+ me = Me()
230
+ gr.ChatInterface(me.chat, type="messages").launch()
231
+
community_contributions/chatbot_rag_evaluation/.gitignore ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.pyc
3
+ .env
4
+ *.env
5
+ .venv/
6
+ google_credentials.json
7
+ user_interest.csv
8
+ *.db
9
+ *.sqlite3
10
+ *.log
11
+ .DS_Store
12
+ career_db/
13
+ .career_db/
community_contributions/chatbot_rag_evaluation/README.md ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # RAG Chat Evaluator Bot
2
+
3
+ A lightweight chatbot app that uses LangChain RAG for chunk retrieval, OpenAI for generation, and Gemini for response evaluation.
4
+
5
+ ## 🔧 Features
6
+
7
+ - 📚 Retrieval-Augmented Generation (RAG) with LangChain + ChromaDB
8
+ - 🤖 Chat interface powered by OpenAI's GPT
9
+ - ✅ Gemini-based evaluator checks tone + accuracy
10
+ - 🛠️ Records user emails to Google Sheets or CSV fallback
11
+
12
+
13
+ ## 🚀 Setup
14
+
15
+ 1. Clone the repo:
16
+
17
+ ```bash
18
+ git clone https://github.com/your-username/rag-chat-evaluator-bot.git
19
+ cd career-chats
20
+ ```
21
+
22
+ 2. Create a virtual environment:
23
+
24
+ ```bash
25
+ python -m venv venv
26
+ source venv/bin/activate # On Windows: venv\Scripts\activate
27
+ ```
28
+
29
+ 3. Install dependencies:
30
+
31
+ ```bash
32
+ install -r requirements.txt
33
+ ```
34
+
35
+ 2. Keys in `.env` file:
36
+ ```
37
+ GOOGLE_API_KEY=<your-api-key>
38
+ OPENAI_API_KEY=<your-api-key>
39
+ GOOGLE_CREDENTIALS_JSON=<b64encoded-json>
40
+ ```
41
+
42
+
community_contributions/chatbot_rag_evaluation/app.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from controller import ChatbotController
3
+
4
+
5
+ controller = ChatbotController()
6
+ with gr.Blocks() as demo:
7
+ chat = gr.Chatbot(type="messages", min_height=600, label="Assistant")
8
+ msg = gr.Textbox(label="Your message", placeholder="Want to know more about Damla’s work? Type your question here...")
9
+
10
+ history_state = gr.State([])
11
+ processed_emails_state = gr.State([])
12
+
13
+ def respond(user_msg, history, recorded_emails_state):
14
+ history.append({"role":"user", "content":user_msg})
15
+ reply, emails = controller.get_response(message=user_msg, history=history, recorded_emails=set(recorded_emails_state))
16
+ history.append({"role":"assistant", "content":reply})
17
+
18
+ return history, history, list(emails)
19
+
20
+ msg.submit(respond, inputs=[msg, history_state, processed_emails_state], outputs=[chat, history_state, processed_emails_state])
21
+ msg.submit(lambda: "", None, msg)
22
+
23
+ demo.launch(inbrowser=True)
community_contributions/chatbot_rag_evaluation/chat.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ from openai import OpenAI
4
+ from dotenv import load_dotenv
5
+ from tools import _record_user_details
6
+
7
+
8
+ load_dotenv(override=True)
9
+
10
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
11
+ MODEL = "gpt-4o-mini-2024-07-18"
12
+ NAME = "Damla"
13
+
14
+ # Tool: Record user interest
15
+ record_user_details_json = {
16
+ "name": "record_user_details",
17
+ "description": "Use this tool to record that a user provided an email address and they are interested in being in touch and provided an email address",
18
+ "parameters": {
19
+ "type": "object",
20
+ "properties": {
21
+ "email": {
22
+ "type": "string",
23
+ "description": "The email address of this user. Format should be similar to this: [email protected]"
24
+ },
25
+ "name": {
26
+ "type": "string",
27
+ "description": "The user's name, if they provided it"
28
+ },
29
+ "notes": {
30
+ "type": "string",
31
+ "description": "Any additional information about the conversation that's worth recording to give context"
32
+ }
33
+ },
34
+ "required": ["email"],
35
+ "additionalProperties": False
36
+ }
37
+ }
38
+
39
+ TOOL_FUNCTIONS = {
40
+ "record_user_details": _record_user_details,
41
+ }
42
+
43
+
44
+ TOOLS = [{"type": "function", "function": record_user_details_json}]
45
+
46
+
47
+ class Chat:
48
+ def __init__(self, name=NAME, model=MODEL, tools=TOOLS):
49
+ self.name = name
50
+ self.model = model
51
+ self.tools = tools
52
+ self.client = OpenAI()
53
+
54
+
55
+ def _get_system_prompt(self):
56
+ return (f"""
57
+ You are acting as {self.name}. You are answering questions on {self.name}'s website, particularly questions related to {self.name}'s career, background, skills, and experience.
58
+ You are given a summary of {self.name}'s background and LinkedIn profile which you should use as the only source of truth to answer questions.
59
+ Interpret and answer based strictly on the information provided.
60
+ You should never generate or write code. If asked to write code or build an app, explain whether {self.name}'s experience or past projects are relevant to the task,
61
+ and what approach {self.name} would take. If {self.name} has no relevant experience, politely acknowledge that.
62
+ If a project is mentioned, specify whether it's a personal project or a professional one. Be professional and engaging —
63
+ the tone should be warm, clear, and appropriate for a potential client or future employer.
64
+ If a visitor engages in a discussion, try to steer them towards getting in touch via email. Ask for their email and record it using your record_user_details tool.
65
+ Only accept inputs that follow the standard email format (like [email protected]). Do not confuse emails with phone numbers or usernames. If in doubt, ask for clarification.
66
+ If you don't know the answer, just say so.
67
+ """
68
+ )
69
+
70
+ def _handle_tool_calls(self, tool_calls, recorded_emails):
71
+ results = []
72
+ for call in tool_calls:
73
+ tool_name = call.function.name
74
+ arguments = json.loads(call.function.arguments)
75
+ if arguments["email"] in recorded_emails:
76
+ result = {"recorded": "ok"}
77
+ results.append({
78
+ "role": "tool",
79
+ "content": json.dumps(result),
80
+ "tool_call_id": call.id
81
+ })
82
+ continue
83
+
84
+ print(f"Tool called: {tool_name}")
85
+
86
+ func = TOOL_FUNCTIONS.get(tool_name)
87
+ if func:
88
+ result = func(**arguments)
89
+ results.append({
90
+ "role": "tool",
91
+ "content": json.dumps(result),
92
+ "tool_call_id": call.id
93
+ })
94
+ recorded_emails.add(arguments["email"])
95
+ return results
96
+
97
+ def chat(self, message, history, recorded_emails=set(), retrieved_chunks=None):
98
+ if retrieved_chunks:
99
+ message += f"\n\nUse the following context if helpful:\n{retrieved_chunks}"
100
+
101
+ messages = [{"role": "system", "content": self._get_system_prompt()}] + history + [{"role": "user", "content": message}]
102
+ done = False
103
+
104
+ while not done:
105
+ response = self.client.chat.completions.create(
106
+ model=self.model,
107
+ messages=messages,
108
+ tools=self.tools,
109
+ max_tokens=400,
110
+ temperature=0.5
111
+ )
112
+
113
+ finish_reason = response.choices[0].finish_reason
114
+ if finish_reason == "tool_calls":
115
+ message_obj = response.choices[0].message
116
+ tool_calls = message_obj.tool_calls
117
+ results = self._handle_tool_calls(tool_calls, recorded_emails)
118
+ messages.append(message_obj)
119
+ messages.extend(results)
120
+ else:
121
+ done = True
122
+
123
+ return response.choices[0].message.content, recorded_emails
124
+
125
+ def rerun(self, original_reply, message, history, feedback):
126
+ updated_prompt = self._get_system_prompt()
127
+ updated_prompt += (
128
+ "\n\n## Previous answer rejected\nYou just tried to reply, but the quality control rejected your reply.\n"
129
+ f"## Your attempted answer:\n{original_reply}\n\n"
130
+ f"## Reason for rejection:\n{feedback}\n"
131
+ )
132
+ messages = [{"role": "system", "content": updated_prompt}] + history + [{"role": "user", "content": message}]
133
+ response = self.client.chat.completions.create(model=self.model, messages=messages)
134
+ return response.choices[0].message.content
community_contributions/chatbot_rag_evaluation/controller.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from chat import Chat
2
+ from rag import Retriever
3
+ from evaluator import Evaluator
4
+
5
+ class ChatbotController:
6
+ def __init__(self):
7
+ self.retriever = Retriever()
8
+ self.chatbot = Chat()
9
+ self.evaluator = Evaluator(name="Damla")
10
+
11
+ def get_response(self, message, history, recorded_emails):
12
+ chunks = self.retriever.get_relevant_chunks(message)
13
+ reply, new_recorded_emails = self.chatbot.chat(message, history, recorded_emails, chunks)
14
+ evaluation = self.evaluator.evaluate(reply, message, history)
15
+
16
+ while not evaluation.is_acceptable:
17
+ print("Retrying due to failed evaluation...")
18
+ reply = self.chatbot.rerun(reply, message, history, evaluation.feedback)
19
+ evaluation = self.evaluator.evaluate(reply, message, history)
20
+
21
+ return reply, new_recorded_emails
community_contributions/chatbot_rag_evaluation/evaluator.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from openai import OpenAI
3
+ import os
4
+ from dotenv import load_dotenv
5
+
6
+
7
+ MODEL = "gemini-2.0-flash"
8
+
9
+ class Evaluation(BaseModel):
10
+ is_acceptable: bool
11
+ feedback: str
12
+
13
+
14
+ class Evaluator:
15
+ def __init__(self, name="", model=MODEL):
16
+ load_dotenv(override=True)
17
+ google_api_key = os.getenv('GOOGLE_API_KEY')
18
+
19
+ self.name=name
20
+ self.model=model
21
+ self._gemini = OpenAI(api_key=google_api_key, base_url="https://generativelanguage.googleapis.com/v1beta/openai/")
22
+
23
+ def _evaluator_system_prompt(self):
24
+ return f"You are an evaluator that decides whether a response to a question is acceptable. \
25
+ You are provided with a conversation between a User and an Agent. Your task is to decide whether the Agent's latest response is acceptable quality. \
26
+ The Agent is playing the role of {self.name} and is representing {self.name} on their website. \
27
+ The Agent has been instructed to be professional and engaging, as if talking to a potential client or future employer who came across the website. \
28
+ The Agent has been provided with context on {self.name} in the form of their summary, experience and CV. \
29
+ With this context, please evaluate the latest response, replying with whether the response is acceptable and your feedback."
30
+
31
+ def _evaluator_user_prompt(self, reply, message, history):
32
+ user_prompt = f"Here's the conversation between the User and the Agent: \n\n{history}\n\n"
33
+ user_prompt += f"Here's the latest message from the User: \n\n{message}\n\n"
34
+ user_prompt += f"Here's the latest response from the Agent: \n\n{reply}\n\n"
35
+ user_prompt += "Please evaluate the response, replying with whether it is acceptable and your feedback."
36
+ return user_prompt
37
+
38
+ def evaluate(self, reply, message, history) -> Evaluation:
39
+ messages = [{"role": "system", "content": self._evaluator_system_prompt()}] + [{"role": "user", "content": self._evaluator_user_prompt(reply, message, history)}]
40
+ response = self._gemini.beta.chat.completions.parse(model=self.model, messages=messages, response_format=Evaluation)
41
+ return response.choices[0].message.parsed
42
+
43
+
community_contributions/chatbot_rag_evaluation/knowledge_base/summary.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ # PLACEHOLDER #
community_contributions/chatbot_rag_evaluation/rag.py ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from langchain_text_splitters import CharacterTextSplitter
3
+ from langchain_community.document_loaders import DirectoryLoader, TextLoader
4
+ from langchain_huggingface import HuggingFaceEmbeddings
5
+ from langchain_chroma import Chroma
6
+
7
+ DB_NAME = 'career_db'
8
+ DIRECTORY_NAME = "knowledge_base"
9
+
10
+ class Retriever:
11
+ def __init__(self, db_name=DB_NAME, directory_name=DIRECTORY_NAME):
12
+ self.db_name = db_name
13
+ self.directory_name = directory_name
14
+ self._embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
15
+ self._retriever = None
16
+ self._init_or_load_db()
17
+
18
+ def _get_documents(self):
19
+ text_loader_kwargs = {'encoding': 'utf-8'}
20
+ loader = DirectoryLoader(self.directory_name, glob="*.txt", loader_cls=TextLoader, loader_kwargs=text_loader_kwargs)
21
+ documents = loader.load()
22
+ return documents
23
+
24
+ def _init_or_load_db(self):
25
+ if os.path.exists(self.db_name):
26
+ vectorstore = Chroma(persist_directory=self.db_name, embedding_function=self._embeddings)
27
+ print("Loaded existing vectorstore.")
28
+ else:
29
+ documents = self._get_documents()
30
+ text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=300)
31
+ chunks = text_splitter.split_documents(documents)
32
+ print(f"Total number of chunks: {len(chunks)}")
33
+
34
+ vectorstore = Chroma.from_documents(documents=chunks, embedding=self._embeddings, persist_directory=self.db_name)
35
+ print(f"Vectorstore created with {vectorstore._collection.count()} documents")
36
+
37
+ self._retriever = vectorstore.as_retriever(search_kwargs={"k": 25})
38
+
39
+ def get_relevant_chunks(self, message: str):
40
+ docs = self._retriever.invoke(message)
41
+ return [doc.page_content for doc in docs]
community_contributions/chatbot_rag_evaluation/requirements.txt ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ aiofiles
2
+ aiohappyeyeballs
3
+ aiohttp
4
+ aiosignal
5
+ annotated-types
6
+ anyio
7
+ attrs
8
+ autoflake
9
+ backoff
10
+ bcrypt
11
+ beautifulsoup4
12
+ black
13
+ blinker
14
+ Brotli
15
+ build
16
+ cachelib
17
+ cachetools
18
+ certifi
19
+ charset-normalizer
20
+ chromadb
21
+ click
22
+ colorama
23
+ coloredlogs
24
+ contourpy
25
+ cycler
26
+ dash
27
+ dash-bootstrap-components
28
+ dash-core-components
29
+ dash-design-kit
30
+ dash-html-components
31
+ dash-mantine-components
32
+ dash-table
33
+ dash_ag_grid
34
+ dataclasses-json
35
+ datasets
36
+ dill
37
+ distro
38
+ durationpy
39
+ fastapi
40
+ ffmpy
41
+ filelock
42
+ Flask
43
+ Flask-Caching
44
+ flatbuffers
45
+ fonttools
46
+ frozenlist
47
+ fsspec
48
+ gitdb
49
+ GitPython
50
+ google-auth
51
+ google-auth-oauthlib
52
+ googleapis-common-protos
53
+ gradio
54
+ gradio_client
55
+ greenlet
56
+ gritql
57
+ groovy
58
+ grpcio
59
+ gspread
60
+ h11
61
+ httpcore
62
+ httplib2
63
+ httptools
64
+ httpx
65
+ httpx-sse
66
+ huggingface-hub
67
+ humanfriendly
68
+ idna
69
+ importlib_metadata
70
+ importlib_resources
71
+ itsdangerous
72
+ Jinja2
73
+ jiter
74
+ joblib
75
+ jsonpatch
76
+ jsonpointer
77
+ jsonschema
78
+ jsonschema-specifications
79
+ kagglehub
80
+ kiwisolver
81
+ kubernetes
82
+ langchain
83
+ langchain-chroma
84
+ langchain-cli
85
+ langchain-community
86
+ langchain-core
87
+ langchain-huggingface
88
+ langchain-text-splitters
89
+ langserve
90
+ langsmith
91
+ markdown-it-py
92
+ MarkupSafe
93
+ marshmallow
94
+ matplotlib
95
+ mdurl
96
+ mmh3
97
+ mpmath
98
+ multidict
99
+ multiprocess
100
+ mypy-extensions
101
+ nest-asyncio
102
+ networkx
103
+ newsapi-python
104
+ newsapi-python-client
105
+ nltk
106
+ numpy
107
+ oauthlib
108
+ ollama
109
+ onnxruntime
110
+ openai
111
+ opentelemetry-api
112
+ opentelemetry-exporter-otlp-proto-common
113
+ opentelemetry-exporter-otlp-proto-grpc
114
+ opentelemetry-proto
115
+ opentelemetry-sdk
116
+ opentelemetry-semantic-conventions
117
+ orjson
118
+ overrides
119
+ packaging
120
+ pandas
121
+ pathspec
122
+ pillow
123
+ platformdirs
124
+ plotly
125
+ posthog
126
+ propcache
127
+ protobuf
128
+ pyarrow
129
+ pyasn1
130
+ pyasn1_modules
131
+ pybase64
132
+ pydantic
133
+ pydantic-settings
134
+ pydantic_core
135
+ pydub
136
+ pyflakes
137
+ pygame
138
+ Pygments
139
+ pyparsing
140
+ PyPDF2
141
+ PyPika
142
+ pyproject_hooks
143
+ pyreadline3
144
+ python-dateutil
145
+ python-dotenv
146
+ python-multipart
147
+ pytz
148
+ PyYAML
149
+ referencing
150
+ regex
151
+ requests
152
+ requests-oauthlib
153
+ requests-toolbelt
154
+ retrying
155
+ rich
156
+ rpds-py
157
+ rsa
158
+ ruff
159
+ safehttpx
160
+ safetensors
161
+ scikit-learn
162
+ scipy
163
+ semantic-version
164
+ sentence-transformers
165
+ setuptools
166
+ shellingham
167
+ six
168
+ smmap
169
+ sniffio
170
+ soupsieve
171
+ SQLAlchemy
172
+ sse-starlette
173
+ starlette
174
+ sympy
175
+ tenacity
176
+ threadpoolctl
177
+ tokenizers
178
+ tomlkit
179
+ torch
180
+ tqdm
181
+ transformers
182
+ typer
183
+ typing-inspect
184
+ typing-inspection
185
+ typing_extensions
186
+ tzdata
187
+ urllib3
188
+ uvicorn
189
+ vizro
190
+ watchfiles
191
+ websocket-client
192
+ websockets
193
+ Werkzeug
194
+ wrapt
195
+ xxhash
196
+ yarl
197
+ zipp
198
+ zstandard
community_contributions/chatbot_rag_evaluation/tools.py ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # tools.py
2
+
3
+ import os
4
+ import csv
5
+ import json
6
+ import base64
7
+ from dotenv import load_dotenv
8
+ from datetime import datetime
9
+
10
+
11
+ try:
12
+ import gspread
13
+ from google.oauth2.service_account import Credentials
14
+ GOOGLE_SHEETS_AVAILABLE = True
15
+ except ImportError:
16
+ GOOGLE_SHEETS_AVAILABLE = False
17
+
18
+
19
+ CSV_FILE = "user_interest.csv"
20
+ SHEET_NAME = "UserInterest"
21
+
22
+
23
+ def _get_google_credentials():
24
+ """
25
+ Loads Google credentials either from local file or HF Spaces secret.
26
+ Returns a ServiceAccountCredentials object.
27
+ """
28
+ load_dotenv(override=True)
29
+ scope = ["https://spreadsheets.google.com/feeds", "https://www.googleapis.com/auth/drive"]
30
+ google_creds_json = os.getenv("GOOGLE_CREDENTIALS_JSON")
31
+
32
+ if google_creds_json:
33
+ json_str = base64.b64decode(google_creds_json).decode('utf-8')
34
+ creds_dict = json.loads(json_str)
35
+ creds = Credentials.from_service_account_info(creds_dict, scopes=scope)
36
+ print("[info] Loaded Google credentials from environment.")
37
+ return creds
38
+
39
+ raise RuntimeError("Google credentials not found.")
40
+
41
+ def _save_to_google_sheets(email, name, notes):
42
+ creds = _get_google_credentials()
43
+ client = gspread.authorize(creds)
44
+ sheet = client.open(SHEET_NAME).sheet1
45
+ row = [datetime.today().strftime('%Y-%m-%d %H:%M'), email, name, notes]
46
+ sheet.append_row(row)
47
+ print(f"[Google Sheets] Recorded: {email}, {name}")
48
+
49
+ def _save_to_csv(email, name, notes):
50
+ file_exists = os.path.isfile(CSV_FILE)
51
+ with open(CSV_FILE, mode='a', newline='', encoding='utf-8') as f:
52
+ writer = csv.writer(f)
53
+ if not file_exists:
54
+ writer.writerow(["Timestamp", "Email", "Name", "Notes"])
55
+ writer.writerow([datetime.today().strftime('%Y-%m-%d %H:%M'), email, name, notes])
56
+ print(f"[CSV] Recorded: {email}, {name}")
57
+
58
+ def _record_user_details(email, name="Name not provided", notes="Not provided"):
59
+ try:
60
+ if GOOGLE_SHEETS_AVAILABLE:
61
+ _save_to_google_sheets(email, name, notes)
62
+ else:
63
+ raise ImportError("gspread not installed.")
64
+ except Exception as e:
65
+ print(f"[Warning] Google Sheets write failed, using CSV. Reason: {e}")
66
+ _save_to_csv(email, name, notes)
67
+
68
+ return {"recorded": "ok"}
community_contributions/claude_based_chatbot_tc/.gitignore ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Virtual environment
7
+ venv/
8
+ env/
9
+ .venv/
10
+
11
+ # Jupyter notebook checkpoints
12
+ .ipynb_checkpoints/
13
+
14
+ # Docs
15
+ docs/claude_self_chatbot.ipynb
16
+ #docs/Multi-modal-tailored-faq.ipynb
17
+ docs/response_evaluation.ipynb
18
+ me/linkedin.pdf
19
+ me/summary.txt
20
+ me/faq.txt
21
+
22
+
23
+ # Environment variable files
24
+ .env
25
+
26
+ # Windows system files
27
+ Thumbs.db
28
+ ehthumbs.db
29
+ Desktop.ini
30
+ $RECYCLE.BIN/
31
+
32
+ # PyCharm/VSCode config
33
+ .idea/
34
+ .vscode/
35
+
36
+
37
+ # Node modules (if any)
38
+ node_modules/
39
+
40
+ # Other temporary files
41
+ *.log
community_contributions/claude_based_chatbot_tc/README.md ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ ---
2
+ title: career-conversation-tc
3
+ app_file: app.py
4
+ sdk: gradio
5
+ sdk_version: 5.33.1
6
+ ---
community_contributions/claude_based_chatbot_tc/app.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Claude-based Chatbot with Tools
3
+
4
+ This app creates a chatbot using Anthropic's Claude model that represents
5
+ a professional profile based on LinkedIn data and other personal information.
6
+
7
+ Features:
8
+ - PDF resume parsing
9
+ - Push notifications
10
+ - Function calling with tools
11
+ - Professional representation
12
+ """
13
+ import gradio as gr
14
+ from modules.chat import chat_function
15
+
16
+ # Wrapper function that only returns the message, not the state
17
+ def chat_wrapper(message, history, state=None):
18
+ result, new_state = chat_function(message, history, state)
19
+ return result
20
+
21
+ def main():
22
+ # Create the chat interface
23
+ chat_interface = gr.ChatInterface(
24
+ fn=chat_wrapper, # Use the wrapper function
25
+ type="messages",
26
+ additional_inputs=[gr.State()]
27
+ )
28
+
29
+ # Launch the interface
30
+ chat_interface.launch()
31
+
32
+ if __name__ == "__main__":
33
+ main()
community_contributions/claude_based_chatbot_tc/docs/Multi-modal-tailored-faq.ipynb ADDED
@@ -0,0 +1,309 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Multi-model Evaluation LinkedIn Summary and FAQ"
8
+ ]
9
+ },
10
+ {
11
+ "cell_type": "code",
12
+ "execution_count": 1,
13
+ "metadata": {},
14
+ "outputs": [
15
+ {
16
+ "data": {
17
+ "text/plain": [
18
+ "True"
19
+ ]
20
+ },
21
+ "execution_count": 1,
22
+ "metadata": {},
23
+ "output_type": "execute_result"
24
+ }
25
+ ],
26
+ "source": [
27
+ "import os\n",
28
+ "import gradio as gr\n",
29
+ "from dotenv import load_dotenv\n",
30
+ "from pypdf import PdfReader\n",
31
+ "from pathlib import Path\n",
32
+ "from IPython.display import Markdown, display\n",
33
+ "from anthropic import Anthropic\n",
34
+ "from openai import OpenAI # Used here to call Ollama-compatible API and Google Gemini\n",
35
+ "\n",
36
+ "load_dotenv(override=True)"
37
+ ]
38
+ },
39
+ {
40
+ "cell_type": "code",
41
+ "execution_count": 2,
42
+ "metadata": {},
43
+ "outputs": [
44
+ {
45
+ "name": "stdout",
46
+ "output_type": "stream",
47
+ "text": [
48
+ "OpenAI API Key not set\n",
49
+ "Anthropic API Key exists and begins sk-ant-\n",
50
+ "Google API Key exists and begins AI\n",
51
+ "DeepSeek API Key not set (and this is optional)\n",
52
+ "Groq API Key exists and begins gsk_\n"
53
+ ]
54
+ }
55
+ ],
56
+ "source": [
57
+ "# Print the key prefixes to help with any debugging\n",
58
+ "\n",
59
+ "openai_api_key = os.getenv('OPENAI_API_KEY')\n",
60
+ "anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
61
+ "google_api_key = os.getenv('GOOGLE_API_KEY')\n",
62
+ "deepseek_api_key = os.getenv('DEEPSEEK_API_KEY')\n",
63
+ "groq_api_key = os.getenv('GROQ_API_KEY')\n",
64
+ "\n",
65
+ "if openai_api_key:\n",
66
+ " print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
67
+ "else:\n",
68
+ " print(\"OpenAI API Key not set\")\n",
69
+ " \n",
70
+ "if anthropic_api_key:\n",
71
+ " print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
72
+ "else:\n",
73
+ " print(\"Anthropic API Key not set (and this is optional)\")\n",
74
+ "\n",
75
+ "if google_api_key:\n",
76
+ " print(f\"Google API Key exists and begins {google_api_key[:2]}\")\n",
77
+ "else:\n",
78
+ " print(\"Google API Key not set (and this is optional)\")\n",
79
+ "\n",
80
+ "if deepseek_api_key:\n",
81
+ " print(f\"DeepSeek API Key exists and begins {deepseek_api_key[:3]}\")\n",
82
+ "else:\n",
83
+ " print(\"DeepSeek API Key not set (and this is optional)\")\n",
84
+ "\n",
85
+ "if groq_api_key:\n",
86
+ " print(f\"Groq API Key exists and begins {groq_api_key[:4]}\")\n",
87
+ "else:\n",
88
+ " print(\"Groq API Key not set (and this is optional)\")"
89
+ ]
90
+ },
91
+ {
92
+ "cell_type": "code",
93
+ "execution_count": 6,
94
+ "metadata": {},
95
+ "outputs": [],
96
+ "source": [
97
+ "anthropic = Anthropic()\n",
98
+ "\n",
99
+ "# === Load PDF and extract resume text ===\n",
100
+ "\n",
101
+ "reader = PdfReader(\"../claude_based_chatbot_tc/me/linkedin.pdf\")\n",
102
+ "linkedin = \"\"\n",
103
+ "for page in reader.pages:\n",
104
+ " text = page.extract_text()\n",
105
+ " if text:\n",
106
+ " linkedin += text\n",
107
+ "\n",
108
+ "# === Create the shared FAQ generation prompt ===\n",
109
+ "faq_prompt = (\n",
110
+ " \"Please read the following professional background and resume content carefully. \"\n",
111
+ " \"Based on this information, generate a well-structured FAQ (Frequently Asked Questions) document that reflects the subject’s professional background.\\n\\n\"\n",
112
+ " \"== RESUME TEXT START ==\\n\"\n",
113
+ " f\"{linkedin}\\n\"\n",
114
+ " \"== RESUME TEXT END ==\\n\\n\"\n",
115
+ "\n",
116
+ " \"**Instructions:**\\n\"\n",
117
+ " \"- Write at least 15 FAQs.\\n\"\n",
118
+ " \"- Each entry should be in the format:\\n\"\n",
119
+ " \" - Q: [Question here]\\n\"\n",
120
+ " \" - A: [Answer here]\\n\"\n",
121
+ " \"- Focus on real-world questions that recruiters, collaborators, or website visitors would ask.\\n\"\n",
122
+ " \"- Be concise, accurate, and use only the information in the resume. Do not speculate or invent details.\\n\"\n",
123
+ " \"- Use a professional tone suitable for publishing on a personal website.\\n\\n\"\n",
124
+ "\n",
125
+ " \"Output only the FAQ content. Do not include commentary, headers, or formatting outside of the Q/A list.\"\n",
126
+ ")\n",
127
+ "\n",
128
+ "messages = [{\"role\": \"user\", \"content\": faq_prompt}]\n",
129
+ "evaluators = []\n",
130
+ "answers = []\n",
131
+ "\n"
132
+ ]
133
+ },
134
+ {
135
+ "cell_type": "code",
136
+ "execution_count": null,
137
+ "metadata": {},
138
+ "outputs": [],
139
+ "source": [
140
+ "# Anthropic API Call\n",
141
+ "\n",
142
+ "model_name = \"claude-3-7-sonnet-latest\"\n",
143
+ "\n",
144
+ "claude = Anthropic()\n",
145
+ "faq_prompt = claude.messages.create(\n",
146
+ " model=model_name, \n",
147
+ " messages=messages, \n",
148
+ " max_tokens=1000\n",
149
+ ")\n",
150
+ "\n",
151
+ "faq_answer = faq_prompt.content[0].text\n",
152
+ "\n",
153
+ "display(Markdown(faq_answer))\n",
154
+ "evaluators.append(model_name)\n",
155
+ "answers.append(faq_answer)"
156
+ ]
157
+ },
158
+ {
159
+ "cell_type": "code",
160
+ "execution_count": null,
161
+ "metadata": {},
162
+ "outputs": [],
163
+ "source": [
164
+ "# === 2. Google Gemini Call ===\n",
165
+ "\n",
166
+ "gemini = OpenAI(api_key=google_api_key, base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\")\n",
167
+ "model_name = \"gemini-2.5-flash\"\n",
168
+ "\n",
169
+ "faq_prompt = gemini.chat.completions.create(model=model_name, messages=messages)\n",
170
+ "faq_answer = faq_prompt.choices[0].message.content\n",
171
+ "\n",
172
+ "display(Markdown(faq_answer))\n",
173
+ "evaluators.append(model_name)\n",
174
+ "answers.append(faq_answer)\n"
175
+ ]
176
+ },
177
+ {
178
+ "cell_type": "code",
179
+ "execution_count": null,
180
+ "metadata": {},
181
+ "outputs": [],
182
+ "source": [
183
+ "# === 2. Ollama Groq Call ===\n",
184
+ "\n",
185
+ "groq = OpenAI(api_key=groq_api_key, base_url=\"https://api.groq.com/openai/v1\")\n",
186
+ "model_name = \"llama-3.3-70b-versatile\"\n",
187
+ "\n",
188
+ "faq_prompt = groq.chat.completions.create(model=model_name, messages=messages)\n",
189
+ "faq_answer = faq_prompt.choices[0].message.content\n",
190
+ "\n",
191
+ "display(Markdown(faq_answer))\n",
192
+ "evaluators.append(model_name)\n",
193
+ "answers.append(faq_answer)"
194
+ ]
195
+ },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": null,
199
+ "metadata": {},
200
+ "outputs": [],
201
+ "source": [
202
+ "# It's nice to know how to use \"zip\"\n",
203
+ "\n",
204
+ "for evaluator, answer in zip(evaluators, answers):\n",
205
+ " print(f\"Evaluator: {evaluator}\\n\\n{answer}\")\n",
206
+ "\n",
207
+ "\n",
208
+ "# Let's bring this together - note the use of \"enumerate\"\n",
209
+ "\n",
210
+ "together = \"\"\n",
211
+ "for index, answer in enumerate(answers):\n",
212
+ " together += f\"# Response from evaluator {index+1}\\n\\n\"\n",
213
+ " together += answer + \"\\n\\n\""
214
+ ]
215
+ },
216
+ {
217
+ "cell_type": "code",
218
+ "execution_count": 15,
219
+ "metadata": {},
220
+ "outputs": [],
221
+ "source": [
222
+ "formatter = f\"\"\"You are a meticulous AI evaluator tasked with synthesizing multiple assistant-generated career FAQs and summaries into one high-quality file. You have received {len(evaluators)} drafts based on the same resume, each containing a 2-line summary and a set of FAQ questions with answers.\n",
223
+ "\n",
224
+ "---\n",
225
+ "**Original Request:**\n",
226
+ "\"{faq_prompt}\"\n",
227
+ "---\n",
228
+ "\n",
229
+ "Your goal is to combine the strongest parts of each submission into a single, polished output. This will be the final `faq.txt` that lives in a public-facing portfolio folder.\n",
230
+ "\n",
231
+ "**Evaluation & Synthesis Instructions:**\n",
232
+ "\n",
233
+ "1. **Prioritize Accuracy:** Only include information clearly supported by the resume. Do not invent or speculate.\n",
234
+ "2. **Best Questions Only:** Select the most relevant and insightful FAQ questions. Discard weak, redundant, or generic ones.\n",
235
+ "3. **Edit for Quality:** Improve the clarity and fluency of answers. Fix grammar, wording, or formatting inconsistencies.\n",
236
+ "4. **Merge Strengths:** If two assistants answer the same question differently, combine the best phrasing and facts from each.\n",
237
+ "5. **Consistency in Voice:** Ensure a single professional tone throughout the summary and FAQ.\n",
238
+ "\n",
239
+ "**Required Output Structure:**\n",
240
+ "\n",
241
+ "1. **2-Line Summary:** Start with the best or synthesized version of the summary, capturing key career strengths.\n",
242
+ "2. **FAQ Entries:** Follow with at least 8–12 strong FAQ entries in this format:\n",
243
+ "\n",
244
+ "Q: [Question] \n",
245
+ "A: [Answer]\n",
246
+ "\n",
247
+ "---\n",
248
+ "**Examples of Strong FAQ Topics:**\n",
249
+ "- Key technical skills or languages\n",
250
+ "- Past projects or employers\n",
251
+ "- Teamwork or communication style\n",
252
+ "- Remote work or leadership experience\n",
253
+ "- Career goals or current availability\n",
254
+ "\n",
255
+ "This will be saved as a plain text file (`faq.txt`). Ensure the tone is accurate, clean, and helpful. Do not add unnecessary commentary or meta-analysis. The final version should look like it was written by a professional assistant who knows the subject well.\n",
256
+ "\"\"\"\n",
257
+ "\n",
258
+ "formatter_messages = [{\"role\": \"user\", \"content\": formatter}]"
259
+ ]
260
+ },
261
+ {
262
+ "cell_type": "code",
263
+ "execution_count": null,
264
+ "metadata": {},
265
+ "outputs": [],
266
+ "source": [
267
+ "# === 1. Final (Claude) API Call ===\n",
268
+ "anthropic = Anthropic(api_key=anthropic_api_key)\n",
269
+ "faq_prompt = anthropic.messages.create(\n",
270
+ " model=\"claude-3-7-sonnet-latest\",\n",
271
+ " messages=formatter_messages,\n",
272
+ " max_tokens=1000,\n",
273
+ ")\n",
274
+ "results = faq_prompt.content[0].text\n",
275
+ "display(Markdown(results))\n"
276
+ ]
277
+ },
278
+ {
279
+ "cell_type": "code",
280
+ "execution_count": null,
281
+ "metadata": {},
282
+ "outputs": [],
283
+ "source": [
284
+ "gr.ChatInterface(results, type=\"messages\").launch()"
285
+ ]
286
+ }
287
+ ],
288
+ "metadata": {
289
+ "kernelspec": {
290
+ "display_name": ".venv",
291
+ "language": "python",
292
+ "name": "python3"
293
+ },
294
+ "language_info": {
295
+ "codemirror_mode": {
296
+ "name": "ipython",
297
+ "version": 3
298
+ },
299
+ "file_extension": ".py",
300
+ "mimetype": "text/x-python",
301
+ "name": "python",
302
+ "nbconvert_exporter": "python",
303
+ "pygments_lexer": "ipython3",
304
+ "version": "3.12.10"
305
+ }
306
+ },
307
+ "nbformat": 4,
308
+ "nbformat_minor": 2
309
+ }
community_contributions/claude_based_chatbot_tc/modules/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ """
2
+ Module initialization
3
+ """
community_contributions/claude_based_chatbot_tc/modules/chat.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Chat functionality for the Claude-based chatbot
3
+ """
4
+ import re
5
+ import time
6
+ import json
7
+ from collections import deque
8
+ from anthropic import Anthropic
9
+ from .config import MODEL_NAME, MAX_TOKENS
10
+ from .tools import tool_schemas, handle_tool_calls
11
+ from .data_loader import load_personal_data
12
+
13
+ # Initialize Anthropic client
14
+ anthropic_client = Anthropic()
15
+
16
+ def sanitize_input(text):
17
+ """Protect against prompt injection by sanitizing user input"""
18
+ return re.sub(r"[^\w\s.,!?@&:;/-]", "", text)
19
+
20
+ def create_system_prompt(name, summary, linkedin):
21
+ """Create the system prompt for Claude"""
22
+ return f"""You are acting as {name}. You are answering questions on {name}'s website,
23
+ particularly questions related to {name}'s career, background, skills and experience.
24
+ Your responsibility is to represent {name} for interactions on the website as faithfully as possible.
25
+ You are given a summary of {name}'s background and LinkedIn profile which you can use to answer questions.
26
+ Be professional and engaging, as if talking to a potential client or future employer who came across the website, and only mention company names if the user asks about them.
27
+
28
+ IMPORTANT: When greeting users for the first time, always start with: "Hello! *Meet {name}'s AI assistant, trained on her career data.* " followed by your introduction.
29
+
30
+ Strict guidelines you must follow:
31
+ - When asked about location, do NOT mention any specific cities or regions, even if asked repeatedly. Avoid mentioning cities even when you are referring to previous work experience, only use countries.
32
+ - Never share {name}'s email or contact information directly. If someone wants to get in touch, ask for their email address (so you can follow up), or encourage them to reach out via LinkedIn.
33
+ - If you don't know the answer to any question, use your record_unknown_question tool to log it.
34
+ - If someone expresses interest in working together or wants to stay in touch, use your record_user_details tool to capture their email address.
35
+ - If the user asks a question that might be answered in the FAQ, use your search_faq tool to search the FAQ.
36
+ - If you don't know the answer, say so.
37
+
38
+ ## Summary:
39
+ {summary}
40
+
41
+ ## LinkedIn Profile:
42
+ {linkedin}
43
+
44
+ With this context, please chat with the user, always staying in character as {name}.
45
+ """
46
+
47
+ def chat_function(message, history, state=None):
48
+ """
49
+ Main chat function that:
50
+ 1. Applies rate limiting
51
+ 2. Sanitizes input
52
+ 3. Handles Claude API calls
53
+ 4. Processes tool calls
54
+ 5. Adds disclaimer to responses
55
+ """
56
+ # Load data
57
+ data = load_personal_data()
58
+ name = "Taissa Conde"
59
+ summary = data["summary"]
60
+ linkedin = data["linkedin"]
61
+
62
+ # Disclaimer to be shown with the first response
63
+ disclaimer = f"""*Note: This AI assistant, trained on her career data and is a representation of professional information only, not personal views, and details may not be fully accurate or current.*"""
64
+
65
+ # Rate limiting: 10 messages/minute
66
+ if state is None:
67
+ state = {"timestamps": deque(), "full_history": [], "first_message": True}
68
+
69
+ # Check if this is actually the first message by looking at history length
70
+ is_first_message = len(history) == 0
71
+
72
+ now = time.time()
73
+ state["timestamps"].append(now)
74
+ while state["timestamps"] and now - state["timestamps"][0] > 60:
75
+ state["timestamps"].popleft()
76
+ if len(state["timestamps"]) > 10:
77
+ return "⚠️ You're sending messages too quickly. Please wait a moment."
78
+
79
+ # Store full history with metadata for your own use
80
+ state["full_history"] = history.copy()
81
+
82
+ # Sanitize user input
83
+ sanitized_input = sanitize_input(message)
84
+
85
+ # Format conversation history for Claude - NO system message in messages array
86
+ # Clean the history to only include role and content (remove any extra fields)
87
+ messages = []
88
+ for turn in history:
89
+ # Only keep role and content, filter out any extra fields like metadata
90
+ clean_turn = {
91
+ "role": turn["role"],
92
+ "content": turn["content"]
93
+ }
94
+ messages.append(clean_turn)
95
+ messages.append({"role": "user", "content": sanitized_input})
96
+
97
+ # Create system prompt
98
+ system_prompt = create_system_prompt(name, summary, linkedin)
99
+
100
+ # Process conversation with Claude, handling tool calls
101
+ done = False
102
+ while not done:
103
+ response = anthropic_client.messages.create(
104
+ model=MODEL_NAME,
105
+ system=system_prompt, # Pass system prompt as separate parameter
106
+ messages=messages,
107
+ max_tokens=MAX_TOKENS,
108
+ tools=tool_schemas,
109
+ )
110
+
111
+ # Check if Claude wants to call a tool
112
+ # In Anthropic API, tool calls are in the content blocks, not a separate attribute
113
+ tool_calls = []
114
+ assistant_content = ""
115
+
116
+ for content_block in response.content:
117
+ if content_block.type == "text":
118
+ assistant_content += content_block.text
119
+ elif content_block.type == "tool_use":
120
+ tool_calls.append(content_block)
121
+
122
+ if tool_calls:
123
+ results = handle_tool_calls(tool_calls)
124
+
125
+ # Add Claude's response with tool calls to conversation
126
+ messages.append({
127
+ "role": "assistant",
128
+ "content": response.content # Keep the original content structure
129
+ })
130
+
131
+ # Add tool results
132
+ messages.extend(results)
133
+ else:
134
+ done = True
135
+
136
+ # Get the final response and add disclaimer
137
+ reply = ""
138
+ for content_block in response.content:
139
+ if content_block.type == "text":
140
+ reply += content_block.text
141
+
142
+ # Remove any disclaimer that Claude might have added
143
+ if reply.startswith("📌"):
144
+ reply = reply.split("\n\n", 1)[-1] if "\n\n" in reply else reply
145
+ if "*Note:" in reply:
146
+ reply = reply.split("*Note:")[0].strip()
147
+
148
+ # Add disclaimer only to first message and at the bottom
149
+ if is_first_message:
150
+ return f"{reply.strip()}\n\n{disclaimer}", state
151
+ else:
152
+ return reply.strip(), state
community_contributions/claude_based_chatbot_tc/modules/config.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Configuration and environment setup for the chatbot
3
+ """
4
+ import os
5
+ from dotenv import load_dotenv
6
+
7
+ # Load environment variables
8
+ load_dotenv(override=True)
9
+
10
+ # Configuration
11
+ MODEL_NAME = "claude-3-7-sonnet-latest"
12
+ MAX_TOKENS = 1000
13
+ RATE_LIMIT = 10 # messages per minute
14
+ DEFAULT_NAME = "Taissa Conde"
15
+
16
+ # Pushover configuration
17
+ PUSHOVER_USER = os.getenv("PUSHOVER_USER")
18
+ PUSHOVER_TOKEN = os.getenv("PUSHOVER_TOKEN")