-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbase_agent.py
More file actions
290 lines (236 loc) · 11.4 KB
/
Copy pathbase_agent.py
File metadata and controls
290 lines (236 loc) · 11.4 KB
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
# base_agent.py - 完全修復版
"""
批改Agent基礎類別 - 支援靈活的檔案命名規則
"""
from abc import ABC, abstractmethod
from typing import Dict, Any, Optional, List
import os
import json
import time
import random
from datetime import datetime
class BaseGradingAgent(ABC):
"""批改Agent抽象基礎類"""
def __init__(self, agent_id: str, base_dir: str = "base"):
self.agent_id = agent_id
self.base_dir = base_dir
self.agent_dir = os.path.join(base_dir, agent_id)
self.student_dir = os.path.join(self.agent_dir, "student")
self.teacher_dir = os.path.join(self.agent_dir, "teacher")
self.progress_file = os.path.join(base_dir, f"progress_{agent_id}.json")
# ✅ 定義進度檔案的命名模式
self.progress_patterns = [
f"progress_{agent_id}_{{assignment_id}}.json", # 新格式:progress_boolean_1.1.json
f"progress_{agent_id}.json" # 舊格式:progress_boolean.json
]
# ✅ 修復:統一方法名稱
self.ensure_directories()
def ensure_directories(self):
"""確保必要的目錄存在"""
for directory in [self.agent_dir, self.student_dir, self.teacher_dir]:
os.makedirs(directory, exist_ok=True)
@abstractmethod
def get_agent_info(self) -> Dict[str, Any]:
"""返回Agent資訊"""
pass
@abstractmethod
def analyze_assignment(self, assignment_file: str, assignment_id: str, **kwargs) -> Optional[Dict[str, Any]]:
"""分析單個作業"""
pass
@abstractmethod
def load_standard_answer(self, assignment_id: str, **kwargs) -> Any:
"""載入標準答案"""
pass
def get_progress_file(self, assignment_id: str) -> str:
"""獲取進度檔案路徑,支援多種命名格式"""
# 嘗試各種命名格式,找到存在的檔案
for pattern in self.progress_patterns:
file_path = os.path.join(self.base_dir, pattern.format(assignment_id=assignment_id))
if os.path.exists(file_path):
return file_path
# 如果都不存在,使用第一個格式作為新檔案
return os.path.join(self.base_dir, self.progress_patterns[0].format(assignment_id=assignment_id))
def load_progress(self, assignment_id: str) -> Dict[str, Any]:
"""載入進度 - 支援多種檔案格式"""
progress_file = self.get_progress_file(assignment_id)
if os.path.exists(progress_file):
try:
with open(progress_file, 'r', encoding='utf-8') as f:
return json.load(f)
except Exception as e:
print(f"載入進度檔案時發生錯誤: {e}")
return {}
return {}
def save_progress(self, assignment_id: str, progress_data: Dict[str, Any]):
"""保存進度 - 使用靈活命名"""
progress_file = self.get_progress_file(assignment_id)
try:
with open(progress_file, 'w', encoding='utf-8') as f:
json.dump(progress_data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存進度檔案時發生錯誤: {e}")
def get_student_folders(self) -> List[tuple]:
"""獲取學生資料夾列表"""
if not os.path.exists(self.student_dir):
return []
students = []
for folder in os.listdir(self.student_dir):
folder_path = os.path.join(self.student_dir, folder)
if os.path.isdir(folder_path):
info = self._parse_student_info(folder)
if info:
students.append((folder, info))
return students
def _parse_student_info(self, folder_name: str) -> Optional[Dict[str, str]]:
"""解析學生資訊"""
try:
clean_name = folder_name.replace('_assignsubmission_file', '')
clean_name = clean_name.replace('_submission', '')
clean_name = clean_name.replace('_file', '')
parts = clean_name.split('_')
if len(parts) >= 2:
name = parts[0]
student_number = parts[1]
elif len(parts) == 1:
if parts[0].isdigit():
name = f"學生{parts[0]}"
student_number = parts[0]
else:
name = parts[0]
student_number = "未知"
else:
raise ValueError(f"無法解析資料夾名稱: {folder_name}")
return {
'full_id': clean_name,
'name': name,
'student_number': student_number,
'original_folder': folder_name
}
except Exception as e:
print(f"解析學生資訊時發生錯誤: {str(e)}")
return None
def save_student_grade(self, assignment_id: str, student_info: Dict[str, str], result: Dict[str, Any]):
"""保存學生成績"""
student_folder_path = os.path.join(self.student_dir, student_info['original_folder'])
# 建立成績檔案名稱
grade_filename = self._generate_grade_filename(assignment_id, student_info, result)
grade_file = os.path.join(student_folder_path, grade_filename)
# 生成成績內容
content = self._generate_grade_content(assignment_id, result)
# 保存檔案
try:
with open(grade_file, 'w', encoding='utf-8') as f:
f.write(content)
print(f"已儲存 {student_info['name']} 的成績到 {grade_filename}")
return True
except Exception as e:
print(f"儲存 {student_info['name']} 成績時發生錯誤: {str(e)}")
return False
def _generate_grade_filename(self, assignment_id: str, student_info: Dict[str, str], result: Dict[str, Any]) -> str:
"""生成成績檔案名稱 - 可以被子類覆寫以自定義命名"""
# 基本格式
base_name = f"{student_info['full_id']}_{self.agent_id}_{assignment_id}"
# 添加架構版本資訊(如果有)
if 'architecture_version' in result:
base_name += f"_{result['architecture_version']}"
return f"{base_name}_grade.txt"
@abstractmethod
def _generate_grade_content(self, assignment_id: str, result: Dict[str, Any]) -> str:
"""生成成績內容"""
pass
def calculate_grade(self, score: float) -> str:
"""計算等第"""
score = float(score)
if score >= 90:
return 'A+'
elif score >= 80:
return 'A'
elif score >= 70:
return 'B'
elif score >= 60:
return 'C'
else:
return 'D'
def find_answer_file(self, student_dir: str, assignment_id: str) -> Optional[str]:
"""尋找學生作業檔案"""
# 支援的圖片格式
image_extensions = ('.png', '.jpg', '.jpeg')
pdf_extension = '.pdf'
# 定義可能的檔案名稱前綴
prefixes = [f"{self.agent_id}_{assignment_id}", assignment_id]
# 先查找特定命名檔案
for filename in os.listdir(student_dir):
for prefix in prefixes:
if filename.startswith(prefix):
if filename.lower().endswith(image_extensions + (pdf_extension,)):
return os.path.join(student_dir, filename)
# 如果沒找到特定命名檔案,使用任何圖片或PDF
for filename in os.listdir(student_dir):
if filename.lower().endswith(image_extensions + (pdf_extension,)):
return os.path.join(student_dir, filename)
return None
def run_grading(self, assignment_id: str, mode: str = 'auto', batch_size: int = 5, **kwargs) -> Dict[str, Any]:
"""執行批改流程"""
print(f"\n開始執行 {self.agent_id} 的批改流程...")
print(f"作業ID: {assignment_id}")
# 載入標準答案
try:
standard_data = self.load_standard_answer(assignment_id, **kwargs)
if not standard_data:
print(f"無法載入 {assignment_id} 的標準答案")
return {}
except Exception as e:
print(f"載入標準答案時發生錯誤: {str(e)}")
return {}
# ✅ 修復:載入進度時傳遞 assignment_id
progress = self.load_progress(assignment_id)
results = {}
# 獲取學生列表
students = self.get_student_folders()
if not students:
print("沒有找到學生資料夾")
return {}
print(f"找到 {len(students)} 個學生資料夾")
student_count = 0
for folder_name, student_info in sorted(students):
# 檢查是否已評分
if student_info['full_id'] in progress and progress[student_info['full_id']].get('grade'):
print(f"跳過 {student_info['name']} - 已經評分完成")
continue
# 尋找作業檔案
student_folder_path = os.path.join(self.student_dir, folder_name)
answer_file = self.find_answer_file(student_folder_path, assignment_id)
if not answer_file:
print(f"警告: {student_info['name']} 的作業檔案不存在")
continue
try:
# 執行分析
result = self.analyze_assignment(answer_file, assignment_id, standard_data=standard_data, **kwargs)
if result:
# 保存結果
results[student_info['full_id']] = result
progress[student_info['full_id']] = result
# ✅ 修復:保存進度時傳遞正確的參數
self.save_progress(assignment_id, progress)
# 保存成績
self.save_student_grade(assignment_id, student_info, result)
# 顯示結果
score_field = 'score' if 'score' in result else 'similarity'
print(f"{student_info['name']} (學號: {student_info['student_number']}) "
f"評分完成: {result['grade']} ({result[score_field]:.1f}分)")
student_count += 1
# 批量暫停
if student_count % batch_size == 0:
pause_time = random.uniform(5, 10)
print(f"\n已處理 {student_count} 個學生,暫停 {pause_time:.1f} 秒...")
time.sleep(pause_time)
else:
print(f"{student_info['name']} 評分失敗")
except Exception as e:
print(f"處理 {student_info['name']} 時發生錯誤: {str(e)}")
continue
if student_count == 0:
print(f"\n{self.agent_id} 作業 {assignment_id} 沒有需要處理的新檔案")
else:
print(f"\n{self.agent_id} 作業 {assignment_id} 處理完成,共處理 {student_count} 份作業")
return results