|
10 | 10 | }, |
11 | 11 | "outputs": [], |
12 | 12 | "source": [ |
13 | | - "! pip install langchain-mcp-adapters langgraph \"langchain[anthropic]\" langgraph-swarm" |
| 13 | + "! pip install langchain-mcp-adapters langgraph \"langchain[anthropic]\" langgraph-swarm httpx markdownify" |
14 | 14 | ] |
15 | 15 | }, |
16 | 16 | { |
17 | 17 | "cell_type": "code", |
18 | | - "execution_count": 1, |
| 18 | + "execution_count": 9, |
19 | 19 | "metadata": {}, |
20 | | - "outputs": [ |
21 | | - { |
22 | | - "ename": "ModuleNotFoundError", |
23 | | - "evalue": "No module named 'langchain_anthropic'", |
24 | | - "output_type": "error", |
25 | | - "traceback": [ |
26 | | - "\u001b[31m---------------------------------------------------------------------------\u001b[39m", |
27 | | - "\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)", |
28 | | - "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[1]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# Imports\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlangchain_anthropic\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m ChatAnthropic\n\u001b[32m 3\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlangchain_mcp_adapters\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mclient\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m MultiServerMCPClient\n\u001b[32m 5\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mlanggraph\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mprebuilt\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m create_react_agent\n", |
29 | | - "\u001b[31mModuleNotFoundError\u001b[39m: No module named 'langchain_anthropic'" |
30 | | - ] |
31 | | - } |
32 | | - ], |
| 20 | + "outputs": [], |
33 | 21 | "source": [ |
34 | 22 | "# Imports\n", |
35 | 23 | "from langchain_anthropic import ChatAnthropic\n", |
|
40 | 28 | "\n", |
41 | 29 | "planner_prompt = \"\"\"\n", |
42 | 30 | "<Task>\n", |
43 | | - "You will help plan the steps to implement a LangGraph application. \n", |
| 31 | + "You will help plan the steps to implement a LangGraph application based on the user's request. \n", |
44 | 32 | "</Task>\n", |
45 | 33 | "\n", |
46 | 34 | "<Instructions>\n", |
47 | 35 | "1. Reflect on the user's request. \n", |
48 | | - "2. Use the list_doc_sources tool to fetch and the fetch_docs tool to read the llms.txt file.\n", |
| 36 | + "2. Use the fetch_doc tool to read this llms.txt file: {llms_txt}\n", |
49 | 37 | "3. Identify documents that are relevant to the user's request.\n", |
50 | | - "4. Ask follow-up questions to help refine the project scope and narrow the set of documents to be used for the project.\n", |
51 | | - "5. When the project scope is clear produce a short description of the project with relevant URLs.\n", |
52 | | - "6. Finally, transfer to transfer_to_researcher_agent.\n", |
| 38 | + "4. Ask any follow-up questions to help refine the project scope and narrow the set of documents to be used for the project.\n", |
| 39 | + "5. When the project scope is clear produce two things: \n", |
| 40 | + "- A short description that lays out the scope of the project \n", |
| 41 | + "- A list of relevant URLs to reference in order to implement the project\n", |
| 42 | + "6. Finally, transfer to the research agent using the transfer_to_researcher_agent tool.\n", |
53 | 43 | "</Instructions>\n", |
54 | 44 | "\"\"\"\n", |
55 | 45 | "\n", |
56 | 46 | "researcher_prompt = \"\"\"\n", |
57 | 47 | "<Task>\n", |
58 | | - "You will perform research on the project scope. \n", |
| 48 | + "You will perform research using provided URLs and use these to implement the solution to the user's request. \n", |
59 | 49 | "</Task>\n", |
60 | 50 | "\n", |
61 | 51 | "<Instructions>\n", |
62 | | - "1. Reflect on the project scope and provided URLs from the planner.\n", |
63 | | - "2. Use the fetch_docs tool to fetch and read each URL.\n", |
| 52 | + "1. Reflect on the project scope and provided URLs.\n", |
| 53 | + "2. Use the fetch_doc tool to fetch and read each URL.\n", |
64 | 54 | "3. Use the information in these URLs to implement the solution to the user's request.\n", |
65 | | - "4. If you need further clarification or additional sources to implement the solution, transfer to transfer_to_planner_agent.\n", |
| 55 | + "4. If you need further clarification or additional sources to implement the solution, then transfer to transfer_to_planner_agent.\n", |
66 | 56 | "</Instructions>\n", |
67 | 57 | "\"\"\"\n", |
68 | 58 | "\n", |
|
72 | 62 | "# Handoff tools\n", |
73 | 63 | "transfer_to_planner_agent = create_handoff_tool(\n", |
74 | 64 | " agent_name=\"planner_agent\",\n", |
75 | | - " description=\"Transfer user to the planner_agent to address clarifying questions or help them plan the steps to complete the user's request.\"\n", |
| 65 | + " description=\"Transfer the user to the planner_agent for clarifying questions related to the user's request.\"\n", |
76 | 66 | ")\n", |
77 | 67 | "transfer_to_researcher_agent = create_handoff_tool(\n", |
78 | 68 | " agent_name=\"researcher_agent\",\n", |
79 | | - " description=\"Transfer user to researcher_agent to perform research on the user's request.\"\n", |
| 69 | + " description=\"Transfer the user to the researcher_agent to perform research and implement the solution to the user's request.\"\n", |
80 | 70 | ")\n", |
81 | 71 | "\n", |
82 | | - "# TODO: Move to configuration\n", |
83 | | - "#configurable = Configuration.from_runnable_config(config)\n", |
84 | | - "#llms_txt_urls = configurable.llms_txt\n", |
85 | | - "llms_txt_urls = \"LangGraph:https://langchain-ai.github.io/langgraph/llms.txt\"\n" |
| 72 | + "# LLMS.txt\n", |
| 73 | + "llms_txt = \"LangGraph:https://langchain-ai.github.io/langgraph/llms.txt\"\n", |
| 74 | + "planner_prompt_formatted = planner_prompt.format(llms_txt=llms_txt)\n" |
| 75 | + ] |
| 76 | + }, |
| 77 | + { |
| 78 | + "cell_type": "code", |
| 79 | + "execution_count": 4, |
| 80 | + "metadata": {}, |
| 81 | + "outputs": [], |
| 82 | + "source": [ |
| 83 | + "import httpx\n", |
| 84 | + "from markdownify import markdownify\n", |
| 85 | + "\n", |
| 86 | + "httpx_client = httpx.Client(follow_redirects=False, timeout=10)\n", |
| 87 | + "\n", |
| 88 | + "def fetch_doc(url: str) -> str:\n", |
| 89 | + " \"\"\" Fetch a document from a URL and return the markdownified text. \n", |
| 90 | + " Args:\n", |
| 91 | + " url (str): The URL of the document to fetch.\n", |
| 92 | + " Returns:\n", |
| 93 | + " str: The markdownified text of the document.\n", |
| 94 | + " \"\"\"\n", |
| 95 | + " \n", |
| 96 | + " try:\n", |
| 97 | + " response = httpx_client.get(url, timeout=10)\n", |
| 98 | + " response.raise_for_status()\n", |
| 99 | + " return markdownify(response.text)\n", |
| 100 | + " except (httpx.HTTPStatusError, httpx.RequestError) as e:\n", |
| 101 | + " return f\"Encountered an HTTP error: {str(e)}\"\n" |
| 102 | + ] |
| 103 | + }, |
| 104 | + { |
| 105 | + "cell_type": "code", |
| 106 | + "execution_count": 10, |
| 107 | + "metadata": {}, |
| 108 | + "outputs": [], |
| 109 | + "source": [ |
| 110 | + "from langgraph.checkpoint.memory import InMemorySaver\n", |
| 111 | + "\n", |
| 112 | + "planner_agent = create_react_agent(model,\n", |
| 113 | + " prompt=planner_prompt_formatted, \n", |
| 114 | + " tools=[fetch_doc,transfer_to_researcher_agent],\n", |
| 115 | + " name=\"planner_agent\") \n", |
| 116 | + "\n", |
| 117 | + "# Researcher agent\n", |
| 118 | + "researcher_agent = create_react_agent(model, \n", |
| 119 | + " prompt=researcher_prompt, \n", |
| 120 | + " tools=[fetch_doc,transfer_to_researcher_agent],\n", |
| 121 | + " name=\"researcher_agent\") \n", |
| 122 | + "\n", |
| 123 | + "# Swarm\n", |
| 124 | + "checkpointer = InMemorySaver()\n", |
| 125 | + "agent_swarm = create_swarm([planner_agent, researcher_agent], default_active_agent=\"planner_agent\")\n", |
| 126 | + "app = agent_swarm.compile(checkpointer=checkpointer)\n" |
86 | 127 | ] |
87 | 128 | }, |
88 | 129 | { |
89 | 130 | "cell_type": "code", |
90 | | - "execution_count": 7, |
| 131 | + "execution_count": null, |
91 | 132 | "metadata": {}, |
92 | 133 | "outputs": [], |
93 | 134 | "source": [ |
94 | 135 | "# Input \n", |
95 | | - "messages = {\"role\": \"user\", \"content\": \"Create a prompt chain that makes and improves a joke based on the user's input.\"}" |
| 136 | + "request = \"Create a LangGraph application that is a prompt chain: it takes a topic from a user, generates a joke, and checks if the joke has a punchline.\"\n", |
| 137 | + "messages = {\"role\": \"user\", \"content\": request}\n", |
| 138 | + "response = app.invoke({\"messages\": messages})" |
96 | 139 | ] |
97 | 140 | }, |
98 | 141 | { |
99 | 142 | "cell_type": "code", |
100 | 143 | "execution_count": null, |
101 | 144 | "metadata": {}, |
102 | 145 | "outputs": [], |
| 146 | + "source": [] |
| 147 | + }, |
| 148 | + { |
| 149 | + "cell_type": "markdown", |
| 150 | + "metadata": {}, |
103 | 151 | "source": [ |
104 | | - "# Research and planning tools \n", |
105 | | - "async with MultiServerMCPClient(\n", |
106 | | - " {\n", |
107 | | - " \"research-server\": {\n", |
108 | | - " \"command\": \"npx\",\n", |
109 | | - " \"args\": [\"@playwright/mcp\"],\n", |
110 | | - " \"transport\": \"stdio\",\n", |
111 | | - " \"env\": {\n", |
112 | | - " \"PATH\": \"/Users/rlm/.cursor/extensions/ms-python.python-2024.12.3-darwin-arm64/python_files/deactivate/zsh:/Users/rlm/Desktop/Code/langgraph-swarm/.venv/bin:/Users/rlm/.bun/bin:/Users/rlm/.poetry/bin:/Users/rlm/Library/Python/3.13/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/Users/rlm/.cargo/bin:/Users/rlm/miniforge3/condabin:/Users/rlm/.local/bin\"\n", |
113 | | - " }\n", |
114 | | - " },\n", |
115 | | - " \"planning-server\": {\n", |
116 | | - " \"command\": \"uvx\",\n", |
117 | | - " \"args\": [\n", |
| 152 | + "### Use MCP " |
| 153 | + ] |
| 154 | + }, |
| 155 | + { |
| 156 | + "cell_type": "code", |
| 157 | + "execution_count": null, |
| 158 | + "metadata": {}, |
| 159 | + "outputs": [], |
| 160 | + "source": [ |
| 161 | + "from langchain_mcp_adapters.client import StdioServerParameters\n", |
| 162 | + "\n", |
| 163 | + "server_params = StdioServerParameters(\n", |
| 164 | + " command=\"uvx\",\n", |
| 165 | + " args=[\n", |
118 | 166 | " \"--from\",\n", |
119 | 167 | " \"mcpdoc\",\n", |
120 | 168 | " \"mcpdoc\",\n", |
121 | 169 | " \"--urls\",\n", |
122 | | - " llms_txt_urls,\n", |
| 170 | + " \"LangGraph:https://langchain-ai.github.io/langgraph/llms.txt\",\n", |
123 | 171 | " \"--transport\",\n", |
124 | 172 | " \"stdio\",\n", |
125 | 173 | " \"--port\",\n", |
126 | 174 | " \"8081\",\n", |
127 | 175 | " \"--host\",\n", |
128 | 176 | " \"localhost\"\n", |
129 | 177 | " ],\n", |
130 | | - " \"transport\": \"stdio\",\n", |
131 | | - " }\n", |
132 | | - " }\n", |
| 178 | + ")\n", |
133 | 179 | "\n", |
134 | | - ") as client:\n", |
135 | | - " # Planner agent\n", |
136 | | - " planner_agent = create_react_agent(model,\n", |
137 | | - " prompt=planner_prompt, \n", |
138 | | - " tools=client.server_name_to_tools[\"planning-server\"].append(transfer_to_researcher_agent),\n", |
139 | | - " name=\"planner_agent\") \n", |
| 180 | + "from mcp import ClientSession, StdioServerParameters\n", |
| 181 | + "from mcp.client.stdio import stdio_client\n", |
| 182 | + "from langchain_mcp_adapters.tools import load_mcp_tools\n", |
140 | 183 | "\n", |
141 | | - " # Researcher agent\n", |
142 | | - " researcher_agent = create_react_agent(model, \n", |
143 | | - " prompt=researcher_prompt, \n", |
144 | | - " tools=client.server_name_to_tools[\"research-server\"].append(transfer_to_planner_agent),\n", |
145 | | - " name=\"researcher_agent\") \n", |
| 184 | + "async with stdio_client(server_params) as (read, write):\n", |
| 185 | + " async with ClientSession(read, write) as session:\n", |
| 186 | + " # Initialize the connection\n", |
| 187 | + " await session.initialize()\n", |
146 | 188 | "\n", |
147 | | - " # Swarm\n", |
148 | | - " agent_swarm = create_swarm([planner_agent, researcher_agent], default_active_agent=\"planner_agent\")\n", |
| 189 | + " # Get tools\n", |
| 190 | + " tools = await load_mcp_tools(session)\n", |
149 | 191 | "\n", |
150 | | - " # app = agent_swarm.compile(config_schema=Configuration)\n", |
151 | | - " agent = agent_swarm.compile()\n", |
152 | | - " response = await agent.ainvoke({\"messages\": messages})\n" |
| 192 | + " planner_agent = create_react_agent(model,\n", |
| 193 | + " prompt=planner_prompt, \n", |
| 194 | + " tools=tools.append(transfer_to_researcher_agent),\n", |
| 195 | + " name=\"planner_agent\") \n", |
| 196 | + "\n", |
| 197 | + " # Researcher agent\n", |
| 198 | + " researcher_agent = create_react_agent(model, \n", |
| 199 | + " prompt=researcher_prompt, \n", |
| 200 | + " tools=tools.append(transfer_to_planner_agent),\n", |
| 201 | + " name=\"researcher_agent\") \n", |
| 202 | + "\n", |
| 203 | + " # Swarm\n", |
| 204 | + " agent_swarm = create_swarm([planner_agent, researcher_agent], default_active_agent=\"planner_agent\")\n", |
| 205 | + "\n", |
| 206 | + " # app = agent_swarm.compile(config_schema=Configuration)\n", |
| 207 | + " agent = agent_swarm.compile()\n", |
| 208 | + " response = await agent.ainvoke({\"messages\": messages})\n" |
153 | 209 | ] |
154 | 210 | }, |
155 | 211 | { |
|
189 | 245 | "execution_count": null, |
190 | 246 | "metadata": {}, |
191 | 247 | "outputs": [], |
192 | | - "source": [] |
| 248 | + "source": [ |
| 249 | + "\n", |
| 250 | + "# Research and planning tools \n", |
| 251 | + "async with MultiServerMCPClient(\n", |
| 252 | + " {\n", |
| 253 | + " \"planning-server\": {\n", |
| 254 | + " \"command\": \"uvx\",\n", |
| 255 | + " \"args\": [\n", |
| 256 | + " \"--from\",\n", |
| 257 | + " \"mcpdoc\",\n", |
| 258 | + " \"mcpdoc\",\n", |
| 259 | + " \"--urls\",\n", |
| 260 | + " llms_txt_urls,\n", |
| 261 | + " \"--transport\",\n", |
| 262 | + " \"stdio\",\n", |
| 263 | + " \"--port\",\n", |
| 264 | + " \"8081\",\n", |
| 265 | + " \"--host\",\n", |
| 266 | + " \"localhost\"\n", |
| 267 | + " ],\n", |
| 268 | + " \"transport\": \"stdio\",\n", |
| 269 | + " }\n", |
| 270 | + " }\n", |
| 271 | + "\n", |
| 272 | + ") as client:\n", |
| 273 | + " # Planner agent\n", |
| 274 | + " planner_agent = create_react_agent(model,\n", |
| 275 | + " prompt=planner_prompt, \n", |
| 276 | + " tools=client.server_name_to_tools[\"planning-server\"].append(transfer_to_researcher_agent),\n", |
| 277 | + " name=\"planner_agent\") \n", |
| 278 | + "\n", |
| 279 | + " # Researcher agent\n", |
| 280 | + " researcher_agent = create_react_agent(model, \n", |
| 281 | + " prompt=researcher_prompt, \n", |
| 282 | + " tools=client.server_name_to_tools[\"planning-server\"].append(transfer_to_planner_agent),\n", |
| 283 | + " name=\"researcher_agent\") \n", |
| 284 | + "\n", |
| 285 | + " # Swarm\n", |
| 286 | + " agent_swarm = create_swarm([planner_agent, researcher_agent], default_active_agent=\"planner_agent\")\n", |
| 287 | + "\n", |
| 288 | + " # app = agent_swarm.compile(config_schema=Configuration)\n", |
| 289 | + " agent = agent_swarm.compile()\n", |
| 290 | + " response = await agent.ainvoke({\"messages\": messages})" |
| 291 | + ] |
193 | 292 | }, |
194 | 293 | { |
195 | 294 | "cell_type": "code", |
196 | 295 | "execution_count": null, |
197 | 296 | "metadata": {}, |
198 | 297 | "outputs": [], |
199 | | - "source": [] |
| 298 | + "source": [ |
| 299 | + "{\n", |
| 300 | + " \"research-server\": {\n", |
| 301 | + " \"command\": \"npx\",\n", |
| 302 | + " \"args\": [\"@playwright/mcp\"],\n", |
| 303 | + " \"transport\": \"stdio\",\n", |
| 304 | + " \"env\": {\n", |
| 305 | + " \"PATH\": \"/Users/rlm/.cursor/extensions/ms-python.python-2024.12.3-darwin-arm64/python_files/deactivate/zsh:/Users/rlm/Desktop/Code/langgraph-swarm/.venv/bin:/Users/rlm/.bun/bin:/Users/rlm/.poetry/bin:/Users/rlm/Library/Python/3.13/bin:/opt/homebrew/bin:/opt/homebrew/sbin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/Users/rlm/.cargo/bin:/Users/rlm/miniforge3/condabin:/Users/rlm/.local/bin\"\n", |
| 306 | + " }\n", |
| 307 | + " },\n", |
| 308 | + " \"planning-server\": {\n", |
| 309 | + " \"command\": \"uvx\",\n", |
| 310 | + " \"args\": [\n", |
| 311 | + " \"--from\",\n", |
| 312 | + " \"mcpdoc\",\n", |
| 313 | + " \"mcpdoc\",\n", |
| 314 | + " \"--urls\",\n", |
| 315 | + " llms_txt_urls,\n", |
| 316 | + " \"--transport\",\n", |
| 317 | + " \"stdio\",\n", |
| 318 | + " \"--port\",\n", |
| 319 | + " \"8081\",\n", |
| 320 | + " \"--host\",\n", |
| 321 | + " \"localhost\"\n", |
| 322 | + " ],\n", |
| 323 | + " \"transport\": \"stdio\",\n", |
| 324 | + " }\n", |
| 325 | + " }" |
| 326 | + ] |
200 | 327 | } |
201 | 328 | ], |
202 | 329 | "metadata": { |
|
0 commit comments