Skip to content

Commit 2cbbb08

Browse files
committed
WIP
1 parent 6a3084a commit 2cbbb08

File tree

2 files changed

+197
-68
lines changed

2 files changed

+197
-68
lines changed

examples/research/pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ readme = "README.md"
99
license = { text = "MIT" }
1010
requires-python = ">=3.9"
1111
dependencies = [
12+
"httpx>=0.28.1",
13+
"markdownify>=1.1.0",
1214
"langchain-anthropic>=0.3.10",
1315
"langchain-mcp-adapters>=0.0.5",
1416
"langgraph>=0.3.21",

examples/research/src/agent/agent.ipynb

Lines changed: 195 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,14 @@
1010
},
1111
"outputs": [],
1212
"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"
1414
]
1515
},
1616
{
1717
"cell_type": "code",
18-
"execution_count": 1,
18+
"execution_count": 9,
1919
"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": [],
3321
"source": [
3422
"# Imports\n",
3523
"from langchain_anthropic import ChatAnthropic\n",
@@ -40,29 +28,31 @@
4028
"\n",
4129
"planner_prompt = \"\"\"\n",
4230
"<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",
4432
"</Task>\n",
4533
"\n",
4634
"<Instructions>\n",
4735
"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",
4937
"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",
5343
"</Instructions>\n",
5444
"\"\"\"\n",
5545
"\n",
5646
"researcher_prompt = \"\"\"\n",
5747
"<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",
5949
"</Task>\n",
6050
"\n",
6151
"<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",
6454
"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",
6656
"</Instructions>\n",
6757
"\"\"\"\n",
6858
"\n",
@@ -72,84 +62,150 @@
7262
"# Handoff tools\n",
7363
"transfer_to_planner_agent = create_handoff_tool(\n",
7464
" 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",
7666
")\n",
7767
"transfer_to_researcher_agent = create_handoff_tool(\n",
7868
" 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",
8070
")\n",
8171
"\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"
86127
]
87128
},
88129
{
89130
"cell_type": "code",
90-
"execution_count": 7,
131+
"execution_count": null,
91132
"metadata": {},
92133
"outputs": [],
93134
"source": [
94135
"# 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})"
96139
]
97140
},
98141
{
99142
"cell_type": "code",
100143
"execution_count": null,
101144
"metadata": {},
102145
"outputs": [],
146+
"source": []
147+
},
148+
{
149+
"cell_type": "markdown",
150+
"metadata": {},
103151
"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",
118166
" \"--from\",\n",
119167
" \"mcpdoc\",\n",
120168
" \"mcpdoc\",\n",
121169
" \"--urls\",\n",
122-
" llms_txt_urls,\n",
170+
" \"LangGraph:https://langchain-ai.github.io/langgraph/llms.txt\",\n",
123171
" \"--transport\",\n",
124172
" \"stdio\",\n",
125173
" \"--port\",\n",
126174
" \"8081\",\n",
127175
" \"--host\",\n",
128176
" \"localhost\"\n",
129177
" ],\n",
130-
" \"transport\": \"stdio\",\n",
131-
" }\n",
132-
" }\n",
178+
")\n",
133179
"\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",
140183
"\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",
146188
"\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",
149191
"\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"
153209
]
154210
},
155211
{
@@ -189,14 +245,85 @@
189245
"execution_count": null,
190246
"metadata": {},
191247
"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+
]
193292
},
194293
{
195294
"cell_type": "code",
196295
"execution_count": null,
197296
"metadata": {},
198297
"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+
]
200327
}
201328
],
202329
"metadata": {

0 commit comments

Comments
 (0)