建生產(chǎn)級MCP服務(wù):從協(xié)議解析到Spring Boot架構(gòu)實踐)
1. 從單體到智能體一個Java工程師的架構(gòu)視角轉(zhuǎn)變最近和團隊里的幾個后端兄弟聊天話題總繞不開AI智能體。大家一邊感慨著GPT-4o、Claude 3的驚人表現(xiàn)一邊又有點迷茫我們這些寫了多年Spring Boot、CRUD、微服務(wù)的Javaer在這個所謂的“AI智能體時代”到底該干點啥難道就是調(diào)調(diào)API把用戶問題扔給大模型再把結(jié)果包裝一下返回去這聽起來和當(dāng)年寫Servlet、Struts似乎沒啥本質(zhì)區(qū)別無非是換了個更復(fù)雜的“遠程服務(wù)”。但當(dāng)我開始深入接觸MCPModel Context Protocol協(xié)議并嘗試為團隊設(shè)計一個生產(chǎn)級的MCP服務(wù)部署架構(gòu)時我才意識到之前的想法太膚淺了。這絕不僅僅是“調(diào)用另一個服務(wù)”那么簡單。傳統(tǒng)的Java后端架構(gòu)無論是單體還是微服務(wù)核心是處理確定性的請求一個HTTP請求進來經(jīng)過一系列清晰的業(yè)務(wù)邏輯和數(shù)據(jù)處理返回一個確定性的響應(yīng)。整個流程是可控、可預(yù)測、可調(diào)試的。而AI智能體尤其是基于MCP協(xié)議構(gòu)建的智能體引入了一個全新的維度非確定性和長時程交互。智能體的一次“思考”或“行動”可能涉及多次、異步地與多個工具Tools或數(shù)據(jù)源Resources進行交互這個過程是動態(tài)的、狀態(tài)化的并且嚴重依賴上下文Context。這就對我們熟悉的Spring Boot應(yīng)用部署架構(gòu)提出了全新的挑戰(zhàn)。我們不能再簡單地把MCP Server當(dāng)作一個普通的REST API服務(wù)來部署它需要處理SSEServer-Sent Events長連接、管理復(fù)雜的會話狀態(tài)、高效調(diào)度異構(gòu)工具并保證在高并發(fā)下的穩(wěn)定性和可觀測性。所以這篇隨想錄我想從一個一線Java架構(gòu)師的角度聊聊如何為我們熟悉的Java技術(shù)棧Spring Boot為核心設(shè)計一個能扛住生產(chǎn)環(huán)境考驗的MCP服務(wù)部署架構(gòu)。這不是一篇簡單的“Hello World”教程而是關(guān)于如何將我們已有的分布式系統(tǒng)經(jīng)驗適配到這個充滿不確定性的新范式中的思考與實踐。2. 理解MCP協(xié)議它為何是智能體時代的“USB-C”接口在動手畫架構(gòu)圖之前我們必須先搞清楚MCP到底是什么以及它解決了什么問題。你可以把它理解為AI智能體時代的“USB-C”協(xié)議。在USB-C出現(xiàn)之前手機、電腦、平板各有各的接口充電線、數(shù)據(jù)線互不兼容混亂不堪。MCP協(xié)議的目標(biāo)就是為AI智能體Client和各種能力提供方Server提供Tools和Resources定義一個統(tǒng)一、標(biāo)準(zhǔn)的通信接口。MCP的核心是標(biāo)準(zhǔn)化工具調(diào)用與資源訪問。在沒有MCP之前每個AI應(yīng)用如Cursor、Claude Desktop如果想接入某個工具比如查詢數(shù)據(jù)庫、操作Git都需要針對該工具開發(fā)特定的、緊耦合的集成代碼。這就像每個電器廠都要為自己的設(shè)備生產(chǎn)專屬插頭。而MCP定義了一套標(biāo)準(zhǔn)的“插座”規(guī)范協(xié)議以及電器如何聲明自己需要什么“電壓和電流”Tools/Resources的定義。作為工具提供方Server你只需要按照MCP協(xié)議實現(xiàn)一個服務(wù)任何支持MCP的AI智能體Client就都能即插即用地使用你的工具。從技術(shù)上看MCP協(xié)議基于JSON-RPC 2.0并默認使用SSEServer-Sent Events作為傳輸層。選擇SSE而非WebSocket是一個值得玩味的設(shè)計。SSE是一種基于HTTP的長連接服務(wù)器可以主動向客戶端推送數(shù)據(jù)但客戶端到服務(wù)器的通信仍然依靠普通的HTTP請求。這種“單向為主雙向為輔”的模型非常契合AI智能體的交互模式智能體Client發(fā)起一個初始化請求然后MCP Server會持續(xù)地將自己提供的工具列表、資源列表等信息“推送”給Client。當(dāng)Client需要調(diào)用某個工具時再發(fā)起一個獨立的JSON-RPC調(diào)用。注意雖然SSE是默認和推薦方式但MCP協(xié)議也允許使用WebSocket或Stdio標(biāo)準(zhǔn)輸入輸出作為傳輸層這為不同部署場景如本地CLI工具集成提供了靈活性。對于我們Java開發(fā)者而言理解這一點至關(guān)重要。它意味著我們的MCP Server需要是一個能夠高效管理大量HTTP長連接的服務(wù)。這和我們平時寫的“請求-響應(yīng)-關(guān)閉連接”的REST API有本質(zhì)不同。連接的生命周期可能很長并且服務(wù)器需要具備主動推送的能力。這直接影響了我們后續(xù)在服務(wù)器選型、連接池配置、線程模型等方面的決策。3. 生產(chǎn)級MCP Server部署架構(gòu)藍圖基于對MCP協(xié)議的理解并結(jié)合Java生態(tài)尤其是Spring Boot的成熟實踐我設(shè)計了一個分層、解耦、可擴展的生產(chǎn)級部署架構(gòu)。這個架構(gòu)的核心思想是將協(xié)議處理、業(yè)務(wù)邏輯、工具執(zhí)行進行分離確保每一層都可以獨立演進、擴展和監(jiān)控。整個架構(gòu)可以劃分為五個核心層次從下至上分別是基礎(chǔ)設(shè)施層這是架構(gòu)的基石負責(zé)提供計算、網(wǎng)絡(luò)和存儲資源。對于MCP服務(wù)我強烈推薦使用容器化部署Docker Kubernetes。原因有三首先MCP Server可能需要調(diào)用各種命令行工具或依賴特定系統(tǒng)環(huán)境容器鏡像能完美封裝這些依賴保證環(huán)境一致性。其次K8s提供了強大的彈性伸縮能力可以輕松應(yīng)對智能體連接數(shù)的波動。最后K8s的Service和Ingress機制能很好地管理服務(wù)發(fā)現(xiàn)和負載均衡特別是對需要保持長連接的SSE服務(wù)。協(xié)議適配與連接管理層這一層是MCP服務(wù)的“門面”專門處理與MCP Client如Cursor、Claude Desktop的通信。它需要實現(xiàn)MCP協(xié)議規(guī)范處理SSE連接的生命周期。在實踐中我建議單獨部署一個輕量級的“MCP網(wǎng)關(guān)”或“協(xié)議代理”服務(wù)。這個服務(wù)只做兩件事1. 維護與客戶端的SSE長連接2. 將客戶端發(fā)來的JSON-RPC請求轉(zhuǎn)發(fā)給后端的業(yè)務(wù)邏輯服務(wù)。這樣做的好處是實現(xiàn)了關(guān)-注點分離。網(wǎng)關(guān)服務(wù)可以用高性能的Netty或Vert.x框架編寫專注于高并發(fā)連接管理而后端的業(yè)務(wù)服務(wù)則可以繼續(xù)使用我們熟悉的Spring Boot專注于工具的實現(xiàn)。核心業(yè)務(wù)邏輯層這是MCP Server的“大腦”以Spring Boot應(yīng)用的形式存在。它接收來自協(xié)議層的標(biāo)準(zhǔn)化工具調(diào)用請求執(zhí)行具體的業(yè)務(wù)邏輯。例如一個“搜索網(wǎng)絡(luò)”的Tool在這里會調(diào)用Google Search API一個“查詢數(shù)據(jù)庫”的Tool會通過MyBatis或JPA執(zhí)行SQL。這一層的設(shè)計要遵循我們熟悉的微服務(wù)最佳實踐清晰的包結(jié)構(gòu)、依賴注入、事務(wù)管理、外部服務(wù)客戶端等。每個Tool的實現(xiàn)都應(yīng)該是一個獨立的、可測試的Spring Bean。工具與資源執(zhí)行層這一層是實際“干活”的地方可能涉及對操作系統(tǒng)、外部API、數(shù)據(jù)庫、消息隊列等的調(diào)用。這里有一個關(guān)鍵的設(shè)計考量安全性和資源隔離。MCP Tool本質(zhì)上允許AI智能體以代碼的名義執(zhí)行某些操作這非常危險。因此我們必須實施嚴格的沙箱機制。對于執(zhí)行命令行工具的Tool必須使用Docker容器或Linux命名空間進行隔離限制其CPU、內(nèi)存、網(wǎng)絡(luò)和文件系統(tǒng)訪問權(quán)限。對于數(shù)據(jù)庫查詢必須使用具有最小必要權(quán)限的數(shù)據(jù)庫用戶。這一層通常以“Worker”進程或容器的形式存在由業(yè)務(wù)邏輯層通過消息隊列如RabbitMQ、Kafka或gRPC進行異步調(diào)度??捎^測性與治理層這是保障服務(wù)穩(wěn)定性的“眼睛”和“大腦”。由于MCP交互的非確定性傳統(tǒng)的基于請求響應(yīng)的監(jiān)控可能不夠用。我們需要建立立體化的監(jiān)控體系連接級監(jiān)控實時監(jiān)控SSE連接數(shù)、連接持續(xù)時間、異常斷開率。工具調(diào)用鏈追蹤為每一次Tool調(diào)用生成唯一的Trace ID貫穿協(xié)議層、業(yè)務(wù)層、執(zhí)行層記錄耗時、參數(shù)、結(jié)果和異常。這能幫助我們在智能體執(zhí)行復(fù)雜任務(wù)失敗時快速定位是哪個環(huán)節(jié)出了問題。工具使用分析與審計記錄每個工具被誰哪個會話、在什么上下文、以什么參數(shù)調(diào)用以及產(chǎn)生了什么結(jié)果。這對于理解智能體行為、優(yōu)化工具設(shè)計、以及滿足安全合規(guī)要求都至關(guān)重要。4. 基于Spring Boot實現(xiàn)MCP Server的核心要點有了宏觀架構(gòu)我們來看看如何用Spring Boot具體實現(xiàn)一個MCP Server。雖然目前社區(qū)有spring-ai等項目在探索AI集成但對于MCP協(xié)議我們可能需要從更底層開始構(gòu)建或者尋找適配的庫。首先處理SSE連接。Spring Framework 5對響應(yīng)式編程和SSE提供了很好的支持。我們可以使用SseEmitter來輕松創(chuàng)建SSE端點。但生產(chǎn)環(huán)境中直接使用SseEmitter可能會遇到連接管理復(fù)雜、超時處理繁瑣等問題。一個更穩(wěn)健的做法是使用Project Reactor的Flux和ServerSentEvent對象結(jié)合WebFlux構(gòu)建一個非阻塞的、高并發(fā)的SSE端點。下面是一個高度簡化的示例展示如何建立一個返回工具列表的SSE流RestController RequestMapping(/mcp) public class McpSseController { GetMapping(value /sse, produces MediaType.TEXT_EVENT_STREAM_VALUE) public FluxServerSentEventObject handleSseSession(ServerWebExchange exchange) { String sessionId generateSessionId(); // 1. 發(fā)送初始化消息如協(xié)議版本、服務(wù)器信息 return Flux.concat( Flux.just(ServerSentEvent.builder() .event(initialized) .data(Map.of(protocolVersion, 2024-11-05, serverInfo, MyMcpServer/1.0)) .build()), // 2. 持續(xù)或按需發(fā)送工具/資源列表 toolListUpdateFlux(sessionId), // 3. 監(jiān)聽來自客戶端的請求這里需要額外的HTTP端點處理JSON-RPC // ... 實際中請求處理是另一個端點 ).doOnSubscribe(sub - log.info(MCP Client connected, session: {}, sessionId)) .doOnTerminate(() - cleanUpSession(sessionId)); } private FluxServerSentEventObject toolListUpdateFlux(String sessionId) { // 模擬工具列表有更新時推送 return Flux.interval(Duration.ofMinutes(5)) .map(tick - fetchLatestTools()) .map(tools - ServerSentEvent.builder().event(tools/list).data(tools).build()); } }其次實現(xiàn)JSON-RPC請求端點。MCP Client通過單獨的HTTP POST請求來調(diào)用工具。我們需要一個端點來解析JSON-RPC 2.0格式的請求路由到對應(yīng)的Tool處理器并返回結(jié)果。PostMapping(/jsonrpc) public ResponseEntityJsonRpcResponse handleJsonRpc(RequestBody JsonRpcRequest request) { try { // 1. 驗證請求格式和會話 validateRequest(request); // 2. 根據(jù) method 字段路由到具體的 Tool 執(zhí)行器 // 例如method 可能是 “tools/call” 或 “resources/read” Object result dispatchToToolExecutor(request); // 3. 構(gòu)建成功的 JSON-RPC 響應(yīng) JsonRpcResponse response new JsonRpcResponse(); response.setId(request.getId()); response.setResult(result); return ResponseEntity.ok(response); } catch (InvalidParamsException e) { // 返回 JSON-RPC 標(biāo)準(zhǔn)錯誤碼 -32602 return ResponseEntity.ok(buildErrorResponse(request.getId(), -32602, Invalid params, e)); } catch (McpToolException e) { // 返回自定義錯誤碼和消息 return ResponseEntity.ok(buildErrorResponse(request.getId(), e.getCode(), e.getMessage(), null)); } catch (Exception e) { // 內(nèi)部服務(wù)器錯誤 -32603 return ResponseEntity.ok(buildErrorResponse(request.getId(), -32603, Internal error, e)); } }第三設(shè)計和注冊Tool。這是業(yè)務(wù)核心。每個Tool應(yīng)該是一個獨立的Spring Bean實現(xiàn)一個統(tǒng)一的接口例如McpTool。我們可以利用Spring的ApplicationListener或PostConstruct在應(yīng)用啟動時自動掃描并注冊所有Tool到某個全局注冊表中。public interface McpTool { String getName(); String getDescription(); JsonSchema getInputSchema(); // 描述輸入?yún)?shù)結(jié)構(gòu)的JSON Schema Object execute(MapString, Object inputs, McpSession session) throws McpToolException; } Service public class WebSearchTool implements McpTool { Override public String getName() { return web_search; } Override public String getDescription() { return Search the web for current information.; } Override public JsonSchema getInputSchema() { // 返回一個定義 query字符串和 max_results數(shù)字的JSON Schema return ...; } Override public Object execute(MapString, Object inputs, McpSession session) { String query (String) inputs.get(query); Integer maxResults (Integer) inputs.getOrDefault(max_results, 10); // 調(diào)用實際的搜索API例如通過一個RestTemplate或WebClient ListSearchResult results searchApiClient.search(query, maxResults); return Map.of(results, results); } Autowired private SearchApiClient searchApiClient; }提示getInputSchema()方法返回的JSON Schema至關(guān)重要。它是AI智能體理解如何調(diào)用該工具的“說明書”。一個清晰、準(zhǔn)確的Schema能極大提升工具被正確使用的概率。務(wù)必詳細定義每個參數(shù)的類型、是否必需、描述和可能的枚舉值。5. 部署、運維與踩坑實錄架構(gòu)設(shè)計得再好最終都要落到部署和運維上。在這一部分我結(jié)合實際的踩坑經(jīng)驗分享幾個關(guān)鍵點。容器化與鏡像構(gòu)建你的Dockerfile需要仔細規(guī)劃。除了打包Spring Boot的JAR文件如果MCP Server需要調(diào)用git、curl、pandoc等命令行工具必須在鏡像中安裝它們。建議使用多階段構(gòu)建以減小最終鏡像體積。# 第一階段構(gòu)建工具層 FROM ubuntu:22.04 AS tools RUN apt-get update apt-get install -y git curl python3-pip ... rm -rf /var/lib/apt/lists/* # 第二階段構(gòu)建應(yīng)用 FROM eclipse-temurin:17-jre-jammy COPY --fromtools /usr/bin/git /usr/bin/curl ... /usr/bin/ COPY target/my-mcp-server.jar app.jar ENTRYPOINT [java, -jar, /app.jar]Kubernetes部署配置在K8s中部署時需要特別注意SSE長連接的特性。以下幾點配置很關(guān)鍵Readiness Probe就緒探針避免使用傳統(tǒng)的HTTP GET探針因為它會建立新連接干擾現(xiàn)有的SSE連接??梢钥紤]使用TCP Socket探針或者一個專門的不影響核心連接的輕量級健康檢查端點。資源限制Resources Limits務(wù)必設(shè)置CPU和內(nèi)存限制。MCP服務(wù)可能因為AI智能體的復(fù)雜請求而消耗大量CPU進行JSON解析和業(yè)務(wù)處理。Pod Disruption Budget (PDB)設(shè)置PDB確保在集群維護時不會一次性終止太多Pod導(dǎo)致大量客戶端連接中斷。Service與Ingress確保你的Ingress控制器如Nginx Ingress支持長連接和WebSocket/SSE。通常需要調(diào)整proxy-read-timeout,proxy-send-timeout等參數(shù)將其設(shè)置為一個較大的值例如1小時。連接管理與狀態(tài)保持這是最大的挑戰(zhàn)之一。HTTP本質(zhì)是無狀態(tài)的但AI智能體與MCP Server的交互往往是多輪次的、有狀態(tài)的。我們需要在服務(wù)器端維護會話Session狀態(tài)。一個簡單的做法是在SSE連接建立時生成一個唯一的sessionId并將其與連接對象綁定。后續(xù)該客戶端的所有JSON-RPC請求都必須攜帶這個sessionId例如放在HTTP Header中以便服務(wù)器能將請求路由到正確的會話上下文。會話中需要存儲哪些數(shù)據(jù)至少包括已初始化的工具列表、本次對話的歷史消息作為上下文、用戶自定義的臨時數(shù)據(jù)等。必須為會話設(shè)置合理的超時和清理機制防止內(nèi)存泄漏。工具調(diào)用的超時與熔斷智能體調(diào)用的工具可能是訪問一個緩慢的外部API或者執(zhí)行一個耗時的計算。必須為每個工具調(diào)用設(shè)置嚴格的超時時間例如30秒并使用Resilience4j或Hystrix實現(xiàn)熔斷機制。如果一個工具頻繁超時或失敗應(yīng)暫時將其熔斷避免拖垮整個服務(wù)。同時要給客戶端返回清晰的錯誤信息幫助智能體調(diào)整策略。安全與權(quán)限控制這是重中之重。絕對不能允許未經(jīng)鑒權(quán)的客戶端連接你的MCP Server。至少需要在SSE連接建立和JSON-RPC請求處添加認證層。可以采用API Key、JWT Token等方式。更細粒度的可以為每個工具設(shè)置訪問權(quán)限控制列表ACL例如“只有內(nèi)部員工可以訪問數(shù)據(jù)庫查詢工具”。所有工具的輸入輸出都應(yīng)進行嚴格的校驗和過濾防止注入攻擊。對于執(zhí)行命令行的工具如前所述必須運行在隔離的沙箱環(huán)境中。6. 性能調(diào)優(yōu)與可觀測性建設(shè)當(dāng)服務(wù)上線后性能監(jiān)控和調(diào)優(yōu)就成為了日常。對于MCP服務(wù)我們需要關(guān)注一些特殊的指標(biāo)。關(guān)鍵性能指標(biāo)KPIsSSE連接數(shù)當(dāng)前活躍的長連接數(shù)量。這是衡量服務(wù)負載的直接指標(biāo)。連接建立成功率/失敗率反映網(wǎng)絡(luò)或認證層是否存在問題。工具調(diào)用QPS與平均耗時按工具類型細分。這能幫你發(fā)現(xiàn)性能瓶頸。工具調(diào)用錯誤率同樣需要按錯誤類型超時、參數(shù)錯誤、外部服務(wù)失敗等細分。會話平均存活時間了解智能體使用服務(wù)的典型模式。系統(tǒng)資源容器的CPU、內(nèi)存使用率特別是JVM的堆內(nèi)存和GC情況。可觀測性三板斧日志、指標(biāo)、鏈路追蹤。結(jié)構(gòu)化日志使用Logback或Log4j2輸出JSON格式的結(jié)構(gòu)化日志。在每個日志事件中務(wù)必包含sessionId、toolName、requestId或TraceId。這樣你才能在海量日志中串聯(lián)起一次完整的智能體交互過程。指標(biāo)收集使用Micrometer將上述KPIs暴露給Prometheus。Spring Boot Actuator可以很方便地集成Micrometer。分布式鏈路追蹤這是理解復(fù)雜交互的“神器”。為每一個進入系統(tǒng)的JSON-RPC請求生成一個唯一的Trace ID并在這個請求觸發(fā)的所有內(nèi)部操作如數(shù)據(jù)庫查詢、外部API調(diào)用、工具執(zhí)行中傳遞這個ID。使用Jaeger或Zipkin進行收集和可視化。當(dāng)用戶報告“AI助手執(zhí)行某個任務(wù)失敗了”你可以通過Trace ID快速還原出完整的調(diào)用鏈精準(zhǔn)定位是網(wǎng)絡(luò)問題、工具bug還是外部服務(wù)異常。JVM與Spring Boot調(diào)優(yōu)由于需要維持大量長連接傳統(tǒng)的“一個請求一個線程”的Tomcat模型可能不是最優(yōu)選??紤]使用Spring WebFlux基于Netty來構(gòu)建非阻塞的響應(yīng)式服務(wù)它能用更少的線程處理更多的并發(fā)連接。相應(yīng)地你需要檢查項目中的所有庫是否支持響應(yīng)式編程特別是數(shù)據(jù)庫驅(qū)動如R2DBC for MySQL/PostgreSQL和HTTP客戶端使用WebClient而非RestTemplate。同時調(diào)整JVM參數(shù)為更長的會話生命周期和可能更大的內(nèi)存占用用于保存會話狀態(tài)做好準(zhǔn)備。7. 從“部署好”到“用得好”架構(gòu)的演進思考部署一個能跑的MCP Server只是第一步。如何讓它更好地融入現(xiàn)有的技術(shù)體系并支撐起真正的智能體應(yīng)用是更長期的課題。與現(xiàn)有微服務(wù)體系的融合你的MCP Server很可能需要調(diào)用公司內(nèi)部已有的微服務(wù)。這時不要直接在MCP Server的業(yè)務(wù)邏輯層寫死HTTP調(diào)用代碼。應(yīng)該通過內(nèi)部服務(wù)發(fā)現(xiàn)如Nacos、Consul和API網(wǎng)關(guān)來調(diào)用復(fù)用現(xiàn)有的服務(wù)治理、熔斷降級、流量染色等能力。將MCP Server視為一個特殊的“客戶端”或“適配層”而非一個孤島。工具的動態(tài)注冊與熱更新在初期工具列表可能在應(yīng)用啟動時靜態(tài)加載。但隨著發(fā)展你可能希望能在不重啟服務(wù)的情況下動態(tài)地添加、移除或更新工具。這可以通過引入一個“工具注冊中心”來實現(xiàn)。MCP Server定期從注冊中心拉取最新的工具配置并更新到內(nèi)存中同時通過SSE向已連接的客戶端推送tools/list更新事件。這大大提升了運維的靈活性。多租戶與資源隔離如果你的MCP服務(wù)需要面向多個團隊或外部客戶提供就需要考慮多租戶架構(gòu)。每個租戶可能有自己獨立的工具集、權(quán)限配置和資源限制如API調(diào)用配額。這需要在會話管理、工具路由、計量計費等多個層面進行設(shè)計。面向失敗的設(shè)計與容錯AI智能體的行為難以預(yù)測可能會發(fā)起不合理或極其耗資源的請求。除了前文提到的工具級超時和熔斷還需要在全局層面設(shè)置防護。例如限制單個會話在單位時間內(nèi)的總工具調(diào)用次數(shù)、總耗時或總數(shù)據(jù)返回量。實現(xiàn)一個全局的“看門狗”機制監(jiān)控異常行為并自動終止有害會話。最后我想說的是作為Java工程師我們過去積累的關(guān)于高并發(fā)、分布式、可觀測性的所有經(jīng)驗在這個新領(lǐng)域依然極其寶貴。MCP服務(wù)部署架構(gòu)的設(shè)計本質(zhì)上是一次將確定性世界的工程智慧應(yīng)用于非確定性智能體交互的挑戰(zhàn)。它要求我們更關(guān)注狀態(tài)、更關(guān)注上下文、更關(guān)注安全邊界。這個過程充滿未知但也正是技術(shù)演進的樂趣所在。