如何基于自定義MCP服務器構建支持工具調(diào)用的Llama智能體(含code)
一、背景與目標:從知識隔離到本地化智能體
在人工智能應用日益普及的今天,隱私保護與數(shù)據(jù)主權成為重要挑戰(zhàn)。傳統(tǒng)的AI模型依賴外部服務,導致私有知識面臨泄露風險。本文將詳細介紹如何構建一個完全本地化的AI智能體,通過自定義的Model Context Protocol(MCP)服務器實現(xiàn)知識隔離,并結(jié)合Llama 3.2輕量級模型實現(xiàn)工具調(diào)用能力。這一方案不僅確保數(shù)據(jù)完全在本地運行,還能通過工具調(diào)用與私有知識庫深度交互,為本地化智能應用提供了可行路徑。
(一)MCP服務器的前世今生
在之前的文章中,筆者構建了一個自定義MCP服務器,其核心目標是實現(xiàn)三大功能:
- 只讀訪問控制避免AI模型對文件系統(tǒng)進行寫入操作,防止數(shù)據(jù)篡改。
 - 路徑隱私保護隱藏文件目錄結(jié)構,僅向模型暴露必要的知識內(nèi)容。
 - 協(xié)議深度理解通過自主實現(xiàn)MCP協(xié)議,深入掌握其工作原理。
 
該服務器通過標準輸入輸出(stdio)與外部通信,能夠連接Obsidian知識庫,提供工具調(diào)用接口。例如,list_knowledges工具用于獲取知識庫中的文件列表,get_knowledge_by_uri工具通過URI檢索具體知識內(nèi)容。這些工具為后續(xù)智能體的構建奠定了基礎。
(二)從外部模型到本地化的挑戰(zhàn)
盡管現(xiàn)有MCP服務器已實現(xiàn)知識隔離,但依賴外部AI模型(如Claude)仍存在兩大問題:
- 成本限制Claude等服務需付費訂閱,免費版本功能受限。
 - 隱私風險私有知識需傳輸至外部服務器,存在泄露隱患。
 
因此,構建完全本地化的智能體成為必然選擇。核心目標包括:
- 實現(xiàn)MCP客戶端,與自定義服務器通信。
 - 集成本地運行的LLM模型,替代外部服務。
 - 構建智能體框架,結(jié)合MCP工具與LLM實現(xiàn)問答邏輯。
 
二、核心技術選型:輕量級模型與工具調(diào)用機制
(一)Llama 3.2模型的選擇
在智能體開發(fā)中,語言模型是核心“大腦”??紤]到本地化運行需求,需選擇輕量級且支持工具調(diào)用的模型。Llama 3.2系列的1B/3B模型成為理想選擇,其特點包括:
- 設備友好性可在本地GPU/CPU運行,無需云端資源。
 - 工具調(diào)用支持內(nèi)置對函數(shù)調(diào)用的理解能力,符合MCP協(xié)議需求。
 - 性能平衡通過結(jié)構化剪枝與知識蒸餾,在模型大小與推理能力間取得平衡。
 
Meta官方數(shù)據(jù)顯示,Llama 3.2 3B模型在保持較小體積的同時,能夠處理復雜指令并生成高質(zhì)量響應。例如,在工具調(diào)用場景中,該模型可解析函數(shù)參數(shù)并生成正確的調(diào)用格式,盡管其多輪對話能力稍遜于70B以上的大型模型。
(二)工具調(diào)用流程解析
Llama模型的工具調(diào)用基于特定的提示格式與角色機制,核心流程如下:
1.系統(tǒng)提示定義工具在系統(tǒng)提示中以JSON格式聲明可用工具及其參數(shù)。
{
  "name": "get_user_name",
  "description": "Retrieve a name for a specific user by their unique identifier",
  "parameters": {
    "type": "dict",
    "required": ["user_id"],
    "properties": {"user_id": {"type": "integer"}}
  }
}2.用戶提問觸發(fā)調(diào)用用戶問題觸發(fā)模型判斷是否需要工具。例如,查詢用戶ID為7890的名稱時,模型生成工具調(diào)用表達式[get_user_name(user_id=7890)]。
3.執(zhí)行工具并反饋結(jié)果應用解析調(diào)用表達式,通過MCP客戶端執(zhí)行工具,將結(jié)果(如{"output": "Hyunjong Lee"})以ipython角色返回模型。
4.結(jié)果合成響應模型結(jié)合工具輸出生成最終回答,如“The name of user who has the ID is Hyunjong Lee”。
需要注意的是,輕量級模型(如3B)在處理多輪工具調(diào)用時可能不穩(wěn)定。Meta建議,對于復雜對話場景,優(yōu)先使用70B以上模型,但在單輪或簡單多輪調(diào)用中,3B模型仍可勝任。
三、智能體架構設計:從客戶端到對話邏輯
智能體的整體架構包含三大核心組件:MCP客戶端與管理器、LLM模型接口、智能體邏輯層。以下是各部分的詳細實現(xiàn)。
(一)MCP客戶端與管理器
1. MCP客戶端實現(xiàn)
使用Python的MCP SDK構建客戶端,通過標準輸入輸出與服務器進程通信。核心類MCPClient負責連接服務器、初始化會話并執(zhí)行工具調(diào)用:
class MCPClient:
    async def connect_to_server(self, server_script_path):
        server_params = StdioServerParameters(command="python", args=[server_script_path])
        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.read, self.write = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.read, self.write))
        init_result = await self.session.initialize()
        self.name = f"{init_result.serverInfo.name}(v{init_result.serverInfo.version})"
    async def call_tool(self, name, args):
        response = await self.session.call_tool(name, args)
        return response.isError, response.content客戶端遵循MCP握手流程:首先發(fā)送初始化請求,獲取服務器信息(如協(xié)議版本、工具列表),然后通過list_tools、list_resources等方法枚舉可用資源,通過call_tool執(zhí)行具體工具。
2. MCP管理器
為支持多服務器管理,設計MCPClientManager類,負責客戶端實例的創(chuàng)建、銷毀及工具映射:
class MCPClientManager:
    def __init__(self):
        self.clients = []
        self.tool_map = {}  # 工具名到客戶端索引的映射
    async def init_mcp_client(self, server_paths):
        for path in server_paths:
            client = MCPClient()
            await client.connect_to_server(path)
            self.clients.append(client)
            tools = await client.list_tools()
            for tool in tools:
                self.tool_map[tool.name] = len(self.clients) - 1
    async def call_tool(self, name, params):
        idx = self.tool_map.get(name, -1)
        if idx == -1:
            raise Exception(f"Tool {name} not found")
        return await self.clients[idx].call_tool(name, params)管理器維護工具與客戶端的映射關系,確保工具調(diào)用請求正確路由至對應的服務器實例。
(二)LLM模型集成
1. Llama.cpp的本地化部署
使用Llama.cpp庫在本地運行Llama模型,步驟如下:
- 模型下載從Hugging Face獲取Llama 3.2 3B-Instruct模型權重。
 
from huggingface_hub import snapshot_download
snapshot_download("meta-llama/Llama-3.2-3B-Instruct", local_dir="./models")- 格式轉(zhuǎn)換使用Llama.cpp提供的腳本將模型轉(zhuǎn)換為GGUF格式,便于高效推理。
 
python convert_hf_to_gguf.py ./models/Llama-3.2-3B-Instruct --outfile model.gguf --outtype f16- Python接口封裝:通過自定義類
LlamaCPP包裝Llama.cpp的推理接口,支持提示生成與響應解析。 
class LlamaCPP:
    def __init__(self, model_path):
        self.model = Llama(model_path=model_path, n_ctx=1024)
    def generate(self, prompt, max_tokens=512):
        output = self.model(prompt, max_tokens=max_tokens)
        return output["choices"][0]["text"].strip()2. 提示工程與對話歷史管理
為適配Llama的提示格式,設計LlamaMessage與LlamaPrompt類,負責消息格式化與對話歷史維護:
class LlamaMessage:
    def __init__(self, role, cnotallow="", tool_scheme=""):
        self.role = role
        self.content = content
        self.tool_scheme = tool_scheme
    def template(self, tool_enabled=False):
        prompt = f"<|start_header_id|>{self.role}<|end_header_id|>"
        if tool_enabled and self.tool_scheme:
            prompt += self.tool_scheme
        if self.content:
            prompt += f"{self.content}<|eot_id|>"
        return prompt
class LlamaPrompt:
    def __init__(self):
        self.system_prompt = LlamaMessage("system", "You are a helpful assistant.")
        self.history = History()
    def get_generation_prompt(self, tool_enabled=False, last=50):
        prompt = [self.system_prompt] + self.history.get_chat_history(last) + [LlamaMessage("assistant")]
        return ''.join([msg.template(tool_enabled) for msg in prompt])LlamaPrompt類支持動態(tài)添加用戶、助手、工具結(jié)果等角色的消息,并根據(jù)tool_enabled參數(shù)決定是否在提示中包含工具調(diào)用說明,避免輕量級模型因持續(xù)看到工具指令而產(chǎn)生混淆。
(三)智能體核心邏輯
1. 工具調(diào)用決策與結(jié)果處理
智能體通過正則表達式匹配工具調(diào)用模式,解析函數(shù)名與參數(shù),并調(diào)用對應的MCP工具:
class Agent:
    def __init__(self, model, prompt, mcp_manager):
        self.llm = model
        self.prompt = prompt
        self.mcp_manager = mcp_manager
        self.tool_pattern = re.compile(r'\[([A-Za-z0-9\_]+\(.*?\),?\s?)+\]')
    def _is_tool_required(self, response):
        return bool(self.tool_pattern.match(response))
    async def get_result_tool(self, response):
        results = []
        for name, params in self.parse_func_params(response):
            is_error, content = await self.mcp_manager.call_tool(name, params)
            results.append({"name": name, "output": [c.text for c in content]})
        return json.dumps(results)2. 對話流程控制
智能體的chat方法實現(xiàn)完整的對話流程:
- 用戶提問:將用戶問題與工具調(diào)用說明組合為用戶提示。
 - 模型響應:生成初步回答,判斷是否需要工具調(diào)用。
 - 工具執(zhí)行:若需要,調(diào)用MCP工具并獲取結(jié)果。
 - 結(jié)果合成:將工具結(jié)果加入提示,生成最終回答。
 
async def chat(self, question):
 tool_scheme = TOOL_CALL_PROMPT.format(function_scheme=self.mcp_manager.get_func_scheme())
 user_msg = self.prompt.get_user_prompt(question, tool_scheme)
 self.prompt.append_history(user_msg)
 response = self.llm.generate(self.prompt.get_generation_prompt(tool_enabled=True))
 if self._is_tool_required(response):
     tool_result = await self.get_result_tool(response)
     tool_msg = self.prompt.get_tool_result_prompt(tool_result)
     self.prompt.append_history(tool_msg)
     response = self.llm.generate(self.prompt.get_generation_prompt(tool_enabled=False))
 return response通過tool_enabled參數(shù)的切換,智能體在工具調(diào)用決策階段包含工具指令,而在結(jié)果合成階段移除指令,避免模型過度關注工具調(diào)用,提升回答的連貫性。
四、實驗與優(yōu)化:從問題發(fā)現(xiàn)到效果提升
(一)工具指令注入方式對比
1. 系統(tǒng)提示注入(持續(xù)暴露工具指令)
- 問題表現(xiàn)
 
合成回答時出現(xiàn)空響應或無關工具調(diào)用。
模型過度依賴工具,即使已有結(jié)果仍重復調(diào)用。
案例用戶查詢特定知識內(nèi)容后,模型在回答時錯誤調(diào)用list_knowledges工具。
2. 用戶提示注入(僅在需要時暴露)
- 優(yōu)化策略
 
僅在生成工具調(diào)用決策時包含工具指令。
結(jié)果合成階段移除指令,專注于內(nèi)容整合。
- 效果提升
 
回答相關性顯著提高,工具調(diào)用更精準。
模型能有效利用工具結(jié)果,如正確解析知識內(nèi)容并生成摘要。
(二)實際應用案例
1. 知識摘要生成
- 查詢“請總結(jié)Obsidian知識庫中關于‘AI倫理’的筆記,并以Markdown表格呈現(xiàn)?!?/span>
 - 流程
 
模型調(diào)用get_knowledge_by_uri工具獲取筆記內(nèi)容。
根據(jù)內(nèi)容長度自動格式化為表格,盡管存在輕微參數(shù)錯誤,但結(jié)果結(jié)構化程度高。
2. 空筆記檢測
- 查詢“列出標題存在但內(nèi)容為空的筆記?!?/span>
 - 流程
 
模型調(diào)用list_knowledges獲取所有筆記元數(shù)據(jù)。
通過文件字節(jié)大小判斷空筆記,結(jié)果部分正確,需進一步優(yōu)化篩選邏輯。
3. 問題生成
- 查詢“根據(jù)‘機器學習基礎’筆記內(nèi)容,生成5個簡答題?!?/span>
 - 流程
 
調(diào)用工具獲取筆記內(nèi)容。
模型分析內(nèi)容結(jié)構,生成符合要求的問題,如“什么是監(jiān)督學習?”
(三)性能與局限性
- 模型性能Llama 3.2 3B模型在本地CPU(MacBook M1)上的推理速度約為15 tokens/秒,適合交互式場景。
 - 功能限制
 
多輪工具調(diào)用能力較弱,難以處理復雜推理任務。
對模糊查詢的理解不足,需明確參數(shù)(如正確的URI)才能有效執(zhí)行工具。
代碼倉庫:https://github.com/hjlee94/mcp-knowledge-base















 
 
 









 
 
 
 