File size: 12,572 Bytes
0bbe763
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
"""
品質管控模組
Quality Control Module

此模組提供對話品質檢查、連貫性驗證、內容分析等功能。
確保生成的對話腳本具有高品質和邏輯連貫性。
"""

import re
import logging
from typing import Dict, List, Tuple, Optional
from dataclasses import dataclass

logger = logging.getLogger(__name__)

@dataclass
class QualityReport:
    """品質檢查報告"""
    overall_score: float  # 0-100 分
    coherence_score: float
    character_consistency_score: float
    content_richness_score: float
    format_compliance_score: float
    issues: List[str]
    suggestions: List[str]


class DialogueQualityChecker:
    """對話品質檢查器"""
    
    def __init__(self):
        self.speaker_patterns = {
            'speaker-1': r'speaker-1:\s*',
            'speaker-2': r'speaker-2:\s*'
        }
        
    def check_dialogue_quality(self, dialogue: str, expected_speakers: List[str] = None) -> QualityReport:
        """
        全面檢查對話品質
        
        Args:
            dialogue: 對話文本
            expected_speakers: 預期的發言者列表
            
        Returns:
            QualityReport: 品質檢查報告
        """
        if expected_speakers is None:
            expected_speakers = ['speaker-1', 'speaker-2']
            
        logger.info("開始進行對話品質檢查")
        
        # 執行各項檢查
        coherence_score = self._check_coherence(dialogue)
        character_score = self._check_character_consistency(dialogue, expected_speakers)
        content_score = self._check_content_richness(dialogue)
        format_score = self._check_format_compliance(dialogue, expected_speakers)
        
        # 計算總分
        overall_score = (coherence_score + character_score + content_score + format_score) / 4
        
        # 收集問題和建議
        issues, suggestions = self._generate_feedback(
            dialogue, coherence_score, character_score, content_score, format_score
        )
        
        report = QualityReport(
            overall_score=overall_score,
            coherence_score=coherence_score,
            character_consistency_score=character_score,
            content_richness_score=content_score,
            format_compliance_score=format_score,
            issues=issues,
            suggestions=suggestions
        )
        
        logger.info(f"品質檢查完成,總分: {overall_score:.1f}")
        return report
    
    def _check_coherence(self, dialogue: str) -> float:
        """檢查對話的邏輯連貫性"""
        lines = [line.strip() for line in dialogue.split('\n') if line.strip()]
        if len(lines) < 5:
            return 30.0  # 對話太短
            
        # 檢查主題切換的自然度
        topic_transitions = 0
        abrupt_changes = 0
        
        for i in range(1, len(lines)):
            prev_line = lines[i-1].lower()
            curr_line = lines[i].lower()
            
            # 簡單的主題連貫性檢查
            if self._is_topic_transition(prev_line, curr_line):
                topic_transitions += 1
                if self._is_abrupt_change(prev_line, curr_line):
                    abrupt_changes += 1
        
        if topic_transitions == 0:
            return 60.0  # 沒有主題變化可能表示內容單調
            
        coherence_ratio = 1 - (abrupt_changes / topic_transitions)
        return max(50.0, coherence_ratio * 100)
    
    def _check_character_consistency(self, dialogue: str, expected_speakers: List[str]) -> float:
        """檢查角色一致性"""
        score = 100.0
        issues = []
        
        # 檢查發言者格式
        for speaker in expected_speakers:
            pattern = self.speaker_patterns.get(speaker, f'{speaker}:\\s*')
            matches = re.findall(pattern, dialogue, re.IGNORECASE)
            
            if not matches:
                score -= 30.0
                issues.append(f"未找到發言者 {speaker}")
        
        # 檢查是否有無效的發言者標記
        valid_patterns = '|'.join([f'{speaker}:' for speaker in expected_speakers])
        invalid_speakers = re.findall(rf'(\w+):\s*(?!{valid_patterns})', dialogue, re.IGNORECASE)
        
        if invalid_speakers:
            score -= len(set(invalid_speakers)) * 10
            issues.append(f"發現無效的發言者標記: {set(invalid_speakers)}")
        
        return max(0.0, score)
    
    def _check_content_richness(self, dialogue: str) -> float:
        """檢查內容豐富度"""
        # 計算對話輪數
        turns = len(re.findall(r'speaker-[12]:', dialogue, re.IGNORECASE))
        
        # 計算平均每輪長度
        lines = dialogue.split('\n')
        content_lines = [line for line in lines if re.match(r'speaker-[12]:', line, re.IGNORECASE)]
        
        if not content_lines:
            return 0.0
            
        avg_length = sum(len(line) for line in content_lines) / len(content_lines)
        
        # 評分標準
        turn_score = min(100, (turns / 50) * 100)  # 50輪為滿分
        length_score = min(100, (avg_length / 200) * 100)  # 200字為滿分
        
        return (turn_score + length_score) / 2
    
    def _check_format_compliance(self, dialogue: str, expected_speakers: List[str]) -> float:
        """檢查格式規範性"""
        score = 100.0
        
        # 檢查是否以正確的開場白開始
        if 'speaker-1:' in dialogue:
            first_speaker_line = re.search(r'speaker-1:\s*(.+)', dialogue, re.IGNORECASE)
            if first_speaker_line:
                first_content = first_speaker_line.group(1).strip()
                if '歡迎收聽' not in first_content or 'David888 Podcast' not in first_content:
                    score -= 20.0
        
        # 檢查是否有不當的格式標記
        if re.search(r'\[Host\]|\[Guest\]|\[.*?\]', dialogue):
            score -= 30.0
        
        # 檢查行格式
        lines = dialogue.split('\n')
        malformed_lines = 0
        for line in lines:
            line = line.strip()
            if line and not re.match(r'speaker-[12]:', line, re.IGNORECASE) and line not in expected_speakers:
                if ':' in line and not line.startswith('#'):  # 可能是格式錯誤的發言
                    malformed_lines += 1
        
        if malformed_lines > 0:
            score -= min(40.0, malformed_lines * 5)
        
        return max(0.0, score)
    
    def _is_topic_transition(self, prev_line: str, curr_line: str) -> bool:
        """判斷是否為主題轉換"""
        transition_keywords = [
            '另外', '接下來', '說到', '談到', '回到', '轉個話題',
            '順便提一下', '相關地', '類似地', '相比之下'
        ]
        
        return any(keyword in curr_line for keyword in transition_keywords)
    
    def _is_abrupt_change(self, prev_line: str, curr_line: str) -> bool:
        """判斷是否為突兀的主題變化"""
        # 簡單的突兀變化檢測
        if len(prev_line) < 10 or len(curr_line) < 10:
            return False
            
        # 這裡可以實現更複雜的語義分析
        # 目前使用簡單的關鍵詞檢查
        common_words = set(prev_line.split()) & set(curr_line.split())
        return len(common_words) < 2
    
    def _generate_feedback(self, dialogue: str, coherence: float, character: float, 
                          content: float, format_score: float) -> Tuple[List[str], List[str]]:
        """生成問題和建議"""
        issues = []
        suggestions = []
        
        if coherence < 70:
            issues.append("對話邏輯連貫性較低")
            suggestions.append("建議增加更自然的主題過渡和銜接詞")
        
        if character < 70:
            issues.append("角色一致性有問題")
            suggestions.append("確保發言者標記格式正確,避免使用無效的角色名稱")
        
        if content < 70:
            issues.append("內容豐富度不足")
            suggestions.append("建議增加對話輪數或每輪的內容深度")
        
        if format_score < 70:
            issues.append("格式規範性不符合要求")
            suggestions.append("檢查開場白格式,移除不當的標記符號")
        
        return issues, suggestions


class ContentCoherenceAnalyzer:
    """內容連貫性分析器"""
    
    def __init__(self):
        self.topic_keywords = {}
        
    def analyze_content_flow(self, dialogue: str) -> Dict[str, float]:
        """分析內容流暢度"""
        lines = [line.strip() for line in dialogue.split('\n') if line.strip()]
        
        # 提取主要話題
        topics = self._extract_topics(dialogue)
        
        # 分析話題分布
        topic_distribution = self._analyze_topic_distribution(lines, topics)
        
        # 計算流暢度分數
        flow_score = self._calculate_flow_score(topic_distribution)
        
        return {
            'flow_score': flow_score,
            'topic_count': len(topics),
            'topic_distribution': topic_distribution
        }
    
    def _extract_topics(self, dialogue: str) -> List[str]:
        """提取對話中的主要話題"""
        # 簡單的關鍵詞提取
        # 在實際應用中可以使用更精密的NLP技術
        common_topics = [
            '技術', '科學', '研究', '發現', '材料', '實驗',
            '理論', '應用', '未來', '發展', '創新', '挑戰'
        ]
        
        found_topics = []
        for topic in common_topics:
            if topic in dialogue:
                found_topics.append(topic)
        
        return found_topics
    
    def _analyze_topic_distribution(self, lines: List[str], topics: List[str]) -> Dict[str, int]:
        """分析話題分布"""
        distribution = {topic: 0 for topic in topics}
        
        for line in lines:
            for topic in topics:
                if topic in line:
                    distribution[topic] += 1
        
        return distribution
    
    def _calculate_flow_score(self, topic_distribution: Dict[str, int]) -> float:
        """計算流暢度分數"""
        if not topic_distribution:
            return 50.0
        
        # 話題分布的均勻度
        values = list(topic_distribution.values())
        if max(values) == 0:
            return 50.0
        
        uniformity = 1 - (max(values) - min(values)) / max(values)
        return uniformity * 100


def validate_dialogue_structure(dialogue: str, template_type: str = 'podcast') -> bool:
    """
    驗證對話結構是否符合模板要求
    
    Args:
        dialogue: 對話文本
        template_type: 模板類型
        
    Returns:
        bool: 是否符合結構要求
    """
    if template_type == 'podcast':
        # 檢查是否有兩個發言者
        has_speaker1 = 'speaker-1:' in dialogue
        has_speaker2 = 'speaker-2:' in dialogue
        
        # 檢查開場白
        has_opening = '歡迎收聽' in dialogue and 'David888 Podcast' in dialogue
        
        return has_speaker1 and has_speaker2 and has_opening
    
    elif template_type == 'podcast-single':
        # 檢查是否只有一個發言者
        has_speaker1 = 'speaker-1:' in dialogue
        has_speaker2 = 'speaker-2:' in dialogue
        
        # 檢查開場白
        has_opening = '歡迎收聽' in dialogue and 'David888 Podcast' in dialogue
        
        return has_speaker1 and not has_speaker2 and has_opening
    
    return True  # 其他模板暫不檢查


def suggest_improvements(quality_report: QualityReport) -> List[str]:
    """
    根據品質報告提供改進建議
    
    Args:
        quality_report: 品質檢查報告
        
    Returns:
        List[str]: 改進建議列表
    """
    suggestions = quality_report.suggestions.copy()
    
    if quality_report.overall_score < 60:
        suggestions.append("整體品質較低,建議重新生成並調整提示詞")
    
    if quality_report.coherence_score < 60:
        suggestions.append("增加內容規劃步驟,確保邏輯流暢")
    
    if quality_report.character_consistency_score < 60:
        suggestions.append("檢查角色定義,確保發言風格一致")
    
    if quality_report.content_richness_score < 60:
        suggestions.append("增加內容深度和對話互動性")
    
    return list(set(suggestions))  # 去重