jasoseo-agent / jd-recommendation /llm_functions.py
kyle8581's picture
.
594e237
import os
import json
import re
import yaml
from openai import OpenAI
# OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
# ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
import os
current_dir = os.path.dirname(os.path.abspath(__file__))
prompt_path = os.path.join(current_dir, 'prompt.yaml')
with open(prompt_path, 'r', encoding='utf-8') as f:
prompt_data = yaml.safe_load(f)
prompt_template = prompt_data['prompt']
def parse_jd_recommendation(content):
"""
AI ์‘๋‹ต์—์„œ ์ง๋ฌด๊ธฐ์ˆ ์„œ JSON์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
"""
try:
print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ๊ธธ์ด: {len(content)}")
print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ์ฒซ 200์ž: {repr(content[:200])}")
# ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ
cleaned_content = content.strip()
# 1. JSON ์ฝ”๋“œ ๋ธ”๋ก ์ฐพ๊ธฐ (```json ... ``` ํ˜•์‹)
json_patterns = [
r'```json\s*(\{.*?\})\s*```',
r'```\s*(\{.*?\})\s*```',
r'```json\s*(.*?)\s*```',
r'```\s*(.*?)\s*```'
]
for pattern in json_patterns:
json_match = re.search(pattern, cleaned_content, re.DOTALL)
if json_match:
json_str = json_match.group(1).strip()
print(f"JSON ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
# JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
json_str = re.sub(r'\n\s*', ' ', json_str)
json_str = re.sub(r',\s*}', '}', json_str)
try:
parsed_json = json.loads(json_str)
if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
return parsed_json['recommended_jd']
except json.JSONDecodeError as e:
print(f"JSON ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
# 2. ์ค‘๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ JSON ์ฐพ๊ธฐ
brace_patterns = [
r'\{.*?\}'
]
for pattern in brace_patterns:
brace_match = re.search(pattern, cleaned_content, re.DOTALL)
if brace_match:
json_str = brace_match.group(0).strip()
print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
# JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
json_str = re.sub(r'\n\s*', ' ', json_str)
json_str = re.sub(r',\s*}', '}', json_str)
try:
parsed_json = json.loads(json_str)
if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
return parsed_json['recommended_jd']
except json.JSONDecodeError as e:
print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
# 3. ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JSON์œผ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
try:
# ์ฝ”๋“œ ๋ธ”๋ก ๋งˆ์ปค ์ œ๊ฑฐ
if cleaned_content.startswith('```'):
lines = cleaned_content.split('\n')
start_idx = 1 if lines[0].startswith('```') else 0
end_idx = len(lines)
for i in range(len(lines)-1, -1, -1):
if lines[i].strip() == '```':
end_idx = i
break
cleaned_content = '\n'.join(lines[start_idx:end_idx])
cleaned_content = cleaned_content.strip()
parsed_json = json.loads(cleaned_content)
if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
return parsed_json['recommended_jd']
except json.JSONDecodeError as e:
print(f"์ „์ฒด JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
# 4. ์ง์ ‘ ํ…์ŠคํŠธ์—์„œ JD ๋‚ด์šฉ ์ถ”์ถœ ์‹œ๋„
print("์ง์ ‘ ํ…์ŠคํŠธ์—์„œ JD ๋‚ด์šฉ ์ถ”์ถœ ์‹œ๋„")
# "recommended_jd" ํ‚ค์›Œ๋“œ ๋’ค์˜ ๋‚ด์šฉ ์ฐพ๊ธฐ
jd_patterns = [
r'"recommended_jd"\s*:\s*"([^"]+)"',
r'recommended_jd["\s]*:\s*["\s]*([^"]+)["\s]*',
r'์ง๋ฌด๊ธฐ์ˆ ์„œ[:\s]*([^\n]+)',
]
for pattern in jd_patterns:
match = re.search(pattern, cleaned_content, re.IGNORECASE)
if match:
jd_content = match.group(1).strip()
if len(jd_content) > 10: # ์ตœ์†Œ ๊ธธ์ด ์ฒดํฌ
return jd_content
# 5. ์ตœํ›„์˜ ์ˆ˜๋‹จ: ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JD๋กœ ๊ฐ„์ฃผ (JSON ๋งˆ์ปค ์ œ๊ฑฐ)
print("์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JD๋กœ ๊ฐ„์ฃผ")
cleaned_text = re.sub(r'```[a-z]*\s*', '', cleaned_content)
cleaned_text = re.sub(r'```\s*', '', cleaned_text)
cleaned_text = re.sub(r'\{.*?\}', '', cleaned_text, flags=re.DOTALL)
cleaned_text = cleaned_text.strip()
if len(cleaned_text) > 20:
return cleaned_text
return "์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
except Exception as e:
print(f"JD ํŒŒ์‹ฑ ์ „์ฒด ์˜ค๋ฅ˜: {e}")
print(f"ํŒŒ์‹ฑ ์‹คํŒจํ•œ ์ปจํ…์ธ : {repr(content)}")
return f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}"
def generate_jd_recommendation(job_title, company_name, experience_level):
"""
OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
"""
try:
if not job_title or not company_name or not experience_level:
return "์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
# ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
prompt = prompt_template.format(
job_title=job_title,
company_name=company_name,
experience_level=experience_level
)
# OpenAI API ํ˜ธ์ถœ
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "๋‹น์‹ ์€ ์ฑ„์šฉ ๊ณต๊ณ  ์ž‘์„ฑ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ JSON ํ˜•์‹์œผ๋กœ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”."},
{"role": "user", "content": prompt}
],
temperature=0.3
)
content = response.choices[0].message.content
print(f"=== AI ์‘๋‹ต ์›๋ณธ ===")
print(content)
print(f"=== AI ์‘๋‹ต ๋ ===")
jd_content = parse_jd_recommendation(content)
if not jd_content or jd_content == "์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.":
return "์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", ""
# ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
result = f"""## ๐Ÿ“‹ {company_name} - {job_title} ์ง๋ฌด๊ธฐ์ˆ ์„œ
### ๐Ÿ’ผ **์ถ”์ฒœ ์ง๋ฌด๊ธฐ์ˆ ์„œ**
{jd_content}
---
### ๐Ÿ“Š **์ง๋ฌด ์ •๋ณด ์š”์•ฝ**
**๐Ÿข ํšŒ์‚ฌ:** {company_name}
**๐Ÿ’ผ ์ง๋ฌด:** {job_title}
**๐Ÿ“ˆ ๊ฒฝ๋ ฅ:** {experience_level}
### ๐Ÿ’ก **์ž์†Œ์„œ ์ž‘์„ฑ ํŒ**
์œ„ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋‹ค์Œ ์‚ฌํ•ญ์„ ์ž์†Œ์„œ์— ๋ฐ˜์˜ํ•ด๋ณด์„ธ์š”:
1. **ํ•ต์‹ฌ ์—…๋ฌด**: ์–ธ๊ธ‰๋œ ์ฃผ์š” ์—…๋ฌด์™€ ๊ด€๋ จ๋œ ๊ฒฝํ—˜์ด๋‚˜ ์—ญ๋Ÿ‰์„ ๊ฐ•์กฐ
2. **์š”๊ตฌ ์Šคํ‚ฌ**: ํ•„์š”ํ•œ ๊ธฐ์ˆ ์ด๋‚˜ ๋Šฅ๋ ฅ์— ๋Œ€ํ•œ ๋ณธ์ธ์˜ ์ค€๋น„๋„๋ฅผ ์–ดํ•„
3. **ํšŒ์‚ฌ ํŠน์„ฑ**: ํ•ด๋‹น ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ์—ฐ๊ด€๋œ ๊ด€์‹ฌ์‚ฌ๋‚˜ ๊ฒฝํ—˜์„ ์–ธ๊ธ‰
4. **์„ฑ์žฅ ์˜์ง€**: ์ง๋ฌด์—์„œ ์š”๊ตฌํ•˜๋Š” ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ๊ณผ ํ•™์Šต ์˜์ง€๋ฅผ ํ‘œํ˜„
---
*๋ณธ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋Š” AI๊ฐ€ ์ƒ์„ฑํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ ์ฑ„์šฉ๊ณต๊ณ ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์†Œ์„œ ์ž‘์„ฑ ์‹œ ์ฐธ๊ณ ์šฉ์œผ๋กœ ํ™œ์šฉํ•˜์„ธ์š”.*
"""
return result, jd_content
except Exception as e:
error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
**์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
"""
return error_msg, ""