-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
293 lines (238 loc) · 10.6 KB
/
Copy pathmain.py
File metadata and controls
293 lines (238 loc) · 10.6 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
291
292
293
# main.py
"""
新的插件化批改系統統一入口點
支援動態載入Agent插件,每個Agent都可以獨立運作
"""
import os
import sys
import argparse
import time
import csv
from datetime import datetime
from typing import Dict, Any, Optional
# 導入插件系統
from agent_registry import registry
from utils import validate_api_key
def setup_environment():
"""設置環境和載入插件"""
# 檢查API金鑰
if not validate_api_key():
print("錯誤: 請先設置 OPENAI_API_KEY 環境變數")
sys.exit(1)
# 自動發現並註冊Agent
agents_dir = "agents"
if os.path.exists(agents_dir):
registry.auto_discover_agents(agents_dir)
# 手動註冊內建Agent(如果agents目錄不存在)
if not registry.list_agents():
try:
from agents.essay_agent import EssayAgent
from agents.boolean_agent import BooleanAgent
from agents.exam_agent import ExamAgent
registry.register_agent(EssayAgent)
registry.register_agent(BooleanAgent)
registry.register_agent(ExamAgent)
except ImportError as e:
print(f"警告: 無法載入部分Agent: {e}")
def generate_csv_report(agent_id: str, assignment_id: str, base_dir: str = "base") -> Optional[str]:
"""從進度檔案生成CSV報告"""
progress_file = os.path.join(base_dir, f"progress_{agent_id}_{assignment_id}.json")
if not os.path.exists(progress_file):
# 嘗試舊格式的進度檔案
old_progress_file = os.path.join(base_dir, f"progress_{agent_id}.json")
if os.path.exists(old_progress_file):
progress_file = old_progress_file
else:
print("沒有找到批改進度檔案,無法生成 CSV 報告")
return None
try:
import json
with open(progress_file, 'r', encoding='utf-8') as f:
progress_data = json.load(f)
except Exception as e:
print(f"讀取進度檔案時發生錯誤: {str(e)}")
return None
if not progress_data:
print("進度檔案為空,無法生成 CSV 報告")
return None
# 創建報告目錄
reports_dir = os.path.join(base_dir, "reports")
os.makedirs(reports_dir, exist_ok=True)
# 生成檔案名稱
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"{agent_id}_{assignment_id}_grades_{timestamp}.csv"
csv_path = os.path.join(reports_dir, filename)
try:
# 準備 CSV 數據
csv_rows = []
for student_id, result in progress_data.items():
# 解析學生資訊
folder_parts = student_id.split('_')
student_name = folder_parts[0] if len(folder_parts) >= 1 else "未知"
# 獲取學號
student_number = result.get('student_id', '未識別')
if student_number == '未識別' and len(folder_parts) >= 2:
student_number = folder_parts[1]
# 獲取分數(根據不同作業類型)
if agent_id == 'essay':
score = result.get('similarity', 0)
else:
score = result.get('score', 0)
# 基本資訊
row = {
'學號': student_number,
'姓名': student_name,
'等第': result.get('grade', '未知'),
'分數': round(score, 1)
}
# 如果是心得報告,添加架構版本資訊
if agent_id == 'essay' and 'architecture_version' in result:
row['架構版本'] = result['architecture_version'].upper()
if 'key_concept_coverage' in result:
row['概念覆蓋率'] = f"{result['key_concept_coverage']:.1f}%"
if 'concept_detail' in result:
row['概念詳細度'] = f"{result['concept_detail']:.1f}/10"
csv_rows.append(row)
# 按學號排序
csv_rows.sort(key=lambda x: x['學號'])
# 定義欄位順序
field_order = ['學號', '姓名', '等第', '分數']
# 如果是心得報告,添加額外欄位
if agent_id == 'essay' and csv_rows and '架構版本' in csv_rows[0]:
field_order.extend(['架構版本', '概念覆蓋率', '概念詳細度'])
# 寫入 CSV
with open(csv_path, 'w', newline='', encoding='utf-8-sig') as csvfile:
writer = csv.DictWriter(csvfile, fieldnames=field_order)
writer.writeheader()
writer.writerows(csv_rows)
print(f"\n成績報告已匯出到: {csv_path}")
print(f"包含 {len(csv_rows)} 位學生的成績")
return csv_path
except Exception as e:
print(f"生成 CSV 報告時發生錯誤: {str(e)}")
return None
def display_grade_statistics(results: Dict[str, Any], agent_id: str):
"""顯示評分結果統計"""
if not results:
print("\n沒有需要批改的新作業")
return
print(f"\n{agent_id.upper()} 批改完成! 共處理 {len(results)} 份作業")
# 計算各等第數量
grades = {'A+': 0, 'A': 0, 'B': 0, 'C': 0, 'D': 0}
total_score = 0
for result in results.values():
if result['grade'] in grades:
grades[result['grade']] += 1
# 獲取分數 (不同作業類型可能使用不同的分數欄位)
if 'score' in result:
score = result['score']
elif 'similarity' in result:
score = result['similarity']
else:
score = 0
total_score += score
avg_score = total_score / len(results) if results else 0
print(f"平均分數: {avg_score:.1f}")
print("等第分布:")
for grade, count in grades.items():
if count > 0: # 只顯示有人數的等第
print(f" {grade}: {count} 人")
# 如果是心得報告且有架構版本資訊,顯示額外統計
if agent_id == 'essay':
coverage_scores = [result.get('key_concept_coverage', 0) for result in results.values() if 'key_concept_coverage' in result]
detail_scores = [result.get('concept_detail', 0) for result in results.values() if 'concept_detail' in result]
if coverage_scores:
print(f"平均概念覆蓋率: {sum(coverage_scores)/len(coverage_scores):.1f}%")
if detail_scores:
print(f"平均概念詳細度: {sum(detail_scores)/len(detail_scores):.1f}/10")
def list_available_agents():
"""列出所有可用的Agent"""
agents = registry.list_agents()
if not agents:
print("沒有可用的Agent")
return
print("可用的批改Agent:")
for agent_id in agents:
agent = registry.get_agent(agent_id)
if agent:
info = agent.get_agent_info()
print(f" {agent_id}: {info.get('name', '未知')} - {info.get('description', '無描述')}")
def main():
"""主函數"""
parser = argparse.ArgumentParser(description='插件化批改系統')
# 全域選項
parser.add_argument('--list-agents', action='store_true', help='列出所有可用的Agent')
parser.add_argument('--base-dir', default='base', help='基礎目錄路徑')
parser.add_argument('--version', action='version', version='插件化批改系統 v3.0')
# 子命令解析器
subparsers = parser.add_subparsers(dest='agent_id', help='要使用的Agent')
# 動態添加Agent子命令
setup_environment()
for agent_id in registry.list_agents():
agent = registry.get_agent(agent_id)
if agent:
info = agent.get_agent_info()
agent_parser = subparsers.add_parser(agent_id, help=info.get('description', f'{agent_id} 批改'))
agent_parser.add_argument('assignment_id', help='作業ID')
agent_parser.add_argument('--test', action='store_true', help='測試模式,輸出詳細資訊')
agent_parser.add_argument('--batch-size', type=int, default=5, help='批量處理大小')
agent_parser.add_argument('--no-csv', action='store_true', help='不生成CSV報告')
# Agent特定參數
if agent_id == 'essay':
agent_parser.add_argument('--force-regenerate', action='store_true', help='強制重新生成標準答案')
elif agent_id == 'boolean':
agent_parser.add_argument('--force-regenerate', action='store_true', help='強制重新生成標準答案')
args = parser.parse_args()
# 列出可用Agent
if args.list_agents:
list_available_agents()
return
# 如果沒有指定Agent,顯示幫助
if not args.agent_id:
print("錯誤: 未指定要使用的Agent")
print("\n可用的Agent:")
list_available_agents()
print("\n使用範例:")
print(" python main.py essay 1.2 # 批改心得報告")
print(" python main.py boolean 1.1 # 批改布林函數")
print(" python main.py exam midterm # 批改考卷")
print(" python main.py --list-agents # 列出所有Agent")
return
# 獲取指定的Agent
agent = registry.get_agent(args.agent_id, args.base_dir)
if not agent:
print(f"錯誤: 找不到Agent '{args.agent_id}'")
print("使用 --list-agents 查看可用的Agent")
return
print(f"使用 {args.agent_id.upper()} Agent 進行批改...")
start_time = time.time()
try:
# 準備Agent特定參數
kwargs = {}
if hasattr(args, 'force_regenerate') and args.force_regenerate:
kwargs['force_regenerate'] = True
# 執行批改
mode = 'test' if args.test else 'auto'
results = agent.run_grading(
args.assignment_id,
mode=mode,
batch_size=args.batch_size,
**kwargs
)
# 顯示結果統計
display_grade_statistics(results, args.agent_id)
# 生成 CSV 報告
if not args.no_csv and results:
generate_csv_report(args.agent_id, args.assignment_id, args.base_dir)
except KeyboardInterrupt:
print("\n程式被使用者中斷")
except Exception as e:
print(f"\n執行時發生錯誤: {str(e)}")
import traceback
traceback.print_exc()
finally:
end_time = time.time()
elapsed = end_time - start_time
print(f"\n批改總耗時: {elapsed:.1f} 秒")
if __name__ == "__main__":
main()