$model, 'max_tokens' => $max_tokens, 'system' => $system_prompt, 'messages' => [ [ 'role' => 'user', 'content' => $user_prompt ] ] ]; $ch = curl_init( self::$api_url ); curl_setopt_array( $ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'x-api-key: ' . $api_key, 'anthropic-version: 2023-06-01' ], CURLOPT_POSTFIELDS => json_encode( $payload ), CURLOPT_TIMEOUT => 60 ] ); $response = curl_exec( $ch ); $http_code = curl_getinfo( $ch, CURLINFO_HTTP_CODE ); curl_close( $ch ); if ( $http_code !== 200 ) { $error = json_decode( $response, true ); return [ 'status' => 'error', 'message' => $error['error']['message'] ?? 'Błąd API Claude (HTTP ' . $http_code . ')' ]; } $data = json_decode( $response, true ); $content = ''; if ( !empty( $data['content'] ) ) { foreach ( $data['content'] as $block ) { if ( $block['type'] === 'text' ) { $content .= $block['text']; } } } $content = trim( $content ); return [ 'status' => 'ok', 'suggestion' => $content ]; } static private function build_context_text( $context ) { $lines = []; $lines[] = 'Nazwa produktu: ' . ( $context['original_name'] ?? '—' ); if ( !empty( $context['current_title'] ) ) $lines[] = 'Obecny tytuł (custom): ' . $context['current_title']; if ( !empty( $context['current_description'] ) ) $lines[] = 'Obecny opis: ' . $context['current_description']; if ( !empty( $context['current_category'] ) ) $lines[] = 'Obecna kategoria Google: ' . $context['current_category']; if ( !empty( $context['offer_id'] ) ) $lines[] = 'ID oferty: ' . $context['offer_id']; if ( !empty( $context['custom_label_4'] ) ) $lines[] = 'Status produktu: ' . $context['custom_label_4']; $lines[] = ''; $lines[] = 'Metryki reklamowe (ostatnie 30 dni):'; $lines[] = '- Wyświetlenia: ' . ( $context['impressions_30'] ?? 0 ); $lines[] = '- Kliknięcia: ' . ( $context['clicks_30'] ?? 0 ); $lines[] = '- CTR: ' . ( $context['ctr'] ?? 0 ) . '%'; $lines[] = '- Koszt: ' . ( $context['cost'] ?? 0 ) . ' PLN'; $lines[] = '- Konwersje: ' . ( $context['conversions'] ?? 0 ); $lines[] = '- Wartość konwersji: ' . ( $context['conversions_value'] ?? 0 ) . ' PLN'; $lines[] = '- ROAS: ' . ( $context['roas'] ?? 0 ) . '%'; if ( !empty( $context['page_content'] ) ) { $lines[] = ''; $lines[] = 'Treść ze strony produktu (użyj tych informacji do stworzenia dokładniejszego opisu):'; $lines[] = $context['page_content']; } return implode( "\n", $lines ); } static private function get_prompt_template( $setting_key, $default_template ) { $template = trim( (string) GoogleAdsApi::get_setting( $setting_key ) ); if ( $template === '' ) { return $default_template; } return $template; } static private function expand_prompt_template( $template, $vars ) { $result = (string) $template; foreach ( (array) $vars as $key => $value ) { $result = str_replace( '{{' . $key . '}}', (string) $value, $result ); } return $result; } static public function suggest_title( $context ) { $context_text = self::build_context_text( $context ); $keyword_planner_text = ''; if ( !empty( $context['keyword_planner_terms'] ) && is_array( $context['keyword_planner_terms'] ) ) { $keyword_lines = []; $keyword_lines[] = 'Najpopularniejsze frazy z Google Ads Keyword Planner (na bazie URL produktu, posortowane malejąco po średniej liczbie wyszukiwań):'; foreach ( array_slice( $context['keyword_planner_terms'], 0, 15 ) as $term ) { $text = trim( (string) ( $term['keyword_text'] ?? '' ) ); if ( $text === '' ) { continue; } $avg_monthly = (int) ( $term['avg_monthly_searches'] ?? 0 ); $keyword_lines[] = '- ' . $text . ' (avg miesięcznie: ' . $avg_monthly . ')'; } if ( count( $keyword_lines ) > 1 ) { $keyword_lines[] = 'Użyj tych fraz WYBIÓRCZO i naturalnie (bez upychania słów kluczowych), tylko jeśli pasują do produktu.'; $keyword_planner_text = "\n\n" . implode( "\n", $keyword_lines ); } } $prompt_template = self::get_prompt_template( 'ai_prompt_title_template', OpenAiApi::get_default_title_prompt_template() ); if ( strpos( $prompt_template, '{{context}}' ) === false ) { $prompt_template .= "\n\n{{context}}"; } if ( strpos( $prompt_template, '{{keyword_terms}}' ) === false ) { $prompt_template .= "{{keyword_terms}}"; } $prompt = self::expand_prompt_template( $prompt_template, [ 'context' => $context_text, 'keyword_terms' => $keyword_planner_text ] ); $prompt .= "\n\nWygeneruj 3 różne warianty tytułu (A/B/C). Zwróć WYŁĄCZNIE poprawny JSON: {\"titles\":[\"wariant A\",\"wariant B\",\"wariant C\"]}."; $result = self::call_api( self::$system_prompt, $prompt, 1200 ); if ( ( $result['status'] ?? '' ) !== 'ok' ) { return $result; } $candidates = OpenAiApi::parse_title_candidates( (string) ( $result['suggestion'] ?? '' ) ); $best_title = OpenAiApi::pick_best_title_candidate( $candidates, $context ); if ( $best_title !== '' ) { $result['suggestion'] = $best_title; $result['title_candidates'] = $candidates; } return $result; } static public function suggest_description( $context ) { $context_text = self::build_context_text( $context ); $has_page = !empty( $context['page_content'] ); $keyword_planner_text = ''; if ( !empty( $context['keyword_planner_terms'] ) && is_array( $context['keyword_planner_terms'] ) ) { $keyword_lines = []; $keyword_lines[] = 'Najpopularniejsze frazy z Google Ads Keyword Planner (na bazie URL produktu, posortowane malejąco po średniej liczbie wyszukiwań):'; foreach ( array_slice( $context['keyword_planner_terms'], 0, 15 ) as $term ) { $text = trim( (string) ( $term['keyword_text'] ?? '' ) ); if ( $text === '' ) { continue; } $avg_monthly = (int) ( $term['avg_monthly_searches'] ?? 0 ); $keyword_lines[] = '- ' . $text . ' (avg miesięcznie: ' . $avg_monthly . ')'; } if ( count( $keyword_lines ) > 1 ) { $keyword_lines[] = 'W opisie wykorzystuj te frazy naturalnie i wyłącznie gdy realnie pasują do produktu (bez keyword stuffing).'; $keyword_planner_text = "\n\n" . implode( "\n", $keyword_lines ); } } $length_guide = $has_page ? '- Napisz rozbudowany, szczegółowy opis: ok. 1000 znaków (800-1200) - Wykorzystaj szczegóły ze strony produktu: skład zestawu, materiały, wymiary, kolory, przeznaczenie - Każdy akapit/punkt powinien wnosić NOWĄ informację — NIE powtarzaj tych samych elementów' : '- Napisz opis o długości ok. 1000 znaków (800-1200) — jeśli brak szczegółów, opisz ogólnie zastosowanie i grupę docelową - Opisuj TYLKO to co wynika z oryginalnej nazwy — nie wymyślaj konkretnych parametrów'; $prompt_template = self::get_prompt_template( 'ai_prompt_description_template', OpenAiApi::get_default_description_prompt_template() ); if ( strpos( $prompt_template, '{{length_guide}}' ) === false ) { $prompt_template .= "\n\n{{length_guide}}"; } if ( strpos( $prompt_template, '{{context}}' ) === false ) { $prompt_template .= "\n\n{{context}}"; } if ( strpos( $prompt_template, '{{keyword_terms}}' ) === false ) { $prompt_template .= "{{keyword_terms}}"; } $prompt = self::expand_prompt_template( $prompt_template, [ 'length_guide' => $length_guide, 'context' => $context_text, 'keyword_terms' => $keyword_planner_text ] ); $tokens = $has_page ? 1500 : 1000; return self::call_api( self::$system_prompt, $prompt, $tokens ); } static public function suggest_category( $context, $categories = [] ) { $context_text = self::build_context_text( $context ); $cats_text = ''; if ( !empty( $categories ) ) { $cats_list = array_slice( $categories, 0, 50 ); $cats_text = "\n\nDostępne kategorie Google Product Taxonomy (format: id - tekst):\n"; foreach ( $cats_list as $cat ) { $cats_text .= $cat['id'] . ' - ' . $cat['text'] . "\n"; } } $prompt = 'Dopasuj produkt do najlepszej kategorii Google Product Taxonomy. Wybierz najbardziej szczegółową (najgłębszą) kategorię pasującą do produktu. ' . $context_text . $cats_text . ' Zwróć TYLKO ID kategorii (liczbę), bez wyjaśnień.'; return self::call_api( self::$system_prompt, $prompt ); } }