# -*- coding: utf-8 -*-
"""
概念提取器 - 基于NER.py的三阶段提取系统
Extraction → Validation (Self-Refine) → Integration
支持多线程并发处理，提升处理速度
"""

import re
import os
import json
import time
import requests
from typing import List, Dict, Set, Tuple
from collections import Counter
from concurrent.futures import ThreadPoolExecutor, as_completed
import threading

class ConceptExtractor:
    """
    概念提取器：使用LLM从字幕和PDF中提取关键概念
    采用三阶段提取流程：提取、验证、整合
    """
    
    def __init__(self, subject="生物学", api_key=None, base_url=None, model=None):
        """
        初始化概念提取器（使用与subtitle_processor相同的API配置）
        
        Args:
            subject: 学科领域（默认：生物学）
            api_key: API密钥（默认使用subtitle_processor相同配置）
            base_url: API基础URL（默认使用subtitle_processor相同配置）
            model: 模型名称（默认：gpt-4o-mini）
        """
        self.subject = subject
        # 与subtitle_processor.py保持一致的API配置
        # CHATFIRE_API_BASE = "https://api.chatfire.cn/v1"
        # CHATFIRE_API_KEY = "sk-QwXT3QeorfDPerfxs2Q76i9yDoKvOOG90Q135BNKtYXB3yE2"
        self.api_key = api_key or os.environ.get('SILICONFLOW_API_KEY', 'sk-5w2FNpdHmV4xoi07RAe2lWiqXGgvRWCbmCk9o8T39hZBhJIc')
        self.base_url = base_url or os.environ.get('SILICONFLOW_BASE_URL', 'https://jeniya.cn/')
        self.model = model or "gpt-4o-mini"  # 使用稳定的模型
        
        # 请求头配置（与subtitle_processor保持一致）
        self.headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}"
        }
        
        # 全局实体集合（跨时间段去重）
        self.all_entity = set()
        self.final_entity = set()
        
        # 线程锁（保护共享资源）
        self.entity_lock = threading.Lock()
        self.final_entity_lock = threading.Lock()
        
        # 多线程配置
        self.max_workers = 5  # 最大并发线程数
        
        print(f"📚 概念提取器初始化（NER从视频字幕提取 + 多线程并发）")
        print(f"   学科: {self.subject}")
        print(f"   API提供商: {self.base_url}")
        print(f"   模型: {self.model}")
        print(f"   数据来源: 视频字幕 (Whisper转录)")
        print(f"   🚀 最大并发线程数: {self.max_workers}")

    def update_config(self, api_key=None, base_url=None, model=None):
        """
        更新LLM配置
        
        Args:
            api_key: API密钥
            base_url: API基础URL
            model: 模型名称
        """
        if api_key:
            self.api_key = api_key
        if base_url:
            self.base_url = base_url
        if model:
            self.model = model
            
        # 更新请求头
        self.headers = {
            "Content-Type": "application/json",
            "Authorization": f"Bearer {self.api_key}"
        }
        
        print(f"🔄 概念提取器配置已更新:")
        if base_url: print(f"   API提供商: {self.base_url}")
        if model: print(f"   模型: {self.model}")
    
    def extract_concepts(self, subtitle_data, pdf_text=None, process_id=None, k=3):
        """
        从视频字幕中提取概念（主入口）
        
        Args:
            subtitle_data: 视频字幕数据（来自Whisper转录和语义分段）
                          格式: [{'timestamp': '00:00-00:18', 'text': '内容'}, ...]
            pdf_text: PDF文本内容（可选，暂不使用）
            process_id: 处理ID
            k: Self-Refine迭代次数（默认3）
            
        Returns:
            dict: 提取的概念数据
        """
        print(f"\n{'='*80}")
        print(f"🔬 开始三阶段NER概念提取（从视频字幕）")
        print(f"{'='*80}")
        print(f"   数据来源: 视频字幕（Whisper转录 → 语义分段）")
        print(f"   API提供商: {self.base_url}")
        
        try:
            # 阶段1: Extraction（提取）
            print(f"\n📌 阶段1: 概念提取 (Extraction)")
            print("-" * 60)
            extraction_results = self._extraction_stage(subtitle_data, process_id)
            
            if not extraction_results:
                print("   ⚠️  提取阶段未获得任何概念")
                return {'concepts': [], 'total': 0}
            
            # 阶段2: Validation（验证 - Self-Refine）
            print(f"\n📌 阶段2: 概念验证 (Self-Refine Validation)")
            print("-" * 60)
            validation_results = self._validation_stage(extraction_results, k, process_id)
            
            # 阶段3: Integration（整合）
            print(f"\n📌 阶段3: 概念整合 (Integration)")
            print("-" * 60)
            final_concepts = self._integration_stage(validation_results, k, process_id)
            
            # 格式化最终结果
            formatted_concepts = self._format_final_concepts(final_concepts)
            
            print(f"\n{'='*80}")
            print(f"✅ 概念提取完成！共提取 {len(formatted_concepts)} 个概念")
            print(f"{'='*80}")
            
            return {
                'concepts': formatted_concepts,
                'total': len(formatted_concepts),
                'subject': self.subject
            }
            
        except Exception as e:
            print(f"❌ 概念提取失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'concepts': [], 'total': 0, 'error': str(e)}
    
    def _extraction_stage(self, subtitle_data, process_id):
        """
        阶段1: 提取 - 从视频字幕数据中提取候选概念（支持多线程）
        
        Args:
            subtitle_data: 视频字幕数据（来自Whisper转录）
            
        Returns:
            List[timestamp, entities_set, sc_score, prompt, max]
        """
        results = []
        
        print(f"   📹 数据来源: 视频字幕 (Whisper语音转文本)")
        print(f"   🚀 使用多线程并发处理（{self.max_workers} 个线程）")
        
        # 提取Prompt（参考NER.py）
        extraction_prompt = f"""
请分析以下视频字幕文本：
a) 识别所有名词/名词短语
b) 过滤并仅保留与{self.subject}相关的概念
c) 严格输出最终的{self.subject}概念（简体中文）
d) 使用以下格式输出单一列表: -xxx\\n-xxx\\n-xxx
e) 不要添加标题/解释/其他文字
f) 不要透露非学科名词或中间分析过程

输出格式示例:
-细胞
-线粒体
-光合作用
"""
        
        # 处理字幕数据
        if isinstance(subtitle_data, str):
            # 如果是文件路径
            if os.path.exists(subtitle_data):
                with open(subtitle_data, 'r', encoding='utf-8') as f:
                    lines = f.readlines()
            else:
                # 如果是文本内容
                lines = subtitle_data.split('\n')
        elif isinstance(subtitle_data, list):
            # 如果是列表
            lines = [f'"{item.get("timestamp", "")}": "{item.get("text", "")}"' for item in subtitle_data]
        else:
            print("   ⚠️  不支持的字幕数据格式")
            return results
        
        print(f"   📝 处理 {len(lines)} 行字幕数据")
        
        # 准备任务列表
        tasks = []
        for idx, line in enumerate(lines):
            if not line.strip():
                continue
            tasks.append((idx, line, extraction_prompt))
        
        print(f"   📋 准备 {len(tasks)} 个提取任务")
        
        # 使用线程池并发处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交所有任务
            future_to_task = {
                executor.submit(self._extract_single_segment, task): task 
                for task in tasks
            }
            
            # 收集结果
            completed = 0
            for future in as_completed(future_to_task):
                completed += 1
                task = future_to_task[future]
                idx = task[0]
                
                try:
                    result = future.result()
                    if result:
                        results.append(result)
                        print(f"   ✅ [{completed}/{len(tasks)}] 片段 {idx+1} 完成")
                    else:
                        print(f"   ⏭️  [{completed}/{len(tasks)}] 片段 {idx+1} 跳过")
                        
                except Exception as e:
                    print(f"   ❌ [{completed}/{len(tasks)}] 片段 {idx+1} 失败: {str(e)}")
        
        # 保存提取结果
        if process_id:
            self._save_extraction_results(results, process_id)
        
        print(f"\n   ✅ 提取阶段完成，共 {len(results)} 个时间段，{len(self.all_entity)} 个唯一概念")
        return results
    
    def _extract_single_segment(self, task):
        """
        提取单个字幕片段的概念（线程安全）
        
        Args:
            task: (idx, line, extraction_prompt)
            
        Returns:
            [timestamp, entities_set, sc_score, prompt, max] 或 None
        """
        idx, line, extraction_prompt = task
        
        try:
            # 解析时间戳和内容
            if '": "' in line:
                timestamp, content = line.strip().strip('"').split('": "', 1)
                content = content.strip('"')
            else:
                timestamp = f"segment_{idx}"
                content = line.strip()
            
            if not content or len(content) < 5:
                return None
            
            # 调用LLM提取概念
            llm_response = self._call_llm(content + "\n\n" + extraction_prompt)
            
            if not llm_response:
                return None
            
            # 解析概念
            entities = re.findall(r"-\s*(\w+)", llm_response)
            
            if not entities:
                return None
            
            # 去重：仅保留新概念（使用锁保护共享资源）
            entity_time = set()
            entity_set = set()
            
            with self.entity_lock:
                for entity in entities:
                    if entity not in self.all_entity:
                        self.all_entity.add(entity)
                        entity_time.add(entity)
                        entity_set.add(entity)
            
            if entity_time:
                # 初始化SC-Score
                sc_score = [1] * len(entity_time)
                max_score = [1] * len(entity_time)
                
                return [timestamp, entity_time, sc_score, "", max_score]
            else:
                return None
                
        except Exception as e:
            return None
    
    def _validation_stage(self, candidates, k, process_id):
        """
        阶段2: 验证 - 使用Self-Refine机制验证概念（支持多线程）
        
        Args:
            candidates: [[timestamp, entities_set, sc_score, prompt, max], ...]
            k: 迭代次数
            
        Returns:
            List[timestamp, entities_set, sc_score, prompt, max]
        """
        results = []
        
        print(f"   🚀 使用多线程并发验证（{self.max_workers} 个线程）")
        
        # Self-Refine的反馈Prompt
        feedback_prompt = f"""
分析提取的K-12 {self.subject}概念：
a) 不要修改或拆分原始术语
b) 识别具有跨学科歧义的术语（即在其他学科领域有不同含义的术语）
c) 对于每个歧义术语，提供一句话说明其在其他学科的潜在含义
d) 严格按照以下格式输出: "歧义术语"："原因"
e) 仅输出符合此格式的行，不添加其他文字
f) 保持原始词汇结构

示例分析模式:
"系统"："在计算机科学中可能指计算机系统"
"能量"："在物理学中有特定定义"
"""
        
        # 精炼Prompt模板
        refine_prompt_template = f"""
K-12 {self.subject} 概念过滤协议

反馈分析:
{{feedback}}
（注意：反馈分析可能包含错误）
（每个反馈项都清楚地表明哪些术语超出范围或在K-12 {self.subject}方面存在歧义。
例如，如果"酒精"的反馈表明化学背景而非{self.subject}，则必须删除"酒精"。）

候选概念池:
{{candidates}}

处理流程:
1. 使用反馈分析进行初步修剪，删除与K-12 {self.subject}不直接相关的术语
2. 二次过滤：应用K-12 {self.subject}标准，确保所有剩余术语本质上是{self.subject}概念，而不是仪器、设备或工具
   （例如，虽然显微镜、物镜、临时玻片和照明组件用于{self.subject}，但它们本质上是物理或工程工具，应该被删除）
3. 消除冗余和非{self.subject}术语，同时保持原始顺序

输出规范:
严格使用Markdown列表格式输出，每个术语单独一行:
-概念1
-概念2
-概念3
保持有效条目的原始顺序
仅输出列表，不添加其他文字

过滤结果:
"""
        
        # 准备任务列表
        tasks = [
            (candidate, feedback_prompt, refine_prompt_template, k)
            for candidate in candidates
            if len(candidate[1]) > 0  # 过滤空候选
        ]
        
        print(f"   📋 准备 {len(tasks)} 个验证任务")
        
        # 使用线程池并发处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_task = {
                executor.submit(self._validate_single_candidate, task): task
                for task in tasks
            }
            
            completed = 0
            for future in as_completed(future_to_task):
                completed += 1
                try:
                    result = future.result()
                    if result:
                        results.append(result)
                        print(f"   ✅ [{completed}/{len(tasks)}] 验证完成")
                except Exception as e:
                    print(f"   ❌ [{completed}/{len(tasks)}] 验证失败: {str(e)}")
        
        # 保存验证结果
        if process_id:
            self._save_validation_results(results, process_id)
        
        print(f"\n   ✅ 验证阶段完成")
        return results
    
    def _validate_single_candidate(self, task):
        """
        验证单个候选概念集（线程安全）
        
        Args:
            task: (candidate, feedback_prompt, refine_prompt_template, k)
            
        Returns:
            [timestamp, entities_set, sc_score, prompt, max] 或 None
        """
        candidate, feedback_prompt, refine_prompt_template, k = task
        timestamp, entities_set, sc_score, prompt, max_scores = candidate
        
        try:
            candidate_list = list(entities_set)
            accumulated_feedback = ""
            
            # Self-Refine迭代
            for iteration in range(k):
                # 步骤1: 获取反馈
                feedback_input = str(candidate_list) + "\n\n" + feedback_prompt
                feedback = self._call_llm(feedback_input)
                
                if not feedback:
                    continue
                
                accumulated_feedback += feedback + "\n"
                
                # 步骤2: 基于反馈精炼
                refine_input = refine_prompt_template.format(
                    feedback=feedback,
                    candidates=str(candidate_list)
                )
                
                refined_response = self._call_llm(refine_input)
                
                if not refined_response:
                    continue
                
                # 解析精炼后的概念
                refined_entities = re.findall(r"-\s*(\w+)", refined_response)
                refined_set = set(refined_entities)
                
                # 更新SC-Score
                for idx, entity in enumerate(entities_set):
                    if entity in refined_set:
                        sc_score[idx] += 1
            
            return [timestamp, entities_set, sc_score, accumulated_feedback, max_scores]
            
        except Exception as e:
            return None
    
    def _integration_stage(self, candidates, k, process_id):
        """
        阶段3: 整合 - 基于SC-Score整合最终概念
        
        Args:
            candidates: [[timestamp, entities_set, sc_score, prompt, max], ...]
            k: 迭代次数
            
        Returns:
            List[timestamp, final_entities]
        """
        results = []
        
        # SC-Score阈值（参考NER.py）
        sc_threshold = 0.6
        
        for timestamp, entities_set, sc_score, prompt, max_scores in candidates:
            final_entities = set()
            
            entities_list = list(entities_set)
            
            for idx, entity in enumerate(entities_list):
                # 条件1: SC-Score > 阈值
                # SC_Score计算: (1 / k) * k * sc_score[idx] = sc_score[idx]
                # 归一化: sc_score[idx] / (k + 1) 因为初始值为1
                normalized_score = sc_score[idx] / (k + 1)
                
                if normalized_score > sc_threshold:
                    final_entities.add(entity)
                    self.final_entity.add(entity)
                
                # 条件2: 在所有迭代中都被保留
                # max_scores[idx] == k 表示在k次迭代中都出现
                # 注意：这里简化处理，因为当前实现中max_scores未更新
            
            if final_entities:
                results.append([timestamp, final_entities])
                print(f"   ✅ {timestamp}: {len(final_entities)} 个最终概念")
        
        # 保存整合结果
        if process_id:
            self._save_integration_results(results, process_id)
        
        print(f"\n   ✅ 整合阶段完成，共 {len(self.final_entity)} 个最终概念")
        return results
    
    def _format_final_concepts(self, integration_results):
        """
        格式化最终概念，统计频率并保留时间戳信息
        
        Returns:
            list: [{'term': '细胞', 'frequency': 3, 'timestamps': ['00:00-00:18', ...]}, ...]
        """
        concept_freq = Counter()
        concept_timestamps = {}  # 保留每个概念对应的时间戳列表
        
        for timestamp, entities in integration_results:
            for entity in entities:
                concept_freq[entity] += 1
                
                # 记录时间戳
                if entity not in concept_timestamps:
                    concept_timestamps[entity] = []
                concept_timestamps[entity].append(timestamp)
        
        # 格式化为前端需要的格式
        formatted = []
        for term, freq in concept_freq.most_common():
            formatted.append({
                'term': term,
                'frequency': freq,
                'importance': freq,
                'timestamps': concept_timestamps.get(term, [])  # 添加时间戳列表
            })
        
        return formatted
    
    def generate_explanations(self, concepts_list, process_id=None):
        """
        为概念生成解释（支持多线程并发）
        
        Args:
            concepts_list: 概念列表 [{'term': '细胞', 'frequency': 3}, ...]
            process_id: 处理ID
            
        Returns:
            list: [{'term': '细胞', 'explanation': '生物体结构和功能的基本单位'}, ...]
        """
        print(f"\n{'='*80}")
        print(f"📖 为概念生成解释（多线程并发）")
        print(f"{'='*80}")
        print(f"   概念数量: {len(concepts_list)}")
        print(f"   学科: {self.subject}")
        print(f"   🚀 使用 {self.max_workers} 个线程并发处理")
        
        explanations = []
        
        # 准备任务列表
        tasks = [
            (idx, concept)
            for idx, concept in enumerate(concepts_list)
            if concept.get('term', '')
        ]
        
        print(f"   📋 准备 {len(tasks)} 个解释生成任务")
        
        # 使用线程池并发处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_task = {
                executor.submit(self._generate_single_explanation, task): task
                for task in tasks
            }
            
            completed = 0
            for future in as_completed(future_to_task):
                completed += 1
                task = future_to_task[future]
                idx = task[0]
                
                try:
                    result = future.result()
                    if result:
                        explanations.append(result)
                        print(f"   ✅ [{completed}/{len(tasks)}] {result['term']}: {result['explanation']}")
                    else:
                        print(f"   ⚠️  [{completed}/{len(tasks)}] 生成失败")
                except Exception as e:
                    print(f"   ❌ [{completed}/{len(tasks)}] 错误: {str(e)}")
        
        # 保存解释到文件
        if process_id:
            self._save_explanations(explanations, process_id)
        
        print(f"\n   ✅ 解释生成完成，共 {len(explanations)} 个概念")
        return explanations
    
    def _generate_single_explanation(self, task):
        """
        为单个概念生成解释（线程安全）
        
        Args:
            task: (idx, concept)
            
        Returns:
            dict 或 None
        """
        idx, concept = task
        term = concept.get('term', '')
        
        if not term:
            return None
        
        try:
            # 使用LLM生成解释
            explanation = self._generate_explanation_with_llm(term)
            
            if explanation:
                return {
                    'term': term,
                    'explanation': explanation,
                    'frequency': concept.get('frequency', 1)
                }
            else:
                return {
                    'term': term,
                    'explanation': '暂无解释',
                    'frequency': concept.get('frequency', 1)
                }
                
        except Exception as e:
            return {
                'term': term,
                'explanation': '暂无解释',
                'frequency': concept.get('frequency', 1)
            }
    
    def _generate_explanation_with_llm(self, term):
        """
        使用LLM为单个概念生成解释（参考 NER.py）
        
        Args:
            term: 概念术语
            
        Returns:
            str: 解释文本
        """
        # 参考 NER.py 的 expandExplanations 函数
        prompt = f"""
你需要为一个{self.subject}学科概念生成简洁的中文解释。要求：
1. 严格限制在20字以内
2. 使用简单语言，优先考虑准确性
3. 避免专业术语，除非必要
4. 仅输出最终句子，不要解释、标点或markdown
5. 概念: {term}

请直接输出解释：
"""
        
        explanation = self._call_llm(prompt, max_retries=2)
        
        if explanation:
            # 清理输出
            explanation = explanation.strip()
            # 移除可能的引号
            explanation = explanation.strip('"\'')
            # 限制长度
            if len(explanation) > 30:
                explanation = explanation[:30]
            
            return explanation
        
        return None
    
    def _save_explanations(self, explanations, process_id):
        """保存概念解释到文件"""
        output_dir = os.path.join('processed', 'concepts', process_id)
        os.makedirs(output_dir, exist_ok=True)
        
        filepath = os.path.join(output_dir, f'{process_id}_explanations.txt')
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(f"学科: {self.subject}\n")
            f.write(f"概念数量: {len(explanations)}\n")
            f.write("="*60 + "\n\n")
            
            for item in explanations:
                f.write(f"{item['term']}: {item['explanation']}\n")
        
        print(f"   💾 概念解释已保存: {filepath}")
    
    def generate_audio_for_concepts(self, explanations, process_id):
        """
        为每个概念的解释生成音频（支持多线程并发）
        
        Args:
            explanations: 概念解释列表 [{'term': '细胞', 'explanation': '...', 'frequency': 3}, ...]
            process_id: 处理ID
            
        Returns:
            list: 带音频路径的解释列表
        """
        print(f"\n{'='*80}")
        print(f"🔊 为概念解释生成音频（多线程并发）")
        print(f"{'='*80}")
        print(f"   概念数量: {len(explanations)}")
        print(f"   TTS服务: https://jeniya.cn/v1/audio/speech")
        print(f"   语音模型: gpt-4o-mini-tts, alloy")
        print(f"   🚀 使用 {self.max_workers} 个线程并发处理")
        
        # 创建音频输出目录
        audio_dir = os.path.join('processed', 'audio', process_id, 'concepts')
        os.makedirs(audio_dir, exist_ok=True)
        print(f"   📂 音频输出目录: {audio_dir}")
        
        results = []
        
        # 准备任务列表（过滤无效项）
        tasks = [
            (idx, item, audio_dir, process_id)
            for idx, item in enumerate(explanations)
            if item.get('term') and item.get('explanation') and item.get('explanation') != '暂无解释'
        ]
        
        # 记录跳过的项
        skipped_count = len(explanations) - len(tasks)
        if skipped_count > 0:
            print(f"   ⏭️  跳过 {skipped_count} 个无效解释")
        
        print(f"   📋 准备 {len(tasks)} 个音频生成任务")
        
        # 使用线程池并发处理
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            future_to_task = {
                executor.submit(self._generate_single_audio, task): task
                for task in tasks
            }
            
            completed = 0
            for future in as_completed(future_to_task):
                completed += 1
                task = future_to_task[future]
                idx, item = task[0], task[1]
                
                try:
                    result = future.result()
                    if result:
                        results.append(result)
                        print(f"   ✅ [{completed}/{len(tasks)}] {result['term']} - 音频生成成功")
                    else:
                        print(f"   ⚠️  [{completed}/{len(tasks)}] {item.get('term')} - 音频生成失败")
                        results.append({**item, 'audio_path': None})
                except Exception as e:
                    print(f"   ❌ [{completed}/{len(tasks)}] {item.get('term')} - 错误: {str(e)}")
                    results.append({**item, 'audio_path': None})
        
        # 添加被跳过的项（无音频）
        for idx, item in enumerate(explanations):
            if not any(r['term'] == item.get('term') for r in results):
                results.append({**item, 'audio_path': None})
        
        # 统计成功数量
        success_count = sum(1 for r in results if r.get('audio_path'))
        print(f"\n   ✅ 音频生成完成: {success_count}/{len(explanations)} 个成功")
        
        return results
    
    def _generate_single_audio(self, task):
        """
        为单个概念生成音频（线程安全）
        
        Args:
            task: (idx, item, audio_dir, process_id)
            
        Returns:
            dict 或 None
        """
        idx, item, audio_dir, process_id = task
        term = item.get('term', '')
        explanation = item.get('explanation', '')
        
        try:
            # 调用语音生成
            audio_path = self._generate_speech(
                concept=term,
                description=explanation,
                save_dir=audio_dir
            )
            
            if audio_path:
                # 生成前端访问路径
                relative_path = f"/processed/audio/{process_id}/concepts/{os.path.basename(audio_path)}"
                
                return {
                    **item,
                    'audio_path': relative_path,
                    'local_audio_path': audio_path
                }
            else:
                return None
                
        except Exception as e:
            return None
    
    def _generate_speech(self, concept, description, save_dir):
        """
        使用 requests 直接调用 TTS API（完全参考新的 Speechgenerator.py）
        
        Args:
            concept: 概念名称
            description: 描述文本
            save_dir: 保存目录
            
        Returns:
            str: 音频文件路径
        """
        # 构建目标路径（使用.mp3格式）
        file_name = f"{concept}.mp3"
        target_path = os.path.join(save_dir, file_name)
        
        # 如果文件已存在，跳过生成
        if os.path.exists(target_path):
            print(f"         ℹ️  音频已存在，跳过")
            return target_path
        
        # API配置（完全参考 Speechgenerator.py）
        API_BASE = "https://jeniya.cn/v1/audio/speech"
        API_KEY = "sk-5w2FNpdHmV4xoi07RAe2lWiqXGgvRWCbmCk9o8T39hZBhJIc"
        
        # 请求参数（完全参考 Speechgenerator.py）
        payload = {
            "model": "gpt-4o-mini-tts",  # 语音合成模型
            "input": description,         # 要合成的文字（概念解释）
            "voice": "alloy",            # 声音风格
            "format": "mp3"              # 输出格式
        }
        
        # 请求头（完全参考 Speechgenerator.py）
        headers = {
            "Authorization": f"Bearer {API_KEY}",
            "Content-Type": "application/json"
        }
        
        # 重试机制
        max_retries = 3
        for attempt in range(max_retries):
            try:
                # 发起请求（完全参考 Speechgenerator.py）
                response = requests.post(API_BASE, headers=headers, json=payload, timeout=60)
                
                # 保存文件（完全参考 Speechgenerator.py）
                if response.status_code == 200:
                    with open(target_path, "wb") as f:
                        f.write(response.content)
                    
                    return target_path
                else:
                    error_msg = f"状态码: {response.status_code}, 响应: {response.text[:200]}"
                    print(f"         ❌ 生成失败: {error_msg}")
                    
                    if attempt < max_retries - 1:
                        print(f"         ⏳ 等待 3 秒后重试...")
                        time.sleep(3)
                        continue
                    else:
                        return None
                
            except requests.exceptions.Timeout:
                print(f"         ⏱️  第 {attempt + 1}/{max_retries} 次尝试超时")
                if attempt < max_retries - 1:
                    time.sleep(3)
                    continue
                else:
                    return None
                    
            except Exception as e:
                print(f"         ⚠️  第 {attempt + 1}/{max_retries} 次尝试失败: {str(e)}")
                
                if attempt < max_retries - 1:
                    print(f"         ⏳ 等待 3 秒后重试...")
                    time.sleep(3)
                    continue
                else:
                    print(f"         ❌ 所有重试均失败")
                    return None
        
        return None
    
    def _call_llm(self, content, max_retries=3):
        """
        调用LLM API（参考NER.py的ner函数）
        
        Args:
            content: 输入内容
            max_retries: 最大重试次数
            
        Returns:
            str: LLM响应
        """
        url = f"{self.base_url.rstrip('/')}/v1/chat/completions"
        
        payload = {
            "model": self.model,
            "messages": [
                {
                    "role": "user",
                    "content": content
                }
            ],
            "temperature": 0.3,
            "max_tokens": 2000
        }
        
        for attempt in range(max_retries):
            try:
                time.sleep(1)  # 避免请求过快
                
                # 使用与subtitle_processor相同的headers配置
                response = requests.post(
                    url,
                    headers=self.headers,
                    json=payload,
                    timeout=60
                )
                
                if response.status_code == 200:
                    response_json = response.json()
                    
                    if 'choices' in response_json and len(response_json['choices']) > 0:
                        result = response_json['choices'][0]['message']['content']
                        return result
                    else:
                        print(f"         ⚠️  响应格式异常")
                        
                else:
                    print(f"         ⚠️  API返回状态码: {response.status_code}")
                
                if attempt < max_retries - 1:
                    wait_time = (2 ** attempt)
                    print(f"         ⏳ 等待 {wait_time} 秒后重试...")
                    time.sleep(wait_time)
                    
            except Exception as e:
                print(f"         ❌ API调用错误: {str(e)}")
                if attempt < max_retries - 1:
                    time.sleep(2 ** attempt)
        
        return None
    
    def _save_extraction_results(self, results, process_id):
        """保存提取阶段结果"""
        output_dir = os.path.join('processed', 'concepts', process_id)
        os.makedirs(output_dir, exist_ok=True)
        
        filepath = os.path.join(output_dir, f'{process_id}_extraction.txt')
        
        with open(filepath, 'w', encoding='utf-8') as f:
            for timestamp, entities, sc_score, prompt, max_scores in results:
                f.write(f"{timestamp}\n")
                f.write(' '.join(entities) + '\n')
                f.write(' '.join(map(str, sc_score)) + '\n')
                f.write(f"{prompt}\n")
                f.write(' '.join(map(str, max_scores)) + '\n')
                f.write('\n')
        
        print(f"   💾 提取结果已保存: {filepath}")
    
    def _save_validation_results(self, results, process_id):
        """保存验证阶段结果"""
        output_dir = os.path.join('processed', 'concepts', process_id)
        os.makedirs(output_dir, exist_ok=True)
        
        filepath = os.path.join(output_dir, f'{process_id}_validation.txt')
        
        with open(filepath, 'w', encoding='utf-8') as f:
            for timestamp, entities, sc_score, prompt, max_scores in results:
                f.write(f"{timestamp}\n")
                f.write(' '.join(entities) + '\n')
                f.write(' '.join(map(str, sc_score)) + '\n')
                f.write(f"Feedback: {prompt[:200]}...\n")
                f.write(' '.join(map(str, max_scores)) + '\n')
                f.write('\n')
        
        print(f"   💾 验证结果已保存: {filepath}")
    
    def _save_integration_results(self, results, process_id):
        """保存整合阶段结果"""
        output_dir = os.path.join('processed', 'concepts', process_id)
        os.makedirs(output_dir, exist_ok=True)
        
        filepath = os.path.join(output_dir, f'{process_id}_final_concepts.txt')
        
        with open(filepath, 'w', encoding='utf-8') as f:
            f.write(f"学科: {self.subject}\n")
            f.write(f"总概念数: {len(self.final_entity)}\n")
            f.write("="*60 + "\n\n")
            
            for timestamp, entities in results:
                f.write(f"{timestamp}:\n")
                for entity in entities:
                    f.write(f"  - {entity}\n")
                f.write('\n')
            
            f.write("\n" + "="*60 + "\n")
            f.write("所有最终概念:\n")
            for entity in sorted(self.final_entity):
                f.write(f"- {entity}\n")
        
        print(f"   💾 最终概念已保存: {filepath}")
