谷歌ADK接入文件操作MCP

文章目錄

  • MCP基礎概念
  • 文件操作服務器
  • 文件操作MCP接入谷歌ADK
    • 項目創建
    • 多輪對話代碼

MCP基礎概念

  • MCP技術體系中,會將外部工具運行腳本稱作服務器,而接入這些外部工具的大模型運行環境稱作客戶端。
    在這里插入圖片描述
  • 一個客戶端可以接入多個不同類型的服務器,但都要遵循MCP通信協議。
  • MCP服務器的輸出內容是一種標準格式的內容,只能被MCP客戶端所識別。在客戶端和服務器都遵循MCP協議的時候,客戶端就能夠像Function calling中大模型調用外部工具一樣,調用MCP服務器里面的工具。
    在這里插入圖片描述
  • ADK作為客戶端,接入MCP工具。核心MCPToolset類,該類是ADK連接到MCP服務器的橋梁。
  • 在實際過程中,ADK代理將執行以下操作MCPToolset
    1. 連接:與MCP服務器進程建立連接。該服務器可以是通過標準輸入/輸出 ( StdioServerParameters ) 進行通信的本地服務器,也可以是使用服務器發送事件 ( SseServerParams ) 的遠程服務器。
    2. 發現:查詢MCP服務器以獲取可用工具(list_tools MCP 方法)。
    3. 適應:將MCP工具模式轉換為ADK兼容BaseTool實例。
    4. 公開:將這些適配的工具呈現給ADK Agent
    5. 代理調用:當Agent決定使用其中一個工具時,發到MCP服務器并返回結果。MCPToolset將調用(call_tool MCP方法)轉
    6. 管理連接:處理與MCP服務器進程的連接的生命周期,通常需要明確清理的周期(如進程結束后清理)。

文件操作服務器

  • Filesystem服務器是一個最基礎同時也是最常用的MCP服務器,同時也是官方推薦的服務器,服務器項目地址。

文件操作MCP接入谷歌ADK

項目創建

  1. 項目框架搭建:
    # 初始化項目
    uv init adk_mcp
    # 進入項目目錄
    cd adk_mcp
    # 創建虛擬環境
    uv venv
    # 激活虛擬環境
    .venv\Scripts\activate
    # 安裝依賴
    uv add google-adk litellm
    
  2. 創建環境配置文件.env
    OPENAI_API_KEY=xxx
    OPENAI_API_BASE=https://api.openai-hk.com/v1
    MODEL=openai/gpt-4o-mini
    
  3. 創建main.py,代碼內容如下:
    # 導入基礎庫
    import os
    import asyncio
    from dotenv import load_dotenv
    from google.genai import types
    from google.adk.agents import Agent
    from google.adk.models.lite_llm import LiteLlm
    from google.adk.agents.llm_agent import LlmAgent
    from google.adk.runners import Runner
    from google.adk.sessions import InMemorySessionService
    from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters,StdioConnectionParamsDS_API_KEY = os.getenv("OPENAI_API_KEY")
    DS_BASE_URL = os.getenv("OPENAI_API_BASE")
    DS_MODEL = os.getenv("MODEL")model = LiteLlm(model=DS_MODEL,api_base=DS_BASE_URL,api_key=DS_API_KEY
    )# --- Step 1: 創建導入MCP工具函數 ---
    async def get_tools_async():"""Gets tools from the File System MCP Server."""print("Attempting to connect to MCP Filesystem server...")# 確保目錄存在test_dir = "D:\\Code\\adk_mcp\\test"if not os.path.exists(test_dir):os.makedirs(test_dir)print(f"Created directory: {test_dir}")try:# 創建MCP工具集實例mcp_toolset = MCPToolset(# 這里采用Studio通信方式進行調用connection_params=StdioConnectionParams(server_params=StdioServerParameters(command='npx',args=["-y", "@modelcontextprotocol/server-filesystem", test_dir],)))# 等待服務器啟動await asyncio.sleep(2)# 獲取工具列表tools = await mcp_toolset.get_tools()print("MCP Toolset created successfully.")print(f"Fetched {len(tools)} tools from MCP server.")# 返回工具和工具集對象用于清理return tools, mcp_toolsetexcept Exception as e:print(f"Error creating MCP tools: {e}")raise# --- Step 2: 創建ADK Agent --
    async def get_agent_async():"""Creates an ADK Agent equipped with tools from the MCP Server."""try:tools, mcp_toolset = await get_tools_async()root_agent = Agent(model=model,name='filesystem_assistant',instruction='Help user interact with the local filesystem using available tools.',tools=tools,  # 將MCP工具加載到Agent中)return root_agent, mcp_toolsetexcept Exception as e:print(f"Error creating agent: {e}")raise# --- Step 3: 執行主邏輯 ---
    async def async_main():mcp_toolset = Nonetry:# 創建會話管理器session_service = InMemorySessionService()session = await session_service.create_session(state={}, app_name='mcp_filesystem_app', user_id='user_fs')# 用戶輸入query = "請幫我查找目前文件夾里都有哪些文件?"print(f"User Query: '{query}'")content = types.Content(role='user', parts=[types.Part(text=query)])root_agent, mcp_toolset = await get_agent_async()runner = Runner(app_name='mcp_filesystem_app',agent=root_agent,session_service=session_service,)print("Running agent...")events_async = runner.run_async(session_id=session.id, user_id=session.user_id, new_message=content)async for event in events_async:print(f"Event received: {event}")except Exception as e:print(f"Error during main execution: {e}")raisefinally:# 運行完成后,關閉MCP服務器連接if mcp_toolset:print("Closing MCP server connection...")await mcp_toolset.close()print("Cleanup complete.")if __name__ == '__main__':try:asyncio.run(async_main())except Exception as e:print(f"An error occurred: {e}")
    
  • 執行結果如下:
    (adk_mcp) D:\Code\adk_mcp>uv run main.py
    D:\Code\adk_mcp\.venv\Lib\site-packages\pydantic\_internal\_fields.py:198: UserWarning: Field name "config_type" in "SequentialAgent" shadows an attribute in parent "BaseAgent"warnings.warn(
    User Query: '請幫我查找目前文件夾里都有哪些文件?'
    Attempting to connect to MCP Filesystem server...
    D:\Code\adk_mcp\.venv\Lib\site-packages\google\adk\tools\mcp_tool\mcp_tool.py:87: UserWarning: [EXPERIMENTAL] BaseAuthenticatedTool: This feature is experimental and may change or be removed in future versions without notice. It may introduce breaking changes at any time.super().__init__(
    auth_config or auth_config.auth_scheme is missing. Will skip authentication.Using FunctionTool instead if authentication is not required.
    MCP Toolset created successfully.
    Fetched 14 tools from MCP server.
    Running agent...
    Event received: content=Content(parts=[Part(text="""首先,我需要確定您當前的工作目錄位置。由于您沒有指定具體路徑,我將先獲取允許訪問的目錄列表:"""),Part(function_call=FunctionCall(args={},id='call_5201be00eeb24a989be18f',name='list_allowed_directories')),],role='model'
    ) grounding_metadata=None partial=False turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=GenerateContentResponseUsageMetadata(candidates_token_count=627,prompt_token_count=1806,total_token_count=2433
    ) live_session_resumption_update=None invocation_id='e-1710d865-f562-4895-ae06-4a3f0155c138' author='filesystem_assistant' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=set() branch=None id='71634100-b10a-4a81-b779-f34f52df12eb' timestamp=1754979735.932518
    Event received: content=Content(parts=[Part(function_response=FunctionResponse(id='call_5201be00eeb24a989be18f',name='list_allowed_directories',response={'result': CallToolResult(content=[<... Max depth ...>,],isError=False)})),],role='user'
    ) grounding_metadata=None partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=None live_session_resumption_update=None invocation_id='e-1710d865-f562-4895-ae06-4a3f0155c138' author='filesystem_assistant' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='00b7f823-5a66-493d-95bd-55ee93466fc2' timestamp=1754979761.37467
    Event received: content=Content(parts=[Part(text="""根據系統配置,允許訪問的目錄是 `D:\Code\adk_mcp\test`。我將列出該目錄下的文件:"""),Part(function_call=FunctionCall(args={'path': 'D:\\Code\\adk_mcp\\test'},id='call_2f66909ac2054847b6f9d3',name='list_directory')),],role='model'
    ) grounding_metadata=None partial=False turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=GenerateContentResponseUsageMetadata(candidates_token_count=1105,prompt_token_count=1906,total_token_count=3011
    ) live_session_resumption_update=None invocation_id='e-1710d865-f562-4895-ae06-4a3f0155c138' author='filesystem_assistant' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=set() branch=None id='39358200-064d-478e-90f6-ab0855f13ec4' timestamp=1754979761.380695
    Event received: content=Content(parts=[Part(function_response=FunctionResponse(id='call_2f66909ac2054847b6f9d3',name='list_directory',response={'result': CallToolResult(content=[<... Max depth ...>,],isError=False)})),],role='user'
    ) grounding_metadata=None partial=None turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=None live_session_resumption_update=None invocation_id='e-1710d865-f562-4895-ae06-4a3f0155c138' author='filesystem_assistant' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='2d1197f7-c81d-41a5-9fc5-15652c50fa7e' timestamp=1754979803.754635
    Event received: content=Content(parts=[Part(text="""當前目錄 `D:\Code\adk_mcp\test` 中的文件如下:- 📄 **系統架構設計師教程_帶目錄高清版.pdf**"""),],role='model'
    ) grounding_metadata=None partial=False turn_complete=None error_code=None error_message=None interrupted=None custom_metadata=None usage_metadata=GenerateContentResponseUsageMetadata(candidates_token_count=128,prompt_token_count=2021,total_token_count=2149
    ) live_session_resumption_update=None invocation_id='e-1710d865-f562-4895-ae06-4a3f0155c138' author='filesystem_assistant' actions=EventActions(skip_summarization=None, state_delta={}, artifact_delta={}, transfer_to_agent=None, escalate=None, requested_auth_configs={}) long_running_tool_ids=None branch=None id='15fdd91c-e645-46fe-a613-76e1f28323c5' timestamp=1754979803.761915
    Closing MCP server connection...
    Cleanup complete.
    

多輪對話代碼

  • 以下代碼是main.py的多輪對話的版本:
    # 導入基礎庫
    import os
    import asyncio
    from dotenv import load_dotenv
    from google.genai import types
    from google.adk.agents import Agent
    from google.adk.models.lite_llm import LiteLlm
    from google.adk.agents.llm_agent import LlmAgent
    from google.adk.runners import Runner
    from google.adk.sessions import InMemorySessionService
    from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset, StdioServerParameters,StdioConnectionParamsDS_API_KEY = os.getenv("OPENAI_API_KEY")
    DS_BASE_URL = os.getenv("OPENAI_API_BASE")
    DS_MODEL = os.getenv("MODEL")model = LiteLlm(model=DS_MODEL,api_base=DS_BASE_URL,api_key=DS_API_KEY
    )# --- Step 1: 創建導入MCP工具函數 ---
    async def get_tools_async():"""Gets tools from the File System MCP Server."""print("Attempting to connect to MCP Filesystem server...")# 確保目錄存在test_dir = "D:\\Code\\adk_mcp\\test"if not os.path.exists(test_dir):os.makedirs(test_dir)print(f"Created directory: {test_dir}")try:# 創建MCP工具集實例mcp_toolset = MCPToolset(# 這里采用Studio通信方式進行調用connection_params=StdioConnectionParams(server_params=StdioServerParameters(command='npx',  # Command to run the serverargs=["-y",  # Arguments for the command"@modelcontextprotocol/server-filesystem",test_dir],)))# 等待服務器啟動await asyncio.sleep(2)# 獲取工具列表tools = await mcp_toolset.get_tools()print("MCP Toolset created successfully.")print(f"Fetched {len(tools)} tools from MCP server.")# 返回工具和工具集對象用于清理return tools, mcp_toolsetexcept Exception as e:print(f"Error creating MCP tools: {e}")raise# --- Step 2: 創建ADK Agent --
    async def get_agent_async():"""Creates an ADK Agent equipped with tools from the MCP Server."""try:tools, mcp_toolset = await get_tools_async()root_agent = Agent(model=model,name='filesystem_assistant',instruction='Help user interact with the local filesystem using available tools.',tools=tools,  # 將MCP工具加載到Agent中)return root_agent, mcp_toolsetexcept Exception as e:print(f"Error creating agent: {e}")raise# 多輪對話函數
    async def chat_loop(runner, user_id, session_id) -> None:print("\n🤖ADK + MCP對話已啟動!輸入'quit'退出。")while True:query = input("\n你: ").strip()if query.lower() == "quit":breaktry:print(f"\n>>> User Query: {query}")# Prepare the user's message in ADK formatcontent = types.Content(role='user', parts=[types.Part(text=query)])final_response_text = "Agent did not produce a final response."  # Default# Key Concept: run_async executes the agent logic and yields Events.# We iterate through events to find the final answer.async for event in runner.run_async(user_id=user_id,session_id=session_id, new_message=content):# You can uncomment the line below to see *all* events during execution# print(f"  [Event] Author: {event.author}, Type:{type(event).__name__}, Final: {event.is_final_response()}, Content:{event.content}")# Key Concept: is_final_response() marks the concluding message for the turn.if event.is_final_response():if event.content and event.content.parts:# Assuming text response in the first partfinal_response_text = event.content.parts[0].textelif event.actions and event.actions.escalate:  # Handle potential errors / escalationsfinal_response_text = f"Agent escalated:{event.error_message or 'No specific message.'}"# Add more checks here if needed (e.g., specific error codes)breakprint(f"<<< Agent Response: {final_response_text}")except Exception as e:print(f"\n?調用過程出錯: {e}")# --- Step 3: 執行主邏輯 ---
    async def async_main():mcp_toolset = Nonetry:# 創建會話管理器session_service = InMemorySessionService()session = await session_service.create_session(state={}, app_name='mcp_filesystem_app', user_id='user_fs')# 用戶輸入query = "請幫我查找目前文件夾里都有哪些文件?"print(f"User Query: '{query}'")content = types.Content(role='user', parts=[types.Part(text=query)])root_agent, mcp_toolset = await get_agent_async()runner = Runner(app_name='mcp_filesystem_app',agent=root_agent,session_service=session_service,)print("Running agent...")events_async = runner.run_async(session_id=session.id, user_id=session.user_id, new_message=content)async for event in events_async:print(f"Event received: {event}")# 啟動多輪對話await chat_loop(runner, session.user_id, session.id)except Exception as e:print(f"Error during main execution: {e}")raisefinally:# 運行完成后,關閉MCP服務器連接if mcp_toolset:print("Closing MCP server connection...")await mcp_toolset.close()print("Cleanup complete.")if __name__ == '__main__':try:asyncio.run(async_main())except Exception as e:print(f"An error occurred: {e}")
    

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95241.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95241.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95241.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

高光譜技術的獨特優勢

高光譜技術憑借其?納米級連續光譜采集能力?和?圖譜合一的探測模式?&#xff0c;在多個領域展現出不可替代的獨特優勢&#xff1a;一、光譜維度&#xff1a;精細物質指紋識別?納米級連續光譜解析? 通過 ?5-10nm帶寬的數百個連續波段?&#xff08;最高330個通道&#xff…

基于Vue+Element UI集成高德地圖的完整實踐指南

本次開發使用deepseek 簡直如虎添翼得心應手 生成模擬數據、解決報錯那真是嘎嘎地 在 Vue Element UI 項目中引入高德地圖 具體實現步驟&#xff1a; 高德開放平臺&#xff1a;注冊賬號 → 進入控制臺 → 創建應用 → 獲取 Web端(JS API)的Key https://lbs.amap.com/ 這里需要…

Day50--圖論--98. 所有可達路徑(卡碼網),797. 所有可能的路徑

Day50–圖論–98. 所有可達路徑&#xff08;卡碼網&#xff09;&#xff0c;797. 所有可能的路徑 刷今天的內容之前&#xff0c;要先去《代碼隨想錄》網站&#xff0c;先看完&#xff1a;圖論理論基礎和深度優先搜索理論基礎。做完之后可以看題解。有余力&#xff0c;把廣度優先…

Python 異常捕獲

一、獲取未知錯誤try:# 相關處理邏輯 異常后面輸出print(輸入信息……) except Exception as e:print(未知錯誤,e)二、獲取已知錯誤except 錯誤單詞&#xff08;來源于錯誤信息的第一個單詞&#xff09;多個已知錯誤使用 except XXXXX:try:# 相關處理邏輯 異常后面輸出print…

RIOT、RT-Thread 和 FreeRTOS 是三種主流的實時操作系統

RIOT、RT-Thread 和 FreeRTOS 是三種主流的實時操作系統&#xff08;RTOS&#xff09;&#xff0c;專為嵌入式系統和物聯網&#xff08;IoT&#xff09;設備設計。它們在架構、功能、生態和應用場景上有顯著差異&#xff0c;以下是詳細對比&#xff1a;1. 架構與設計理念特性RI…

【FAQ】Win11創建資源不足繞開微軟賬號登錄

Win11安裝資源限制 因為 Windows 11 有兩項強制檢測 VMware 8 默認沒提供&#xff1a; TPM 2.0&#xff08;可信平臺模塊&#xff09;Secure Boot&#xff08;安全啟動&#xff09; 一步到位解決辦法&#xff08;官方兼容方式&#xff09; 關閉虛擬機電源編輯虛擬機設置 選項 →…

Docker使用----(安裝_Windows版)

一、Docker Docker 鏡像就像是一個軟件包&#xff0c;里面包括了應用程序的代碼、運行所需的庫和工具、配置文件等等&#xff0c;所有這些都打包在一起&#xff0c;以確保應用程序在不同的計算機上運行時&#xff0c;都能保持一致性。 可以把 Docker 鏡像想象成一個軟件安裝文件…

91、23種經典設計模式

設計模式是軟件設計中反復出現的解決方案的模板&#xff0c;用于解決特定問題并提高代碼的可維護性、可擴展性和可復用性。23種經典設計模式可分為創建型、結構型和行為型三大類&#xff0c;以下是具體分類及模式概述&#xff1a; 一、創建型模式&#xff08;5種&#xff09; 關…

Illustrator總監級AI魔法:一鍵讓低清logo變矢量高清,徹底告別手動描摹!

在海外從事設計十幾年&#xff0c;我敢說&#xff0c;每個設計師都經歷過一種“史詩級”的折磨&#xff1a;客戶發來一個像素低得感人、邊緣模糊不清的JPG格式Logo&#xff0c;然后要求你把它用在巨幅海報或者高清視頻上。這意味著什么&#xff1f;意味著我們要打開Illustrator…

各種 dp 刷題下

6.#8518 杰瑞征途 / 洛谷 P4072 征途 題意 Pine 開始了從 SSS 地到 TTT 地的征途。從 SSS 地到 TTT 地的路可以劃分成 nnn 段&#xff0c;相鄰兩段路的分界點設有休息站。Pine 計劃用 mmm 天到達 TTT 地。除第 mmm 天外&#xff0c;每一天晚上 Pine 都必須在休息站過夜。所以…

本地WSL部署接入 whisper + ollama qwen3:14b 總結字幕增加利用 Whisper 分段信息,全新 Prompt功能

1. 實現功能 M4-3: 智能后處理 - 停頓感知增強版 (終極版) 本腳本是 M4-3 的重大升級&#xff0c;引入了“停頓感知”能力&#xff1a; 利用 Whisper 分段信息: 將 Whisper 的 segments 間的自然停頓作為強信號 ([P]) 提供給 LLM。全新 Prompt: 設計了專門的 Prompt&#xff0c…

微算法科技(NASDAQ:MLGO)開發經典增強量子優化算法(CBQOA):開創組合優化新時代

近年來&#xff0c;量子計算在組合優化領域的應用日益受到關注&#xff0c;各類量子優化算法層出不窮。然而&#xff0c;由于現階段量子硬件的局限性&#xff0c;如何充分利用已有的經典計算能力來增強量子優化算法的表現&#xff0c;成為當前研究的重要方向。基于此&#xff0…

功能、延遲、部署、成本全解析:本地化音視頻 SDK 對比 云端方案

引言 在構建實時音視頻系統時&#xff0c;技術選型往往決定了項目的天花板。開發者面臨的第一個關鍵抉擇&#xff0c;就是是選擇完全可控的本地化音視頻內核&#xff0c;還是依賴云廠商的實時音視頻服務。 以大牛直播SDK&#xff08;SmartMediaKit&#xff09;為代表的本地部…

微調入門:為什么微調

歡迎來到啾啾的博客&#x1f431;。 記錄學習點滴。分享工作思考和實用技巧&#xff0c;偶爾也分享一些雜談&#x1f4ac;。 有很多很多不足的地方&#xff0c;歡迎評論交流&#xff0c;感謝您的閱讀和評論&#x1f604;。 目錄1 什么時候我們需要微調呢&#xff1f;1.1 微調的…

3、匹配一組字符

在本章里&#xff0c;你將學習如何與字符集合打交道。與可以匹配任意單個字符的.字符&#xff08;參見第2章&#xff09;不同&#xff0c;字符集合能匹配特定的字符和字符區間。3.1 匹配多個字符中的某一個第2章介紹的.?字符&#xff0c;可以匹配任意單個字符。當時在最后一個…

強化學習在量化交易中的禁區:回測表現好實盤虧錢的4個原因

引言 “為什么你的強化學習策略在回測中年化 50%,到了實盤卻三個月虧光本金?” 如果你做過量化交易,尤其是嘗試用強化學習(Reinforcement Learning, RL),這種場景可能并不陌生: 回測曲線平滑向上,最大回撤可控,勝率穩定 模型參數和架構調到極致,每次迭代都帶來更高的…

代碼隨想錄day62圖論11

文章目錄Floyd 算法精講A * 算法精講 &#xff08;A star算法&#xff09;Floyd 算法精講 題目鏈接 文章講解 #include <iostream> #include <vector> #include <algorithm> using namespace std;int main() {int n, m;cin >> n >> m; // 輸入…

【18】OpenCV C++實戰篇——【項目實戰】OpenCV C++ 精準定位“十字刻度尺”中心坐標,過濾圖片中的干擾,精準獲取十字交點坐標

文章目錄1 問題及分析2 多尺度霍夫直線 與 漸進概率霍夫線段 細節對比2.1 多尺度霍夫直線 HoughLines2.2 漸進概率霍夫線段 HoughLinesP2.3 HoughLines 和 HoughLinesP 所求結果細節對比2.4 為什么 HoughLinesP 直線兩端沒有呈放射狀態呢&#xff1f;直線總是平行嗎&#xff1f…

云原生應用的DevOps2(Jenkins滲透場景)

結論 Jenkins歷史漏洞 Jenkins未授權訪問 登錄后命令執行 Jenkins代碼倉庫信息 Jenkins服務器建立多臺服務器信任連接 背景 目前我看到紅隊人員的現狀,不管是什么系統就是拿Shell,拿權限,然后把這臺機器當作跳板繼續橫向其它機器。而Jenkins在內網中是經常能夠遇到的,…

Gradle 配置教程:與 Maven 對比詳解(含完整遷移指南)

一、基礎對比&#xff1a;Gradle vs Maven1.1 核心特性對比維度MavenGradle配置語言XML (冗長)Groovy/Kotlin DSL (簡潔靈活)構建速度較慢(全量構建)快2-10倍(增量構建緩存)多模塊管理<modules> <parent>settings.gradle project()依賴管理<dependencies>d…