codeContextService->getRepoPath($appName); if (!$repoPath) { return [ 'success' => false, 'error' => "未配置项目路径: {$appName}", ]; } $tool = $this->getConfiguredTool(); try { $prompt = $this->buildPrompt($logsContent, $aiSummary); $output = $this->runTool($tool, $repoPath, $prompt); return [ 'success' => true, 'output' => $output, 'repo_path' => $repoPath, 'tool' => $tool, ]; } catch (ProcessFailedException $e) { Log::error("{$tool} execution failed", [ 'app_name' => $appName, 'repo_path' => $repoPath, 'tool' => $tool, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => "{$tool} 执行失败: " . $e->getProcess()->getErrorOutput(), 'tool' => $tool, ]; } catch (\Exception $e) { Log::error("Code analysis failed", [ 'app_name' => $appName, 'repo_path' => $repoPath, 'tool' => $tool, 'error' => $e->getMessage(), ]); return [ 'success' => false, 'error' => $e->getMessage(), 'tool' => $tool, ]; } } /** * 获取配置的分析工具 */ public function getConfiguredTool(): string { $tool = $this->configService->get('log_analysis.code_analysis_tool', self::TOOL_CLAUDE); if (!in_array($tool, [self::TOOL_CLAUDE, self::TOOL_CODEX])) { return self::TOOL_CLAUDE; } return $tool; } /** * 设置分析工具 */ public function setTool(string $tool): void { if (!in_array($tool, [self::TOOL_CLAUDE, self::TOOL_CODEX])) { throw new \InvalidArgumentException("不支持的工具: {$tool}"); } $this->configService->set( 'log_analysis.code_analysis_tool', $tool, '代码分析工具 (claude/codex)' ); } /** * 获取可用的工具列表 */ public function getAvailableTools(): array { return [ self::TOOL_CLAUDE => [ 'name' => 'Claude CLI', 'description' => 'Anthropic Claude 命令行工具', ], self::TOOL_CODEX => [ 'name' => 'Codex CLI', 'description' => 'OpenAI Codex 命令行工具', ], ]; } /** * 构建提示词 */ private function buildPrompt(string $logsContent, ?string $aiSummary): string { $prompt = "分析以下错误日志,在代码库中排查根本原因并给出具体优化方案:\n\n"; $prompt .= "=== 日志内容 ===\n{$logsContent}\n\n"; if ($aiSummary) { $prompt .= "=== AI 初步分析 ===\n{$aiSummary}\n\n"; } $prompt .= "请:\n"; $prompt .= "1. 定位相关代码文件\n"; $prompt .= "2. 分析根本原因\n"; $prompt .= "3. 给出具体修复方案\n"; return $prompt; } /** * 执行分析工具 */ private function runTool(string $tool, string $workingDirectory, string $prompt): string { return match ($tool) { self::TOOL_CODEX => $this->runCodex($workingDirectory, $prompt), default => $this->runClaude($workingDirectory, $prompt), }; } /** * 执行 Claude CLI 命令 */ private function runClaude(string $workingDirectory, string $prompt): string { $process = new Process( ['claude', '--print', $prompt], $workingDirectory ); $process->setTimeout($this->timeout); $process->mustRun(); return trim($process->getOutput()); } /** * 执行 Codex CLI 命令 */ private function runCodex(string $workingDirectory, string $prompt): string { $process = new Process( ['codex', '--quiet', '--full-auto', $prompt], $workingDirectory ); $process->setTimeout($this->timeout); $process->mustRun(); return trim($process->getOutput()); } /** * 设置超时时间 */ public function setTimeout(int $timeout): void { $this->timeout = $timeout; } }