학교에서 가장 의미없고 힘들며 지루한 업무를 하나 뽑으라고 하면 나는 생기부 점검을 뽑을 것이다.
중학교에서는 생활기록부가 그리 중요치 않지만 고등학교에서는 입시와 관련되어 있기 때문에 무척 중요하다.
문제는 이런 생기부 점검에서 찾는 것이 고작 단순 오탈자, 기재 금지용어, 맞춤법 등이라는 것이다.
이 글은 이런 질문에서 시작한다.
이제 단순 점검은 AI에게 맡겨도 괜찮지 않을까?
1. AI를 이용한 생기부 생성의 한계점

고등학교 교사들이라면 모두 알겠지만, 손으로 쓴 생기부와 AI에게 맡긴 생기부는 문체부터 다르다.
특히 최근에는 입시 흐름에 따라 구체적인 관찰과 교사의 평가를 이어지도록 쓰는게 유행이다 보니 AI에게 맡기면 아래와 같은 문체가 나온다.
문제 풀이 과정에서 조건을 확인하고 식을 세우는 과정을 충실히 수행하였으며, 계산 결과를 점검하는 태도가 관찰됨.
식의 변형이나 그래프 해석 과정에서 근거를 들어 설명하려는 태도가 관찰되었고, 결과에 이르기까지의 사고 흐름을 비교적 명확하게 드러냄. 추론 중심의 문제 해결 역량을 보여줌.
계산 실수와 개념 적용의 차이를 구분하려는 노력이 나타났으며, 반복 학습을 통해 유사 문제 해결 시 정확도가 향상됨.
텍스트 속 표현에 주목해 의미를 추론하고, 자신의 경험과 연결해 사고를 확장함.
AI가 쓴 글의 특징은 학생이 구체적으로 무엇을 배우고 얻은 것인지 알기 어렵게 쓴다는 것이다.
예를들어 사고의 흐름, 추론 중심의 문제 해결 역량, 사고를 확장함과 같은 서술들은 교사가 무엇을 말하고 싶은건지 쉽게 알기 어렵다.
그리고 ~하려는 태도가 관찰되었고 와 같은 문체들도 무척 어색하다.
이런 부분들은 교사들이 수기로 생활기록부를 채울 수 밖에 없도록 만드는 원인이 된다.
그래서 교사들은 주로 오탈자, 띄어쓰기 2개, 사용 금지 용어들을 주로 찾게 된다.
2. chatGPT, NotebookLM으로는 불가능한가?

당연히 웹에서 제공하는 일반적인 LLM들로도 생기부 점검은 가능하다.
문제는 입력할 수 있는 내용의 길이 제한이 있고, 대화가 진행될 수록 초기에 주어진 프롬프트를 망각한다는 것이다.
그래서 중간중간 처음 입력했던 프롬프트를 다시 입력해주어야 하는 문제가 생긴다.
그럼, 매 대화를 처음하는 것처럼 프롬프트를 넣어서 점검하면 되지 않을까?
3. 데이터 클리닝

이렇게 생기부 분석을 자동화 하고 싶은데 가장 장애물이 되는 것은 업무포털이다.
위의 그림은 나이스에서 생기부를 다운받았을 때 데이터의 형태를 더미로 재현한 것이다.
아래에 예제 링크를 업로드 해둔다.
데이터 형태가 정말 최악 중의 최악이다.
이제 각각의 행에 과목, 학기, 번호 등 라벨을 모두 달아주고, 8월과 12열 같이 끊어진 부분은 이어준다.
그리고 페이지와 반복되는 목차는 삭제한다.
코드애 대한 설명은 주석으로 달아두었다.
import pandas as pd
from pathlib import Path
# 먼저 폴더 생성해 줌
cwd = Path.cwd()
resultFolter = cwd / "result"
resultFolter.mkdir(exist_ok=True)
targetFolder = cwd / "target"
# 타겟 폴더안의 .xlsx 파일을 for문으로 돈다.
for file in targetFolder.glob("*.xlsx"):
# 일단 4줄 뛰어넘고, 쓸데없는 null행과 열들은 잘라낸다.
df = pd.read_excel(file, skiprows=4)
df = df.iloc[:, :6]
stNumCol = [i for i in df.columns if "번" in i][0]
subjectNmae = [i for i in df.columns if "과" in i][0]
df.drop(df[df[subjectNmae] == subjectNmae].index, inplace=True)
df.dropna(subset=stNumCol, inplace=True)
df.reset_index(drop=True, inplace=True)
# 열을 돌면서 비어있는 행들을 모두 채워준다.
dropRowIndex = []
for i in range(len(df)):
rowSubNmae = df.iloc[i, 0]
rowGrade = df.iloc[i, 1]
rowClass = df.iloc[i, 2]
rowStNum = df.iloc[i, 3]
rowStName = df.iloc[i, 4]
rowInnerText = df.iloc[i, 5]
if i == 0 :
continue
if pd.isna(rowSubNmae):
df.iloc[i, 0] = df.iloc[i - 1, 0]
if pd.isna(rowGrade):
df.iloc[i, 1] = df.iloc[i - 1, 1]
if pd.isna(rowClass):
df.iloc[i, 2] = df.iloc[i - 1, 2]
if pd.isna(rowStNum):
df.iloc[i, 3] = df.iloc[i - 1, 3]
# 만약 글이 쪼개져 있으면 두 행의 내용을 합쳐주고 하나는 지운다.
# 굳이 dropRowIndex를 만들어 추가한 다음에 지우는 이유는
# for문이 실행되는 동안 df의 길이변화가 일어나지 않도록 하기 위함이다.
if df.iloc[i, 3] == df.iloc[i-1, 3]:
dropRowIndex.append(i)
df.iloc[i-1, 5] = df.iloc[i-1, 5] + df.iloc[i, 5]
df.drop(dropRowIndex, inplace=True)
df.reset_index(drop=True, inplace=True)
df.to_excel(cwd / "교과세특 학년별 과목별_정제.xlsx", index=False)이렇게 한번의 정제를 거치고 나면 데이터가 아래처럼 깔끔해진다.
이제 이걸 이용해 AI에게 검토 요청을 날리면 된다.

4. 프롬프트(prompt) 추출

모든 점검의 기준이 프롬프트이기에, 설계 전체에서 가장 신경써야 할 부분이다.
인터넷에서 pdf형태로 있는 생활기록부 기재요령을 다운 받아 프롬프트를 제작하면 가장 확실하다.
위의 사이트에서 파일을 다운받아 인공지능에 내가 점검하길 원하는 부분을 프롬프트로 제작해달라고 맡긴다.
그리고 해당 프롬프트를 가지고 API 요청을 해본다.
프롬프트의 일부를 남겨본다.
prompt = """
너는 학생부 세부능력 및 특기사항 검수 전문가다.
입력 JSON 데이터 중 “세부능력 및 특기사항(세특)” 항목만 점검하시오.
[기재 유의어 → 대체 표현 치환 규칙]
다음에 해당하는 경우, 표준 대체 표현으로 반드시 수정한다.
- Google(구글), NAVER(네이버), Daum(다음) 등 → 포털사이트
- Google Classroom(구글 클래스룸), EBS 온라인클래스 등 → 학습 플랫폼 / 원격 학습플랫폼
...중략...
[예외적 허용 항목 — 아래는 수정하지 않음]
- 단순 지명, 국가명, 산업명(학교 특정·진학 연계가 불가능한 경우)
- 교육부/교육청 등 공교육기관 명시
- 교내 수행평가·지필평가의 “정량 미제시” 성취 표현
- 수업 중 개념/원리 학습(예: Z점수 개념 이해, 반도체 공정 원리)
- 일상생활 경험(아르바이트 등)이 학습 연결 맥락일 때
- 일반 공간(아파트 등) 언급
- 미래형 포부/계획(‘가고 싶다’, ‘탐색 중’, ‘꿈꾸고 있다’)
- 독서 제목 및 저자 / 인용문 (‘ ’ 사용 가능)
- 단순 정보 제공이나 개념 학습의 수치(예: 최저임금 1만원, GDP 성장률 2%)는 금지 항목이 아님.
[판정 기준]
- “과거형 실적”이면 수정
- “미래 포부/계획/탐색”이면 유지
- 반드시 앞, 뒤 문맥 기반으로 판단할 것
"""5. 코드
OpenAI의 공식 사이트에 가면 쉬운 예제를 볼 수 있다.
아래 코드를 바탕으로 하거나, AI를 이용해 바이브 코딩으로 API 요청을 제작하면 된다.
AI의 대답을 자연어가 아닌 테이블의 형태로 받으면 가장 깔끔할 것 같았다.
생기부를 점검 후, 동료 교사들에게 해당 내용을 전달 할 때 메세지의 형태는 보통 아래와 같다.
과목 | 학번 | 이름 | 수정 전 | 수정 후 | 수정 사유 |
|---|---|---|---|---|---|
지구과학1 | 10100 | 사공힘센 | ~을 함으로써 | ~을 함으로서 | 조사수정 |
생명과학2 | 10300 | 남궁캥거루 | 학생 으로 수업 중 | 학생으로 수업 중 | 띄어쓰기 수정 |
이런식으로 응답을 강제하면 된다.
문제는 전체 과목을 검토해버리면 토큰의 길이가 너무 길어진다는 것.
이를 방지하기 위해 과목별로 내용을 쪼개고, 응답을 판다스로 정리한 후 한꺼번에 저장하면 된다.
# 먼저 파일을 과목별로 묶어 jsonDtat로 만든다.
data = df[df["과 목"] == sub]
jsonData = data.to_json(orient="records", force_ascii=False)
# AI에게 응답을 강제하도록 구조를 설계한다.
response = client.responses.create(
model="gpt-5.2",
input=[{
"role": "user",
"content": [
{"type": "input_text", "text": prompt},
{"type": "input_text", "text": jsonData}
]
},
],
text={
"format":{
"type": "json_schema",
"name": "response_format",
"strict": True,
"schema": {
"type": "object",
"properties": {
"rows": {
"type": "array",
"items": {
"type": "object",
"properties": {
"subjectName": {"type": "string"},
"studentName": {"type": "string"},
"before": {"type": "string"},
"after": {"type": "string"},
"edit": {"type": "string"},
"editReason": {"type": "string"}
},
"required": ["subjectName", "studentName", "before", "after", "edit", "editReason"],
"additionalProperties": False
}
}
},
"required": ["rows"],
"additionalProperties": False
}
}
}
)
responseJson = json.loads(response.output[0].content[0].text)["rows"]이렇게 한번 요청을 날린 뒤 응답을 정리하면 아래와 같은 내용을 볼 수 있다.

6. 프롬프트 반복 수정
이제 기본적인 뼈대는 잡았으니, 프롬프트를 계속해서 반복 수정하면 된다.
나는 더 자세한 출력을 위해 출력규칙, 판정 규칙을 보완하고 기재 유의어들을 모두 입력했다.
7. 사용 후기

무엇보다 정확도가 남다르다.
이렇게 점검을 하니 오탈자, 띄어쓰기 문제는 거의 대부분 해결되었다.
어떤 교과에서는 해외봉사 경험에 대한 내용이 입력되어 있는 것을 발견하고 수정하기도 했다.
생기부 지침이 매번 바뀌기에, 모든 것을 교사가 기억한다는 것 자체도 쉽지 않은 일이다.
그리고 이런 생기부 기재 활동이 교육의 질과 도대체 무슨 관련이 있는지도 잘 이해가 가지 않는다.
이렇게 교사의 잡무를 점차 줄여나가면서, 조금 더 교육에 힘쓸 수 있는 날이 오기만을 고대한다.
댓글을 불러오는 중...