├── README.md
├── tool_calling.ipynb
└── vid_screenshot.png
/README.md:
--------------------------------------------------------------------------------
1 | # Code from my Tool & Function Calling Deep Dive Video!
2 |
3 | [](https://youtu.be/gMeTK6zzaO4)
4 | Click play to watch :)
5 |
--------------------------------------------------------------------------------
/tool_calling.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "attachments": {},
5 | "cell_type": "markdown",
6 | "id": "10a1bb14-351c-41e7-8185-b9f184334225",
7 | "metadata": {},
8 | "source": [
9 | "
\n",
10 | "\n",
11 | "# LLM Tool/Function Calling\n",
12 | "\n",
13 | "*Function calling is the ability to reliably connect LLMs to external tools to enable effective tool usage and interaction with external APIs.*\n",
14 | "\n",
15 | "This is often a fine-tuned feature, not available to all models, but super useful where it is!\n",
16 | "\n",
17 | "### Common use cases\n",
18 | "\n",
19 | "Function calling allows you to more reliably get structured data back from the model. For example, you can:\n",
20 | "\n",
21 | "* Create assistants that answer questions by calling external APIs\n",
22 | " * e.g. define functions like `send_email(to: string, body: string)`, or `get_current_weather(location: string, unit: 'celsius' | 'fahrenheit')`\n",
23 | "* Convert natural language into API calls\n",
24 | " * e.g. convert \"Who are my top customers?\" to `get_customers(min_revenue: int, created_before: string, limit: int)` and call your internal API\n",
25 | "* Extract structured data from text\n",
26 | " * e.g. define a function called `extract_data(name: string, birthday: string)`, or `sql_query(query: string)`\n",
27 | " \n",
28 | "### The basic sequence of steps for function calling is as follows:\n",
29 | "\n",
30 | "1. Call the model with the user query and a set of functions defined in the functions parameter.\n",
31 | "2. The model can choose to call one or more functions; if so, the content will be a stringified JSON object adhering to your custom schema\n",
32 | "3. Parse the string into JSON in your code, and call your function with the provided arguments if they exist.\n",
33 | "4. Call the model again by appending the function response as a new message, and let the model summarize the results back to the user.\n",
34 | "\n",
35 | "While the name implies that the model is performing some action, this is actually not the case! The model is coming up with the arguments to a tool, and actually running the tool (or not) is up to the user"
36 | ]
37 | },
38 | {
39 | "cell_type": "markdown",
40 | "id": "9d2a35ce-e262-45b8-8e75-af83b8510f5d",
41 | "metadata": {},
42 | "source": [
43 | "---\n",
44 | "# Creating LLM Functions"
45 | ]
46 | },
47 | {
48 | "cell_type": "markdown",
49 | "id": "b88a0d09-c69b-4bc6-b1ba-78ed408c31d7",
50 | "metadata": {},
51 | "source": [
52 | "## OpenAI Function Schema"
53 | ]
54 | },
55 | {
56 | "cell_type": "markdown",
57 | "id": "25a4f118-d737-4268-a5b3-b55cdd2a5b17",
58 | "metadata": {},
59 | "source": [
60 | "**[OpenAI Function Calling Documentation](https://platform.openai.com/docs/guides/function-calling)**\n",
61 | "\n",
62 | "*According to the JSON standard, a JSON value is one of the following JSON-language data types: object, array, number, string, Boolean (value true or false), or null (value null). All values except objects and arrays are scalar.*\n",
63 | "\n",
64 | "https://en.wikipedia.org/wiki/JSON#Data_types"
65 | ]
66 | },
67 | {
68 | "cell_type": "code",
69 | "execution_count": 1,
70 | "id": "0ef21b42-7a01-4273-b18e-f544b42c2591",
71 | "metadata": {},
72 | "outputs": [],
73 | "source": [
74 | "first_tools = [\n",
75 | " # Tool 1 - Get Exchange Rate\n",
76 | " { \n",
77 | " \"type\": \"function\",\n",
78 | " \"function\": {\n",
79 | " \"name\": \"get_exchange_rate\",\n",
80 | " \"description\": \"Get the current exchange rate of a base currency and target currency\",\n",
81 | " \"parameters\": {\n",
82 | " \"type\": \"object\",\n",
83 | " \"properties\": {\n",
84 | " \"base_currency\": {\n",
85 | " \"type\": \"string\",\n",
86 | " \"description\": \"The base currency for exchange rate calculations, i.e. USD, EUR, RUB\",\n",
87 | " },\n",
88 | " \"target_currency\": {\n",
89 | " \"type\": \"string\", \n",
90 | " \"description\": \"The target currency for exchange rate calculations, i.e. USD, EUR, RUB\"\n",
91 | " },\n",
92 | " \"date\": {\n",
93 | " \"type\": \"string\", \n",
94 | " \"description\": \"A specific day to reference, in YYYY-MM-DD format.\"\n",
95 | " },\n",
96 | " },\n",
97 | " \"required\": [\"base_currency\", \"target_currency\"],\n",
98 | " },\n",
99 | " },\n",
100 | " },\n",
101 | " # Tool 2 - Search Internet\n",
102 | " { \n",
103 | " \"type\": \"function\",\n",
104 | " \"function\": {\n",
105 | " \"name\": \"search_internet\",\n",
106 | " \"description\": \"Get internet search results for real time information\",\n",
107 | " \"parameters\": {\n",
108 | " \"type\": \"object\",\n",
109 | " \"properties\": {\n",
110 | " \"search_query\": {\n",
111 | " \"type\": \"string\",\n",
112 | " \"description\": \"The query to search the web for\",\n",
113 | " }\n",
114 | " },\n",
115 | " \"required\": [\"search_query\"],\n",
116 | " },\n",
117 | " },\n",
118 | " }\n",
119 | " ]"
120 | ]
121 | },
122 | {
123 | "cell_type": "code",
124 | "execution_count": 5,
125 | "id": "b4e899c4-f512-4ec9-a941-cef28bbef237",
126 | "metadata": {},
127 | "outputs": [],
128 | "source": [
129 | "from openai import OpenAI\n",
130 | "import json\n",
131 | "\n",
132 | "client = OpenAI()\n",
133 | "\n",
134 | "messages = [{\"role\": \"user\", \n",
135 | " \"content\": \"How much is a dollar worth in Japan? How about poland? Whats the current news in Argentina?\"}]\n",
136 | "\n",
137 | "\n",
138 | "response = client.chat.completions.create(\n",
139 | " model=\"gpt-4o\",\n",
140 | " messages=messages,\n",
141 | " tools=first_tools,\n",
142 | " tool_choice=\"auto\", # auto is default, but we'll be explicit\n",
143 | " )"
144 | ]
145 | },
146 | {
147 | "cell_type": "code",
148 | "execution_count": 3,
149 | "id": "a9588bcd-14f2-4f54-b12a-e0be4127bdd3",
150 | "metadata": {},
151 | "outputs": [],
152 | "source": [
153 | "# Function for printing out responses neatly\n",
154 | "def pprint_response(response):\n",
155 | " print(\"--- Full Response ---\\n\")\n",
156 | " print(response, \"\\n\")\n",
157 | " \n",
158 | " print(\"--- Chat Completion Message ---\\n\")\n",
159 | " print(response.choices[0].message, \"\\n\")\n",
160 | " \n",
161 | " if response.choices[0].message.tool_calls:\n",
162 | " for i in range(0, len(response.choices[0].message.tool_calls)):\n",
163 | " print(f\"--- Tool Call {i+1} ---\\n\")\n",
164 | " print(f\"Function: {response.choices[0].message.tool_calls[i].function.name}\\n\")\n",
165 | " print(f\"Arguments: {response.choices[0].message.tool_calls[i].function.arguments}\\n\")"
166 | ]
167 | },
168 | {
169 | "cell_type": "code",
170 | "execution_count": 6,
171 | "id": "32cde175-accf-4242-b22b-af5dcd7c8185",
172 | "metadata": {},
173 | "outputs": [
174 | {
175 | "name": "stdout",
176 | "output_type": "stream",
177 | "text": [
178 | "--- Full Response ---\n",
179 | "\n",
180 | "ChatCompletion(id='chatcmpl-9ljQtArRTkzhHmsFj3RdOQNNfhVBQ', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_qPo46RVBKvqzUIH19u5zdWuF', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_eLFxyxINbApbtVRLi11mIjOg', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"PLN\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_jYLIX1csd06icKV7JVi62hlf', function=Function(arguments='{\"search_query\": \"current news in Argentina\"}', name='search_internet'), type='function')]))], created=1721163087, model='gpt-4o-2024-05-13', object='chat.completion', system_fingerprint='fp_400f27fa1f', usage=CompletionUsage(completion_tokens=78, prompt_tokens=163, total_tokens=241)) \n",
181 | "\n",
182 | "--- Chat Completion Message ---\n",
183 | "\n",
184 | "ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_qPo46RVBKvqzUIH19u5zdWuF', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_eLFxyxINbApbtVRLi11mIjOg', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"PLN\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_jYLIX1csd06icKV7JVi62hlf', function=Function(arguments='{\"search_query\": \"current news in Argentina\"}', name='search_internet'), type='function')]) \n",
185 | "\n",
186 | "--- Tool Call 1 ---\n",
187 | "\n",
188 | "Function: get_exchange_rate\n",
189 | "\n",
190 | "Arguments: {\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}\n",
191 | "\n",
192 | "--- Tool Call 2 ---\n",
193 | "\n",
194 | "Function: get_exchange_rate\n",
195 | "\n",
196 | "Arguments: {\"base_currency\": \"USD\", \"target_currency\": \"PLN\"}\n",
197 | "\n",
198 | "--- Tool Call 3 ---\n",
199 | "\n",
200 | "Function: search_internet\n",
201 | "\n",
202 | "Arguments: {\"search_query\": \"current news in Argentina\"}\n",
203 | "\n"
204 | ]
205 | }
206 | ],
207 | "source": [
208 | "pprint_response(response)"
209 | ]
210 | },
211 | {
212 | "cell_type": "markdown",
213 | "id": "c2194856-0d37-4a01-9685-aba8da6f2ac5",
214 | "metadata": {},
215 | "source": [
216 | "---\n",
217 | "## Executing the Functions\n",
218 | "\n",
219 | "As the LLM is just populating the arguments for calling the function, it is up to us to actually execute the functions.\n",
220 | "\n",
221 | "For our Web Search tool, we'll be using the DuckDuckGo API: https://pypi.org/project/duckduckgo-search/ \n",
222 | "And for our Currency Conversion, we'll be using the Free Currency Exchange API: https://github.com/fawazahmed0/exchange-api"
223 | ]
224 | },
225 | {
226 | "cell_type": "code",
227 | "execution_count": 12,
228 | "id": "3eb061eb-4fe8-47b8-9e50-6d386108646d",
229 | "metadata": {},
230 | "outputs": [],
231 | "source": [
232 | "from duckduckgo_search import DDGS\n",
233 | "import requests\n",
234 | "\n",
235 | "def search_internet(search_query: str) -> list:\n",
236 | " results = DDGS().text(str(search_query), max_results=5)\n",
237 | " return results\n",
238 | "\n",
239 | "def get_exchange_rate(base_currency: str, target_currency: str, date: str = \"latest\") -> float:\n",
240 | " \n",
241 | " url = f\"https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@{date}/v1/currencies/{base_currency.lower()}.json\"\n",
242 | " response = requests.get(url)\n",
243 | " \n",
244 | " if response.status_code == 200:\n",
245 | " data = response.json()\n",
246 | " return data.get(base_currency.lower(), {}).get(target_currency.lower(), None)\n",
247 | " else:\n",
248 | " raise Exception(f\"Failed to fetch exchange rate: {response.status_code}\")"
249 | ]
250 | },
251 | {
252 | "cell_type": "code",
253 | "execution_count": 13,
254 | "id": "fe7b6b8d-cc3a-40a9-b5fe-8d9d35c7e811",
255 | "metadata": {},
256 | "outputs": [
257 | {
258 | "name": "stdout",
259 | "output_type": "stream",
260 | "text": [
261 | "\n",
262 | "Initial Message: [{'role': 'user', 'content': 'How much is a dollar worth in Japan? How about poland? Whats the current news in Argentina?'}]\n",
263 | "\n",
264 | "Response Message: ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_T4noNrQKJTHL9rWyfKBz5pil', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_fzPLFXAH57FU0H04tpM36ay8', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"PLN\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_TiDiLVR6PqzK50ZnwCs6CsEu', function=Function(arguments='{\"search_query\": \"current news in Argentina\"}', name='search_internet'), type='function')])\n",
265 | "\n",
266 | "Tool Calls: [ChatCompletionMessageToolCall(id='call_T4noNrQKJTHL9rWyfKBz5pil', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_fzPLFXAH57FU0H04tpM36ay8', function=Function(arguments='{\"base_currency\": \"USD\", \"target_currency\": \"PLN\"}', name='get_exchange_rate'), type='function'), ChatCompletionMessageToolCall(id='call_TiDiLVR6PqzK50ZnwCs6CsEu', function=Function(arguments='{\"search_query\": \"current news in Argentina\"}', name='search_internet'), type='function')]\n",
267 | "\n",
268 | "Calling with arguments {'base_currency': 'USD', 'target_currency': 'JPY', 'date': 'latest'}\n",
269 | "\n",
270 | "Function Response: 158.51506391\n",
271 | "\n",
272 | "Appending Message: {'tool_call_id': 'call_T4noNrQKJTHL9rWyfKBz5pil', 'role': 'tool', 'name': 'get_exchange_rate', 'content': '158.51506391'}\n",
273 | "\n",
274 | "Calling with arguments {'base_currency': 'USD', 'target_currency': 'PLN', 'date': 'latest'}\n",
275 | "\n",
276 | "Function Response: 3.90821251\n",
277 | "\n",
278 | "Appending Message: {'tool_call_id': 'call_fzPLFXAH57FU0H04tpM36ay8', 'role': 'tool', 'name': 'get_exchange_rate', 'content': '3.90821251'}\n",
279 | "\n",
280 | "Calling with arguments {'search_query': 'current news in Argentina'}\n",
281 | "\n",
282 | "Function Response: [{'title': \"Argentina | Today's latest from Al Jazeera\", 'href': 'https://www.aljazeera.com/where/argentina', 'body': \"Stay on top of Argentina latest developments on the ground with Al Jazeera's fact-based news, exclusive video footage, photos and updated maps.\"}, {'title': 'News from Argentina and beyond - in English | Buenos Aires Times', 'href': 'https://www.batimes.com.ar/', 'body': \"Homepage for the Buenos Aires Times, Argentina's only English-language newspaper. News from Argentina and beyond - in English. News, analysis, politics, finance, economy, culture, sports - and much, much more! ... The last three years have been ones to remember for this Argentina team and with this latest victory, they are surely the ...\"}, {'title': 'Argentina | AP News', 'href': 'https://apnews.com/hub/argentina', 'body': \"President Javier Milei of Argentina, who owns four clones of his dead dog Conan, has never been the most conventional occupant of Argentina's highest office. Stay informed and read the latest breaking news and updates on Argentina from AP News, the definitive source for independent journalism.\"}, {'title': 'Argentina | Buenos Aires Times', 'href': 'https://www.batimes.com.ar/section/argentina', 'body': \"News of argentina. Homepage for the Buenos Aires Times, Argentina's only English-language newspaper. News from Argentina and beyond - in English. ... 06-07-2024 05:23 US Ambassador Marc Stanley pays tributes to Argentine politicians from previous and current governments as he welcomes more than 1,000 guests to the Palacio Bosch for annual ...\"}, {'title': 'Argentina - BBC News', 'href': 'https://www.bbc.com/news/topics/c77jz3mdm3rt', 'body': \"Important to get winning start - Messi. Lionel Messi reflects on the importance of a winning start after Argentina's 2-0 win against Canada in the opening game of Copa America. All the latest ...\"}]\n",
283 | "\n",
284 | "Appending Message: {'tool_call_id': 'call_TiDiLVR6PqzK50ZnwCs6CsEu', 'role': 'tool', 'name': 'search_internet', 'content': '[{\\'title\\': \"Argentina | Today\\'s latest from Al Jazeera\", \\'href\\': \\'https://www.aljazeera.com/where/argentina\\', \\'body\\': \"Stay on top of Argentina latest developments on the ground with Al Jazeera\\'s fact-based news, exclusive video footage, photos and updated maps.\"}, {\\'title\\': \\'News from Argentina and beyond - in English | Buenos Aires Times\\', \\'href\\': \\'https://www.batimes.com.ar/\\', \\'body\\': \"Homepage for the Buenos Aires Times, Argentina\\'s only English-language newspaper. News from Argentina and beyond - in English. News, analysis, politics, finance, economy, culture, sports - and much, much more! ... The last three years have been ones to remember for this Argentina team and with this latest victory, they are surely the ...\"}, {\\'title\\': \\'Argentina | AP News\\', \\'href\\': \\'https://apnews.com/hub/argentina\\', \\'body\\': \"President Javier Milei of Argentina, who owns four clones of his dead dog Conan, has never been the most conventional occupant of Argentina\\'s highest office. Stay informed and read the latest breaking news and updates on Argentina from AP News, the definitive source for independent journalism.\"}, {\\'title\\': \\'Argentina | Buenos Aires Times\\', \\'href\\': \\'https://www.batimes.com.ar/section/argentina\\', \\'body\\': \"News of argentina. Homepage for the Buenos Aires Times, Argentina\\'s only English-language newspaper. News from Argentina and beyond - in English. ... 06-07-2024 05:23 US Ambassador Marc Stanley pays tributes to Argentine politicians from previous and current governments as he welcomes more than 1,000 guests to the Palacio Bosch for annual ...\"}, {\\'title\\': \\'Argentina - BBC News\\', \\'href\\': \\'https://www.bbc.com/news/topics/c77jz3mdm3rt\\', \\'body\\': \"Important to get winning start - Messi. Lionel Messi reflects on the importance of a winning start after Argentina\\'s 2-0 win against Canada in the opening game of Copa America. All the latest ...\"}]'}\n",
285 | "\n",
286 | "LLM Response: ChatCompletion(id='chatcmpl-9ljfqqMxBlTEynOqEYVtHvyYJBUSh', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='### Exchange Rates:\\n1. **USD to JPY (Japan):** 1 USD is approximately 158.52 JPY.\\n2. **USD to PLN (Poland):** 1 USD is approximately 3.91 PLN.\\n\\n### Current News in Argentina:\\nHere are some sources where you can find the latest news about Argentina:\\n\\n1. [**Al Jazeera - Argentina**](https://www.aljazeera.com/where/argentina)\\n - Provides fact-based news, exclusive video footage, photos, and updated maps on developments in Argentina.\\n\\n2. [**Buenos Aires Times**](https://www.batimes.com.ar/)\\n - The only English-language newspaper in Argentina. Covers news from Argentina and beyond, including analysis in politics, finance, economy, culture, and sports.\\n\\n3. [**AP News - Argentina**](https://apnews.com/hub/argentina)\\n - Offers the latest breaking news and updates on Argentina, covering various topics including politics and current events.\\n\\n4. [**BBC News - Argentina**](https://www.bbc.com/news/topics/c77jz3mdm3rt)\\n - Features news articles and updates on important events and personalities, such as sports insights from Lionel Messi.\\n\\nFeel free to explore these sources for more in-depth and up-to-date information.', role='assistant', function_call=None, tool_calls=None))], created=1721164014, model='gpt-4o-2024-05-13', object='chat.completion', system_fingerprint='fp_5e997b69d8', usage=CompletionUsage(completion_tokens=270, prompt_tokens=563, total_tokens=833))\n",
287 | "\n",
288 | "---Formatted LLM Response---\n",
289 | "\n",
290 | " ### Exchange Rates:\n",
291 | "1. **USD to JPY (Japan):** 1 USD is approximately 158.52 JPY.\n",
292 | "2. **USD to PLN (Poland):** 1 USD is approximately 3.91 PLN.\n",
293 | "\n",
294 | "### Current News in Argentina:\n",
295 | "Here are some sources where you can find the latest news about Argentina:\n",
296 | "\n",
297 | "1. [**Al Jazeera - Argentina**](https://www.aljazeera.com/where/argentina)\n",
298 | " - Provides fact-based news, exclusive video footage, photos, and updated maps on developments in Argentina.\n",
299 | "\n",
300 | "2. [**Buenos Aires Times**](https://www.batimes.com.ar/)\n",
301 | " - The only English-language newspaper in Argentina. Covers news from Argentina and beyond, including analysis in politics, finance, economy, culture, and sports.\n",
302 | "\n",
303 | "3. [**AP News - Argentina**](https://apnews.com/hub/argentina)\n",
304 | " - Offers the latest breaking news and updates on Argentina, covering various topics including politics and current events.\n",
305 | "\n",
306 | "4. [**BBC News - Argentina**](https://www.bbc.com/news/topics/c77jz3mdm3rt)\n",
307 | " - Features news articles and updates on important events and personalities, such as sports insights from Lionel Messi.\n",
308 | "\n",
309 | "Feel free to explore these sources for more in-depth and up-to-date information.\n"
310 | ]
311 | }
312 | ],
313 | "source": [
314 | "import inspect\n",
315 | "\n",
316 | "# Main conversation function\n",
317 | "def run_conversation(prompt, tools, tool_choice = \"auto\"):\n",
318 | " \n",
319 | " messages = [{\"role\": \"user\", \"content\": prompt}]\n",
320 | " \n",
321 | " print(\"\\nInitial Message: \", messages)\n",
322 | " \n",
323 | " # Send the conversation and available functions to the model\n",
324 | " response = client.chat.completions.create(\n",
325 | " model=\"gpt-4o\",\n",
326 | " messages=messages,\n",
327 | " tools=tools,\n",
328 | " tool_choice=tool_choice,\n",
329 | " )\n",
330 | " response_message = response.choices[0].message\n",
331 | " print(\"\\nResponse Message: \", response_message)\n",
332 | " \n",
333 | " tool_calls = response_message.tool_calls\n",
334 | " print(\"\\nTool Calls: \", tool_calls)\n",
335 | " \n",
336 | " # Check if the model wanted to call a function\n",
337 | " if tool_calls:\n",
338 | " \n",
339 | " # Call the functions\n",
340 | " available_functions = {\n",
341 | " \"get_exchange_rate\": get_exchange_rate,\n",
342 | " \"search_internet\": search_internet,\n",
343 | " } \n",
344 | " # extend conversation with assistant's reply\n",
345 | " messages.append(response_message)\n",
346 | " \n",
347 | " # Call the function and add the response\n",
348 | " for tool_call in tool_calls:\n",
349 | " function_name = tool_call.function.name\n",
350 | " function_to_call = available_functions[function_name]\n",
351 | " function_args = json.loads(tool_call.function.arguments)\n",
352 | " \n",
353 | " \n",
354 | " # Get the function signature and call the function with given arguments\n",
355 | " sig = inspect.signature(function_to_call)\n",
356 | " call_args = {\n",
357 | " k: function_args.get(k, v.default)\n",
358 | " for k, v in sig.parameters.items()\n",
359 | " if k in function_args or v.default is not inspect.Parameter.empty\n",
360 | " }\n",
361 | " print(f\"\\nCalling {function_to_call} with arguments {call_args}\")\n",
362 | " \n",
363 | " function_response = str(function_to_call(**call_args))\n",
364 | " \n",
365 | " print(\"\\nFunction Response: \", function_response)\n",
366 | "\n",
367 | " # Put output into a tool message\n",
368 | " tool_message = {\n",
369 | " \"tool_call_id\": tool_call.id, # Needed for Parallel Tool Calling\n",
370 | " \"role\": \"tool\",\n",
371 | " \"name\": function_name,\n",
372 | " \"content\": function_response,\n",
373 | " }\n",
374 | " print(\"\\nAppending Message: \", tool_message)\n",
375 | " \n",
376 | " # Extend conversation with function response\n",
377 | " messages.append(tool_message) \n",
378 | "\n",
379 | " # Get a new response from the model where it can see the entire conversation including the function call outputs\n",
380 | " second_response = client.chat.completions.create(\n",
381 | " model=\"gpt-4o\",\n",
382 | " messages=messages,\n",
383 | " ) \n",
384 | "\n",
385 | " print(\"\\nLLM Response: \", second_response)\n",
386 | "\n",
387 | " print(\"\\n---Formatted LLM Response---\")\n",
388 | " print(\"\\n\",second_response.choices[0].message.content)\n",
389 | " \n",
390 | " return\n",
391 | "\n",
392 | "prompt = \"How much is a dollar worth in Japan? How about poland? Whats the current news in Argentina?\"\n",
393 | "\n",
394 | "run_conversation(prompt, first_tools)"
395 | ]
396 | },
397 | {
398 | "cell_type": "markdown",
399 | "id": "f4a810b4-3119-4555-88c6-78be70346fdd",
400 | "metadata": {},
401 | "source": [
402 | "---\n",
403 | "## Function Calling Behavior \n",
404 | "\n",
405 | "The default behavior for `tool_choice` is `tool_choice: \"auto\"`. This lets the model decide whether to call functions and, if so, which functions to call.\n",
406 | "\n",
407 | "Three ways to customize the default behavior depending on your use case:\n",
408 | "\n",
409 | "1. To force the model to always call one or more functions, you can set `tool_choice: \"required\"`. The model will then select which function(s) to call.\n",
410 | "2. To force the model to call only one specific function, you can set `tool_choice: {\"type\": \"function\", \"function\": {\"name\": \"my_function\"}}`.\n",
411 | "3. To disable function calling and force the model to only generate a user-facing message, you can set `tool_choice: \"none\"`.\n",
412 | "\n",
413 | "Additionally, there exists the option for **Parallel Function Calling**\n",
414 | "\n",
415 | "Parallel function calling is the model's ability to perform multiple function calls together, allowing the effects and results of these function calls to be resolved in parallel. This is especially useful if functions take a long time, and reduces round trips with the API. For example, the model may call functions to get the weather in 3 different locations at the same time, which will result in a message with 3 function calls in the tool_calls array, each with an id. To respond to these function calls, add 3 new messages to the conversation, each containing the result of one function call, with a tool_call_id referencing the id from tool_calls.\n",
416 | "- Parallel function calling can be disabled by passing `parallel_tool_calls: false` in the request. The model will only call one function at a time when parallel function calling is disabled.\n"
417 | ]
418 | },
419 | {
420 | "cell_type": "markdown",
421 | "id": "baf30de2-bf34-42eb-9942-d6a85cea14b4",
422 | "metadata": {},
423 | "source": [
424 | "---\n",
425 | "## More Advanced Functions\n",
426 | "\n",
427 | "Those were relatively simple examples, let's look at setting up more advanced functions.\n",
428 | "\n",
429 | "An easy way to manage more complicated and advanced function properties are with [Pydantic JSON Schemas](https://docs.pydantic.dev/latest/concepts/json_schema/), as demonstrated below."
430 | ]
431 | },
432 | {
433 | "cell_type": "code",
434 | "execution_count": 14,
435 | "id": "5cd8f95b-bf88-4e87-b502-1cbc90fa4430",
436 | "metadata": {},
437 | "outputs": [],
438 | "source": [
439 | "from pydantic import BaseModel, Field, HttpUrl\n",
440 | "from typing import List, Optional, Dict\n",
441 | "from datetime import datetime\n",
442 | "\n",
443 | "# Defining the tool properties with Pydantic\n",
444 | "class SendEmailCampaign(BaseModel):\n",
445 | " recipients: List[str] = Field(\n",
446 | " ..., \n",
447 | " description=\"List of strings, each an email address. Example: ['example1@mail.com', 'example2@mail.com']\"\n",
448 | " )\n",
449 | " subject: str = Field(\n",
450 | " ..., \n",
451 | " description=\"String specifying the email's subject line. Example: 'Exciting News!'\"\n",
452 | " )\n",
453 | " body_text: str = Field(\n",
454 | " ..., \n",
455 | " description=\"Plain text content of the email body. Example: 'We have some exciting updates to share with you.'\"\n",
456 | " )\n",
457 | " attachments: Optional[List[HttpUrl]] = Field(\n",
458 | " default=[], \n",
459 | " description=\"List of URLs to attachment files. Example: ['http://example.com/attachment1.pdf', 'http://example.com/attachment2.png']\"\n",
460 | " )\n",
461 | " personalization: Optional[Dict[str, Dict[str, str]]] = Field(\n",
462 | " default={}, \n",
463 | " description=\"Dictionary for personalizing email content. Key is recipient email, value is a dictionary of variables. Example: {'example1@mail.com': {'first_name': 'John'}, 'example2@mail.com': {'first_name': 'Jane'}}\"\n",
464 | " )\n",
465 | " send_time: Optional[datetime] = Field(\n",
466 | " None, \n",
467 | " description=\"The time when the email campaign is to be sent. Example: '2024-07-13T14:30:00'\"\n",
468 | " )\n",
469 | " priority: Optional[str] = Field(\n",
470 | " default=\"normal\", \n",
471 | " description=\"Email priority level. Options: 'low', 'normal', 'high'. Example: 'high'\", \n",
472 | " enum=[\"low\", \"normal\", \"high\"]\n",
473 | " )\n",
474 | " tracking: Optional[Dict[str, bool]] = Field(\n",
475 | " default={\"open\": True, \"click\": True}, \n",
476 | " description=\"Tracking options for the email. Keys can be 'open' and 'click', values are booleans. Example: {'open': True, 'click': True}\"\n",
477 | " )\n",
478 | " campaign_id: Optional[str] = Field(\n",
479 | " None, \n",
480 | " description=\"Unique identifier for the email campaign for tracking purposes. Example: 'campaign_12345'\"\n",
481 | " )\n",
482 | "\n",
483 | "# Creating the Tool with PyDantic Schema\n",
484 | "email_campaign_tool = [\n",
485 | " {\n",
486 | " \"type\": \"function\",\n",
487 | " \"function\": {\n",
488 | " \"name\": \"send_email_campaign\",\n",
489 | " \"description\": \"Send out a marketing email campaign\",\n",
490 | " \"parameters\": SendEmailCampaign.schema(), # Passing in our Pydantic schema\n",
491 | " },\n",
492 | " }\n",
493 | "]\n"
494 | ]
495 | },
496 | {
497 | "cell_type": "code",
498 | "execution_count": 17,
499 | "id": "17e3cf61-fe7c-4e1d-8dc4-0d387c705de5",
500 | "metadata": {},
501 | "outputs": [
502 | {
503 | "data": {
504 | "text/plain": [
505 | "[{'type': 'function',\n",
506 | " 'function': {'name': 'send_email_campaign',\n",
507 | " 'description': 'Send out a marketing email campaign',\n",
508 | " 'parameters': {'properties': {'recipients': {'description': \"List of strings, each an email address. Example: ['example1@mail.com', 'example2@mail.com']\",\n",
509 | " 'items': {'type': 'string'},\n",
510 | " 'title': 'Recipients',\n",
511 | " 'type': 'array'},\n",
512 | " 'subject': {'description': \"String specifying the email's subject line. Example: 'Exciting News!'\",\n",
513 | " 'title': 'Subject',\n",
514 | " 'type': 'string'},\n",
515 | " 'body_text': {'description': \"Plain text content of the email body. Example: 'We have some exciting updates to share with you.'\",\n",
516 | " 'title': 'Body Text',\n",
517 | " 'type': 'string'},\n",
518 | " 'attachments': {'anyOf': [{'items': {'format': 'uri',\n",
519 | " 'maxLength': 2083,\n",
520 | " 'minLength': 1,\n",
521 | " 'type': 'string'},\n",
522 | " 'type': 'array'},\n",
523 | " {'type': 'null'}],\n",
524 | " 'default': [],\n",
525 | " 'description': \"List of URLs to attachment files. Example: ['http://example.com/attachment1.pdf', 'http://example.com/attachment2.png']\",\n",
526 | " 'title': 'Attachments'},\n",
527 | " 'personalization': {'anyOf': [{'additionalProperties': {'additionalProperties': {'type': 'string'},\n",
528 | " 'type': 'object'},\n",
529 | " 'type': 'object'},\n",
530 | " {'type': 'null'}],\n",
531 | " 'default': {},\n",
532 | " 'description': \"Dictionary for personalizing email content. Key is recipient email, value is a dictionary of variables. Example: {'example1@mail.com': {'first_name': 'John'}, 'example2@mail.com': {'first_name': 'Jane'}}\",\n",
533 | " 'title': 'Personalization'},\n",
534 | " 'send_time': {'anyOf': [{'format': 'date-time', 'type': 'string'},\n",
535 | " {'type': 'null'}],\n",
536 | " 'default': None,\n",
537 | " 'description': \"The time when the email campaign is to be sent. Example: '2024-07-13T14:30:00'\",\n",
538 | " 'title': 'Send Time'},\n",
539 | " 'priority': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
540 | " 'default': 'normal',\n",
541 | " 'description': \"Email priority level. Options: 'low', 'normal', 'high'. Example: 'high'\",\n",
542 | " 'enum': ['low', 'normal', 'high'],\n",
543 | " 'title': 'Priority'},\n",
544 | " 'tracking': {'anyOf': [{'additionalProperties': {'type': 'boolean'},\n",
545 | " 'type': 'object'},\n",
546 | " {'type': 'null'}],\n",
547 | " 'default': {'open': True, 'click': True},\n",
548 | " 'description': \"Tracking options for the email. Keys can be 'open' and 'click', values are booleans. Example: {'open': True, 'click': True}\",\n",
549 | " 'title': 'Tracking'},\n",
550 | " 'campaign_id': {'anyOf': [{'type': 'string'}, {'type': 'null'}],\n",
551 | " 'default': None,\n",
552 | " 'description': \"Unique identifier for the email campaign for tracking purposes. Example: 'campaign_12345'\",\n",
553 | " 'title': 'Campaign Id'}},\n",
554 | " 'required': ['recipients', 'subject', 'body_text'],\n",
555 | " 'title': 'SendEmailCampaign',\n",
556 | " 'type': 'object'}}}]"
557 | ]
558 | },
559 | "execution_count": 17,
560 | "metadata": {},
561 | "output_type": "execute_result"
562 | }
563 | ],
564 | "source": [
565 | "email_campaign_tool"
566 | ]
567 | },
568 | {
569 | "cell_type": "markdown",
570 | "id": "2432e3fd-4bfb-4832-8dd8-6e2c34cd4d40",
571 | "metadata": {},
572 | "source": [
573 | "And trying it out:"
574 | ]
575 | },
576 | {
577 | "cell_type": "code",
578 | "execution_count": 18,
579 | "id": "8f913888-3c59-4d8d-8fcf-a36e398b4bfd",
580 | "metadata": {},
581 | "outputs": [],
582 | "source": [
583 | "prompt = \"\"\"\n",
584 | "\"Can you send an email to adamlucek@youtube.com, samaltman@openai.com, and elonmusk@twitter.com about the cool youtube channel https://www.youtube.com/@AdamLucek \n",
585 | "for July 20th at 8 am pst, this is high priority and include just click tracking and name personalization, \n",
586 | "the campaign is campaign_13, \n",
587 | "also add my linkedin as an attachment https://www.linkedin.com/in/adamrlucek/,\n",
588 | "Sign off your response with Adam Ł in the body.\n",
589 | "\"\"\"\n",
590 | "\n",
591 | "messages = [{\"role\": \"user\", \n",
592 | " \"content\": prompt}]\n",
593 | "\n",
594 | "\n",
595 | "response = client.chat.completions.create(\n",
596 | " model=\"gpt-4o\",\n",
597 | " messages=messages,\n",
598 | " tools=email_campaign_tool,\n",
599 | " tool_choice=\"auto\", # auto is default, but we'll be explicit\n",
600 | " )"
601 | ]
602 | },
603 | {
604 | "cell_type": "code",
605 | "execution_count": 19,
606 | "id": "00f4a484-ae43-490c-b3e7-955babdbac74",
607 | "metadata": {},
608 | "outputs": [
609 | {
610 | "name": "stdout",
611 | "output_type": "stream",
612 | "text": [
613 | "--- Full Response ---\n",
614 | "\n",
615 | "ChatCompletion(id='chatcmpl-9ljvcOfHyOD8K5uxBVjTKZF56Dd5F', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_lFaVbiJ2EC93zu1k7vfzG5j1', function=Function(arguments='{\"recipients\": [\"adamlucek@youtube.com\", \"samaltman@openai.com\", \"elonmusk@twitter.com\"], \"subject\": \"Check Out This Cool YouTube Channel!\", \"body_text\": \"Hello,\\\\n\\\\nI wanted to share an amazing YouTube channel that I think you\\'ll love: https://www.youtube.com/@AdamLucek.\\\\n\\\\nBest Regards,\\\\nAdam Ł\", \"attachments\": [\"https://www.linkedin.com/in/adamrlucek/\"], \"personalization\": {\"adamlucek@youtube.com\": {\"first_name\": \"Adam\"}, \"samaltman@openai.com\": {\"first_name\": \"Sam\"}, \"elonmusk@twitter.com\": {\"first_name\": \"Elon\"}}, \"send_time\": \"2023-07-20T08:00:00-08:00\", \"priority\": \"high\", \"tracking\": {\"click\": true}, \"campaign_id\": \"campaign_13\"}', name='send_email_campaign'), type='function')]))], created=1721164992, model='gpt-4o-2024-05-13', object='chat.completion', system_fingerprint='fp_c4e5b6fa31', usage=CompletionUsage(completion_tokens=203, prompt_tokens=553, total_tokens=756)) \n",
616 | "\n",
617 | "--- Chat Completion Message ---\n",
618 | "\n",
619 | "ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_lFaVbiJ2EC93zu1k7vfzG5j1', function=Function(arguments='{\"recipients\": [\"adamlucek@youtube.com\", \"samaltman@openai.com\", \"elonmusk@twitter.com\"], \"subject\": \"Check Out This Cool YouTube Channel!\", \"body_text\": \"Hello,\\\\n\\\\nI wanted to share an amazing YouTube channel that I think you\\'ll love: https://www.youtube.com/@AdamLucek.\\\\n\\\\nBest Regards,\\\\nAdam Ł\", \"attachments\": [\"https://www.linkedin.com/in/adamrlucek/\"], \"personalization\": {\"adamlucek@youtube.com\": {\"first_name\": \"Adam\"}, \"samaltman@openai.com\": {\"first_name\": \"Sam\"}, \"elonmusk@twitter.com\": {\"first_name\": \"Elon\"}}, \"send_time\": \"2023-07-20T08:00:00-08:00\", \"priority\": \"high\", \"tracking\": {\"click\": true}, \"campaign_id\": \"campaign_13\"}', name='send_email_campaign'), type='function')]) \n",
620 | "\n",
621 | "--- Tool Call 1 ---\n",
622 | "\n",
623 | "Function: send_email_campaign\n",
624 | "\n",
625 | "Arguments: {\"recipients\": [\"adamlucek@youtube.com\", \"samaltman@openai.com\", \"elonmusk@twitter.com\"], \"subject\": \"Check Out This Cool YouTube Channel!\", \"body_text\": \"Hello,\\n\\nI wanted to share an amazing YouTube channel that I think you'll love: https://www.youtube.com/@AdamLucek.\\n\\nBest Regards,\\nAdam Ł\", \"attachments\": [\"https://www.linkedin.com/in/adamrlucek/\"], \"personalization\": {\"adamlucek@youtube.com\": {\"first_name\": \"Adam\"}, \"samaltman@openai.com\": {\"first_name\": \"Sam\"}, \"elonmusk@twitter.com\": {\"first_name\": \"Elon\"}}, \"send_time\": \"2023-07-20T08:00:00-08:00\", \"priority\": \"high\", \"tracking\": {\"click\": true}, \"campaign_id\": \"campaign_13\"}\n",
626 | "\n"
627 | ]
628 | }
629 | ],
630 | "source": [
631 | "pprint_response(response)"
632 | ]
633 | },
634 | {
635 | "cell_type": "code",
636 | "execution_count": 20,
637 | "id": "ff954bb1-d274-490f-8702-7f1cb5f8b021",
638 | "metadata": {},
639 | "outputs": [
640 | {
641 | "name": "stdout",
642 | "output_type": "stream",
643 | "text": [
644 | "\n",
645 | "Sending email campaign 'campaign_13' with priority 'high':\n",
646 | "-----\n",
647 | "\n",
648 | "To: adamlucek@youtube.com\n",
649 | "Subject: Adam, Check Out This Cool YouTube Channel!\n",
650 | "Body: Hello,\n",
651 | "\n",
652 | "I wanted to share an amazing YouTube channel that I think you'll love: https://www.youtube.com/@AdamLucek.\n",
653 | "\n",
654 | "Best Regards,\n",
655 | "Adam Ł\n",
656 | "\tAttachments: https://www.linkedin.com/in/adamrlucek/\n",
657 | "-----\n",
658 | "\n",
659 | "To: samaltman@openai.com\n",
660 | "Subject: Sam, Check Out This Cool YouTube Channel!\n",
661 | "Body: Hello,\n",
662 | "\n",
663 | "I wanted to share an amazing YouTube channel that I think you'll love: https://www.youtube.com/@AdamLucek.\n",
664 | "\n",
665 | "Best Regards,\n",
666 | "Adam Ł\n",
667 | "\tAttachments: https://www.linkedin.com/in/adamrlucek/\n",
668 | "-----\n",
669 | "\n",
670 | "To: elonmusk@twitter.com\n",
671 | "Subject: Elon, Check Out This Cool YouTube Channel!\n",
672 | "Body: Hello,\n",
673 | "\n",
674 | "I wanted to share an amazing YouTube channel that I think you'll love: https://www.youtube.com/@AdamLucek.\n",
675 | "\n",
676 | "Best Regards,\n",
677 | "Adam Ł\n",
678 | "\tAttachments: https://www.linkedin.com/in/adamrlucek/\n",
679 | "-----\n",
680 | "\n",
681 | "Scheduled Send Time: 2023-07-20 08:00:00-08:00\n",
682 | "Campaign ID: campaign_13\n",
683 | "Tracking: {'click': True}\n"
684 | ]
685 | }
686 | ],
687 | "source": [
688 | "# Example function of sending off an Email Campaign\n",
689 | "def send_email_campaign(data: dict):\n",
690 | " try:\n",
691 | " # Validate and parse the data using the SendEmailCampaign model\n",
692 | " campaign = SendEmailCampaign(**data)\n",
693 | " \n",
694 | " # Simulate sending the email campaign\n",
695 | " print(f\"\\nSending email campaign '{campaign.campaign_id or 'N/A'}' with priority '{campaign.priority}':\")\n",
696 | " for recipient in campaign.recipients:\n",
697 | " personalized_subject = campaign.subject\n",
698 | " if recipient in campaign.personalization:\n",
699 | " if 'first_name' in campaign.personalization[recipient]:\n",
700 | " personalized_subject = f\"{campaign.personalization[recipient]['first_name']}, {campaign.subject}\"\n",
701 | " \n",
702 | " print(\"-----\")\n",
703 | " print(f\"\\nTo: {recipient}\")\n",
704 | " print(f\"Subject: {personalized_subject}\")\n",
705 | " print(f\"Body: {campaign.body_text}\")\n",
706 | " if campaign.attachments:\n",
707 | " attachment_urls = [str(url) for url in campaign.attachments]\n",
708 | " print(f\"\\tAttachments: {', '.join(attachment_urls)}\")\n",
709 | "\n",
710 | " print(\"-----\")\n",
711 | " print(f\"\\nScheduled Send Time: {campaign.send_time or 'Immediate'}\")\n",
712 | " print(f\"Campaign ID: {campaign.campaign_id or 'N/A'}\")\n",
713 | " print(f\"Tracking: {campaign.tracking}\")\n",
714 | " except Exception as e:\n",
715 | " print(f\"Failed to send email campaign: {e}\")\n",
716 | "\n",
717 | "args = json.loads(response.choices[0].message.tool_calls[0].function.arguments)\n",
718 | "send_email_campaign(args)"
719 | ]
720 | },
721 | {
722 | "cell_type": "markdown",
723 | "id": "5db571a0-74f9-48fc-9e15-7ac7e87fb73f",
724 | "metadata": {},
725 | "source": [
726 | "\n",
727 | "\n",
728 | "**One thing to note**, if you have too many functions or your functions are very complicated, it's possible to do further [fine-tuning](https://platform.openai.com/docs/guides/fine-tuning/fine-tuning-examples) to address this"
729 | ]
730 | },
731 | {
732 | "cell_type": "markdown",
733 | "id": "1758ae9c-72dc-4674-a83d-07ce9cde401e",
734 | "metadata": {},
735 | "source": [
736 | "---\n",
737 | "# Other Models w/LangChain\n",
738 | "\n",
739 | "Function calling with OpenAI models is cool, but let's also show what other models support function calling, and how to easily format your tools for different LLMs with LangChain's [integrations](https://python.langchain.com/v0.1/docs/modules/model_io/chat/function_calling/)\n",
740 | "\n",
741 | "Here are some common providers and their specific model function calling documentation: [OpenAI](https://platform.openai.com/docs/guides/function-calling), [Anthropic](https://docs.anthropic.com/en/docs/build-with-claude/tool-use), [Google](https://cloud.google.com/vertex-ai/generative-ai/docs/multimodal/function-calling), [Cohere](https://docs.cohere.com/docs/tools), [Fireworks.ai](https://fireworks.ai/blog/firefunction-v1-gpt-4-level-function-calling), [Groq](https://console.groq.com/docs/tool-use), [Mistral](https://docs.mistral.ai/capabilities/function_calling/), [Together.ai](https://docs.together.ai/docs/function-calling)\n",
742 | "\n",
743 | "Langchain makes it very easy to plug and play with function calling using compatible LLMs with their standardized message and tool framework"
744 | ]
745 | },
746 | {
747 | "cell_type": "code",
748 | "execution_count": 21,
749 | "id": "f86d85cc-95c4-47aa-9a0f-e442d72efe1b",
750 | "metadata": {},
751 | "outputs": [],
752 | "source": [
753 | "from langchain_anthropic import ChatAnthropic\n",
754 | "\n",
755 | "# Claude 3.5 Sonnet\n",
756 | "llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0.7)"
757 | ]
758 | },
759 | {
760 | "cell_type": "markdown",
761 | "id": "c9dcebb3-e9d8-474f-b902-8e89017c8fc6",
762 | "metadata": {},
763 | "source": [
764 | "### Defining Tools\n",
765 | "\n",
766 | "LangChain offers multiple ways of defining tools, you can:\n",
767 | "* Use the `@tool` decorator along with a description on an exisiting function\n",
768 | "* Use Pydantic schemas to define tool arguments\n",
769 | "* Use [built in tools](https://python.langchain.com/v0.1/docs/integrations/tools/) as part of LangChain's integrations\n",
770 | "* Use a combination of all above methods to define [custom tools](https://python.langchain.com/v0.1/docs/modules/tools/custom_tools/) from LangChain's BaseTool class"
771 | ]
772 | },
773 | {
774 | "cell_type": "code",
775 | "execution_count": 23,
776 | "id": "da35bfcc-c8c0-459d-9419-2db523f8093b",
777 | "metadata": {},
778 | "outputs": [],
779 | "source": [
780 | "from langchain_core.tools import tool\n",
781 | "\n",
782 | "# Using the tool decorator to attach onto an existing function\n",
783 | "@tool\n",
784 | "def get_exchange_rate(base_currency: str, target_currency: str, date: str = \"latest\") -> float:\n",
785 | " \"\"\"Get the latest exchange rate between two currency. Date defaults latest if not provided.\"\"\"\n",
786 | " url = f\"https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/{base_currency.lower()}.json\"\n",
787 | " response = requests.get(url)\n",
788 | " \n",
789 | " if response.status_code == 200:\n",
790 | " data = response.json()\n",
791 | " return data.get(base_currency.lower(), {}).get(target_currency.lower(), None)\n",
792 | " else:\n",
793 | " raise Exception(f\"Failed to fetch exchange rate: {response.status_code}\")\n",
794 | "\n",
795 | "\n",
796 | "# Using Pydantic schemas (non-invokable tool)\n",
797 | "from langchain_core.pydantic_v1 import BaseModel, Field\n",
798 | "\n",
799 | "class SendEmailCampaignLC(BaseModel):\n",
800 | " \"Send a marketing email campaign out to your mailing list\"\n",
801 | " \n",
802 | " recipients: List[str] = Field(\n",
803 | " ..., \n",
804 | " description=\"List of strings, each an email address. Example: ['example1@mail.com', 'example2@mail.com']\"\n",
805 | " )\n",
806 | " subject: str = Field(\n",
807 | " ..., \n",
808 | " description=\"String specifying the email's subject line. Example: 'Exciting News!'\"\n",
809 | " )\n",
810 | " body_text: str = Field(\n",
811 | " ..., \n",
812 | " description=\"Plain text content of the email body. Example: 'We have some exciting updates to share with you.'\"\n",
813 | " )\n",
814 | "\n",
815 | "# Using built in LangChain tool integrations\n",
816 | "from langchain_community.utilities import DuckDuckGoSearchAPIWrapper\n",
817 | "from langchain_community.tools import DuckDuckGoSearchResults\n",
818 | "\n",
819 | "wrapper = DuckDuckGoSearchAPIWrapper(max_results=10)\n",
820 | "web_search = DuckDuckGoSearchResults(api_wrapper=wrapper)"
821 | ]
822 | },
823 | {
824 | "cell_type": "code",
825 | "execution_count": 24,
826 | "id": "97554b0e-b816-42bf-a67d-6325433aaf28",
827 | "metadata": {},
828 | "outputs": [
829 | {
830 | "data": {
831 | "text/plain": [
832 | "158.51506391"
833 | ]
834 | },
835 | "execution_count": 24,
836 | "metadata": {},
837 | "output_type": "execute_result"
838 | }
839 | ],
840 | "source": [
841 | "get_exchange_rate.invoke({\"base_currency\": \"usd\", \"target_currency\": \"jpy\"})"
842 | ]
843 | },
844 | {
845 | "cell_type": "code",
846 | "execution_count": 25,
847 | "id": "d8c27c8d-38ed-499c-abff-bd045fd61558",
848 | "metadata": {},
849 | "outputs": [
850 | {
851 | "data": {
852 | "text/plain": [
853 | "\"[snippet: If you want the best views of San Francisco, take a hike to Twin Peaks. These famous grassy peaks rise 922 feet in elevation, making them the second highest point in the city (after Mount Davidson ..., title: 30 Top-Rated Things to Do in San Francisco | U.S. News Travel, link: https://travel.usnews.com/San_Francisco_CA/Things_To_Do/], [snippet: Other hot spots include Cavaña, a Latin American-inspired bar in Mission Bay, and Rise Over Run, a lush drinking destination on the top floor of the LINE Hotel. Clara Hogan. Advertising. 3 ..., title: 50 Best Things to Do in San Francisco in 2024 - Time Out, link: https://www.timeout.com/san-francisco/things-to-do/best-things-to-do-in-san-francisco], [snippet: 213 Cool, Hidden, and Unusual Things to Do in San Francisco, California. From the Gold Rush to Silicon Valley, the Bay Area has long attracted people from all over the country and the world. And ..., title: 213 Cool and Unusual Things to Do in San Francisco, link: https://www.atlasobscura.com/things-to-do/san-francisco-california], [snippet: The audio tour is one of the best things to do in San Francisco for tourists, but even locals will have plenty to learn on a visit to this historic island. 13. Ride a cable car. Photo by Ragnar Vorel on Unsplash. SF's cable cars are the only moving national landmark in the United States, dating back to 1873., title: 53 Best Attractions, Events, And Things To Do In San Francisco, link: https://secretsanfrancisco.com/things-to-do-san-francisco/]\""
854 | ]
855 | },
856 | "execution_count": 25,
857 | "metadata": {},
858 | "output_type": "execute_result"
859 | }
860 | ],
861 | "source": [
862 | "web_search.invoke(\"Fun things to do in SF\")"
863 | ]
864 | },
865 | {
866 | "cell_type": "markdown",
867 | "id": "530970ce-0eca-4fa9-b9f6-f026ed051c6d",
868 | "metadata": {},
869 | "source": [
870 | "### Attaching tools to LLM object\n",
871 | "\n",
872 | "Using LangChain's `.bind_tools()` method, we can automatically format the tools to the right format for the model provider and connect them to the LLM anytime we use it.\n",
873 | "\n",
874 | "This is also where you can pass in the `tool_choice` argument, however Anthropic doesn't support this feature currently for the example."
875 | ]
876 | },
877 | {
878 | "cell_type": "code",
879 | "execution_count": 26,
880 | "id": "dc21dfa9-9a78-401f-ae26-05dc3eba8ed7",
881 | "metadata": {},
882 | "outputs": [],
883 | "source": [
884 | "tools = [web_search, SendEmailCampaignLC, get_exchange_rate]\n",
885 | "\n",
886 | "llm_tools = llm.bind_tools(tools)"
887 | ]
888 | },
889 | {
890 | "cell_type": "markdown",
891 | "id": "f25c9c67-8913-4d6c-b0ff-3c54d3ceba53",
892 | "metadata": {},
893 | "source": [
894 | "Now the LLM has the ability to use and send back Tool messages"
895 | ]
896 | },
897 | {
898 | "cell_type": "code",
899 | "execution_count": 28,
900 | "id": "8e013945-0c21-40ed-8c86-487b6242aaba",
901 | "metadata": {},
902 | "outputs": [
903 | {
904 | "name": "stdout",
905 | "output_type": "stream",
906 | "text": [
907 | "\n",
908 | "---Response---\n",
909 | "To answer your questions, I'll need to use two different tools: one for the search about LangChain and another for the current exchange rate. Let me fetch that information for you.\n",
910 | "\n",
911 | "---Tool Calls---\n",
912 | "[{'name': 'duckduckgo_results_json', 'args': {'query': 'When was LangChain founded'}, 'id': 'toolu_01FtmJZQHLXuHTWs52APGFEn', 'type': 'tool_call'}, {'name': 'get_exchange_rate', 'args': {'base_currency': 'USD', 'target_currency': 'JPY'}, 'id': 'toolu_011kr5hsYLCrwesD4UmdhsAJ', 'type': 'tool_call'}]\n"
913 | ]
914 | }
915 | ],
916 | "source": [
917 | "# prompt = \"How much is a dollar worth in Japan right now\"\n",
918 | "# prompt = \"Can you send an email to adamlucek@youtube.com, samaltman@openai.com, and elonmusk@twitter.com about the cool youtube channel https://www.youtube.com/@AdamLucek\"\n",
919 | "# prompt = \"When was langgraph cloud released?\"\n",
920 | "prompt = \"When was langchain founded? and how much is a dollar worth in japan rn\"\n",
921 | "\n",
922 | "output = llm_tools.invoke(prompt)\n",
923 | "print(\"\\n---Response---\")\n",
924 | "print(output.content[0]['text'])\n",
925 | "print(\"\\n---Tool Calls---\")\n",
926 | "print(output.tool_calls)"
927 | ]
928 | },
929 | {
930 | "cell_type": "markdown",
931 | "id": "1e98eb4f-2693-46c1-8a34-501fcacc120a",
932 | "metadata": {},
933 | "source": [
934 | "### Handling Tool Calls\n",
935 | "\n",
936 | "Execute and feed back the tools once we got the arguments from the LLM, now that the tools and messages are formatted by LangChain\n",
937 | "\n",
938 | "Tools can be directly invoked when formatted as a LangChain tool, simplifying the execution process greatly"
939 | ]
940 | },
941 | {
942 | "cell_type": "code",
943 | "execution_count": 31,
944 | "id": "c50bfc89-c7dd-4875-8723-d4257560f9d5",
945 | "metadata": {},
946 | "outputs": [
947 | {
948 | "name": "stdout",
949 | "output_type": "stream",
950 | "text": [
951 | "-------\n",
952 | "human: content='When was langchain founded? and how much is a dollar worth in japan rn'\n",
953 | "-------\n",
954 | "ai: content=[{'text': \"To answer your questions, I'll need to use two different tools: one to search for information about LangChain's founding, and another to check the current exchange rate between US dollars and Japanese yen. Let me do that for you.\", 'type': 'text'}, {'id': 'toolu_01UVv5mjsF5EzGYkagSxxSv1', 'input': {'query': 'When was LangChain founded'}, 'name': 'duckduckgo_results_json', 'type': 'tool_use'}, {'id': 'toolu_01AcWtXxsfK9o43RHGCyYpKD', 'input': {'base_currency': 'USD', 'target_currency': 'JPY'}, 'name': 'get_exchange_rate', 'type': 'tool_use'}] response_metadata={'id': 'msg_01Tbm7WGDuGPaV2uHAhPoM4L', 'model': 'claude-3-5-sonnet-20240620', 'stop_reason': 'tool_use', 'stop_sequence': None, 'usage': {'input_tokens': 707, 'output_tokens': 176}} id='run-ad4c2bef-f767-41f7-a475-ebd32f95f7f6-0' tool_calls=[{'name': 'duckduckgo_results_json', 'args': {'query': 'When was LangChain founded'}, 'id': 'toolu_01UVv5mjsF5EzGYkagSxxSv1', 'type': 'tool_call'}, {'name': 'get_exchange_rate', 'args': {'base_currency': 'USD', 'target_currency': 'JPY'}, 'id': 'toolu_01AcWtXxsfK9o43RHGCyYpKD', 'type': 'tool_call'}] usage_metadata={'input_tokens': 707, 'output_tokens': 176, 'total_tokens': 883}\n",
955 | "-------\n",
956 | "tool: content=\"[snippet: LangChain is a framework designed to facilitate the development of applications powered by language models. It enables applications to be context-aware, connecting language models to various ..., title: Introduction to LangChain | by Zilliz | Medium, link: https://medium.com/@zilliz_learn/introduction-to-langchain-b01618c82f3c], [snippet: The first commit to LangChain shows its humble origins as a light wrapper around Python's formatter.format for prompt templating.But as Harrison tells the story, even his first experience with text-davinci-002 in early 2022 was focused on chatting with data from their internal company Notion 2 and Slack, what is now known as Retrieval Augmented Generation (RAG)., title: The Point of LangChain — with Harrison Chase of LangChain, link: https://www.latent.space/p/langchain], [snippet: LangChain on Github. In October 2022, LangChain started as an open-source project created by Harrison Chase. In April 2023, LangChain became a startup and has released more services besides ..., title: What is Langchain? Explained in Everyday Langauge for AI Beginners, link: https://medium.com/ai-for-absolute-beginners/what-is-langchain-explained-in-everyday-langauge-for-ai-beginners-fc8ffa6fb5f2], [snippet: LangChain is a framework that supports the development of applications that run on large language models (LLMs). In software development, a framework acts as a template for building apps, containing a collection of resources created and tested by developers and engineers. Since LangChain is open-source, anyone can access it and tailor it to ..., title: What Is LangChain? | Built In, link: https://builtin.com/articles/langchain]\" tool_call_id='toolu_01UVv5mjsF5EzGYkagSxxSv1'\n",
957 | "-------\n",
958 | "tool: content='158.51506391' tool_call_id='toolu_01AcWtXxsfK9o43RHGCyYpKD'\n"
959 | ]
960 | }
961 | ],
962 | "source": [
963 | "from langchain_core.messages import HumanMessage, ToolMessage\n",
964 | "\n",
965 | "query = \"When was langchain founded? and how much is a dollar worth in japan rn\"\n",
966 | "\n",
967 | "messages = [HumanMessage(query)]\n",
968 | "ai_msg = llm_tools.invoke(messages)\n",
969 | "messages.append(ai_msg)\n",
970 | "\n",
971 | "# Only using web search and exchange rate, and the Pydantic schema is not a full function, just a container for arguments\n",
972 | "for tool_call in ai_msg.tool_calls:\n",
973 | " selected_tool = {\"duckduckgo_results_json\": web_search, \"get_exchange_rate\": get_exchange_rate}[tool_call[\"name\"].lower()]\n",
974 | " tool_output = selected_tool.invoke(tool_call[\"args\"])\n",
975 | " messages.append(ToolMessage(tool_output, tool_call_id=tool_call[\"id\"]))\n",
976 | "\n",
977 | "for i in range(0, len(messages)):\n",
978 | " print(\"-------\")\n",
979 | " print(f\"{messages[i].type}: \", messages[i])"
980 | ]
981 | },
982 | {
983 | "cell_type": "markdown",
984 | "id": "d30ec36f-7120-4438-84b5-cd82ce934bdc",
985 | "metadata": {},
986 | "source": [
987 | "**Putting the recorded messages history together, all formatted correctly with their types of messages, we get the final answer:**"
988 | ]
989 | },
990 | {
991 | "cell_type": "code",
992 | "execution_count": 32,
993 | "id": "a548f16a-5627-4cfc-9638-6de019210116",
994 | "metadata": {},
995 | "outputs": [
996 | {
997 | "name": "stdout",
998 | "output_type": "stream",
999 | "text": [
1000 | "Now, let me answer your questions based on the information I've gathered:\n",
1001 | "\n",
1002 | "1. When was LangChain founded?\n",
1003 | "Based on the search results, LangChain was founded in October 2022. It started as an open-source project created by Harrison Chase. The project has since grown, and in April 2023, LangChain became a startup and expanded its services.\n",
1004 | "\n",
1005 | "2. How much is a dollar worth in Japan right now?\n",
1006 | "According to the current exchange rate, 1 US Dollar (USD) is worth 158.51506391 Japanese Yen (JPY).\n",
1007 | "\n",
1008 | "So, to summarize:\n",
1009 | "- LangChain was founded in October 2022.\n",
1010 | "- Currently, 1 USD = 158.52 JPY (rounded to two decimal places for simplicity).\n",
1011 | "\n",
1012 | "Is there anything else you'd like to know about these topics?\n"
1013 | ]
1014 | }
1015 | ],
1016 | "source": [
1017 | "final_response = llm_tools.invoke(messages)\n",
1018 | "print(final_response.content)"
1019 | ]
1020 | },
1021 | {
1022 | "cell_type": "markdown",
1023 | "id": "fbdcba30-6263-4804-ba69-8310e1b71cc6",
1024 | "metadata": {},
1025 | "source": [
1026 | "---\n",
1027 | "# Agents: The Final Frontier of Tools\n",
1028 | "\n",
1029 | "The most common use case for tools and function calling is for creating [agentic systems](https://aws.amazon.com/what-is/ai-agents/).\n",
1030 | "\n",
1031 | "We can use [LangChain's built in agent builders](https://python.langchain.com/v0.1/docs/modules/agents/quick_start/) like the OpenAI functions agent to easily attach our tools to an agentic flow, and execute it all in one, or add more complex chain-of-thought reasoning with tools like the [ReAct agent](https://python.langchain.com/v0.1/docs/modules/agents/agent_types/react/)."
1032 | ]
1033 | },
1034 | {
1035 | "cell_type": "markdown",
1036 | "id": "5e623743-0c59-4802-b76e-df2427043c51",
1037 | "metadata": {},
1038 | "source": [
1039 | "### Tool Calling Agent\n",
1040 | "\n",
1041 | "Simple flow that will essentially do what we've been setting up manually, [prompt link here](https://smith.langchain.com/hub/hwchase17/openai-functions-agent?organizationId=130ad41c-23ab-5f86-ba94-80f9634e5dca), with calling tools, executing tools, and then returning a final response."
1042 | ]
1043 | },
1044 | {
1045 | "cell_type": "code",
1046 | "execution_count": 33,
1047 | "id": "4b641018-4193-4947-8561-1afb2ec7a026",
1048 | "metadata": {},
1049 | "outputs": [],
1050 | "source": [
1051 | "from langchain import hub\n",
1052 | "from langchain.agents import create_tool_calling_agent\n",
1053 | "from langchain.agents import AgentExecutor\n",
1054 | "\n",
1055 | "# Get the prompt\n",
1056 | "oaif_prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
1057 | "\n",
1058 | "llm = ChatAnthropic(model=\"claude-3-5-sonnet-20240620\", temperature=0.7)\n",
1059 | "tools = [web_search, get_exchange_rate]\n",
1060 | "\n",
1061 | "# Create the agent\n",
1062 | "oaif_agent = create_tool_calling_agent(llm, tools, oaif_prompt)\n",
1063 | "\n",
1064 | "# Create the Agent Executor\n",
1065 | "# This is the runtime for an agent. This is what actually calls the agent, executes the actions it chooses, passes the action outputs back to the agent, and repeats. I\n",
1066 | "oaif_agent_executor = AgentExecutor(agent=oaif_agent, tools=tools, verbose=True)"
1067 | ]
1068 | },
1069 | {
1070 | "cell_type": "markdown",
1071 | "id": "7f3f94aa-ac82-404b-81d8-3debe9c1dd66",
1072 | "metadata": {},
1073 | "source": [
1074 | "Invoking the Agent"
1075 | ]
1076 | },
1077 | {
1078 | "cell_type": "code",
1079 | "execution_count": 34,
1080 | "id": "d73ad26a-aa8e-49af-9767-97fef2e6bb40",
1081 | "metadata": {},
1082 | "outputs": [
1083 | {
1084 | "name": "stdout",
1085 | "output_type": "stream",
1086 | "text": [
1087 | "\n",
1088 | "\n",
1089 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
1090 | "\u001b[32;1m\u001b[1;3m\n",
1091 | "Invoking: `duckduckgo_results_json` with `{'query': 'langgraph cloud release date'}`\n",
1092 | "responded: [{'text': \"To answer your questions, I'll need to use two different tools: one for searching about langgraph cloud, and another for checking the current exchange rate between US dollars and Japanese yen. Let me fetch that information for you.\", 'type': 'text', 'index': 0}, {'id': 'toolu_01Y9eWd1uKY3vS7Am5kkW5DW', 'input': {}, 'name': 'duckduckgo_results_json', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"query\": \"langgraph cloud release date\"}'}, {'id': 'toolu_01A8ZCAmdDmKjfyW8c4cKYKA', 'input': {}, 'name': 'get_exchange_rate', 'type': 'tool_use', 'index': 2, 'partial_json': '{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}'}]\n",
1093 | "\n",
1094 | "\u001b[0m\u001b[36;1m\u001b[1;3m[snippet: Announcing LangGraph v0.1 & LangGraph Cloud: Running agents at scale, reliably. Our new infrastructure for running agents at scale, LangGraph Cloud, is available in beta. We also have a new stable release of LangGraph. At LangChain, we aim to make it easy to build LLM applications - systems that connect LLMs to external sources of data and ..., title: Announcing LangGraph v0.1 & LangGraph Cloud: Running agents at scale ..., link: https://blog.langchain.dev/langgraph-cloud/], [snippet: This builds upon our latest stable release of LangGraph v0.1, which gives you control in building agents with support for human-in-the-loop collaboration (e.g. humans can approve or edit agent actions) and first-class streaming support. Join the waitlist today for LangGraph Cloud. And to learn more, read our blog post announcement., title: [Week of 6/24] LangChain Release Notes, link: https://blog.langchain.dev/week-of-6-24-langchain-release-notes/], [snippet: LangGraph. LangGraph is framework-agnostic, with each node functioning as a regular Python function. It extends the core Runnable API (a shared interface for streaming, async, and batch calls) to facilitate: Seamless state management across multiple conversation turns or tool calls., title: LangChain Just Launched LangGraph Cloud | by Cobus Greyling | Jul, 2024 ..., link: https://cobusgreyling.medium.com/langchain-just-launched-langgraph-cloud-bf8f65e45a54], [snippet: LangGraph helps construct a powerful agent executor that allows for loops in logic while keeping track of application state. Agents constructed with LangGraph can handle more ambiguous inputs and accomplish tasks more consistently. These agents can: Force call a tool. Wait for human-in-the-loop approval. Manage agent steps., title: [Week of 1/22/24] LangChain Release Notes, link: https://blog.langchain.dev/week-of-1-22-24-langchain-release-notes/]\u001b[0m\u001b[32;1m\u001b[1;3m\n",
1095 | "Invoking: `get_exchange_rate` with `{'base_currency': 'USD', 'target_currency': 'JPY'}`\n",
1096 | "responded: [{'text': \"To answer your questions, I'll need to use two different tools: one for searching about langgraph cloud, and another for checking the current exchange rate between US dollars and Japanese yen. Let me fetch that information for you.\", 'type': 'text', 'index': 0}, {'id': 'toolu_01Y9eWd1uKY3vS7Am5kkW5DW', 'input': {}, 'name': 'duckduckgo_results_json', 'type': 'tool_use', 'index': 1, 'partial_json': '{\"query\": \"langgraph cloud release date\"}'}, {'id': 'toolu_01A8ZCAmdDmKjfyW8c4cKYKA', 'input': {}, 'name': 'get_exchange_rate', 'type': 'tool_use', 'index': 2, 'partial_json': '{\"base_currency\": \"USD\", \"target_currency\": \"JPY\"}'}]\n",
1097 | "\n",
1098 | "\u001b[0m\u001b[33;1m\u001b[1;3m158.51506391\u001b[0m\u001b[32;1m\u001b[1;3m[{'text': \"\\n\\nNow, let me answer your questions based on the information I've gathered:\\n\\n1. When was langgraph cloud released?\\n\\nBased on the search results, LangGraph Cloud was announced and released in beta in June 2023. The exact release date isn't specified, but we can see from the blog post titles and snippets that it was announced around June 24, 2023. The announcement was made alongside the stable release of LangGraph v0.1.\\n\\n2. How much is a dollar worth in Japan right now?\\n\\nAccording to the current exchange rate, 1 US Dollar (USD) is worth 158.51 Japanese Yen (JPY).\\n\\nTo put it another way, $1 USD = ¥158.51 JPY at the current exchange rate.\\n\\nIs there anything else you'd like to know about LangGraph Cloud or currency exchange rates?\", 'type': 'text', 'index': 0}]\u001b[0m\n",
1099 | "\n",
1100 | "\u001b[1m> Finished chain.\u001b[0m\n",
1101 | "\n",
1102 | " \n",
1103 | "\n",
1104 | "Now, let me answer your questions based on the information I've gathered:\n",
1105 | "\n",
1106 | "1. When was langgraph cloud released?\n",
1107 | "\n",
1108 | "Based on the search results, LangGraph Cloud was announced and released in beta in June 2023. The exact release date isn't specified, but we can see from the blog post titles and snippets that it was announced around June 24, 2023. The announcement was made alongside the stable release of LangGraph v0.1.\n",
1109 | "\n",
1110 | "2. How much is a dollar worth in Japan right now?\n",
1111 | "\n",
1112 | "According to the current exchange rate, 1 US Dollar (USD) is worth 158.51 Japanese Yen (JPY).\n",
1113 | "\n",
1114 | "To put it another way, $1 USD = ¥158.51 JPY at the current exchange rate.\n",
1115 | "\n",
1116 | "Is there anything else you'd like to know about LangGraph Cloud or currency exchange rates?\n"
1117 | ]
1118 | }
1119 | ],
1120 | "source": [
1121 | "query = \"When was langgraph cloud released? and how much is a dollar worth in japan rn\"\n",
1122 | "\n",
1123 | "response = oaif_agent_executor.invoke({\"input\": query})\n",
1124 | "\n",
1125 | "print(\"\\n\", response['output'][0]['text'])"
1126 | ]
1127 | },
1128 | {
1129 | "cell_type": "markdown",
1130 | "id": "6e58ff68-a860-4b76-bde1-1ce2c096fcab",
1131 | "metadata": {},
1132 | "source": [
1133 | "### ReAct Agent - Chain-of-Thought with Tool Execution\n",
1134 | "\n",
1135 | "LLM flow based off the [ReAct: Synergizing Reasoning and Acting in Language Models Paper](https://react-lm.github.io/), [prompt link here](https://smith.langchain.com/hub/hwchase17/react?organizationId=130ad41c-23ab-5f86-ba94-80f9634e5dca). The prompt and flow looks a little like this:\n",
1136 | "\n",
1137 | "```\n",
1138 | "Use the following format:\n",
1139 | "\n",
1140 | "Question: the input question you must answer\n",
1141 | "\n",
1142 | "Thought: you should always think about what to do\n",
1143 | "\n",
1144 | "Action: the action to take, should be one of [{tool_names}]\n",
1145 | "\n",
1146 | "Action Input: the input to the action\n",
1147 | "\n",
1148 | "Observation: the result of the action\n",
1149 | "\n",
1150 | "... (this Thought/Action/Action Input/Observation can repeat N times)\n",
1151 | "\n",
1152 | "Thought: I now know the final answer\n",
1153 | "\n",
1154 | "Final Answer: the final answer to the original input question\n",
1155 | "```"
1156 | ]
1157 | },
1158 | {
1159 | "cell_type": "code",
1160 | "execution_count": 35,
1161 | "id": "1d26abc4-1c82-4996-927c-00a0bb21dba8",
1162 | "metadata": {},
1163 | "outputs": [],
1164 | "source": [
1165 | "from langchain.agents import create_react_agent\n",
1166 | "\n",
1167 | "react_prompt = hub.pull(\"hwchase17/react\")\n",
1168 | "react_agent = create_react_agent(llm, tools, react_prompt)\n",
1169 | "react_agent_executor = AgentExecutor(agent=react_agent, tools=tools, verbose=True)"
1170 | ]
1171 | },
1172 | {
1173 | "cell_type": "code",
1174 | "execution_count": 36,
1175 | "id": "098d53f0-8eb3-4b21-bea1-103552db5b06",
1176 | "metadata": {},
1177 | "outputs": [
1178 | {
1179 | "name": "stdout",
1180 | "output_type": "stream",
1181 | "text": [
1182 | "\n",
1183 | "\n",
1184 | "\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
1185 | "\u001b[32;1m\u001b[1;3mTo answer this question, I'll need to search for information about langgraph cloud and LangChain. Let's start with langgraph cloud.\n",
1186 | "\n",
1187 | "Action: duckduckgo_results_json\n",
1188 | "Action Input: langgraph cloud release date\n",
1189 | "\u001b[0m\u001b[36;1m\u001b[1;3m[snippet: Our new infrastructure for running agents at scale, LangGraph Cloud, is available in beta. We also have a new stable release of LangGraph., title: Announcing LangGraph v0.1 & LangGraph Cloud: Running agents at scale ..., link: https://blog.langchain.dev/langgraph-cloud/], [snippet: This builds upon our latest stable release of LangGraph v0.1, which gives you control in building agents with support for human-in-the-loop collaboration (e.g. humans can approve or edit agent actions) and first-class streaming support. Join the waitlist today for LangGraph Cloud. And to learn more, read our blog post announcement., title: [Week of 6/24] LangChain Release Notes, link: https://blog.langchain.dev/week-of-6-24-langchain-release-notes/], [snippet: LangChain Just Launched LangGraph Cloud. LangGraph is a fairly recent addition to the ever expanding LangChain ecosystem. With the launch of LangGraph Cloud, a managed, hosted service is introduced for deploying and hosting LangGraph applications. Cobus Greyling., title: LangChain Just Launched LangGraph Cloud | by Cobus Greyling | Jul, 2024 ..., link: https://cobusgreyling.medium.com/langchain-just-launched-langgraph-cloud-bf8f65e45a54], [snippet: This builds on LangGraph v0.1, our latest stable release supporting diverse control flows - whether it's single- or multi-agent, hierarchal or sequential. You'll be able to create flexible agents with human-in-the-loop collaboration and first-class streaming support. We can't wait to see the agentic workflows you'll ship. LangGraph Cloud is available in closed beta today., title: Introducing LangGraph Cloud LangGraph helps you build reliable ..., link: https://www.threads.net/@langchain.ai/post/C8uhvFUyLUT]\u001b[0m\u001b[32;1m\u001b[1;3mBased on the search results, I can answer part of the question about langgraph cloud. However, I don't have specific information about LangChain's founding date yet. Let's break this down:\n",
1190 | "\n",
1191 | "1. Regarding langgraph cloud:\n",
1192 | "The search results indicate that LangGraph Cloud was recently released in beta. The most specific information comes from the LangChain release notes for the week of 6/24, which mentions LangGraph Cloud being available in beta. This suggests that LangGraph Cloud was released in beta around late June 2023.\n",
1193 | "\n",
1194 | "2. Regarding LangChain's founding date:\n",
1195 | "The search results don't provide information about when LangChain was founded. We need to perform another search for this information.\n",
1196 | "\n",
1197 | "Let's search for LangChain's founding date.\n",
1198 | "\n",
1199 | "Action: duckduckgo_results_json\n",
1200 | "Action Input: LangChain founding date\n",
1201 | "\u001b[0m\u001b[36;1m\u001b[1;3m[snippet: The first commit to LangChain shows its humble origins as a light wrapper around Python's formatter.format for prompt templating.But as Harrison tells the story, even his first experience with text-davinci-002 in early 2022 was focused on chatting with data from their internal company Notion 2 and Slack, what is now known as Retrieval Augmented Generation (RAG)., title: The Point of LangChain — with Harrison Chase of LangChain, link: https://www.latent.space/p/langchain], [snippet: LangChain on Github. In October 2022, LangChain started as an open-source project created by Harrison Chase. In April 2023, LangChain became a startup and has released more services besides ..., title: What is Langchain? Explained in Everyday Langauge for AI Beginners, link: https://medium.com/ai-for-absolute-beginners/what-is-langchain-explained-in-everyday-langauge-for-ai-beginners-fc8ffa6fb5f2], [snippet: LangChain has over 100 different document loaders for all types of documents (html, pdf, code), from all types of locations (S3, public websites) and integrations with AirByte and Unstructured., title: Introduction to LangChain | by Zilliz | Medium, link: https://medium.com/@zilliz_learn/introduction-to-langchain-b01618c82f3c], [snippet: LangChain provides two types of agents that help to achieve that: action agents make decisions, take actions and make observations on the results of that actions, repeating this cycle until a ..., title: Introducing LangChain: a beginner's guide ️ | by ... - Medium, link: https://medium.com/@ellen.hoeven/introducing-langchain-a-beginners-guide-️-5e751622edb7]\u001b[0m\u001b[32;1m\u001b[1;3mBased on the search results, I can now provide more information about both LangGraph Cloud and LangChain's founding. Let's compile the information:\n",
1202 | "\n",
1203 | "1. Regarding LangGraph Cloud:\n",
1204 | "As mentioned earlier, LangGraph Cloud was released in beta around late June 2023. This is based on the LangChain release notes for the week of 6/24, which announced its availability in beta.\n",
1205 | "\n",
1206 | "2. Regarding LangChain's founding:\n",
1207 | "While we don't have an exact founding date for LangChain as a company, we can infer some key dates from the search results:\n",
1208 | "\n",
1209 | "- The project started as an open-source initiative in October 2022, created by Harrison Chase.\n",
1210 | "- The earliest work on what would become LangChain seems to have begun in early 2022, when Harrison Chase first experimented with text-davinci-002 for what is now known as Retrieval Augmented Generation (RAG).\n",
1211 | "- In April 2023, LangChain transitioned from an open-source project to a startup.\n",
1212 | "\n",
1213 | "Now I can provide a final answer to the original questions.\n",
1214 | "\n",
1215 | "Final Answer: LangGraph Cloud was released in beta around late June 2023. As for LangChain, it wasn't founded as a company immediately. The project started as an open-source initiative in October 2022, created by Harrison Chase. The earliest work on concepts that would become LangChain began in early 2022. LangChain then transitioned from an open-source project to a startup in April 2023.\u001b[0m\n",
1216 | "\n",
1217 | "\u001b[1m> Finished chain.\u001b[0m\n"
1218 | ]
1219 | }
1220 | ],
1221 | "source": [
1222 | "query = \"When was langgraph cloud released? When was LangChain founded?\"\n",
1223 | "\n",
1224 | "response = react_agent_executor.invoke({\"input\": query})"
1225 | ]
1226 | },
1227 | {
1228 | "cell_type": "code",
1229 | "execution_count": null,
1230 | "id": "f8516a49-3564-474a-93f6-84bf185fbb52",
1231 | "metadata": {},
1232 | "outputs": [],
1233 | "source": []
1234 | }
1235 | ],
1236 | "metadata": {
1237 | "kernelspec": {
1238 | "display_name": "Python 3 (ipykernel)",
1239 | "language": "python",
1240 | "name": "python3"
1241 | },
1242 | "language_info": {
1243 | "codemirror_mode": {
1244 | "name": "ipython",
1245 | "version": 3
1246 | },
1247 | "file_extension": ".py",
1248 | "mimetype": "text/x-python",
1249 | "name": "python",
1250 | "nbconvert_exporter": "python",
1251 | "pygments_lexer": "ipython3",
1252 | "version": "3.12.1"
1253 | }
1254 | },
1255 | "nbformat": 4,
1256 | "nbformat_minor": 5
1257 | }
1258 |
--------------------------------------------------------------------------------
/vid_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ALucek/tool-calling-guide/e7f7af1a7165f132f46f1b093324d56583a6f407/vid_screenshot.png
--------------------------------------------------------------------------------