本文以实际例子来加深对 MCP 和 Function Calling 的理解。

实现这样一个场景:和大模型聊天,然后让大模型将回答的内容总结后保存到 flomo 笔记中。

Function Calling

我们知道 Function Calling 和模型的能力有关,我使用的是 qwen2.5:7b 模型,用 ollama 在本地运行。

思路

1、写一个 api 接口,这个接口的作用将输入的内容存入 flomo 笔记中。

2、利用 qwen-agent 框架来实现 function calling ,最终调用自定义开发的 api 接口。

实现

1、api 接口使用任何语言都行,我这里使用的是 python 的 flask 框架。

@api_bp.route('/flomo/save', methods=['POST'])
def save_to_flomo():
    # 获取请求数据
    data = request.get_json()
    
    # 验证请求数据
    if not data or 'content' not in data:
        return jsonify({"error": "Missing required field: content"}), 400
    
    content = data['content']
    tags = data.get('tags', [])  # 可选的标签列表
    
    # 验证Flomo API URL是否配置
    flomo_api_url = current_app.config.get('FLOMO_API_URL')
    if not flomo_api_url:
        return jsonify({"error": "Flomo API URL not configured"}), 500
    
    try:
        # 准备发送到Flomo的数据
        flomo_data = {
            "content": content
        }
        
        # 如果有标签,添加到内容中
        if tags:
            # Flomo使用 #tag 格式的标签
            tag_text = ' '.join([f"#{tag}" for tag in tags])
            flomo_data["content"] = f"{content}\n\n{tag_text}"
        
        # 发送请求到Flomo API
        headers = {
            'Content-Type': 'application/json'
        }
        
        response = requests.post(
            flomo_api_url,
            headers=headers,
            data=json.dumps(flomo_data),
            timeout=10  # 设置超时时间,处理大文本可能需要更长时间
        )
        
        # 检查响应
        if response.status_code == 200:
            return jsonify({
                "message": "Content successfully saved to Flomo",
                "flomo_response": response.json()
            }), 200
        else:
            return jsonify({
                "error": "Failed to save to Flomo",
                "status_code": response.status_code,
                "response": response.text
            }), 500
            
    except requests.RequestException as e:
        # 处理请求异常
        return jsonify({
            "error": f"Request to Flomo failed: {str(e)}"
        }), 500
    except Exception as e:
        # 处理其他异常
        return jsonify({
            "error": f"Unexpected error: {str(e)}"
        }), 500

2、创建一个 qwen-client.py 的文件,内容如下:

import json
import requests
from qwen_agent.llm import get_chat_model

def save_to_flomo(content):
    """Save content to Flomo notes"""
    try:
        api_url = "http://localhost:6500/api/flomo/save" 
        
        data = {"content": content}
        
        response = requests.post(
            api_url,
            headers={"Content-Type": "application/json"},
            json=data,
            timeout=10
        )
        
        if response.status_code == 200:
            print(f"Successfully saved to Flomo: {content}")
            return json.dumps(response.json())
        else:
            error_message = f"Failed to save to Flomo. Status code: {response.status_code}, Response: {response.text}"
            print(error_message)
            return json.dumps({"error": error_message})
            
    except Exception as e:
        error_message = f"Error calling Flomo API: {str(e)}"
        print(error_message)
        return json.dumps({"error": error_message})


def test(fncall_prompt_type: str = 'qwen'):
    llm = get_chat_model({
        'model': 'qwen2.5:7b',
        'model_server': 'http://localhost:11434/v1',
        'api_key': "",
        'generate_cfg': {
            'fncall_prompt_type': fncall_prompt_type
        }
    })

    # 第1步:将对话和可用函数发送给模型
    messages = [{'role': 'user', 'content': "怎么学习软件架构,总结为三点,保存到笔记"}]
    functions = [{
        'name': 'save_to_flomo',
        'description': '保存内容到Flomo笔记',
        'parameters': {
            'type': 'object',
            'properties': {
                'content': {
                    'type': 'string',
                    'description': '内容',
                }
            },
            'required': ['content'],
        },
    }]

    responses = []
    for responses in llm.chat(
            messages=messages,
            functions=functions,
            stream=False,  
    ):
        print(responses)

    # 如果使用stream=False,responses直接是结果,不需要循环
    if isinstance(responses, list):
        messages.extend(responses) 
    else:
        messages.append(responses) 

    # 第2步:检查模型是否想要调用函数
    last_response = messages[-1]
    if last_response.get('function_call', None):
        # 第3步:调用函数
        available_functions = {
            'save_to_flomo': save_to_flomo,
        }
        function_name = last_response['function_call']['name']
        function_to_call = available_functions[function_name]
        function_args = json.loads(last_response['function_call']['arguments'])
        function_response = function_to_call(
            content=function_args.get('content'),
        )
        print('# Function Response:')
        print(function_response)

        # 第4步:发送每个函数调用和函数响应到模型,让大模型返回最终的结果
        messages.append({
            'role': 'function',
            'name': function_name,
            'content': function_response,
        }) 

        for responses in llm.chat(
                messages=messages,
                functions=functions,
                stream=False,
        ): 
            print(responses)

if __name__ == '__main__':
    test()
  • save_to_flomo 方法就是大模型需要用到的函数,函数中调用第一步写的接口,将内容存储到 flomo 笔记中。
  • test 方法中首先进行初始化,http://localhost:11434/v1 是本地通过 ollama 运行 qwen2.5:7b 模型的地址。
  • 后面的步骤在上面代码中写有注释。

3、在 qwen_client.py 所在目录执行下面的命令安装 qweb-agent 框架:

pip install -U "qwen-agent[gui,rag,code_interpreter,mcp]"

4、执行 python qwen_client.py 运行程序。

图片

5、检查 flomo 客户端,可以看到内容已经存储进来了。

图片

MCP

MCP 的使用,可以自己开发服务端,也可以使用 MCP 服务站的服务,比如 mcp.so 。客户端有很多,比如:Windsurf、Cursor、CherryStudio 等。

Windsurf 中使用 MCP

1、先在 mcp.so 中找到 flomo 的 Server 。

图片

2、连接 Server 的方式选择了 Original 。

图片

3、在 Windsurf 中的 MCP 设置中添加 flomo 的 Server 。

图片

4、配置好后,在 chat 模式下进行提问:“根据最新的内容对比下 mcp 和 A2A,将结果存储到笔记中”。

图片

Windsurf 一通查询,整理后,调用 MCP 工具,将结果存到我的 flomo 中了。

图片

代码示例

很久没用 dotnet 了,这个例子就用 dotnet 来实现吧。

工具和环境:

  • dotnet:8.0
  • ModelContextProtocol:0.1.0-preview.8
  • 工具:Windsurf

1、创建 mcp-server 控制台项目,Program 代码如下:

using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using Microsoft.Extensions.DependencyInjection;
using FlomoMcpServer;

try
{
  Console.WriteLine("启动 MCP 服务...");

  var builder = Host.CreateEmptyApplicationBuilder(settings: null);
  builder.Services
    .AddMcpServer()
    .WithStdioServerTransport()
    .WithToolsFromAssembly();


  await builder.Build().RunAsync();
}
catch (Exception)
{
  Console.WriteLine("启动 MCP 服务失败");
}

2、添加 flomo 工具类 FlomoTools.cs ,内容如下:

using ModelContextProtocol.Server;
using System.ComponentModel;

namespace FlomoMcpServer
{
    [McpServerToolType]
    public static class FlomoTools
    {
        [McpServerTool]
        [Description("写笔记到 Flomo")]
        public static async Task WriteNote(string content)
        {
            Console.WriteLine("写笔记到 Flomo...");
            if (string.IsNullOrEmpty(content))
            {
                throw new ArgumentNullException("content");
            }

            var apiUrl = "https://flomoapp.com/iwh/xxxxxxxxxxxxx/";

            using (var httpClient = new HttpClient())
            {
                var payload = new { content = content };
                var json = System.Text.Json.JsonSerializer.Serialize(payload);
                var httpContent = new StringContent(json, System.Text.Encoding.UTF8, "application/json");
                var response = await httpClient.PostAsync(apiUrl, httpContent);
                response.EnsureSuccessStatusCode();
            }

            Console.WriteLine("写笔记到 Flomo 完成");
        }
    }
}

3、创建 mcp-client 控制台项目,Program 代码如下:

using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using System.Collections.Generic;

var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
    Name = "flomo",
    Command = "dotnet",
    Arguments = new[] { "/Users/fengwei/Projects/ai-demo/dotnet-mcp-demo/mcp-server/bin/Debug/net8.0/mcp-server.dll" }
});

await using var client = await McpClientFactory.CreateAsync(clientTransport);

var tools = await client.ListToolsAsync();
foreach (var tool in tools)
{
    Console.WriteLine($"{tool.Name}: {tool.Description}");
}

上面例子中使用的是本地 Stdio 的模式。通过 client.ListToolsAsync(); 获取 MCP 服务中的所有工具,并打印出来。执行效果如下:

图片

4、client 的 Program 中继续添加下面代码进行直接的 Server 端方法调用,来测试下 client 和 server 是否是连通的。

var result = await client.CallToolAsync("WriteNote", new Dictionary<string, object?>
{
    ["content"] = "Hello, oec2003!"
});
Console.WriteLine($"Result: {result}");

执行完后,如果 flomo 中笔记插入正常,说明调用成功。

5、接着调用本地 ollama 运行的大模型来实现跟大模型对话,然后将对话结果保存到 flomo 。Client 端的 Program 完整代码如下:

using Microsoft.Extensions.Hosting;
using ModelContextProtocol;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol.Transport;
using Microsoft.Extensions.DependencyInjection;
using System.Text;
using System.Text.Json;
using System.Net.Http;
using System.Net.Http.Json;
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;

Console.WriteLine("启动 MCP 客户端...");

var clientTransport = new StdioClientTransport(new StdioClientTransportOptions
{
    Name = "flomo",
    Command = "dotnet",
    Arguments = new[] { "/Users/fengwei/Projects/ai-demo/dotnet-mcp-demo/mcp-server/bin/Debug/net8.0/mcp-server.dll" }
});

await using var mcpClient = await McpClientFactory.CreateAsync(clientTransport);

Console.WriteLine("已连接到 MCP 服务器");
Console.WriteLine("可用工具:");
foreach (var tool in await mcpClient.ListToolsAsync())
{
    Console.WriteLine($"{tool.Name}: {tool.Description}");
}

// 配置硅基流动API参数
var apiKeyCredential = new ApiKeyCredential("xx");
var aiClientOptions = new OpenAIClientOptions();
aiClientOptions.Endpoint = new Uri("http://localhost:11434/v1");
var aiClient = new OpenAIClient(apiKeyCredential, aiClientOptions)
    .AsChatClient("qwen2.5:7b");

var chatClient = new ChatClientBuilder(aiClient)
    .UseFunctionInvocation()
    .Build();

var mcpTools = await mcpClient.ListToolsAsync();
var chatOptions = new ChatOptions() {
    Tools = [..mcpTools]
};

Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"助手> 请输入想要记录的内容,AI总结后会存入笔记");
while (true)
{
    Console.ForegroundColor = ConsoleColor.White;
    Console.Write("用户> ");
    var question = Console.ReadLine();
    
    if (!string.IsNullOrWhiteSpace(question) && question.ToUpper() == "EXIT")
        break;

    var messages = new List<ChatMessage> {
        new(ChatRole.System, "你是一个笔记助手,请将用户的输入总结为简洁的笔记形式,使用markdown格式。保留关键信息,删除冗余内容。"),
        new(ChatRole.User, question)
    };
    
    try 
    {
        var response = await chatClient.GetResponseAsync(messages, chatOptions);
        var content = response.ToString();
        Console.WriteLine($"助手> {content}");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"错误: {ex.Message}");
    }
    
    Console.WriteLine();
}

输入 dotnet run 运行程序,结果如下:

图片

如何学习大模型 AI ?

由于新岗位的生产效率,要优于被取代岗位的生产效率,所以实际上整个社会的生产效率是提升的。

但是具体到个人,只能说是:

“最先掌握AI的人,将会比较晚掌握AI的人有竞争优势”。

这句话,放在计算机、互联网、移动互联网的开局时期,都是一样的道理。

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在人工智能学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多互联网行业朋友无法获得正确的资料得到学习提升,故此将并将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。

在这里插入图片描述

第一阶段(10天):初阶应用

该阶段让大家对大模型 AI有一个最前沿的认识,对大模型 AI 的理解超过 95% 的人,可以在相关讨论时发表高级、不跟风、又接地气的见解,别人只会和 AI 聊天,而你能调教 AI,并能用代码将大模型和业务衔接。

  • 大模型 AI 能干什么?
  • 大模型是怎样获得「智能」的?
  • 用好 AI 的核心心法
  • 大模型应用业务架构
  • 大模型应用技术架构
  • 代码示例:向 GPT-3.5 灌入新知识
  • 提示工程的意义和核心思想
  • Prompt 典型构成
  • 指令调优方法论
  • 思维链和思维树
  • Prompt 攻击和防范

第二阶段(30天):高阶应用

该阶段我们正式进入大模型 AI 进阶实战学习,学会构造私有知识库,扩展 AI 的能力。快速开发一个完整的基于 agent 对话机器人。掌握功能最强的大模型开发框架,抓住最新的技术进展,适合 Python 和 JavaScript 程序员。

  • 为什么要做 RAG
  • 搭建一个简单的 ChatPDF
  • 检索的基础概念
  • 什么是向量表示(Embeddings)
  • 向量数据库与向量检索
  • 基于向量检索的 RAG
  • 搭建 RAG 系统的扩展知识
  • 混合检索与 RAG-Fusion 简介
  • 向量模型本地部署

第三阶段(30天):模型训练

恭喜你,如果学到这里,你基本可以找到一份大模型 AI相关的工作,自己也能训练 GPT 了!通过微调,训练自己的垂直大模型,能独立训练开源多模态大模型,掌握更多技术方案。

到此为止,大概2个月的时间。你已经成为了一名“AI小子”。那么你还想往下探索吗?

  • 为什么要做 RAG
  • 什么是模型
  • 什么是模型训练
  • 求解器 & 损失函数简介
  • 小实验2:手写一个简单的神经网络并训练它
  • 什么是训练/预训练/微调/轻量化微调
  • Transformer结构简介
  • 轻量化微调
  • 实验数据集的构建

第四阶段(20天):商业闭环

对全球大模型从性能、吞吐量、成本等方面有一定的认知,可以在云端和本地等多种环境下部署大模型,找到适合自己的项目/创业方向,做一名被 AI 武装的产品经理。

  • 硬件选型
  • 带你了解全球大模型
  • 使用国产大模型服务
  • 搭建 OpenAI 代理
  • 热身:基于阿里云 PAI 部署 Stable Diffusion
  • 在本地计算机运行大模型
  • 大模型的私有化部署
  • 基于 vLLM 部署大模型
  • 案例:如何优雅地在阿里云私有部署开源大模型
  • 部署一套开源 LLM 项目
  • 内容安全
  • 互联网信息服务算法备案

学习是一个过程,只要学习就会有挑战。天道酬勤,你越努力,就会成为越优秀的自己。

如果你能在15天内完成所有的任务,那你堪称天才。然而,如果你能完成 60-70% 的内容,你就已经开始具备成为一名大模型 AI 的正确特征了。

这份完整版的大模型 AI 学习资料已经上传CSDN,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

在这里插入图片描述

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐