Spring AI工具已經(jīng)調(diào)用成功,為什么最終回答仍為空?返回值、循環(huán)與上下文完整排查
文章摘要有些Spring AI項目可以在日志中看到工具已經(jīng)被調(diào)用數(shù)據(jù)庫查詢或HTTP請求也成功執(zhí)行但客戶端最終收到空字符串、模型重復(fù)調(diào)用同一工具或者回答完全沒有使用工具結(jié)果。這類問題與“模型沒有選擇工具”不同通常發(fā)生在工具返回值序列化、異常處理、Tool Calling循環(huán)、流式事件拼接、結(jié)果過長、消息持久化和終止條件等環(huán)節(jié)。本文給出從工具執(zhí)行結(jié)果到最終Assistant回答的完整排查路徑。一、先把鏈路分成五個階段一個完整工具調(diào)用不是只有“方法執(zhí)行成功”模型選擇工具 → 參數(shù)解析 → 工具執(zhí)行 → 結(jié)果寫入Tool Response → 模型基于結(jié)果生成最終回答日志只顯示orderService.query()執(zhí)行成功只能證明第三階段完成。后面仍可能失敗返回對象無法序列化Tool Response為空結(jié)果沒有進入下一輪模型請求模型再次調(diào)用工具流式客戶端漏掉最終事件上下文超限Advisor提前返回最終回答被安全策略攔截。二、工具不要返回null錯誤實現(xiàn)Tool(description查詢訂單)publicOrderResultqueryOrder(StringorderId){returnrepository.find(orderId).orElse(null);}工具返回null時模型可能只看到一個空結(jié)果無法區(qū)分訂單不存在 系統(tǒng)異常 沒有權(quán)限 返回值丟失推薦結(jié)構(gòu)化返回publicrecordToolResultT(booleansuccess,Stringcode,Stringmessage,Tdata){publicstaticTToolResultTsuccess(Tdata){returnnewToolResult(true,OK,執(zhí)行成功,data);}publicstaticTToolResultTfailure(Stringcode,Stringmessage){returnnewToolResult(false,code,message,null);}}不存在時返回{success:false,code:ORDER_NOT_FOUND,message:訂單不存在,data:null}三、不要直接返回數(shù)據(jù)庫Entity數(shù)據(jù)庫實體可能包含Hibernate代理懶加載集合雙向關(guān)系循環(huán)引用內(nèi)部字段敏感字段超大關(guān)聯(lián)對象。例如Order → Customer → Orders → Customer → ……序列化可能失敗或生成巨大結(jié)果。推薦返回專用DTOpublicrecordOrderSummary(StringorderId,Stringstatus,BigDecimalamount,Stringcurrency,InstantupdatedAt){}只返回模型完成任務(wù)真正需要的字段。四、返回值是否被異常轉(zhuǎn)換成字符串一些工具為了“方便”這樣寫catch(Exceptionexception){returnexception.getMessage();}模型會把錯誤字符串當(dāng)成正常業(yè)務(wù)結(jié)果。更危險的寫法return查詢完成;但沒有返回真實數(shù)據(jù)模型無法回答用戶問題。推薦區(qū)分業(yè)務(wù)成功 業(yè)務(wù)失敗 技術(shù)異常 權(quán)限拒絕 等待確認每類都使用穩(wěn)定錯誤碼。五、檢查工具結(jié)果的實際序列化內(nèi)容不要只打印Java對象log.info(result{},result);還應(yīng)在安全脫敏后檢查發(fā)送給模型的內(nèi)容tool_result_json result_size_bytes serialization_status可以在測試環(huán)境中顯式序列化StringjsonobjectMapper.writeValueAsString(result);檢查是否為合法JSON是否包含所需字段是否出現(xiàn)空對象{}是否被截斷是否包含敏感信息是否大到無法進入上下文。六、工具結(jié)果過長會發(fā)生什么如果工具返回5000條數(shù)據(jù)庫記錄 整個日志文件 完整網(wǎng)頁HTML 幾十萬字文檔下一輪模型請求可能超過上下文窗口被Provider拒絕成本驟增模型忽略關(guān)鍵信息最終回答變空流式連接超時。工具應(yīng)該返回摘要 分頁信息 少量關(guān)鍵記錄 可繼續(xù)查詢的游標例如{total:2387,returned:20,nextCursor:eyJwYWdlIjoyfQ,items:[]}七、Tool Calling循環(huán)是否繼續(xù)進入下一輪Spring AI 2.0通過ToolCallingAdvisor執(zhí)行循環(huán)模型請求工具 → 執(zhí)行工具 → 把結(jié)果加入對話 → 再次調(diào)用模型 → 得到最終回答如果自動Tool Advisor被關(guān)閉AdvisorParams.toolCallingAdvisorAutoRegister(false)應(yīng)用必須自己完成后續(xù)循環(huán)。否則你只能拿到工具調(diào)用請求或工具執(zhí)行結(jié)果卻沒有最終自然語言回答。八、是否錯誤地注冊了多個ToolAdvisor一個調(diào)用鏈中不應(yīng)該同時存在多個負責(zé)工具執(zhí)行循環(huán)的Advisor。重復(fù)注冊可能造成工具執(zhí)行兩次對話歷史重復(fù)循環(huán)順序混亂最終消息被覆蓋冪等沖突。檢查ChatClient自動注冊的ToolCallingAdvisor 自定義ToolCallingAdvisor ToolSearchToolCallingAdvisor應(yīng)該只有一個工具循環(huán)策略。九、模型為什么重復(fù)調(diào)用同一個工具常見原因1. 結(jié)果不包含完成信號返回{status:PROCESSING}模型可能繼續(xù)查詢。2. 工具描述暗示需要再次確認3. 返回值缺少用戶要求的字段用戶問物流單號工具只返回訂單狀態(tài)。4. Tool Response沒有進入下一輪上下文5. Prompt要求“直到確認成功為止”6. 工具調(diào)用失敗卻被包裝為成功建議在結(jié)果中加入terminal retryable nextAction例如{success:true,terminal:true,retryable:false,data:{trackingNo:SF123456}}十、必須為副作用工具設(shè)置冪等鍵如果重復(fù)調(diào)用的是創(chuàng)建訂單退款發(fā)郵件修改權(quán)限提交審批發(fā)布內(nèi)容后果可能很嚴重。冪等鍵建議由業(yè)務(wù)系統(tǒng)生成或驗證tenant_id user_id conversation_id tool_name business_request_id示例StringidempotencyKeyString.join(:,tenantId,conversationId,cancel_order,orderId);數(shù)據(jù)庫建立唯一約束不能只依賴內(nèi)存緩存。十一、流式接口是否漏掉最終事件流式工具調(diào)用可能包含模型文本增量 工具參數(shù)增量 工具調(diào)用開始 工具結(jié)果 下一輪模型文本 完成事件如果前端只處理第一類文本事件工具執(zhí)行后生成的第二輪回答可能被忽略。檢查SSE事件類型是否在工具輪次后繼續(xù)訂閱是否過早調(diào)用takeUntil是否收到CompleteNginx是否斷開長連接客戶端是否因空Chunk判定結(jié)束取消信號是否傳播到上游。十二、Memory位置是否導(dǎo)致工具消息丟失MessageChatMemoryAdvisor放在Tool Calling循環(huán)外部時通常只持久化最終用戶消息和Assistant消息。如果業(yè)務(wù)需要完整工具軌跡必須確認所用Memory Repository支持AI Tool Call RequestTool Response Message多輪工具消息。否則工具結(jié)果可能在當(dāng)前請求可用但下一輪對話無法恢復(fù)。不要為了保存工具消息盲目把Memory Advisor移入循環(huán)。還要同時處理重復(fù)寫入Repository序列化能力上下文膨脹敏感參數(shù)存儲。十三、最終回答是否被其他Advisor改變調(diào)用鏈可能還有內(nèi)容審核輸出過濾結(jié)構(gòu)化輸出驗證緩存日志自定義響應(yīng)轉(zhuǎn)換。工具執(zhí)行成功后最終回答可能被安全策略阻斷Schema校驗反復(fù)重試緩存返回舊空結(jié)果自定義Advisor提前替換響應(yīng)轉(zhuǎn)換器解析失敗。建議記錄每個Advisor的enter exit order duration response_present十四、最終回答為空時的最小實驗第一步固定工具返回值Tool(description返回測試訂單)publicOrderSummarytestOrder(){returnnewOrderSummary(A1001,SHIPPED,newBigDecimal(99.00),CNY,Instant.now());}第二步要求模型必須復(fù)述字段調(diào)用testOrder并返回orderId和status。第三步關(guān)閉其他Advisor只保留Tool Calling。第四步分別測試call與stream如果同步正常、流式失敗重點檢查事件消費。第五步查看第二輪模型請求確認Tool Response是否真的進入上下文。十五、建議記錄的觀測字段tool_call_id tool_name arguments_hash execution_status execution_duration_ms result_serialization_status result_size_bytes tool_result_hash loop_iteration model_after_tool_called final_response_present stream_completed advisor_chain高風(fēng)險業(yè)務(wù)還應(yīng)記錄idempotency_key approval_id operator business_result_id十六、完整排查清單□ 工具沒有返回null □ 返回的是DTO而不是數(shù)據(jù)庫Entity □ 結(jié)果可以穩(wěn)定序列化 □ 錯誤沒有被偽裝成成功字符串 □ 結(jié)果大小受到限制 □ ToolCallingAdvisor完成了第二輪模型調(diào)用 □ 沒有重復(fù)注冊ToolAdvisor □ 結(jié)果包含terminal與retryable語義 □ 副作用工具使用數(shù)據(jù)庫級冪等 □ 流式客戶端沒有漏掉工具后的回答 □ Memory與工具消息能力匹配 □ 其他Advisor沒有替換最終響應(yīng) □ Trace中可以看到工具結(jié)果進入下一輪模型請求總結(jié)“工具調(diào)用成功但最終回答為空”說明問題已經(jīng)越過工具選擇階段應(yīng)該重點檢查返回值序列化 → Tool Response寫入 → Tool Calling下一輪 → 流式事件消費 → 最終Advisor處理只有把工具調(diào)用拆成完整階段并逐段觀測才能判斷結(jié)果究竟丟在了哪里。

相關(guān)新聞

麒麟Linux系統(tǒng)安裝配置MongoDB 6.0全攻略:從倉庫適配到安全部署

麒麟Linux系統(tǒng)安裝配置MongoDB 6.0全攻略:從倉庫適配到安全部署

1. 項目緣起:為什么要在麒麟Linux上折騰MongoDB?最近接手了一個新項目,后端數(shù)據(jù)模型變動頻繁,傳統(tǒng)的關(guān)系型數(shù)據(jù)庫在頻繁修改表結(jié)構(gòu)時顯得有點力不從心。團隊討論后,決定引入文檔型數(shù)據(jù)庫MongoDB來應(yīng)對這種靈活多變的場…

2026/7/29 13:56:45 閱讀更多
春節(jié)遷徙背后的經(jīng)濟學(xué)邏輯與城鄉(xiāng)決策分析

春節(jié)遷徙背后的經(jīng)濟學(xué)邏輯與城鄉(xiāng)決策分析

1. 春節(jié)遷徙現(xiàn)象的經(jīng)濟學(xué)觀察 每年春節(jié)前后,中國大地上都會上演一場規(guī)??涨暗?quot;人類大遷徙"。作為一名長期關(guān)注城鄉(xiāng)經(jīng)濟的研究者,我注意到這個現(xiàn)象背后蘊含著深刻的經(jīng)濟邏輯。2023年春運期間,全國鐵路發(fā)送旅客達3.48億人次&#xf…

2026/7/29 13:56:45 閱讀更多
多孔介質(zhì)模型在催化器仿真中的應(yīng)用:從達西定律到化學(xué)反應(yīng)耦合

多孔介質(zhì)模型在催化器仿真中的應(yīng)用:從達西定律到化學(xué)反應(yīng)耦合

1. 項目概述:當(dāng)流體穿過催化器時發(fā)生了什么?催化器,無論是汽車尾氣凈化用的三元催化器,還是化工生產(chǎn)中的固定床反應(yīng)器,其內(nèi)部都不是空蕩蕩的管道。里面填滿了密密麻麻、布滿微小孔道的陶瓷或金屬載體,這些載…

2026/7/29 13:56:45 閱讀更多
在行空板上部署MediaPipe實現(xiàn)實時人像分割與摳圖

在行空板上部署MediaPipe實現(xiàn)實時人像分割與摳圖

1. 項目緣起:為什么要在行空板上折騰MediaPipe摳圖? 最近在做一個智能交互展項,需要在資源受限的嵌入式設(shè)備上實時處理攝像頭畫面,把人物從背景里干凈地“摳”出來。一開始想到的是上云或者用高性能工控機跑OpenCVDeeplabv3&#…

2026/7/29 15:07:17 閱讀更多
數(shù)字生命技能工具:動態(tài)流程引擎與協(xié)作效率提升

數(shù)字生命技能工具:動態(tài)流程引擎與協(xié)作效率提升

1. 數(shù)字生命技能工具的核心價值解析在2023年Q3的團隊效率調(diào)研報告中,一個令人震驚的數(shù)據(jù)浮現(xiàn):知識工作者平均每天要花費2.1小時在重復(fù)性的流程溝通上。這正是我們團隊開發(fā)數(shù)字生命技能工具的初衷——將那些"只存在于老員工腦子里"的協(xié)作經(jīng)驗轉(zhuǎn)…

2026/7/29 15:07:17 閱讀更多
COMSOL納米孔陣列超表面透射譜仿真技巧與優(yōu)化

COMSOL納米孔陣列超表面透射譜仿真技巧與優(yōu)化

1. 項目概述:納米孔陣列超表面透射譜仿真 第一次在COMSOL中嘗試仿真納米孔陣列超表面時,我遇到了一個有趣的現(xiàn)象:理論上應(yīng)該出現(xiàn)明顯透射峰的位置,仿真結(jié)果卻顯示為平坦的直線。這個問題困擾了我整整三天,直到發(fā)現(xiàn)是網(wǎng)…

2026/7/29 15:07:17 閱讀更多
從C語言文件管理系統(tǒng)到Linux裝NAS:開發(fā)者的自建存儲之路

從C語言文件管理系統(tǒng)到Linux裝NAS:開發(fā)者的自建存儲之路

開發(fā)者論壇里的一次討論 “用易語言寫個內(nèi)網(wǎng)穿透軟件難不難?”這是某開發(fā)者社區(qū)里一個常見問題?;卮饏^(qū)里的思路大致分兩派:一派認為易語言上手快,做個簡單的端口映射工具并不復(fù)雜;另一派則提醒,穩(wěn)定的內(nèi)網(wǎng)穿透涉及NAT…

2026/7/29 14:57:16 閱讀更多
面試官大笑:“一個任務(wù)拆給 5 個 Subagent 并行跑,不比 1 個快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個任務(wù)拆給 5 個 Subagent 并行跑,不比 1 個快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個月,我在重構(gòu) AlgoMooc 網(wǎng)站過程中,發(fā)現(xiàn)一個問題:在 Claude Code 里把一個任務(wù)拆給 5 個 Subagent 并行跑,結(jié)果可能比 1 個 agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過來的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號與動畫渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開發(fā)實戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號與動畫渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂應(yīng)用,模擬了真實擲骰子的過程。應(yīng)用投擲兩個骰子(六面標準骰),使用 Unicode 骰面符號直觀展示每個骰子的點數(shù),并伴有快速滾動的動畫效果?!?/p>

2026/7/29 0:15:24 閱讀更多