当前位置: 首页 > news >正文

Model Context Protocol (MCP)笔记

目录

  • 摘要
  • MCP理论
    • MCP的作用
    • MCP 传输机制 Stdio 与 SSE
      • STDIO
      • SSE 传输
      • 部署模式
    • 模型是如何确定工具的选用的?
    • Fc x MCP x LangChain
  • MCP快速开始
    • 编写客户端
    • 基于golang的mcp

摘要

Model Context Protocol(MCP,模型上下文协议)是由 Anthropic 于 2024 年 11 月推出的一项开放标准,旨在解决大型语言模型(LLMs)与外部工具和数据源集成的复杂性问题。在 MCP 出现之前,开发者通常需要为每个数据源或工具编写定制的连接器,导致集成工作繁琐且难以维护。(维基百科,自由的百科全书, Wikipedia)

MCP 的核心理念是提供一个统一的通信协议,使得 LLMs 能够通过标准化的方式访问外部资源、调用工具函数,并利用预设提示模板来增强其功能。该协议基于 JSON-RPC 2.0,支持多种通信方式,包括本地的标准输入输出(stdio)和网络传输(如 HTTP 和 Server-Sent Events)。(维基百科,自由的百科全书, Wikipedia)

自发布以来,MCP 得到了广泛的支持和应用。例如,开发者可以通过 MCP 让 AI 助手直接访问 GitHub 仓库,执行代码检索和提交操作;或连接企业内部的数据库,实现实时数据查询和分析。目前,已有多家知名公司和开源项目采用 MCP,包括 OpenAI、Google DeepMind、Replit 和 Sourcegraph 等。(全栈开发网, Wikipedia)

MCP 的出现标志着 AI 系统向更高效、可扩展和标准化方向发展的重要一步,为构建更智能、灵活的 AI 应用奠定了坚实的基础。(维基百科,自由的百科全书)

MCP理论

MCP 是一个开放协议,用于标准化应用程序向大语言模型(LLMs)提供上下文的方式。

可以将 MCP 想象成 AI 应用中的 USB-C 接口。就像 USB-C 提供了一个标准化的方式,让设备可以连接各种外设和配件一样,MCP 提供了一个统一的标准,使 AI 模型能够连接到不同的数据源和工具。

为什么选择 MCP?

MCP 帮助你在 LLM 之上构建智能代理和复杂的工作流。由于 LLM 通常需要集成数据和工具,MCP 提供了以下优势:

  • 越来越多的预构建集成,可供你的 LLM 直接连接使用
  • 在不同的 LLM 提供商和厂商之间灵活切换的能力
  • 实现数据在你自有基础设施内安全使用的最佳实践

通用架构:

在这里插入图片描述

从本质上讲,MCP 采用的是一种客户端-服务器架构,其中一个宿主应用可以连接多个服务器:

  • MCP 宿主(MCP Hosts):如 Claude Desktop、集成开发环境(IDEs)或其他希望通过 MCP 访问数据的 AI 工具
  • MCP 客户端(MCP Clients):协议客户端,负责与每个服务器建立 1:1 的连接
  • MCP 服务器(MCP Servers):轻量级程序,每个服务器通过标准化的 Model Context Protocol 提供特定功能
  • 本地数据源(Local Data Sources):如计算机上的文件、数据库和服务,MCP 服务器可以安全地访问这些资源
  • 远程服务(Remote Services):可通过互联网访问的外部系统(例如通过 API 提供服务),MCP 服务器可以与其建立连接

在这里插入图片描述
假设你正在使用支持 MCP(Model Context Protocol)的 AI 应用(如 Claude Desktop)询问“明天天气怎么样?”时,整个请求的处理流程如下:

  1. 用户输入请求
    你在 Claude Desktop 中输入:“明天天气怎么样?”

  2. LLM 解析意图并生成函数调用
    Claude 的语言模型(LLM)解析你的自然语言请求,识别出需要获取天气预报的信息。它生成一个结构化的函数调用请求,例如:

    {"method": "get-forecast","arguments": {"location": "Osaka","date": "2025-05-06"}
    }
    
  3. MCP 客户端转发请求
    Claude Desktop 内置的 MCP 客户端接收到这个函数调用请求,并通过 MCP 协议将其转发给已连接的 MCP 服务器。

  4. MCP 服务器处理请求
    MCP 服务器是一个轻量级程序,专门处理特定功能。在本例中,它提供了一个名为 get-forecast 的工具,用于获取天气预报。服务器接收到请求后,调用相应的天气 API(如 OpenWeatherMap)获取大阪市 2025 年 5 月 6 日的天气预报数据。

  5. MCP 服务器返回响应
    MCP 服务器将获取到的天气信息封装成响应,返回给 MCP 客户端。例如:

    {"forecast": "2025年5月6日,大阪市多云,最高气温22°C,最低气温15°C,有小雨。"
    }
    
  6. LLM 生成自然语言回复
    Claude 的语言模型接收到天气信息后,将其转化为自然语言回复,例如:“明天大阪市多云,最高气温22°C,最低气温15°C,有小雨。”

  7. 用户收到回复
    最终,你在 Claude Desktop 中看到 AI 的回复,完成整个请求流程。

MCP的作用

上文提到,MCP定义了应用程序和 AI 模型之间交换上下文信息的方式

这就使得开发者能够以一致的方式将各种数据源、工具和功能连接到 AI 模型,就像 USB-C 让不同设备能够通过相同的接口连接一样。MCP 的目标是创建一个通用标准,使 AI 应用程序的开发和集成变得更加简单和统一。

在这里插入图片描述

想象一下没有 MCP 之前我们会怎么做?

我们可能会人工从数据库中筛选或者使用工具检索可能需要的信息,手动的粘贴到 prompt 中。随着我们要解决的问题越来越复杂,手工把信息引入到 prompt 中会变得越来越困难。

为了克服手工 prompt 的局限性,许多 LLM 平台(如 OpenAI、Google)引入了 function call 功能。这一机制允许模型在需要时调用预定义的函数来获取数据或执行操作,显著提升了自动化水平。

但是 function call 也有其局限性,不同 LLM 平台的 function call API 实现差异较大。例如,OpenAI 的函数调用方式与 Google 的不兼容,开发者在切换模型时需要重写代码,增加了适配成本。再比如,虽然许多 LLM 提供商都采用了类似的 JSON schema 来定义函数调用,但在具体实现上仍存在一些关键差异。

  • OpenAI:使用符合 OpenAPI JSON schema 的结构,函数定义包含 namedescriptionparameters 等字段。(DEV Community)

  • Anthropic Claude:采用类似的结构,但参数定义字段为 input_schema,而不是 OpenAI 的 parameters。(Superface: Connect AI agents)

  • Cohere Command-R:使用 parameter_definitions 字段,并在每个参数中显式指定是否为必需项,而不是使用单独的 required 数组。(DEV Community)

  • Google Gemini:遵循 OpenAPI JSON schema,但在请求结构上有所不同,例如使用 function_declarations 来包装函数,并在 tool_config 中指定函数调用配置。(Medium)

这些差异意味着,开发者在从一个平台切换到另一个平台时,可能需要调整函数定义的结构,以确保兼容性。除此之外,还有安全性,交互性等问题。

  • 安全性:研究表明,LLM 的函数调用功能可能被滥用,导致执行未经授权的操作。例如,攻击者可能通过精心设计的输入诱导模型调用敏感函数,如删除用户账户等。

  • 交互性:早期的实现中,模型通常在每次交互中只能调用一个函数,这限制了多步骤工作流的实现。虽然一些平台已经开始支持并行或多函数调用,但仍需开发者手动协调函数之间的依赖关系。(mikelev.in)

API 就像一扇扇独立的门——每扇门都有各自的钥匙和规则

在这里插入图片描述

MCP 传输机制 Stdio 与 SSE

Model Context Protocol (MCP) 支持两种主要的传输机制,用于 Cline 和 MCP 服务器之间的通信:标准输入/输出 (STDIO) 和服务器发送事件 (SSE)。每种机制都有其独特的特点、优势和适用场景。

STDIO

STDIO 传输在本地机器上运行,并通过标准输入/输出流进行通信:

  • 客户端 (Cline) 将 MCP 服务器作为子进程启动
  • 通信通过进程流进行:客户端写入服务器的 STDIN,服务器通过 STDOUT 响应
  • 每条消息以换行符分隔
  • 消息格式为 JSON-RPC 2.0
客户端                服务器|                     ||<---- JSON消息 ----->| (通过STDIN)|                     | (处理请求)|<---- JSON消息 ------| (通过STDOUT)|                     |

STDIO 特性:

  • 本地性:与 Cline 在同一台机器上运行
  • 性能:非常低的延迟和开销(不涉及网络栈)
  • 简单性:无需网络配置的直接进程通信
  • 关系:客户端和服务器之间是一对一关系
  • 安全性:由于没有网络暴露,因此本质上更安全

STDIO 传输适用于:

  • 在同一机器上运行的本地集成和工具
  • 安全敏感操作
  • 低延迟需求
  • 单客户端场景(每个服务器一个 Cline 实例)
  • 命令行工具或 IDE 扩展

SSE 传输

服务器发送事件 (SSE) 传输在远程服务器上运行,并通过 HTTP/HTTPS 进行通信:

  • 客户端 (Cline) 通过 HTTP GET 请求连接到服务器的 SSE 端点
  • 这建立了一个持久连接,服务器可以通过该连接向客户端推送事件
  • 对于客户端到服务器的通信,客户端向单独的端点发出 HTTP POST 请求
  • 通信通过两个通道进行:
    • 事件流 (GET):服务器到客户端的更新
    • 消息端点 (POST):客户端到服务器的请求
客户端                服务器|                     ||---- HTTP GET /events ----------->| (建立 SSE 连接)|<---- SSE 事件流 --------------| (持久连接)|                     ||---- HTTP POST /message -------->| (客户端请求)|<---- 带响应的 SSE 事件 ---------| (服务器响应)|                     |

SSE 特性

  • 远程访问:可以托管在与您的 Cline 实例不同的机器上
  • 可扩展性:可以同时处理多个客户端连接
  • 协议:通过标准 HTTP 工作(不需要特殊协议)
  • 持久性:为服务器到客户端的消息维持持久连接
  • 认证:可以使用标准 HTTP 认证机制

SSE 传输更适合:

  • 跨网络的远程访问
  • 多客户端场景
  • 公共服务
  • 许多用户需要访问的集中式工具
  • 与 Web 服务集成

部署模式

MCP 支持两种主要的传输方式:STDIOSSE,它们决定了服务器的部署模式——是本地运行,还是作为远程服务托管运行。

🖥️ STDIO:本地部署模型

STDIO 模式下,MCP 服务作为 子进程 与客户端运行在 同一台机器,适合本地模型接入场景。

✅ 特点:

  • 安装:每台用户机器都需要安装服务器(例如通过 pip、npm、pkg)
  • 分发:需要为不同平台打包安装器(Windows/macOS/Linux)
  • 更新:每台设备需要手动或自动更新
  • 资源占用:使用本地 CPU、内存、磁盘资源
  • 权限控制:继承本地文件系统的访问权限
  • 系统集成:方便访问文件、运行 shell 命令、控制本地服务等
  • 生命周期:随着客户端进程一起启动与销毁
  • 依赖管理:服务所需依赖必须安装在用户的本地环境

📌 示例:

本地文件搜索工具:

  • 安装在用户机器
  • 直接访问本地磁盘
  • 用户点击时自动调用
  • 不需要网络
  • 与前端一起打包或通过包管理器安装

☁️ SSE:托管部署模型

SSE 模式通过 HTTP 网络通信,允许 MCP 服务运行在远程服务器、容器或云平台上,适合集中管理和多用户共享场景。

✅ 特点:

  • 安装:仅需在服务端部署一次
  • 分发:一个服务支持多个客户端连接
  • 更新:服务端统一更新即生效
  • 资源使用:使用远程服务器资源
  • 权限控制:使用认证/授权机制(如 Token、OAuth)
  • 集成能力:访问受控远程资源(如数据库、API)
  • 运行模式:常驻后台服务,持续运行
  • 依赖管理:集中在服务端维护依赖,客户端无需关心

📌 示例:

数据库查询工具:

  • 运行在远程服务器
  • 使用统一配置连接数据库
  • 多用户共享服务
  • 需要网络访问
  • 可通过 Docker/K8s 等方式部署

🔁 混合部署(Hybrid)

根据需求,您还可以使用混合策略:

  • STDIO + 网络访问:本地代理连接远程服务
  • SSE + 本地回调:远程服务通过回调控制本地操作
  • 网关模式:本地服务器代理访问多个远程功能模块

📊 STDIO vs SSE 对比表

考虑因素STDIO(本地)SSE(托管)
位置本地机器本地或远程服务器
客户端单客户端多客户端共享
性能低延迟,无网络开销网络延迟可能较高
部署复杂度简单(无服务端)高(需要 HTTP 服务)
安全性继承本地权限,安全性好需配置认证/授权
网络要求无需网络需要稳定网络连接
可扩展性有限,受限于本机资源高,可水平扩展
更新管理每台机器各自更新服务端集中更新
资源占用使用客户端资源使用服务器资源
依赖管理安装在本地安装在服务器端

模型是如何确定工具的选用的?

当你在使用支持 MCP(Model Context Protocol)的 AI 应用(如 Claude Desktop 或 Cursor)提出问题时,例如“明天天气怎么样?”,整个请求的处理流程可以分为两个主要阶段:

🧠 阶段一:LLM 决策与工具选择

  1. 用户输入请求
    你在 Claude Desktop 中输入:“明天天气怎么样?”

  2. LLM 解析意图
    Claude 的语言模型(LLM)解析你的自然语言请求,识别出需要获取天气预报的信息。

  3. 工具选择
    LLM 检查可用的 MCP 工具列表,决定使用哪个工具来获取所需信息。例如,选择名为 get-forecast 的天气预报工具。

⚙️ 阶段二:工具调用与结果处理

  1. MCP 客户端转发请求
    Claude Desktop 内置的 MCP 客户端将函数调用请求通过 MCP 协议转发给相应的 MCP 服务器。

  2. MCP 服务器处理请求
    MCP 服务器接收到请求后,调用相应的天气 API(如 OpenWeatherMap)获取天气预报数据。

  3. 返回响应
    MCP 服务器将获取到的天气信息封装成响应,返回给 MCP 客户端。

  4. LLM 生成回复
    Claude 的语言模型接收到天气信息后,将其转化为自然语言回复,例如:“明天大阪市多云,最高气温22°C,最低气温15°C,有小雨。”

  5. 用户收到回复
    最终,你在 Claude Desktop 中看到 AI 的回复,完成整个请求流程。

🔄 总结:两阶段协同工作

  • 阶段一(决策层):由 LLM 解析用户意图并选择合适的工具。
  • 阶段二(执行层):通过 MCP 协议调用选定的工具,获取并处理结果。

这种架构使得 AI 应用能够高效、安全地与各种数据源和工具集成,实现如获取天气信息等功能,提升用户体验。

在这里插入图片描述
模型如何确定该使用哪些工具?这里以 MCP 官方提供的 client example 为例。通过阅读代码,可以发现模型是通过 prompt 来确定当前有哪些工具。

async def start(self):# 初始化所有的 mcp serverfor server in self.servers:await server.initialize()​# 获取所有的 tools 命名为 all_toolsall_tools = []for server in self.servers:tools = await server.list_tools()all_tools.extend(tools)​# 将所有的 tools 的功能描述格式化成字符串供 LLM 使用# tool.format_for_llm() 我放到了这段代码最后,方便阅读。tools_description = "\n".join([tool.format_for_llm() for tool in all_tools])​# 这里就不简化了,以供参考,实际上就是基于 prompt 和当前所有工具的信息# 询问 LLM(Claude) 应该使用哪些工具。system_message = ("You are a helpful assistant with access to these tools:\n\n"f"{tools_description}\n""Choose the appropriate tool based on the user's question. ""If no tool is needed, reply directly.\n\n""IMPORTANT: When you need to use a tool, you must ONLY respond with ""the exact JSON object format below, nothing else:\n""{\n"'    "tool": "tool-name",\n''    "arguments": {\n''        "argument-name": "value"\n'"    }\n""}\n\n""After receiving a tool's response:\n""1. Transform the raw data into a natural, conversational response\n""2. Keep responses concise but informative\n""3. Focus on the most relevant information\n""4. Use appropriate context from the user's question\n""5. Avoid simply repeating the raw data\n\n""Please use only the tools that are explicitly defined above.")messages = [{"role": "system", "content": system_message}]...class Tool:"""Represents a tool with its properties and formatting."""​def __init__(self, name: str, description: str, input_schema: dict[str, Any]) -> None:self.name: str = nameself.description: str = descriptionself.input_schema: dict[str, Any] = input_schema​def format_for_llm(self) -> str:"""Format tool information for LLM.​Returns:A formatted string describing the tool."""args_desc = []if "properties" in self.input_schema:for param_name, param_info in self.input_schema["properties"].items():arg_desc = (f"- {param_name}: {param_info.get('description', 'No description')}")if param_name in self.input_schema.get("required", []):arg_desc += " (required)"args_desc.append(arg_desc)return f"""Tool: {self.name}Description: {self.description}Arguments:{chr(10).join(args_desc)}""""""

那 tool 的描述和代码中的 input_schema 是从哪里来的呢?通过进一步分析 MCP 的 Python SDK 源代码可以发现:大部分情况下,当使用装饰器 @mcp.tool() 来装饰函数时,对应的 name 和 description 等其实直接源自用户定义函数的函数名以及函数的 docstring 等。

@classmethod
def from_function(cls,fn: Callable,name: str | None = None,description: str | None = None,context_kwarg: str | None = None,
) -> "Tool":"""Create a Tool from a function."""func_name = name or fn.__name__ # 获取函数名
​if func_name == "<lambda>":raise ValueError("You must provide a name for lambda functions")
​func_doc = description or fn.__doc__ or ""is_async = inspect.iscoroutinefunction(fn)

总结:模型是通过 prompt engineering,即提供所有工具的结构化描述和 few-shot 的 example 来确定该使用哪些工具。

写到这里突然想到了langChain的Tool抽象,就是为了更方便的定义工具,让LLM可以正常使用。从函数创建工具可能足以满足大多数用例,可以通过简单的 @tool 装饰器 来完成。

比如我们要生成一个加法计算的工具,我们可以这样做:

from langchain_core.tools import tool@tool
def multiply(a: int, b: int) -> int:"""Multiply two numbers."""return a * b# Let's inspect some of the attributes associated with the tool.
print(multiply.name)
print(multiply.description)
print(multiply.args)multiply
Multiply two numbers.
{'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}

而一个tool最重要的三要素:参数,name,描述,也是通过函数签名部分可以直接拿到。那么至此,我们就可以总结出 Function calling x MCP x LangChain的关系!

Fc x MCP x LangChain

🔧 Function Calling:LLM 与外部函数的桥梁

Function Calling 是 OpenAI 于 2023 年推出的功能,允许大语言模型(LLM)生成结构化的函数调用请求。开发者可以预定义函数的名称、参数和描述,模型在解析用户输入后,生成相应的函数调用请求,开发者再根据请求调用实际的后端函数或 API。

特点:

  • 结构化调用:模型生成的函数调用请求为 JSON 格式,易于解析和处理。
  • 平台依赖性:不同平台(如 OpenAI、Anthropic、Google)的函数调用实现存在差异,可能导致兼容性问题。
  • 适用于简单任务:适合处理单步、明确的任务调用。

🧰 LangChain:构建 LLM 应用的开发框架

LangChain 是一个用于构建由 LLM 驱动的应用程序的开发框架。它提供了工具、链(Chains)和代理(Agents)等组件,帮助开发者管理与 LLM 的交互,并集成外部工具和数据源。(CSDN博客)

特点:

  • 模块化设计:通过工具、链和代理等组件,支持构建复杂的应用逻辑。
  • 工具集成:允许开发者将外部函数或 API 封装为工具,供代理在任务执行中调用。
  • 支持多模型:兼容多种 LLM 提供商,提供统一的接口。

🔌 MCP(Model Context Protocol):标准化的模型上下文协议

MCP 是 Anthropic 提出的开放协议,旨在标准化 LLM 与外部工具和数据源的交互方式。它采用客户端-服务器架构,允许 LLM 通过 MCP 客户端与多个 MCP 服务器通信,从而访问各种工具和数据。(CSDN博客)

特点:

  • 标准化接口:定义统一的协议和数据格式,促进工具和数据源的互操作性。
  • 多样化工具接入:支持通过 MCP 服务器接入本地或远程的工具和数据源。
  • 灵活的传输协议:支持多种通信方式,如 HTTP、WebSocket、标准输入输出等。(CSDN博客)

🔄 三者关系总结

维度Function CallingLangChainMCP(Model Context Protocol)
定位LLM 与外部函数的桥梁构建 LLM 应用的开发框架标准化的模型上下文协议
核心功能结构化函数调用工具集成、链式处理、代理任务管理标准化工具和数据源的接入与通信
扩展性受限于平台实现高度模块化,支持自定义扩展支持多种工具和数据源的接入
适用场景简单、单步的函数调用构建复杂的 LLM 应用构建具备上下文感知的智能代理系统
与其他组件关系可作为 LangChain 工具的调用方式可集成 Function Calling 和 MCP 工具可通过适配器集成到 LangChain 中

🧩 实际应用中的协同方式

在实际应用中,Function Calling、LangChain 和 MCP 可以协同工作,构建功能强大的 LLM 应用。例如:

  1. Function Calling 与 LangChain:开发者可以将 Function Calling 定义的函数封装为 LangChain 的工具,供代理在任务执行中调用。

  2. MCP 与 LangChain:通过使用如 langchain-mcp-adapters 这样的适配器库,开发者可以将 MCP 服务器提供的工具和资源转换为 LangChain 可用的格式,实现无缝集成 。(CSDN博客)

  3. 综合应用:在一个复杂的 LLM 应用中,LangChain 作为框架,集成了通过 Function Calling 定义的函数和通过 MCP 接入的工具,代理根据任务需求调用相应的工具,完成多步骤的任务执行。

说到协同就涉及到传统的开发范式和MCP下的开发范式了:

在传统的开发模式中,工具函数通常被直接集成在应用程序的代码库中,作为内部函数存在。而在 MCP 架构中,工具被定义在 MCP 服务器中,并通过标准化的接口向 MCP 客户端(如 Claude Desktop、Cursor 等)暴露。这种设计允许 LLM(大语言模型)通过 MCP 客户端调用这些外部工具,实现与外部系统的交互、数据处理等功能。

具体而言,MCP 工具的定义包括:

  • 名称(name):工具的唯一标识符。
  • 描述(description):供人类阅读的工具说明。
  • 输入模式(inputSchema):定义工具所需参数的 JSON Schema。(首页, MCP 中文文档)

MCP 客户端可以通过 tools/list 接口获取可用工具列表,并通过 tools/call 接口调用特定工具。 (首页)

传统的工具集成方式通常将工具函数直接嵌入到应用程序中,这种方式的特点是:

  • 紧耦合:工具函数与应用程序代码紧密集成,修改或替换工具可能需要更改应用程序代码。
  • 扩展性差:添加新工具或修改现有工具可能涉及到应用程序的重新部署。

而 MCP 的外部化机制具有以下优势:

  • 模块化:工具作为独立服务存在,便于开发、测试和部署。
  • 灵活性:可以根据需要动态添加、修改或移除工具,而无需修改主应用程序。
  • 可扩展性:支持多种通信协议(如 HTTP、WebSocket 等),便于与不同系统集成。

mcp_function_call_server 项目为例,该项目实现了一个兼容 MCP 协议的工具服务器,提供了如 Web 搜索、天气查询等功能。这些工具被定义在服务器的 tools/ 目录中,并通过 MCP 协议向外部暴露。LLM 应用程序可以通过 MCP 客户端调用这些工具,实现相应的功能。 (MCP)

同时langChain也提供了langchain_mcp_adapters用于连接MCP Server将远程的服务转为本地的Tool:

from langgraph.prebuilt import create_react_agent
from langchain_openai import ChatOpenAI
from langchain_mcp_adapters.client import MultiServerMCPClientimport asyncioasync def main():async with MultiServerMCPClient({"weather": {# make sure you start your weather server on port 8000"url": "http://localhost:8990/sse","transport": "sse",}}) as client:avTools = client.get_tools()print(avTools)if __name__ == "__main__":asyncio.run(main())

通过 MCP 的架构设计,工具函数被外部化为独立的服务,主应用程序通过标准化的协议与这些工具进行交互。这种方式提高了系统的模块化程度,增强了灵活性和可扩展性,适应了现代 AI 应用对动态集成和快速迭代的需求。

MCP快速开始

MCP 和 LLMs 没有直接关系,只要模型具备一定的指令遵循能力,就可以使用 MCP,这也是 MCP 区别于 Function Call 的地方,就是为了更通用。

MCP 虽然是 Anthropic 提出来的,但完全可以脱离 Claude 的环境和生态进行使用,也就是说,你既不需要用 Claude 的服务器或客户端,也不需要用 Claude 的模型或 API,可以自己写 MCP Server,可以用第三方的 Client,可以用任何一个模型进行对话和工具调用。

这里我们使用官方的python案例:

在这里插入图片描述
使用pip快速安装MCP的python实现:pip install mcp,然后我们可以集成一系列操作到app中,比如物联网设备的操作,网络请求web search,数据库查询操作,宿主机操作…

除此之外还需要:uv工具,uv是一个Python依赖管理工具,可以通过pip install uv来安装!

from mcp.server.fastmcp import FastMCP# Initialize FastMCP server
mcp_app = FastMCP("weather")@mcp_app.tool()
async def get_forecast(latitude: float, longitude: float) -> str:"""Get weather forecast for a location.Args:latitude: Latitude of the locationlongitude: Longitude of the location"""return f"--{latitude}°--{longitude}°--的温度是: 37°C"@mcp_app.tool()
def read_file(file_path: str) -> str:"""读取指定路径的文件内容。Args:file_path: 文件的完整路径Returns:文件的内容"""with open(file_path, 'r', encoding='utf-8') as file:return file.read()@mcp_app.tool()
def open_the_fucking_door(ip: str) -> str:"""打开指定ip的智能门Args:ip: ip地址"""return f"ip为:{ip}的门已打开!"if __name__ == "__main__":# Initialize and run the servermcp_app.run(transport='stdio')

然后我们启动服务,如果没有node和npm可能需要提前安装:

在这里插入图片描述
这些命令会将 npm 的全局安装路径和缓存路径分别设置为您指定的目录:

npm config set prefix "D:\nodejs\node_global"
npm config set cache "D:\nodejs\node_cache"验证:
npm config get prefix
npm config get cache

在这里插入图片描述
打开上文地址:http://127.0.0.1:6274/,就可以看到开发者模式下的看板:

在这里插入图片描述
我们让其打开一扇门,可以看到右边输出了我们的debug信息!

在这里插入图片描述

编写客户端

MCP是典型的C/S架构! 我们作为开发者,除了要写MCP Server外,还需要编写客户端将工具|模型|用户输入糅和在一起产生一个理想的输出!

import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_clientclass MCPClient:def __init__(self):# Initialize session and client objectsself.session: Optional[ClientSession] = Noneself.exit_stack = AsyncExitStack()# 暂时不初始化self.llm = Noneasync def connect_to_server(self):server_script_path = "./weather.py"is_python = server_script_path.endswith('.py')is_js = server_script_path.endswith('.js')if not (is_python or is_js):raise ValueError("Server script must be a .py or .js file")command = "python" if is_python else "node"server_params = StdioServerParameters(command=command,args=[server_script_path],env=None)stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))self.stdio, self.write = stdio_transportself.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))await self.session.initialize()# List available toolsresponse = await self.session.list_tools()tools = response.toolsprint("\nConnected to server with tools:", [tool.name for tool in tools])async def process_query(self, query: str) -> str:"""Process a query using Claude and available tools"""messages = [{"role": "user","content": query}]response = await self.session.list_tools()available_tools = [{"name": tool.name,"description": tool.description,"input_schema": tool.inputSchema} for tool in response.tools]# Initial Claude API callresponse = self.anthropic.messages.create(model="claude-3-5-sonnet-20241022",max_tokens=1000,messages=messages,tools=available_tools)# Process response and handle tool callsfinal_text = []assistant_message_content = []for content in response.content:if content.type == 'text':final_text.append(content.text)assistant_message_content.append(content)elif content.type == 'tool_use':tool_name = content.nametool_args = content.input# Execute tool callresult = await self.session.call_tool(tool_name, tool_args)final_text.append(f"[Calling tool {tool_name} with args {tool_args}]")assistant_message_content.append(content)messages.append({"role": "assistant","content": assistant_message_content})messages.append({"role": "user","content": [{"type": "tool_result","tool_use_id": content.id,"content": result.content}]})# Get next response from Clauderesponse = self.anthropic.messages.create(model="claude-3-5-sonnet-20241022",max_tokens=1000,messages=messages,tools=available_tools)final_text.append(response.content[0].text)return "\n".join(final_text)async def chat_loop(self):"""Run an interactive chat loop"""print("\nMCP Client Started!")print("Type your queries or 'quit' to exit.")while True:try:query = input("\nQuery: ").strip()if query.lower() == 'quit':breakresponse = await self.process_query(query)print("\n" + response)except Exception as e:print(f"\nError: {str(e)}")async def cleanup(self):"""Clean up resources"""await self.exit_stack.aclose()async def main():client = MCPClient()try:await client.connect_to_server()await client.chat_loop()finally:await client.cleanup()if __name__ == "__main__":asyncio.run(main())
[05/05/25 19:16:40] INFO     Processing request of type           server.py:534ListToolsRequest                                  Connected to server with tools: ['get_forecast', 'read_file', 'open_the_fucking_door']MCP Client Started!
Type your queries or 'quit' to exit.Query: quit

这样我们就可以连接到MCP Server,接下来就是搞定模型的问题!(MCP 和 LLMs 没有直接关系,只要模型具备一定的指令遵循能力,就可以使用 MCP)

上文我们定义的工具实际上会生成如下schema:

[{'description': 'Get weather forecast for a location.\n''\n''    Args:\n''        latitude: Latitude of the location\n''        longitude: Longitude of the location\n''    ','input_schema': {'properties': {'latitude': {'title': 'Latitude','type': 'number'},'longitude': {'title': 'Longitude','type': 'number'}},'required': ['latitude', 'longitude'],'title': 'get_forecastArguments','type': 'object'},'name': 'get_forecast'},{'description': '\n''    读取指定路径的文件内容。\n''\n''    Args:\n''        file_path: 文件的完整路径\n''\n''    Returns:\n''        文件的内容\n''    ','input_schema': {'properties': {'file_path': {'title': 'File Path','type': 'string'}},'required': ['file_path'],'title': 'read_fileArguments','type': 'object'},'name': 'read_file'},{'description': '\n    打开指定ip的智能门\n\n    Args:\n        ip: ip地址\n    ','input_schema': {'properties': {'ip': {'title': 'Ip', 'type': 'string'}},'required': ['ip'],'title': 'open_the_fucking_doorArguments','type': 'object'},'name': 'open_the_fucking_door'}]

查询Qwen官方,我们需要的格式是这样的:

TOOLS = [{"type": "function","function": {"name": "get_current_temperature","description": "Get current temperature at a location.","parameters": {"type": "object","properties": {"location": {"type": "string","description": 'The location to get the temperature for, in the format "City, State, Country".',},"unit": {"type": "string","enum": ["celsius", "fahrenheit"],"description": 'The unit to return the temperature in. Defaults to "celsius".',},},"required": ["location"],},},},{"type": "function","function": {"name": "get_temperature_date","description": "Get temperature at a location and date.","parameters": {"type": "object","properties": {"location": {"type": "string","description": 'The location to get the temperature for, in the format "City, State, Country".',},"date": {"type": "string","description": 'The date to get the temperature for, in the format "Year-Month-Day".',},"unit": {"type": "string","enum": ["celsius", "fahrenheit"],"description": 'The unit to return the temperature in. Defaults to "celsius".',},},"required": ["location", "date"],},},},
]

因此,我们改为Qwen适配的格式:

qwen_tools = [{"type": "function","function": {"name": tool.name,"description": tool.description,"parameters": tool.inputSchema}}for tool in response.tools]

当我们询问经纬度为x的地区天气状况时,模型已经可以给出正确的函数调用了!

model='qwen2.5:7b' created_at='2025-05-05T11:45:09.210411Z' done=True done_reason='stop' total_duration=8104453800 load_duration=3359794600 prompt_eval_count=347 prompt_eval_duration=569000000 eval_count=108 eval_duration=3811000000 message=Message(role='assistant', content='', images=None, tool_calls=[ToolCall(function=Function(name='get_forecast', arguments={'latitude': 1, 'longitude': 1}))])

最后改造为:

import asyncio
from typing import Optional
from contextlib import AsyncExitStack
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
from ollama import AsyncClient
from pprint import pprint
from ollama import ChatResponseclass MCPClient:def __init__(self):# Initialize session and client objectsself.session: Optional[ClientSession] = Noneself.exit_stack = AsyncExitStack()# 暂时不初始化self.llm = AsyncClient()async def connect_to_server(self):server_script_path = "./weather.py"is_python = server_script_path.endswith('.py')is_js = server_script_path.endswith('.js')if not (is_python or is_js):raise ValueError("Server script must be a .py or .js file")command = "python" if is_python else "node"server_params = StdioServerParameters(command=command,args=[server_script_path],env=None)stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))self.stdio, self.write = stdio_transportself.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.write))await self.session.initialize()# List available toolsresponse = await self.session.list_tools()tools = response.toolsprint("\nConnected to server with tools:", [tool.name for tool in tools])async def process_query(self, query: str) -> str:"""Process a query using Claude and available tools"""messages = [{"role": "user","content": query}]response = await self.session.list_tools()qwen_tools = [{"type": "function","function": {"name": tool.name,"description": tool.description,"parameters": tool.inputSchema}}for tool in response.tools]ollama_response: ChatResponse = await self.llm.chat(model="qwen2.5:7b", messages=messages, tools=qwen_tools)# Process response and handle tool callsfinal_text = []# 得到对话内容message = ollama_response.messagemessages.append({"content": message.content,"role": message.role})# 函数调用for t in message.tool_calls or []:fn = t.functionresult = await self.session.call_tool(fn.name, fn.arguments)final_text.append(f"[Calling tool {fn.name} with args {fn.arguments}]")messages.append({"role": "tool","content": result.content[0].text,})pprint(messages)return "\n".join(final_text)async def chat_loop(self):"""Run an interactive chat loop"""print("\nMCP Client Started!")print("Type your queries or 'quit' to exit.")while True:try:query = input("\nQuery: ").strip()if query.lower() == 'quit':breakresponse = await self.process_query(query)print("\n" + response)except Exception as e:print(f"\nError: {str(e)}")async def cleanup(self):"""Clean up resources"""await self.exit_stack.aclose()async def main():client = MCPClient()try:await client.connect_to_server()await client.chat_loop()finally:await client.cleanup()if __name__ == "__main__":asyncio.run(main())

在这里插入图片描述

基于golang的mcp

目前官方没有给出对golang的实现,不过你可以在awesome-mcp-servers项目中找到基于go的SDK,如foxy-contexts、mcp-go、mcp-golang、当前这些库使用的golang版本均比较高,使用的时候需要注意下,目前,MCP 的 Golang 实现主要由 mark3labs/mcp-go 提供。这个库为开发者提供了一套完整的工具,用于创建和管理 MCP Server。它支持工具定义、参数处理、工具调用等功能,并且提供了丰富的配置选项和日志记录功能。

以https://github.com/mark3labs/mcp-go为例,下载:go get github.com/mark3labs/mcp-go

package mainimport ("context""errors""fmt""github.com/mark3labs/mcp-go/mcp""github.com/mark3labs/mcp-go/server"
)func main() {// Create a new MCP servers := server.NewMCPServer("Calculator Demo","1.0.0",server.WithResourceCapabilities(true, true),server.WithLogging(),server.WithRecovery(),)// Add a calculator toolcalculatorTool := mcp.NewTool("calculate",mcp.WithDescription("Perform basic arithmetic operations"),mcp.WithString("operation",mcp.Required(),mcp.Description("The operation to perform (add, subtract, multiply, divide)"),mcp.Enum("add", "subtract", "multiply", "divide"),),mcp.WithNumber("x",mcp.Required(),mcp.Description("First number"),),mcp.WithNumber("y",mcp.Required(),mcp.Description("Second number"),),)// Add the calculator handlers.AddTool(calculatorTool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {op := request.Params.Arguments["operation"].(string)x := request.Params.Arguments["x"].(float64)y := request.Params.Arguments["y"].(float64)var result float64switch op {case "add":result = x + ycase "subtract":result = x - ycase "multiply":result = x * ycase "divide":if y == 0 {return mcp.NewToolResultError("cannot divide by zero"), nil}result = x / y}return mcp.NewToolResultText(fmt.Sprintf("%.2f", result)), nil})// Start the serverif err := server.ServeStdio(s); err != nil {fmt.Printf("Server error: %v\n", err)}
}

在实现 MCP Server 后,需要将其配置到客户端插件中。以下是一个在 Windows 系统中配置cline插件的 MCP Server 的示例 JSON 文件:

{"mcpServers": {"culculate_server": {"command": "C:\\Users\\Administrator\\go\\bin\\calculator.exe","args": [],"env": {"GOPATH": "C:\\Users\\Administrator\\go","GOMODCACHE": "C:\\Users\\Administrator\\go\\pkg\\mod"}}}
}

配置完成后,你可以通过对话框向大语言模型提问,例如计算 89989 + 4378247 的结果。大语言模型会自动调用 MCP Server 来完成计算任务,并返回结果。

除此之外,也可以通过一些支持 MCP Server 的客户端(AI代码编辑器)进行调试,比如Cursor,cline。

比如上面的python代码可以配置到Cursor:

uv --directory /Users/ryanjhzheng/Documents/my_mcp/weather run weather.py

在这里插入图片描述
在这里插入图片描述

cline则需要添加配置文件:

{"mcpServers": {"weather": {"command": "uv","args": ["--directory","/ABSOLUTE/PATH/TO/PARENT/FOLDER/weather","run","weather.py"]}}
}

在这里插入图片描述

相关文章:

Model Context Protocol (MCP)笔记

目录 摘要MCP理论MCP的作用MCP 传输机制 Stdio 与 SSESTDIOSSE 传输部署模式 模型是如何确定工具的选用的&#xff1f;Fc x MCP x LangChain MCP快速开始编写客户端基于golang的mcp 摘要 Model Context Protocol&#xff08;MCP&#xff0c;模型上下文协议&#xff09;是由 An…...

【codeforces 2070c】二分答案详解

【codeforces 2070c】二分答案详解 二分答案转化成判定 对于任何问题&#xff0c;如果我们有了一个判定算法&#xff0c;那把解空间枚举并判定一遍&#xff0c;当然就可以得到解了。而当解空间具有单调性时&#xff0c;我们就可以使用二分法代替枚举。 考虑如下问题&#xf…...

启发式算法-禁忌搜索算法

禁忌搜索是一种可以用于解决组合优化问题的启发式算法&#xff0c;通过引入记忆机制跳出局部最优&#xff0c;避免重复搜索。该算法从一个初始解开始&#xff0c;通过邻域搜索策略来寻找当前解的邻域解&#xff0c;并在邻域解中选择一个最优解作为下一次迭代的当前解&#xff0…...

simulink 外循环与内循环执行流程

目录 前言 一、外循环 模型 执行流程 二、内循环 模型 执行流程 仓库 前言 某些需求需要使用到simulink外循环和内循环&#xff0c;本篇通过对其执行顺序进行记录&#xff0c;以便后续查阅。 一、外循环 模型 下面是我搭建的简单模型 执行流程 0-step:执行en step…...

Gradio全解20——Streaming:流式传输的多媒体应用(6)——构建视频流目标检测系统

Gradio全解20——Streaming&#xff1a;流式传输的多媒体应用&#xff08;6&#xff09;——构建视频流目标检测系统 本篇摘要20. Streaming&#xff1a;流式传输的多媒体应用20.6 RT-DETR模型构建视频流目标检测系统20.6.1 RT-DETR模型1. 模型介绍2. 使用示例 20.6.2 系统配置…...

比较两种判断相同二叉树的方法:递归与遍历序列对比

在二叉树操作中&#xff0c;判断两棵树是否相同是一个常见的问题。本文将对比两种不同的解决方案&#xff1a;递归法和遍历序列对比法&#xff0c;分析它们的优缺点&#xff0c;并探讨为何递归法是更优的选择。 问题描述 给定两棵二叉树的根节点 p 和 q&#xff0c;判断它们是…...

Java IO流核心处理方式详解

一、IO流概述 Java IO&#xff08;Input/Output&#xff09;流是处理输入输出操作的核心机制&#xff0c;通过流&#xff08;Stream&#xff09;的形式实现设备间的数据传输。所有操作都基于以下两个核心抽象&#xff1a; InputStream/OutputStream&#xff1a;字节流基类 Re…...

C++竞赛指南

关注支持&#xff0c;好运连连 目录 关注支持&#xff0c;好运连连 一、竞赛C核心优势 二、必备语法与STL组件 1. 输入输出优化 2. 常用STL容器 3. 算法函数 三、竞赛常用算法 1. 时间复杂度分析 2. 高频算法模板 二分查找 快速幂&#xff08;模运算&#xff09; …...

Python字符串全面指南:从基础到高级操作

字符串是Python编程中最基础也是最重要的数据类型之一。本文将全面介绍Python字符串的相关知识&#xff0c;从基础概念到高级操作&#xff0c;帮助您彻底掌握字符串的使用。 1. 字符串基础 1.1 字符串的概念 字符串是由一系列字符组成的不可变序列容器&#xff0c;存储的是字…...

【推荐】智慧矿山矿业信息化智能化资料汇总-共25份

智慧矿山矿业信息化智能化资料汇总 25 份&#xff1a; 有色金属矿山智能化采选生产线智能矿山建设与示范智能矿山建设实践与思考智慧矿山建设解决方案与实现途径以信息化、智能化为手段打造生态型、效益型国际一流示范矿山新型智能 X 荧光多通道高精度在线品位分析仪的研制与应…...

Oracle OCP认证考试考点详解083系列08

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 36. 第36题&#xff1a; 题目 解析及答案&#xff1a; 关于数据库闪回&#xff08;FLASHBACK DATABASE&#xff09;功能&#xff0c;以下…...

备战蓝桥杯国赛第一天-atcoder-beginner-contest404

B. 因为只有四种情况&#xff0c;旋转90/180/270度后替换&#xff0c;直接替换&#xff0c;暴力即可 C. 循环图的定义是每个点出度为2&#xff0c;而且只有一个环的&#xff0c;所以先判断出度&#xff0c;再判断是否成环 #include <bits/stdc.h> using namespace st…...

Python异步编程进阶:深入探索asyncio高级特性

异步上下文管理器 (async with) 异步上下文管理器允许你在异步环境中管理资源,比如数据库连接或文件操作。 基本实现 class AsyncDatabaseConnection:async def __aenter__(self):print("建立数据库连接")await asyncio.sleep(0.5) # 模拟连接建立return selfas…...

【Java ee初阶】多线程(7)

一、线程池 线程池的一些参数&#xff1a; corePoolSize&#xff1a;核心线程数量 maximumPoolSize:核心线程数量临时线程数量 上述是“java 的线程池策略”&#xff08;其他语言&#xff0c;其他库的线程池可能不同&#xff09; keepAliveTime :临时线程的存活时间.临时线程…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】6.2 预测分析基础(线性回归/逻辑回归实现)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 PostgreSQL数据分析实战&#xff1a;预测分析基础&#xff08;线性回归/逻辑回归实现&#xff09;6.2 预测分析基础——线性回归与逻辑回归实现6.2.1 预测分析核心理论框架1…...

【NLP】29. 高效训练与替代模型:让语言模型更轻、更快、更强

高效训练与替代模型&#xff1a;让语言模型更轻、更快、更强 本文介绍语言模型如何通过结构优化与新模型探索&#xff0c;提升训练和推理的效率&#xff0c;适应资源受限环境&#xff0c;同时概述了一些 Transformer 替代模型的最新进展。 一、如何让语言模型更高效&#xff1f…...

【LaTeX+VSCode本地Win11编译教程】

LaTeXVSCode本地编译教程参考视频&#xff1a; LaTeXVSCode本地编译教程 下面提供一种Win11的Latex环境配置和设置方案&#xff0c;首先vscode安装参考博客&#xff1a;【VscodeGit教程】&#xff0c;然后准备安装Latex相关组件 在 https://miktex.org/download 下载 miktex 并…...

组合两个表 --- MySQL [Leetcode 题目详解]

目录 题目链接 往期相关基础内容讲解博客 题目详解 1. 题目内容 2. 解题思路 3. 代码编写 题目链接 // 175. 组合两个表 往期相关基础内容讲解博客 // 聚合查询和联合查询博客 题目详解 1. 题目内容 // 编写解决方案&#xff0c;报告 Person 表中每个人的姓、名、城市…...

STM32 PulseSensor心跳传感器驱动代码

STM32CubeMX中准备工作&#xff1a; 1、设置AD 通道 2、设置一个定时器中断&#xff0c;间隔时间2ms&#xff0c;我这里采用的是定时器7 3、代码优化01 PulseSensor.c文件 #include "main.h" #include "PulseSensor/PulseSensor.h"/******************…...

macOS 上是否有类似 WinRAR 的压缩软件?

对于习惯使用 Windows 的用户来说&#xff0c;WinRAR 是经典的压缩/解压工具&#xff0c;但 macOS 系统原生并不支持 RAR 格式的解压&#xff0c;更无法直接使用 WinRAR。不过&#xff0c;macOS 平台上有许多功能相似甚至更强大的替代工具&#xff0c;以下是一些推荐&#xff1…...

Java求职面试:Spring Boot与微服务的幽默探讨

Java求职者面试&#xff1a;技术与幽默的碰撞 场景概述 在某互联网大厂的面试现场&#xff0c;面试官严肃认真&#xff0c;程序员则是一个搞笑的水货角色。面试者名叫张伟&#xff0c;年龄28岁&#xff0c;硕士学历&#xff0c;拥有5年的Java开发经验。以下是面试的详细过程。…...

《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》封面颜色空间一图的选图历程

禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》 学图像处理的都知道&#xff0c;彩色图像的颜色空间很多&#xff0c;而且又是三维&#xff0c;不同的角度有不同的视觉效果&#xff0c;MATLAB的图又有有box和没有box。…...

Docker 使用下 (二)

Docker 使用下 &#xff08;二&#xff09; 文章目录 Docker 使用下 &#xff08;二&#xff09;前言一、初识Docker1.1 、Docker概述1.2 、Docker的历史1.3 、Docker解决了什么问题1.4 、Docker 的优点1.5 、Docker的架构图 二、镜像三、容器四、数据卷4.1、数据卷的概念4.2 、…...

【群晖NAS】Docker + WebStation + DDNS 部署无端口号HTTPs WordPress

前言 群晖提供官方的DDNS服务&#xff0c;可以直接配置一个类似于xxxx.synology.me的DDNS解析IPv4/IPv6到自己的NAS&#xff1b;群晖还有Web Station应用可以配置Docker的端口号映射&#xff0c;但是他自己占用了80端口&#xff0c;如果给自己的应用手动指定其他端口号&#x…...

手机SIM卡打电话时识别对方按下的DTMF按键(二)

手机SIM卡打电话时识别对方按下的DTMF按键&#xff08;二&#xff09; --本地AI电话机器人 前言 书接上篇&#xff0c;在上一篇章《手机打电话时如何识别对方按下的DTMF按键的字符》中&#xff0c;我们从理论的角度来论述了DTMF的频率组成。并尝试使用400Kb左右的【TarsosDS…...

N-Gram 模型

N-Gram 模型 什么是N-Gram&#xff1f;为什么叫 N-Gram&#xff1f;N-Gram怎么知道下一个词可能是什么&#xff1f;N-Gram 能做什么&#xff1f;N-Gram的问题 本文回答了四个问题&#xff1a; 一、N-Gram是什么&#xff1f;二、N-Gram为什么叫N-Gram&#xff1f;三、N-Gram具体…...

【漫话机器学习系列】240.真正类率(True Positive Rate,TPR)

理解真正类率&#xff08;True Positive Rate&#xff0c;TPR&#xff09;&#xff1a;公式、意义与应用 在机器学习与深度学习模型评估中&#xff0c;"真正类率"&#xff08;True Positive Rate&#xff0c;简称TPR&#xff09;是一个非常重要的指标。TPR反映了分类…...

ThreadLocal源码深度剖析:内存管理与哈希机制

ThreadLocal是Java并发编程中的重要工具&#xff0c;它为每个线程提供独立的变量存储空间&#xff0c;实现了线程之间的数据隔离。本文将从源码实现角度&#xff0c;深入分析ThreadLocal的内部机制&#xff0c;特别是强弱引用关系、内存泄漏问题、ThreadLocalMap的扩容机制以及…...

Softmax回归与单层感知机对比

(1) 输出形式 Softmax回归 输出是一个概率分布&#xff0c;通过Softmax函数将线性得分转换为概率&#xff1a; 其中 KK 是类别数&#xff0c;模型同时计算所有类别的概率。 单层感知机 输出是二分类的硬决策&#xff08;如0/1或1&#xff09;&#xff1a; 无概率解释&#x…...

数字社会学家唐兴通谈数字行动主义网络行动主义与标签行动主义,理解它才算抓住AI社会学与网络社会学关键所在

让我们继续探讨一个在数字时代至关重要的概念——数字行动主义&#xff08;Digital Activism&#xff09;、网络行动主义&#xff08;Cyberactivism&#xff09;以及标签行动主义&#xff08;Hashtag Activism&#xff09;。我将尽力从一个数字社会学家的角度&#xff0c;抽丝剥…...

PandasAI:对话式数据分析新时代

PandasAI&#xff1a;对话式数据分析新时代 引言项目概述分析基本信息 核心功能详解1. 自然语言查询处理2. 数据可视化生成3. 多数据源集成分析4. 安全沙箱执行5. 云平台协作功能 安装和使用教程1.环境要求2.安装步骤3.基本使用方法4.切换其他LLM 应用场景和实际价值1.适用业务…...

全球化电商平台AWS云架构设计

业务需求&#xff1a; 支撑全球三大区域&#xff08;北美/欧洲/亚洲&#xff09;用户访问&#xff0c;延迟<100ms处理每秒50,000订单的峰值流量混合云架构整合本地ERP系统全年可用性99.99%满足GDPR和PCI DSS合规要求 以下是一个体现AWS专家能力的全球化电商平台架构设计方…...

Linux 怎么使用局域网内电脑的网络访问外部

一次性 export http_proxy"http://192.168.0.188:7890" export https_proxy"http://192.168.0.188:7890"一直生效 写入 ~/.bashrc&#xff08;或 ~/.bash_profile&#xff09; nano ~/.bashrc加入这一行&#xff1a; export http_proxy"http://19…...

Python-numpy中ndarray对象创建,数据类型,基本属性

numpy库 numpy中的数据结构ndarrayndarray中的dtypendarray中的dtype的指定方式创建ndarray及指定dtype从列表创建ndarray使用 np.empty(), np.zeros(), np.ones() 和 np.full() 创建特定值的数组使用 np.arange() 创建等差数列数组使用 np.linspace() 创建等差数组使用np.logs…...

Python从入门到高手8.2节-元组的常用操作符

目录 ​8.2.1 元组的常用操作符 8.2.2 []操作符: 索引访问元组 8.2.3 [:]操作符&#xff1a;元组的切片 8.2.4 操作符&#xff1a;元组的加法 8.2.5 *操作符&#xff1a;元组的乘法 8.2.6 元组的关系运算 8.2.7 in操作符&#xff1a;查找元素 8.2.8 五一她玩了个狗吃…...

Python内置函数

Python作为一门简洁强大的编程语言&#xff0c;提供了丰富的内置函数&#xff08;Built-in Functions&#xff09;&#xff0c;这些函数无需导入任何模块即可直接使用。本文将介绍Python中最常用、最重要的内置函数&#xff0c;帮助初学者快速掌握这些强大的工具。 官方地址&a…...

一款基于 .NET 开源的多功能的 B 站视频下载工具

前言 哔哩哔哩&#xff08;B站&#xff09;是一个知名的视频学习平台&#xff0c;作为程序员而言这是一个非常值得推荐的网站。今天大姚给大家推荐一款基于 .NET 开源的多功能的 B 站视频下载工具&#xff1a;downkyi。 项目介绍 downkyi&#xff08;哔哩下载姬&#xff09;…...

【PostgreSQL数据分析实战:从数据清洗到可视化全流程】5.2 数据分组与透视(CUBE/ROLLUP/GROUPING SETS)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 5.2 数据分组与透视&#xff1a;CUBE/ROLLUP/GROUPING SETS深度解析5.2.1 数据准备与分析目标数据集与表结构分析目标 5.2.2 ROLLUP&#xff1a;层级化分组汇总功能与语法示…...

20、数据可视化:魔镜报表——React 19 图表集成

一、魔镜的预言本质 "数据可视化是霍格沃茨的预言水晶球&#xff0c;将混沌的数据星尘转化为可解读的命运轨迹&#xff01;" 魔法部占卜司官员挥舞魔杖&#xff0c;Echarts与Three.js的图表矩阵在空中交织成动态星图。 ——基于《国际魔法联合会》第9号可视化协议&a…...

笔记本电脑升级计划(2017———2025)

ThinkPad T470 (2017) vs ThinkBook 16 (2025) 完整性能对比报告 一、核心硬件性能对比 1. CPU性能对比&#xff08;i5-7200U vs Ultra9-285H&#xff09; 参数i5-7200U (2017)Ultra9-285H (2025)提升百分比核心架构2核4线程 (Skylake)16核16线程 (6P8E2LPE)700%核心数制程工…...

Flutter——数据库Drift开发详细教程(四)

目录 参考正文表达式1.比较2.布尔代数3.算术BigIn 4.空值检查6.日期和时间7.IN和NOT IN8.聚合函数&#xff08;例如 count 和 sum&#xff09;8.1比较8.2算术8.4计数8.5group_concat8.9窗口函数 9.数学函数和正则表达式10.子查询10.1 标量子查询10.2 isInQuery10.3 存在10.4完整…...

android-ndk开发(6): 查看反汇编

android-ndk开发(6): 查看反汇编 2025/05/05 1. 概要 android-ndk 是基于 clang 的工具链&#xff0c; clang 则保持了和 gcc 的高度兼容。 在 Linux 开发机上&#xff0c; GCC 套件里的 objdump 提供了反汇编的功能。 实际上 android-ndk 也提供了一份 objdump&#xff0c;…...

浅析AI大模型为何需要向量数据库?【入门基础】

文章目录 引言&#xff1a;大模型时代的存储挑战一、向量数据库&#xff1a;大模型的"海马体"1.1 什么是向量数据库&#xff1f;1.2 为什么大模型离不开向量数据库&#xff1f;(1) 嵌入(Embedding)的本质(2) 突破上下文窗口限制 二、相似性度量&#xff1a;欧氏距离与…...

Java面试:微服务与大数据场景下的技术挑战

面试对话场景 第一轮&#xff1a;基础知识考察 面试官&#xff1a;谢先生&#xff0c;您能简单介绍一下Java SE 8的新特性吗&#xff1f; 谢飞机&#xff1a;当然&#xff0c;Java SE 8引入了Lambda表达式、Stream API和新的日期时间API&#xff0c;大大简化了代码编写。 面…...

[前端]异步请求的竞态问题

竞态条件简介 遇到的问题 切换标签请求数据&#xff0c;但又快速切换标签请求数据&#xff0c;展示的是前一个标签的数据&#xff0c; 需要在切换标签时添加取消请求的机制&#xff0c;使用AbortController来取消正在进行的请求。当用户快速切换标签时&#xff0c;取消之前的请…...

【PDF拆分+提取内容改名】批量拆分PDF提取拆分后的每个PDF物流面单数据改名或导出表格,基于WPF的PDF物流面单批量处理方案

应用场景 物流行业每天需要处理大量包含物流面单的PDF文件,这些文件通常包含运单号、收发货人信息、货物详情等重要数据。传统手动处理方式效率低下且容易出错。本方案通过WPF实现一个自动化工具,能够: 批量拆分多页PDF为单页文件提取每页面单中的关键信息(如运单号、收件人…...

adb无线调试步骤

环境&#xff1a; macOS&#xff1b; 换成 linux 或 windows 也支持的小米15 Pro&#xff1b; 换成其他 android 手机也支持的 电脑和手机接入相同Wifi在电脑上&#xff0c;确保安装了 adb 对于 Android 开发者&#xff0c; 一般是是通过 Android Studio 安装对于 ndk 开发者…...

RocketMQ与Kafka的区别

文章目录 相同之处不同之处存储形式性能对比传输系统调用存储可靠性单机支持的队列数延时消息消息重复消息过滤消息失败重试死信队列 DLQ回溯消息分布式事务服务发现开发语言友好性开源社区活跃度商业支持成熟度 总结Kafka 和 RocketMQ 怎么选&#xff1f; 本文参考&#xff1a…...

剥开 MP4 的 千层 “数字洋葱”:从外到内拆解通用媒体容器的核心

在当今数字化时代&#xff0c;MP4 格式随处可见&#xff0c;无论是在线视频、手机拍摄的短片&#xff0c;还是从各种渠道获取的音频视频文件&#xff0c;MP4 都占据着主流地位。它就像一个万能的 “数字媒体集装箱”&#xff0c;高效地整合和传输着各种视听内容。接下来&#x…...

设计模式(结构型)-组合模式

定义 组合模式的定义为&#xff1a;将对象组合成树形结构以表示 “部分 - 整体” 的层次结构&#xff0c;并且使得用户对单个对象和组合对象的使用具有一致性。其最关键的实现要点在于&#xff0c;简单对象和复合对象必须实现相同的接口&#xff0c;这一特性正是组合模式能够对…...