Rsr2425 commited on
Commit
20eaf62
·
1 Parent(s): d4c5040

Added agent pipeline notebook

Browse files
Files changed (1) hide show
  1. test_agent_system_OLD.ipynb +282 -0
test_agent_system_OLD.ipynb ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 21,
6
+ "metadata": {},
7
+ "outputs": [],
8
+ "source": [
9
+ "import os\n",
10
+ "import getpass\n",
11
+ "\n",
12
+ "os.environ[\"OPENAI_API_KEY\"] = getpass.getpass(\"OpenAI API Key:\")\n",
13
+ "os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"TAVILY_API_KEY\")"
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "code",
18
+ "execution_count": 23,
19
+ "metadata": {},
20
+ "outputs": [
21
+ {
22
+ "name": "stdout",
23
+ "output_type": "stream",
24
+ "text": [
25
+ "Requirement already satisfied: pymupdf in /opt/anaconda3/lib/python3.12/site-packages (1.25.3)\n"
26
+ ]
27
+ }
28
+ ],
29
+ "source": [
30
+ "!pip install pymupdf"
31
+ ]
32
+ },
33
+ {
34
+ "cell_type": "code",
35
+ "execution_count": 25,
36
+ "metadata": {},
37
+ "outputs": [],
38
+ "source": [
39
+ "# Basic RAG Chain\n",
40
+ "from backend.app.vectorstore import get_vector_db\n",
41
+ "\n",
42
+ "qdrant_retriever = get_vector_db().as_retriever()\n",
43
+ "\n",
44
+ "from langchain_core.prompts import ChatPromptTemplate\n",
45
+ "\n",
46
+ "RAG_PROMPT = \"\"\"\n",
47
+ "CONTEXT:\n",
48
+ "{context}\n",
49
+ "\n",
50
+ "QUERY:\n",
51
+ "{question}\n",
52
+ "\n",
53
+ "You are a helpful assistant. Use the available context to answer the question. If you can't answer the question, say you don't know.\n",
54
+ "\"\"\"\n",
55
+ "\n",
56
+ "rag_prompt = ChatPromptTemplate.from_template(RAG_PROMPT)\n",
57
+ "\n",
58
+ "from langchain_openai import ChatOpenAI\n",
59
+ "\n",
60
+ "openai_chat_model = ChatOpenAI(model=\"gpt-4o-mini\")\n",
61
+ "\n",
62
+ "from operator import itemgetter\n",
63
+ "from langchain.schema.output_parser import StrOutputParser\n",
64
+ "\n",
65
+ "rag_chain = (\n",
66
+ " {\"context\": itemgetter(\"question\") | qdrant_retriever, \"question\": itemgetter(\"question\")}\n",
67
+ " | rag_prompt | openai_chat_model | StrOutputParser()\n",
68
+ ")\n"
69
+ ]
70
+ },
71
+ {
72
+ "cell_type": "code",
73
+ "execution_count": 26,
74
+ "metadata": {},
75
+ "outputs": [],
76
+ "source": [
77
+ "# Helper functions\n",
78
+ "from typing import Any, Callable, List, Optional, TypedDict, Union\n",
79
+ "\n",
80
+ "from langchain.agents import AgentExecutor, create_openai_functions_agent\n",
81
+ "from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser\n",
82
+ "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
83
+ "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
84
+ "from langchain_core.runnables import Runnable\n",
85
+ "from langchain_core.tools import BaseTool\n",
86
+ "from langchain_openai import ChatOpenAI\n",
87
+ "\n",
88
+ "from langgraph.graph import END, StateGraph\n",
89
+ "\n",
90
+ "def agent_node(state, agent, name):\n",
91
+ " result = agent.invoke(state)\n",
92
+ " return {\"messages\": [HumanMessage(content=result[\"output\"], name=name)]}\n",
93
+ "\n",
94
+ "def create_agent(\n",
95
+ " llm: ChatOpenAI,\n",
96
+ " tools: list,\n",
97
+ " system_prompt: str,\n",
98
+ ") -> str:\n",
99
+ " \"\"\"Create a function-calling agent and add it to the graph.\"\"\"\n",
100
+ " system_prompt += (\"\\nWork autonomously according to your specialty, using the tools available to you.\"\n",
101
+ " \" Do not ask for clarification.\"\n",
102
+ " \" Your other team members (and other teams) will collaborate with you with their own specialties.\"\n",
103
+ " \" You are chosen for a reason! You are one of the following team members: {{team_members}}.\")\n",
104
+ " prompt = ChatPromptTemplate.from_messages(\n",
105
+ " [\n",
106
+ " (\n",
107
+ " \"system\",\n",
108
+ " system_prompt,\n",
109
+ " ),\n",
110
+ " MessagesPlaceholder(variable_name=\"messages\"),\n",
111
+ " MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
112
+ " ]\n",
113
+ " )\n",
114
+ " agent = create_openai_functions_agent(llm, tools, prompt)\n",
115
+ " executor = AgentExecutor(agent=agent, tools=tools)\n",
116
+ " return executor\n",
117
+ "\n",
118
+ "def create_team_supervisor(llm: ChatOpenAI, system_prompt, members) -> str:\n",
119
+ " \"\"\"An LLM-based router.\"\"\"\n",
120
+ " options = [\"FINISH\"] + members\n",
121
+ " function_def = {\n",
122
+ " \"name\": \"route\",\n",
123
+ " \"description\": \"Select the next role.\",\n",
124
+ " \"parameters\": {\n",
125
+ " \"title\": \"routeSchema\",\n",
126
+ " \"type\": \"object\",\n",
127
+ " \"properties\": {\n",
128
+ " \"next\": {\n",
129
+ " \"title\": \"Next\",\n",
130
+ " \"anyOf\": [\n",
131
+ " {\"enum\": options},\n",
132
+ " ],\n",
133
+ " },\n",
134
+ " },\n",
135
+ " \"required\": [\"next\"],\n",
136
+ " },\n",
137
+ " }\n",
138
+ " prompt = ChatPromptTemplate.from_messages(\n",
139
+ " [\n",
140
+ " (\"system\", system_prompt),\n",
141
+ " MessagesPlaceholder(variable_name=\"messages\"),\n",
142
+ " (\n",
143
+ " \"system\",\n",
144
+ " \"Given the conversation above, who should act next?\"\n",
145
+ " \" Or should we FINISH? Select one of: {options}\",\n",
146
+ " ),\n",
147
+ " ]\n",
148
+ " ).partial(options=str(options), team_members=\", \".join(members))\n",
149
+ " return (\n",
150
+ " prompt\n",
151
+ " | llm.bind_functions(functions=[function_def], function_call=\"route\")\n",
152
+ " | JsonOutputFunctionsParser()\n",
153
+ " )\n"
154
+ ]
155
+ },
156
+ {
157
+ "cell_type": "code",
158
+ "execution_count": 32,
159
+ "metadata": {},
160
+ "outputs": [
161
+ {
162
+ "data": {
163
+ "image/png": "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",
164
+ "text/plain": [
165
+ "<IPython.core.display.Image object>"
166
+ ]
167
+ },
168
+ "metadata": {},
169
+ "output_type": "display_data"
170
+ }
171
+ ],
172
+ "source": [
173
+ "# Research team\n",
174
+ "from langchain_community.tools.tavily_search import TavilySearchResults\n",
175
+ "\n",
176
+ "tavily_tool = TavilySearchResults(max_results=5)\n",
177
+ "\n",
178
+ "from typing import Annotated, List, Tuple, Union\n",
179
+ "from langchain_core.tools import tool\n",
180
+ "\n",
181
+ "@tool\n",
182
+ "def retrieve_information(\n",
183
+ " query: Annotated[str, \"query to ask the retrieve information tool\"]\n",
184
+ " ):\n",
185
+ " \"\"\"Use Retrieval Augmented Generation to retrieve information about the 'Extending Llama-3’s Context Ten-Fold Overnight' paper.\"\"\"\n",
186
+ " return rag_chain.invoke({\"question\" : query})\n",
187
+ "\n",
188
+ "\n",
189
+ "import functools\n",
190
+ "import operator\n",
191
+ "\n",
192
+ "from langchain_core.messages import AIMessage, BaseMessage, HumanMessage\n",
193
+ "from langchain_openai.chat_models import ChatOpenAI\n",
194
+ "import functools\n",
195
+ "\n",
196
+ "class ResearchTeamState(TypedDict):\n",
197
+ " messages: Annotated[List[BaseMessage], operator.add]\n",
198
+ " team_members: List[str]\n",
199
+ " next: str\n",
200
+ "\n",
201
+ "llm = ChatOpenAI(model=\"gpt-4-turbo\")\n",
202
+ "\n",
203
+ "search_agent = create_agent(\n",
204
+ " llm,\n",
205
+ " [tavily_tool],\n",
206
+ " \"You are a research assistant who can search for up-to-date info using the tavily search engine.\",\n",
207
+ ")\n",
208
+ "search_node = functools.partial(agent_node, agent=search_agent, name=\"Search\")\n",
209
+ "\n",
210
+ "research_agent = create_agent(\n",
211
+ " llm,\n",
212
+ " [retrieve_information],\n",
213
+ " \"You are a resarch assistant who can provide information about how to use the library langchain to build an RAG system.\",\n",
214
+ " \"You are a research assistant who can provide specific information on the provided paper: 'Extending Llama-3’s Context Ten-Fold Overnight'. You must only respond with information about the paper related to the request.\",\n",
215
+ ")\n",
216
+ "research_node = functools.partial(agent_node, agent=research_agent, name=\"PaperInformationRetriever\")\n",
217
+ "\n",
218
+ "supervisor_agent = create_team_supervisor(\n",
219
+ " llm,\n",
220
+ " (\"You are a supervisor tasked with managing a conversation between the\"\n",
221
+ " \" following workers: Search, PaperInformationRetriever. Given the following user request,\"\n",
222
+ " \" determine the subject to be researched and respond with the worker to act next. Each worker will perform a\"\n",
223
+ " \" task and respond with their results and status. \"\n",
224
+ " \" You should never ask your team to do anything beyond research. They are not required to write content or posts.\"\n",
225
+ " \" You should only pass tasks to workers that are specifically research focused.\"\n",
226
+ " \" When finished, respond with FINISH.\"),\n",
227
+ " [\"Search\", \"PaperInformationRetriever\"],\n",
228
+ ")\n",
229
+ "\n",
230
+ "research_graph = StateGraph(ResearchTeamState)\n",
231
+ "\n",
232
+ "research_graph.add_node(\"Search\", search_node)\n",
233
+ "research_graph.add_node(\"PaperInformationRetriever\", research_node)\n",
234
+ "research_graph.add_node(\"supervisor\", supervisor_agent)\n",
235
+ "\n",
236
+ "research_graph.add_edge(\"Search\", \"supervisor\")\n",
237
+ "research_graph.add_edge(\"PaperInformationRetriever\", \"supervisor\")\n",
238
+ "research_graph.add_conditional_edges(\n",
239
+ " \"supervisor\",\n",
240
+ " lambda x: x[\"next\"],\n",
241
+ " {\"Search\": \"Search\", \"PaperInformationRetriever\": \"PaperInformationRetriever\", \"FINISH\": END},\n",
242
+ ")\n",
243
+ "\n",
244
+ "research_graph.set_entry_point(\"supervisor\")\n",
245
+ "chain = research_graph.compile()\n",
246
+ "\n",
247
+ "def enter_chain(message: str):\n",
248
+ " results = {\n",
249
+ " \"messages\": [HumanMessage(content=message)],\n",
250
+ " }\n",
251
+ " return results\n",
252
+ "\n",
253
+ "research_chain = enter_chain | chain\n",
254
+ "\n",
255
+ "from IPython.display import Image, display\n",
256
+ "\n",
257
+ "display(Image(chain.get_graph(xray=True).draw_mermaid_png()))"
258
+ ]
259
+ },
260
+ {
261
+ "cell_type": "code",
262
+ "execution_count": null,
263
+ "metadata": {},
264
+ "outputs": [],
265
+ "source": []
266
+ },
267
+ {
268
+ "cell_type": "code",
269
+ "execution_count": null,
270
+ "metadata": {},
271
+ "outputs": [],
272
+ "source": []
273
+ }
274
+ ],
275
+ "metadata": {
276
+ "language_info": {
277
+ "name": "python"
278
+ }
279
+ },
280
+ "nbformat": 4,
281
+ "nbformat_minor": 2
282
+ }