MCP原理與Function Call對比分析
發布時間 : 2025-05-06
瀏覽量 : 5.1K

MCP的發展背景

MCP的誕生標誌著prompt engineering進入了一個新的發展階段。它通過提供更結構化的上下文信息,顯著提升了模型的能力。在設計prompt時,我們的目標是能夠將更具體的信息(如本地文件、數據庫內容或網絡即時數據等)集成進來,使模型能夠更好地理解和解決實際問題。

傳統方法的侷限性

在沒有MCP的時代,為了解決複雜問題,我們不得不:

  • 手動從數據庫中篩選信息
  • 使用工具來檢索相關信息
  • 將篩選的信息逐一添加到prompt中

這種方法在處理簡單問題(如需要大模型做歸納總結)時效果不錯,但隨著問題複雜度增加,變得越來越難以應對。

Function Call的出現

為了克服這些挑戰,許多大型語言模型(LLM)平臺(如OpenAI和Google)引入了function call功能。這一機制允許模型根據需要調用預定義函數以獲取數據或執行特定操作,大大提高了自動化程度。

Function Call的侷限性

然而,function call也存在明顯的侷限性:

  • 對平臺的高度依賴
  • 不同LLM平臺間API實現的差異
  • 開發者在切換模型時必須重寫代碼,增加開發成本
  • 安全性和交互性等方面的挑戰

MCP的設計理念

實際上,數據和工具一直都在那裡,關鍵在於如何更智能、更統一地將它們與模型連接起來。Anthropic基於這樣的需求設計了MCP,作為AI模型的"萬能適配器",讓LLM能夠輕鬆訪問數據或調用工具。

MCP的優勢

模型如何智能選擇Agent/工具

MCP的核心是讓我們能方便地調用多個工具,那麼LLM(模型)是在什麼時候確定使用哪些工具的呢?

工具選擇流程

Anthropic為我們提供了詳細的解釋,當用戶提出一個問題時:

  1. 客戶端(Claude Desktop/Cursor)將問題發送給LLM
  2. LLM分析可用的工具,並決定使用哪一個(或多個)
  3. 客戶端通過MCP Server執行所選的工具
  4. 工具的執行結果被送回給LLM
  5. LLM結合執行結果,歸納總結後生成自然語言展示給用戶

模型如何確定使用哪些工具?

通過分析MCP官方提供的client example代碼,可以看出模型是依靠prompt來識別當前可用的工具。具體做法是:

  • 將各個工具的使用描述以文本形式傳遞給模型
  • 使模型能夠了解有哪些工具可供選擇
  • 基於即時情況做出最佳選擇

關鍵代碼分析

async def start(self):
    # 初始化所有的 mcp server
    for server in self.servers:
        await server.initialize()

    # 獲取所有的 tools 命名為 all_tools
    all_tools = []
    for server in self.servers:
        tools = await server.list_tools()
        all_tools.extend(tools)

    # 將所有的 tools 的功能描述格式化成字符串供 LLM 使用
    tools_description = "\\n".join(
        [tool.format_for_llm() for tool in all_tools]
    )

    # 詢問 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}]

    while True:
        # 假設這裡已經處理了用戶消息輸入
        messages.append({"role": "user", "content": user_input})

        # 將 system_message 和用戶消息輸入一起發送給 LLM
        llm_response = self.llm_client.get_response(messages)

工具格式化

工具描述信息是如何格式化的:

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 = name
        self.description: str = description
        self.input_schema: dict[str, Any] = input_schema

    # 把工具的名字/工具的用途(description)和工具所需要的參數(args_desc)轉化為文本
    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)} """

工具信息來源

工具的描述和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 ""  # 獲取函數docstring
    is_async = inspect.iscoroutinefunction(fn)

總結:模型是通過prompt engineering,即提供所有工具的結構化描述和few-shot的example來確定該使用哪些工具。另一方面,Anthropic肯定對Claude做了專門的訓練,畢竟是自家協議,Claude更能理解工具的prompt以及輸出結構化的tool call json代碼。

工具執行與結果反饋機制

工具的執行相對簡單和直接。承接上一步,我們把system prompt(指令與工具調用描述)和用戶消息一起發送給模型,然後接收模型的回覆。

執行流程

當模型分析用戶請求後,它會決定是否需要調用工具:

  • 無需工具時:模型直接生成自然語言回覆
  • 需要工具時:模型輸出結構化JSON格式的工具調用請求

如果回覆中包含結構化JSON格式的工具調用請求,則客戶端會根據這個json代碼執行對應的工具。

代碼實現

async def start(self):
    # 上面已經介紹過了,模型如何選擇工具

    while True:
        # 假設這裡已經處理了用戶消息輸入
        messages.append({"role": "user", "content": user_input})

        # 獲取 LLM 的輸出
        llm_response = self.llm_client.get_response(messages)

        # 處理 LLM 的輸出(如果有 tool call 則執行對應的工具)
        result = await self.process_llm_response(llm_response)

        # 如果 result 與 llm_response 不同,說明執行了 tool call(有額外信息了)
        # 則將 tool call 的結果重新發送給 LLM 進行處理
        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}
            )
        # 否則代表沒有執行 tool call,則直接將 LLM 的輸出返回給用戶
        else:
            messages.append({"role": "assistant", "content": llm_response})

錯誤處理

如果tool call的json代碼存在問題或者模型產生了幻覺,系統會skip掉無效的調用請求。

結論與實踐建議

根據上述原理分析,可以看出工具文檔至關重要。模型依賴於工具描述文本來理解和選擇適用的工具,這意味著精心編寫的工具名稱、文檔字符串(docstring)以及參數說明顯得尤為重要。

鑑於MCP的選擇機制基於prompt實現,理論上任何模型只要能夠提供相應的工具描述就能與MCP兼容使用。


AIbase
智啟未來,您的人工智慧解決方案智庫
© 2025AIbase