开源模型应用落地-模型上下文协议(MCP)-Resources-资源的使用逻辑

发布于:2025-05-16 ⋅ 阅读:(21) ⋅ 点赞:(0)

一、前言

    在大型语言模型与外部世界交互的探索中,如何高效、灵活地接入多样化数据始终是核心命题。MCP(Model Context Protocol)协议中的Resources 机制,正是为这一问题提供了优雅的解决方案。通过URI(统一资源标识符)的抽象设计,MCP服务器能够将文件系统、数据库、API响应甚至实时系统日志等数据转化为LLM可理解的上下文。这种“无副作用”的读取方式不仅简化了数据调用逻辑,更通过应用层控制权的让渡,赋予客户端对资源访问的自主性与灵活性。

    本文将深入解析MCP资源的使用逻辑,从协议规范到实战场景,探讨如何通过这一机制释放大模型的无限潜能。


二、术语介绍

2.1.MCP

     MCP协议(Model Context Protocol,模型上下文协议)是由Anthropic公司于2024年11月推出的开放标准协议,旨在为大型语言模型(LLM)与外部数据源、工具之间建立统一的通信接口。其核心作用在于解决AI模型因数据孤岛限制难以发挥潜力的问题,通过标准化交互方式提升AI应用的扩展性和实用性。

一、MCP协议的核心定义
    1.1. 协议定位
    MCP被类比为AI领域的“USB-C接口”,通过解耦LLM与外部系统的集成,实现一次开发即可动态连接多种服务(如数据库、API、本地文件系统等)。例如,开发者无需为每个新工具单独定制接口,只需遵循MCP标准即可实现AI与工具的即插即用。

    1.2. ​技术架构
    采用客户端-服务器模型,包含三个核心组件:

  • ​MCP主机:运行AI模型的环境(如Claude桌面应用);
  • ​MCP客户端:负责与服务器通信并转发请求;
  • ​MCP服务器:提供具体功能服务的轻量级程序(如文件管理或API调用)。

二、MCP协议的核心作用
​    2.1. 统一交互标准
    标准化JSON-RPC 2.0协议支持本地(标准输入输出)与远程(HTTP/SSE)通信,兼容Python、Java等多种编程语言,降低开发复杂度。例如,本地通信保障医疗数据安全传输,远程通信支持云端服务扩展。

​    2.2. 动态扩展能力
    通过资源(Resource)、工具(Tool)、提示(Prompt)三大核心概念,AI模型可自动发现新工具并实时交互。例如连接GitHub时,AI可自动提交代码或修复BUG,无需人工预定义接口。

    ​2.3. 安全与权限控制
    内置权限验证机制,用户需手动批准敏感操作(如文件删除或邮件发送),确保数据隐私。在企业场景中,该特性尤为重要,如金融分析时需严格管控市场数据访问权限。

2.2.资源

  代表服务器向客户端暴露的、可供读取的数据或内容原语。其主要作用是为大型语言模型提供交互所需的上下文信息。

具体特点包括:

1.数据类型多样性

  • Resources 可以涵盖多种结构化或非结构化数据,例如文件内容、数据库记录、API响应、实时系统数据、日志文件、屏幕截图等。

2.只读性

  • Resources 被设计为只读 实体,客户端只能检索数据而无法修改,确保数据的安全性和一致性。

3.上下文与应用控制

  • 它们作为LLM交互的上下文来源,同时由应用程序(服务器端)控制,即客户端仅能按预设规则访问特定资源。

4.协议功能支撑

  • Resources 是MCP协议三大核心能力之一(另两类为工具和提示),用于实现模型对本地或外部数据的定制化访问

三、前置条件

3.1.基础环境及前置条件

  1. 操作系统:无限

3.2.MCP环境安装

   参见:开源模型应用落地-模型上下文协议(MCP)-从数据孤岛到万物互联(一)_pycharm mcp-CSDN博客文章浏览阅读2.2w次,点赞57次,收藏50次。模型上下文协议(MCP)通过标准化的接口和交互语法,重新定义AI与物理世界的连接,提升人机协作的深度与广度。_pycharm mcp https://charles.blog.csdn.net/article/details/147068018?spm=1011.2415.3001.5331

3.3.QWEN3推理环境准备

    参见:

开源模型应用落地-qwen模型小试-Qwen3-8B-推理加速-vLLM(一)-CSDN博客文章浏览阅读8.9k次,点赞22次,收藏18次。解析Qwen3-8B与vLLM的技术协同逻辑,探讨其在复杂任务中的性能表现及落地价值。 https://charles.blog.csdn.net/article/details/147718340?spm=1011.2415.3001.5331开源模型应用落地-qwen模型小试-Qwen3-8B-推理加速-vLLM-Docker(二)_docker 方式启动 vllm,运行qwen3-8b-fp8 参数详解-CSDN博客文章浏览阅读1.3w次,点赞25次,收藏19次。通过容器化技术整合高性能推理框架vLLM与Qwen3-8B模型,可实现资源高效利用、灵活部署及推理性能优化,为大模型本地化应用提供轻量化解决方案_docker 方式启动 vllm,运行qwen3-8b-fp8 参数详解 https://charles.blog.csdn.net/article/details/147719905?spm=1011.2415.3001.5331


四、技术实现

4.1.资源准备

  文件名:广州的名称衍变.txt

楚庭是传说中广州最早的名称。关于“楚庭”之得名,有不同的说法:一说周夷王八年(公元前878年),南海臣服于楚国,因而建造楚庭。一说公元前600多年的春秋时期,楚成王熊恽平南方夷越之乱,在南海建楚庭。有研究者解释称,楚庭(又作楚亭)最早或是宫室,或是市集的意思,后来才慢慢被用作广州的代称。清代,人们将楚庭看作广州最早的名字,并建牌坊纪念。
“南武”也是记载中广州早期的名称。“南武”之得名也有多个说法,一说认为此名本为越国旧有地名,楚国灭越后,由公师隅在今天的广州所在地建城,并沿用了故国的名称;一说认为赵佗号为南越武帝,故将南海郡改称南武郡,以郡名为城市命名。不过,关于楚庭、南武的说法。
广州众多别名中,“羊城”“穗城”最广为人知。这两个别名源于五仙乘羊赠穗的美丽神话。相传周朝时,五个仙人骑五羊送来稻穗。五羊衔谷的传说最早见于晋人所著的《广州记》,书中称“五羊衔谷萃于楚庭”。在后来的古籍中,故事情节发展为五位仙人手里拿着一茎六出的谷穗,骑着五羊而来,并祝福此方土地永无饥荒。唐代以来,“五羊”“羊城”逐渐成为广州城的代称,诗人常用“羊城”代指广州,如高适写有“海对羊城阔,山连象郡高”之句,殷尧藩有“遐荒迢递五羊城,归兴浓消客里情”之句。
“广州”作为一个行政区域概念,距今已经有1796年历史,“广州”二字在历史上的地域含义因时而异。西汉年间,长江以南被划分为三个州:扬州、荆州、交州。交州属下设立九郡,广州就属于交州南海郡。汉武帝平定南越国后,颁布圣旨称:初开粤地,宜广布恩信,意思是希望地方官员以怀柔政策管理此地。州府所在地因此定名为“广信”(一说今广西梧州,一说今广东封开),“广州”之“广”因此得名。 “广州”这一地名出现在三国年间。226年,孙权考虑到岭南地区过于辽阔,不易管辖,又因交州刺史士燮势力太大,遂决定把交州一拆为二,史称“交广分治”,“广州”作为一个地名从此出现,合浦以西为交州,合浦以东为广州。广州辖区范围相当于今广东、广西之大部。

4.2.MCP Server实现

# -*-  coding:utf-8 -*-
from mcp.server.fastmcp import FastMCP
import aiofiles

DEAULT_TRANSPORT = "sse"

# Create an MCP server
mcp = FastMCP("Demo", port=9999)

@mcp.resource(
    uri="file://knowledge.txt",
    name="knowledge",
    description="获取广州名称的衍变历史信息",
    mime_type="text/plain"
)
async def knowledge_base():
    """
    获取知了课堂的相关介绍信息
    """
    async with aiofiles.open(r"E:\data\广州的名称衍变.txt", mode="r", encoding='utf-8') as fp:
        content = await fp.read()
        print(f'获取的内容:{content}')
    return content

if __name__ == "__main__":
    # Initialize and run the server
    print(f"start mcp server,transport: {DEAULT_TRANSPORT}...")  # 记录服务启动日志
    mcp.run(transport=DEAULT_TRANSPORT)

需要安装aiofiles依赖:uv pip install aiofiles

调用:

 直接在pycharm中运行

4.3.MCP Client实现

# -*- coding:utf-8 -*-
import asyncio

from openai import OpenAI
from mcp.client.sse import sse_client
from mcp import ClientSession
from contextlib import AsyncExitStack

class MCPClient:
    def __init__(self, api_key: str, llm_server_url: str, mcp_server_url):
        self.client = OpenAI(api_key=api_key, base_url=llm_server_url)
        models = self.client.models.list()
        self.model = models.data[0].id
        self.mcp_server_url = mcp_server_url
        self.exit_stack = AsyncExitStack()
        self.resources = {}

    async def run(self, query):
        read_stream, write_stream = await self.exit_stack.enter_async_context(sse_client(self.mcp_server_url))
        session: ClientSession = await self.exit_stack.enter_async_context(ClientSession(read_stream, write_stream))

        # 初始化
        await session.initialize()

        # 获取可用的资源
        response = await session.list_resources()
        resources = response.resources
        tools = []

        for resource in resources:
            uri = resource.uri
            name = resource.name
            description = resource.description
            mime_type = resource.mimeType
            tools.append({
                "type": "function",
                "function": {
                    "name": name,
                    "description": description,
                    "input_schema": None
                }
            })
            self.resources[name] = {
                "uri": uri,
                "name": name,
                "description": description,
                "mime_type": mime_type,
            }

        # 定义消息
        messages = [{
            "role": "user",
            "content": query
        }]
        output = self.client.chat.completions.create(
            messages=messages,
            model=self.model,
            tools=tools,
            stream=False
        )
        print(f"首次调用响应: {output}")

        choice = output.choices[0]
        if choice.finish_reason == 'tool_calls':
            messages.append(output.choices[0].message.model_dump())
            print(f"更新消息历史: {messages}")

            tool_call = choice.message.tool_calls[0]
            tool_id = tool_call.id
            function = tool_call.function
            function_name = function.name
            function_uri = self.resources[function_name]["uri"]
            print(f'function_name: {function_name},function_uri: {function_uri}')
            response = await session.read_resource(function_uri)
            result = response.contents[0].text
            print(f"获取到内容: {result}")

            messages.append({
                "role": "tool",
                "content": result,
                "tool_call_id": tool_id,
                "name": function_name
            })

            # 第二次调用:流式响应生成最终回答
            output = self.client.chat.completions.create(
                messages=messages,
                model=self.model,
                stream=True
            )

            print("\nAI助手:")
            for chunk in output:
                if chunk.choices and chunk.choices[0].delta.content:
                    print(chunk.choices[0].delta.content, end='', flush=True)

    async def aclose(self):
        await self.exit_stack.aclose()

async def main():
    # 环境配置
    openai_api_key = "EMPTY"
    openai_api_base = "http://localhost:9000/v1"
    mcp_server_url = "http://localhost:9999/sse"

    client = MCPClient(api_key=openai_api_key,llm_server_url=openai_api_base,mcp_server_url=mcp_server_url)
    try:
        await client.run("帮我查一下广州这个名称的由来")
    finally:
        await client.aclose()


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

调用:

 server端输出

  client端输出

首次调用响应: ChatCompletion(id='chatcmpl-677658b2f5284c9c8ad241791eae7359', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content='<think>\n好的,用户让我帮忙查一下“广州”这个名称的由来。首先,我需要确定用户的需求是什么。他们可能对广州的历史感兴趣,或者是在学习中国城市名称的演变。接下来,我要看看有哪些工具可用。根据提供的工具,有一个名为“knowledge”的函数,可以获取广州名称的衍变历史信息,而且不需要参数。这说明直接调用这个函数就能得到所需的信息。\n\n然后,我需要确认这个函数是否可靠。因为描述中提到它能获取衍变历史,所以应该能提供关于“广州”名称起源的详细资料。用户的问题比较直接,所以调用这个函数应该就能满足需求。不需要额外的参数,所以工具调用会很直接。最后,我应该生成一个工具调用的JSON,指定函数名和空参数,然后等待结果返回给用户。这样处理应该能有效解决问题。\n</think>\n\n', refusal=None, role='assistant', annotations=None, audio=None, function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='chatcmpl-tool-705aad63263c4485afbef6ec99ff131b', function=Function(arguments='{}', name='knowledge'), type='function')], reasoning_content=None), stop_reason=None)], created=1747295376, model='/Qwen3-8B', object='chat.completion', service_tier=None, system_fingerprint=None, usage=CompletionUsage(completion_tokens=201, prompt_tokens=138, total_tokens=339, completion_tokens_details=None, prompt_tokens_details=None), prompt_logprobs=None)
更新消息历史: [{'role': 'user', 'content': '帮我查一下广州这个名称的由来'}, {'content': '<think>\n好的,用户让我帮忙查一下“广州”这个名称的由来。首先,我需要确定用户的需求是什么。他们可能对广州的历史感兴趣,或者是在学习中国城市名称的演变。接下来,我要看看有哪些工具可用。根据提供的工具,有一个名为“knowledge”的函数,可以获取广州名称的衍变历史信息,而且不需要参数。这说明直接调用这个函数就能得到所需的信息。\n\n然后,我需要确认这个函数是否可靠。因为描述中提到它能获取衍变历史,所以应该能提供关于“广州”名称起源的详细资料。用户的问题比较直接,所以调用这个函数应该就能满足需求。不需要额外的参数,所以工具调用会很直接。最后,我应该生成一个工具调用的JSON,指定函数名和空参数,然后等待结果返回给用户。这样处理应该能有效解决问题。\n</think>\n\n', 'refusal': None, 'role': 'assistant', 'annotations': None, 'audio': None, 'function_call': None, 'tool_calls': [{'id': 'chatcmpl-tool-705aad63263c4485afbef6ec99ff131b', 'function': {'arguments': '{}', 'name': 'knowledge'}, 'type': 'function'}], 'reasoning_content': None}]
function_name: knowledge,function_uri: file://knowledge.txt/
获取到内容: 楚庭是传说中广州最早的名称。关于“楚庭”之得名,有不同的说法:一说周夷王八年(公元前878年),南海臣服于楚国,因而建造楚庭。一说公元前600多年的春秋时期,楚成王熊恽平南方夷越之乱,在南海建楚庭。有研究者解释称,楚庭(又作楚亭)最早或是宫室,或是市集的意思,后来才慢慢被用作广州的代称。清代,人们将楚庭看作广州最早的名字,并建牌坊纪念。
“南武”也是记载中广州早期的名称。“南武”之得名也有多个说法,一说认为此名本为越国旧有地名,楚国灭越后,由公师隅在今天的广州所在地建城,并沿用了故国的名称;一说认为赵佗号为南越武帝,故将南海郡改称南武郡,以郡名为城市命名。不过,关于楚庭、南武的说法。
广州众多别名中,“羊城”“穗城”最广为人知。这两个别名源于五仙乘羊赠穗的美丽神话。相传周朝时,五个仙人骑五羊送来稻穗。五羊衔谷的传说最早见于晋人所著的《广州记》,书中称“五羊衔谷萃于楚庭”。在后来的古籍中,故事情节发展为五位仙人手里拿着一茎六出的谷穗,骑着五羊而来,并祝福此方土地永无饥荒。唐代以来,“五羊”“羊城”逐渐成为广州城的代称,诗人常用“羊城”代指广州,如高适写有“海对羊城阔,山连象郡高”之句,殷尧藩有“遐荒迢递五羊城,归兴浓消客里情”之句。
“广州”作为一个行政区域概念,距今已经有1796年历史,“广州”二字在历史上的地域含义因时而异。西汉年间,长江以南被划分为三个州:扬州、荆州、交州。交州属下设立九郡,广州就属于交州南海郡。汉武帝平定南越国后,颁布圣旨称:初开粤地,宜广布恩信,意思是希望地方官员以怀柔政策管理此地。州府所在地因此定名为“广信”(一说今广西梧州,一说今广东封开),“广州”之“广”因此得名。 “广州”这一地名出现在三国年间。226年,孙权考虑到岭南地区过于辽阔,不易管辖,又因交州刺史士燮势力太大,遂决定把交州一拆为二,史称“交广分治”,“广州”作为一个地名从此出现,合浦以西为交州,合浦以东为广州。广州辖区范围相当于今广东、广西之大部。

AI助手:
<think>
好的,用户让我查广州名称的由来,我之前已经调用了knowledge工具,现在需要整理这些信息。首先,我需要确认用户的需求是否被完全满足。他们可能对广州的历史名称演变感兴趣,包括楚庭、南武、羊城、穗城以及“广州”这一名称的起源。

首先,用户可能想知道广州名称的多个来源,包括古代别名和现代名称。我需要将这些信息分门别类,确保每个名称的由来都清晰明了。比如,楚庭和南武的起源有不同的说法,需要分别说明。然后,羊城和穗城的神话故事也需要详细解释,尤其是五仙乘羊的传说,这是广州文化的重要部分。

接下来是“广州”作为行政区域名称的起源,这部分涉及西汉和三国时期的历史,需要明确“广”字的由来,以及“广州”名称的正式出现时间。用户可能对历史事件和地名演变之间的关系感兴趣,所以需要将这些历史背景与名称变化结合起来。

另外,用户可能没有明确说明是否需要更深入的细节,比如不同朝代的行政区划变化,或者现代广州名称的使用情况。不过根据之前的回答,已经涵盖了主要的历史阶段,所以可能不需要进一步扩展。需要注意的是,回答要结构清晰,分点说明,避免信息混杂。

最后,检查是否有遗漏的信息,比如是否有其他别名或名称,但根据提供的工具响应,主要名称已经覆盖。确保回答准确,引用正确的历史事件和文献,如《广州记》和三国时期的分治事件。同时,保持语言简洁,易于理解,避免使用过于学术化的术语,让用户能够轻松理解广州名称的由来。
</think>

广州名称的由来与历史演变可概括如下:

### 一、早期名称
1. **楚庭**(传说中最早名称)  
   - 周夷王八年(前878年)或春秋时期(约前600年),楚国平定南海后建楚庭,或为宫室、市集之意。清代文献将其视为广州最早名称。

2. **南武**(早期行政名称)  
   - 一说为越国旧地名,楚灭越后由公师隅建城沿用;另一说因赵佗称南越武帝,改南海郡为南武郡,后演变为城市名称。

### 二、别名与神话
1. **羊城/穗城**  
   - 源自“五仙乘羊赠穗”的传说:周朝五仙人骑五羊携稻穗降临楚庭,寓意土地丰饶。晋代《广州记》首次记载此传说,后成为广州的文化象征。

### 三、行政名称“广州”的起源
1. **“广”字由来**  
   - 西汉时期,交州下设九郡,广州属交州南海郡。汉武帝平定南越后,以“广布恩信”为由,将州府所在地命名为“广信”(今广西梧州或广东封开),取“广”字寓意。

2. **“广州”名称正式出现**  
   - 三国时期(226年),孙权为加强对岭南的管理,将交州分为交州(合浦以西)和广州(合浦以东),广州作为地名首次出现,辖区涵盖今广东、广西大部分地区。

### 四、名称演变总结
- **楚庭→南武→广信→广州**:从传说名称到行政名称的演变。
- **羊城/穗城**:文化象征性别名,与历史传说紧密关联。
- **现代广州**:自三国分治后,“广州”作为行政区划名称沿用至今,成为城市的核心名称。

这一名称体系既包含历史地理变迁,也融合了神话传说与文化认同,体现了广州作为岭南文化中心的深厚底蕴。
Process finished with exit code 0