// ============================================================ // N8N CODE NODE: Объединение результатов AI Agent и Regex // ============================================================ // // INPUT: Массив из 34 элементов // - Первые 17: результаты от AI Agent // - Последние 17: результаты от Regex // // OUTPUT: Объединённые результаты с итоговой оценкой // ============================================================ // Определяем 17 критериев const CRITERIA = [ { id: 1, name: "Юридическая идентификация и верификация", description: "ИНН, ОГРН, полное наименование организации" }, { id: 2, name: "Адрес", description: "Юридический и фактический адрес, местонахождение" }, { id: 3, name: "Контакты", description: "Телефон, email, форма обратной связи" }, { id: 4, name: "Режим работы", description: "Часы работы, график приема, колл-центр" }, { id: 5, name: "Политика ПДн (152-ФЗ)", description: "Политика персональных данных, обработка ПДн" }, { id: 7, name: "Договор-оферта / Правила оказания услуг", description: "Публичная оферта, пользовательское соглашение" }, { id: 8, name: "Рекламации и споры", description: "Претензии, возврат, обмен, жалобы" }, { id: 9, name: "Цены/прайс", description: "Цены, стоимость, тарифы" }, { id: 10, name: "Способы оплаты", description: "Наличные, карта, СБП" }, { id: 11, name: "Онлайн-оплата", description: "Эквайринг, оплата онлайн" }, { id: 12, name: "Онлайн-бронирование", description: "Забронировать, booking" }, { id: 13, name: "FAQ", description: "Частые вопросы, вопрос-ответ" }, { id: 14, name: "Доступность для ЛОВЗ", description: "Инвалиды, безбарьерная среда" }, { id: 15, name: "Партнёры/бренды", description: "Партнеры, поставщики, сотрудничество" }, { id: 16, name: "Команда/сотрудники", description: "Команда, персонал, руководство" }, { id: 17, name: "Уголок потребителя", description: "Права потребителей, защита" }, { id: 18, name: "Актуальность документов", description: "Дата обновления, версия" } ]; /** * Рассчитывает итоговую уверенность */ function calculateFinalConfidence(aiConf, regexConf, aiFound, regexFound) { // Если оба нашли - очень высокая if (aiFound && regexFound) { return "Очень высокая"; } // Если один нашёл с высокой уверенностью if ((aiFound && aiConf === "Высокая") || (regexFound && regexConf === "Высокая")) { return "Высокая"; } // Если один нашёл со средней уверенностью if ((aiFound && aiConf === "Средняя") || (regexFound && regexConf === "Средняя")) { return "Средняя"; } // Если оба не нашли с высокой уверенностью - точно нет if (!aiFound && !regexFound && aiConf === "Высокая" && regexConf === "Высокая") { return "Высокая (не найдено)"; } // Иначе - низкая return "Низкая"; } /** * Объединяет результаты AI и Regex */ function mergeResults(allResults) { // Разделяем на AI (первые 17) и Regex (последние 17) const aiResults = allResults.slice(0, 17); const regexResults = allResults.slice(17, 34); const merged = []; for (let i = 0; i < CRITERIA.length; i++) { const criterion = CRITERIA[i]; // AI результаты const aiItem = aiResults[i] || {}; const aiOutput = aiItem.output || {}; const aiFound = aiOutput.found || false; const aiScore = aiOutput.score || 0; const aiQuote = aiOutput.quote || ''; const aiUrl = aiOutput.url || ''; const aiDetails = aiOutput.details || ''; const aiConfidence = aiOutput.confidence || 'Не определена'; const aiCheckedPages = aiOutput.checked_pages || 0; // Regex результаты const regexItem = regexResults[i] || {}; const regexOutput = regexItem.output || {}; const regexFound = regexOutput.found || false; const regexAnswer = regexOutput.answer || 'НЕТ'; const regexExtracted = regexOutput.extracted || ''; const regexConfidence = regexOutput.confidence || 'Не определена'; // Итоговый результат const found = aiFound || regexFound; const finalScore = Math.max(aiScore, regexFound ? 1 : 0); const finalConfidence = calculateFinalConfidence(aiConfidence, regexConfidence, aiFound, regexFound); // Собираем объединённый результат const mergedItem = { criterion_id: criterion.id, criterion_name: criterion.name, criterion_description: criterion.description, // Общий результат found: found, status: found ? "НАЙДЕНО" : "НЕ НАЙДЕНО", score: finalScore, final_confidence: finalConfidence, // AI Agent результаты ai_agent: { found: aiFound, score: aiScore, quote: aiQuote, url: aiUrl, details: aiDetails, confidence: aiConfidence, checked_pages: aiCheckedPages }, // Regex результаты regex: { found: regexFound, answer: regexAnswer, extracted: regexExtracted, confidence: regexConfidence } }; merged.push(mergedItem); } return merged; } /** * Формирует итоговую сводку */ function formatSummary(mergedResults, hotelName, region) { const total = mergedResults.length; const foundCount = mergedResults.filter(r => r.found).length; const notFoundCount = total - foundCount; const compliancePercentage = Math.round((foundCount / total) * 100 * 10) / 10; return { hotel_name: hotelName || "Не указано", region: region || "Не указано", audit_date: new Date().toISOString().split('T')[0], total_criteria: total, found: foundCount, not_found: notFoundCount, compliance_percentage: compliancePercentage, criteria_results: mergedResults }; } // ============================================================ // ГЛАВНЫЙ КОД // ============================================================ // Получаем входные данные const inputData = $input.all(); // Извлекаем массив результатов let allResults = []; if (Array.isArray(inputData) && inputData.length > 0) { // Вариант 1: Aggregate вернул один item с массивом внутри if (inputData.length === 1 && inputData[0].json && Array.isArray(inputData[0].json)) { allResults = inputData[0].json; } // Вариант 2: Aggregate вернул один item с полем data (массив) else if (inputData.length === 1 && inputData[0].json && Array.isArray(inputData[0].json.data)) { allResults = inputData[0].json.data; } // Вариант 3: Пришло 34 отдельных items (без Aggregate) else if (inputData.length === 34) { allResults = inputData.map(item => item.json || item); } // Вариант 4: Пришло много items, берём все else { allResults = inputData.map(item => item.json || item); } } else { throw new Error('Неверный формат входных данных. Ожидается массив из 34 элементов.'); } // Отладочная информация console.log(`📊 Получено элементов: ${allResults.length}`); console.log(`📦 Формат входных данных: ${inputData.length} items`); // Проверяем количество if (allResults.length !== 34) { console.log(`⚠️ Предупреждение: получено ${allResults.length} элементов вместо 34`); console.log(`Первый элемент:`, JSON.stringify(allResults[0], null, 2).substring(0, 200)); } // Объединяем результаты const mergedResults = mergeResults(allResults); // Получаем данные об отеле из первого элемента или workflow let hotelName = "Неизвестный отель"; let region = "Неизвестный регион"; try { // Пытаемся получить из первого input item const firstItem = $input.first().json; hotelName = firstItem.hotel_name || hotelName; region = firstItem.region || region; } catch (e) { // Если не получилось, используем значения по умолчанию console.log('Не удалось получить hotel_name и region из input'); } // Формируем итоговую сводку const summary = formatSummary(mergedResults, hotelName, region); // Возвращаем результат return [{ json: summary }]; // ============================================================ // ПРИМЕЧАНИЯ: // ============================================================ // 1. Входные данные должны быть массивом из 34 элементов // 2. Первые 17 - от AI Agent (с детальными ответами) // 3. Последние 17 - от Regex (с простыми ДА/НЕТ) // 4. На выходе - объединённый результат с итоговой оценкой // ============================================================