焦慮?一文講透Java在AI應(yīng)用開(kāi)發(fā)中的真實(shí)位置)
1. 一個(gè)框架停更引發(fā)的技術(shù)選型焦慮前幾天在技術(shù)群里看到有人轉(zhuǎn)了一篇文章標(biāo)題大意是某個(gè)Java生態(tài)的AI框架停更了然后配了一句“Java是不是沒(méi)戲了”。群里瞬間炸鍋有人說(shuō)趕緊轉(zhuǎn)Python有人說(shuō)Java做AI本來(lái)就是偽命題也有人默默發(fā)了個(gè)“”。我盯著屏幕看了半天心里其實(shí)挺不是滋味的。這種焦慮我太熟悉了——每隔一段時(shí)間就會(huì)來(lái)一波。前幾年是“Java已死”后來(lái)是“Go要取代Java”現(xiàn)在輪到“Java在AI時(shí)代沒(méi)希望了”。每次都是同一個(gè)配方同一個(gè)味道。但這次有點(diǎn)不一樣。因?yàn)锳I應(yīng)用開(kāi)發(fā)確實(shí)在改變很多技術(shù)棧的格局Python在這個(gè)領(lǐng)域的先發(fā)優(yōu)勢(shì)是實(shí)打?qū)嵉?。所以?dāng)有人看到某個(gè)Java AI框架停更的消息時(shí)產(chǎn)生動(dòng)搖是完全可以理解的。不過(guò)一個(gè)框架停更和“Java在AI領(lǐng)域有沒(méi)有希望”之間其實(shí)隔著十萬(wàn)八千里。這篇文章我想把這件事掰開(kāi)揉碎了聊清楚一個(gè)AI框架停更到底意味著什么Java在AI應(yīng)用開(kāi)發(fā)中真正的位置在哪里以及如果你是一個(gè)Java開(kāi)發(fā)者現(xiàn)在應(yīng)該怎么規(guī)劃自己的技術(shù)路線(xiàn)。先給結(jié)論一個(gè)框架停更不等于Java在AI領(lǐng)域沒(méi)有希望。但如果你把寶全押在某個(gè)單一框架上那確實(shí)該焦慮了。這篇文章適合所有在Java生態(tài)里做開(kāi)發(fā)、同時(shí)對(duì)AI應(yīng)用感興趣的朋友不管你是剛?cè)腴T(mén)還是已經(jīng)做過(guò)幾個(gè)項(xiàng)目都能從中找到對(duì)自己有用的判斷依據(jù)。2. 框架停更這件事到底該怎么理解2.1 停更不等于技術(shù)路線(xiàn)失敗很多人看到“停更”兩個(gè)字第一反應(yīng)就是“這條路走不通了”。但實(shí)際情況往往復(fù)雜得多。一個(gè)開(kāi)源項(xiàng)目停更可能的原因有很多種維護(hù)者精力不夠了、公司戰(zhàn)略調(diào)整了、社區(qū)貢獻(xiàn)者流失了、或者更常見(jiàn)的情況——這個(gè)項(xiàng)目要合并到另一個(gè)更大的項(xiàng)目里去了。在開(kāi)源世界里項(xiàng)目合并、重構(gòu)、改名、拆分都是家常便飯。我經(jīng)歷過(guò)好幾次類(lèi)似的事情。早些年有個(gè)做HTTP客戶(hù)端的庫(kù)社區(qū)用得好好的突然宣布停止維護(hù)當(dāng)時(shí)也是一片哀嚎。結(jié)果沒(méi)過(guò)多久作者把它捐給了一個(gè)基金會(huì)代碼重構(gòu)后以新的名字重新發(fā)布功能更強(qiáng)了。那些當(dāng)時(shí)急著換方案的人反而多花了一遍遷移成本。所以看到停更消息第一件事不是慌而是去看清楚是徹底不維護(hù)了還是換了個(gè)方式繼續(xù)是核心功能被上游吸收了還是整個(gè)方向被放棄了2.2 判斷一個(gè)框架是否值得繼續(xù)關(guān)注的方法我自己總結(jié)了一套簡(jiǎn)單的判斷流程遇到類(lèi)似情況時(shí)會(huì)走一遍判斷維度具體看什么危險(xiǎn)信號(hào)安全信號(hào)代碼倉(cāng)庫(kù)狀態(tài)最近提交時(shí)間、Issue響應(yīng)速度超過(guò)6個(gè)月無(wú)提交、Issue無(wú)人回復(fù)仍有維護(hù)者活躍、PR被合并官方公告停更聲明中的具體措辭“不再維護(hù)”“建議遷移”“合并至XX項(xiàng)目”“由XX團(tuán)隊(duì)接管”下游依賴(lài)是否有大項(xiàng)目仍在使用主要用戶(hù)已遷移核心依賴(lài)方仍在投入社區(qū)活躍度討論群、論壇、問(wèn)答平臺(tái)無(wú)人討論、問(wèn)題無(wú)人解答仍有新項(xiàng)目采用、教程持續(xù)產(chǎn)出替代方案是否有平滑遷移路徑無(wú)替代或遷移成本極高有官方遷移指南或兼容層這套方法不復(fù)雜但能幫你快速區(qū)分“真停更”和“假警報(bào)”。大部分情況下你會(huì)發(fā)現(xiàn)事情沒(méi)有標(biāo)題黨說(shuō)的那么嚴(yán)重。2.3 從單一框架依賴(lài)到能力矩陣思維比“這個(gè)框架還能不能用”更重要的問(wèn)題是你為什么要把自己的技術(shù)能力綁定在一個(gè)框架上。我見(jiàn)過(guò)太多開(kāi)發(fā)者簡(jiǎn)歷上寫(xiě)著“精通XX框架”結(jié)果框架一停更整個(gè)人就慌了。這其實(shí)暴露了一個(gè)更深層的問(wèn)題你把框架當(dāng)成了能力本身。框架是工具能力是你對(duì)某個(gè)領(lǐng)域的理解深度。一個(gè)AI應(yīng)用框架停更了你換一個(gè)就是了。但如果你對(duì)AI應(yīng)用的核心概念——比如提示詞工程、上下文管理、向量檢索、工具調(diào)用編排——沒(méi)有自己的理解那換哪個(gè)框架你都是從零開(kāi)始。所以我的建議是把框架當(dāng)成學(xué)習(xí)工具而不是能力本身。用某個(gè)框架入門(mén)但要有意識(shí)地抽離出通用的概念和方法論。這樣不管框架怎么變你的核心能力都在。3. Java在AI應(yīng)用開(kāi)發(fā)中的真實(shí)位置3.1 Java的優(yōu)勢(shì)領(lǐng)域和AI的結(jié)合點(diǎn)聊完框架停更的事我們回到更根本的問(wèn)題Java在AI應(yīng)用開(kāi)發(fā)中到底有沒(méi)有位置我的判斷是有而且位置不小但和Python的位置不一樣。Python在AI領(lǐng)域的優(yōu)勢(shì)主要集中在模型訓(xùn)練、實(shí)驗(yàn)性研究、算法原型開(kāi)發(fā)這些環(huán)節(jié)。這些場(chǎng)景需要大量的數(shù)學(xué)計(jì)算、靈活的數(shù)據(jù)結(jié)構(gòu)、快速的迭代試錯(cuò)Python的生態(tài)和語(yǔ)法特點(diǎn)確實(shí)更合適。但AI應(yīng)用的落地不只是訓(xùn)練模型。一個(gè)完整的AI應(yīng)用系統(tǒng)除了模型本身還需要高并發(fā)的請(qǐng)求處理當(dāng)你的AI服務(wù)要同時(shí)面對(duì)成千上萬(wàn)的用戶(hù)請(qǐng)求時(shí)Java的線(xiàn)程模型和成熟的Web框架優(yōu)勢(shì)就體現(xiàn)出來(lái)了復(fù)雜的企業(yè)級(jí)集成大部分企業(yè)的核心業(yè)務(wù)系統(tǒng)是Java寫(xiě)的AI能力要嵌入這些系統(tǒng)Java是最自然的粘合劑穩(wěn)定的服務(wù)治理服務(wù)發(fā)現(xiàn)、負(fù)載均衡、熔斷降級(jí)、鏈路追蹤這些Java生態(tài)有非常成熟的方案事務(wù)與數(shù)據(jù)一致性AI應(yīng)用也要處理訂單、用戶(hù)、權(quán)限這些數(shù)據(jù)Java在這方面的積累不是Python能比的說(shuō)白了Python適合做AI的“大腦”Java適合做AI的“骨架和四肢”。一個(gè)AI應(yīng)用要真正跑在生產(chǎn)環(huán)境里兩者往往都需要。3.2 實(shí)際項(xiàng)目中Java承擔(dān)AI角色的幾種模式我在實(shí)際項(xiàng)目中見(jiàn)過(guò)幾種Java參與AI應(yīng)用的方式從輕到重排列第一種Java作為API網(wǎng)關(guān)和編排層。AI模型部署在獨(dú)立的服務(wù)里Java應(yīng)用負(fù)責(zé)接收用戶(hù)請(qǐng)求、做參數(shù)校驗(yàn)、調(diào)用模型服務(wù)、處理返回結(jié)果、記錄日志和計(jì)費(fèi)。這種模式下Java不直接做AI推理但承擔(dān)了所有工程化的臟活累活。第二種Java調(diào)用本地模型推理庫(kù)。通過(guò)JNI或者專(zhuān)門(mén)的Java綁定庫(kù)在Java進(jìn)程內(nèi)直接調(diào)用模型推理。這種模式適合對(duì)延遲敏感、不想額外部署服務(wù)的場(chǎng)景。不過(guò)要注意內(nèi)存管理和線(xiàn)程安全問(wèn)題模型推理通常不是線(xiàn)程安全的。第三種Java做向量檢索和RAG管道。向量數(shù)據(jù)庫(kù)的Java客戶(hù)端、文檔解析、分塊、嵌入向量生成、檢索排序這一整套流程完全可以用Java實(shí)現(xiàn)。很多向量數(shù)據(jù)庫(kù)都提供了完善的Java SDK。第四種Java做AI工作流編排。把多個(gè)AI能力文本生成、圖像識(shí)別、語(yǔ)音轉(zhuǎn)文字串聯(lián)成一個(gè)業(yè)務(wù)流程Java的工作流引擎和狀態(tài)機(jī)框架在這方面很有優(yōu)勢(shì)。這四種模式我都在不同項(xiàng)目里用過(guò)沒(méi)有哪種是“唯一正確”的關(guān)鍵看你的團(tuán)隊(duì)技術(shù)棧和業(yè)務(wù)需求。3.3 和Python生態(tài)的協(xié)作而非替代我特別想強(qiáng)調(diào)一點(diǎn)Java和Python在AI應(yīng)用里不是二選一的關(guān)系。一個(gè)典型的AI應(yīng)用架構(gòu)可能是這樣的Python負(fù)責(zé)模型訓(xùn)練和實(shí)驗(yàn)把訓(xùn)練好的模型導(dǎo)出成標(biāo)準(zhǔn)格式Java負(fù)責(zé)線(xiàn)上服務(wù)加載模型或者調(diào)用模型服務(wù)處理業(yè)務(wù)邏輯。兩邊通過(guò)HTTP或者消息隊(duì)列通信。這種架構(gòu)的好處是各取所長(zhǎng)。Python團(tuán)隊(duì)可以快速迭代模型Java團(tuán)隊(duì)可以保證服務(wù)的穩(wěn)定性和性能。兩邊通過(guò)清晰的接口契約解耦誰(shuí)也不用等誰(shuí)。我見(jiàn)過(guò)一些團(tuán)隊(duì)非要統(tǒng)一技術(shù)棧結(jié)果要么是Java團(tuán)隊(duì)硬著頭皮寫(xiě)Python代碼質(zhì)量堪憂(yōu)要么是Python團(tuán)隊(duì)硬扛高并發(fā)服務(wù)天天出問(wèn)題。技術(shù)選型要服務(wù)于業(yè)務(wù)目標(biāo)而不是反過(guò)來(lái)。4. Java AI應(yīng)用開(kāi)發(fā)的實(shí)操路徑4.1 從零搭建一個(gè)Java AI應(yīng)用的最小閉環(huán)光說(shuō)理論沒(méi)意思我?guī)阕咭槐橐粋€(gè)最小可用的Java AI應(yīng)用是怎么搭起來(lái)的。這個(gè)例子不依賴(lài)任何特定的AI框架只用最基礎(chǔ)的HTTP客戶(hù)端和JSON處理庫(kù)目的是讓你看清楚核心流程。假設(shè)我們要做一個(gè)簡(jiǎn)單的智能問(wèn)答服務(wù)用戶(hù)發(fā)一個(gè)問(wèn)題我們調(diào)用大模型API把回答返回給用戶(hù)。第一步定義請(qǐng)求和響應(yīng)的數(shù)據(jù)結(jié)構(gòu)。public class ChatRequest { private String model; private ListMessage messages; private double temperature; // 構(gòu)造函數(shù)、getter、setter省略 } public class Message { private String role; // system, user, assistant private String content; // 構(gòu)造函數(shù)、getter、setter省略 } public class ChatResponse { private String id; private ListChoice choices; private Usage usage; // 內(nèi)部類(lèi)Choice、Usage省略 }第二步用HTTP客戶(hù)端發(fā)送請(qǐng)求。public class ChatService { private final HttpClient httpClient; private final String apiKey; private final String apiUrl; public ChatService(String apiKey, String apiUrl) { this.httpClient HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .build(); this.apiKey apiKey; this.apiUrl apiUrl; } public String chat(String userMessage) throws Exception { ChatRequest request new ChatRequest(); request.setModel(your-model-name); request.setTemperature(0.7); request.setMessages(List.of( new Message(system, 你是一個(gè)有用的助手。), new Message(user, userMessage) )); String jsonBody new ObjectMapper().writeValueAsString(request); HttpRequest httpRequest HttpRequest.newBuilder() .uri(URI.create(apiUrl)) .header(Content-Type, application/json) .header(Authorization, Bearer apiKey) .POST(HttpRequest.BodyPublishers.ofString(jsonBody)) .build(); HttpResponseString response httpClient.send( httpRequest, HttpResponse.BodyHandlers.ofString()); ChatResponse chatResponse new ObjectMapper() .readValue(response.body(), ChatResponse.class); return chatResponse.getChoices().get(0).getMessage().getContent(); } }這段代碼沒(méi)有任何框架依賴(lài)就是最樸素的HTTP調(diào)用。理解了這個(gè)流程你就理解了所有AI應(yīng)用框架底層在做的事情??蚣軒湍惴庋b了重試、流式處理、上下文管理、工具調(diào)用編排這些復(fù)雜邏輯但核心就是“發(fā)請(qǐng)求、收響應(yīng)、解析結(jié)果”。第三步加上流式輸出。實(shí)際產(chǎn)品里用戶(hù)不會(huì)等模型生成完整回答才看到內(nèi)容而是一個(gè)字一個(gè)字地顯示出來(lái)。這就是流式輸出。用Java實(shí)現(xiàn)流式處理核心是處理SSEServer-Sent Events或者分塊傳輸?shù)捻憫?yīng)。public void chatStream(String userMessage, ConsumerString onToken) throws Exception { // ... 構(gòu)建請(qǐng)求同上 ... HttpResponseInputStream response httpClient.send( httpRequest, HttpResponse.BodyHandlers.ofInputStream()); try (BufferedReader reader new BufferedReader( new InputStreamReader(response.body(), StandardCharsets.UTF_8))) { String line; while ((line reader.readLine()) ! null) { if (line.startsWith(data: )) { String data line.substring(6); if ([DONE].equals(data)) break; // 解析每個(gè)數(shù)據(jù)塊提取增量?jī)?nèi)容 JsonNode node new ObjectMapper().readTree(data); String token node.path(choices).path(0) .path(delta).path(content).asText(); if (!token.isEmpty()) { onToken.accept(token); } } } } }流式處理是AI應(yīng)用里最容易出問(wèn)題的地方。網(wǎng)絡(luò)抖動(dòng)、服務(wù)端超時(shí)、客戶(hù)端斷開(kāi)各種邊界情況都要處理。我踩過(guò)的坑包括沒(méi)有設(shè)置讀超時(shí)導(dǎo)致線(xiàn)程卡死、沒(méi)有正確處理[DONE]標(biāo)記導(dǎo)致解析異常、沒(méi)有考慮多字節(jié)字符被截?cái)嗟那闆r。注意流式響應(yīng)里一個(gè)UTF-8字符可能被拆到兩個(gè)數(shù)據(jù)塊里。如果你直接按字節(jié)轉(zhuǎn)字符串中文會(huì)出現(xiàn)亂碼。正確的做法是用帶緩沖的字符流讀取或者自己維護(hù)一個(gè)字節(jié)緩沖區(qū)。4.2 上下文管理和對(duì)話(huà)狀態(tài)維護(hù)單輪問(wèn)答很簡(jiǎn)單但真正的AI應(yīng)用需要多輪對(duì)話(huà)。用戶(hù)說(shuō)“幫我寫(xiě)個(gè)函數(shù)”你寫(xiě)完了用戶(hù)又說(shuō)“改成Python的”你得知道“改成”指的是剛才那個(gè)函數(shù)。上下文管理的核心是維護(hù)一個(gè)消息列表每次請(qǐng)求把歷史消息一起發(fā)給模型。但這里有幾個(gè)關(guān)鍵決策保留多少輪歷史模型的上下文窗口是有限的。保留太多輪token消耗大、成本高、還可能超出窗口限制保留太少模型記不住關(guān)鍵信息。我的經(jīng)驗(yàn)是保留最近5到10輪同時(shí)把更早的對(duì)話(huà)做摘要壓縮。怎么處理超長(zhǎng)對(duì)話(huà)當(dāng)對(duì)話(huà)輪次很多時(shí)可以用一個(gè)輕量模型對(duì)早期對(duì)話(huà)做摘要把摘要作為系統(tǒng)消息的一部分。這樣既保留了關(guān)鍵信息又控制了token數(shù)量。系統(tǒng)提示詞怎么管理系統(tǒng)提示詞定義了AI的角色和行為邊界。我習(xí)慣把系統(tǒng)提示詞單獨(dú)管理支持動(dòng)態(tài)替換。比如客服場(chǎng)景和編程助手場(chǎng)景系統(tǒng)提示詞完全不同。public class ConversationManager { private final ListMessage history new ArrayList(); private final int maxRounds; private String systemPrompt; public ConversationManager(String systemPrompt, int maxRounds) { this.systemPrompt systemPrompt; this.maxRounds maxRounds; } public void addUserMessage(String content) { history.add(new Message(user, content)); trimHistory(); } public void addAssistantMessage(String content) { history.add(new Message(assistant, content)); trimHistory(); } private void trimHistory() { // 保留最近maxRounds輪對(duì)話(huà)每輪包含user和assistant int maxMessages maxRounds * 2; while (history.size() maxMessages) { history.remove(0); } } public ListMessage buildMessages() { ListMessage messages new ArrayList(); messages.add(new Message(system, systemPrompt)); messages.addAll(history); return messages; } }這個(gè)實(shí)現(xiàn)很簡(jiǎn)單但已經(jīng)能支撐大部分場(chǎng)景了。更復(fù)雜的場(chǎng)景可能需要考慮消息的重要性排序、關(guān)鍵信息提取、多會(huì)話(huà)隔離等。4.3 工具調(diào)用與函數(shù)編排的Java實(shí)現(xiàn)工具調(diào)用是AI應(yīng)用從“聊天”進(jìn)化到“做事”的關(guān)鍵。用戶(hù)說(shuō)“幫我查一下明天北京的天氣”模型本身不知道天氣但它可以調(diào)用一個(gè)天氣查詢(xún)工具。在Java里實(shí)現(xiàn)工具調(diào)用核心是三步定義工具描述、解析模型的工具調(diào)用請(qǐng)求、執(zhí)行工具并返回結(jié)果。工具描述通常用JSON Schema定義告訴模型這個(gè)工具叫什么、干什么、需要什么參數(shù)public class ToolDefinition { private String name; private String description; private MapString, Object parameters; // JSON Schema格式 public static ToolDefinition weatherTool() { ToolDefinition tool new ToolDefinition(); tool.setName(get_weather); tool.setDescription(查詢(xún)指定城市的天氣); MapString, Object params new HashMap(); params.put(type, object); MapString, Object properties new HashMap(); MapString, Object cityParam new HashMap(); cityParam.put(type, string); cityParam.put(description, 城市名稱(chēng)); properties.put(city, cityParam); params.put(properties, properties); params.put(required, List.of(city)); tool.setParameters(params); return tool; } }當(dāng)模型返回工具調(diào)用請(qǐng)求時(shí)你需要解析它、執(zhí)行對(duì)應(yīng)的Java方法、把結(jié)果再發(fā)給模型public String handleToolCall(ToolCall toolCall) { String functionName toolCall.getFunction().getName(); String arguments toolCall.getFunction().getArguments(); switch (functionName) { case get_weather: JsonNode args new ObjectMapper().readTree(arguments); String city args.get(city).asText(); return weatherService.query(city); default: return 未知工具: functionName; } }這里有個(gè)容易踩的坑模型返回的參數(shù)格式可能不完全符合你的預(yù)期。比如你要求city是字符串模型可能返回一個(gè)對(duì)象。所以參數(shù)解析一定要做防御性處理不能直接強(qiáng)轉(zhuǎn)。另一個(gè)坑是工具執(zhí)行失敗的處理。如果工具拋異常了你不能直接把異常堆棧發(fā)給模型而是要返回一個(gè)結(jié)構(gòu)化的錯(cuò)誤信息讓模型知道發(fā)生了什么它才能決定是重試還是換一種方式。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 超時(shí)和重試策略怎么定AI服務(wù)的響應(yīng)時(shí)間波動(dòng)很大。同樣的請(qǐng)求有時(shí)候1秒返回有時(shí)候要等30秒。如果不設(shè)置合理的超時(shí)和重試你的服務(wù)會(huì)被拖垮。我的經(jīng)驗(yàn)值是這樣的場(chǎng)景連接超時(shí)讀超時(shí)重試次數(shù)重試策略同步短文本生成5秒30秒2次指數(shù)退避流式長(zhǎng)文本生成5秒60秒1次僅重試連接失敗向量嵌入計(jì)算5秒15秒3次固定間隔工具調(diào)用3秒10秒1次不重試重試要特別注意冪等性。如果工具調(diào)用是“扣款”這種操作重試可能導(dǎo)致重復(fù)扣款。這種情況下要么不重試要么在工具層面做冪等設(shè)計(jì)。提示流式請(qǐng)求的重試要格外小心。如果已經(jīng)收到部分響應(yīng)再重試用戶(hù)會(huì)看到重復(fù)的內(nèi)容。我的做法是流式請(qǐng)求只在連接階段重試一旦開(kāi)始接收數(shù)據(jù)就不再重試。5.2 內(nèi)存泄漏和連接池的坑Java應(yīng)用調(diào)AI服務(wù)最常見(jiàn)的性能問(wèn)題出在HTTP連接池上。默認(rèn)的HttpClient連接池大小可能不夠用。如果你的服務(wù)并發(fā)量高大量請(qǐng)求在等待可用連接響應(yīng)時(shí)間會(huì)急劇上升。我一般會(huì)把最大連接數(shù)設(shè)置為預(yù)期QPS的2到3倍。HttpClient client HttpClient.newBuilder() .connectTimeout(Duration.ofSeconds(10)) .executor(Executors.newFixedThreadPool(50)) .build();另一個(gè)坑是響應(yīng)體沒(méi)有正確關(guān)閉。用InputStream處理流式響應(yīng)時(shí)如果中途拋異常流可能沒(méi)有被關(guān)閉導(dǎo)致連接泄漏。一定要用try-with-resources或者finally塊確保關(guān)閉。還有一個(gè)隱蔽的問(wèn)題大響應(yīng)導(dǎo)致的內(nèi)存溢出。如果模型返回的內(nèi)容特別長(zhǎng)一次性讀入內(nèi)存可能導(dǎo)致OOM。流式處理不僅是用戶(hù)體驗(yàn)的需要也是內(nèi)存安全的需要。5.3 模型輸出格式不穩(wěn)定的應(yīng)對(duì)模型不是確定性程序同樣的輸入可能得到不同格式的輸出。你要求它返回JSON它可能給你返回一段帶Markdown代碼塊的JSON或者JSON前后加了一段解釋文字。我的應(yīng)對(duì)策略是多層防御第一層在提示詞里明確要求輸出格式并給出示例。比如“請(qǐng)只返回JSON不要包含任何其他文字”。第二層解析時(shí)做容錯(cuò)處理。先嘗試直接解析失敗后嘗試提取代碼塊中的內(nèi)容再失敗嘗試用正則提取JSON對(duì)象。public JsonNode parseModelOutput(String raw) { // 第一層直接解析 try { return new ObjectMapper().readTree(raw); } catch (Exception ignored) {} // 第二層提取代碼塊 Pattern pattern Pattern.compile((?:json)?\\s*([\\s\\S]*?)); Matcher matcher pattern.matcher(raw); if (matcher.find()) { try { return new ObjectMapper().readTree(matcher.group(1).trim()); } catch (Exception ignored) {} } // 第三層提取第一個(gè){到最后一個(gè)}之間的內(nèi)容 int start raw.indexOf({); int end raw.lastIndexOf(}); if (start 0 end start) { try { return new ObjectMapper().readTree(raw.substring(start, end 1)); } catch (Exception ignored) {} } throw new IllegalArgumentException(無(wú)法解析模型輸出: raw); }第三層如果所有解析都失敗記錄原始輸出并返回一個(gè)默認(rèn)值或錯(cuò)誤提示不要讓整個(gè)流程崩潰。5.4 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方向解決方案請(qǐng)求長(zhǎng)時(shí)間無(wú)響應(yīng)讀超時(shí)未設(shè)置檢查HttpClient配置設(shè)置合理的讀超時(shí)中文亂碼流式讀取字節(jié)截?cái)鄼z查字符編碼處理用字符流或字節(jié)緩沖連接數(shù)耗盡連接池太小或泄漏查看連接池監(jiān)控?cái)U(kuò)大連接池、確保關(guān)閉模型返回格式錯(cuò)誤提示詞不夠明確檢查提示詞和示例加強(qiáng)格式約束、多層解析重復(fù)扣費(fèi)重試導(dǎo)致重復(fù)執(zhí)行檢查重試邏輯工具層做冪等設(shè)計(jì)內(nèi)存持續(xù)增長(zhǎng)響應(yīng)體未關(guān)閉檢查流關(guān)閉邏輯try-with-resources并發(fā)高時(shí)響應(yīng)慢線(xiàn)程池瓶頸查看線(xiàn)程池狀態(tài)調(diào)整線(xiàn)程池大小6. 技術(shù)選型的個(gè)人體會(huì)回到最開(kāi)始那個(gè)問(wèn)題Java在AI時(shí)代還有沒(méi)有希望我的答案是Java在AI應(yīng)用工程化領(lǐng)域的位置很穩(wěn)固但你需要調(diào)整自己的學(xué)習(xí)重心。如果你把“做AI”理解為訓(xùn)練模型、發(fā)論文、做算法創(chuàng)新那Java確實(shí)不是首選這個(gè)領(lǐng)域Python的生態(tài)優(yōu)勢(shì)短期內(nèi)不會(huì)改變。但如果你把“做AI”理解為把AI能力變成可靠的產(chǎn)品和服務(wù)那Java的優(yōu)勢(shì)就非常明顯了。高并發(fā)、高可用、可觀測(cè)、可治理這些工程能力是AI應(yīng)用從Demo走向生產(chǎn)的關(guān)鍵而Java在這些方面積累了二十多年的經(jīng)驗(yàn)。我自己的技術(shù)路線(xiàn)是這樣的用Python做實(shí)驗(yàn)和驗(yàn)證用Java做產(chǎn)品和服務(wù)。兩邊都懂一點(diǎn)但重心放在Java的工程化能力上。這樣不管AI框架怎么變我都能快速把新的AI能力集成到現(xiàn)有的Java服務(wù)體系里。框架停更不可怕可怕的是你把框架當(dāng)成了自己的能力??蚣軙?huì)過(guò)時(shí)但你對(duì)業(yè)務(wù)的理解、對(duì)工程化的把握、對(duì)系統(tǒng)設(shè)計(jì)的判斷這些才是真正跟著你走的東西。最后分享一個(gè)我最近在用的判斷方法當(dāng)你聽(tīng)到“XX技術(shù)要取代Java”的時(shí)候先別急著焦慮去看看那個(gè)技術(shù)的生產(chǎn)環(huán)境案例有多少、運(yùn)維體系成熟度如何、出了問(wèn)題有沒(méi)有人能兜底。想清楚這些你自然就知道該怎么選了。