@@ -576,15 +576,24 @@ def _process_llm_interaction_with_client(
576576 # LLMからレスポンスを取得
577577 resp , functions = llm_client .get_response ()
578578 self .logger .info ("LLM応答: %s" , resp )
579+
580+ # 空レスポンスのチェック
581+ if not resp or not resp .strip ():
582+ self .logger .error ("LLMから空の応答が返されました" )
583+ if count >= MAX_JSON_PARSE_ERRORS :
584+ task .comment ("LLM応答エラーでスキップ" )
585+ return True
586+ return False
579587
580588 # レスポンスの前処理
581589 resp_clean = self ._process_think_tags (task , resp )
590+ self .logger .debug ("think処理後のレスポンス: %s" , resp_clean )
582591
583592 # JSON応答の解析
584593 try :
585594 data = self ._extract_json (resp_clean )
586595 except Exception :
587- self .logger .exception ("LLM応答JSONパース失敗" )
596+ self .logger .exception ("LLM応答JSONパース失敗 (応答内容: %s)" , resp_clean [: 200 ] )
588597 if count >= MAX_JSON_PARSE_ERRORS :
589598 task .comment ("LLM応答エラーでスキップ" )
590599 return True
@@ -627,6 +636,67 @@ def _process_response_data_with_context(
627636 task .comment (str (data ["plan" ]))
628637 llm_client .send_user_message (str (data ["plan" ]))
629638
639+ # function_call形式の処理 (OpenAI/LMStudio互換)
640+ if "function_call" in data :
641+ func_call = data ["function_call" ]
642+ tool_name = func_call ["name" ]
643+ args_str = func_call .get ("arguments" , "{}" )
644+
645+ # argumentsが文字列の場合はパース
646+ if isinstance (args_str , str ):
647+ args = json .loads (args_str ) if args_str else {}
648+ else :
649+ args = args_str
650+
651+ args = self .sanitize_arguments (args )
652+ mcp_server , tool_func = tool_name .split ("_" , 1 )
653+
654+ start_time = time .time ()
655+ try :
656+ output = self .mcp_clients [mcp_server ].call_tool (tool_func , args )
657+ duration_ms = (time .time () - start_time ) * 1000
658+
659+ # Record tool execution
660+ tool_store .add_tool_call (
661+ tool_name = tool_name ,
662+ args = args ,
663+ result = output ,
664+ status = "success" ,
665+ duration_ms = duration_ms ,
666+ )
667+ context_manager .update_statistics (tool_calls = 1 )
668+
669+ # Send result to LLM
670+ llm_client .send_function_result (tool_name , output )
671+
672+ # Reset error count on success
673+ if error_state .get ("last_tool" ) == tool_name :
674+ error_state ["tool_error_count" ] = 0
675+
676+ except Exception as e :
677+ duration_ms = (time .time () - start_time ) * 1000
678+ error_msg = str (e )
679+
680+ # Record tool error
681+ tool_store .add_tool_call (
682+ tool_name = tool_name ,
683+ args = args ,
684+ result = None ,
685+ status = "error" ,
686+ duration_ms = duration_ms ,
687+ error = error_msg ,
688+ )
689+ context_manager .update_statistics (tool_calls = 1 )
690+
691+ self .logger .exception ("ツール呼び出しエラー: %s" , error_msg )
692+ llm_client .send_function_result (tool_name , {"error" : error_msg })
693+
694+ # Update error state
695+ self ._update_error_count (tool_name , error_state )
696+ if error_state .get ("tool_error_count" , 0 ) >= MAX_CONSECUTIVE_TOOL_ERRORS :
697+ task .comment (f"同じツール({ tool_name } )で3回連続エラーが発生したため処理を中止します。" )
698+ return True
699+
630700 if "call_tool" in data :
631701 # ツール呼び出し処理
632702 for tool_call in data ["call_tool" ]:
0 commit comments