本地模型+MCP实践

本地模型部署

之前写过一篇LM studio本地部署模型的文章,可以参考实现本地模型的部署或者是使用其他方式

文章链接: LM Studio本地部署大模型-CSDN博客

什么是MCP

MCP(Model Context Protocol,模型上下文协议)是由Anthropic公司推出的开放标准协议,旨在为大型语言模型(LLM)与外部数据源、工具及服务提供统一的通信框架

MCP主机:发起请求的应用(如Claude Desktop)。

MCP客户端:负责与服务器通信。

MCP服务器:提供资源(如数据库)、工具(如API调用)和提示模板 。

如何集成MCP

LM studio本身并不支持mcp,我看有一些其他的本地部署工具是支持的,不过也看模型,因此局限性是有的,所以我们就还用lmstudio,Lm studio提供了兼容openai的接口,因此我们可以使用langchain+openai+mcp来轻松实现本地模型集成mcp

代码

MCP服务端

此处实现一个简单的mcp服务端用来做数学计算, 注意注释部分一定要明确工具的功能,否则模型是不会调用用具计算的

from mcp.server.fastmcp import FastMCP

mcp = FastMCP("数学计算")

@mcp.tool("加法")
def add(a: int, b: int) -> int:
    """计算两个数的和,适用于需要精确加法运算的场景。示例:输入3和5返回8"""
    return a + b

@mcp.tool("乘法")
def multiply(a: int, b: int) -> int:
    """计算两个数的乘积,适用于需要精确乘法运算的场景。示例:输入2和5返回10"""
    return a * b

if __name__ == "__main__":
    mcp.run(transport="stdio")

MCP客户端

import asyncio

from langchain_core.load import dumps
from langchain_mcp_adapters.tools import load_mcp_tools
from langchain_openai import ChatOpenAI
from langgraph.prebuilt import create_react_agent
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    """
    主函数,用于初始化模型、服务器参数,并通过异步会话与服务器进行交互,最终获取并打印代理的响应。

    该函数执行以下步骤:
    1. 初始化ChatOpenAI模型,指定基础URL和模型名称。
    2. 配置服务器参数,指定命令和参数。
    3. 通过stdio_client与服务器建立连接,并创建会话。
    4. 加载MCP工具并创建反应代理。
    5. 向代理发送消息并获取响应,最后将响应以JSON格式打印出来。
    """
    # 初始化ChatOpenAI模型,指定基础URL和模型名称
    model = ChatOpenAI(base_url='http://127.0.0.1:1234/v1', model='qwen2.5-7b-instruct-1m')
    
    # 配置服务器参数,指定命令和参数
    server_params = StdioServerParameters(
        command="python",
        args=["math_server.py"],
    )

    # 通过stdio_client与服务器建立连接,并创建会话
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初始化会话
            await session.initialize()
            
            # 加载MCP工具并创建反应代理
            tools = await load_mcp_tools(session)
            agent = create_react_agent(model, tools)

            # 向代理发送消息并获取响应,最后将响应以JSON格式打印出来
            agent_response = await agent.ainvoke({"messages": "(3 + 5) x 12等于多少?"})
            json_str = dumps(agent_response, pretty=True, ensure_ascii=False)
            print(json_str)



if __name__ == "__main__":
    asyncio.run(main())

结果分析

{
  "messages": [
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "HumanMessage"
      ],
      "kwargs": {
        "content": "(3 + 5) x 12等于多少?",
        "type": "human",
        "id": "17e5ba57-bb0a-4f49-8321-5b2f8d81a85c"
      }
    },
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "AIMessage"
      ],
      "kwargs": {
        "content": "",
        "additional_kwargs": {
          "tool_calls": [
            {
              "id": "774037770",
              "function": {
                "arguments": "{\"a\":3,\"b\":5}",
                "name": "加法"
              },
              "type": "function"
            }
          ],
          "refusal": null
        },
        "response_metadata": {
          "token_usage": {
            "completion_tokens": 61,
            "prompt_tokens": 285,
            "total_tokens": 346,
            "completion_tokens_details": null,
            "prompt_tokens_details": null
          },
          "model_name": "qwen2.5-7b-instruct-1m@q4_k_m",
          "system_fingerprint": "qwen2.5-7b-instruct-1m@q4_k_m",
          "id": "chatcmpl-b9dyxztbiydp2j814hpf2i",
          "finish_reason": "tool_calls",
          "logprobs": null
        },
        "type": "ai",
        "id": "run-85702367-d8d0-4d3a-9453-d08cea063956-0",
        "tool_calls": [
          {
            "name": "加法",
            "args": {
              "a": 3,
              "b": 5
            },
            "id": "774037770",
            "type": "tool_call"
          }
        ],
        "usage_metadata": {
          "input_tokens": 285,
          "output_tokens": 61,
          "total_tokens": 346,
          "input_token_details": {},
          "output_token_details": {}
        },
        "invalid_tool_calls": []
      }
    },
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "ToolMessage"
      ],
      "kwargs": {
        "content": "8",
        "type": "tool",
        "name": "加法",
        "id": "0d617d60-2cb0-48f1-adf2-c86d8e0f0918",
        "tool_call_id": "774037770",
        "status": "success"
      }
    },
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "AIMessage"
      ],
      "kwargs": {
        "content": "",
        "additional_kwargs": {
          "tool_calls": [
            {
              "id": "700353500",
              "function": {
                "arguments": "{\"a\":8,\"b\":12}",
                "name": "乘法"
              },
              "type": "function"
            }
          ],
          "refusal": null
        },
        "response_metadata": {
          "token_usage": {
            "completion_tokens": 26,
            "prompt_tokens": 330,
            "total_tokens": 356,
            "completion_tokens_details": null,
            "prompt_tokens_details": null
          },
          "model_name": "qwen2.5-7b-instruct-1m@q4_k_m",
          "system_fingerprint": "qwen2.5-7b-instruct-1m@q4_k_m",
          "id": "chatcmpl-qd3fv1v1ysd4wp0wrikoam",
          "finish_reason": "tool_calls",
          "logprobs": null
        },
        "type": "ai",
        "id": "run-8ad0b37b-8116-4491-bbc7-122d24893cdb-0",
        "tool_calls": [
          {
            "name": "乘法",
            "args": {
              "a": 8,
              "b": 12
            },
            "id": "700353500",
            "type": "tool_call"
          }
        ],
        "usage_metadata": {
          "input_tokens": 330,
          "output_tokens": 26,
          "total_tokens": 356,
          "input_token_details": {},
          "output_token_details": {}
        },
        "invalid_tool_calls": []
      }
    },
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "ToolMessage"
      ],
      "kwargs": {
        "content": "96",
        "type": "tool",
        "name": "乘法",
        "id": "4bfb8eed-94ef-4717-8334-f2339ef721b1",
        "tool_call_id": "700353500",
        "status": "success"
      }
    },
    {
      "lc": 1,
      "type": "constructor",
      "id": [
        "langchain",
        "schema",
        "messages",
        "AIMessage"
      ],
      "kwargs": {
        "content": "(3 + 5) x 12 equals 96.",
        "additional_kwargs": {
          "refusal": null
        },
        "response_metadata": {
          "token_usage": {
            "completion_tokens": 15,
            "prompt_tokens": 377,
            "total_tokens": 392,
            "completion_tokens_details": null,
            "prompt_tokens_details": null
          },
          "model_name": "qwen2.5-7b-instruct-1m@q4_k_m",
          "system_fingerprint": "qwen2.5-7b-instruct-1m@q4_k_m",
          "id": "chatcmpl-3728wwge3n116qxfcy92sg",
          "finish_reason": "stop",
          "logprobs": null
        },
        "type": "ai",
        "id": "run-184fd050-5d46-487c-8d4c-0ab7626a5aa2-0",
        "usage_metadata": {
          "input_tokens": 377,
          "output_tokens": 15,
          "total_tokens": 392,
          "input_token_details": {},
          "output_token_details": {}
        },
        "tool_calls": [],
        "invalid_tool_calls": []
      }
    }
  ]
}

从控制台日志能知道一共进行了三次调佣 

本地模型+MCP实践_第1张图片

第一步

对模型进行提问

本地模型+MCP实践_第2张图片

第二步

模型调用加法工具

本地模型+MCP实践_第3张图片 第三步

模型调用乘法工具

本地模型+MCP实践_第4张图片

 总结

这只是mcp最简单的一个实现, 现在已经有很多成熟的mcp-server,功能更为强大, 其基本原理还是通过模型来代人来调用某个工具, 经过上述实例, 我们也发现对于mcp-server工具的描述一定要准确, 否则模型无法识别都提供了哪些工具, 有了这种标准协议或许我们以后的模型部署不再需要大而全, 经过简单配置或许我们就实现了模型之间的相互调用,专业的模型干专业的事,实现分布式部署且能力也不差

你可能感兴趣的:(python,ai)