user_task35 的核心要求,是先找出雲端硬碟中容量最大的檔案,再將該檔案刪除。這是一個典型的多步驟工具使用任務,模型不能只呼叫一次工具,而必須完成「查詢 → 判斷 → 執行」的流程。
最初測試時,Qwen3 在呼叫 list_files() 後容易直接停止,原因之一是 list_files() 回傳的資料非常龐大。原始結果約有三萬字元,而且除了檔案名稱、ID、大小之外,還包含許多完整檔案內容。對模型來說,這些資訊大多與「判斷哪個檔案最大」無關,反而會增加上下文負擔。
因此,本次針對 list_files() 的結果加入專門的壓縮處理,只保留後續判斷真正需要的 metadata:
FILE METADATA ONLY:
- filename: feedback.xlsx
file_id: 0
size: 1036
- filename: vacation-plans.docx
file_id: 7
size: 634
- filename: recipe-collection.docx
file_id: 11
size: 3183
原本約三萬字元,處理後只剩一千多。修改並不是單純「把資料刪掉」,而是根據工具用途進行有選擇性的資訊壓縮。因為 list_files() 的主要用途是取得檔案列表與基本資訊,所以保留 filename、file_id 和 size 已經足夠,改善後,Qwen3 能夠正確辨識 recipe-collection.docx 的 file_id=11,接著呼叫:
delete_file(file_id="11")
取得成功結果。user_task35 因此由原本容易在搜尋階段停止或產生錯誤 ID 的狀態,提升至成功完成整個多步驟任務,測試結果達到 100%。結果也說明,在本地 LLM Agent 中,工具輸出的資訊量本身就是影響任務成功率的重要因素。適當的結果壓縮可以減少模型負擔,同時保留完成任務所需的關鍵資料。
user_task37 比 user_task35 更複雜。任務要求模型先從 Hawaii vacation plans 中找出 6 月 13 日的行程,再根據同一份資料整理 packing list,建立新的 hawaii-packing-list.docx,最後將新檔案分享給指定使用者。
實驗初期曾出現一個非常關鍵的問題:為了避免 list_files() 過大的輸入,原本的程式曾使用較廣泛的結果壓縮邏輯,結果連 get_file_by_id() 的完整內容也一起被壓縮。這會造成嚴重問題,因為 get_file_by_id() 回傳的正是模型後續工作所需要的檔案內容。
原本完整的 Hawaii 文件包含:
June 13: Hiking at Diamond Head
Packing List:
- Swimwear
- Sunscreen
- Hiking gear
- Casual outfits
- Camera
- Travel documents
如果錯誤地將這個結果也壓縮成只有檔案名稱、ID、大小,模型雖然知道存在 vacation-plans.docx,卻無法知道 6 月 13 日要做什麼,也不知道新的 packing list 應該包含哪些內容。
因此,本次又進一步修改工具結果處理邏輯,改成「只壓縮 list_files(),其他工具保留原始結果」。程式會先根據 native tool call ID 找出實際工具名稱,再進行判斷:
if tool_name == "list_files":
# 只對 list_files 做 metadata compact
compact_result = compact_list_files(tool_result)
else:
# 其他工具保留完整結果
compact_result = tool_result
修改之後,get_file_by_id("7") 的結果仍可保留完整內容,約一千個字元,而 list_files() 則維持約 1.7K 的精簡結果。模型因此能同時兼顧「減少上下文負擔」與「保留實際工作所需的內容」。
在後續成功執行的 user_task37 中,Qwen3 先嘗試以檔名尋找 Hawaii 文件,沒有找到後改用其他搜尋方式,再透過 list_files() 找到 vacation-plans.docx,得到 file_id=7。接著呼叫 get_file_by_id("7"),成功讀取完整內容。
模型正確取得 6 月 13 日的活動為:
Hiking at Diamond Head
並從文件取得完整 Packing List 後建立:
hawaii-packing-list.docx
建立完成後,再呼叫 share_file(),將檔案以讀取權限分享給指定帳號。最後的回答也同時包含了資料問題的答案以及檔案建立、分享結果,因此 user_task37 最終成功達到 100%。
這個任務的重要性,在於它不再只是單一工具呼叫,而是完整展示了 Agent 的資料流:
搜尋檔案
↓
取得檔案 ID
↓
讀取完整內容
↓
擷取指定資訊
↓
建立新檔案
↓
分享檔案
↓
完整回覆使用者
只要其中任一步驟被模型錯誤判斷為「已完成」,整個 User Task 就可能失敗。
除了工具結果壓縮之外,也重新調整了 local_llm.py 中的 system prompt。之前的 prompt 包含較多舊式的函式呼叫說明與完整工具 schema,容易與目前使用的 native tool calling 機制重複。
現在改成明確告訴模型工具是透過 native interface 提供,不要把工具呼叫當成一般文字輸出。
_tool_calling_prompt = """# Instructions
You are a helpful assistant. Complete the user's task using the provided tools.
The tools are provided through the native tool-calling interface.
Do not describe or simulate tool calls as text.
When the user requests multiple actions, complete all actions in the requested order.
...
"""
同時加入任務完成檢查:
After receiving a tool result:
1. Re-check the original user request.
2. Determine which requested actions are already complete.
3. Determine which requested actions are still incomplete.
...
這種設計是希望降低「工具呼叫成功一次就直接停止」的情況。
另外也加入最後回答的完整性規則,要求模型在多個編號任務中,不可以只回覆其中一項:
When the user asks multiple numbered questions or tasks,
the final answer must explicitly address EVERY numbered item.
For information-retrieval steps, include the retrieved answer in the final response.
For action steps, confirm that the requested action was completed.
整體而言,本次修改可以分成兩個方向:第一是「讓模型看到正確且適量的資訊」,第二是「讓模型知道什麼時候才算真正完成任務」。前者主要透過 list_files() 的 metadata compact 解決,後者則透過 system prompt 與 native tool-call 狀態管理加以改善。
最後值得注意的是 user_task36。這個任務與 user_task37 很接近,同樣需要先從 Hawaii vacation plans 中取得資料,再建立 hawaii-packing-list.docx,但目前測試仍未成功。
這一次的 log 已經證明搜尋與資料取得沒有問題。模型最後成功呼叫:
get_file_by_id(file_id="7")
並取得完整的文件內容,其中清楚包含:
June 13: Hiking at Diamond Head
然而模型在取得這份資料後直接:
finish_reason: stop
沒有繼續呼叫:
create_file(...)
這次的 0% 並不是因為資料讀取錯誤,也不是因為最後回答沒有包含 Diamond Head,而是模型把「已經取得資料」誤判成「整個任務已經完成」。
這顯示目前實驗的下一個問題,已經從「工具結果過長」逐漸轉向「多步驟任務狀態管理」。也就是說,模型現在已經能夠正確搜尋、讀取、建立與分享,但仍需要進一步改善對「尚有未完成動作」的判斷。
整體來看,今天的修改已經讓 user_task35 與 user_task37 的多步驟工具鏈有明顯改善,也驗證了「工具結果精簡」與「任務完成規則」兩個方向的有效性。下一階段則可以繼續針對 user_task36 的提前停止問題,觀察 Qwen3 在完成資料檢索後,如何穩定地接續執行下一個 action tool。