{ "cells": [ { "cell_type": "code", "execution_count": 1, "id": "b3352b1f", "metadata": {}, "outputs": [], "source": [ "# 安装依赖:uv pip install langchain_mcp_adapters langchain-deepseek -i https://pypi.tuna.tsinghua.edu.cn/simple\n", "\n", "from langchain_mcp_adapters.client import MultiServerMCPClient\n", "\n", "# MultiServerMCPClient 可以同时挂多个 MCP Server,给每个 Server 起个名字即可\n", "mcp_client = MultiServerMCPClient({\n", " \"amap\": {\n", " \"transport\": \"sse\",\n", " \"url\": \"https://mcp.api-inference.modelscope.net/6ae8935632f641/sse\"\n", " },\n", " \"mcp_tools\": {\n", " \"url\": \"http://localhost:8000/sse\", # 指向上面启动的 FastMCP 服务\n", " \"transport\": \"sse\",\n", " },\n", " })" ] }, { "cell_type": "code", "execution_count": 2, "id": "5152d261", "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ " + Exception Group Traceback (most recent call last):\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\IPython\\core\\interactiveshell.py\", line 3746, in run_code\n", " | await eval(code_obj, self.user_global_ns, self.user_ns)\n", " | File \"C:\\Users\\29448\\AppData\\Local\\Temp\\ipykernel_56308\\1519922208.py\", line 2, in \n", " | tools = await mcp_client.get_tools()\n", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_mcp_adapters\\client.py\", line 213, in get_tools\n", " | tools_list = await asyncio.gather(*load_mcp_tool_tasks)\n", " | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_mcp_adapters\\tools.py\", line 590, in load_mcp_tools\n", " | async with create_session(\n", " | File \"C:\\Users\\29448\\AppData\\Roaming\\uv\\python\\cpython-3.11.14-windows-x86_64-none\\Lib\\contextlib.py\", line 210, in __aenter__\n", " | return await anext(self.gen)\n", " | ^^^^^^^^^^^^^^^^^^^^^\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_mcp_adapters\\sessions.py\", line 449, in create_session\n", " | async with _create_sse_session(**params) as session:\n", " | File \"C:\\Users\\29448\\AppData\\Roaming\\uv\\python\\cpython-3.11.14-windows-x86_64-none\\Lib\\contextlib.py\", line 210, in __aenter__\n", " | return await anext(self.gen)\n", " | ^^^^^^^^^^^^^^^^^^^^^\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_mcp_adapters\\sessions.py\", line 304, in _create_sse_session\n", " | async with (\n", " | File \"C:\\Users\\29448\\AppData\\Roaming\\uv\\python\\cpython-3.11.14-windows-x86_64-none\\Lib\\contextlib.py\", line 210, in __aenter__\n", " | return await anext(self.gen)\n", " | ^^^^^^^^^^^^^^^^^^^^^\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\mcp\\client\\sse.py\", line 62, in sse_client\n", " | async with anyio.create_task_group() as tg:\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\anyio\\_backends\\_asyncio.py\", line 815, in __aexit__\n", " | raise BaseExceptionGroup(\n", " | ExceptionGroup: unhandled errors in a TaskGroup (1 sub-exception)\n", " +-+---------------- 1 ----------------\n", " | Traceback (most recent call last):\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\mcp\\client\\sse.py\", line 73, in sse_client\n", " | event_source.response.raise_for_status()\n", " | File \"d:\\code\\shuheAI\\.venv\\Lib\\site-packages\\httpx\\_models.py\", line 829, in raise_for_status\n", " | raise HTTPStatusError(message, request=request, response=self)\n", " | httpx.HTTPStatusError: Server error '502 Bad Gateway' for url 'http://localhost:8000/sse'\n", " | For more information check: https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/502\n", " +------------------------------------\n" ] } ], "source": [ "#获取工具\n", "tools = await mcp_client.get_tools()\n", "print(f\"当前可用工具:{[t.name for t in tools]}\")" ] }, { "cell_type": "code", "execution_count": 3, "id": "97bb3998", "metadata": {}, "outputs": [ { "ename": "OpenAIError", "evalue": "Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable.", "output_type": "error", "traceback": [ "\u001b[31m---------------------------------------------------------------------------\u001b[39m", "\u001b[31mOpenAIError\u001b[39m Traceback (most recent call last)", "\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[3]\u001b[39m\u001b[32m, line 12\u001b[39m\n\u001b[32m 8\u001b[39m \n\u001b[32m 9\u001b[39m api_key = os.getenv(\u001b[33m'OPENAI_API_KEY'\u001b[39m)\n\u001b[32m 10\u001b[39m \n\u001b[32m 11\u001b[39m \u001b[38;5;66;03m# 使用 DeepSeek 作为底座模型(国内访问快、性价比高)\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m12\u001b[39m llm = ChatDeepSeek(\n\u001b[32m 13\u001b[39m model=\u001b[33m\"deepseek-chat\"\u001b[39m,\n\u001b[32m 14\u001b[39m api_key=api_key, \u001b[38;5;66;03m# 替换为你自己的 API Key\u001b[39;00m\n\u001b[32m 15\u001b[39m )\n", "\u001b[36mFile \u001b[39m\u001b[32md:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_core\\load\\serializable.py:118\u001b[39m, in \u001b[36mSerializable.__init__\u001b[39m\u001b[34m(self, *args, **kwargs)\u001b[39m\n\u001b[32m 116\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m__init__\u001b[39m(\u001b[38;5;28mself\u001b[39m, *args: Any, **kwargs: Any) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 117\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"\"\"\"\u001b[39;00m \u001b[38;5;66;03m# noqa: D419 # Intentional blank docstring\u001b[39;00m\n\u001b[32m--> \u001b[39m\u001b[32m118\u001b[39m \u001b[30;43msuper\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m)\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43m__init__\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43margs\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43mkwargs\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n", " \u001b[31m[... skipping hidden 1 frame]\u001b[39m\n", "\u001b[36mFile \u001b[39m\u001b[32md:\\code\\shuheAI\\.venv\\Lib\\site-packages\\langchain_deepseek\\chat_models.py:250\u001b[39m, in \u001b[36mChatDeepSeek.validate_environment\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 248\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;28mself\u001b[39m.client \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 249\u001b[39m sync_specific: \u001b[38;5;28mdict\u001b[39m = {\u001b[33m\"\u001b[39m\u001b[33mhttp_client\u001b[39m\u001b[33m\"\u001b[39m: \u001b[38;5;28mself\u001b[39m.http_client}\n\u001b[32m--> \u001b[39m\u001b[32m250\u001b[39m \u001b[38;5;28mself\u001b[39m.root_client = \u001b[30;43mopenai\u001b[39;49m\u001b[30;43m.\u001b[39;49m\u001b[30;43mOpenAI\u001b[39;49m\u001b[30;43m(\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43mclient_params\u001b[39;49m\u001b[30;43m,\u001b[39;49m\u001b[30;43m \u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43m*\u001b[39;49m\u001b[30;43msync_specific\u001b[39;49m\u001b[30;43m)\u001b[39;49m\n\u001b[32m 251\u001b[39m \u001b[38;5;28mself\u001b[39m.client = \u001b[38;5;28mself\u001b[39m.root_client.chat.completions\n\u001b[32m 252\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;28mself\u001b[39m.async_client \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m):\n", "\u001b[36mFile \u001b[39m\u001b[32md:\\code\\shuheAI\\.venv\\Lib\\site-packages\\openai\\_client.py:230\u001b[39m, in \u001b[36mOpenAI.__init__\u001b[39m\u001b[34m(self, api_key, admin_api_key, workload_identity, organization, project, webhook_secret, provider, base_url, websocket_base_url, timeout, max_retries, default_headers, default_query, http_client, _strict_response_validation, _enforce_credentials)\u001b[39m\n\u001b[32m 220\u001b[39m \u001b[38;5;28mself\u001b[39m.admin_api_key = admin_api_key \u001b[38;5;28;01mif\u001b[39;00m provider_runtime \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;28;01melse\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 222\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[32m 223\u001b[39m provider_runtime \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 224\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m _enforce_credentials\n\u001b[32m (...)\u001b[39m\u001b[32m 228\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m.admin_api_key \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 229\u001b[39m ):\n\u001b[32m--> \u001b[39m\u001b[32m230\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m OpenAIError(\n\u001b[32m 231\u001b[39m \u001b[33m\"\u001b[39m\u001b[33mMissing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable.\u001b[39m\u001b[33m\"\u001b[39m\n\u001b[32m 232\u001b[39m )\n\u001b[32m 234\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m organization \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;129;01mand\u001b[39;00m provider_runtime \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m 235\u001b[39m organization = os.environ.get(\u001b[33m\"\u001b[39m\u001b[33mOPENAI_ORG_ID\u001b[39m\u001b[33m\"\u001b[39m)\n", "\u001b[31mOpenAIError\u001b[39m: Missing credentials. Please pass an `api_key`, `workload_identity`, `admin_api_key`, or set the `OPENAI_API_KEY` or `OPENAI_ADMIN_KEY` environment variable." ] } ], "source": [ "from langchain_deepseek import ChatDeepSeek\n", "from langchain.agents import create_agent\n", "from langchain_core.prompts import ChatPromptTemplate\n", "import os\n", "from dotenv import load_dotenv\n", "\n", "load_dotenv()\n", "\n", "api_key = os.getenv('OPENAI_API_KEY')\n", "\n", "# 使用 DeepSeek 作为底座模型(国内访问快、性价比高)\n", "llm = ChatDeepSeek(\n", " model=\"deepseek-chat\",\n", " api_key=api_key, # 替换为你自己的 API Key\n", ")\n", "\n", "async def ask_agent(question: str) -> str:\n", " \"\"\"\n", " 核心调用函数:从 MCP Server 拉取工具 → 注入 Agent → 执行对话。\n", "\n", " 每次调用都会重新拉取工具列表,保证服务端的工具变更能实时生效。\n", " \"\"\"\n", " # 第一步:向所有已配置的 MCP Server 拉取可用工具\n", " tools = await mcp_client.get_tools()\n", "\n", " # 第二步:用工具 + 模型 + 提示词组装 Agent\n", " agent = create_agent(\n", " model=llm,\n", " tools=tools,\n", " system_prompt=\"你是一个出行助手,可以使用地图工具帮用户查天气、规划路线。回答要简洁,不要编造数据。\",\n", " )\n", "\n", " # 第三步:执行对话,让 LLM 自行判断是否需要调用工具\n", " result = await agent.ainvoke({\n", " \"messages\": [{\"role\": \"user\", \"content\": question}]\n", " })\n", "\n", " # 第四步:返回最后一条消息(即 LLM 的最终回复)\n", " return result[\"messages\"][-1].content" ] }, { "cell_type": "code", "execution_count": null, "id": "0a3a305f", "metadata": {}, "outputs": [], "source": [ "import asyncio\n", "\n", "async def main():\n", " # 查天气——LLM 会自动调用 amap 的天气工具\n", " # weather = await ask_agent(\"成都今天天气怎么样?\")\n", " # print(f\"天气查询结果:{weather}\\n\")\n", "\n", " # # 规划路线——不同的问题,LLM 会选择不同的工具\n", " # route = await ask_agent(\"帮我规划一条从春熙路到双流机场的地铁换乘路线\")\n", " # print(f\"路线规划结果:{route}\")\n", "\n", " route = await ask_agent(\"帮助我计算 bmi,我的体重是 100kg,身高 192cm\")\n", " print(f\"路线规划结果:{route}\")" ] }, { "cell_type": "code", "execution_count": null, "id": "a448e38a", "metadata": {}, "outputs": [], "source": [ "await main()" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3.11 (shuheAI)", "language": "python", "name": "shuheai" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.14" } }, "nbformat": 4, "nbformat_minor": 5 }