“자신에게 적합한 장학금을 찾는 대학생을 위해 생성형 AI를 이용하여 각 사용자에게 맞는 장학금을 추천하고 이전 수혜자들의 조언을 바탕으로 장학금 수혜 팁을 제공해주는 서비스”
⇒ 이 중 두 번째 기능의 구현 방법을 작성해보려고 한다.
OpenAI를 호출하여 GPT-4o-mini 모델을 사용하여 프롬프트 엔지니어링을 통해 추천 로직을 구현했다. 이를 Django 프레임 워크를 활용해 웹 애플리케이션과 연동했다.
아래에서 자세한 과정에 대해 설명하겠다.
{
"대학구분": "해당없음",
"모집시작일": "2024-05-07",
"모집종료일": "2024-05-27",
"번호": 1515,
"상품구분": "장학금",
"상품명": "주거비지원 장학금",
"선발방법 상세내용": "○ 생활정도 100점 (평가요소 : 소득분위)※ 기초수급자 100점/차상위계층 98점 부여○ 동점자 처리기준 : 화성시 거주기간에 따라 점수 차등 부여",
"선발인원 상세내용": "○ 100명",
"성적기준 상세내용": "해당없음",
"소득기준 상세내용": "○ 소득분위 6분위 이내의 대학생",
"운영기관구분": "지자체",
"운영기관명": "재)화성시인재육성재단",
"자격제한 상세내용": "○ 한국장학재단 2024년 1학기 학자금 지원구간 통지서 미제출자○ 대학교 재학 상태가 아닌 경우",
"제출서류 상세내용": "○ 주민등록초본○ 재학증명서○ 2024년 1학기 학자금 지원구간 통지서 또는 저소득층 증빙서류○ 본인 또는 부모·친권자 명의의 주거현황 증빙서류○ 신청자 기준의 가족관계증명서(해당자)※ 자세한 사항은 홈페이지 공고문 참조",
"지역거주여부 상세내용": "○ 본인 또는 부모·친권자 중 1명이 주민등록상 공고일 기준 1년 이상 화성시에 계속 거주하고 있는 자",
"지원내역 상세내용": "○ 200만원(학기별 분할 지급)※ 생활비성 장학금",
"추천필요여부 상세내용": "해당없음",
"특정자격 상세내용": "○ 비수도권 소재 대학교에 재학하고 있는 대학생(전학년)※ 비수도권 : 수도권(서울·경기·인천) 이외의 국내지역○ 공고일 포함 2024년 재학기간(1·2학기) 내 학교 내 인근 거주 증빙이 가능한 자※ 자세한 사항은 홈페이지 공고문 참조",
"학과구분": "해당없음",
"학년구분": "해당없음",
"학자금유형구분": "지역연고",
"홈페이지 주소": "https://www.hstree.org"
}
장학금 데이터 형식은 위와 같고, 위의 데이터 형식을 바탕으로 추천 로직을 구현했다.
사용자 프로필을 바탕으로 사용자와 맞는 장학금 추천 로직 순서는 다음과 같다.
def filter_scholarships_by_date(current_date, scholarships):
filtered_scholarships = scholarships.filter(
recruitment_start__lte=current_date,
recruitment_end__gte=current_date
)
return filtered_scholarships
scholarships = filter_scholarships_by_date(current_date, scholarships)
위의 함수를 통해 사용자가 입력한 날짜(current_date)를 기준으로 장학금 모집 기간(recruitment_start 및 recruitment_end)을 비교하여 해당 날짜에 모집 중인 장학금만 필터링한다.
함수의 인수
current_date: 클라이언트로부터 POST로 받은 날짜scholarships: db에 저장된 모든 장학금 데이터반환값
filtered_scholarships: 주어진 날짜에 모집 중인 장학금def filter_basic(scholarships, user_info):
# 대학구분 필터링
filtered_by_university = scholarships.filter(
models.Q(university_type__icontains=user_info.univ_category) |
models.Q(university_type__icontains='해당없음') |
models.Q(university_type__icontains='특정대학')
)
# 학년구분 필터링
filtered_by_academic_year = filtered_by_university.filter(
models.Q(academic_year_type__icontains=user_info.semester) |
models.Q(academic_year_type__icontains='해당없음')
)
# 학과구분 필터링
filtered_by_major = filtered_by_academic_year.filter(
models.Q(major_field_type__icontains=user_info.major_category) |
models.Q(major_field_type__icontains='제한없음') |
models.Q(major_field_type__icontains='특정학과')
)
return filtered_by_major
scholarships = filter_basic(scholarships, user_profile)
위의 함수를 통해 사용자 프로필 정보(user_info)를 기준으로 대학구분, 학년구분, 학과구분 조건에 따라 장학금을 필터링한다.
대학구분, 학년구분, 학과구분에서 특정 조건이 없으면 ‘제한없음’이나 ‘해당없음’이라고 적혀있기 때문에 해당 장학금들은 필터링되지 않도록 했다.
대학구분 필터링
user_info.univ_category)과 장학금의 university_type이 일치하는지 확인학년구분 필터링
user_info.semester)과 장학금의 academic_year_type이 일치하는지 확인학과구분 필터링
user_info.major_category)과 장학금의 major_field_type이 일치하는지 확인university_type이 '특정대학'인 경우, 나머지 조건을 뒤의 추가 필터링 단계(6번) 단계)에서 처리하기 위해 필터링하지 않고 남겨뒀다.
함수의 인수
user_info: 사용자 프로필 정보scholarships: 위의 1번에서 필터링 된 후 남은 장학금 데이터def separate_scholarships(scholarships):
no_criteria_scholarships = scholarships.filter(residency_requirement_details="해당없음")
criteria_scholarships = scholarships.exclude(residency_requirement_details="해당없음")
return no_criteria_scholarships, criteria_scholarships
no_criteria_scholarships, criteria_scholarships = separate_scholarships(scholarships)
거주 지역 조건이 없는 장학금은 ‘해당 없음’이라고 적혀있으므로 위의 함수를 통해 거주 지역 조건이 있는 장학금과 없는 장학금으로 분리한다.
반환 값
no_criteria_scholarships: 거주 지역 조건이 없는 장학금criteria_scholarships: 거주 지역 조건이 있는 장학금def gpt_filter_region(user_info, criteria_scholarships):
user_info_text = f"학생의 거주지역: {user_info.residence}"
scholarships_text = "\n".join([
f"번호: {scholarship.product_id}, 운영기관명: {scholarship.foundation_name}, 상품명: {scholarship.name}, 지역 거주 여부: {scholarship.residency_requirement_details}, 특정 자격: {scholarship.specific_qualification_details}"
for scholarship in scholarships
])
prompt = f"""
{user_info_text}
아래는 현재 모집 중인 장학금 목록입니다.
이 목록에서 학생의 거주 지역 조건과 일치하는 장학금만 반환해 주세요.
조건에 맞지 않는 장학금은 제외해 주세요.
주의사항:
1. 학생의 거주지역이 명시된 장학금이 아닐 경우, 해당 장학금을 제외해 주세요.
2. '관내 지역'이라는 표현이 있는 경우, 장학금에서 언급된 지역과 학생의 거주지역이 정확히 일치해야 합니다. 예를 들어, '의성군 관내'라면 학생이 의성군에 거주하는 경우만 해당 장학금이 적합합니다.
3. 학생의 거주지역이 장학금의 지역 조건과 일치하지 않으면 그 장학금은 제외해 주세요.
장학금 목록:
{scholarships_text}
"""
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=1000,
temperature=0.3
)
remaining_scholarships_text = response['choices'][0]['message']['content'].strip()
remaining_scholarships_ids = re.findall(r"번호: (\d+)", remaining_scholarships_text)
return [int(scholarship_id) for scholarship_id in remaining_scholarships_ids]
filtered_ids = gpt_filter_region(user_profile, criteria_scholarships)
위의 함수에서 사용자의 거주 지역 정보와 장학금의 거주 지역 조건을 비교하여, 조건에 맞는 장학금을 필터링한다. 이를 위해서 GPT-4o-mini 모델에 프롬프트를 전달하여 거주 지역 조건만을 기준으로 장학금을 걸러낸다.
프롬프트의 내용
user_info.residence에 포함된 사용자의 거주 지역 정보를 프롬프트에 명시함residency_requirement_details 필드를 포함해 장학금의 거주 지역 조건을 명확히 전달결과
filtered_ids: 거주 지역 조건이 있는 장학금(criteria_scholarships)을 위의 함수를 통해 필터링되고 남은 장학금들의 고유 id → 사용자의 거주 지역과 일치하는 장학금들filtered_scholarships = criteria_scholarships.filter(product_id__in=filtered_ids)
final_scholarships = filtered_scholarships | no_criteria_scholarships
filtered_scholarships: 4번에서 필터링되고 남은 장학금들no_criteria_scholarships: 지역 조건이 없는 장학금들final_scholarships: 4번에서 필터링되고 남은 장학금들과 지역 조건이 없는 장학금을 합친 장학금들# 소득분위와 중위소득 비율 매핑
income_brackets = {
'1분위': '30%',
'2분위': '50%',
'3분위': '70%',
'4분위': '90%',
'5분위': '100%',
'6분위': '130%',
'7분위': '150%',
'8분위': '200%',
'9분위': '300%',
'10분위': '300% 초과'
}
# GPT 기반 추천 장학금 필터링
def recommend_scholarships(user_info, scholarships):
middle_income_ratio = income_brackets.get(user_info.income, '알 수 없음') # 필드로 접근
user_info_text = f"""
학생의 프로필:
- 대학구분: {user_info.univ_category}
- 성별: {user_info.gender}
- 나이: {user_info.age}
- 대학교: {user_info.university}
- 학년: {user_info.semester}
- 학과: {user_info.major_category}
- 전공: {user_info.major}
- 평균 성적: {user_info.totalGPA} (4.5점 만점)
- 소득분위: {user_info.income} (중위소득 비율: {middle_income_ratio})
- 거주지역: {user_info.residence}
- 기타 정보: {user_info.etc}
"""
scholarships_text = "\n".join([
f"번호: {scholarship.product_id}, 운영기관명: {scholarship.foundation_name}, 상품명: {scholarship.name}, 성적 기준: {scholarship.grade_criteria_details}, 소득 기준: {scholarship.income_criteria_details}, 지역 거주 여부: {scholarship.residency_requirement_details}, 특정 자격: {scholarship.specific_qualification_details}"
for scholarship in scholarships
])
prompt = f"""
{user_info_text}
위 학생의 조건에 맞는 장학금만 추천해 주세요.
1. 성적 기준: 사용자의 성적이 장학금의 성적 기준보다 높거나 '해당 없음'인 장학금만 추천하세요.
2. 소득 기준: 사용자의 소득분위가 장학금의 소득 기준보다 낮거나 '해당 없음'인 장학금만 추천하세요.
3. 거주지역 요건: 사용자의 거주지역과 장학금의 거주지역 기준이 일치하거나 '해당 없음'인 장학금만 추천하세요.
4. 전공 요건: 사용자의 전공과 일치하거나 '전공 불문'인 장학금만 추천하세요.특정 전공을 요구하는 장학금에서 사용자의 전공과 맞지 않는 경우에는 제외해 주세요.
5. 특정 자격 요건: 특정 자격(예: 특정 직업의 자녀, 특정 대회 입상자 등)을 요구하는 장학금은, 사용자가 해당 조건을 충족하지 않는 경우 제외해 주세요.
6. 기타: 사용자의 프로필의 기타 정보에 적힌 내용과 일치하는 장학금만 추천해 주세요.
사용자에게 적합한 장학금이면 모두 추천해 주세요.
**사용자와 조건이 하나라도 일치하지 않으면 제외해주세요.**
장학금 목록:
{scholarships_text}
출력 형식은 아래와 같이 해줘
(예시)
1. **장학금 번호: 1,119**
- 운영기관명:
- 성적 기준:
- 소득 기준:
- 지역 거주 여부:
- 특정 자격:
2. **장학금 번호: 1,244**
- 운영기관명:
- 성적 기준:
- 소득 기준:
- 지역 거주 여부:
- 특정 자격:
"""
response = openai.ChatCompletion.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=1000
)
recommended_scholarships_text = response['choices'][0]['message']['content'].strip()
위의 함수에서 5번에서 합친 장학금 데이터를 사용자 프로필 정보와 비교하여, 모든 조건을 충족하는 장학금을 필터링한다. 이를 위해 GPT-4o-mini 모델에 프롬프트를 전달하여 조건 비교를 수행한다.
성적 기준, 소득 기준, 거주지역 요건, 전공 요건, 특정 자격 요건 등 각 조건별로 장학금과 사용자 정보를 비교하여 하나라도 충족되지 않으면 해당 장학금 제외하도록 프롬프트를 작성했다. 그리고 데이터베이스에 저장할 때 장학금 id로 저장하기 떄문에 출력형식을 지정하여 반환하도록 했다.
사용자 정보
user_info)을 기반으로 성적, 소득분위, 거주지역 등 정보를 문자열로 작성하여 프롬프트에 포함장학금 정보
프롬프트 내용
for scholarship in final_scholarships:
RecommendResult.objects.create(
user=user_profile.user,
scholarship=scholarship,
product_id=scholarship.product_id
)
serializer = self.get_serializer(final_scholarships, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
final_scholarships: 6번까지의 과정을 통해 최종 추천된 장학금 리스트RecommendResult 모델을 사용하여 최종 추천된 장학금을 사용자(user_profile.user)와 연결하여 추천된 장학금의 product_id도 함께 데이터베이스에 저장한다.









약 2000개의 장학금 데이터를 GPT에 바로 보내면 성능이 떨어지므로 먼저 필터링하여 장학금 개수를 줄이고, GPT를 통해 추천받도록 했다.