mbudisic commited on
Commit
f098c5a
·
1 Parent(s): cf6b047

Full research (Tavily+RAG) works

Browse files
notebooks/transcript_agents.ipynb CHANGED
@@ -1,8 +1,15 @@
1
  {
2
  "cells": [
 
 
 
 
 
 
 
3
  {
4
  "cell_type": "code",
5
- "execution_count": 1,
6
  "metadata": {},
7
  "outputs": [],
8
  "source": [
@@ -15,7 +22,7 @@
15
  },
16
  {
17
  "cell_type": "code",
18
- "execution_count": 2,
19
  "metadata": {},
20
  "outputs": [],
21
  "source": [
@@ -24,9 +31,18 @@
24
  },
25
  {
26
  "cell_type": "code",
27
- "execution_count": 3,
28
  "metadata": {},
29
- "outputs": [],
 
 
 
 
 
 
 
 
 
30
  "source": [
31
  "%load_ext autoreload\n",
32
  "%autoreload 2\n"
@@ -34,10 +50,13 @@
34
  },
35
  {
36
  "cell_type": "code",
37
- "execution_count": 4,
38
  "metadata": {},
39
  "outputs": [],
40
  "source": [
 
 
 
41
  "\n",
42
  "load_dotenv()\n",
43
  "\n",
@@ -48,12 +67,15 @@
48
  " os.environ[key_name] = getpass.getpass(prompt_message)\n",
49
  "\n",
50
  "set_api_key_if_not_present(\"OPENAI_API_KEY\")\n",
51
- "set_api_key_if_not_present(\"TAVILY_API_KEY\")"
 
 
 
52
  ]
53
  },
54
  {
55
  "cell_type": "code",
56
- "execution_count": 5,
57
  "metadata": {},
58
  "outputs": [],
59
  "source": [
@@ -70,100 +92,189 @@
70
  },
71
  {
72
  "cell_type": "code",
73
- "execution_count": 6,
74
  "metadata": {},
75
  "outputs": [],
76
  "source": [
77
- "import functools\n",
78
- "import operator\n",
79
- "from typing import Annotated, List, TypedDict\n",
80
  "\n",
81
- "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
82
  "from langchain_openai.chat_models import ChatOpenAI\n",
83
  "\n",
84
- "class PsTutsTeamState(TypedDict):\n",
85
- " messages: Annotated[List[BaseMessage], operator.add]\n",
86
- " team_members: List[str]\n",
87
- " next: str"
88
  ]
89
  },
90
  {
91
  "cell_type": "code",
92
- "execution_count": 7,
93
  "metadata": {},
94
  "outputs": [],
95
  "source": [
96
- "llm_tool_calling = ChatOpenAI(model=params.tool_calling_model,temperature=0)"
 
97
  ]
98
  },
99
  {
100
  "cell_type": "code",
101
- "execution_count": 8,
102
  "metadata": {},
103
  "outputs": [],
104
  "source": [
105
- "from langchain_community.tools.tavily_search import TavilySearchResults\n",
106
- "\n",
107
- "adobe_help_search = TavilySearchResults(max_results=5,include_domains=[\"helpx.adobe.com\"])"
108
  ]
109
  },
110
  {
111
- "cell_type": "code",
112
- "execution_count": 9,
113
  "metadata": {},
114
- "outputs": [],
115
  "source": [
116
- "from pstuts_rag.agents import create_agent, agent_node\n",
117
- "\n",
118
- "adobe_help_agent = create_agent(\n",
119
- " llm_tool_calling,\n",
120
- " [adobe_help_search],\n",
121
- " \"You are a research assistant who can search\"\n",
122
- " \"for Adobe Photoshop help topics using the tavily search engine.\"\n",
123
- " \"Users may provide you with partial questions - try your best to determine their intent.\"\n",
124
- " \"If sending a request to search, craft your query so that it enhances user's ability\"\n",
125
- " \"to receive a helpful answer.\",\n",
126
- ")\n",
127
- "adobe_help_node = functools.partial(agent_node, agent=adobe_help_agent, name=\"AdobeHelp\")"
128
  ]
129
  },
130
  {
131
  "cell_type": "code",
132
- "execution_count": 10,
133
  "metadata": {},
134
  "outputs": [],
135
  "source": [
136
- "retval = adobe_help_agent.invoke({\"question\":\"What are layers?\",\"messages\":[],\"team_members\":[]})"
 
 
 
137
  ]
138
  },
139
  {
140
  "cell_type": "code",
141
- "execution_count": 11,
142
  "metadata": {},
143
  "outputs": [
144
  {
145
  "data": {
146
  "text/plain": [
147
- "{'question': 'What are layers?',\n",
148
- " 'messages': [],\n",
149
- " 'team_members': [],\n",
150
- " 'output': 'You can find comprehensive Adobe Photoshop help topics and user guides on the official Adobe website. These cover a wide range of subjects including color management, web and screen design, video and animation, nondestructive editing, layers and groups, masks, Smart Filters, blending modes, and more.\\n\\nHere are some useful links to explore:\\n- Common questions about Photoshop on the web: https://helpx.adobe.com/photoshop/using/photoshop-web-faq.html\\n- Photoshop User Guide: https://helpx.adobe.com/photoshop/user-guide.html\\n- Photoshop tools, options, and task bars: https://helpx.adobe.com/photoshop/using/using-tools.html\\n- View all Adobe Photoshop tutorials: https://helpx.adobe.com/in/photoshop/view-all-tutorials.filter-bar.html#!topic-title\\n\\nThese resources provide detailed instructions and tutorials to help you with various Photoshop features and tasks.'}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  ]
152
  },
153
- "execution_count": 11,
154
  "metadata": {},
155
  "output_type": "execute_result"
156
  }
157
  ],
158
  "source": [
159
- "retval"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
160
  ]
161
  },
162
  {
163
  "cell_type": "markdown",
164
  "metadata": {},
165
  "source": [
166
- "# Data Preparation\n",
167
  "\n",
168
  "First, we will read in the transcripts of the videos and convert them to Documents\n",
169
  "with appropriate metadata."
@@ -171,7 +282,7 @@
171
  },
172
  {
173
  "cell_type": "code",
174
- "execution_count": 12,
175
  "metadata": {},
176
  "outputs": [],
177
  "source": [
@@ -184,13 +295,6 @@
184
  "data:List[Dict[str,Any]] = await load_json_files(filename)\n"
185
  ]
186
  },
187
- {
188
- "cell_type": "markdown",
189
- "metadata": {},
190
- "source": [
191
- "## R - retrieval"
192
- ]
193
- },
194
  {
195
  "cell_type": "markdown",
196
  "metadata": {},
@@ -200,7 +304,7 @@
200
  },
201
  {
202
  "cell_type": "code",
203
- "execution_count": 13,
204
  "metadata": {},
205
  "outputs": [],
206
  "source": [
@@ -209,151 +313,150 @@
209
  "\n",
210
  "client = QdrantClient(path=\":memory:\")\n",
211
  "\n",
212
- "retriever_factory = DatastoreManager(qdrant_client=client,name=\"local_test\")\n",
213
- "if retriever_factory.count_docs() == 0:\n",
214
- " await retriever_factory.populate_database(raw_docs=data)"
215
  ]
216
  },
217
  {
218
  "cell_type": "markdown",
219
  "metadata": {},
220
  "source": [
221
- "## Generation\n",
222
- "\n",
223
- "We will use a 4.1-nano to generate answers."
224
  ]
225
  },
226
  {
227
  "cell_type": "code",
228
- "execution_count": 24,
229
  "metadata": {},
230
- "outputs": [],
 
 
 
 
 
 
 
 
231
  "source": [
232
- "from json import tool\n",
233
- "from pstuts_rag.rag import RAGChainFactory\n",
234
  "from langchain_openai import ChatOpenAI\n",
235
  "from langchain_core.tools import tool\n",
 
 
 
 
 
 
 
 
 
 
 
 
236
  "\n",
237
- "rag_factory = RAGChainFactory(retriever=retriever_factory.get_retriever())\n",
238
- "get_videos = rag_factory.get_rag_chain(llm=ChatOpenAI(model=params.tool_calling_model))\n",
239
- "\n",
240
- "@tool\n",
241
- "def get_videos_call(\n",
242
- " query: Annotated[str, \"Query to pose to Photoshop training video transcript archive\"]\n",
243
- " ):\n",
244
- " \"\"\"Extract information from Photoshop training video archive.\"\"\"\n",
245
- " return get_videos.invoke({\"question\" : query})\n",
246
- "\n",
247
- "get_videos_agent = create_agent(\n",
248
- " llm_tool_calling,\n",
249
- " [get_videos_call],\n",
250
- " \"\"\"You are an expert trainer in Adobe Photoshop who\n",
251
- " has an archive of training videos at her disposal.\n",
252
- " You can request transcripts of those videos and then summarize and shape \n",
253
- " them to provide helpful answers.\"\"\"\n",
254
- ")\n",
255
- "\n",
256
- "get_videos_node = functools.partial(agent_node, \n",
257
- " agent=get_videos_agent,\n",
258
- " name=\"VideoArchiveSearch\")\n"
259
  ]
260
  },
261
  {
262
  "cell_type": "code",
263
- "execution_count": 25,
264
  "metadata": {},
265
  "outputs": [
266
  {
267
- "data": {
268
- "text/plain": [
269
- "AIMessage(content='Layers in Photoshop are like separate flat pieces of glass stacked on top of each other, where each layer contains its own separate content. This allows you to work on different parts of an image independently. For example, you can toggle the visibility of each layer on or off to see what content it holds. Transparent areas in a layer let you see through to the layers below. This stacking and independence are the biggest benefits of using layers—you can edit one part of an image without affecting others. You can see all layers and work with them in the Layers panel. (See 0:28–2:00 and 1:25–2:32 for details) 🎨🖼️\\n**REFERENCES**\\n[\\n {\\n \"title\": \"Understand layers\",\\n \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\\n \"start\": 0.47,\\n \"stop\": 62.14\\n },\\n {\\n \"title\": \"Understand layers\",\\n \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\\n \"start\": 85.75,\\n \"stop\": 152.97\\n }\\n]', additional_kwargs={'refusal': None, 'context': [Document(metadata={'video_id': 19172, 'title': 'Understand layers', 'desc': 'Learn what layers are and why they are so useful.', 'length': '00:04:44.75', 'group': 'test.json', 'source': 'https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4', 'speech_start_stop_times': [[0.47, 3.41], [3.81, 9.13], [9.309999, 15.01], [15.299999, 20.57], [20.88, 23.3], [23.83, 27.93], [29.38, 32.79], [32.96, 33.92], [34.43, 40.21], [41.91, 45.37], [45.88, 49.01], [49.54, 55.130001], [55.72, 58.49], [58.72, 62.14]], 'start': 0.47, 'stop': 62.14, '_id': 21, '_collection_name': 'local_test'}, page_content=\"Layers are the building blocks of any image in Photoshop CC. So, it's important to understand, what layers are and why to use them - which we'll cover in this video. If you're following along, open this layered image from the downloadable practice files for this tutorial. You might think of layers like separate flat pints of glass, stacked one on top of the other. Each layer contains separate pieces of content. To get a sense of how layers are constructed, let's take a look at this Layers panel. I've closed my other panels, so that we can focus on the Layers panel. But you can skip that. By the way: If your Layers panel isn't showing, go up to the Window menu and choose Layers from there. The Layers panel is where you go to select and work with layers. In this image there are 4 layers, each with separate content. If you click the Eye icon to the left of a layer, you can toggle the visibility of that layer off and on. So, I'm going to turn off the visibility of the tailor layer. And keep your eye on the image, so you can see what's on that layer.\"), Document(metadata={'video_id': 19172, 'title': 'Understand layers', 'desc': 'Learn what layers are and why they are so useful.', 'length': '00:04:44.75', 'group': 'test.json', 'source': 'https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4', 'speech_start_stop_times': [[85.75, 88.659999], [89.42, 100.11], [101.469999, 108.64], [109.09, 117.459999], [117.75, 129.45], [129.97, 133.37], [133.73, 143.98], [144.76, 152.97]], 'start': 85.75, 'stop': 152.97, '_id': 23, '_collection_name': 'local_test'}, page_content=\"Now let's take a look at just one layer, the tailor layer. A quick way to turn off all the layers except the tailor layer, is to hold down the Option key on the Mac, or the ALT key on the PC, and click on the Eye icon to the left of the tailor layer. In the Document window, you can see that this layer contains just the one small photo surrounded by a gray and white checkerboard pattern. That pattern represents transparent pixels, which allow us to see down through the corresponding part of this layer to the content of the layers below. So, let's turn that content back on by going back to the Layers panel, again holding the Option key on the Mac or the ALT key on the PC and clicking on the Eye icon to the left of the tailor layer. And all the other layers and their Eye icons come back into view. So again: You might think of layers like a stack of pints of glass, each with its own artwork and in some cases transparent areas that let you see down through to the layers below. The biggest benefit of having items on separate layers like this, is that you'll be able to edit pieces of an image independently without affecting the rest of the image.\")], 'question': 'What are layers?'}, response_metadata={'token_usage': {'completion_tokens': 140, 'prompt_tokens': 1446, 'total_tokens': 1586, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 1408}}, 'model_name': 'gpt-4.1-mini-2025-04-14', 'system_fingerprint': 'fp_79b79be41f', 'finish_reason': 'stop', 'logprobs': None}, id='run--767b8147-ec59-4767-a6d3-8e7b54a89e4f-0', usage_metadata={'input_tokens': 1446, 'output_tokens': 140, 'total_tokens': 1586, 'input_token_details': {'audio': 0, 'cache_read': 1408}, 'output_token_details': {'audio': 0, 'reasoning': 0}})"
270
- ]
271
- },
272
- "execution_count": 25,
273
- "metadata": {},
274
- "output_type": "execute_result"
275
  }
276
  ],
277
  "source": [
278
- "retval = get_videos_call.invoke(\"What are layers?\")\n",
279
- "retval"
280
  ]
281
  },
282
  {
283
  "cell_type": "code",
284
- "execution_count": 26,
285
  "metadata": {},
286
- "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
287
  "source": [
288
- "val = await get_videos.ainvoke({\"question\":\"What are layers\"})"
 
 
 
 
 
 
 
 
289
  ]
290
  },
291
  {
292
  "cell_type": "code",
293
- "execution_count": 51,
294
  "metadata": {},
295
  "outputs": [],
296
  "source": [
297
  "from pstuts_rag.agents import create_team_supervisor\n",
 
298
  "supervisor_agent = create_team_supervisor(\n",
299
  " llm_tool_calling,\n",
300
- " \"\"\"You are the Supervisor for an agentic RAG system. Your job is to interpret the user’s request, extract the core research topic, and decide which research-focused worker to invoke next. Reply only with the next worker and the subject to research, or FINISH when the workflow is complete.\n",
301
- "\n",
302
- "Workers\n",
303
- "• VideoArchiveSearch – retrieves videos related to the query \n",
304
- "• AdobeHelp – searches Adobe’s documentation and training resources \n",
305
- "\n",
306
- "Routing Rules\n",
307
- "1. Topic Extraction \n",
308
- " • Read the user’s request and identify a concise research topic (e.g. “Photoshop timeline keyframes”).\n",
309
- "\n",
310
- "2. Primary Preference \n",
311
- " • First invoke VideoArchiveSearch with that topic. \n",
312
- " • If VideoArchiveSearch returns “I don’t know” or “no results,” fall back to AdobeHelp.\n",
313
- "\n",
314
- "3. AdobeHelp Behavior \n",
315
- " • When routing to AdobeHelp, always also check for related training videos in Adobe’s library.\n",
316
- "\n",
317
- "4. Research-Only \n",
318
- " • Only invoke workers that perform research tasks.\n",
319
- "\n",
320
- "5. Completion \n",
321
- " • When neither worked can provide value, go to FINISH. If AdobeHelp\n",
322
- " expands the list of topics, make sure to attempt to search for them with the\n",
323
- " VideoArchiveSearch.\n",
324
- "\n",
325
- "Response Format\n",
326
- "<WorkerName>: <Research Topic>\n",
327
- "\n",
328
- "Example:\n",
329
- "VideoArchiveSearch: exporting vector layers from After Effects\n",
330
- "\n",
331
- "And, once there’s no further research needed:\n",
332
- "FINISH\n",
333
- "\"\"\",\n",
334
  " [\"VideoArchiveSearch\", \"AdobeHelp\"],\n",
335
  ")\n",
336
  "\n"
337
  ]
338
  },
339
- {
340
- "cell_type": "markdown",
341
- "metadata": {},
342
- "source": [
343
- "## Graph Creation"
344
- ]
345
- },
346
  {
347
  "cell_type": "code",
348
- "execution_count": 52,
349
  "metadata": {},
350
- "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
351
  "source": [
352
  "from langgraph.graph import END, StateGraph\n",
353
  "\n",
354
  "adobe_help_graph = StateGraph(PsTutsTeamState)\n",
355
  "\n",
356
- "adobe_help_graph.add_node(\"VideoArchiveSearch\", get_videos_node)\n",
357
  "adobe_help_graph.add_node(\"AdobeHelp\", adobe_help_node)\n",
358
  "adobe_help_graph.add_node(\"supervisor\", supervisor_agent)\n",
359
  "\n",
@@ -370,24 +473,14 @@
370
  " {\"VideoArchiveSearch\":\"VideoArchiveSearch\",\n",
371
  " \"AdobeHelp\":\"AdobeHelp\", \n",
372
  " \"FINISH\": END},\n",
373
- ")\n",
374
- "adobe_help_graph.set_entry_point(\"supervisor\")\n",
375
- "adobe_help_compiled = adobe_help_graph.compile()"
376
  ]
377
  },
378
  {
379
  "cell_type": "code",
380
- "execution_count": 53,
381
  "metadata": {},
382
- "outputs": [
383
- {
384
- "name": "stderr",
385
- "output_type": "stream",
386
- "text": [
387
- "Adding an edge to a graph that has already been compiled. This will not be reflected in the compiled graph.\n"
388
- ]
389
- }
390
- ],
391
  "source": [
392
  "adobe_help_graph.set_entry_point(\"supervisor\")\n",
393
  "compiled_research_graph = adobe_help_graph.compile()"
@@ -395,7 +488,7 @@
395
  },
396
  {
397
  "cell_type": "code",
398
- "execution_count": 54,
399
  "metadata": {},
400
  "outputs": [],
401
  "source": [
@@ -405,7 +498,7 @@
405
  },
406
  {
407
  "cell_type": "code",
408
- "execution_count": 55,
409
  "metadata": {},
410
  "outputs": [
411
  {
@@ -420,10 +513,10 @@
420
  " * \n",
421
  " +------------+ \n",
422
  " | supervisor | \n",
423
- " *****+------------+..... \n",
424
- " **** * .... \n",
425
- " ***** * ..... \n",
426
- " *** * ... \n",
427
  "+-----------+ +--------------------+ +---------+ \n",
428
  "| AdobeHelp | | VideoArchiveSearch | | __end__ | \n",
429
  "+-----------+ +--------------------+ +---------+ \n"
@@ -439,7 +532,7 @@
439
  },
440
  {
441
  "cell_type": "code",
442
- "execution_count": 56,
443
  "metadata": {},
444
  "outputs": [],
445
  "source": [
@@ -471,7 +564,7 @@
471
  },
472
  {
473
  "cell_type": "code",
474
- "execution_count": 58,
475
  "metadata": {},
476
  "outputs": [
477
  {
@@ -483,11 +576,66 @@
483
  "================================\u001b[1m Human Message \u001b[0m=================================\n",
484
  "Name: VideoArchiveSearch\n",
485
  "\n",
486
- "Layers in Photoshop are like separate flat panes of glass stacked on top of each other, with each layer containing separate pieces of content. You can think of them as the building blocks of any image. In the Layers panel, you can see all the layers in your image, each with an Eye icon to toggle its visibility on or off, allowing you to see or hide that layer's content.\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
487
  "\n",
488
- "A single layer can have transparent pixels, shown as a gray and white checkerboard pattern, which lets you see through to the layers beneath it. You can also hold down the Option key (Mac) or ALT key (PC) and click the Eye icon next to a layer to hide all other layers except the one you clicked on, focusing only on that layer. Doing the same action again will bring all other layers back into view.\n",
489
  "\n",
490
- "The main benefit of using layers is that you can edit parts of your image independently without affecting other layers, giving you great flexibility in your editing process.\n",
 
 
 
 
 
 
 
 
 
 
491
  "---\n",
492
  "{'supervisor': {'next': 'FINISH'}}\n",
493
  "---\n"
@@ -495,7 +643,7 @@
495
  }
496
  ],
497
  "source": [
498
- "demo_research_chain(\"What are layers?\")"
499
  ]
500
  },
501
  {
 
1
  {
2
  "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# Adobe research unit"
8
+ ]
9
+ },
10
  {
11
  "cell_type": "code",
12
+ "execution_count": 282,
13
  "metadata": {},
14
  "outputs": [],
15
  "source": [
 
22
  },
23
  {
24
  "cell_type": "code",
25
+ "execution_count": 283,
26
  "metadata": {},
27
  "outputs": [],
28
  "source": [
 
31
  },
32
  {
33
  "cell_type": "code",
34
+ "execution_count": 284,
35
  "metadata": {},
36
+ "outputs": [
37
+ {
38
+ "name": "stdout",
39
+ "output_type": "stream",
40
+ "text": [
41
+ "The autoreload extension is already loaded. To reload it, use:\n",
42
+ " %reload_ext autoreload\n"
43
+ ]
44
+ }
45
+ ],
46
  "source": [
47
  "%load_ext autoreload\n",
48
  "%autoreload 2\n"
 
50
  },
51
  {
52
  "cell_type": "code",
53
+ "execution_count": 285,
54
  "metadata": {},
55
  "outputs": [],
56
  "source": [
57
+ "from uuid import uuid4\n",
58
+ "\n",
59
+ "unique_id = uuid4().hex[0:8]\n",
60
  "\n",
61
  "load_dotenv()\n",
62
  "\n",
 
67
  " os.environ[key_name] = getpass.getpass(prompt_message)\n",
68
  "\n",
69
  "set_api_key_if_not_present(\"OPENAI_API_KEY\")\n",
70
+ "set_api_key_if_not_present(\"TAVILY_API_KEY\")\n",
71
+ "os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
72
+ "os.environ[\"LANGCHAIN_PROJECT\"] = f\"AIE - MBUDISIC - CERT - {unique_id}\"\n",
73
+ "set_api_key_if_not_present(\"LANGCHAIN_API_KEY\")\n"
74
  ]
75
  },
76
  {
77
  "cell_type": "code",
78
+ "execution_count": 286,
79
  "metadata": {},
80
  "outputs": [],
81
  "source": [
 
92
  },
93
  {
94
  "cell_type": "code",
95
+ "execution_count": 287,
96
  "metadata": {},
97
  "outputs": [],
98
  "source": [
 
 
 
99
  "\n",
100
+ "from langchain_core.messages import HumanMessage\n",
101
  "from langchain_openai.chat_models import ChatOpenAI\n",
102
  "\n",
103
+ "from pstuts_rag.agents import PsTutsTeamState\n"
 
 
 
104
  ]
105
  },
106
  {
107
  "cell_type": "code",
108
+ "execution_count": 288,
109
  "metadata": {},
110
  "outputs": [],
111
  "source": [
112
+ "test_query = PsTutsTeamState(messages=[HumanMessage(content=\"What are layers?\")],\n",
113
+ " team_members=[], next=\"\")"
114
  ]
115
  },
116
  {
117
  "cell_type": "code",
118
+ "execution_count": 289,
119
  "metadata": {},
120
  "outputs": [],
121
  "source": [
122
+ "llm_tool_calling = ChatOpenAI(model=params.tool_calling_model,temperature=0)"
 
 
123
  ]
124
  },
125
  {
126
+ "cell_type": "markdown",
 
127
  "metadata": {},
 
128
  "source": [
129
+ "## Tavily"
 
 
 
 
 
 
 
 
 
 
 
130
  ]
131
  },
132
  {
133
  "cell_type": "code",
134
+ "execution_count": 290,
135
  "metadata": {},
136
  "outputs": [],
137
  "source": [
138
+ "from pstuts_rag.agent_tavily import create_tavily_node\n",
139
+ "\n",
140
+ "\n",
141
+ "adobe_help_node, adobe_help_agent,adobe_search = create_tavily_node(llm=llm_tool_calling,name=\"AdobeHelp\")"
142
  ]
143
  },
144
  {
145
  "cell_type": "code",
146
+ "execution_count": 291,
147
  "metadata": {},
148
  "outputs": [
149
  {
150
  "data": {
151
  "text/plain": [
152
+ "[{'title': 'Top 4 reasons to crop your photo',\n",
153
+ " 'url': 'https://helpx.adobe.com/lv/photoshop/how-to/cropping-photo-basics.html',\n",
154
+ " 'content': 'Cropping is the easiest way to remove unwanted objects or people at the edges of a photograph. Anything outside the crop boundary will disappear from your image',\n",
155
+ " 'score': 0.585789},\n",
156
+ " {'title': 'Crop, resize, and resample images in Photoshop Elements',\n",
157
+ " 'url': 'https://helpx.adobe.com/au/photoshop-elements/kb/crop-resize-resample-photoshop-elements.html',\n",
158
+ " 'content': 'When you crop an image, you trim away material from the edges to show a smaller area, often for artistic reasons.',\n",
159
+ " 'score': 0.585789},\n",
160
+ " {'title': 'How to crop and straighten photos in Photoshop',\n",
161
+ " 'url': 'https://helpx.adobe.com/photoshop/using/crop-straighten-photos.html',\n",
162
+ " 'content': \"Try it in the app\\nYou'll get a sample file to follow along with as you learn how to crop photos.\\nOpen Photoshop\\nCropping is the process of removing portions of a photo to create focus or strengthen the composition. Use the Crop tool to crop and straighten photos in Photoshop. The Crop tool is non-destructive, and you can choose to retain the cropped pixels to optimize the crop boundaries later. The Crop tool also provides intuitive methods to straighten a photo while cropping. [...] Press Enter (Windows) or Return (Mac OS) to crop the photo.\\n\\n\\nContent-Aware Fill on Crop\\nIntroduced in Photoshop CC 2015.5 release\\nPhotoshop now uses content-aware technology to intelligently fill in the gaps when you use the Crop tool for straightening or rotating an image, or expanding your canvas beyond the image's original size. \\nFollow these steps:\\n\\n\\nFrom the toolbar, select the Crop Tool (). Crop borders display on the edges of the photo. [...] See the video Adjust perspective in a photo for more information.\\nResize the canvas using the Crop tool\\nYou can use the Crop tool to resize the image canvas.\\n\\n\\nFrom the toolbar, select the Crop Tool\\xa0. Crop borders display on the edges of the image.\\n\\n\\nDrag the crop handles outwards to enlarge the canvas. Use the Alt/Option modifier key to enlarge from all sides.\\n\\n\\nPress Enter (Windows) or Return (Mac OS) to confirm the action.\",\n",
163
+ " 'score': 0.5826579},\n",
164
+ " {'title': 'Crop images in Photoshop Elements',\n",
165
+ " 'url': 'https://helpx.adobe.com/ca/photoshop-elements/using/cropping.html',\n",
166
+ " 'content': 'The Crop tool removes the part of an image surrounding the selection. Crop to remove distractive background elements and create a focus on',\n",
167
+ " 'score': 0.5367749},\n",
168
+ " {'title': 'Crop a photo using the Crop tool',\n",
169
+ " 'url': 'https://helpx.adobe.com/photoshop/using/tool-techniques/crop-tool.html',\n",
170
+ " 'content': 'The Crop tool allows you to select an area of a photo and remove or crop everything outside the selected area. Photoshop Crop Tool.',\n",
171
+ " 'score': 0.49567938}]"
172
  ]
173
  },
174
+ "execution_count": 291,
175
  "metadata": {},
176
  "output_type": "execute_result"
177
  }
178
  ],
179
  "source": [
180
+ "adobe_search(\"What is crop?\")"
181
+ ]
182
+ },
183
+ {
184
+ "cell_type": "code",
185
+ "execution_count": 292,
186
+ "metadata": {},
187
+ "outputs": [],
188
+ "source": [
189
+ "retval = adobe_help_agent.invoke({\"input\":\"What is crop?\",\"messages\":[],\"team_members\":[],\"next\":[]}),\n"
190
+ ]
191
+ },
192
+ {
193
+ "cell_type": "code",
194
+ "execution_count": 293,
195
+ "metadata": {},
196
+ "outputs": [
197
+ {
198
+ "name": "stdout",
199
+ "output_type": "stream",
200
+ "text": [
201
+ "('To create and use clipping masks in Adobe Photoshop, follow these steps:\\n'\n",
202
+ " '\\n'\n",
203
+ " '1. Arrange your layers in the Layers panel so that the layer you want to use '\n",
204
+ " 'as a mask is directly below the layer you want to mask.\\n'\n",
205
+ " '\\n'\n",
206
+ " '2. Select the top layer (the one you want to be clipped) in the Layers '\n",
207
+ " 'panel.\\n'\n",
208
+ " '\\n'\n",
209
+ " '3. Choose Layer > Create Clipping Mask from the menu. Alternatively, you can '\n",
210
+ " 'right-click the top layer and select \"Create Clipping Mask.\"\\n'\n",
211
+ " '\\n'\n",
212
+ " '4. The top layer will now be clipped to the shape and transparency of the '\n",
213
+ " 'layer below it, meaning it will only show through where the bottom layer has '\n",
214
+ " 'pixels.\\n'\n",
215
+ " '\\n'\n",
216
+ " 'You can use two or more layers as a clipping mask, and you can release the '\n",
217
+ " 'clipping mask by selecting the clipped layer and choosing Layer > Release '\n",
218
+ " 'Clipping Mask.\\n'\n",
219
+ " '\\n'\n",
220
+ " \"For more detailed instructions, you can visit Adobe's official help page on \"\n",
221
+ " 'clipping masks:\\n'\n",
222
+ " 'https://helpx.adobe.com/photoshop/using/revealing-layers-clipping-masks.html')\n"
223
+ ]
224
+ }
225
+ ],
226
+ "source": [
227
+ "from pprint import pp\n",
228
+ "pp(retval[0][\"output\"])"
229
+ ]
230
+ },
231
+ {
232
+ "cell_type": "code",
233
+ "execution_count": 294,
234
+ "metadata": {},
235
+ "outputs": [],
236
+ "source": [
237
+ "output = adobe_help_node(test_query)"
238
+ ]
239
+ },
240
+ {
241
+ "cell_type": "code",
242
+ "execution_count": 295,
243
+ "metadata": {},
244
+ "outputs": [
245
+ {
246
+ "name": "stdout",
247
+ "output_type": "stream",
248
+ "text": [
249
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
250
+ "Name: AdobeHelp\n",
251
+ "\n",
252
+ "Layers in Adobe Photoshop are fundamental elements that allow you to work on different parts of an image independently without affecting other parts. Think of layers as stacked, transparent sheets of glass, where each layer can contain images, text, effects, or objects. You can edit, move, and apply changes to one layer without altering the content on other layers.\n",
253
+ "\n",
254
+ "Layers help you make nondestructive edits by stacking and managing images, text, and graphics separately. They are arranged in a stack in the Layers panel, usually located in the bottom right of the workspace. You can add multiple layers to composite images, add text, apply filters, and create complex designs.\n",
255
+ "\n",
256
+ "The bottommost layer is often the Background layer, which is locked by default but can be converted to a regular layer for more flexibility.\n",
257
+ "\n",
258
+ "In summary, layers let you:\n",
259
+ "- Work on different elements independently\n",
260
+ "- Apply effects and adjustments to specific parts\n",
261
+ "- Rearrange content by changing the stacking order\n",
262
+ "- Control opacity and blending modes for creative effects\n",
263
+ "\n",
264
+ "For more details, you can visit Adobe's official help page about layers: \n",
265
+ "https://helpx.adobe.com/photoshop/web/edit-images/manage-layers/about-layers.html\n"
266
+ ]
267
+ }
268
+ ],
269
+ "source": [
270
+ "output[\"messages\"][-1].pretty_print()"
271
  ]
272
  },
273
  {
274
  "cell_type": "markdown",
275
  "metadata": {},
276
  "source": [
277
+ "## Data Preparation\n",
278
  "\n",
279
  "First, we will read in the transcripts of the videos and convert them to Documents\n",
280
  "with appropriate metadata."
 
282
  },
283
  {
284
  "cell_type": "code",
285
+ "execution_count": 296,
286
  "metadata": {},
287
  "outputs": [],
288
  "source": [
 
295
  "data:List[Dict[str,Any]] = await load_json_files(filename)\n"
296
  ]
297
  },
 
 
 
 
 
 
 
298
  {
299
  "cell_type": "markdown",
300
  "metadata": {},
 
304
  },
305
  {
306
  "cell_type": "code",
307
+ "execution_count": 297,
308
  "metadata": {},
309
  "outputs": [],
310
  "source": [
 
313
  "\n",
314
  "client = QdrantClient(path=\":memory:\")\n",
315
  "\n",
316
+ "datastore_manager = DatastoreManager(qdrant_client=client,name=\"local_test\")\n",
317
+ "if datastore_manager.count_docs() == 0:\n",
318
+ " await datastore_manager.populate_database(raw_docs=data)"
319
  ]
320
  },
321
  {
322
  "cell_type": "markdown",
323
  "metadata": {},
324
  "source": [
325
+ "## RAG Agent"
 
 
326
  ]
327
  },
328
  {
329
  "cell_type": "code",
330
+ "execution_count": 298,
331
  "metadata": {},
332
+ "outputs": [
333
+ {
334
+ "name": "stdout",
335
+ "output_type": "stream",
336
+ "text": [
337
+ "<built-in function repr>\n"
338
+ ]
339
+ }
340
+ ],
341
  "source": [
342
+ "from pstuts_rag.agent_rag import create_rag_node\n",
 
343
  "from langchain_openai import ChatOpenAI\n",
344
  "from langchain_core.tools import tool\n",
345
+ "rag_node, rag_search = create_rag_node(retriever=datastore_manager.get_retriever(),\n",
346
+ " llm=ChatOpenAI(model=\"gpt-4.1-mini\",temperature=0),\n",
347
+ " name=\"VideoArchiveSearch\" )\n",
348
+ "\n"
349
+ ]
350
+ },
351
+ {
352
+ "cell_type": "code",
353
+ "execution_count": 299,
354
+ "metadata": {},
355
+ "outputs": [],
356
+ "source": [
357
  "\n",
358
+ "retval = rag_search(\"What is Seinfeld?\")\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
359
  ]
360
  },
361
  {
362
  "cell_type": "code",
363
+ "execution_count": 300,
364
  "metadata": {},
365
  "outputs": [
366
  {
367
+ "name": "stdout",
368
+ "output_type": "stream",
369
+ "text": [
370
+ "\"I don't know. This isn’t covered in the training videos.\"\n"
371
+ ]
 
 
 
372
  }
373
  ],
374
  "source": [
375
+ "from pprint import pp\n",
376
+ "pp(retval)"
377
  ]
378
  },
379
  {
380
  "cell_type": "code",
381
+ "execution_count": 301,
382
  "metadata": {},
383
+ "outputs": [
384
+ {
385
+ "name": "stdout",
386
+ "output_type": "stream",
387
+ "text": [
388
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
389
+ "Name: VideoArchiveSearch\n",
390
+ "\n",
391
+ "Layers are the building blocks of any image in Photoshop CC. You can think of layers like separate flat panes of glass stacked on top of each other, with each layer containing separate pieces of content. Some parts of a layer can be transparent, allowing you to see through to the layers below. This setup lets you edit parts of an image independently without affecting the rest of the image. You work with layers in the Layers panel, where you can toggle their visibility on and off using the Eye icon. (See explanation around 0:28–2:00 and 1:25–2:32 in the video) 🎨🖼️\n",
392
+ "**REFERENCES**\n",
393
+ "[\n",
394
+ " {\n",
395
+ " \"title\": \"Understand layers\",\n",
396
+ " \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\n",
397
+ " \"start\": 0.47,\n",
398
+ " \"stop\": 62.14\n",
399
+ " },\n",
400
+ " {\n",
401
+ " \"title\": \"Understand layers\",\n",
402
+ " \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\n",
403
+ " \"start\": 85.75,\n",
404
+ " \"stop\": 152.97\n",
405
+ " }\n",
406
+ "]\n"
407
+ ]
408
+ }
409
+ ],
410
  "source": [
411
+ "rag_output = rag_node(test_query)\n",
412
+ "rag_output[\"messages\"][-1].pretty_print()"
413
+ ]
414
+ },
415
+ {
416
+ "cell_type": "markdown",
417
+ "metadata": {},
418
+ "source": [
419
+ "## Graph Creation"
420
  ]
421
  },
422
  {
423
  "cell_type": "code",
424
+ "execution_count": 302,
425
  "metadata": {},
426
  "outputs": [],
427
  "source": [
428
  "from pstuts_rag.agents import create_team_supervisor\n",
429
+ "from pstuts_rag.prompt_templates import SUPERVISOR_SYSTEM\n",
430
  "supervisor_agent = create_team_supervisor(\n",
431
  " llm_tool_calling,\n",
432
+ " SUPERVISOR_SYSTEM,\n",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
  " [\"VideoArchiveSearch\", \"AdobeHelp\"],\n",
434
  ")\n",
435
  "\n"
436
  ]
437
  },
 
 
 
 
 
 
 
438
  {
439
  "cell_type": "code",
440
+ "execution_count": 303,
441
  "metadata": {},
442
+ "outputs": [
443
+ {
444
+ "data": {
445
+ "text/plain": [
446
+ "<langgraph.graph.state.StateGraph at 0x71685a01e120>"
447
+ ]
448
+ },
449
+ "execution_count": 303,
450
+ "metadata": {},
451
+ "output_type": "execute_result"
452
+ }
453
+ ],
454
  "source": [
455
  "from langgraph.graph import END, StateGraph\n",
456
  "\n",
457
  "adobe_help_graph = StateGraph(PsTutsTeamState)\n",
458
  "\n",
459
+ "adobe_help_graph.add_node(\"VideoArchiveSearch\", rag_node)\n",
460
  "adobe_help_graph.add_node(\"AdobeHelp\", adobe_help_node)\n",
461
  "adobe_help_graph.add_node(\"supervisor\", supervisor_agent)\n",
462
  "\n",
 
473
  " {\"VideoArchiveSearch\":\"VideoArchiveSearch\",\n",
474
  " \"AdobeHelp\":\"AdobeHelp\", \n",
475
  " \"FINISH\": END},\n",
476
+ ")\n"
 
 
477
  ]
478
  },
479
  {
480
  "cell_type": "code",
481
+ "execution_count": 304,
482
  "metadata": {},
483
+ "outputs": [],
 
 
 
 
 
 
 
 
484
  "source": [
485
  "adobe_help_graph.set_entry_point(\"supervisor\")\n",
486
  "compiled_research_graph = adobe_help_graph.compile()"
 
488
  },
489
  {
490
  "cell_type": "code",
491
+ "execution_count": 305,
492
  "metadata": {},
493
  "outputs": [],
494
  "source": [
 
498
  },
499
  {
500
  "cell_type": "code",
501
+ "execution_count": 306,
502
  "metadata": {},
503
  "outputs": [
504
  {
 
513
  " * \n",
514
  " +------------+ \n",
515
  " | supervisor | \n",
516
+ " .....+------------+..... \n",
517
+ " .... . .... \n",
518
+ " ..... . ..... \n",
519
+ " ... . ... \n",
520
  "+-----------+ +--------------------+ +---------+ \n",
521
  "| AdobeHelp | | VideoArchiveSearch | | __end__ | \n",
522
  "+-----------+ +--------------------+ +---------+ \n"
 
532
  },
533
  {
534
  "cell_type": "code",
535
+ "execution_count": 307,
536
  "metadata": {},
537
  "outputs": [],
538
  "source": [
 
564
  },
565
  {
566
  "cell_type": "code",
567
+ "execution_count": 308,
568
  "metadata": {},
569
  "outputs": [
570
  {
 
576
  "================================\u001b[1m Human Message \u001b[0m=================================\n",
577
  "Name: VideoArchiveSearch\n",
578
  "\n",
579
+ "Layers are the building blocks of any image in Photoshop CC. You can think of layers like separate flat panes of glass stacked on top of each other, where each layer contains separate pieces of content. Some parts of a layer can be transparent, allowing you to see through to the layers below. This setup lets you edit parts of an image independently without affecting the rest of the image. You work with layers in the Layers panel, where you can toggle their visibility on and off to see what each layer contains (explained around 0:28 to 1:03 and 1:25 to 2:33 in the video).\n",
580
+ "**REFERENCES**\n",
581
+ "[\n",
582
+ " {\n",
583
+ " \"title\": \"Understand layers\",\n",
584
+ " \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\n",
585
+ " \"start\": 0.47,\n",
586
+ " \"stop\": 62.14\n",
587
+ " },\n",
588
+ " {\n",
589
+ " \"title\": \"Understand layers\",\n",
590
+ " \"source\": \"https://images-tv.adobe.com/avp/vr/b758b4c4-2a74-41f4-8e67-e2f2eab83c6a/f810fc5b-2b04-4e23-8fa4-5c532e7de6f8/e268fe4d-e5c7-415c-9f5c-d34d024b14d8_20170727011753.1280x720at2400_h264.mp4\",\n",
591
+ " \"start\": 85.75,\n",
592
+ " \"stop\": 152.97\n",
593
+ " }\n",
594
+ "]\n",
595
+ "---\n",
596
+ "{'supervisor': {'next': 'FINISH'}}\n",
597
+ "---\n"
598
+ ]
599
+ }
600
+ ],
601
+ "source": [
602
+ "demo_research_chain(\"What are layers?\")"
603
+ ]
604
+ },
605
+ {
606
+ "cell_type": "code",
607
+ "execution_count": 309,
608
+ "metadata": {},
609
+ "outputs": [
610
+ {
611
+ "name": "stdout",
612
+ "output_type": "stream",
613
+ "text": [
614
+ "{'supervisor': {'next': 'VideoArchiveSearch'}}\n",
615
+ "---\n",
616
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
617
+ "Name: VideoArchiveSearch\n",
618
+ "\n",
619
+ "I don't know. This isn’t covered in the training videos.\n",
620
+ "---\n",
621
+ "{'supervisor': {'next': 'AdobeHelp'}}\n",
622
+ "---\n",
623
+ "================================\u001b[1m Human Message \u001b[0m=================================\n",
624
+ "Name: AdobeHelp\n",
625
  "\n",
626
+ "To crop a layer in Adobe Photoshop, you can use the Crop tool in a way that targets the active layer or a selection within that layer. Here's how to do it:\n",
627
  "\n",
628
+ "1. Select the layer you want to crop by clicking its thumbnail in the Layers panel.\n",
629
+ "2. Choose the Crop tool from the toolbar.\n",
630
+ "3. A bounding box will appear around the active layer or your selection.\n",
631
+ "4. Drag the handles of the bounding box to set the crop size.\n",
632
+ "5. You can also rotate the layer by moving the rotation dial below the bounding box if needed.\n",
633
+ "6. Once you have the desired crop area, apply the crop.\n",
634
+ "\n",
635
+ "This method allows you to crop and rotate an active layer or the contents of a selection non-destructively.\n",
636
+ "\n",
637
+ "For more details, you can visit Adobe's official help page on cropping and rotating layers:\n",
638
+ "https://helpx.adobe.com/photoshop/using/crop-move-rotate-photos.html\n",
639
  "---\n",
640
  "{'supervisor': {'next': 'FINISH'}}\n",
641
  "---\n"
 
643
  }
644
  ],
645
  "source": [
646
+ "demo_research_chain(\"How do we crop a layer?\")"
647
  ]
648
  },
649
  {
pstuts_rag/pstuts_rag/agent_rag.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pstuts_rag.prompt_templates import AGENT_SYSTEM, RAG_PROMPT_TEMPLATES
2
+ from .rag import RAGChainFactory
3
+ from langchain_core.vectorstores import VectorStoreRetriever
4
+ from langchain_core.tools import tool
5
+ from typing import Annotated
6
+ from langchain_core.runnables import RunnableLambda
7
+ from .agents import agent_node, create_agent
8
+ from langchain_core.language_models.chat_models import BaseChatModel
9
+ import functools
10
+ from langchain_core.output_parsers import StrOutputParser
11
+ from langchain_core.prompts import ChatPromptTemplate
12
+ from operator import itemgetter
13
+
14
+
15
+ def create_rag_node(
16
+ llm: BaseChatModel,
17
+ retriever: VectorStoreRetriever,
18
+ name: str = "VideoSearch",
19
+ ):
20
+ """Create a RAG node for the agent graph.
21
+
22
+ Args:
23
+ llm: The language model to use
24
+ retriever: The retriever to use for RAG
25
+ name: The name of the node
26
+
27
+ Returns:
28
+ tuple: (node, search_function)
29
+ """
30
+ rag_factory = RAGChainFactory(retriever=retriever)
31
+ rag_chain = rag_factory.get_rag_chain(llm=llm)
32
+
33
+ def search_transcripts(query: str) -> str:
34
+ """Search through video transcripts to find relevant information about Photoshop.
35
+
36
+ Args:
37
+ query: The search query about Photoshop features or techniques
38
+
39
+ Returns:
40
+ str: Relevant information from the video transcripts
41
+ """
42
+ result = rag_chain.invoke(
43
+ {"question": query, "input": query, "query": query}
44
+ )
45
+ return result.content
46
+
47
+ # Create a simple agent that just does the search
48
+ def search_agent(state):
49
+ # Extract query from input or messages
50
+ query = state.get("input", None)
51
+ if not query and state.get("messages", []):
52
+ last_message = state["messages"][-1]
53
+ query = (
54
+ last_message.content
55
+ if hasattr(last_message, "content")
56
+ else str(last_message)
57
+ )
58
+
59
+ if not query:
60
+ return {"output": "No query found in input or messages"}
61
+
62
+ result = search_transcripts(query)
63
+ return {"output": result}
64
+
65
+ # Use agent_node to create the node
66
+ rag_node = functools.partial(
67
+ agent_node, agent=RunnableLambda(search_agent), name=name
68
+ )
69
+
70
+ return rag_node, search_transcripts
pstuts_rag/pstuts_rag/agent_tavily.py ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Callable, Tuple
2
+ from langchain_community.tools.tavily_search import TavilySearchResults
3
+ from .agents import create_agent, agent_node
4
+ from langchain_core.language_models.chat_models import BaseChatModel
5
+ import functools
6
+ from langchain.agents.agent import AgentExecutor
7
+ from .prompt_templates import TAVILY_SYSTEM
8
+
9
+
10
+ def create_tavily_node(
11
+ llm: BaseChatModel, name: str = "AdobeHelp"
12
+ ) -> Tuple[Callable, AgentExecutor, TavilySearchResults]:
13
+ """Initialize tool, agent, and node for Tavily search of helpx.adobe.com.
14
+
15
+ This function sets up a search agent that can query Adobe Photoshop help topics
16
+ using the Tavily search engine, specifically targeting helpx.adobe.com.
17
+
18
+ Args:
19
+ llm: The language model to power the agent.
20
+ name: The name to assign to the agent node. Defaults to "AdobeHelp".
21
+
22
+ Returns:
23
+ Tuple containing:
24
+ - A callable node function that can be added to a graph
25
+ - The configured agent executor
26
+ - The Tavily search tool instance
27
+ """
28
+
29
+ adobe_help_search = TavilySearchResults(
30
+ max_results=5, include_domains=["helpx.adobe.com"]
31
+ )
32
+ adobe_help_agent = create_agent(
33
+ llm=llm, tools=[adobe_help_search], system_prompt=TAVILY_SYSTEM
34
+ )
35
+ adobe_help_node = functools.partial(
36
+ agent_node, agent=adobe_help_agent, name=name
37
+ )
38
+
39
+ return adobe_help_node, adobe_help_agent, adobe_help_search
pstuts_rag/pstuts_rag/agents.py CHANGED
@@ -1,5 +1,5 @@
1
- from typing import Any, Callable, List, Optional, TypedDict, Union
2
-
3
  from langchain.agents import AgentExecutor, create_openai_functions_agent
4
  from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
5
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
@@ -13,13 +13,19 @@ from langgraph.graph import END, StateGraph
13
  import pstuts_rag.prompt_templates
14
 
15
 
16
- def agent_node(state, agent, name):
 
 
 
 
 
 
17
  """agent_node calls the invoke function of the agent Runnable"""
18
  # Initialize team_members if it's not already in the state
19
  if "team_members" not in state:
20
  state["team_members"] = []
21
  result = agent.invoke(state)
22
- return {"messages": [HumanMessage(content=result["output"], name=name)]}
23
 
24
 
25
  def create_agent(
@@ -31,10 +37,7 @@ def create_agent(
31
  system_prompt += pstuts_rag.prompt_templates.AGENT_SYSTEM
32
  prompt = ChatPromptTemplate.from_messages(
33
  [
34
- (
35
- "system",
36
- system_prompt,
37
- ),
38
  MessagesPlaceholder(variable_name="messages"),
39
  MessagesPlaceholder(variable_name="agent_scratchpad"),
40
  ]
 
1
+ from typing import Any, Callable, List, Optional, TypedDict, Union, Annotated
2
+ import operator
3
  from langchain.agents import AgentExecutor, create_openai_functions_agent
4
  from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
5
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
 
13
  import pstuts_rag.prompt_templates
14
 
15
 
16
+ class PsTutsTeamState(TypedDict):
17
+ messages: Annotated[List[BaseMessage], operator.add]
18
+ team_members: List[str]
19
+ next: str
20
+
21
+
22
+ def agent_node(state, agent, name, outputfield: str = "output"):
23
  """agent_node calls the invoke function of the agent Runnable"""
24
  # Initialize team_members if it's not already in the state
25
  if "team_members" not in state:
26
  state["team_members"] = []
27
  result = agent.invoke(state)
28
+ return {"messages": [HumanMessage(content=result[outputfield], name=name)]}
29
 
30
 
31
  def create_agent(
 
37
  system_prompt += pstuts_rag.prompt_templates.AGENT_SYSTEM
38
  prompt = ChatPromptTemplate.from_messages(
39
  [
40
+ ("system", system_prompt),
 
 
 
41
  MessagesPlaceholder(variable_name="messages"),
42
  MessagesPlaceholder(variable_name="agent_scratchpad"),
43
  ]
pstuts_rag/pstuts_rag/prompt_templates.py CHANGED
@@ -1,11 +1,10 @@
1
- from typing import List, Tuple
2
 
3
- RAG_PROMPT_TEMPLATES: List[Tuple[str, str]] = []
4
 
5
- RAG_PROMPT_TEMPLATES.append(
6
- (
7
- "system",
8
- """\
9
  You are a helpful and friendly Photoshop expert.
10
 
11
  Your job is to answer user questions based **only** on transcript excerpts from training videos. These transcripts include **timestamps** that indicate when in the video the information was spoken.
@@ -32,14 +31,11 @@ The transcript is from **spoken audio**, so it may include informal phrasing, fi
32
  - ❌ Don't guess or summarize from general knowledge.
33
  - ❌ Don’t fabricate steps, names, or features not in the transcript.
34
  - ❌ Don’t omit the fallback response when required.
35
- """,
36
- )
37
- )
38
-
39
- RAG_PROMPT_TEMPLATES.append(
40
- (
41
- "user",
42
- """\
43
  ### Question
44
  {question}
45
 
@@ -48,9 +44,8 @@ NEVER invent the explanation. ALWAYS use ONLY the context information.
48
  ### Context
49
  {context}
50
 
51
- """,
52
- )
53
- )
54
  SUPERVISOR_SYSTEM = """Given the conversation above, who should act next? Or should we FINISH?
55
  If the last answer was 'I don't know', do not FINISH.
56
  Select one of: {options}"""
@@ -58,6 +53,64 @@ Select one of: {options}"""
58
  AGENT_SYSTEM = """Work autonomously according to your specialty, using the tools available to you.
59
  Do not ask for clarification.
60
  Your other team members (and other teams) will collaborate with you with their own specialties.
 
 
 
 
61
 
62
  You are chosen for a reason! You are one of the following team members: {team_members}.
63
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Dict
2
 
3
+ RAG_PROMPT_TEMPLATES: Dict[str, str] = {}
4
 
5
+ RAG_PROMPT_TEMPLATES[
6
+ "system"
7
+ ] = """\
 
8
  You are a helpful and friendly Photoshop expert.
9
 
10
  Your job is to answer user questions based **only** on transcript excerpts from training videos. These transcripts include **timestamps** that indicate when in the video the information was spoken.
 
31
  - ❌ Don't guess or summarize from general knowledge.
32
  - ❌ Don’t fabricate steps, names, or features not in the transcript.
33
  - ❌ Don’t omit the fallback response when required.
34
+ """
35
+
36
+ RAG_PROMPT_TEMPLATES[
37
+ "user"
38
+ ] = """\
 
 
 
39
  ### Question
40
  {question}
41
 
 
44
  ### Context
45
  {context}
46
 
47
+ """
48
+
 
49
  SUPERVISOR_SYSTEM = """Given the conversation above, who should act next? Or should we FINISH?
50
  If the last answer was 'I don't know', do not FINISH.
51
  Select one of: {options}"""
 
53
  AGENT_SYSTEM = """Work autonomously according to your specialty, using the tools available to you.
54
  Do not ask for clarification.
55
  Your other team members (and other teams) will collaborate with you with their own specialties.
56
+ Assume that the question is related to Adobe Photoshop.
57
+
58
+ If you find URLs in your context, make sure to emit them in your output as well
59
+ if you use them to generate the text.
60
 
61
  You are chosen for a reason! You are one of the following team members: {team_members}.
62
  """
63
+
64
+ TAVILY_SYSTEM = """
65
+ You are a research assistant who can search
66
+ for Adobe Photoshop help topics using the tavily search engine.
67
+ Users may provide you with partial questions - try your best to determine their intent.
68
+
69
+ If Tavily provides no references, respond with "I don't know".
70
+
71
+ IMPORTANT: Include ALL urls from all references Tavily provides.
72
+ Separate them from the rest of the text using a line containing "**URL**"
73
+ """
74
+
75
+ SUPERVISOR_SYSTEM = """You are the Supervisor for an agentic RAG system. Your job is to
76
+ interpret the user's request, extract the core research topic, and decide which
77
+ research-focused worker to invoke next. Reply only with the next worker and the
78
+ subject to research, or FINISH when the workflow is complete.
79
+
80
+ Workers
81
+ • VideoArchiveSearch – retrieves videos related to the query
82
+ • AdobeHelp – searches Adobe's documentation and training resources
83
+
84
+ Routing Rules
85
+ 1. Topic Extraction
86
+ • Read the user's request and identify a concise research topic (e.g.
87
+ "Photoshop timeline keyframes").
88
+
89
+ 2. Primary Preference
90
+ • First invoke VideoArchiveSearch with that topic.
91
+ • If VideoArchiveSearch returns "I don't know" or "no results," fall back to
92
+ AdobeHelp.
93
+
94
+ 3. AdobeHelp Behavior
95
+ • Use specific queries to ask AdobeHelp for answers.
96
+ • Always provide URL for the page where you found answers.
97
+ • If returned answer contains new technical terms, query VideoArchiveSearch
98
+ to see if there are any videos on the topic.
99
+
100
+ 4. Research-Only
101
+ • Only invoke workers that perform research tasks.
102
+
103
+ 5. Completion
104
+ • When neither worked can provide value, go to FINISH. If AdobeHelp
105
+ expands the list of topics, make sure to attempt to search for them with the
106
+ VideoArchiveSearch.
107
+
108
+ Response Format
109
+ <WorkerName>: <Research Topic>
110
+
111
+ Example:
112
+ VideoArchiveSearch: exporting vector layers from After Effects
113
+
114
+ And, once there's no further research needed:
115
+ FINISH
116
+ """
pstuts_rag/pstuts_rag/rag.py CHANGED
@@ -90,9 +90,11 @@ class RAGChainFactory:
90
  context=input["context"]
91
  )
92
 
93
- text_w_references = "\n".join(
94
- [answer.content, "**REFERENCES**", references]
95
- )
 
 
96
 
97
  output: AIMessage = answer.model_copy(
98
  update={
@@ -138,9 +140,11 @@ class RAGChainFactory:
138
  }
139
 
140
  self.prompt_template = ChatPromptTemplate.from_messages(
141
- RAG_PROMPT_TEMPLATES
142
  )
143
 
 
 
144
  def get_rag_chain(
145
  self,
146
  llm: BaseChatModel = ChatOpenAI(model="gpt-4.1-mini", temperature=0),
 
90
  context=input["context"]
91
  )
92
 
93
+ text_w_references = answer.content
94
+ if "I don't know" not in answer.content:
95
+ text_w_references = "\n".join(
96
+ [str(text_w_references), "**REFERENCES**", references]
97
+ )
98
 
99
  output: AIMessage = answer.model_copy(
100
  update={
 
140
  }
141
 
142
  self.prompt_template = ChatPromptTemplate.from_messages(
143
+ list(RAG_PROMPT_TEMPLATES.items())
144
  )
145
 
146
+ print(repr)
147
+
148
  def get_rag_chain(
149
  self,
150
  llm: BaseChatModel = ChatOpenAI(model="gpt-4.1-mini", temperature=0),