Home avatar

知易行难恒愈难

MCP和LLM的调用细节

内容简介
介绍MCP和LLM之间的协作细节,讲解LLM是如何利用MCP服务来扩展自己的边际能力的。

在用go做了几个MCP的Demo服务后,又对MCP的架构和协议细节进行了深入地学习,对MCP的理解深刻了很多。但是在开发过程中还是有两个关键的问题未得到解答:

  • cline插件、我的MCP服务、大模型这三者之间的调用流程是怎样的?
  • 大模型是在什么时候确定使用哪些MCP服务的呢?

这里需要注意下,我使用的是vscode的cline插件,所以这里拿cline举例,但是其实客户端也可以是cursor、cherry studio等其他客户端。

在 MCP 官网为我们提供了一个解释

  1. 客户端将你的问题发送给 Claude
  2. Claude 分析可用的工具,并决定使用哪一个或多个
  3. 客户端通过 MCP Server 执行所选的工具
  4. 工具的执行结果被送回给 Claude
  5. Claude 结合执行结果生成回答
  6. 回应最终展示给用户

从以上的解释可以看出,大模型和MCP服务之间的调用过程是分两步完成的:

  1. 由 LLM 确定使用哪些 MCP Server
  2. 执行对应的 MCP Server 并对执行结果进行重新处理

所以 MCP Server 是由大模型主动选择并调用的。但是大模型具体又是如何确定该使用哪些工具呢?从 MCP 官方提供的 pyhton client example 中可以得到答案:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
    async def start(self) -> None:
        """Main chat session handler."""
        try:
            for server in self.servers:
                try:
                    await server.initialize()
                except Exception as e:
                    logging.error(f"Failed to initialize server: {e}")
                    await self.cleanup_servers()
                    return

            all_tools = []
            for server in self.servers:
                tools = await server.list_tools()
                all_tools.extend(tools)

            tools_description = "\n".join([tool.format_for_llm() for tool in all_tools])

            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}]

            while True:
                try:
                    user_input = input("You: ").strip().lower()
                    if user_input in ["quit", "exit"]:
                        logging.info("\nExiting...")
                        break

                    messages.append({"role": "user", "content": user_input})

                    llm_response = self.llm_client.get_response(messages)
                    logging.info("\nAssistant: %s", llm_response)

                    result = await self.process_llm_response(llm_response)

                    if result != llm_response:
                        messages.append({"role": "assistant", "content": llm_response})
                        messages.append({"role": "system", "content": result})

                        final_response = self.llm_client.get_response(messages)
                        logging.info("\nFinal response: %s", final_response)
                        messages.append(
                            {"role": "assistant", "content": final_response}
                        )
                    else:
                        messages.append({"role": "assistant", "content": llm_response})

                except KeyboardInterrupt:
                    logging.info("\nExiting...")
                    break

        finally:
            await self.cleanup_servers()

            ... # 省略其他代码

从代码可以看出,在和大模型进行交互前,将所有工具的结构化描述放到tools_description中,再添加到system_message中,然后把system_message和用户消息一起发送给模型。当模型分析用户请求后,它会决定是否需要调用工具:

MCP架构和协议解析

内容简介
介绍 MCP 的架构和协议实现。

随着最近 MCP 在各类文章和评论区中逐渐活跃,我也找了几篇 MCP 相关的文章进行阅读学习,但是我发现这些文章都大同小异,没有让我真正深入理解MCP。我也跟着一些文章用 go 语言实现了几个 MCP 的Demo服务,但是对 MCP 依然一知半解,所以感觉还是得系统深入的学习并总结下。

MCP是什么

MCP 是一种开放协议,用于标准化应用程序如何为 LLM 提供上下文。可以将 MCP 视为 AI 应用程序的 USB-C 端口。正如 USB-C 提供了一种标准化的方式来将设备连接到各种外围设备和配件,MCP 也提供了一种标准化的方式,用于将 AI 模型连接到不同的数据源和工具。

Nacos集群搭建

内容简介
详细介绍nacos集群搭建步骤和过程。

环境准备

本文采用的Linux系统为 Rocky Linux release 8.10 版本,nacos版本为 2.2.3,数据库使用mysql的 5.7.43 版本。

Hugo 自定义页面

内容简介
基于主题在 Hugo 博客中添加一个新的页面。这里说的页面不是指 Hugo 利用 markdown 文件生成的页面,而是我们自己手动创建的 html 页面。

我想添加一个网站合集页面,这个页面分类展示各类有用的网站。但是 FixIt 主题中并没有提供这个页面实现,所以只能自己动手写一个。

前置知识

添加自定义页面其实并不复杂,但是需要了解一些 HTML、CSS、JavaScript 的基础,还有 Hugo 中函数的用法。其次不太推荐使用类似 Vue 这样的 JS 框架,因为 Hugo 的页面使用了大量的 golang 模板语法,就是你在主题文件中看到的{{}}语法,而 Vue 中也有到这个语法,这可能会在无意中给你的开发造成障碍,甚至在解析模板时出错。

Hugo学习

内容简介
简单介绍 Hugo 的一些核心概念和原理,包括核心工作流程、构建过程解析、目录结构、如何安装等。

Hugo简介

Hugo是一个用 Go 语言编写的开源静态网站生成器,具有以下核心特点:

0%