自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

MCP 和 Function Calling:示例

開發(fā) 后端
本文以實際例子來加深對 MCP 和 Function Calling 的理解。實現(xiàn)這樣一個場景:和大模型聊天,然后讓大模型將回答的內容總結后保存到 flomo 筆記中。

本文以實際例子來加深對 MCP 和 Function Calling 的理解。

實現(xiàn)這樣一個場景:和大模型聊天,然后讓大模型將回答的內容總結后保存到 flomo 筆記中。

Function Calling

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

思路

  • 寫一個 api 接口,這個接口的作用將輸入的內容存入 flomo 筆記中。
  • 利用 qwen-agent 框架來實現(xiàn) function calling ,最終調用自定義開發(fā)的 api 接口。

實現(xiàn)

api 接口使用任何語言都行,我這里使用的是 python 的 flask 框架。

@api_bp.route('/flomo/save', methods=['POST'])
def save_to_flomo():
    # 獲取請求數(shù)據
    data = request.get_json()
    
    # 驗證請求數(shù)據
    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:
        # 準備發(fā)送到Flomo的數(shù)據
        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}"
        
        # 發(fā)送請求到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

創(chuàng)建一個 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步:將對話和可用函數(shù)發(fā)送給模型
    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直接是結果,不需要循環(huán)
    if isinstance(responses, list):
        messages.extend(responses) 
    else:
        messages.append(responses) 

    # 第2步:檢查模型是否想要調用函數(shù)
    last_response = messages[-1]
    if last_response.get('function_call', None):
        # 第3步:調用函數(shù)
        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步:發(fā)送每個函數(shù)調用和函數(shù)響應到模型,讓大模型返回最終的結果
        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 方法就是大模型需要用到的函數(shù),函數(shù)中調用第一步寫的接口,將內容存儲到 flomo 筆記中。
  • test 方法中首先進行初始化,http://localhost:11434/v1 是本地通過 ollama 運行 qwen2.5:7b 模型的地址。
  • 后面的步驟在上面代碼中寫有注釋。

在 qwen_client.py  所在目錄執(zhí)行下面的命令安裝 qweb-agent 框架:

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

執(zhí)行 python qwen_client.py 運行程序。

檢查 flomo 客戶端,可以看到內容已經存儲進來了。

MCP

MCP 的使用,可以自己開發(fā)服務端,也可以使用 MCP 服務站的服務,比如 mcp.so 。客戶端有很多,比如:Windsurf、Cursor、CherryStudio 等。

Windsurf 中使用 MCP

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

連接 Server 的方式選擇了 Original 。

在 Windsurf 中的 MCP 設置中添加 flomo 的 Server 。

配置好后,在 chat 模式下進行提問:“根據最新的內容對比下 mcp 和 A2A,將結果存儲到筆記中”。

Windsurf 一通查詢,整理后,調用 MCP 工具,將結果存到我的 flomo 中了。

代碼示例

很久沒用 dotnet 了,這個例子就用 dotnet 來實現(xiàn)吧。

工具和環(huán)境:

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

創(chuàng)建 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 服務失敗");
}

添加 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 完成");
        }
    }
}

創(chuàng)建 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 服務中的所有工具,并打印出來。執(zhí)行效果如下:

client 的 Program 中繼續(xù)添加下面代碼進行直接的 Server 端方法調用,來測試下 client 和 server 是否是連通的。

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

執(zhí)行完后,如果 flomo 中筆記插入正常,說明調用成功。

接著調用本地 ollama 運行的大模型來實現(xiàn)跟大模型對話,然后將對話結果保存到 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參數(shù)
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 運行程序,結果如下:

責任編輯:姜華 來源: 不止dotNET
相關推薦

2025-04-01 08:45:56

2025-04-22 09:17:41

2024-09-29 10:58:56

2023-10-30 17:36:08

OpenAIAPI插件

2025-03-13 03:00:00

DockerAgentic工具

2025-03-31 00:00:00

MCPAPI服務器通信

2025-03-21 09:00:00

2025-04-02 10:06:00

2023-05-29 09:18:28

.NET網絡通信

2025-04-11 09:43:57

2016-04-09 17:29:33

銳捷網絡云營銷

2023-02-24 07:42:30

Java動態(tài)代理

2025-03-26 03:01:00

2022-01-25 18:11:55

vdomclassfunction

2025-04-02 03:55:00

MCPAI智能體

2025-04-18 00:00:00

MCPSSEHTTP

2013-07-30 13:35:12

methodfunction

2025-03-18 08:14:05

2025-04-14 00:00:00

MCPjson 信息地理編碼
點贊
收藏

51CTO技術棧公眾號