Yasu777 commited on
Commit
7c69035
·
verified ·
1 Parent(s): 00700bc

Create design_implementation_validator.py

Browse files
validators/design_implementation_validator.py ADDED
@@ -0,0 +1,575 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import ast
3
+ import difflib
4
+ from typing import Dict, Any, List, Optional, Set, Tuple
5
+
6
+ from validators.base import BaseValidator, Validator
7
+
8
+ class DesignImplementationValidator(BaseValidator, Validator):
9
+ """設計書と実装コードの整合性を検証するクラス"""
10
+
11
+ def __init__(self, client=None):
12
+ """設計・実装検証クラスの初期化"""
13
+ super().__init__(client)
14
+ self.validation_results = {}
15
+
16
+ async def validate(self, content, context=None):
17
+ """設計書と実装コードの整合性を検証する(インターフェース実装)"""
18
+ # contentが設計書、contextが実装コードの場合
19
+ if context and isinstance(content, str) and isinstance(context, str):
20
+ design_doc = content
21
+ implementation_code = context
22
+ else:
23
+ # 単一コンテンツの場合、設計書と実装コードを分離
24
+ design_code_parts = self._separate_design_and_code(content)
25
+ if design_code_parts:
26
+ design_doc, implementation_code = design_code_parts
27
+ else:
28
+ # 分離できない場合は、元のコンテンツをそのまま返す
29
+ return content
30
+
31
+ # 検証実行
32
+ validation_result = self.validate_design_implementation(design_doc, implementation_code)
33
+ self.validation_results = validation_result
34
+
35
+ # 検証結果に基づいて注釈を追加
36
+ if not validation_result.get("is_compliant", True):
37
+ implementation_code = self._add_compliance_notes(implementation_code, validation_result)
38
+
39
+ return implementation_code
40
+
41
+ def get_result_summary(self):
42
+ """検証結果の要約を返す(インターフェース実装)"""
43
+ if not self.validation_results:
44
+ return "設計書と実装コードの整合性はまだ検証されていません。"
45
+
46
+ is_compliant = self.validation_results.get("is_compliant", True)
47
+ compliance_score = self.validation_results.get("compliance_score", 0.0)
48
+
49
+ if is_compliant:
50
+ return f"設計書と実装コードは整合しています(整合性スコア: {compliance_score}/10.0)"
51
+
52
+ missing_components = self.validation_results.get("missing_components", [])
53
+ missing_methods = self.validation_results.get("missing_methods", [])
54
+ missing_requirements = self.validation_results.get("missing_requirements", [])
55
+
56
+ summary = f"設計書と実装コードに不整合があります(整合性スコア: {compliance_score}/10.0)\n\n"
57
+
58
+ if missing_components:
59
+ summary += "不足しているコンポーネント:\n"
60
+ summary += "\n".join([f"- {component}" for component in missing_components[:3]])
61
+ if len(missing_components) > 3:
62
+ summary += f"\n...他{len(missing_components) - 3}個"
63
+ summary += "\n\n"
64
+
65
+ if missing_methods:
66
+ summary += "不足しているメソッド:\n"
67
+ summary += "\n".join([f"- {method}" for method in missing_methods[:3]])
68
+ if len(missing_methods) > 3:
69
+ summary += f"\n...他{len(missing_methods) - 3}個"
70
+ summary += "\n\n"
71
+
72
+ if missing_requirements:
73
+ summary += "未実装の要件:\n"
74
+ summary += "\n".join([f"- {req}" for req in missing_requirements[:3]])
75
+ if len(missing_requirements) > 3:
76
+ summary += f"\n...他{len(missing_requirements) - 3}個"
77
+
78
+ return summary
79
+
80
+ def _separate_design_and_code(self, content: str) -> Optional[Tuple[str, str]]:
81
+ """コンテンツから設計書部分と実装コード部分を分離する"""
82
+ # 設計書セクションのマーカー
83
+ design_markers = [
84
+ "# 設計書", "## 設計書", "# 設計", "## 設計",
85
+ "# アーキテクチャ設計", "# 詳細設計",
86
+ "# Design Document", "## Design Document",
87
+ "# Architecture", "## Architecture"
88
+ ]
89
+
90
+ # 実装コードセクションのマーカー
91
+ code_markers = [
92
+ "# 実装", "## 実装", "# 実装コード", "## 実装コード",
93
+ "# Implementation", "## Implementation",
94
+ "# Code", "## Code",
95
+ "```python", "```java", "```javascript", "```cpp"
96
+ ]
97
+
98
+ # 設計書と実装コードの開始位置を検出
99
+ design_start = -1
100
+ code_start = -1
101
+
102
+ for marker in design_markers:
103
+ if marker in content:
104
+ design_start = content.find(marker)
105
+ break
106
+
107
+ for marker in code_markers:
108
+ if marker in content:
109
+ marker_pos = content.find(marker)
110
+ if marker_pos > design_start: # 設計書の後に出現するコードマーカー���み考慮
111
+ code_start = marker_pos
112
+ break
113
+
114
+ # 設計書と実装コードの両方が見つかった場合
115
+ if design_start >= 0 and code_start > design_start:
116
+ design_doc = content[design_start:code_start].strip()
117
+ implementation_code = content[code_start:].strip()
118
+ return design_doc, implementation_code
119
+
120
+ # 分離できない場合
121
+ return None
122
+
123
+ def _add_compliance_notes(self, implementation_code: str, validation_result: Dict[str, Any]) -> str:
124
+ """検証結果に基づいて実装コードに注釈を追加する"""
125
+ notes = ["# 設計書との整合性の問題点:"]
126
+
127
+ # 各種不足要素を注釈として追加
128
+ missing_components = validation_result.get("missing_components", [])
129
+ if missing_components:
130
+ notes.append("# 不足しているコンポーネント:")
131
+ for component in missing_components:
132
+ notes.append(f"# - {component}")
133
+
134
+ missing_methods = validation_result.get("missing_methods", [])
135
+ if missing_methods:
136
+ notes.append("# 不足しているメソッド:")
137
+ for method in missing_methods:
138
+ notes.append(f"# - {method}")
139
+
140
+ missing_requirements = validation_result.get("missing_requirements", [])
141
+ if missing_requirements:
142
+ notes.append("# 未実装の要件:")
143
+ for req in missing_requirements:
144
+ notes.append(f"# - {req}")
145
+
146
+ # 提案があれば追加
147
+ suggestion = validation_result.get("suggestion")
148
+ if suggestion:
149
+ notes.append("# 提案:")
150
+ for line in suggestion.split("\n"):
151
+ notes.append(f"# {line}")
152
+
153
+ # 注釈を実装コードの先頭に追加
154
+ notes_str = "\n".join(notes)
155
+ return f"{notes_str}\n\n{implementation_code}"
156
+
157
+ def validate_design_implementation(self, design_doc: str, implementation_code: str) -> Dict[str, Any]:
158
+ """設計書と実装コードの整合性を検証する"""
159
+ result = {
160
+ "is_compliant": True,
161
+ "issues": [],
162
+ "missing_components": [],
163
+ "missing_methods": [],
164
+ "missing_requirements": [],
165
+ "extra_components": [],
166
+ "compliance_score": 0.0,
167
+ "suggestion": None
168
+ }
169
+
170
+ # 設計書から要素を抽出
171
+ design_components = self._extract_design_components(design_doc)
172
+
173
+ # 実装コードから要素を抽出
174
+ implementation_components = self._extract_implementation_components(implementation_code)
175
+
176
+ # 設計書と実装の整合性を検証
177
+ self._validate_consistency(design_components, implementation_components, result)
178
+
179
+ # 検証結果に基づいてコンプライアンススコアを計算
180
+ self._calculate_compliance_score(result)
181
+
182
+ # 改善のための提案を生成
183
+ self._generate_suggestions(result)
184
+
185
+ return result
186
+
187
+ def _extract_design_components(self, design_doc: str) -> Dict[str, Any]:
188
+ """設計書からコンポーネント、クラス、メソッド、要件などを抽出する"""
189
+ components = {
190
+ "classes": [],
191
+ "interfaces": [],
192
+ "methods": {},
193
+ "attributes": {},
194
+ "requirements": [],
195
+ "dependencies": [],
196
+ "data_structures": []
197
+ }
198
+
199
+ # 1. クラス定義の抽出
200
+ class_pattern = r'(class\s+(\w+)(?:\s*\(\s*\w+\s*\))?\s*:)'
201
+ for match in re.finditer(class_pattern, design_doc):
202
+ class_name = match.group(2)
203
+ components["classes"].append(class_name)
204
+ components["methods"][class_name] = []
205
+ components["attributes"][class_name] = []
206
+
207
+ # クラス定義の範囲を特定
208
+ class_start = match.end()
209
+ next_class_match = re.search(class_pattern, design_doc[class_start:])
210
+ class_end = class_start + next_class_match.start() if next_class_match else len(design_doc)
211
+ class_body = design_doc[class_start:class_end]
212
+
213
+ # メソッド定義の抽出
214
+ method_pattern = r'def\s+(\w+)\s*\((.*?)\)'
215
+ for method_match in re.finditer(method_pattern, class_body):
216
+ method_name = method_match.group(1)
217
+ components["methods"][class_name].append(method_name)
218
+
219
+ # 属性の抽出(単純なパターンマッチング)
220
+ attribute_pattern = r'self\.(\w+)\s*='
221
+ for attr_match in re.finditer(attribute_pattern, class_body):
222
+ attr_name = attr_match.group(1)
223
+ if attr_name not in components["attributes"][class_name]:
224
+ components["attributes"][class_name].append(attr_name)
225
+
226
+ # 2. インターフェース定義の抽出(ABC, Protocolなど)
227
+ interface_pattern = r'(class\s+(\w+)(?:\s*\(\s*(?:ABC|Protocol)\s*\))?\s*:)'
228
+ for match in re.finditer(interface_pattern, design_doc):
229
+ interface_name = match.group(2)
230
+ if interface_name not in components["classes"]: # クラスとして既に登録されていない場合
231
+ components["interfaces"].append(interface_name)
232
+ components["methods"][interface_name] = []
233
+
234
+ # インターフェース定義の範囲を特定
235
+ interface_start = match.end()
236
+ next_interface_match = re.search(interface_pattern, design_doc[interface_start:])
237
+ interface_end = interface_start + next_interface_match.start() if next_interface_match else len(design_doc)
238
+ interface_body = design_doc[interface_start:interface_end]
239
+
240
+ # メソッド定義の抽出
241
+ method_pattern = r'def\s+(\w+)\s*\((.*?)\)'
242
+ for method_match in re.finditer(method_pattern, interface_body):
243
+ method_name = method_match.group(1)
244
+ components["methods"][interface_name].append(method_name)
245
+
246
+ # 3. 要件の抽出
247
+ requirement_sections = [
248
+ "要件", "機能要件", "非機能要件", "制約", "前提条件",
249
+ "Requirements", "Functional Requirements", "Non-functional Requirements"
250
+ ]
251
+
252
+ for section in requirement_sections:
253
+ section_match = re.search(rf'{section}[::]\s*(.*?)(?=\n\n|\n#|\Z)', design_doc, re.DOTALL)
254
+ if section_match:
255
+ section_text = section_match.group(1).strip()
256
+
257
+ # 箇条書きの抽出
258
+ bullet_patterns = [
259
+ r'[-*•]\s*(.*?)(?=\n[-*•]|\n\n|\Z)', # ハイフン、アスタリスク、中黒
260
+ r'\d+\.\s*(.*?)(?=\n\d+\.|\n\n|\Z)' # 番号付きリスト
261
+ ]
262
+
263
+ for pattern in bullet_patterns:
264
+ for req_match in re.finditer(pattern, section_text, re.DOTALL):
265
+ requirement = req_match.group(1).strip()
266
+ if requirement and requirement not in components["requirements"]:
267
+ components["requirements"].append(requirement)
268
+
269
+ # 4. データ構造の抽出
270
+ data_structure_patterns = [
271
+ r'(class|struct)\s+(\w+)', # クラスまたは構造体
272
+ r'(enum)\s+(\w+)', # 列挙型
273
+ r'(type|typedef)\s+(\w+)' # 型定義
274
+ ]
275
+
276
+ for pattern in data_structure_patterns:
277
+ for match in re.finditer(pattern, design_doc):
278
+ structure_type = match.group(1)
279
+ structure_name = match.group(2)
280
+ if structure_name not in components["classes"] and structure_name not in components["interfaces"]:
281
+ components["data_structures"].append(structure_name)
282
+
283
+ # 5. 依存関係の抽出
284
+ dependency_patterns = [
285
+ r'依存[::]\s*(.*?)(?=\n\n|\Z)',
286
+ r'Dependencies[::]\s*(.*?)(?=\n\n|\Z)',
287
+ r'利用[::]\s*(.*?)(?=\n\n|\Z)',
288
+ r'Uses[::]\s*(.*?)(?=\n\n|\Z)',
289
+ r'import\s+(\w+)',
290
+ r'from\s+(\w+)\s+import'
291
+ ]
292
+
293
+ for pattern in dependency_patterns:
294
+ for match in re.finditer(pattern, design_doc):
295
+ dependency = match.group(1).strip()
296
+ if dependency and dependency not in components["dependencies"]:
297
+ components["dependencies"].append(dependency)
298
+
299
+ return components
300
+
301
+ def _extract_implementation_components(self, implementation_code: str) -> Dict[str, Any]:
302
+ """実装コードからコンポーネント、クラス、メソッド、属性などを抽出する"""
303
+ components = {
304
+ "classes": [],
305
+ "interfaces": [],
306
+ "methods": {},
307
+ "attributes": {},
308
+ "imports": [],
309
+ "functions": []
310
+ }
311
+
312
+ try:
313
+ # ASTを使ってPythonコードを解析
314
+ tree = ast.parse(implementation_code)
315
+
316
+ # インポート文の抽出
317
+ for node in ast.walk(tree):
318
+ if isinstance(node, ast.Import):
319
+ for name in node.names:
320
+ components["imports"].append(name.name)
321
+ elif isinstance(node, ast.ImportFrom):
322
+ if node.module:
323
+ components["imports"].append(node.module)
324
+
325
+ # クラスの抽出
326
+ for node in ast.walk(tree):
327
+ if isinstance(node, ast.ClassDef):
328
+ class_name = node.name
329
+
330
+ # イ���ターフェース(抽象クラス)かどうかを判定
331
+ is_interface = False
332
+ for base in node.bases:
333
+ if isinstance(base, ast.Name) and base.id in ["ABC", "Protocol"]:
334
+ is_interface = True
335
+ break
336
+
337
+ if is_interface:
338
+ components["interfaces"].append(class_name)
339
+ else:
340
+ components["classes"].append(class_name)
341
+
342
+ components["methods"][class_name] = []
343
+ components["attributes"][class_name] = []
344
+
345
+ # メソッドと属性の抽出
346
+ for item in node.body:
347
+ if isinstance(item, ast.FunctionDef):
348
+ components["methods"][class_name].append(item.name)
349
+ elif isinstance(item, ast.Assign):
350
+ for target in item.targets:
351
+ if isinstance(target, ast.Name):
352
+ components["attributes"][class_name].append(target.id)
353
+
354
+ # トップレベル関数の抽出
355
+ for node in ast.walk(tree):
356
+ if isinstance(node, ast.FunctionDef) and node.name not in [m for methods in components["methods"].values() for m in methods]:
357
+ if not any(isinstance(parent, ast.ClassDef) for parent in ast.iter_child_nodes(tree)):
358
+ components["functions"].append(node.name)
359
+
360
+ except SyntaxError as e:
361
+ print(f"[Warning] Syntax error during code analysis: {e}")
362
+ # 構文エラーの場合は正規表現ベースのフォールバック解析を使用
363
+ self._fallback_extract_implementation_components(implementation_code, components)
364
+
365
+ return components
366
+
367
+ def _fallback_extract_implementation_components(self, code: str, components: Dict[str, Any]) -> None:
368
+ """構文エラーがある場合のフォールバック抽出メソッド(正規表現ベース)"""
369
+ # クラス定義の抽出
370
+ class_pattern = r'class\s+(\w+)(?:\s*\(.*?\))?\s*:'
371
+ for match in re.finditer(class_pattern, code):
372
+ class_name = match.group(1)
373
+ if class_name not in components["classes"]:
374
+ components["classes"].append(class_name)
375
+ components["methods"][class_name] = []
376
+ components["attributes"][class_name] = []
377
+
378
+ # クラス本体の抽出
379
+ class_start = match.end()
380
+ # 簡易的なブロック終了の検出(インデントベース)
381
+ class_body = ""
382
+ in_class = True
383
+ indent_level = None
384
+
385
+ for line in code[class_start:].split('\n'):
386
+ if not line.strip():
387
+ class_body += line + '\n'
388
+ continue
389
+
390
+ current_indent = len(line) - len(line.lstrip())
391
+
392
+ if indent_level is None:
393
+ if current_indent > 0: # クラス本体の最初の行
394
+ indent_level = current_indent
395
+ class_body += line + '\n'
396
+ continue
397
+
398
+ if current_indent >= indent_level:
399
+ class_body += line + '\n'
400
+ else:
401
+ break # クラスブロックの終了
402
+
403
+ # メソッド定義の抽出
404
+ method_pattern = r'def\s+(\w+)\s*\('
405
+ for method_match in re.finditer(method_pattern, class_body):
406
+ method_name = method_match.group(1)
407
+ if method_name not in components["methods"][class_name]:
408
+ components["methods"][class_name].append(method_name)
409
+
410
+ # 属性の抽出
411
+ attribute_pattern = r'self\.(\w+)\s*='
412
+ for attr_match in re.finditer(attribute_pattern, class_body):
413
+ attr_name = attr_match.group(1)
414
+ if attr_name not in components["attributes"][class_name]:
415
+ components["attributes"][class_name].append(attr_name)
416
+
417
+ # インポート文の抽出
418
+ import_pattern = r'import\s+(\w+)'
419
+ for match in re.finditer(import_pattern, code):
420
+ import_name = match.group(1)
421
+ if import_name not in components["imports"]:
422
+ components["imports"].append(import_name)
423
+
424
+ from_import_pattern = r'from\s+(\w+)\s+import'
425
+ for match in re.finditer(from_import_pattern, code):
426
+ import_name = match.group(1)
427
+ if import_name not in components["imports"]:
428
+ components["imports"].append(import_name)
429
+
430
+ # トップレベル関数の抽出
431
+ function_pattern = r'^def\s+(\w+)\s*\('
432
+ for match in re.finditer(function_pattern, code, re.MULTILINE):
433
+ func_name = match.group(1)
434
+ if func_name not in components["functions"]:
435
+ components["functions"].append(func_name)
436
+
437
+ def _validate_consistency(self, design: Dict[str, Any], implementation: Dict[str, Any], result: Dict[str, Any]) -> None:
438
+ """設計と実装の整合性を検証する"""
439
+ # 1. クラスの整合性チェック
440
+ for class_name in design["classes"]:
441
+ if class_name not in implementation["classes"] and class_name not in implementation["interfaces"]:
442
+ result["is_compliant"] = False
443
+ result["missing_components"].append(f"Class: {class_name}")
444
+ result["issues"].append(f"設計書で定義されているクラス '{class_name}' が実装に存在しません。")
445
+
446
+ # 2. インターフェースの整合性チェック
447
+ for interface_name in design["interfaces"]:
448
+ if interface_name not in implementation["interfaces"] and interface_name not in implementation["classes"]:
449
+ result["is_compliant"] = False
450
+ result["missing_components"].append(f"Interface: {interface_name}")
451
+ result["issues"].append(f"設計書で定義されているインターフェース '{interface_name}' が実装に存在しません。")
452
+
453
+ # 3. メソッドの整合性チェック
454
+ for class_name, methods in design["methods"].items():
455
+ if class_name in implementation["methods"]:
456
+ for method_name in methods:
457
+ if method_name not in implementation["methods"][class_name]:
458
+ result["is_compliant"] = False
459
+ result["missing_methods"].append(f"{class_name}.{method_name}")
460
+ result["issues"].append(f"設計書で定義されているメソッド '{class_name}.{method_name}' が実装に存在しません。")
461
+
462
+ # 4. 属性の整合性チェック(厳密でない - 属性は実装時に追加されることもある)
463
+ for class_name, attrs in design["attributes"].items():
464
+ if class_name in implementation["attributes"]:
465
+ for attr_name in attrs:
466
+ if attr_name not in implementation["attributes"][class_name]:
467
+ # 属性の不一致は警告レベル
468
+ result["issues"].append(f"設計書で定義されている属性 '{class_name}.{attr_name}' が実装に存在しません(警告)。")
469
+
470
+ # 5. 要件の実装検証(キーワードベースの簡易検証)
471
+ code_text = implementation_code if hasattr(self, 'implementation_code') else ""
472
+ for req in design["requirements"]:
473
+ # 要件からキーワードを抽出
474
+ keywords = self._extract_keywords(req)
475
+
476
+ # キーワードが実装に含まれているかチェック
477
+ if keywords and not all(keyword.lower() in code_text.lower() for keyword in keywords if len(keyword) > 3):
478
+ result["is_compliant"] = False
479
+ result["missing_requirements"].append(req)
480
+ result["issues"].append(f"要件 '{req}' が実装に反映されていない可能性があります。")
481
+
482
+ # 6. 実装に存在する余分なコンポーネントを確認
483
+ for class_name in implementation["classes"]:
484
+ if class_name not in design["classes"] and class_name not in design["interfaces"]:
485
+ result["extra_components"].append(f"Class: {class_name}")
486
+ # 余分なコンポーネントは警告レベル
487
+ result["issues"].append(f"実装に存在するクラス '{class_name}' が設計書に定義されていません(警告)。")
488
+
489
+ def _extract_keywords(self, text: str) -> List[str]:
490
+ """テキストから重要なキーワードを抽出する"""
491
+ # ストップワードを定義
492
+ stop_words = {
493
+ "a", "an", "the", "this", "that", "these", "those",
494
+ "is", "are", "was", "were", "be", "been", "being",
495
+ "have", "has", "had", "do", "does", "did", "will", "would", "shall", "should",
496
+ "can", "could", "may", "might", "must", "and", "or", "but", "if", "then", "else",
497
+ "when", "where", "why", "how", "all", "any", "both", "each", "few", "more", "most",
498
+ "other", "some", "such", "no", "nor", "not", "only", "own", "same", "so", "than",
499
+ "too", "very", "を", "に", "は", "が", "で", "と", "から", "まで", "より", "して", "する", "ます", "です"
500
+ }
501
+
502
+ # 単語分割(英語と日本語の両方に対応)
503
+ words = []
504
+ # 英単語の抽出
505
+ english_words = re.findall(r'\b[a-zA-Z]+\b', text.lower())
506
+ words.extend(english_words)
507
+
508
+ # 日本語単語の簡易抽出(文字単位の分割)
509
+ japanese_text = re.sub(r'[a-zA-Z0-9\s]', '', text)
510
+ for char in japanese_text:
511
+ if char not in "、。!?「」『』()[]【】…:;":
512
+ words.append(char)
513
+
514
+ # ストップワードと短すぎる単語を除外
515
+ keywords = [w for w in words if w not in stop_words and len(w) > 1]
516
+
517
+ # 重複を削除
518
+ unique_keywords = list(set(keywords))
519
+
520
+ return unique_keywords
521
+
522
+ def _calculate_compliance_score(self, result: Dict[str, Any]) -> None:
523
+ """コンプライアンススコアを計算する(0.0〜10.0の範囲)"""
524
+ # 初期スコア
525
+ score = 10.0
526
+
527
+ # 不足しているコンポーネントごとに減点
528
+ score -= len(result["missing_components"]) * 2.0
529
+
530
+ # 不足しているメソッドごとに減点
531
+ score -= len(result["missing_methods"]) * 1.0
532
+
533
+ # 不足している要件ごとに減点
534
+ score -= len(result["missing_requirements"]) * 1.5
535
+
536
+ # 余分なコンポーネントごとに軽く減点
537
+ score -= len(result["extra_components"]) * 0.5
538
+
539
+ # スコアの範囲を調整
540
+ result["compliance_score"] = max(0.0, min(10.0, score))
541
+
542
+ # コンプライアンス判定(スコアが7.0以上で合格)
543
+ result["is_compliant"] = result["compliance_score"] >= 7.0
544
+
545
+ def _generate_suggestions(self, result: Dict[str, Any]) -> None:
546
+ """検証結果に基づいて改善提案を生成する"""
547
+ if result["is_compliant"]:
548
+ if result["compliance_score"] == 10.0:
549
+ result["suggestion"] = "設計書と実装は完全に一致しています。素晴らしい実装です。"
550
+ else:
551
+ result["suggestion"] = "設計書と実装は概ね一致していますが、いくつかの軽微な問題があります。詳細は issues リストを確認してください。"
552
+ else:
553
+ # 不足コンポーネントの提案
554
+ if result["missing_components"]:
555
+ result["suggestion"] = f"設計書で定義されている以下のコンポーネントを実装してください: {', '.join(result['missing_components'][:3])}"
556
+ if len(result["missing_components"]) > 3:
557
+ result["suggestion"] += f" ... 他 {len(result['missing_components']) - 3} 件"
558
+
559
+ # 不足メソッドの提案
560
+ elif result["missing_methods"]:
561
+ result["suggestion"] = f"設計書で定義されている以下のメソッドを実装してください: {', '.join(result['missing_methods'][:3])}"
562
+ if len(result["missing_methods"]) > 3:
563
+ result["suggestion"] += f" ... 他 {len(result['missing_methods']) - 3} 件"
564
+
565
+ # 不足要件の提案
566
+ elif result["missing_requirements"]:
567
+ result["suggestion"] = "以下の要件が実装に反映されていない可能性があります。要件を満たす機能を実装してください。"
568
+ for i, req in enumerate(result["missing_requirements"][:3]):
569
+ result["suggestion"] += f"\n{i+1}. {req}"
570
+ if len(result["missing_requirements"]) > 3:
571
+ result["suggestion"] += f"\n... 他 {len(result['missing_requirements']) - 3} 件"
572
+
573
+ # 一般的な提案
574
+ else:
575
+ result["suggestion"] = "設計書と実装の間にいくつかの不一致があります。issues リストを確認して修正してください。"