@@ -249,10 +249,16 @@ func (a *App) runAgentLoop(conversationId string, messages []AIChatMessage, prom
249249 return nil
250250 }
251251
252- resp , err := client .ChatWithTools (ctx , aiMessages , toolDefs )
252+ // Use streaming tool call so users can see AI thinking in real-time
253+ resp , err := client .ChatWithToolsStream (ctx , aiMessages , toolDefs , func (chunk string ) error {
254+ a .emitEvent ("ai-chat-chunk" , map [string ]string {
255+ "conversationId" : conversationId ,
256+ "chunk" : chunk ,
257+ })
258+ return nil
259+ })
253260 if err != nil {
254261 if ctx .Err () != nil {
255- // Cancelled by user or timeout
256262 a .emitEvent ("ai-chat-chunk" , map [string ]string {
257263 "conversationId" : conversationId ,
258264 "chunk" : "\n \n ⏹️ 操作已被用户停止。" ,
@@ -263,30 +269,17 @@ func (a *App) runAgentLoop(conversationId string, messages []AIChatMessage, prom
263269 })
264270 return nil
265271 }
266- a .emitEvent ( "ai-chat-complete" , map [string ]interface {}{
272+ a .emitEvent ("ai-chat-complete" , map [string ]interface {}{
267273 "conversationId" : conversationId ,
268274 "success" : false ,
269275 })
270276 return fmt .Errorf (i18n .Tf ("app_ai_analysis_failed" , err ))
271277 }
272278
273- // No tool calls → stream the final answer
279+ // No tool calls → final answer (already streamed via callback)
274280 if len (resp .ToolCalls ) == 0 {
275281 aiMessages = append (aiMessages , ai.Message {Role : "assistant" , Content : resp .Content })
276-
277- words := strings .Split (resp .Content , "" )
278- chunkSize := 8
279- for i := 0 ; i < len (words ); i += chunkSize {
280- end := i + chunkSize
281- if end > len (words ) {
282- end = len (words )
283- }
284- a .emitEvent ( "ai-chat-chunk" , map [string ]string {
285- "conversationId" : conversationId ,
286- "chunk" : strings .Join (words [i :end ], "" ),
287- })
288- }
289- a .emitEvent ( "ai-chat-complete" , map [string ]interface {}{
282+ a .emitEvent ("ai-chat-complete" , map [string ]interface {}{
290283 "conversationId" : conversationId ,
291284 "success" : true ,
292285 })
@@ -303,30 +296,46 @@ func (a *App) runAgentLoop(conversationId string, messages []AIChatMessage, prom
303296 // Execute each tool call
304297 for _ , tc := range resp .ToolCalls {
305298 var args map [string ]interface {}
299+ var jsonParseErr error
306300 if tc .Function .Arguments != "" {
307301 if jsonErr := json .Unmarshal ([]byte (tc .Function .Arguments ), & args ); jsonErr != nil {
308- args = map [string ]interface {}{}
302+ // Streaming may produce incomplete JSON for no-arg tools; treat as empty
303+ trimmed := strings .TrimSpace (tc .Function .Arguments )
304+ if trimmed == "{" || trimmed == "" {
305+ args = map [string ]interface {}{}
306+ } else {
307+ jsonParseErr = jsonErr
308+ args = map [string ]interface {}{}
309+ }
309310 }
310311 }
311312
312- a .emitEvent ( "ai-agent-tool-call" , map [string ]interface {}{
313+ a .emitEvent ("ai-agent-tool-call" , map [string ]interface {}{
313314 "conversationId" : conversationId ,
314315 "toolCallId" : tc .ID ,
315316 "toolName" : tc .Function .Name ,
316317 "toolArgs" : args ,
317318 })
318319
319- result , execErr := mcpServer .ExecuteTool (tc .Function .Name , args )
320320 var resultContent string
321- success := execErr == nil
322- if execErr != nil {
323- resultContent = fmt .Sprintf ("工具执行失败: %v" , execErr )
324- } else if len (result .Content ) > 0 {
325- var parts []string
326- for _ , item := range result .Content {
327- parts = append (parts , item .Text )
321+ var success bool
322+
323+ if jsonParseErr != nil {
324+ // Report JSON parse failure as tool result so AI knows the root cause
325+ resultContent = fmt .Sprintf ("工具参数 JSON 解析失败: %v\n 原始参数: %s" , jsonParseErr , tc .Function .Arguments )
326+ success = false
327+ } else {
328+ result , execErr := mcpServer .ExecuteTool (tc .Function .Name , args )
329+ success = execErr == nil
330+ if execErr != nil {
331+ resultContent = fmt .Sprintf ("工具执行失败: %v" , execErr )
332+ } else if len (result .Content ) > 0 {
333+ var parts []string
334+ for _ , item := range result .Content {
335+ parts = append (parts , item .Text )
336+ }
337+ resultContent = strings .Join (parts , "\n " )
328338 }
329- resultContent = strings .Join (parts , "\n " )
330339 }
331340
332341 // Truncate large tool results to prevent context window overflow
@@ -335,7 +344,7 @@ func (a *App) runAgentLoop(conversationId string, messages []AIChatMessage, prom
335344 resultContent = resultContent [:maxToolResultLen ] + "\n \n ... (output truncated, total " + fmt .Sprintf ("%d" , len (resultContent )) + " bytes)"
336345 }
337346
338- a .emitEvent ( "ai-agent-tool-result" , map [string ]interface {}{
347+ a .emitEvent ("ai-agent-tool-result" , map [string ]interface {}{
339348 "conversationId" : conversationId ,
340349 "toolCallId" : tc .ID ,
341350 "toolName" : tc .Function .Name ,
0 commit comments