22from sqlalchemy .orm import Session
33from database .nolly import get_db
44from crud .answer import get_answer_history , get_answer_scores
5- from schemas .answer import AnswerResponse , AnswerScoreResponse
5+ from schemas .answer import AnswerResponse , AnswerScoreResponse , AnswerSubmit
6+ from dependencies import get_tokenizer , get_model
7+ from models .question import Question
8+ from models .answer import Answer
9+ import torch
10+ import json
11+ import re
612
713router = APIRouter ()
814
@@ -22,4 +28,133 @@ async def get_answer_scores_api(user_id: int, question_id: int, db: Session = De
2228 if not answer_scores :
2329 raise HTTPException (status_code = 404 , detail = "채점 결과를 찾을 수 없습니다." )
2430
25- return answer_scores
31+ return answer_scores
32+
33+ @router .post ("grade/{user_id}/{question_id}" )
34+ async def grade_answers (
35+ user_id : int , question_id : int ,
36+ answer_data : AnswerSubmit , tokenizer = Depends (get_tokenizer ), model = Depends (get_model ),
37+ db : Session = Depends (get_db )
38+ ):
39+ question = db .query (Question ).filter (
40+ Question .question_id == question_id
41+ ).first ()
42+
43+ if not question :
44+ raise HTTPException (status_code = 404 , detail = "해당 문제를 찾을 수 없습니다." )
45+
46+ description = question .description # 문제 설명
47+
48+ # ✅ 패딩 토큰 명확하게 설정 (경고 방지)
49+ if tokenizer .pad_token is None :
50+ tokenizer .pad_token = tokenizer .eos_token
51+
52+ # 1. 사용자 답변 불러오기 (JSON 출력을 강제하는 프롬프트 추가)
53+ input_text = [
54+ {"role" : "system" , "content" : """답변을 다음 항목으로 나누어 JSON 형식으로 평가해 주세요.
55+ 반드시 JSON 객체 하나로 반환하세요.
56+ 예시:
57+ {
58+ "논리력": {"점수": 8, "해설": "설명이 논리적입니다."},
59+ "사고력": {"점수": 7, "해설": "분석력이 뛰어납니다."},
60+ "창의력": {"점수": 9, "해설": "새로운 아이디어가 포함되었습니다."},
61+ "설득력": {"점수": 6, "해설": "설명이 명확합니다."},
62+ "추론의 깊이": {"점수": 7, "해설": "근거가 논리적입니다."}
63+ }
64+ """ .strip ()},
65+ {"role" : "assistant" , "content" : f"{ description } " },
66+ {"role" : "user" , "content" : f"{ answer_data .answer } " }
67+ ]
68+
69+ # 2. 모델에 넣을 수 있도록 토큰화
70+ inputs = tokenizer .apply_chat_template (
71+ input_text ,
72+ add_generation_prompt = True ,
73+ return_tensors = "pt" ,
74+ padding = True ,
75+ truncation = True
76+ ).to (model .device )
77+
78+ terminators = [
79+ tokenizer .convert_tokens_to_ids ("<|end_of_text|>" ),
80+ tokenizer .convert_tokens_to_ids ("<|eot_id|>" )
81+ ]
82+
83+ # 3. 모델 추론 수행
84+ outputs = model .generate (
85+ inputs ,
86+ max_new_tokens = 1024 ,
87+ pad_token_id = tokenizer .pad_token_id ,
88+ eos_token_id = tokenizer .eos_token_id ,
89+ do_sample = True ,
90+ temperature = 0.6 ,
91+ top_p = 0.9
92+ )
93+
94+ # 4. 모델 결과 디코딩
95+ generated_text = tokenizer .decode (outputs [:, inputs .shape [1 ]:][0 ], skip_special_tokens = True )
96+ print ("🔍 모델 생성 결과:" , generated_text )
97+
98+ # ✅ JSON 자동 보정 (불완전한 JSON을 수정)
99+ try :
100+ # 1️⃣ JSON 내부 개별 `{}` 블록을 하나의 JSON 객체로 병합
101+ json_str = re .sub (r"}\s*{" , "},{" , generated_text .strip ()) # 중괄호 사이 개행 문제 수정
102+ json_str = f"{{{ json_str .strip ()} }}" if not json_str .startswith ("{" ) else json_str # 중괄호 감싸기
103+ json_str = json_str .replace ("\n " , "" ).replace ("\t " , "" ) # 불필요한 줄바꿈 제거
104+ json_str = re .sub (r",\s*}" , "}" , json_str ) # 마지막 쉼표 제거
105+
106+ result = json .loads (json_str ) # JSON 변환
107+
108+ except json .JSONDecodeError :
109+ raise HTTPException (status_code = 500 , detail = f"모델 응답을 JSON으로 변환하는데 실패했습니다. 출력된 텍스트: { generated_text } " )
110+
111+ # 5. 점수와 리뷰 추출
112+ scores = [
113+ result .get ("논리력" , {}).get ("점수" , 0 ),
114+ result .get ("사고력" , {}).get ("점수" , 0 ),
115+ result .get ("창의력" , {}).get ("점수" , 0 ),
116+ result .get ("설득력" , {}).get ("점수" , 0 ),
117+ result .get ("추론의 깊이" , {}).get ("점수" , 0 )
118+ ]
119+
120+ reviews = [
121+ result .get ("논리력" , {}).get ("해설" , "" ),
122+ result .get ("사고력" , {}).get ("해설" , "" ),
123+ result .get ("창의력" , {}).get ("해설" , "" ),
124+ result .get ("설득력" , {}).get ("해설" , "" ),
125+ result .get ("추론의 깊이" , {}).get ("해설" , "" )
126+ ]
127+
128+ total_score = sum (scores ) / len (scores ) if scores else 0
129+
130+ # 6. DB 저장
131+ existing_answer = db .query (Answer ).filter (
132+ Answer .user_id == user_id ,
133+ Answer .question_id == question_id
134+ ).first ()
135+
136+ if existing_answer :
137+ existing_answer .content = answer_data .answer
138+ existing_answer .creativity = scores [2 ]
139+ existing_answer .logic = scores [0 ]
140+ existing_answer .thinking = scores [1 ]
141+ existing_answer .persuasion = scores [3 ]
142+ existing_answer .depth = scores [4 ]
143+ existing_answer .creativity_review = reviews [2 ]
144+ existing_answer .logic_review = reviews [0 ]
145+ existing_answer .thinking_review = reviews [1 ]
146+ existing_answer .persuasion_review = reviews [3 ]
147+ existing_answer .depth_review = reviews [4 ]
148+ existing_answer .total_score = total_score
149+ else :
150+ db .add (Answer (
151+ user_id = user_id , question_id = question_id , content = answer_data .answer ,
152+ creativity = scores [2 ], logic = scores [0 ], thinking = scores [1 ],
153+ persuasion = scores [3 ], depth = scores [4 ],
154+ creativity_review = reviews [2 ], logic_review = reviews [0 ],
155+ thinking_review = reviews [1 ], persuasion_review = reviews [3 ], depth_review = reviews [4 ],
156+ total_score = total_score
157+ ))
158+
159+ db .commit ()
160+ return {"message" :"답변 제출 완료" }
0 commit comments