kyle8581 commited on
Commit
594e237
ยท
1 Parent(s): a71ae02
Files changed (38) hide show
  1. commonly-asked-question/.gradio/certificate.pem +31 -0
  2. commonly-asked-question/__pycache__/llm_functions.cpython-312.pyc +0 -0
  3. commonly-asked-question/eval.json +93 -0
  4. commonly-asked-question/llm_functions.py +251 -0
  5. commonly-asked-question/main.py +351 -0
  6. commonly-asked-question/prompt.yaml +43 -0
  7. company-size-classification/.gradio/certificate.pem +31 -0
  8. company-size-classification/__pycache__/llm_functions.cpython-312.pyc +0 -0
  9. company-size-classification/eval.json +48 -0
  10. company-size-classification/llm_functions.py +110 -0
  11. company-size-classification/main.py +228 -0
  12. company-size-classification/prompt.yaml +85 -0
  13. industry-classification/.gradio/certificate.pem +31 -0
  14. industry-classification/__pycache__/llm_functions.cpython-312.pyc +0 -0
  15. industry-classification/eval.json +256 -0
  16. industry-classification/llm_functions.py +271 -0
  17. industry-classification/main.py +305 -0
  18. industry-classification/prompt.yaml +128 -0
  19. jasoseo-context-report/.gradio/certificate.pem +31 -0
  20. jasoseo-context-report/__pycache__/llm_functions.cpython-312.pyc +0 -0
  21. jasoseo-context-report/eval.json +123 -0
  22. jasoseo-context-report/llm_functions.py +282 -0
  23. jasoseo-context-report/main.py +314 -0
  24. jasoseo-context-report/prompt.yaml +96 -0
  25. jd-recommendation/__pycache__/llm_functions.cpython-312.pyc +0 -0
  26. jd-recommendation/eval.json +117 -0
  27. jd-recommendation/llm_functions.py +205 -0
  28. jd-recommendation/main.py +329 -0
  29. jd-recommendation/prompt.yaml +69 -0
  30. jd-recommendation/propmt.yaml +0 -0
  31. main.py +474 -0
  32. question-recommendation/.gradio/certificate.pem +31 -0
  33. question-recommendation/__pycache__/llm_functions.cpython-312.pyc +0 -0
  34. question-recommendation/eval.json +67 -0
  35. question-recommendation/llm_functions.py +116 -0
  36. question-recommendation/main.py +190 -0
  37. question-recommendation/prompt.yaml +34 -0
  38. requirements.txt +6 -0
commonly-asked-question/.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
commonly-asked-question/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (9.86 kB). View file
 
commonly-asked-question/eval.json ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "sample_question_generation_eval": {
3
+ "task_definition": {
4
+ "description": "Based on the user's input (company, job, experience level) and a predefined list of common interview questions, select and refine 3 sample questions that are most relevant to the user's situation.",
5
+ "input": {
6
+ "job_title": "string",
7
+ "company_name": "string",
8
+ "experience_level": "์‹ ์ž… | ๊ฒฝ๋ ฅ | ์ธํ„ด | ๊ธฐํƒ€",
9
+ "common_questions": ["string", "..."]
10
+ },
11
+ "output": {
12
+ "sample_questions": ["string", "string", "string"]
13
+ }
14
+ },
15
+ "examples": [
16
+ {
17
+ "input": {
18
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
19
+ "company_name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
20
+ "experience_level": "์‹ ์ž…",
21
+ "common_questions": ["์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”", "์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "๊ฐ€์žฅ ๋„์ „์ ์ธ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”"]
22
+ },
23
+ "output": {
24
+ "sample_questions": [
25
+ "์ˆ˜๋งŽ์€ ํ•€ํ…Œํฌ ๊ธฐ์—… ์ค‘ ์™œ 'ํ† ์Šค'์— ์ง€์›ํ•˜์…จ๋‚˜์š”?",
26
+ "๊ฐœ๋ฐœ์ž๋กœ์„œ ๊ฐ€์žฅ ํฌ๊ฒŒ ์„ฑ์žฅํ–ˆ๋˜ ๊ธฐ์ˆ ์  ๋„์ „ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”?",
27
+ "๋ณธ์ธ์ด ํ† ์Šค์˜ ๊ธฐ์ˆ  ๋ฌธํ™”(์ž์œจ๊ณผ ์ฑ…์ž„, ๋†’์€ ๋™๋ฃŒ ์‹ ๋ขฐ ๋“ฑ)์™€ ์ž˜ ๋งž๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋ฅผ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์†Œ๊ฐœํ•ด ์ฃผ์„ธ์š”."
28
+ ]
29
+ }
30
+ },
31
+ {
32
+ "input": {
33
+ "job_title": "์‹ํ’ˆ๋งˆ์ผ€ํŒ…",
34
+ "company_name": "CJ์ œ์ผ์ œ๋‹น",
35
+ "experience_level": "๊ฒฝ๋ ฅ",
36
+ "common_questions": ["์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”", "์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "๋ณธ์ธ์˜ ๊ฐ•์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ๊ณต ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”"]
37
+ },
38
+ "output": {
39
+ "sample_questions": [
40
+ "CJ์ œ์ผ์ œ๋‹น์˜ ์—ฌ๋Ÿฌ ๋ธŒ๋žœ๋“œ ์ค‘ ํŠน๋ณ„ํžˆ ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์€ ๋ธŒ๋žœ๋“œ์™€ ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”?",
41
+ "์„ฑ๊ณต์ ์œผ๋กœ ์ด๋Œ์—ˆ๋˜ ๋งˆ์ผ€ํŒ… ์บ ํŽ˜์ธ ์ค‘, ๋ณธ์ธ์˜ ์—ญํ• ๊ณผ ์„ฑ๊ณผ๋ฅผ ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.",
42
+ "์ตœ๊ทผ ์‹ํ’ˆ์—…๊ณ„ ํŠธ๋ Œ๋“œ(HMR, ํ—ฌ์‹œํ”Œ๋ ˆ์ € ๋“ฑ)์™€ ์—ฐ๊ด€์ง€์–ด ์ž…์‚ฌ ํ›„ ์–ด๋–ค ๋งˆ์ผ€ํŒ…์„ ํŽผ์น˜๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?"
43
+ ]
44
+ }
45
+ },
46
+ {
47
+ "input": {
48
+ "job_title": "A&R",
49
+ "company_name": "ํ•˜์ด๋ธŒ",
50
+ "experience_level": "์ธํ„ด",
51
+ "common_questions": ["์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ๊ฒฉ์˜ ์žฅ๋‹จ์ ์„ ๋งํ•ด์ฃผ์„ธ์š”", "์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”", "๋งˆ์ง€๋ง‰์œผ๋กœ ํ•˜๊ณ  ์‹ถ์€ ๋ง์€?"]
52
+ },
53
+ "output": {
54
+ "sample_questions": [
55
+ "ํ•˜์ด๋ธŒ์˜ ์Œ์•…/์•„ํ‹ฐ์ŠคํŠธ๊ฐ€ ๋ณธ์ธ์—๊ฒŒ ์–ด๋–ค ์˜๋ฏธ์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  A&R ์ง๋ฌด์— ์™œ ์ง€์›ํ–ˆ๋Š”์ง€ ์—ฐ๊ฒฐ์ง€์–ด ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.",
56
+ "A&R ์ง๋ฌด์— ํ•„์š”ํ•œ ์—ญ๋Ÿ‰ ์ค‘ ๋ณธ์ธ์ด ๊ฐ€์žฅ ์ž์‹  ์žˆ๋Š” ๊ฒƒ๊ณผ ๋ณด์™„ํ•˜๊ณ  ์‹ถ์€ ์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”?",
57
+ "์ธํ„ด์œผ๋กœ์„œ ํ•˜์ด๋ธŒ์—์„œ ๊ฐ€์žฅ ๋ฐฐ์šฐ๊ณ  ์‹ถ์€ ์ ์€ ๋ฌด์—‡์ด๋ฉฐ, ์–ด๋–ป๊ฒŒ ํŒ€์— ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ๋‚˜์š”?"
58
+ ]
59
+ }
60
+ },
61
+ {
62
+ "input": {
63
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
64
+ "company_name": "ํ˜„๋Œ€๊ฑด์„ค",
65
+ "experience_level": "์‹ ์ž…",
66
+ "common_questions": ["์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ์žฅ ๊ณผ์ •์„ ๋งํ•ด์ฃผ์„ธ์š”", "๊ฐ€์žฅ ํž˜๋“ค์—ˆ๋˜ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์กด๊ฒฝํ•˜๋Š” ์ธ๋ฌผ์€ ๋ˆ„๊ตฌ์ธ๊ฐ€์š”"]
67
+ },
68
+ "output": {
69
+ "sample_questions": [
70
+ "๊ฑด์„ค ์‚ฐ์—…์˜ ํŠน์„ฑ์„ ๊ณ ๋ คํ•˜์—ฌ ํ˜„๋Œ€๊ฑด์„ค์— ์ง€์›ํ•œ ๋™๊ธฐ๋ฅผ ๋ง์”€ํ•ด์ฃผ์„ธ์š”.",
71
+ "ํŒ€ ํ”„๋กœ์ ํŠธ์—์„œ ๊ฐˆ๋“ฑ์„ ํ•ด๊ฒฐํ•˜๊ฑฐ๋‚˜ ์–ด๋ ค์šด ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ–ˆ๋˜ ๊ฒฝํ—˜์ด ์žˆ๋‹ค๋ฉด ๊ตฌ์ฒด์ ์œผ๋กœ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”.",
72
+ "๊ฒฝ์˜๊ธฐํš ์ง๋ฌด ๋‹ด๋‹น์ž๋กœ์„œ ํ˜„๋Œ€๊ฑด์„ค์˜ ๋ฏธ๋ž˜ ์„ฑ์žฅ์— ์–ด๋–ป๊ฒŒ ๊ธฐ์—ฌํ•˜๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?"
73
+ ]
74
+ }
75
+ },
76
+ {
77
+ "input": {
78
+ "job_title": "ํ•ด์™ธ์˜์—…",
79
+ "company_name": "์‚ผ์„ฑ์ „์ž",
80
+ "experience_level": "๊ฒฝ๋ ฅ",
81
+ "common_questions": ["๊ฒฝ๋ ฅ ์œ„์ฃผ๋กœ ์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”", "์ด์ง ์‚ฌ์œ ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ๊ณต ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”", "์‹คํŒจ ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”"]
82
+ },
83
+ "output": {
84
+ "sample_questions": [
85
+ "๊ฐ€์žฅ ์„ฑ๊ณต์ ์ด์—ˆ๋˜ ํ•ด์™ธ ์ˆ˜์ฃผ/์˜์—… ๊ฒฝํ—˜๊ณผ ๊ทธ ๊ณผ์ •์—์„œ ๋ณธ์ธ์˜ ํ•ต์‹ฌ์ ์ธ ์—ญํ• ์€ ๋ฌด์—‡์ด์—ˆ๋‚˜์š”?",
86
+ "์‚ผ์„ฑ์ „์ž์˜ ๋ฐ˜๋„์ฒด/๊ฐ€์ „ ์‚ฌ์—…๋ถ€ ์ค‘ ๋ณธ์ธ์˜ ์ „๋ฌธ์„ฑ์„ ๊ฐ€์žฅ ์ž˜ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ๋Š” ๋ถ„์•ผ๋Š” ์–ด๋””๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ, ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?",
87
+ "ํ•ด์™ธ ๊ณ ๊ฐ๊ณผ์˜ ๋ฌธํ™”์  ์ฐจ์ด๋‚˜ ์–ด๋ ค์šด ํ˜‘์ƒ ์ƒํ™ฉ์„ ๊ทน๋ณตํ–ˆ๋˜ ์‚ฌ๋ก€๊ฐ€ ์žˆ๋‹ค๋ฉด ๋ง์”€ํ•ด์ฃผ์„ธ์š”."
88
+ ]
89
+ }
90
+ }
91
+ ]
92
+ }
93
+ }
commonly-asked-question/llm_functions.py ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import yaml
5
+ from openai import OpenAI
6
+
7
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
8
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9
+
10
+ # ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
11
+ import os
12
+ current_dir = os.path.dirname(os.path.abspath(__file__))
13
+ prompt_path = os.path.join(current_dir, 'prompt.yaml')
14
+ with open(prompt_path, 'r', encoding='utf-8') as f:
15
+ prompt_data = yaml.safe_load(f)
16
+ prompt_template = prompt_data['prompt']
17
+
18
+ def parse_prediction(content):
19
+ """
20
+ AI ์‘๋‹ต์—์„œ JSON ํ˜•์‹์˜ ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
21
+ """
22
+ try:
23
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ๊ธธ์ด: {len(content)}")
24
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ์ฒซ 200์ž: {repr(content[:200])}")
25
+
26
+ # ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ - ๋ถˆํ•„์š”ํ•œ ๊ณต๋ฐฑ๊ณผ ํŠน์ˆ˜๋ฌธ์ž ์ œ๊ฑฐ
27
+ cleaned_content = content.strip()
28
+
29
+ # 1. JSON ์ฝ”๋“œ ๋ธ”๋ก ์ฐพ๊ธฐ (```json ... ``` ํ˜•์‹)
30
+ json_patterns = [
31
+ r'```json\s*(\{.*?\})\s*```',
32
+ r'```\s*(\{.*?\})\s*```',
33
+ r'```json\s*(.*?)\s*```',
34
+ r'```\s*(.*?)\s*```'
35
+ ]
36
+
37
+ for pattern in json_patterns:
38
+ json_match = re.search(pattern, cleaned_content, re.DOTALL)
39
+ if json_match:
40
+ json_str = json_match.group(1).strip()
41
+ print(f"JSON ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
42
+
43
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
44
+ json_str = re.sub(r'\n\s*', ' ', json_str) # ์ค„๋ฐ”๊ฟˆ๊ณผ ๊ณต๋ฐฑ ์ •๋ฆฌ
45
+ json_str = re.sub(r',\s*}', '}', json_str) # ๋งˆ์ง€๋ง‰ ์ฝค๋งˆ ์ œ๊ฑฐ
46
+ json_str = re.sub(r',\s*]', ']', json_str) # ๋ฐฐ์—ด ๋งˆ์ง€๋ง‰ ์ฝค๋งˆ ์ œ๊ฑฐ
47
+
48
+ try:
49
+ parsed_json = json.loads(json_str)
50
+ if 'sample_questions' in parsed_json:
51
+ return parsed_json['sample_questions']
52
+ except json.JSONDecodeError as e:
53
+ print(f"JSON ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
54
+ print(f"์‹คํŒจํ•œ JSON: {repr(json_str)}")
55
+
56
+ # 2. ์ค‘๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ์ „์ฒด JSON ์ฐพ๊ธฐ
57
+ brace_patterns = [
58
+ r'\{[^{}]*"sample_questions"[^{}]*\[[^\]]*\][^{}]*\}',
59
+ r'\{.*?"sample_questions".*?\[.*?\].*?\}'
60
+ ]
61
+
62
+ for pattern in brace_patterns:
63
+ brace_match = re.search(pattern, cleaned_content, re.DOTALL)
64
+ if brace_match:
65
+ json_str = brace_match.group(0).strip()
66
+ print(f"์ค‘๊ด„ํ˜ธ JSON ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
67
+
68
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
69
+ json_str = re.sub(r'\n\s*', ' ', json_str)
70
+ json_str = re.sub(r',\s*}', '}', json_str)
71
+ json_str = re.sub(r',\s*]', ']', json_str)
72
+
73
+ try:
74
+ parsed_json = json.loads(json_str)
75
+ if 'sample_questions' in parsed_json:
76
+ return parsed_json['sample_questions']
77
+ except json.JSONDecodeError as e:
78
+ print(f"์ค‘๊ด„ํ˜ธ JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
79
+
80
+ # 3. sample_questions ๋ฐฐ์—ด๋งŒ ์ง์ ‘ ์ฐพ๊ธฐ
81
+ array_patterns = [
82
+ r'"sample_questions"\s*:\s*\[(.*?)\]',
83
+ r'sample_questions\s*:\s*\[(.*?)\]'
84
+ ]
85
+
86
+ for pattern in array_patterns:
87
+ array_match = re.search(pattern, cleaned_content, re.DOTALL)
88
+ if array_match:
89
+ array_content = array_match.group(1).strip()
90
+ print(f"๋ฐฐ์—ด ๋‚ด์šฉ ๋ฐœ๊ฒฌ: {repr(array_content[:100])}")
91
+
92
+ # ๋ฐฐ์—ด ๋‚ด์šฉ์—์„œ ๋ฌธ์ž์—ด ์ถ”์ถœ
93
+ questions = []
94
+ # ๋”ฐ์˜ดํ‘œ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ๋ฌธ์ž์—ด๋“ค ์ฐพ๊ธฐ
95
+ question_matches = re.findall(r'"([^"]+)"', array_content)
96
+ for q in question_matches:
97
+ if len(q.strip()) > 10: # ์˜๋ฏธ์žˆ๋Š” ๊ธธ์ด์˜ ์งˆ๋ฌธ๋งŒ
98
+ questions.append(q.strip())
99
+
100
+ if questions:
101
+ return questions
102
+
103
+ # 4. ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JSON์œผ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
104
+ try:
105
+ # ์ฝ”๋“œ ๋ธ”๋ก ๋งˆ์ปค ์ œ๊ฑฐ
106
+ if cleaned_content.startswith('```'):
107
+ lines = cleaned_content.split('\n')
108
+ start_idx = 1 if lines[0].startswith('```') else 0
109
+ end_idx = len(lines)
110
+ for i in range(len(lines)-1, -1, -1):
111
+ if lines[i].strip() == '```':
112
+ end_idx = i
113
+ break
114
+ cleaned_content = '\n'.join(lines[start_idx:end_idx])
115
+
116
+ cleaned_content = cleaned_content.strip()
117
+ parsed_json = json.loads(cleaned_content)
118
+ if 'sample_questions' in parsed_json:
119
+ return parsed_json['sample_questions']
120
+ except json.JSONDecodeError as e:
121
+ print(f"์ „์ฒด JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
122
+
123
+ # 5. ์ตœํ›„์˜ ์ˆ˜๋‹จ: ํŒจํ„ด ๋งค์นญ์œผ๋กœ ์งˆ๋ฌธ ์ถ”์ถœ
124
+ print("ํŒจํ„ด ๋งค์นญ์œผ๋กœ ์งˆ๋ฌธ ์ถ”์ถœ ์‹œ๋„")
125
+ questions = []
126
+
127
+ # ๋‹ค์–‘ํ•œ ํŒจํ„ด์œผ๋กœ ์งˆ๋ฌธ ์ฐพ๊ธฐ
128
+ patterns = [
129
+ r'"([^"]{20,}[?])"', # ๋”ฐ์˜ดํ‘œ ์•ˆ์˜ ๋ฌผ์Œํ‘œ๋กœ ๋๋‚˜๋Š” ๊ธด ๋ฌธ์žฅ
130
+ r'"([^"]{20,})"', # ๋”ฐ์˜ดํ‘œ ์•ˆ์˜ ๊ธด ๋ฌธ์žฅ
131
+ r'[1-9]\.\s*([^"\n]{20,}[?])', # ๋ฒˆํ˜ธ. ์งˆ๋ฌธ ํ˜•ํƒœ
132
+ r'[1-9]\.\s*([^"\n]{20,})', # ๋ฒˆํ˜ธ. ๋ฌธ์žฅ ํ˜•ํƒœ
133
+ ]
134
+
135
+ for pattern in patterns:
136
+ matches = re.findall(pattern, cleaned_content)
137
+ for match in matches:
138
+ question = match.strip()
139
+ if len(question) > 15 and question not in questions:
140
+ questions.append(question)
141
+ if len(questions) >= 5: # ์ตœ๋Œ€ 5๊ฐœ
142
+ break
143
+ if questions:
144
+ break
145
+
146
+ return questions[:5] if questions else []
147
+
148
+ except Exception as e:
149
+ print(f"JSON ํŒŒ์‹ฑ ์ „์ฒด ์˜ค๋ฅ˜: {e}")
150
+ print(f"ํŒŒ์‹ฑ ์‹คํŒจํ•œ ์ปจํ…์ธ : {repr(content)}")
151
+ return []
152
+
153
+ def generate_interview_questions(company_name, job_title, experience_level, selected_questions, num_questions=3):
154
+ """
155
+ OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
156
+ """
157
+ # try:
158
+ if True:
159
+ if not company_name or not job_title or not experience_level or not selected_questions:
160
+ return "๋ชจ๋“  ํ•„๋“œ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", []
161
+
162
+ # ์„ ํƒ๋œ ์งˆ๋ฌธ๋“ค์„ ๋ฆฌ์ŠคํŠธ๋กœ ๋ณ€ํ™˜
163
+ if isinstance(selected_questions, str):
164
+ common_questions = [q.strip() for q in selected_questions.split(',')]
165
+ else:
166
+ common_questions = selected_questions
167
+
168
+ # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
169
+ prompt = prompt_template.format(
170
+ company_name=company_name,
171
+ job_title=job_title,
172
+ experience_level=experience_level,
173
+ common_questions=common_questions,
174
+ num_questions=num_questions
175
+ )
176
+
177
+ print(prompt)
178
+ # OpenAI Responses API ํ˜ธ์ถœ (Web Search Preview ์‚ฌ์šฉ)
179
+ response = client.responses.create(
180
+ model="gpt-4o",
181
+ tools=[{
182
+ "type": "web_search_preview",
183
+ "search_context_size": "high",
184
+ }],
185
+ input=f"๋‹น์‹ ์€ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์ตœ์‹  ๊ธฐ์—… ์ •๋ณด์™€ ์ฑ„์šฉ ๋™ํ–ฅ์„ ํ™•์ธํ•˜๊ณ  ์ฃผ์–ด์ง„ ์กฐ๊ฑด์— ๋งž๋Š” ๊ตฌ์ฒด์ ์ด๊ณ  ์‹ค์šฉ์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.\n\n{prompt}"
186
+ )
187
+
188
+ content = response.output_text
189
+ print(f"=== AI ์‘๋‹ต ์›๋ณธ ===")
190
+ print(content)
191
+ print(f"=== ์ „์ฒด ์‘๋‹ต ๊ฐ์ฒด ===")
192
+ print(response)
193
+
194
+ # ์›น ๊ฒ€์ƒ‰ ์ฐธ๊ณ  ๋งํฌ ์ถœ๋ ฅ
195
+ if hasattr(response, 'web_search_results') and response.web_search_results:
196
+ print(f"=== ์ฐธ๊ณ ํ•œ ์›น ๊ฒ€์ƒ‰ ๋งํฌ ===")
197
+ for i, result in enumerate(response.web_search_results, 1):
198
+ if hasattr(result, 'url'):
199
+ print(f"{i}. {result.url}")
200
+ elif hasattr(result, 'link'):
201
+ print(f"{i}. {result.link}")
202
+
203
+ print(f"=== AI ์‘๋‹ต ๋ ===")
204
+ questions = parse_prediction(content)
205
+
206
+ if not questions:
207
+ return "์งˆ๋ฌธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", []
208
+
209
+ # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
210
+ result = f"""## ๐ŸŽฏ {company_name} - {job_title} ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ
211
+
212
+ ### ๐Ÿ“‹ **์ƒ์„ฑ๋œ ์งˆ๋ฌธ๋“ค**
213
+
214
+ """
215
+ for i, question in enumerate(questions, 1):
216
+ result += f"**{i}.** {question}\n\n"
217
+
218
+ result += f"""
219
+ ---
220
+ **๐Ÿ“ ์ž…๋ ฅ ์ •๋ณด:**
221
+ - ํšŒ์‚ฌ: {company_name}
222
+ - ์ง๋ฌด: {job_title}
223
+ - ๊ฒฝ๋ ฅ: {experience_level}
224
+ - ์ƒ์„ฑ๋œ ์งˆ๋ฌธ ์ˆ˜: {len(questions)}๊ฐœ (์š”์ฒญ: {num_questions}๊ฐœ)
225
+ - ์ฐธ๊ณ  ์งˆ๋ฌธ ์ˆ˜: {len(common_questions)}๊ฐœ
226
+
227
+ *๋ณธ ์งˆ๋ฌธ๋“ค์€ AI๊ฐ€ ์ƒ์„ฑํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ ๋ฉด์ ‘๊ณผ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.*
228
+ """
229
+
230
+ return result, questions
231
+
232
+ # except Exception as e:
233
+ # error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
234
+
235
+ # ์งˆ๋ฌธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
236
+
237
+ # **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
238
+
239
+ # ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
240
+ # """
241
+ # return error_msg, []
242
+
243
+ if __name__ == "__main__":
244
+ company_name = "ํ† ์Šค"
245
+ job_title = "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ"
246
+ experience_level = "์‹ ์ž…"
247
+ selected_questions = "์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”, ์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”, ๊ฐ€์žฅ ๋„์ „์ ์ธ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”, ์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”"
248
+ num_questions = 3
249
+ result, questions = generate_interview_questions(company_name, job_title, experience_level, selected_questions, num_questions)
250
+ print(result)
251
+ print(questions)
commonly-asked-question/main.py ADDED
@@ -0,0 +1,351 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from llm_functions import generate_interview_questions
3
+
4
+ # LLM ๊ด€๋ จ ํ•จ์ˆ˜๋“ค์€ llm_functions.py๋กœ ์ด๋™๋จ
5
+
6
+ # ์˜ˆ์ œ ๋ฐ์ดํ„ฐ
7
+ example_companies = ["์‚ผ์„ฑ์ „์ž", "ํ† ์Šค", "์นด์นด์˜ค", "๋„ค์ด๋ฒ„", "LG์ „์ž", "ํ˜„๋Œ€์ž๋™์ฐจ", "CJ์ œ์ผ์ œ๋‹น", "ํ•˜์ด๋ธŒ", "์ฟ ํŒก", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ"]
8
+ example_jobs = ["๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ", "๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธํ‹ฐ์ŠคํŠธ", "๋งˆ์ผ€ํŒ…", "์˜์—…", "๊ธฐํš", "๋””์ž์ธ", "HR", "์žฌ๋ฌด", "A&R"]
9
+ experience_levels = ["์‹ ์ž…", "๊ฒฝ๋ ฅ", "์ธํ„ด", "๊ธฐํƒ€"]
10
+ common_questions_list = [
11
+ "์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”",
12
+ "์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”",
13
+ "๋ณธ์ธ์˜ ๊ฐ•์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”",
14
+ "๊ฐ€์žฅ ๋„์ „์ ์ธ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”",
15
+ "์„ฑ๊ณต ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”",
16
+ "์‹คํŒจ ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”",
17
+ "์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”",
18
+ "์„ฑ๊ฒฉ์˜ ์žฅ๋‹จ์ ์„ ๋งํ•ด์ฃผ์„ธ์š”",
19
+ "์กด๊ฒฝํ•˜๋Š” ์ธ๋ฌผ์€ ๋ˆ„๊ตฌ์ธ๊ฐ€์š”",
20
+ "๋งˆ์ง€๋ง‰์œผ๋กœ ํ•˜๊ณ  ์‹ถ์€ ๋ง์€?"
21
+ ]
22
+
23
+ # generate_interview_questions ํ•จ์ˆ˜๋Š” llm_functions.py๋กœ ์ด๋™๋จ
24
+
25
+ def create_question_cards(questions):
26
+ """
27
+ ์ƒ์„ฑ๋œ ์งˆ๋ฌธ๋“ค์„ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œํ•˜๋Š” HTML ์ƒ์„ฑ
28
+ """
29
+ if not questions:
30
+ return "<div style='text-align: center; color: #6B7280; padding: 20px;'>์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>"
31
+
32
+ cards_html = "<div style='display: flex; flex-direction: column; gap: 15px;'>"
33
+
34
+ colors = ["#EBF8FF", "#ECFDF5", "#FEF2F2", "#F5F3FF", "#FFF7ED"]
35
+ border_colors = ["#1E40AF", "#059669", "#DC2626", "#7C3AED", "#EA580C"]
36
+
37
+ for i, question in enumerate(questions):
38
+ color = colors[i % len(colors)]
39
+ border_color = border_colors[i % len(border_colors)]
40
+
41
+ cards_html += f"""
42
+ <div style="
43
+ background-color: {color};
44
+ border: 2px solid {border_color};
45
+ border-radius: 12px;
46
+ padding: 20px;
47
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
48
+ ">
49
+ <div style="
50
+ font-weight: bold;
51
+ color: {border_color};
52
+ font-size: 16px;
53
+ margin-bottom: 10px;
54
+ ">์งˆ๋ฌธ {i+1}</div>
55
+ <div style="
56
+ color: #374151;
57
+ font-size: 15px;
58
+ line-height: 1.5;
59
+ ">{question}</div>
60
+ </div>
61
+ """
62
+
63
+ cards_html += "</div>"
64
+ return cards_html
65
+
66
+ def process_question_generation(company_name, job_title, experience_level, selected_questions, num_questions):
67
+ """
68
+ ์งˆ๋ฌธ ์ƒ์„ฑ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI์— ํ‘œ์‹œํ•  ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
69
+ """
70
+ try:
71
+ content, questions = generate_interview_questions(company_name, job_title, experience_level, selected_questions, num_questions)
72
+ question_cards = create_question_cards(questions)
73
+ return content, question_cards
74
+ except Exception as e:
75
+ error_content = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
76
+
77
+ ์งˆ๋ฌธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
78
+
79
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
80
+
81
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
82
+ """
83
+ error_cards = create_question_cards([])
84
+ return error_content, error_cards
85
+
86
+ # ์ „์—ญ ์ƒํƒœ ๊ด€๋ฆฌ
87
+ class AppState:
88
+ def __init__(self):
89
+ self.companies = ["ํ† ์Šค", "๋„ค์ด๋ฒ„", "์นด์นด์˜ค", "์‚ผ์„ฑ์ „์ž", "LG์ „์ž", "ํ˜„๋Œ€์ž๋™์ฐจ", "SKํ•˜์ด๋‹‰์Šค", "CJ์ œ์ผ์ œ๋‹น", "ํ•˜์ด๋ธŒ", "ํ˜„๋Œ€๊ฑด์„ค"]
90
+ self.jobs = ["๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ์ž", "ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๋ฐœ์ž", "๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธํ‹ฐ์ŠคํŠธ", "๋งˆ์ผ€ํŒ…", "์˜์—…", "๊ธฐํš", "๋””์ž์ด๋„ˆ", "A&R", "ํ•ด์™ธ์˜์—…", "์‚ฌ์—…๊ธฐํš"]
91
+ self.questions = common_questions_list.copy()
92
+
93
+ app_state = AppState()
94
+
95
+ # ๋” ์ด์ƒ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋“ค ์ œ๊ฑฐ๋จ
96
+
97
+ def add_company(new_company):
98
+ """์ƒˆ ํšŒ์‚ฌ ์ถ”๊ฐ€"""
99
+ if new_company and new_company.strip() and new_company.strip() not in app_state.companies:
100
+ app_state.companies.append(new_company.strip())
101
+ return gr.Dropdown.update(choices=app_state.companies), ""
102
+ return gr.Dropdown.update(), ""
103
+
104
+ def add_job(new_job):
105
+ """์ƒˆ ์ง๋ฌด ์ถ”๊ฐ€"""
106
+ if new_job and new_job.strip() and new_job.strip() not in app_state.jobs:
107
+ app_state.jobs.append(new_job.strip())
108
+ return gr.Dropdown.update(choices=app_state.jobs), ""
109
+ return gr.Dropdown.update(), ""
110
+
111
+ def add_question(new_question):
112
+ """์ƒˆ ์ผ๋ฐ˜ ์งˆ๋ฌธ ์ถ”๊ฐ€"""
113
+ if new_question and new_question.strip() and new_question.strip() not in app_state.questions:
114
+ app_state.questions.append(new_question.strip())
115
+ return gr.CheckboxGroup.update(choices=app_state.questions), ""
116
+
117
+ def create_interface():
118
+ """
119
+ Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
120
+ """
121
+ with gr.Blocks(
122
+ title="๐ŸŽฏ AI ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ๊ธฐ",
123
+ theme=gr.themes.Soft(),
124
+ css="""
125
+ .main-header {
126
+ text-align: center;
127
+ padding: 20px;
128
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
129
+ color: white;
130
+ border-radius: 10px;
131
+ margin-bottom: 20px;
132
+ }
133
+ .input-section {
134
+ background-color: #f8f9fa;
135
+ padding: 20px;
136
+ border-radius: 8px;
137
+ margin: 10px 0;
138
+ }
139
+ .example-section {
140
+ background-color: #f0f9ff;
141
+ padding: 15px;
142
+ border-radius: 8px;
143
+ margin: 10px 0;
144
+ }
145
+ """
146
+ ) as demo:
147
+
148
+ # ํ—ค๋”
149
+ gr.HTML("""
150
+ <div class="main-header">
151
+ <h1>๐ŸŽฏ AI ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ๊ธฐ</h1>
152
+ <p>ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ AI๊ฐ€ ์ƒ์„ฑํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค</p>
153
+ </div>
154
+ """)
155
+
156
+ # ์„ค๋ช…
157
+ gr.Markdown("""
158
+ ### ๐Ÿš€ **์‚ฌ์šฉ ๋ฐฉ๋ฒ•**
159
+ 1. **ํšŒ์‚ฌ๋ช…**: ์ง€์›ํ•˜๊ณ ์ž ํ•˜๋Š” ํšŒ์‚ฌ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”
160
+ 2. **์ง๋ฌด**: ์ง€์› ์ง๋ฌด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”
161
+ 3. **๊ฒฝ๋ ฅ ์ˆ˜์ค€**: ๋ณธ์ธ์˜ ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ์„ ํƒํ•˜์„ธ์š”
162
+ 4. **์ผ๋ฐ˜ ์งˆ๋ฌธ**: ์ฐธ๊ณ ํ•  ์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ๋“ค์„ ์„ ํƒํ•˜์„ธ์š”
163
+ 5. **์ƒ์„ฑ**: '์งˆ๋ฌธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๋งž์ถคํ˜• ์งˆ๋ฌธ์„ ๋ฐ›์•„๋ณด์„ธ์š”
164
+
165
+ โœจ **ํŠน์ง•**: ํšŒ์‚ฌ์˜ ํŠน์„ฑ, ์ง๋ฌด ์š”๊ตฌ์‚ฌํ•ญ, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ชจ๋‘ ๊ณ ๋ คํ•œ ๊ตฌ์ฒด์ ์ด๊ณ  ํ˜„์‹ค์ ์ธ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
166
+ """)
167
+
168
+ with gr.Row():
169
+ with gr.Column(scale=2):
170
+ # ์ž…๋ ฅ ์„น์…˜
171
+ gr.HTML('<div class="input-section">')
172
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
173
+
174
+ with gr.Row():
175
+ company_input = gr.Textbox(
176
+ label="๐Ÿข ํšŒ์‚ฌ๋ช…",
177
+ placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ",
178
+ value="",
179
+ scale=2,
180
+ elem_id="company_input"
181
+ )
182
+
183
+ job_input = gr.Textbox(
184
+ label="๐Ÿ’ผ ์ง๋ฌด",
185
+ placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๋งˆ์ผ€ํŒ…, ๊ธฐํš ๋“ฑ",
186
+ value="",
187
+ scale=2,
188
+ elem_id="job_input"
189
+ )
190
+
191
+ with gr.Row():
192
+ experience_input = gr.Dropdown(
193
+ label="๐Ÿ“Š ๊ฒฝ๋ ฅ ์ˆ˜์ค€",
194
+ choices=experience_levels,
195
+ value="์‹ ์ž…",
196
+ interactive=True,
197
+ scale=2
198
+ )
199
+
200
+ num_questions_input = gr.Dropdown(
201
+ label="๐Ÿ”ข ์งˆ๋ฌธ ๊ฐœ์ˆ˜",
202
+ choices=[1, 2, 3, 4, 5],
203
+ value=3,
204
+ interactive=True,
205
+ scale=1
206
+ )
207
+
208
+ common_questions_input = gr.CheckboxGroup(
209
+ label="๐Ÿ“‹ ์ฐธ๊ณ ํ•  ์ผ๋ฐ˜ ๋ฉด์ ‘ ์งˆ๋ฌธ (3-5๊ฐœ ์„ ํƒ ๊ถŒ์žฅ)",
210
+ choices=common_questions_list,
211
+ value=common_questions_list[:4],
212
+ interactive=True
213
+ )
214
+
215
+ generate_btn = gr.Button(
216
+ "๐ŸŽฏ ๋งž์ถคํ˜• ์งˆ๋ฌธ ์ƒ์„ฑ",
217
+ variant="primary",
218
+ size="lg"
219
+ )
220
+ gr.HTML('</div>')
221
+
222
+ with gr.Column(scale=1):
223
+ # ์˜ˆ์ œ ๋ฐ ๊ฐ€์ด๋“œ
224
+ gr.HTML('<div class="example-section">')
225
+
226
+ # ์‚ฌ์šฉ์ž ์ •์˜ ํšŒ์‚ฌ ์ถ”๊ฐ€
227
+ gr.Markdown("### ๐Ÿข **ํšŒ์‚ฌ ๊ด€๋ฆฌ**")
228
+ with gr.Row():
229
+ new_company_input = gr.Textbox(
230
+ label="์ƒˆ ํšŒ์‚ฌ ์ถ”๊ฐ€",
231
+ placeholder="ํšŒ์‚ฌ๋ช… ์ž…๋ ฅ",
232
+ scale=2
233
+ )
234
+ add_company_btn = gr.Button("์ถ”๊ฐ€", size="sm", scale=1)
235
+
236
+ # ์˜ˆ์ œ ํšŒ์‚ฌ ๋“œ๋กญ๋‹ค์šด
237
+ company_dropdown = gr.Dropdown(
238
+ label="์˜ˆ์ œ ํšŒ์‚ฌ ์„ ํƒ",
239
+ choices=app_state.companies,
240
+ value=None,
241
+ interactive=True
242
+ )
243
+
244
+ # ์‚ฌ์šฉ์ž ์ •์˜ ์ง๋ฌด ์ถ”๊ฐ€
245
+ gr.Markdown("### ๐Ÿ’ผ **์ง๋ฌด ๊ด€๋ฆฌ**")
246
+ with gr.Row():
247
+ new_job_input = gr.Textbox(
248
+ label="์ƒˆ ์ง๋ฌด ์ถ”๊ฐ€",
249
+ placeholder="์ง๋ฌด๋ช… ์ž…๋ ฅ",
250
+ scale=2
251
+ )
252
+ add_job_btn = gr.Button("์ถ”๊ฐ€", size="sm", scale=1)
253
+
254
+ # ์˜ˆ์ œ ์ง๋ฌด ๋“œ๋กญ๋‹ค์šด
255
+ job_dropdown = gr.Dropdown(
256
+ label="์˜ˆ์ œ ์ง๋ฌด ์„ ํƒ",
257
+ choices=app_state.jobs,
258
+ value=None,
259
+ interactive=True
260
+ )
261
+
262
+ # ์‚ฌ์šฉ์ž ์ •์˜ ์ผ๋ฐ˜ ์งˆ๋ฌธ ์ถ”๊ฐ€
263
+ gr.Markdown("### ๐Ÿ“‹ **์ผ๋ฐ˜ ์งˆ๋ฌธ ๊ด€๋ฆฌ**")
264
+ with gr.Row():
265
+ new_question_input = gr.Textbox(
266
+ label="์ƒˆ ์ผ๋ฐ˜ ์งˆ๋ฌธ ์ถ”๊ฐ€",
267
+ placeholder="์งˆ๋ฌธ ๋‚ด์šฉ ์ž…๋ ฅ",
268
+ scale=2
269
+ )
270
+ add_question_btn = gr.Button("์ถ”๊ฐ€", size="sm", scale=1)
271
+
272
+ gr.HTML('</div>')
273
+
274
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
275
+ with gr.Row():
276
+ with gr.Column(scale=2):
277
+ gr.Markdown("### ๐Ÿ“‹ **์ƒ์„ฑ ๊ฒฐ๊ณผ**")
278
+ result_output = gr.Markdown(
279
+ value="๊ธฐ๋ณธ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '๋งž์ถคํ˜• ์งˆ๋ฌธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
280
+ elem_classes=["result-output"]
281
+ )
282
+
283
+ with gr.Column(scale=1):
284
+ gr.Markdown("### ๐ŸŽฏ **์งˆ๋ฌธ ์นด๋“œ**")
285
+ question_cards = gr.HTML(
286
+ value="<div style='text-align: center; color: #6B7280; padding: 20px;'>์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>",
287
+ elem_classes=["question-cards"]
288
+ )
289
+
290
+ # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์—ฐ๊ฒฐ
291
+ add_company_btn.click(
292
+ fn=add_company,
293
+ inputs=[new_company_input],
294
+ outputs=[company_dropdown, new_company_input]
295
+ )
296
+
297
+ add_job_btn.click(
298
+ fn=add_job,
299
+ inputs=[new_job_input],
300
+ outputs=[job_dropdown, new_job_input]
301
+ )
302
+
303
+ add_question_btn.click(
304
+ fn=add_question,
305
+ inputs=[new_question_input],
306
+ outputs=[common_questions_input, new_question_input]
307
+ )
308
+
309
+ # ๋“œ๋กญ๋‹ค์šด ์„ ํƒ ์‹œ ์ž…๋ ฅ ํ•„๋“œ์— ์ž๋™ ์ž…๋ ฅ
310
+ company_dropdown.change(
311
+ fn=lambda x: x if x else "",
312
+ inputs=[company_dropdown],
313
+ outputs=[company_input]
314
+ )
315
+
316
+ job_dropdown.change(
317
+ fn=lambda x: x if x else "",
318
+ inputs=[job_dropdown],
319
+ outputs=[job_input]
320
+ )
321
+
322
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
323
+ generate_btn.click(
324
+ fn=process_question_generation,
325
+ inputs=[company_input, job_input, experience_input, common_questions_input, num_questions_input],
326
+ outputs=[result_output, question_cards],
327
+ api_name="generate_questions"
328
+ )
329
+
330
+ # ํ‘ธํ„ฐ
331
+ gr.Markdown("""
332
+ ---
333
+ **๐Ÿ’ก ์ƒ์„ฑ๋œ ์งˆ๋ฌธ ํ™œ์šฉ ํŒ**:
334
+ - ๊ฐ ์งˆ๋ฌธ์— ๋Œ€ํ•ด STAR(Situation, Task, Action, Result) ๊ธฐ๋ฒ•์œผ๋กœ ๋‹ต๋ณ€์„ ์ค€๋น„ํ•ด๋ณด์„ธ์š”
335
+ - ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ๋Œ€ํ•œ ์‚ฌ์ „ ์กฐ์‚ฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ตฌ์ฒด์ ์ธ ์‚ฌ๋ก€๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”
336
+ - ์ƒ์„ฑ๋œ ์งˆ๋ฌธ์„ ๋ฐ”ํƒ•์œผ๋กœ ์ถ”๊ฐ€ ์งˆ๋ฌธ๋“ค๋„ ์˜ˆ์ƒํ•ด๋ณด์„ธ์š”
337
+
338
+ ๐Ÿค– **Powered by**: OpenAI GPT-4o with Web Search
339
+ """)
340
+
341
+ return demo
342
+
343
+ if __name__ == "__main__":
344
+ # Gradio ์•ฑ ์‹คํ–‰
345
+ demo = create_interface()
346
+ demo.launch(
347
+ server_name="0.0.0.0",
348
+ # server_port=7864,
349
+ share=True,
350
+ show_error=True
351
+ )
commonly-asked-question/prompt.yaml ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt: |
2
+ ๋‹น์‹ ์€ ์‚ฌ์šฉ์ž์˜ ์ƒํ™ฉ์— ๋งž์ถฐ ๋ฉด์ ‘ ์˜ˆ์ƒ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์ฃผ๋Š” ์ „๋ฌธ AI ์ปจ์„คํ„ดํŠธ์ž…๋‹ˆ๋‹ค.
3
+ ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•˜๋Š” [์ž…๋ ฅ ์ •๋ณด]์™€ [์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ]๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ, ํ•ด๋‹น ๊ธฐ์—…๊ณผ ์ง๋ฌด, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์— ๊ฐ€์žฅ ์ ํ•ฉํ•œ ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
4
+
5
+ ### ์ง€์นจ
6
+ 1. **์›น ๊ฒ€์ƒ‰ ํ™œ์šฉ:** ํšŒ์‚ฌ๋ช…์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ตœ์‹  ๊ธฐ์—… ์ •๋ณด, ์‚ฌ์—… ํ˜„ํ™ฉ, ์ฑ„์šฉ ๋™ํ–ฅ, ๋ฉด์ ‘ ํ›„๊ธฐ, ๊ธฐ์—… ๋ฌธํ™” ๋“ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
7
+ 2. **์ƒํ™ฉ ๋ฐ˜์˜:** ๊ฒ€์ƒ‰๋œ ๊ธฐ์—…์˜ ํŠน์„ฑ(์—…์ข…, ๋ฌธํ™”, ์ตœ๊ทผ ์ด์Šˆ ๋“ฑ)๊ณผ ์ง๋ฌด์˜ ํ•ต์‹ฌ ์—ญ๋Ÿ‰, ๊ฒฝ๋ ฅ ์ˆ˜์ค€(์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด)์„ ๋ชจ๋‘ ๊ณ ๋ คํ•˜์—ฌ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
8
+ 3. **์งˆ๋ฌธ ๊ตฌ์ฒดํ™”:** "์ง€์› ๋™๊ธฐ"์™€ ๊ฐ™์ด ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ธ ์งˆ๋ฌธ์€ ํ”ผํ•˜๊ณ , ๊ฒ€์ƒ‰๋œ ์ •๋ณด๋ฅผ ํ™œ์šฉํ•˜์—ฌ ํ›จ์”ฌ ๋” ๊ตฌ์ฒด์ ์ด๊ณ  ๊นŠ์ด ์žˆ๋Š” ์งˆ๋ฌธ์œผ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
9
+ 4. **๋‹ค์–‘ํ•œ ์ธก๋ฉด ํ‰๊ฐ€:** ์ƒ์„ฑ๋œ ์งˆ๋ฌธ๋“ค์€ ์ง€์›์ž์˜ ์ง๋ฌด ์—ญ๋Ÿ‰, ์ปฌ์ฒ˜ํ•(์กฐ์ง ์ ํ•ฉ์„ฑ), ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ ๋“ฑ ๋‹ค์–‘ํ•œ ์ธก๋ฉด์„ ์ข…ํ•ฉ์ ์œผ๋กœ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
10
+ 5. **์ฐฝ์˜์ ์ด์ง€๋งŒ ํ˜„์‹ค์ ์ธ ์งˆ๋ฌธ:** ๋„ˆ๋ฌด ํ™ฉ๋‹นํ•˜๊ฑฐ๋‚˜ ํ˜„์‹ค๊ณผ ๋™๋–จ์–ด์ง„ ์งˆ๋ฌธ์ด ์•„๋‹Œ, ์‹ค์ œ ๋ฉด์ ‘์—์„œ ๋‚˜์˜ฌ ๋ฒ•ํ•œ ํ˜„์‹ค์ ์ธ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.
11
+
12
+ ### ์ž…๋ ฅ ์ •๋ณด
13
+ - **ํšŒ์‚ฌ๋ช…:** {company_name}
14
+ - **์ง๋ฌด:** {job_title}
15
+ - **๊ฒฝ๋ ฅ ์ˆ˜์ค€:** {experience_level}
16
+ - **์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ:** {common_questions}
17
+
18
+ ### ์ถœ๋ ฅ ํ˜•์‹
19
+ - ๋ฐ˜๋“œ์‹œ {num_questions}๊ฐœ์˜ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
20
+ - ๊ฐ ์งˆ๋ฌธ์€ JSON ํ˜•์‹์˜ `sample_questions` ๋ฐฐ์—ด์— ํฌํ•จ๋˜์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
21
+ - ๋ฐ˜๋“œ์‹œ ์•„๋ž˜ ์˜ˆ์‹œ์™€ ์ •ํ™•ํžˆ ๊ฐ™์€ JSON ํ˜•์‹์œผ๋กœ๋งŒ ์‘๋‹ตํ•˜์„ธ์š”.
22
+ - ๋‹ค๋ฅธ ํ…์ŠคํŠธ, ์„ค๋ช…, ์ฃผ์„์€ ์ ˆ๋Œ€ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”.
23
+ - JSON ํ˜•์‹์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜์„ธ์š” (์ฝค๋งˆ, ๋”ฐ์˜ดํ‘œ, ์ค‘๊ด„ํ˜ธ ๋“ฑ).
24
+
25
+ ### ์˜ˆ์‹œ
26
+ [์ž…๋ ฅ]
27
+ - ํšŒ์‚ฌ๋ช…: ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)
28
+ - ์ง๋ฌด: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ
29
+ - ๊ฒฝ๋ ฅ ์ˆ˜์ค€: ์‹ ์ž…
30
+ - ์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ๋ฆฌ์ŠคํŠธ: ["์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”", "์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "๊ฐ€์žฅ ๋„์ „์ ์ธ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”"]
31
+
32
+ [์ถœ๋ ฅ]
33
+ ```json
34
+ {{
35
+ "sample_questions": [
36
+ "์ˆ˜๋งŽ์€ ํ•€ํ…Œํฌ ๊ธฐ์—… ์ค‘ ์™œ 'ํ† ์Šค'์— ์ง€์›ํ•˜์…จ๋‚˜์š”?",
37
+ "๊ฐœ๋ฐœ์ž๋กœ์„œ ๊ฐ€์žฅ ํฌ๊ฒŒ ์„ฑ์žฅํ–ˆ๋˜ ๊ธฐ์ˆ ์  ๋„์ „ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”?",
38
+ "๋ณธ์ธ์ด ํ† ์Šค์˜ ๊ธฐ์ˆ  ๋ฌธํ™”(์ž์œจ๊ณผ ์ฑ…์ž„, ๋†’์€ ๋™๋ฃŒ ์‹ ๋ขฐ ๋“ฑ)์™€ ์ž˜ ๋งž๋Š”๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š” ์ด์œ ๋ฅผ ๊ฒฝํ—˜์„ ๋ฐ”ํƒ•์œผ๋กœ ์†Œ๊ฐœํ•ด ์ฃผ์„ธ์š”."
39
+ ]
40
+ }}
41
+ ```
42
+
43
+ ์ด์ œ ๋‹ค์Œ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์œ„ JSON ํ˜•์‹์œผ๋กœ๋งŒ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”. ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋Š” ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”.
company-size-classification/.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
company-size-classification/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (4.2 kB). View file
 
company-size-classification/eval.json ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "company_size_classification": {
3
+ "enum": [
4
+ "๋Œ€๊ธฐ์—…",
5
+ "์ค‘๊ฒฌ๊ธฐ์—…",
6
+ "์ค‘์†Œ๊ธฐ์—…",
7
+ "์Šคํƒ€ํŠธ์—…",
8
+ "์™ธ๊ตญ๊ณ„๊ธฐ์—…",
9
+ "๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…",
10
+ "๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ",
11
+ "๊ธˆ์œต์—…"
12
+ ]
13
+ },
14
+ "examples": [
15
+ {
16
+ "input": "์‚ผ์„ฑ์ „์ž",
17
+ "output": "๋Œ€๊ธฐ์—…"
18
+ },
19
+ {
20
+ "input": "LG์ „์ž",
21
+ "output": "๋Œ€๊ธฐ์—…"
22
+ },
23
+ {
24
+ "input": "์นด์นด์˜ค",
25
+ "output": "์ค‘๊ฒฌ๊ธฐ์—…"
26
+ },
27
+ {
28
+ "input": "์ฟ ํŒก",
29
+ "output": "์Šคํƒ€ํŠธ์—…"
30
+ },
31
+ {
32
+ "input": "JP๋ชจ๊ฑด",
33
+ "output": "์™ธ๊ตญ๊ณ„๊ธฐ์—…"
34
+ },
35
+ {
36
+ "input": "์„œ์šธ์‹œ์ฒญ",
37
+ "output": "๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…"
38
+ },
39
+ {
40
+ "input": "ํ•œ๊ตญ๋ ˆ๋“œํฌ๋กœ์Šค",
41
+ "output": "๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ"
42
+ },
43
+ {
44
+ "input": "์‹ ํ•œ์€ํ–‰",
45
+ "output": "๊ธˆ์œต์—…"
46
+ }
47
+ ]
48
+ }
company-size-classification/llm_functions.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import yaml
3
+ import re
4
+
5
+ # OpenAI client ์ดˆ๊ธฐํ™”
6
+ client = openai.OpenAI()
7
+
8
+ # ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
9
+ import os
10
+ current_dir = os.path.dirname(os.path.abspath(__file__))
11
+ prompt_path = os.path.join(current_dir, 'prompt.yaml')
12
+ with open(prompt_path, 'r', encoding='utf-8') as f:
13
+ prompt_data = yaml.safe_load(f)
14
+ prompt_template = prompt_data['prompt']
15
+
16
+
17
+ def parse_prediction(content):
18
+ """
19
+ AI ์‘๋‹ต์—์„œ ๊ธฐ์—… ๊ทœ๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
20
+ """
21
+ predicted_category = "๋ถ„๋ฅ˜ ๋ถˆ๊ฐ€"
22
+
23
+ # ์ฒซ ๋ฒˆ์งธ ๋ฐฉ๋ฒ•: ```<๊ธฐ์—…๊ทœ๋ชจ>``` ํ˜•์‹์—์„œ ํŒŒ์‹ฑ
24
+ category_match = re.search(r'```<([^>]+)>```', content)
25
+ if category_match:
26
+ predicted_category = category_match.group(1)
27
+ else:
28
+ # ๋ฐฑ์—…: ๊ธฐ์กด ๋ฐฉ์‹์œผ๋กœ ์นดํ…Œ๊ณ ๋ฆฌ ์ฐพ๊ธฐ
29
+ categories = ["๋Œ€๊ธฐ์—…", "์ค‘๊ฒฌ๊ธฐ์—…", "์ค‘์†Œ๊ธฐ์—…", "์Šคํƒ€ํŠธ์—…", "์™ธ๊ตญ๊ณ„๊ธฐ์—…", "๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…", "๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ", "๊ธˆ์œต์—…"]
30
+ for category in categories:
31
+ if category in content:
32
+ predicted_category = category
33
+ break
34
+
35
+ return predicted_category
36
+
37
+ def analyze_company_size(company_name):
38
+ """
39
+ OpenAI Search API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์—… ๊ทœ๋ชจ๋ฅผ ์˜ˆ์ธกํ•˜๋Š” ํ•จ์ˆ˜
40
+ """
41
+ try:
42
+ # OpenAI Search API๋ฅผ ์‚ฌ์šฉํ•œ ํšŒ์‚ฌ ์ •๋ณด ๊ฒ€์ƒ‰
43
+ search_response = client.responses.create(
44
+ model="gpt-4o",
45
+ tools=[
46
+ {
47
+ "type": "web_search_preview",
48
+ "search_context_size": "low",
49
+ }
50
+ ],
51
+ input= prompt_template.format(company_name=company_name)
52
+ )
53
+
54
+ # ์‘๋‹ต์—์„œ ์‹ค์ œ ๋ฉ”์‹œ์ง€ ์ฐพ๊ธฐ (์›น ๊ฒ€์ƒ‰ ํ˜ธ์ถœ๊ณผ ๋ถ„๋ฆฌ)
55
+ print(search_response)
56
+ message_output = None
57
+ for output in search_response.output:
58
+ if hasattr(output, 'content') and output.type == 'message':
59
+ message_output = output
60
+ break
61
+
62
+ if message_output is None:
63
+ raise Exception("์‘๋‹ต์—์„œ ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
64
+
65
+ # ์‘๋‹ต์—์„œ ๋‚ด์šฉ๊ณผ URL ์ถ”์ถœ
66
+ content = message_output.content[0].text
67
+
68
+ # URL ์ธ์šฉ ์ •๋ณด ์ถ”์ถœ
69
+ citations = []
70
+ if hasattr(message_output.content[0], 'annotations') and message_output.content[0].annotations:
71
+ for annotation in message_output.content[0].annotations:
72
+ if hasattr(annotation, 'url_citation'):
73
+ citations.append({
74
+ 'title': annotation.url_citation.title,
75
+ 'url': annotation.url_citation.url
76
+ })
77
+
78
+ # ๊ธฐ์—… ๊ทœ๋ชจ ์นดํ…Œ๊ณ ๋ฆฌ ์ถ”์ถœ
79
+ predicted_category = parse_prediction(content)
80
+
81
+ # ์ฐธ์กฐ URL ํ˜•์‹ํ™”
82
+ reference_text = ""
83
+ if citations:
84
+ reference_text = "\n\n๐Ÿ“š **์ฐธ๊ณ  ์ž๋ฃŒ:**\n"
85
+ for i, citation in enumerate(citations, 1):
86
+ reference_text += f"{i}. [{citation['title']}]({citation['url']})\n"
87
+
88
+ # ์ตœ์ข… ๊ฒฐ๊ณผ ํ˜•์‹ํ™” (์นดํ…Œ๊ณ ๋ฆฌ์™€ ๋ถ„์„ ๋‚ด์šฉ ๋ถ„๋ฆฌ ๋ฐ˜ํ™˜)
89
+ result_content = f"""## ๐Ÿข {company_name} ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„ ๊ฒฐ๊ณผ
90
+
91
+ {content}
92
+
93
+ {reference_text}
94
+
95
+ ---
96
+ *๋ณธ ๋ถ„์„์€ OpenAI Search API๋ฅผ ํ†ตํ•ด ์ˆ˜์ง‘๋œ ์ตœ์‹  ์›น ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ˆ˜ํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค.*
97
+ """
98
+
99
+ return result_content, predicted_category
100
+
101
+ except Exception as e:
102
+ error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
103
+
104
+ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. {company_name}์˜ ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
105
+
106
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
107
+
108
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์‹œ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.
109
+ """
110
+ return error_msg, "์˜ค๋ฅ˜ ๋ฐœ์ƒ"
company-size-classification/main.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from llm_functions import analyze_company_size
3
+
4
+ # LLM ๊ด€๋ จ ํ•จ์ˆ˜๋“ค์€ llm_functions.py๋กœ ์ด๋™๋จ
5
+
6
+ example_company_name = ["์‚ผ์„ฑ์ „์ž", "ํ˜„๋Œ€์ž๋™์ฐจ", "LG์ „์ž", "SKํ•˜์ด๋‹‰์Šค", "ํฌ์Šค์ฝ”ํ™€๋”ฉ์Šค", "ํ•œํ™”", "๋กฏ๋ฐ์ผ€๋ฏธ์นผ", "CJ์ œ์ผ์ œ๋‹น", "๋‘์‚ฐ์—๋„ˆ๋นŒ๋ฆฌํ‹ฐ", "KT", "์…€ํŠธ๋ฆฌ์˜จํ—ฌ์Šค์ผ€์–ด", "์—์Šค์—ํ”„์—์ด", "ํ•˜๋ฆผ์ง€์ฃผ", "์ด๋…น์Šค์ฒจ๋‹จ์†Œ์žฌ", "์ฝ”์›จ์ด", "ํ•œ์†”์ผ€๋ฏธ์นผ", "๋ดํ‹ฐ์›€", "์—์ฝ”ํ”„๋กœ์—์ด์น˜์—”", "๋‹ค์šฐ๊ธฐ์ˆ ", "์ œ๋„ฅ์‹ ", "์˜ค์Šคํ…œ์ž„ํ”Œ๋ž€ํŠธ", "๋น„์ธ ๋กœ์…€", "๋ฐ”๋””ํ”„๋žœ๋“œ", "์”จ์•„์ด์—์Šค", "์•„์ด์„ผ์Šค", "์ด๋ฃจ๋‹ค", "๋‚˜๋…ธ์‹ ์†Œ์žฌ", "์œ„๋ฉ”์ด๋“œ๋งฅ์Šค", "์—์ด์น˜์—˜์‚ฌ์ด์–ธ์Šค", "๋”๋„ค์ด์ณํ™€๋”ฉ์Šค", "ํ”ผ์—์Šค์ผ€์ด", "๋‰ดํŒŒ์›Œํ”„๋ผ์ฆˆ๋งˆ", "์ง€๋ˆ„์Šค", "์›์ตIPS", "์ง€์•„์ด์ด๋…ธ๋ฒ ์ด์…˜", "๋คผ์ด๋“œ", "์„ผ๋“œ๋ฒ„๋“œ", "์ง๋ฐฉ", "๋ฆฌ๋””", "๋ฒ„์ฆˆ๋นŒ", "๋‹น๊ทผ๋งˆ์ผ“", "์ปฌ๋ฆฌ", "์™“์ฑ ", "๋ฃจ๋‹›", "ํ”Œ๋ผ๋„คํƒ€๋ฆฌ์›€", "ํฌ๋ž˜ํ”„ํ†ค ๋ฒค์ฒ˜์Šค", "๋ฑ…ํฌ์ƒ๋Ÿฌ๋“œ", "์งํ† ", "ํŠธ๋ ˆ๋ฐ”๋ฆฌ", "๋ฉ”์Šคํ”„๋ ˆ์†Œ"]
7
+
8
+ # analyze_company_size ํ•จ์ˆ˜๋Š” llm_functions.py๋กœ ์ด๋™๋จ
9
+
10
+ def get_category_button_style(category):
11
+ """
12
+ ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์ƒ‰๊น”๊ณผ ์Šคํƒ€์ผ์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
13
+ """
14
+ category_styles = {
15
+ "๋Œ€๊ธฐ์—…": {"color": "#1E40AF", "bg_color": "#EBF8FF", "emoji": "๐Ÿข"},
16
+ "์ค‘๊ฒฌ๊ธฐ์—…": {"color": "#059669", "bg_color": "#ECFDF5", "emoji": "๐Ÿญ"},
17
+ "์ค‘์†Œ๊ธฐ์—…": {"color": "#DC2626", "bg_color": "#FEF2F2", "emoji": "๐Ÿช"},
18
+ "์Šคํƒ€ํŠธ์—…": {"color": "#7C3AED", "bg_color": "#F5F3FF", "emoji": "๐Ÿš€"},
19
+ "์™ธ๊ตญ๊ณ„๊ธฐ์—…": {"color": "#EA580C", "bg_color": "#FFF7ED", "emoji": "๐ŸŒ"},
20
+ "๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…": {"color": "#0F766E", "bg_color": "#F0FDFA", "emoji": "๐Ÿ›๏ธ"},
21
+ "๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ": {"color": "#BE185D", "bg_color": "#FDF2F8", "emoji": "๐Ÿค"},
22
+ "๊ธˆ์œต์—…": {"color": "#7C2D12", "bg_color": "#FEF7ED", "emoji": "๐Ÿฆ"},
23
+ "๋ถ„๋ฅ˜ ๋ถˆ๊ฐ€": {"color": "#6B7280", "bg_color": "#F9FAFB", "emoji": "โ“"},
24
+ "์˜ค๋ฅ˜ ๋ฐœ์ƒ": {"color": "#B91C1C", "bg_color": "#FEF2F2", "emoji": "โš ๏ธ"}
25
+ }
26
+
27
+ return category_styles.get(category, category_styles["๋ถ„๋ฅ˜ ๋ถˆ๊ฐ€"])
28
+
29
+ def create_category_html(category):
30
+ """
31
+ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์œ„ํ•œ HTML ๋ฒ„ํŠผ์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
32
+ """
33
+ style = get_category_button_style(category)
34
+
35
+ html = f"""
36
+ <div style="text-align: center; padding: 10px;">
37
+ <div style="
38
+ background-color: {style['bg_color']};
39
+ border: 2px solid {style['color']};
40
+ border-radius: 15px;
41
+ padding: 15px 20px;
42
+ margin: 10px 0;
43
+ font-weight: bold;
44
+ font-size: 16px;
45
+ color: {style['color']};
46
+ text-align: center;
47
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
48
+ ">
49
+ <div style="font-size: 24px; margin-bottom: 5px;">{style['emoji']}</div>
50
+ <div>{category}</div>
51
+ </div>
52
+ </div>
53
+ """
54
+ return html
55
+
56
+ def process_analysis_result(company_name):
57
+ """
58
+ ๋ถ„์„ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI์— ํ‘œ์‹œํ•  ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
59
+ """
60
+ try:
61
+ content, category = analyze_company_size(company_name)
62
+ category_html = create_category_html(category)
63
+ return content, category_html
64
+ except Exception as e:
65
+ error_content = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
66
+
67
+ ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค. {company_name}์˜ ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
68
+
69
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
70
+
71
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์‹œ๊ฑฐ๋‚˜ ๋‹ค๋ฅธ ๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.
72
+ """
73
+ error_html = create_category_html("์˜ค๋ฅ˜ ๋ฐœ์ƒ")
74
+ return error_content, error_html
75
+
76
+ def create_interface():
77
+ """
78
+ Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
79
+ """
80
+ with gr.Blocks(
81
+ title="๐Ÿข AI ๊ธฐ์—… ๊ทœ๋ชจ ์˜ˆ์ธก๊ธฐ",
82
+ theme=gr.themes.Soft(),
83
+ css="""
84
+ .main-header {
85
+ text-align: center;
86
+ padding: 20px;
87
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
88
+ color: white;
89
+ border-radius: 10px;
90
+ margin-bottom: 20px;
91
+ }
92
+ .example-section {
93
+ background-color: #f8f9fa;
94
+ padding: 15px;
95
+ border-radius: 8px;
96
+ margin: 10px 0;
97
+ }
98
+ """
99
+ ) as demo:
100
+
101
+ # ํ—ค๋”
102
+ gr.HTML("""
103
+ <div class="main-header">
104
+ <h1>๐Ÿข AI ๊ธฐ์—… ๊ทœ๋ชจ ์˜ˆ์ธก๊ธฐ</h1>
105
+ <p>OpenAI Search API๋ฅผ ํ™œ์šฉํ•œ ์‹ค์‹œ๊ฐ„ ๊ธฐ์—… ์ •๋ณด ๋ถ„์„</p>
106
+ </div>
107
+ """)
108
+
109
+ # ์„ค๋ช…
110
+ gr.Markdown("""
111
+ ### ๐Ÿš€ **์‚ฌ์šฉ ๋ฐฉ๋ฒ•**
112
+ 1. **์ง์ ‘ ์ž…๋ ฅ**: ๋ถ„์„ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ์—…๋ช…์„ ์ž…๋ ฅ์ฐฝ์— ์ง์ ‘ ์ž‘์„ฑ
113
+ 2. **์˜ˆ์ œ ์„ ํƒ**: ์•„๋ž˜ ์˜ˆ์ œ ๊ธฐ์—… ์ค‘ ํ•˜๋‚˜๋ฅผ ํด๋ฆญํ•˜์—ฌ ์ž๋™ ์ž…๋ ฅ
114
+ 3. **๋ถ„์„ ์‹คํ–‰**: '๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๊ฒฐ๊ณผ ํ™•์ธ
115
+
116
+ โœจ **ํŠน์ง•**: ์ตœ์‹  ์›น ์ •๋ณด๋ฅผ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ •ํ™•ํ•œ ๊ธฐ์—… ๊ทœ๋ชจ๋ฅผ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.
117
+ """)
118
+
119
+ with gr.Row():
120
+ with gr.Column(scale=2):
121
+ # ์ž…๋ ฅ ์„น์…˜
122
+ company_input = gr.Textbox(
123
+ label="๐Ÿข ๊ธฐ์—…๋ช… ์ž…๋ ฅ",
124
+ placeholder="๋ถ„์„ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š” (์˜ˆ: ์‚ผ์„ฑ์ „์ž, ์นด์นด์˜ค, ๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ ๋“ฑ)",
125
+ value="",
126
+ lines=1
127
+ )
128
+
129
+ analyze_btn = gr.Button(
130
+ "๐Ÿ” ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„",
131
+ variant="primary",
132
+ size="lg"
133
+ )
134
+
135
+ with gr.Column(scale=1):
136
+ # ์ •๋ณด ๋ฐ•์Šค
137
+ gr.Markdown("""
138
+ ### โ„น๏ธ **๋ถ„๋ฅ˜ ๊ธฐ์ค€**
139
+ - **๋Œ€๊ธฐ์—…**: ๋งค์ถœ 1์กฐ์› ์ด์ƒ
140
+ - **์ค‘๊ฒฌ๊ธฐ์—…**: ๋งค์ถœ 1000์–ต-1์กฐ์›
141
+ - **์ค‘์†Œ๊ธฐ์—…**: ๋งค์ถœ 1000์–ต์› ๋ฏธ๋งŒ
142
+ - **์Šคํƒ€ํŠธ์—…**: ์„ค๋ฆฝ 10๋…„ ์ด๋‚ด
143
+ - **์™ธ๊ตญ๊ณ„๊ธฐ์—…**: ํ•ด์™ธ ๋ณธ์‚ฌ ๊ธฐ์—…
144
+ - **๊ณต๊ณต๊ธฐ๊ด€**: ์ •๋ถ€ ์ถœ์ž/์ถœ์—ฐ
145
+ - **๋น„์˜๋ฆฌ๋‹จ์ฒด**: ๋น„์˜๋ฆฌ ๋ชฉ์ 
146
+ - **๊ธˆ์œต์—…**: ๊ธˆ์œต ์„œ๋น„์Šค์—…
147
+ """)
148
+
149
+ # ์˜ˆ์ œ ๊ธฐ์—… ์„น์…˜
150
+ gr.HTML('<div class="example-section">')
151
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ๊ธฐ์—… ์„ ํƒ** (ํด๋ฆญํ•˜๋ฉด ์ž๋™ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค)")
152
+
153
+ # ์˜ˆ์ œ ๋ฒ„ํŠผ๋“ค์„ ํ–‰๋ณ„๋กœ ๋ฐฐ์น˜
154
+ example_rows = [example_company_name[i:i+10] for i in range(0, len(example_company_name), 10)]
155
+
156
+ for row in example_rows:
157
+ with gr.Row():
158
+ for company in row:
159
+ example_btn = gr.Button(
160
+ company,
161
+ size="sm",
162
+ variant="secondary",
163
+ scale=1
164
+ )
165
+ example_btn.click(
166
+ fn=lambda x=company: x,
167
+ outputs=company_input
168
+ )
169
+
170
+ gr.HTML('</div>')
171
+
172
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
173
+ with gr.Row():
174
+ with gr.Column(scale=3):
175
+ gr.Markdown("### ๐Ÿ“‹ **๋ถ„์„ ๊ฒฐ๊ณผ**")
176
+ result_output = gr.Markdown(
177
+ label="๋ถ„์„ ๊ฒฐ๊ณผ",
178
+ value="๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•˜๊ณ  '๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
179
+ elem_classes=["result-output"]
180
+ )
181
+
182
+ with gr.Column(scale=1):
183
+ gr.Markdown("### ๐Ÿ“Š **๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ**")
184
+ # ์นดํ…Œ๊ณ ๋ฆฌ ํ‘œ์‹œ์šฉ HTML ์ปดํฌ๋„ŒํŠธ
185
+ category_display = gr.HTML(
186
+ value="""<div style="text-align: center; padding: 20px;">
187
+ <div style="color: #6B7280; font-size: 14px;">๋ถ„์„์„ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”</div>
188
+ </div>""",
189
+ elem_classes=["category-display"]
190
+ )
191
+
192
+ # ๋ถ„์„ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
193
+ analyze_btn.click(
194
+ fn=process_analysis_result,
195
+ inputs=[company_input],
196
+ outputs=[result_output, category_display],
197
+ api_name="analyze_company"
198
+ )
199
+
200
+ # ์—”ํ„ฐํ‚ค๋กœ๋„ ๋ถ„์„ ์‹คํ–‰ ๊ฐ€๋Šฅ
201
+ company_input.submit(
202
+ fn=process_analysis_result,
203
+ inputs=[company_input],
204
+ outputs=[result_output, category_display]
205
+ )
206
+
207
+ # ํ‘ธํ„ฐ
208
+ gr.Markdown("""
209
+ ---
210
+ **๐Ÿ’ก ํŒ**:
211
+ - ํ•œ๊ตญ ๊ธฐ์—…๋ช…๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ธ€๋กœ๋ฒŒ ๊ธฐ์—…๋„ ๋ถ„์„ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค
212
+ - ๋ถ„์„ ๊ฒฐ๊ณผ์—๋Š” ์‹ค์‹œ๊ฐ„ ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•œ ์ตœ์‹  ์ •๋ณด๊ฐ€ ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค
213
+ - ์ฐธ๊ณ  ์ž๋ฃŒ ๋งํฌ๋ฅผ ํ†ตํ•ด ์ƒ์„ธ ์ •๋ณด๋ฅผ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค
214
+
215
+ ๐Ÿ” **Powered by**: OpenAI Search API (gpt-4o-mini-search-preview)
216
+ """)
217
+
218
+ return demo
219
+
220
+ if __name__ == "__main__":
221
+ # Gradio ์•ฑ ์‹คํ–‰
222
+ demo = create_interface()
223
+ demo.launch(
224
+ server_name="0.0.0.0",
225
+ server_port=7859,
226
+ share=True,
227
+ show_error=True
228
+ )
company-size-classification/prompt.yaml ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt_simple: |
2
+ ๋‹น์‹ ์€ ํ•œ๊ตญ ๊ธฐ์—…์˜ ๊ทœ๋ชจ๋ฅผ ๋ถ„์„ํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์ฃผ์–ด์ง„ ๊ธฐ์—…์˜ ๊ทœ๋ชจ๋ฅผ ๋‹ค์Œ ์นดํ…Œ๊ณ ๋ฆฌ ์ค‘ ํ•˜๋‚˜๋กœ ๋ถ„๋ฅ˜ํ•ด์ฃผ์„ธ์š”.
3
+ ๋‹ต๋ณ€์€ ๋ฐ˜๋“œ์‹œ ์•„๋ž˜ ์นดํ…Œ๊ณ ๋ฆฌ ์ค‘ ํ•˜๋‚˜๋งŒ ํฌํ•จํ•ด์•ผ ํ•˜๋ฉฐ, ๋‹ค๋ฅธ ์„ค๋ช…์€ ํฌํ•จํ•˜์ง€ ๋งˆ์„ธ์š”.
4
+
5
+ ### ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„๋ฅ˜ ๊ธฐ์ค€
6
+ - ๋Œ€๊ธฐ์—…: ๋Œ€๊ทœ๋ชจ ๊ณ„์—ด์‚ฌ, ๋งค์ถœ 1์กฐ์› ์ด์ƒ, ์ง์› 1๋งŒ๋ช… ์ด์ƒ
7
+ - ์ค‘๊ฒฌ๊ธฐ์—…: ๋งค์ถœ 1000์–ต-1์กฐ์›, ์ง์› 300๋ช…-1๋งŒ๋ช…
8
+ - ์ค‘์†Œ๊ธฐ์—…: ๋งค์ถœ 1000์–ต์› ๋ฏธ๋งŒ, ์ง์› 300๋ช… ๋ฏธ๋งŒ
9
+ - ์Šคํƒ€ํŠธ์—…: ์„ค๋ฆฝ 10๋…„ ์ด๋‚ด, ๋น ๋ฅธ ์„ฑ์žฅ ๋‹จ๊ณ„
10
+ - ์™ธ๊ตญ๊ณ„๊ธฐ์—…: ํ•ด์™ธ ๋ณธ์‚ฌ๋ฅผ ๋‘” ๊ธฐ์—…์˜ ํ•œ๊ตญ ์ง€์‚ฌ
11
+ - ๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…: ์ •๋ถ€ ์ถœ์ž/์ถœ์—ฐ ๊ธฐ๊ด€
12
+ - ๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ: ๋น„์˜๋ฆฌ ๋ชฉ์  ์กฐ์ง
13
+ - ๊ธˆ์œต์—…: ์€ํ–‰, ๋ณดํ—˜, ์ฆ๊ถŒ ๋“ฑ ๊ธˆ์œต ์„œ๋น„์Šค
14
+
15
+ ### ์˜ˆ์‹œ
16
+ [์˜ˆ์‹œ 1]
17
+ ๊ธฐ์—… ์ด๋ฆ„: ์‚ผ์„ฑ์ „์ž
18
+ ๊ธฐ์—… ๊ทœ๋ชจ: ๋Œ€๊ธฐ์—…
19
+
20
+ [Example 2]
21
+ ๊ธฐ์—… ์ด๋ฆ„: LG์ „์ž
22
+ ๊ธฐ์—… ๊ทœ๋ชจ: ๋Œ€๊ธฐ์—…
23
+
24
+ [Example 3]
25
+ ๊ธฐ์—… ์ด๋ฆ„: ์นด์นด์˜ค
26
+ ๊ธฐ์—… ๊ทœ๋ชจ: ์ค‘๊ฒฌ๊ธฐ์—…
27
+
28
+ [Example 4]
29
+ ๊ธฐ์—… ์ด๋ฆ„: ์ฟ ํŒก
30
+ ๊ธฐ์—… ๊ทœ๋ชจ: ์Šคํƒ€ํŠธ์—…
31
+
32
+ [Example 5]
33
+ ๊ธฐ์—… ์ด๋ฆ„: JP๋ชจ๊ฑด
34
+ ๊ธฐ์—… ๊ทœ๋ชจ: ์™ธ๊ตญ๊ณ„๊ธฐ์—…
35
+
36
+ [Example 6]
37
+ ๊ธฐ์—… ์ด๋ฆ„: ์„œ์šธ์‹œ์ฒญ
38
+ ๊ธฐ์—… ๊ทœ๋ชจ: ๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…
39
+
40
+ [Example 7]
41
+ ๊ธฐ์—… ์ด๋ฆ„: ํ•œ๊ตญ๋ ˆ๋“œํฌ๋กœ์Šค
42
+ ๊ธฐ์—… ๊ทœ๋ชจ: ๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ
43
+
44
+ [Example 8]
45
+ ๊ธฐ์—… ์ด๋ฆ„: ์‹ ํ•œ์€ํ–‰
46
+ ๊ธฐ์—… ๊ทœ๋ชจ: ๊ธˆ์œต์—…
47
+
48
+ ### Input
49
+ ๊ธฐ์—… ์ด๋ฆ„: {company_name}
50
+ ๊ธฐ์—… ๊ทœ๋ชจ:
51
+
52
+
53
+
54
+ prompt: |
55
+ ๋‹น์‹ ์€ ํ•œ๊ตญ ๊ธฐ์—…์˜ ๊ทœ๋ชจ๋ฅผ ๋ถ„์„ํ•˜๋Š” ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์–ป์€ ์ตœ์‹  ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ธฐ์—…์˜ ๊ทœ๋ชจ๋ฅผ ์ •ํ™•ํžˆ ๋ถ„๋ฅ˜ํ•˜๊ณ  ์ƒ์„ธํ•œ ๋ถ„์„์„ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”.
56
+
57
+ ### ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„๋ฅ˜ ๊ธฐ์ค€
58
+ - ๋Œ€๊ธฐ์—…: ๋Œ€๊ทœ๋ชจ ๊ณ„์—ด์‚ฌ, ๋งค์ถœ 1์กฐ์› ์ด์ƒ, ์ง์› 1๋งŒ๋ช… ์ด์ƒ
59
+ - ์ค‘๊ฒฌ๊ธฐ์—…: ๋งค์ถœ 1000์–ต-1์กฐ์›, ์ง์› 300๋ช…-1๋งŒ๋ช…
60
+ - ์ค‘์†Œ๊ธฐ์—…: ๋งค์ถœ 1000์–ต์› ๋ฏธ๋งŒ, ์ง์› 300๋ช… ๋ฏธ๋งŒ
61
+ - ์Šคํƒ€ํŠธ์—…: ์„ค๋ฆฝ 10๋…„ ์ด๋‚ด, ๋น ๋ฅธ ์„ฑ์žฅ ๋‹จ๊ณ„
62
+ - ์™ธ๊ตญ๊ณ„๊ธฐ์—…: ํ•ด์™ธ ๋ณธ์‚ฌ๋ฅผ ๋‘” ๊ธฐ์—…์˜ ํ•œ๊ตญ ์ง€์‚ฌ
63
+ - ๊ณต๊ณต๊ธฐ๊ด€ ๋ฐ ๊ณต๊ธฐ์—…: ์ •๋ถ€ ์ถœ์ž/์ถœ์—ฐ ๊ธฐ๊ด€
64
+ - ๋น„์˜๋ฆฌ๋‹จ์ฒด ๋ฐ ํ˜‘ํšŒ์žฌ๋‹จ: ๋น„์˜๋ฆฌ ๋ชฉ์  ์กฐ์ง
65
+ - ๊ธˆ์œต์—…: ์€ํ–‰, ๋ณดํ—˜, ์ฆ๊ถŒ ๋“ฑ ๊ธˆ์œต ์„œ๋น„์Šค
66
+ - ๋ถ„๋ฅ˜๋ถˆ๊ฐ€: ์ถฉ๋ถ„ํ•œ ์ •๋ณด๊ฐ€ ์—†์–ด ๋ถ„๋ฅ˜๊ฐ€ ์–ด๋ ค์šด ๊ฒฝ์ถฉ
67
+
68
+ ### ๋‹ต๋ณ€ ํ˜•์‹
69
+ ๋‹ค์Œ ํ˜•์‹์œผ๋กœ ์ƒ์„ธํ•œ ๋ถ„์„์„ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”:
70
+
71
+ **๊ธฐ์—… ๊ฐœ์š”:**
72
+ - ํ•ด๋‹น ๊ธฐ์—…์— ๋Œ€ํ•œ ๊ฐ„๋‹จํ•œ ์†Œ๊ฐœ (3-4์ค„)
73
+
74
+ **๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„๋ฅ˜:** [์œ„ 8๊ฐœ ์นดํ…Œ๊ณ ๋ฆฌ ์ค‘ ํ•˜๋‚˜]
75
+
76
+ **๋ถ„๋ฅ˜ ๊ทผ๊ฑฐ:**
77
+ - ๋งค์ถœ ๊ทœ๋ชจ: (๊ตฌ์ฒด์  ์ˆ˜์น˜ ํฌํ•จ)
78
+ - ์ง์› ์ˆ˜: (๊ตฌ์ฒด์  ์ˆ˜์น˜ ํฌํ•จ)
79
+ - ์—…์ข…/์‚ฌ์—… ์˜์—ญ:
80
+ - ๊ธฐํƒ€ ํŠน์ง•:
81
+
82
+ ### ๋ถ„์„ ๋Œ€์ƒ ๊ธฐ์—…
83
+ ๊ธฐ์—… ์ด๋ฆ„: {company_name}
84
+
85
+ ์œ„ ํ˜•์‹์— ๋”ฐ๋ผ ์ƒ์„ธํ•œ ๋ถ„์„์„ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”. ๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ๋Š” ๋ฐ˜๋“œ์‹œ ๋‹ต๋ณ€ ๋งˆ์ง€๋ง‰์— ```<๊ธฐ์—…๊ทœ๋ชจ>``` ํ˜•์‹์œผ๋กœ ํ‘œ์‹œํ•ด์ฃผ์„ธ์š”. ๋ถ„๋ฅ˜ ์นดํ…Œ๊ณ ๋ฆฌ๋Š” ๋ฐ˜๋“œ์‹œ ๋‹ต๋ณ€ ๋งˆ์ง€๋ง‰์— ```<๊ธฐ์—…๊ทœ๋ชจ>``` ํ˜•์‹์œผ๋กœ ํ‘œ์‹œํ•ด์ฃผ์„ธ์š” ("<" ์™€ ">" ๊ธฐํ˜ธ ํฌํ•จ).
industry-classification/.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
industry-classification/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (11.2 kB). View file
 
industry-classification/eval.json ADDED
@@ -0,0 +1,256 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "industryCategories": [
3
+ {
4
+ "id": "it-platform-game",
5
+ "name": "IT ยท ํ”Œ๋žซํผ ยท ๊ฒŒ์ž„",
6
+ "isPrimary": true,
7
+ "tags": [
8
+ { "tagId": "platform-portal", "tagName": "#ํ”Œ๋žซํผ/ํฌํ„ธ" },
9
+ { "tagId": "e-commerce", "tagName": "#์ด์ปค๋จธ์Šค" },
10
+ { "tagId": "game", "tagName": "#๊ฒŒ์ž„" },
11
+ { "tagId": "it-solution-si", "tagName": "#IT์†”๋ฃจ์…˜/SI" },
12
+ { "tagId": "o2o-vertical", "tagName": "#O2O/๋ฒ„ํ‹ฐ์ปฌ" },
13
+ { "tagId": "ai-data", "tagName": "#AI/๋ฐ์ดํ„ฐ" },
14
+ { "tagId": "cloud-saas", "tagName": "#ํด๋ผ์šฐ๋“œ/SaaS" },
15
+ { "tagId": "fintech", "tagName": "#ํ•€ํ…Œํฌ" }
16
+ ]
17
+ },
18
+ {
19
+ "id": "manufacturing-hardware",
20
+ "name": "์ œ์กฐ ยท ํ•˜๋“œ์›จ์–ด",
21
+ "isPrimary": true,
22
+ "tags": [
23
+ { "tagId": "semiconductor", "tagName": "#๋ฐ˜๋„์ฒด" },
24
+ { "tagId": "electronics-home", "tagName": "#๊ฐ€์ „/์ „์ž์ œํ’ˆ" },
25
+ { "tagId": "automotive-mobility", "tagName": "#์ž๋™์ฐจ/๋ชจ๋นŒ๋ฆฌํ‹ฐ" },
26
+ { "tagId": "battery", "tagName": "#2์ฐจ์ „์ง€" },
27
+ { "tagId": "display", "tagName": "#๋””์Šคํ”Œ๋ ˆ์ด" },
28
+ { "tagId": "heavy-industry-shipbuilding", "tagName": "#์ค‘๊ณต์—…/์กฐ์„ " },
29
+ { "tagId": "steel-metal", "tagName": "#์ฒ ๊ฐ•/๊ธˆ์†" }
30
+ ]
31
+ },
32
+ {
33
+ "id": "finance",
34
+ "name": "๊ธˆ์œต",
35
+ "isPrimary": true,
36
+ "tags": [
37
+ { "tagId": "bank", "tagName": "#์€ํ–‰" },
38
+ { "tagId": "securities", "tagName": "#์ฆ๊ถŒ" },
39
+ { "tagId": "insurance", "tagName": "#๋ณดํ—˜" },
40
+ { "tagId": "card", "tagName": "#์นด๋“œ" },
41
+ { "tagId": "asset-management", "tagName": "#์ž์‚ฐ์šด์šฉ" }
42
+ ]
43
+ },
44
+ {
45
+ "id": "retail-fmcg-life",
46
+ "name": "์œ ํ†ต ยท ์†Œ๋น„์žฌ ยท ์ƒํ™œ",
47
+ "isPrimary": true,
48
+ "tags": [
49
+ { "tagId": "dept-store-mart", "tagName": "#๋ฐฑํ™”์ /๋งˆํŠธ" },
50
+ { "tagId": "convenience-store", "tagName": "#ํŽธ์˜์ " },
51
+ { "tagId": "fmcg-beverage", "tagName": "#FMCG/์‹์Œ๋ฃŒ" },
52
+ { "tagId": "fashion-beauty", "tagName": "#ํŒจ์…˜/๋ทฐํ‹ฐ" },
53
+ { "tagId": "duty-free", "tagName": "#๋ฉด์„ธ์ " }
54
+ ]
55
+ },
56
+ {
57
+ "id": "bio-pharma-medical",
58
+ "name": "๋ฐ”์ด์˜ค ยท ์ œ์•ฝ ยท ์˜๋ฃŒ",
59
+ "isPrimary": true,
60
+ "tags": [
61
+ { "tagId": "pharma-new-drug", "tagName": "#์ œ์•ฝ/์‹ ์•ฝ๊ฐœ๋ฐœ" },
62
+ { "tagId": "bio-cmo", "tagName": "#๋ฐ”์ด์˜ค/CMO" },
63
+ { "tagId": "medical-device", "tagName": "#์˜๋ฃŒ๊ธฐ๊ธฐ" },
64
+ { "tagId": "digital-healthcare", "tagName": "#๋””์ง€ํ„ธํ—ฌ์Šค์ผ€์–ด" }
65
+ ]
66
+ },
67
+ {
68
+ "id": "media-contents-ad",
69
+ "name": "๋ฏธ๋””์–ด ยท ์ฝ˜ํ…์ธ  ยท ๊ด‘๊ณ ",
70
+ "isPrimary": true,
71
+ "tags": [
72
+ { "tagId": "entertainment", "tagName": "#์—”ํ„ฐํ…Œ์ธ๋จผํŠธ" },
73
+ { "tagId": "contents-video", "tagName": "#์ฝ˜ํ…์ธ /์˜์ƒ์ œ์ž‘" },
74
+ { "tagId": "ad-agency", "tagName": "#๊ด‘๊ณ ๋Œ€ํ–‰์‚ฌ" },
75
+ { "tagId": "webtoon-webnovel", "tagName": "#์›นํˆฐ/์›น์†Œ์„ค" },
76
+ { "tagId": "broadcasting-press", "tagName": "#๋ฐฉ์†ก/์–ธ๋ก " }
77
+ ]
78
+ },
79
+ {
80
+ "id": "construction-realestate",
81
+ "name": "๊ฑด์„ค ยท ๋ถ€๋™์‚ฐ",
82
+ "isPrimary": true,
83
+ "tags": [
84
+ { "tagId": "construction-engineering", "tagName": "#๊ฑด์„ค/์—”์ง€๋‹ˆ์–ด๋ง" },
85
+ { "tagId": "realestate-development", "tagName": "#๋ถ€๋™์‚ฐ๊ฐœ๋ฐœ" },
86
+ { "tagId": "plant", "tagName": "#ํ”Œ๋žœํŠธ" },
87
+ { "tagId": "interior", "tagName": "#์ธํ…Œ๋ฆฌ์–ด" }
88
+ ]
89
+ },
90
+ {
91
+ "id": "public-corp",
92
+ "name": "๊ณต๊ธฐ์—… ยท ๊ณต๊ณต๊ธฐ๊ด€",
93
+ "isPrimary": true,
94
+ "tags": [
95
+ { "tagId": "public-soc", "tagName": "#SOC (๊ณตํ•ญ,๋„๋กœ,์ฒ ๋„)" },
96
+ { "tagId": "public-energy", "tagName": "#์—๋„ˆ์ง€ ๊ณต๊ธฐ์—…" },
97
+ { "tagId": "public-finance", "tagName": "#๊ธˆ์œต ๊ณต๊ธฐ์—…" },
98
+ { "tagId": "public-admin", "tagName": "#์ผ๋ฐ˜ํ–‰์ •" }
99
+ ]
100
+ },
101
+ {
102
+ "id": "materials-parts-equipment",
103
+ "name": "์†Œ์žฌ ยท ๋ถ€ํ’ˆ ยท ์žฅ๋น„ (์†Œ๋ถ€์žฅ)",
104
+ "isPrimary": false,
105
+ "tags": [
106
+ { "tagId": "mpe-semiconductor", "tagName": "#๋ฐ˜๋„์ฒด ์†Œ๋ถ€์žฅ" },
107
+ { "tagId": "mpe-display", "tagName": "#๋””์Šคํ”Œ๋ ˆ์ด ์†Œ๋ถ€์žฅ" },
108
+ { "tagId": "mpe-battery", "tagName": "#2์ฐจ์ „์ง€ ์†Œ๋ถ€์žฅ" },
109
+ { "tagId": "auto-parts", "tagName": "#์ž๋™์ฐจ ๋ถ€ํ’ˆ" },
110
+ { "tagId": "chemical-materials", "tagName": "#ํ™”ํ•™/์†Œ์žฌ" }
111
+ ]
112
+ },
113
+ {
114
+ "id": "hospitality-travel-airline",
115
+ "name": "ํ˜ธํ…” ยท ์—ฌํ–‰ ยท ํ•ญ๊ณต",
116
+ "isPrimary": false,
117
+ "tags": [
118
+ { "tagId": "hotel", "tagName": "#ํ˜ธํ…”" },
119
+ { "tagId": "travel-agency", "tagName": "#์—ฌํ–‰์‚ฌ" },
120
+ { "tagId": "airline", "tagName": "#ํ•ญ๊ณต์‚ฌ" },
121
+ { "tagId": "leisure-resort", "tagName": "#๋ ˆ์ €/๋ฆฌ์กฐํŠธ" }
122
+ ]
123
+ },
124
+ {
125
+ "id": "professional-services",
126
+ "name": "์ „๋ฌธ ์„œ๋น„์Šค",
127
+ "isPrimary": false,
128
+ "tags": [
129
+ { "tagId": "consulting", "tagName": "#์ปจ์„คํŒ…" },
130
+ { "tagId": "accounting-tax", "tagName": "#ํšŒ๊ณ„/์„ธ๋ฌด" },
131
+ { "tagId": "law-firm", "tagName": "#๋ฒ•๋ฅ  (๋กœํŽŒ)" },
132
+ { "tagId": "market-research", "tagName": "#๋ฆฌ์„œ์น˜" }
133
+ ]
134
+ },
135
+ {
136
+ "id": "transportation-logistics",
137
+ "name": "์šด์†ก ยท ๋ฌผ๋ฅ˜",
138
+ "isPrimary": false,
139
+ "tags": [
140
+ { "tagId": "logistics-delivery", "tagName": "#๋ฌผ๋ฅ˜/ํƒ๋ฐฐ" },
141
+ { "tagId": "shipping", "tagName": "#ํ•ด์šด" },
142
+ { "tagId": "forwarding", "tagName": "#ํฌ์›Œ๋”ฉ" },
143
+ { "tagId": "land-transport", "tagName": "#์œก์ƒ์šด์†ก" }
144
+ ]
145
+ },
146
+ {
147
+ "id": "education",
148
+ "name": "๊ต์œก",
149
+ "isPrimary": false,
150
+ "tags": [
151
+ { "tagId": "edutech", "tagName": "#์—๋“€ํ…Œํฌ" },
152
+ { "tagId": "private-academy", "tagName": "#์ž…์‹œ/๋ณด์Šตํ•™์›" },
153
+ { "tagId": "edu-publishing", "tagName": "#๊ต์œก์ถœํŒ" },
154
+ { "tagId": "language-edu", "tagName": "#์™ธ๊ตญ์–ด๊ต์œก" }
155
+ ]
156
+ },
157
+ {
158
+ "id": "nonprofit-social-enterprise",
159
+ "name": "๋น„์˜๋ฆฌ ยท ์‚ฌํšŒ์ ๊ธฐ์—…",
160
+ "isPrimary": false,
161
+ "tags": [
162
+ { "tagId": "ngo-npo", "tagName": "#NGO/NPO" },
163
+ { "tagId": "social-enterprise", "tagName": "#์‚ฌํšŒ์ ๊ธฐ์—…" },
164
+ { "tagId": "foundation", "tagName": "#์žฌ๋‹จ" }
165
+ ]
166
+ }
167
+ ],
168
+ "industry_classification_eval": {
169
+ "task_definition": {
170
+ "description": "Classify the company's industry based on job title and company name. The output must be an array containing one or more relevant 'tagId's from the provided industry list.",
171
+ "input": {
172
+ "job_title": "string",
173
+ "company_name": "string"
174
+ },
175
+ "output": {
176
+ "type": "array",
177
+ "items": {
178
+ "type": "string",
179
+ "description": "A valid tagId from the industryCategories list."
180
+ }
181
+ }
182
+ },
183
+ "examples": [
184
+ {
185
+ "input": {
186
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
187
+ "company_name": "์‚ผ์„ฑ์ „์ž"
188
+ },
189
+ "output": ["semiconductor", "electronics-home"]
190
+ },
191
+ {
192
+ "input": {
193
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
194
+ "company_name": "์‹ ํ•œ์€ํ–‰"
195
+ },
196
+ "output": ["bank"]
197
+ },
198
+ {
199
+ "input": {
200
+ "job_title": "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…",
201
+ "company_name": "์ฟ ํŒก"
202
+ },
203
+ "output": ["e-commerce", "platform-portal"]
204
+ },
205
+ {
206
+ "input": {
207
+ "job_title": "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…",
208
+ "company_name": "์•„๋ชจ๋ ˆํผ์‹œํ”ฝ"
209
+ },
210
+ "output": ["fashion-beauty", "fmcg-beverage"]
211
+ },
212
+ {
213
+ "input": {
214
+ "job_title": "HRM(์ธ์‚ฌ์šด์˜)",
215
+ "company_name": "ํ˜„๋Œ€๊ฑด์„ค"
216
+ },
217
+ "output": ["construction-engineering"]
218
+ },
219
+ {
220
+ "input": {
221
+ "job_title": "HRM(์ธ์‚ฌ์šด์˜)",
222
+ "company_name": "ํ•˜์ด๋ธŒ"
223
+ },
224
+ "output": ["entertainment"]
225
+ },
226
+ {
227
+ "input": {
228
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
229
+ "company_name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)"
230
+ },
231
+ "output": ["fintech", "platform-portal"]
232
+ },
233
+ {
234
+ "input": {
235
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
236
+ "company_name": "๋„ฅ์Šจ"
237
+ },
238
+ "output": ["game"]
239
+ },
240
+ {
241
+ "input": {
242
+ "job_title": "ํ•ด์™ธ์˜์—…",
243
+ "company_name": "์‚ผ์„ฑ๋ฐ”์ด์˜ค๋กœ์ง์Šค"
244
+ },
245
+ "output": ["bio-cmo", "pharma-new-drug"]
246
+ },
247
+ {
248
+ "input": {
249
+ "job_title": "ํ•ด์™ธ์˜์—…",
250
+ "company_name": "HMM (์—์ด์น˜์— ์— )"
251
+ },
252
+ "output": ["shipping"]
253
+ }
254
+ ]
255
+ }
256
+ }
industry-classification/llm_functions.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import yaml
5
+ from openai import OpenAI
6
+
7
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
8
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9
+
10
+ # ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
11
+ import os
12
+ current_dir = os.path.dirname(os.path.abspath(__file__))
13
+ prompt_path = os.path.join(current_dir, 'prompt.yaml')
14
+ with open(prompt_path, 'r', encoding='utf-8') as f:
15
+ prompt_data = yaml.safe_load(f)
16
+ prompt_template = prompt_data['prompt']
17
+
18
+ def parse_industry_tags(content):
19
+ """
20
+ AI ์‘๋‹ต์—์„œ ์‚ฐ์—… ํƒœ๊ทธ ๋ฐฐ์—ด์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
21
+ """
22
+ try:
23
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ๊ธธ์ด: {len(content)}")
24
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ์ฒซ 200์ž: {repr(content[:200])}")
25
+
26
+ # ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ
27
+ cleaned_content = content.strip()
28
+
29
+ # 1. JSON ์ฝ”๋“œ ๋ธ”๋ก ์ฐพ๊ธฐ (```json ... ``` ํ˜•์‹)
30
+ json_patterns = [
31
+ r'```json\s*(\[.*?\])\s*```',
32
+ r'```\s*(\[.*?\])\s*```',
33
+ r'```json\s*(.*?)\s*```',
34
+ r'```\s*(.*?)\s*```'
35
+ ]
36
+
37
+ for pattern in json_patterns:
38
+ json_match = re.search(pattern, cleaned_content, re.DOTALL)
39
+ if json_match:
40
+ json_str = json_match.group(1).strip()
41
+ print(f"JSON ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
42
+
43
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
44
+ json_str = re.sub(r'\n\s*', ' ', json_str)
45
+ json_str = re.sub(r',\s*]', ']', json_str)
46
+
47
+ try:
48
+ parsed_json = json.loads(json_str)
49
+ if isinstance(parsed_json, list):
50
+ return parsed_json
51
+ except json.JSONDecodeError as e:
52
+ print(f"JSON ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
53
+
54
+ # 2. ๋Œ€๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ๋ฐฐ์—ด ์ฐพ๊ธฐ
55
+ array_patterns = [
56
+ r'\[(.*?)\]'
57
+ ]
58
+
59
+ for pattern in array_patterns:
60
+ array_match = re.search(pattern, cleaned_content, re.DOTALL)
61
+ if array_match:
62
+ array_content = array_match.group(1).strip()
63
+ print(f"๋ฐฐ์—ด ๋‚ด์šฉ ๋ฐœ๊ฒฌ: {repr(array_content[:100])}")
64
+
65
+ # ๋ฐฐ์—ด ๋‚ด์šฉ์—์„œ ๋ฌธ์ž์—ด ์ถ”์ถœ
66
+ tags = []
67
+ # ๋”ฐ์˜ดํ‘œ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ๋ฌธ์ž์—ด๋“ค ์ฐพ๊ธฐ
68
+ tag_matches = re.findall(r'"([^"]+)"', array_content)
69
+ for tag in tag_matches:
70
+ if tag.strip():
71
+ tags.append(tag.strip())
72
+
73
+ if tags:
74
+ return tags
75
+
76
+ # 3. ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JSON์œผ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
77
+ try:
78
+ # ์ฝ”๋“œ ๋ธ”๋ก ๋งˆ์ปค ์ œ๊ฑฐ
79
+ if cleaned_content.startswith('```'):
80
+ lines = cleaned_content.split('\n')
81
+ start_idx = 1 if lines[0].startswith('```') else 0
82
+ end_idx = len(lines)
83
+ for i in range(len(lines)-1, -1, -1):
84
+ if lines[i].strip() == '```':
85
+ end_idx = i
86
+ break
87
+ cleaned_content = '\n'.join(lines[start_idx:end_idx])
88
+
89
+ cleaned_content = cleaned_content.strip()
90
+ parsed_json = json.loads(cleaned_content)
91
+ if isinstance(parsed_json, list):
92
+ return parsed_json
93
+ except json.JSONDecodeError as e:
94
+ print(f"์ „์ฒด JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
95
+
96
+ # 4. ์ตœํ›„์˜ ์ˆ˜๋‹จ: ํŒจํ„ด ๋งค์นญ์œผ๋กœ ํƒœ๊ทธ ์ถ”์ถœ
97
+ print("ํŒจํ„ด ๋งค์นญ์œผ๋กœ ํƒœ๊ทธ ์ถ”์ถœ ์‹œ๋„")
98
+ tags = []
99
+
100
+ # ๋‹ค์–‘ํ•œ ํŒจํ„ด์œผ๋กœ ํƒœ๊ทธ ์ฐพ๊ธฐ
101
+ patterns = [
102
+ r'"([a-z-]+)"', # ๋”ฐ์˜ดํ‘œ ์•ˆ์˜ ํƒœ๊ทธ ํ˜•ํƒœ
103
+ r'([a-z-]+)', # ๋‹จ์ˆœ ํƒœ๊ทธ ํ˜•ํƒœ
104
+ ]
105
+
106
+ for pattern in patterns:
107
+ matches = re.findall(pattern, cleaned_content)
108
+ for match in matches:
109
+ tag = match.strip()
110
+ if len(tag) > 2 and '-' in tag and tag not in tags:
111
+ tags.append(tag)
112
+ if len(tags) >= 5: # ์ตœ๋Œ€ 5๊ฐœ
113
+ break
114
+ if tags:
115
+ break
116
+
117
+ return tags[:5] if tags else []
118
+
119
+ except Exception as e:
120
+ print(f"ํƒœ๊ทธ ํŒŒ์‹ฑ ์ „์ฒด ์˜ค๋ฅ˜: {e}")
121
+ print(f"ํŒŒ์‹ฑ ์‹คํŒจํ•œ ์ปจํ…์ธ : {repr(content)}")
122
+ return []
123
+
124
+ def classify_industry(job_title, company_name):
125
+ """
126
+ OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ธฐ์—…์˜ ์‚ฐ์—…์„ ๋ถ„๋ฅ˜ํ•˜๋Š” ํ•จ์ˆ˜
127
+ """
128
+ try:
129
+ if not job_title or not company_name:
130
+ return "์ง๋ฌด์™€ ํšŒ์‚ฌ๋ช…์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", []
131
+
132
+ # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
133
+ prompt = prompt_template.format(
134
+ job_title=job_title,
135
+ company_name=company_name
136
+ )
137
+
138
+ # OpenAI Responses API ํ˜ธ์ถœ (Web Search Preview ์‚ฌ๏ฟฝ๏ฟฝ๏ฟฝ)
139
+ response = client.responses.create(
140
+ model="gpt-4o",
141
+ tools=[{
142
+ "type": "web_search_preview",
143
+ "search_context_size": "high",
144
+ }],
145
+ input=f"๋‹น์‹ ์€ ๊ธฐ์—… ์‚ฐ์—… ๋ถ„๋ฅ˜ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์ตœ์‹  ์ •๋ณด๋ฅผ ํ™•์ธํ•˜๊ณ  ์ •ํ™•ํ•œ ์‚ฐ์—… ํƒœ๊ทธ๋ฅผ JSON ๋ฐฐ์—ด ํ˜•์‹์œผ๋กœ ๋ฐ˜ํ™˜ํ•ด์ฃผ์„ธ์š”.\n\n{prompt}"
146
+ )
147
+
148
+ content = response.output_text
149
+ print(f"=== AI ์‘๋‹ต ์›๋ณธ ===")
150
+ print(content)
151
+ print(f"=== ์ „์ฒด ์‘๋‹ต ๊ฐ์ฒด ===")
152
+ print(response)
153
+
154
+ # ์›น ๊ฒ€์ƒ‰ ์ฐธ๊ณ  ๋งํฌ ์ถœ๋ ฅ
155
+ if hasattr(response, 'web_search_results') and response.web_search_results:
156
+ print(f"=== ์ฐธ๊ณ ํ•œ ์›น ๊ฒ€์ƒ‰ ๋งํฌ ===")
157
+ for i, result in enumerate(response.web_search_results, 1):
158
+ if hasattr(result, 'url'):
159
+ print(f"{i}. {result.url}")
160
+ elif hasattr(result, 'link'):
161
+ print(f"{i}. {result.link}")
162
+
163
+ print(f"=== AI ์‘๋‹ต ๋ ===")
164
+
165
+ tags = parse_industry_tags(content)
166
+
167
+ if not tags:
168
+ return "์‚ฐ์—… ๋ถ„๋ฅ˜์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", []
169
+
170
+ # ํƒœ๊ทธ๋ช… ๋งคํ•‘ (ํ‘œ์‹œ์šฉ)
171
+ tag_mapping = {
172
+ "platform-portal": "ํ”Œ๋žซํผ/ํฌํ„ธ",
173
+ "e-commerce": "์ด์ปค๋จธ์Šค",
174
+ "game": "๊ฒŒ์ž„",
175
+ "it-solution-si": "IT์†”๋ฃจ์…˜/SI",
176
+ "o2o-vertical": "O2O/๋ฒ„ํ‹ฐ์ปฌ",
177
+ "ai-data": "AI/๋ฐ์ดํ„ฐ",
178
+ "cloud-saas": "ํด๋ผ์šฐ๋“œ/SaaS",
179
+ "fintech": "ํ•€ํ…Œํฌ",
180
+ "semiconductor": "๋ฐ˜๋„์ฒด",
181
+ "electronics-home": "๊ฐ€์ „/์ „์ž์ œํ’ˆ",
182
+ "automotive-mobility": "์ž๋™์ฐจ/๋ชจ๋นŒ๋ฆฌํ‹ฐ",
183
+ "battery": "2์ฐจ์ „์ง€",
184
+ "display": "๋””์Šคํ”Œ๋ ˆ์ด",
185
+ "heavy-industry-shipbuilding": "์ค‘๊ณต์—…/์กฐ์„ ",
186
+ "steel-metal": "์ฒ ๊ฐ•/๊ธˆ์†",
187
+ "bank": "์€ํ–‰",
188
+ "securities": "์ฆ๊ถŒ",
189
+ "insurance": "๋ณดํ—˜",
190
+ "card": "์นด๋“œ",
191
+ "asset-management": "์ž์‚ฐ์šด์šฉ",
192
+ "dept-store-mart": "๋ฐฑํ™”์ /๋งˆํŠธ",
193
+ "convenience-store": "ํŽธ์˜์ ",
194
+ "fmcg-beverage": "FMCG/์‹์Œ๋ฃŒ",
195
+ "fashion-beauty": "ํŒจ์…˜/๋ทฐํ‹ฐ",
196
+ "duty-free": "๋ฉด์„ธ์ ",
197
+ "pharma-new-drug": "์ œ์•ฝ/์‹ ์•ฝ๊ฐœ๋ฐœ",
198
+ "bio-cmo": "๋ฐ”์ด์˜ค/CMO",
199
+ "medical-device": "์˜๋ฃŒ๊ธฐ๊ธฐ",
200
+ "digital-healthcare": "๋””์ง€ํ„ธํ—ฌ์Šค์ผ€์–ด",
201
+ "entertainment": "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ",
202
+ "contents-video": "์ฝ˜ํ…์ธ /์˜์ƒ์ œ์ž‘",
203
+ "ad-agency": "๊ด‘๊ณ ๋Œ€ํ–‰์‚ฌ",
204
+ "webtoon-webnovel": "์›นํˆฐ/์›น์†Œ์„ค",
205
+ "broadcasting-press": "๋ฐฉ์†ก/์–ธ๋ก ",
206
+ "construction-engineering": "๊ฑด์„ค/์—”์ง€๋‹ˆ์–ด๋ง",
207
+ "realestate-development": "๋ถ€๋™์‚ฐ๊ฐœ๋ฐœ",
208
+ "plant": "ํ”Œ๋žœํŠธ",
209
+ "interior": "์ธํ…Œ๋ฆฌ์–ด",
210
+ "public-soc": "SOC (๊ณตํ•ญ,๋„๋กœ,์ฒ ๋„)",
211
+ "public-energy": "์—๋„ˆ์ง€ ๊ณต๊ธฐ์—…",
212
+ "public-finance": "๊ธˆ์œต ๊ณต๊ธฐ์—…",
213
+ "public-admin": "์ผ๋ฐ˜ํ–‰์ •",
214
+ "mpe-semiconductor": "๋ฐ˜๋„์ฒด ์†Œ๋ถ€์žฅ",
215
+ "mpe-display": "๋””์Šคํ”Œ๋ ˆ์ด ์†Œ๋ถ€์žฅ",
216
+ "mpe-battery": "2์ฐจ์ „์ง€ ์†Œ๋ถ€์žฅ",
217
+ "auto-parts": "์ž๋™์ฐจ ๋ถ€ํ’ˆ",
218
+ "chemical-materials": "ํ™”ํ•™/์†Œ์žฌ",
219
+ "hotel": "ํ˜ธํ…”",
220
+ "travel-agency": "์—ฌํ–‰์‚ฌ",
221
+ "airline": "ํ•ญ๊ณต์‚ฌ",
222
+ "leisure-resort": "๋ ˆ์ €/๋ฆฌ์กฐํŠธ",
223
+ "consulting": "์ปจ์„คํŒ…",
224
+ "accounting-tax": "ํšŒ๊ณ„/์„ธ๋ฌด",
225
+ "law-firm": "๋ฒ•๋ฅ  (๋กœํŽŒ)",
226
+ "market-research": "๋ฆฌ์„œ์น˜",
227
+ "logistics-delivery": "๋ฌผ๋ฅ˜/ํƒ๋ฐฐ",
228
+ "shipping": "ํ•ด์šด",
229
+ "forwarding": "ํฌ์›Œ๋”ฉ",
230
+ "land-transport": "์œก์ƒ์šด์†ก",
231
+ "edutech": "์—๋“€ํ…Œํฌ",
232
+ "private-academy": "์ž…์‹œ/๋ณด์Šตํ•™์›",
233
+ "edu-publishing": "๊ต์œก์ถœํŒ",
234
+ "language-edu": "์™ธ๊ตญ์–ด๊ต์œก",
235
+ "ngo-npo": "NGO/NPO",
236
+ "social-enterprise": "์‚ฌํšŒ์ ๊ธฐ์—…",
237
+ "foundation": "์žฌ๋‹จ"
238
+ }
239
+
240
+ # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
241
+ result = f"""## ๐Ÿข {company_name} - {job_title} ์‚ฐ์—… ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ
242
+
243
+ ### ๐Ÿท๏ธ **๋ถ„๋ฅ˜๋œ ์‚ฐ์—… ํƒœ๊ทธ**
244
+
245
+ """
246
+ for i, tag in enumerate(tags, 1):
247
+ tag_name = tag_mapping.get(tag, tag)
248
+ result += f"**{i}.** #{tag_name} (`{tag}`)\n\n"
249
+
250
+ result += f"""
251
+ ---
252
+ **๐Ÿ“ ์ž…๋ ฅ ์ •๋ณด:**
253
+ - ํšŒ์‚ฌ: {company_name}
254
+ - ์ง๋ฌด: {job_title}
255
+ - ๋ถ„๋ฅ˜๋œ ํƒœ๊ทธ ์ˆ˜: {len(tags)}๊ฐœ
256
+
257
+ *๋ณธ ๋ถ„๋ฅ˜๋Š” AI๊ฐ€ ์ˆ˜ํ–‰ํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.*
258
+ """
259
+
260
+ return result, tags
261
+
262
+ except Exception as e:
263
+ error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
264
+
265
+ ์‚ฐ์—… ๋ถ„๋ฅ˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
266
+
267
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
268
+
269
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
270
+ """
271
+ return error_msg, []
industry-classification/main.py ADDED
@@ -0,0 +1,305 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from llm_functions import classify_industry
3
+
4
+ # ์˜ˆ์ œ ๋ฐ์ดํ„ฐ
5
+ example_companies = ["์‚ผ์„ฑ์ „์ž", "์‹ ํ•œ์€ํ–‰", "์ฟ ํŒก", "์•„๋ชจ๋ ˆํผ์‹œํ”ฝ", "ํ˜„๋Œ€๊ฑด์„ค", "ํ•˜์ด๋ธŒ", "ํ† ์Šค", "๋„ฅ์Šจ", "์‚ผ์„ฑ๋ฐ”์ด์˜ค๋กœ์ง์Šค", "HMM"]
6
+ example_jobs = ["๊ฒฝ์˜๊ธฐํš", "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…", "HRM(์ธ์‚ฌ์šด์˜)", "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "ํ•ด์™ธ์˜์—…", "๋ฐ์ดํ„ฐ ๋ถ„์„", "์˜์—…", "๋งˆ์ผ€ํŒ…", "๊ธฐํš", "๊ฐœ๋ฐœ"]
7
+
8
+ def create_tag_cards(tags):
9
+ """
10
+ ๋ถ„๋ฅ˜๋œ ํƒœ๊ทธ๋“ค์„ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œํ•˜๋Š” HTML ์ƒ์„ฑ
11
+ """
12
+ if not tags:
13
+ return "<div style='text-align: center; color: #6B7280; padding: 20px;'>๋ถ„๋ฅ˜๋ฅผ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”</div>"
14
+
15
+ # ํƒœ๊ทธ๋ช… ๋งคํ•‘
16
+ tag_mapping = {
17
+ "platform-portal": "ํ”Œ๋žซํผ/ํฌํ„ธ",
18
+ "e-commerce": "์ด์ปค๋จธ์Šค",
19
+ "game": "๊ฒŒ์ž„",
20
+ "it-solution-si": "IT์†”๋ฃจ์…˜/SI",
21
+ "o2o-vertical": "O2O/๋ฒ„ํ‹ฐ์ปฌ",
22
+ "ai-data": "AI/๋ฐ์ดํ„ฐ",
23
+ "cloud-saas": "ํด๋ผ์šฐ๋“œ/SaaS",
24
+ "fintech": "ํ•€ํ…Œํฌ",
25
+ "semiconductor": "๋ฐ˜๋„์ฒด",
26
+ "electronics-home": "๊ฐ€์ „/์ „์ž์ œํ’ˆ",
27
+ "automotive-mobility": "์ž๋™์ฐจ/๋ชจ๋นŒ๋ฆฌํ‹ฐ",
28
+ "battery": "2์ฐจ์ „์ง€",
29
+ "display": "๋””์Šคํ”Œ๋ ˆ์ด",
30
+ "heavy-industry-shipbuilding": "์ค‘๊ณต์—…/์กฐ์„ ",
31
+ "steel-metal": "์ฒ ๊ฐ•/๊ธˆ์†",
32
+ "bank": "์€ํ–‰",
33
+ "securities": "์ฆ๊ถŒ",
34
+ "insurance": "๋ณดํ—˜",
35
+ "card": "์นด๋“œ",
36
+ "asset-management": "์ž์‚ฐ์šด์šฉ",
37
+ "dept-store-mart": "๋ฐฑํ™”์ /๋งˆํŠธ",
38
+ "convenience-store": "ํŽธ์˜์ ",
39
+ "fmcg-beverage": "FMCG/์‹์Œ๋ฃŒ",
40
+ "fashion-beauty": "ํŒจ์…˜/๋ทฐํ‹ฐ",
41
+ "duty-free": "๋ฉด์„ธ์ ",
42
+ "pharma-new-drug": "์ œ์•ฝ/์‹ ์•ฝ๊ฐœ๋ฐœ",
43
+ "bio-cmo": "๋ฐ”์ด์˜ค/CMO",
44
+ "medical-device": "์˜๋ฃŒ๊ธฐ๊ธฐ",
45
+ "digital-healthcare": "๋””์ง€ํ„ธํ—ฌ์Šค์ผ€์–ด",
46
+ "entertainment": "์—”ํ„ฐํ…Œ์ธ๋จผํŠธ",
47
+ "contents-video": "์ฝ˜ํ…์ธ /์˜์ƒ์ œ์ž‘",
48
+ "ad-agency": "๊ด‘๊ณ ๋Œ€ํ–‰์‚ฌ",
49
+ "webtoon-webnovel": "์›นํˆฐ/์›น์†Œ์„ค",
50
+ "broadcasting-press": "๋ฐฉ์†ก/์–ธ๋ก ",
51
+ "construction-engineering": "๊ฑด์„ค/์—”์ง€๋‹ˆ์–ด๋ง",
52
+ "realestate-development": "๋ถ€๋™์‚ฐ๊ฐœ๋ฐœ",
53
+ "plant": "ํ”Œ๋žœํŠธ",
54
+ "interior": "์ธํ…Œ๋ฆฌ์–ด",
55
+ "public-soc": "SOC (๊ณตํ•ญ,๋„๋กœ,์ฒ ๋„)",
56
+ "public-energy": "์—๋„ˆ์ง€ ๊ณต๊ธฐ์—…",
57
+ "public-finance": "๊ธˆ์œต ๊ณต๊ธฐ์—…",
58
+ "public-admin": "์ผ๋ฐ˜ํ–‰์ •",
59
+ "mpe-semiconductor": "๋ฐ˜๋„์ฒด ์†Œ๋ถ€์žฅ",
60
+ "mpe-display": "๋””์Šคํ”Œ๋ ˆ์ด ์†Œ๋ถ€์žฅ",
61
+ "mpe-battery": "2์ฐจ์ „์ง€ ์†Œ๋ถ€์žฅ",
62
+ "auto-parts": "์ž๋™์ฐจ ๋ถ€ํ’ˆ",
63
+ "chemical-materials": "ํ™”ํ•™/์†Œ์žฌ",
64
+ "hotel": "ํ˜ธํ…”",
65
+ "travel-agency": "์—ฌํ–‰์‚ฌ",
66
+ "airline": "ํ•ญ๊ณต์‚ฌ",
67
+ "leisure-resort": "๋ ˆ์ €/๋ฆฌ์กฐํŠธ",
68
+ "consulting": "์ปจ์„คํŒ…",
69
+ "accounting-tax": "ํšŒ๊ณ„/์„ธ๋ฌด",
70
+ "law-firm": "๋ฒ•๋ฅ  (๋กœํŽŒ)",
71
+ "market-research": "๋ฆฌ์„œ์น˜",
72
+ "logistics-delivery": "๋ฌผ๋ฅ˜/ํƒ๋ฐฐ",
73
+ "shipping": "ํ•ด์šด",
74
+ "forwarding": "ํฌ์›Œ๋”ฉ",
75
+ "land-transport": "์œก์ƒ์šด์†ก",
76
+ "edutech": "์—๋“€ํ…Œํฌ",
77
+ "private-academy": "์ž…์‹œ/๋ณด์Šตํ•™์›",
78
+ "edu-publishing": "๊ต์œก์ถœํŒ",
79
+ "language-edu": "์™ธ๊ตญ์–ด๊ต์œก",
80
+ "ngo-npo": "NGO/NPO",
81
+ "social-enterprise": "์‚ฌํšŒ์ ๊ธฐ์—…",
82
+ "foundation": "์žฌ๋‹จ"
83
+ }
84
+
85
+ cards_html = "<div style='display: flex; flex-wrap: wrap; gap: 10px; justify-content: center;'>"
86
+
87
+ colors = ["#EBF8FF", "#ECFDF5", "#FEF2F2", "#F5F3FF", "#FFF7ED", "#F0FDFA", "#FDF2F8"]
88
+ border_colors = ["#1E40AF", "#059669", "#DC2626", "#7C3AED", "#EA580C", "#0F766E", "#BE185D"]
89
+
90
+ for i, tag in enumerate(tags):
91
+ color = colors[i % len(colors)]
92
+ border_color = border_colors[i % len(border_colors)]
93
+ tag_name = tag_mapping.get(tag, tag)
94
+
95
+ cards_html += f"""
96
+ <div style="
97
+ background-color: {color};
98
+ border: 2px solid {border_color};
99
+ border-radius: 12px;
100
+ padding: 15px;
101
+ margin: 5px;
102
+ min-width: 150px;
103
+ text-align: center;
104
+ box-shadow: 0 2px 8px rgba(0,0,0,0.1);
105
+ ">
106
+ <div style="
107
+ font-weight: bold;
108
+ color: {border_color};
109
+ font-size: 14px;
110
+ margin-bottom: 5px;
111
+ ">#{tag_name}</div>
112
+ <div style="
113
+ color: #6B7280;
114
+ font-size: 12px;
115
+ font-family: monospace;
116
+ ">{tag}</div>
117
+ </div>
118
+ """
119
+
120
+ cards_html += "</div>"
121
+ return cards_html
122
+
123
+ def process_classification(job_title, company_name):
124
+ """
125
+ ๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI์— ํ‘œ์‹œํ•  ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
126
+ """
127
+ try:
128
+ content, tags = classify_industry(job_title, company_name)
129
+ tag_cards = create_tag_cards(tags)
130
+ return content, tag_cards
131
+ except Exception as e:
132
+ error_content = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
133
+
134
+ ์‚ฐ์—… ๋ถ„๋ฅ˜ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
135
+
136
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
137
+
138
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
139
+ """
140
+ error_cards = create_tag_cards([])
141
+ return error_content, error_cards
142
+
143
+ def create_interface():
144
+ """
145
+ Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
146
+ """
147
+ with gr.Blocks(
148
+ title="๐Ÿท๏ธ AI ์‚ฐ์—… ๋ถ„๋ฅ˜๊ธฐ",
149
+ theme=gr.themes.Soft(),
150
+ css="""
151
+ .main-header {
152
+ text-align: center;
153
+ padding: 20px;
154
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
155
+ color: white;
156
+ border-radius: 10px;
157
+ margin-bottom: 20px;
158
+ }
159
+ .input-section {
160
+ background-color: #f8f9fa;
161
+ padding: 20px;
162
+ border-radius: 8px;
163
+ margin: 10px 0;
164
+ }
165
+ .example-section {
166
+ background-color: #f0f9ff;
167
+ padding: 15px;
168
+ border-radius: 8px;
169
+ margin: 10px 0;
170
+ }
171
+ """
172
+ ) as demo:
173
+
174
+ # ํ—ค๋”
175
+ gr.HTML("""
176
+ <div class="main-header">
177
+ <h1>๐Ÿท๏ธ AI ์‚ฐ์—… ๋ถ„๋ฅ˜๊ธฐ</h1>
178
+ <p>์ง๋ฌด์™€ ํšŒ์‚ฌ๋ช…์„ ๋ฐ”ํƒ•์œผ๋กœ ์ •ํ™•ํ•œ ์‚ฐ์—… ๋ถ„์•ผ๋ฅผ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค</p>
179
+ </div>
180
+ """)
181
+
182
+ # ์„ค๋ช…
183
+ gr.Markdown("""
184
+ ### ๐Ÿš€ **์‚ฌ์šฉ ๋ฐฉ๋ฒ•**
185
+ 1. **์ง๋ฌด**: ๋ถ„๋ฅ˜ํ•˜๊ณ  ์‹ถ์€ ์ง๋ฌด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”
186
+ 2. **ํšŒ์‚ฌ๋ช…**: ํ•ด๋‹น ํšŒ์‚ฌ๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”
187
+ 3. **๋ถ„๋ฅ˜**: '์‚ฐ์—… ๋ถ„๋ฅ˜' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”
188
+
189
+ โœจ **ํŠน์ง•**: ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ์ง๋ฌด ํŠน์„ฑ์„ ์ข…ํ•ฉ์ ์œผ๋กœ ๊ณ ๋ คํ•˜์—ฌ ์ •ํ™•ํ•œ ์‚ฐ์—… ํƒœ๊ทธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
190
+ """)
191
+
192
+ with gr.Row():
193
+ with gr.Column(scale=2):
194
+ # ์ž…๋ ฅ ์„น์…˜
195
+ gr.HTML('<div class="input-section">')
196
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
197
+
198
+ with gr.Row():
199
+ job_input = gr.Textbox(
200
+ label="๐Ÿ’ผ ์ง๋ฌด",
201
+ placeholder="์˜ˆ: ๊ฒฝ์˜๊ธฐํš, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ… ๋“ฑ",
202
+ value="",
203
+ scale=1
204
+ )
205
+
206
+ company_input = gr.Textbox(
207
+ label="๐Ÿข ํšŒ์‚ฌ๋ช…",
208
+ placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ",
209
+ value="",
210
+ scale=1
211
+ )
212
+
213
+ classify_btn = gr.Button(
214
+ "๐Ÿท๏ธ ์‚ฐ์—… ๋ถ„๋ฅ˜",
215
+ variant="primary",
216
+ size="lg"
217
+ )
218
+ gr.HTML('</div>')
219
+
220
+ with gr.Column(scale=1):
221
+ # ์˜ˆ์ œ ๋ฐ ๊ฐ€์ด๋“œ
222
+ gr.HTML('<div class="example-section">')
223
+ gr.Markdown("### ๐Ÿ’ก **์˜ˆ์ œ ํšŒ์‚ฌ**")
224
+
225
+ company_rows = [example_companies[i:i+2] for i in range(0, len(example_companies), 2)]
226
+ for row in company_rows:
227
+ with gr.Row():
228
+ for company in row:
229
+ example_btn = gr.Button(
230
+ company,
231
+ size="sm",
232
+ variant="secondary",
233
+ scale=1
234
+ )
235
+ example_btn.click(
236
+ fn=lambda x=company: x,
237
+ outputs=company_input
238
+ )
239
+
240
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ์ง๋ฌด**")
241
+
242
+ job_rows = [example_jobs[i:i+2] for i in range(0, len(example_jobs), 2)]
243
+ for row in job_rows:
244
+ with gr.Row():
245
+ for job in row:
246
+ job_btn = gr.Button(
247
+ job,
248
+ size="sm",
249
+ variant="secondary",
250
+ scale=1
251
+ )
252
+ job_btn.click(
253
+ fn=lambda x=job: x,
254
+ outputs=job_input
255
+ )
256
+ gr.HTML('</div>')
257
+
258
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
259
+ with gr.Row():
260
+ with gr.Column(scale=2):
261
+ gr.Markdown("### ๐Ÿ“‹ **๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ**")
262
+ result_output = gr.Markdown(
263
+ value="์ง๋ฌด์™€ ํšŒ์‚ฌ๋ช…์„ ์ž…๋ ฅํ•˜๊ณ  '์‚ฐ์—… ๋ถ„๋ฅ˜' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
264
+ elem_classes=["result-output"]
265
+ )
266
+
267
+ with gr.Column(scale=1):
268
+ gr.Markdown("### ๐Ÿท๏ธ **์‚ฐ์—… ํƒœ๊ทธ**")
269
+ tag_cards = gr.HTML(
270
+ value="<div style='text-align: center; color: #6B7280; padding: 20px;'>๋ถ„๋ฅ˜๋ฅผ ์‹คํ–‰ํ•ด์ฃผ์„ธ์š”</div>",
271
+ elem_classes=["tag-cards"]
272
+ )
273
+
274
+ # ๋ถ„๋ฅ˜ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
275
+ classify_btn.click(
276
+ fn=process_classification,
277
+ inputs=[job_input, company_input],
278
+ outputs=[result_output, tag_cards],
279
+ api_name="classify_industry"
280
+ )
281
+
282
+ # ํ‘ธํ„ฐ
283
+ gr.Markdown("""
284
+ ---
285
+ **๐Ÿ’ก ๋ถ„๋ฅ˜ ๊ธฐ์ค€**:
286
+ - **ITยทํ”Œ๋žซํผยท๊ฒŒ์ž„**: ํ”Œ๋žซํผ, ์ด์ปค๋จธ์Šค, ๊ฒŒ์ž„, IT์†”๋ฃจ์…˜, ํ•€ํ…Œํฌ ๋“ฑ
287
+ - **์ œ์กฐยทํ•˜๋“œ์›จ์–ด**: ๋ฐ˜๋„์ฒด, ๊ฐ€์ „, ์ž๋™์ฐจ, 2์ฐจ์ „์ง€, ๋””์Šคํ”Œ๋ ˆ์ด ๋“ฑ
288
+ - **๊ธˆ์œต**: ์€ํ–‰, ์ฆ๊ถŒ, ๋ณดํ—˜, ์นด๋“œ, ์ž์‚ฐ์šด์šฉ ๋“ฑ
289
+ - **์œ ํ†ตยท์†Œ๋น„์žฌ**: ๋ฐฑํ™”์ , ํŽธ์˜์ , FMCG, ํŒจ์…˜๋ทฐํ‹ฐ ๋“ฑ
290
+ - **๊ธฐํƒ€**: ๋ฐ”์ด์˜ค์ œ์•ฝ, ๋ฏธ๋””์–ด์ฝ˜ํ…์ธ , ๊ฑด์„ค๋ถ€๋™์‚ฐ, ๊ณต๊ธฐ์—… ๋“ฑ
291
+
292
+ ๐Ÿค– **Powered by**: OpenAI GPT-4o-mini with Web Search
293
+ """)
294
+
295
+ return demo
296
+
297
+ if __name__ == "__main__":
298
+ # Gradio ์•ฑ ์‹คํ–‰
299
+ demo = create_interface()
300
+ demo.launch(
301
+ server_name="0.0.0.0",
302
+ # server_port=7862,
303
+ share=True,
304
+ show_error=True
305
+ )
industry-classification/prompt.yaml ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt: |
2
+ ๋‹น์‹ ์€ ๊ธฐ์—…์˜ ์‚ฐ์—… ๋ถ„๋ฅ˜๋ฅผ ์ „๋ฌธ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” AI ์ปจ์„คํ„ดํŠธ์ž…๋‹ˆ๋‹ค.
3
+ ์ฃผ์–ด์ง„ ์ง๋ฌด์™€ ํšŒ์‚ฌ๋ช…์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•ด๋‹น ๊ธฐ์—…์ด ์†ํ•˜๋Š” ์‚ฐ์—… ๋ถ„์•ผ๋ฅผ ์ •ํ™•ํ•˜๊ฒŒ ๋ถ„๋ฅ˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
4
+
5
+ ### ์ง€์นจ
6
+ 1. **์›น ๊ฒ€์ƒ‰ ํ™œ์šฉ**: ํšŒ์‚ฌ๋ช…์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ตœ์‹  ์‚ฌ์—… ์˜์—ญ๊ณผ ์ฃผ๋ ฅ ๋ถ„์•ผ๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
7
+ 2. **์ •ํ™•ํ•œ ๋ถ„๋ฅ˜**: ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์™€ ์ง๋ฌด๋ฅผ ์ข…ํ•ฉ์ ์œผ๋กœ ๊ณ ๋ คํ•˜์—ฌ ๊ฐ€์žฅ ์ ํ•ฉํ•œ ์‚ฐ์—… ํƒœ๊ทธ๋ฅผ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค.
8
+ 3. **๋‹ค์ค‘ ๋ถ„๋ฅ˜ ๊ฐ€๋Šฅ**: ํ•˜๋‚˜์˜ ๊ธฐ์—…์ด ์—ฌ๋Ÿฌ ์‚ฐ์—… ๋ถ„์•ผ์— ๊ฑธ์ณ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ๊ด€๋ จ๋œ ๋ชจ๋“  ํƒœ๊ทธ๋ฅผ ํฌํ•จํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
9
+ 4. **ํƒœ๊ทธ ID ์‚ฌ์šฉ**: ๋ฐ˜๋“œ์‹œ ์ œ๊ณต๋œ ํƒœ๊ทธ ๋ชฉ๋ก์—์„œ ์ •ํ™•ํ•œ tagId๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
10
+
11
+ ### ์ž…๋ ฅ ์ •๋ณด
12
+ - **์ง๋ฌด:** {job_title}
13
+ - **ํšŒ์‚ฌ๋ช…:** {company_name}
14
+
15
+ ### ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์‚ฐ์—… ํƒœ๊ทธ ๋ชฉ๋ก
16
+
17
+ **IT ยท ํ”Œ๋žซํผ ยท ๊ฒŒ์ž„**
18
+ - platform-portal: #ํ”Œ๋žซํผ/ํฌํ„ธ
19
+ - e-commerce: #์ด์ปค๋จธ์Šค
20
+ - game: #๊ฒŒ์ž„
21
+ - it-solution-si: #IT์†”๋ฃจ์…˜/SI
22
+ - o2o-vertical: #O2O/๋ฒ„ํ‹ฐ์ปฌ
23
+ - ai-data: #AI/๋ฐ์ดํ„ฐ
24
+ - cloud-saas: #ํด๋ผ์šฐ๋“œ/SaaS
25
+ - fintech: #ํ•€ํ…Œํฌ
26
+
27
+ **์ œ์กฐ ยท ํ•˜๋“œ์›จ์–ด**
28
+ - semiconductor: #๋ฐ˜๋„์ฒด
29
+ - electronics-home: #๊ฐ€์ „/์ „์ž์ œํ’ˆ
30
+ - automotive-mobility: #์ž๋™์ฐจ/๋ชจ๋นŒ๋ฆฌํ‹ฐ
31
+ - battery: #2์ฐจ์ „์ง€
32
+ - display: #๋””์Šคํ”Œ๋ ˆ์ด
33
+ - heavy-industry-shipbuilding: #์ค‘๊ณต์—…/์กฐ์„ 
34
+ - steel-metal: #์ฒ ๊ฐ•/๊ธˆ์†
35
+
36
+ **๊ธˆ์œต**
37
+ - bank: #์€ํ–‰
38
+ - securities: #์ฆ๊ถŒ
39
+ - insurance: #๋ณดํ—˜
40
+ - card: #์นด๋“œ
41
+ - asset-management: #์ž์‚ฐ์šด์šฉ
42
+
43
+ **์œ ํ†ต ยท ์†Œ๋น„์žฌ ยท ์ƒํ™œ**
44
+ - dept-store-mart: #๋ฐฑํ™”์ /๋งˆํŠธ
45
+ - convenience-store: #ํŽธ์˜์ 
46
+ - fmcg-beverage: #FMCG/์‹์Œ๋ฃŒ
47
+ - fashion-beauty: #ํŒจ์…˜/๋ทฐํ‹ฐ
48
+ - duty-free: #๋ฉด์„ธ์ 
49
+
50
+ **๋ฐ”์ด์˜ค ยท ์ œ์•ฝ ยท ์˜๋ฃŒ**
51
+ - pharma-new-drug: #์ œ์•ฝ/์‹ ์•ฝ๊ฐœ๋ฐœ
52
+ - bio-cmo: #๋ฐ”์ด์˜ค/CMO
53
+ - medical-device: #์˜๋ฃŒ๊ธฐ๊ธฐ
54
+ - digital-healthcare: #๋””์ง€ํ„ธํ—ฌ์Šค์ผ€์–ด
55
+
56
+ **๋ฏธ๋””์–ด ยท ์ฝ˜ํ…์ธ  ยท ๊ด‘๊ณ **
57
+ - entertainment: #์—”ํ„ฐํ…Œ์ธ๋จผํŠธ
58
+ - contents-video: #์ฝ˜ํ…์ธ /์˜์ƒ์ œ์ž‘
59
+ - ad-agency: #๊ด‘๊ณ ๋Œ€ํ–‰์‚ฌ
60
+ - webtoon-webnovel: #์›นํˆฐ/์›น์†Œ์„ค
61
+ - broadcasting-press: #๋ฐฉ์†ก/์–ธ๋ก 
62
+
63
+ **๊ฑด์„ค ยท ๋ถ€๋™์‚ฐ**
64
+ - construction-engineering: #๊ฑด์„ค/์—”์ง€๋‹ˆ์–ด๋ง
65
+ - realestate-development: #๋ถ€๋™์‚ฐ๊ฐœ๋ฐœ
66
+ - plant: #ํ”Œ๋žœํŠธ
67
+ - interior: #์ธํ…Œ๋ฆฌ์–ด
68
+
69
+ **๊ณต๊ธฐ์—… ยท ๊ณต๊ณต๊ธฐ๊ด€**
70
+ - public-soc: #SOC (๊ณตํ•ญ,๋„๋กœ,์ฒ ๋„)
71
+ - public-energy: #์—๋„ˆ์ง€ ๊ณต๊ธฐ์—…
72
+ - public-finance: #๊ธˆ์œต ๊ณต๊ธฐ์—…
73
+ - public-admin: #์ผ๋ฐ˜ํ–‰์ •
74
+
75
+ **์†Œ์žฌ ยท ๋ถ€ํ’ˆ ยท ์žฅ๋น„ (์†Œ๋ถ€์žฅ)**
76
+ - mpe-semiconductor: #๋ฐ˜๋„์ฒด ์†Œ๋ถ€์žฅ
77
+ - mpe-display: #๋””์Šคํ”Œ๋ ˆ์ด ์†Œ๋ถ€์žฅ
78
+ - mpe-battery: #2์ฐจ์ „์ง€ ์†Œ๋ถ€์žฅ
79
+ - auto-parts: #์ž๋™์ฐจ ๋ถ€ํ’ˆ
80
+ - chemical-materials: #ํ™”ํ•™/์†Œ์žฌ
81
+
82
+ **ํ˜ธํ…” ยท ์—ฌํ–‰ ยท ํ•ญ๊ณต**
83
+ - hotel: #ํ˜ธํ…”
84
+ - travel-agency: #์—ฌํ–‰์‚ฌ
85
+ - airline: #ํ•ญ๊ณต์‚ฌ
86
+ - leisure-resort: #๋ ˆ์ €/๋ฆฌ์กฐํŠธ
87
+
88
+ **์ „๋ฌธ ์„œ๋น„์Šค**
89
+ - consulting: #์ปจ์„คํŒ…
90
+ - accounting-tax: #ํšŒ๊ณ„/์„ธ๋ฌด
91
+ - law-firm: #๋ฒ•๋ฅ  (๋กœํŽŒ)
92
+ - market-research: #๋ฆฌ์„œ์น˜
93
+
94
+ **์šด์†ก ยท ๋ฌผ๋ฅ˜**
95
+ - logistics-delivery: #๋ฌผ๋ฅ˜/ํƒ๋ฐฐ
96
+ - shipping: #ํ•ด์šด
97
+ - forwarding: #ํฌ์›Œ๋”ฉ
98
+ - land-transport: #์œก์ƒ์šด์†ก
99
+
100
+ **๊ต์œก**
101
+ - edutech: #์—๋“€ํ…Œํฌ
102
+ - private-academy: #์ž…์‹œ/๋ณด์Šตํ•™์›
103
+ - edu-publishing: #๊ต์œก์ถœํŒ
104
+ - language-edu: #์™ธ๊ตญ์–ด๊ต์œก
105
+
106
+ **๋น„์˜๋ฆฌ ยท ์‚ฌํšŒ์ ๊ธฐ์—…**
107
+ - ngo-npo: #NGO/NPO
108
+ - social-enterprise: #์‚ฌํšŒ์ ๊ธฐ์—…
109
+ - foundation: #์žฌ๋‹จ
110
+
111
+ ### ์ถœ๋ ฅ ํ˜•์‹
112
+ ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ JSON ๋ฐฐ์—ด ํ˜•์‹์œผ๋กœ๋งŒ ์‘๋‹ตํ•˜์„ธ์š”:
113
+
114
+ ```json
115
+ ["tagId1", "tagId2", ...]
116
+ ```
117
+
118
+ ### ์˜ˆ์‹œ
119
+ [์ž…๋ ฅ]
120
+ - ์ง๋ฌด: ๊ฒฝ์˜๊ธฐํš
121
+ - ํšŒ์‚ฌ๋ช…: ์‚ผ์„ฑ์ „์ž
122
+
123
+ [์ถœ๋ ฅ]
124
+ ```json
125
+ ["semiconductor", "electronics-home"]
126
+ ```
127
+
128
+ ์ด์ œ ๋‹ค์Œ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์‚ฐ์—… ๋ถ„๋ฅ˜๋ฅผ ์ˆ˜ํ–‰ํ•ด์ฃผ์„ธ์š”.
jasoseo-context-report/.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ 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
31
+ -----END CERTIFICATE-----
jasoseo-context-report/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (10.3 kB). View file
 
jasoseo-context-report/eval.json ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "context_report_generation_eval": {
3
+ "task_definition": {
4
+ "description": "Generate a structured context report based on the user's input (job, company, etc.). The output must be a JSON object following the specified schema, providing essential background information for writing a '์ž์†Œ์„œ'. The content should be synthesized from public information as if processed by a web search and LLM pipeline.",
5
+ "input": {
6
+ "job_title": "string",
7
+ "company_name": "string",
8
+ "experience_level": "์‹ ์ž… | ๊ฒฝ๋ ฅ | ์ธํ„ด | ๊ธฐํƒ€"
9
+ },
10
+ "output_schema": {
11
+ "company_profile": {
12
+ "name": "string",
13
+ "vision_mission": "string",
14
+ "core_values": ["string", "..."],
15
+ "talent_philosophy": "string",
16
+ "recent_news_summary": "string",
17
+ "main_products_services": ["string", "..."]
18
+ },
19
+ "position_analysis": {
20
+ "role_summary": "string",
21
+ "required_skills": {
22
+ "hard": ["string", "..."],
23
+ "soft": ["string", "..."]
24
+ },
25
+ "keywords": ["string", "..."]
26
+ },
27
+ "industry_context": {
28
+ "trends": ["string", "..."],
29
+ "competitors": ["string", "..."]
30
+ }
31
+ }
32
+ },
33
+ "examples": [
34
+ {
35
+ "input": {
36
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
37
+ "company_name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
38
+ "experience_level": "์‹ ์ž…"
39
+ },
40
+ "output": {
41
+ "company_profile": {
42
+ "name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
43
+ "vision_mission": "๊ธˆ์œต์˜ ๋ชจ๋“  ์ˆœ๊ฐ„์„ ์‰ฝ๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ๊ธฐ์ˆ  ๊ธฐ๋ฐ˜ ๊ธˆ์œต ํ˜์‹  ๊ธฐ์—…์ž…๋‹ˆ๋‹ค.",
44
+ "core_values": ["Customer Obsession (๊ณ ๊ฐ ์ค‘์‹ฌ)", "Excellence (ํƒ์›”ํ•จ)", "Radical Transparency (๊ทน๋„์˜ ํˆฌ๋ช…์„ฑ)", "Freedom and Responsibility (์ž์œจ๊ณผ ์ฑ…์ž„)"],
45
+ "talent_philosophy": "์Šค์Šค๋กœ ๋ฌธ์ œ๋ฅผ ์ •์˜ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋ฉฐ, ๋™๋ฃŒ์™€ ํˆฌ๋ช…ํ•˜๊ฒŒ ์†Œํ†ตํ•˜์—ฌ ์ตœ๊ณ ์˜ ์„ฑ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ์ธ์žฌ๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.",
46
+ "recent_news_summary": "ํ† ์Šค๋ฑ…ํฌ, ํ† ์Šค์ฆ๊ถŒ ๋“ฑ ์ข…ํ•ฉ ๊ธˆ์œต ํ”Œ๋žซํผ์œผ๋กœ์˜ ํ™•์žฅ์„ ๋„˜์–ด, ์•Œ๋œฐํฐ(MVNO) ์‚ฌ์—… ์ง„์ถœ ๋“ฑ ๋น„๊ธˆ์œต ๋ถ„์•ผ๋กœ ์„œ๋น„์Šค๋ฅผ ๋‹ค๊ฐํ™”ํ•˜๋ฉฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ˜์‹ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",
47
+ "main_products_services": ["ํ† ์Šค (๊ฐ„ํŽธ์†ก๊ธˆ, ๊ฒฐ์ œ)", "ํ† ์Šค๋ฑ…ํฌ", "ํ† ์Šค์ฆ๊ถŒ", "ํ† ์Šคํ”Œ๋ ˆ์ด์Šค"]
48
+ },
49
+ "position_analysis": {
50
+ "role_summary": "๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์•ˆ์ •์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ธˆ์œต ์„œ๋น„์Šค์˜ ๋ฐฑ์—”๋“œ API๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ธˆ์œต ๋ฌธ์ œ๋ฅผ ๊ธฐ์ˆ ๋กœ ํ•ด๊ฒฐํ•˜๋ฉฐ ์ตœ๊ณ ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ•ฉ๋‹ˆ๋‹ค.",
51
+ "required_skills": {
52
+ "hard": ["Java/Kotlin", "Spring Framework", "RDBMS/NoSQL", "MSA (Microservice Architecture) ์ดํ•ด"],
53
+ "soft": ["๋ฌธ์ œ ํ•ด๊ฒฐ ๋Šฅ๋ ฅ", "ํ˜‘์—… ๋ฐ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜", "ํ•™์Šต ๋ฐ ์„ฑ์žฅ ์˜์ง€", "๋†’์€ ์ˆ˜์ค€์˜ ์ฑ…์ž„๊ฐ"]
54
+ },
55
+ "keywords": ["ํ•€ํ…Œํฌ", "๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ", "MSA", "๊ฒฐ์ œ ์‹œ์Šคํ…œ", "API", "๊ธˆ์œต ํ”Œ๋žซํผ"]
56
+ },
57
+ "industry_context": {
58
+ "trends": ["๊ฐ„ํŽธ๊ฒฐ์ œ ์‹œ์žฅ์˜ ์ง€์†์  ์„ฑ์žฅ", "๋งˆ์ด๋ฐ์ดํ„ฐ ์‚ฌ์—… ํ™œ์„ฑํ™”", "๋น…ํ…Œํฌ ๊ธฐ์—…์˜ ๊ธˆ์œต์—… ์ง„์ถœ ๊ฐ€์†ํ™”"],
59
+ "competitors": ["์นด์นด์˜คํŽ˜์ด", "๋„ค์ด๋ฒ„ํŒŒ์ด๋‚ธ์…œ"]
60
+ }
61
+ }
62
+ },
63
+ {
64
+ "input": {
65
+ "job_title": "์‹ํ’ˆ๋งˆ์ผ€ํŒ…",
66
+ "company_name": "CJ์ œ์ผ์ œ๋‹น",
67
+ "experience_level": "์‹ ์ž…"
68
+ },
69
+ "output": {
70
+ "company_profile": {
71
+ "name": "CJ์ œ์ผ์ œ๋‹น",
72
+ "vision_mission": "๊ธ€๋กœ๋ฒŒ No.1 ์‹ํ’ˆ & ๋ฐ”์ด์˜ค ๊ธฐ์—…์œผ๋กœ ์„ฑ์žฅํ•˜์—ฌ ์ „ ์„ธ๊ณ„์— ํ•œ๊ตญ์˜ ์‹๋ฌธํ™”๋ฅผ ์ „ํŒŒํ•˜๋Š” ๊ฒƒ์„ ๋น„์ „์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค.",
73
+ "core_values": ["์ธ์žฌ", "์ƒ์ƒ", "์ •์ง", "์ฐฝ์˜"],
74
+ "talent_philosophy": "๋Š์ž„์—†์ด ๋„์ „ํ•˜๊ณ  ์„ฑ์žฅํ•˜๋ฉฐ, ๋งก์€ ๋ถ„์•ผ์—์„œ ์ตœ๊ณ ๋ฅผ ์ถ”๊ตฌํ•˜๋Š” 'ํ•˜๊ณ ์žก์ด' ์ธ์žฌ๋ฅผ ์„ ํ˜ธํ•ฉ๋‹ˆ๋‹ค.",
75
+ "recent_news_summary": "'๋น„๋น„๊ณ ' ๋ธŒ๋žœ๋“œ๋ฅผ ์ค‘์‹ฌ์œผ๋กœ K-ํ‘ธ๋“œ์˜ ๊ธ€๋กœ๋ฒŒ ์˜ํ†  ํ™•์žฅ์„ ๊ฐ€์†ํ™”ํ•˜๊ณ  ์žˆ์œผ๋ฉฐ, ๋ถ๋ฏธ ๋ฐ ์œ ๋Ÿฝ ์‹œ์žฅ ๊ณต๋žต์„ ๊ฐ•ํ™”ํ•˜๊ณ  ์‹๋ฌผ์„ฑ ์‹ํ’ˆ(Plant-based) ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ํ™•๋Œ€ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",
76
+ "main_products_services": ["๋น„๋น„๊ณ  (๋งŒ๋‘, ๊น€์น˜ ๋“ฑ)", "ํ–‡๋ฐ˜", "์ŠคํŒธ", "๊ฐ์ข… ์กฐ๋ฏธ๋ฃŒ ๋ฐ ์†Œ์Šค๋ฅ˜"]
77
+ },
78
+ "position_analysis": {
79
+ "role_summary": "๋‹ด๋‹น ๋ธŒ๋žœ๋“œ์˜ ๋งˆ์ผ€ํŒ… ์ „๋žต์„ ์ˆ˜๋ฆฝํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‹œ์žฅ ๋ฐ ์†Œ๋น„์ž ๋ฐ์ดํ„ฐ ๋ถ„์„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์‹ ์ œํ’ˆ ๋Ÿฐ์นญ, ๊ด‘๊ณ  ์บ ํŽ˜์ธ, ํ”„๋กœ๋ชจ์…˜ ๋“ฑ์„ ๊ธฐํšํ•˜๊ณ  ์šด์˜ํ•˜์—ฌ ๋ธŒ๋žœ๋“œ ์„ฑ์žฅ์„ ์ฃผ๋„ํ•ฉ๋‹ˆ๋‹ค.",
80
+ "required_skills": {
81
+ "hard": ["๋ฐ์ดํ„ฐ ๋ถ„์„ ๋Šฅ๋ ฅ (GA, SQL ๋“ฑ)", "์‹œ์žฅ ์กฐ์‚ฌ ๋ฐ ๋ถ„์„", "๋งˆ์ผ€ํŒ… ์ „๋žต ๏ฟฝ๏ฟฝ๏ฟฝ๋ฆฝ"],
82
+ "soft": ["์ฐฝ์˜์  ์‚ฌ๊ณ ", "ํŠธ๋ Œ๋“œ ๋ฏผ๊ฐ์„ฑ", "์›ํ™œํ•œ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜", "๊ฒฐ๊ณผ ์ง€ํ–ฅ์  ํƒœ๋„"]
83
+ },
84
+ "keywords": ["FMCG", "๋ธŒ๋žœ๋“œ ๋งˆ์ผ€ํŒ…", "K-ํ‘ธ๋“œ", "์‹ ์ œํ’ˆ ๋Ÿฐ์นญ", "์†Œ๋น„์ž ์กฐ์‚ฌ", "๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜"]
85
+ },
86
+ "industry_context": {
87
+ "trends": ["HMR(๊ฐ€์ •๊ฐ„ํŽธ์‹) ์‹œ์žฅ์˜ ๊ธ‰์„ฑ์žฅ", "๊ฑด๊ฐ•๊ณผ ์ง€์†๊ฐ€๋Šฅ์„ฑ์„ ์ค‘์‹œํ•˜๋Š” 'ํ—ฌ์‹œํ”Œ๋ ˆ์ €' ํŠธ๋ Œ๋“œ ํ™•์‚ฐ", "์˜จ๋ผ์ธ ์ฑ„๋„ ์ค‘์‹ฌ์˜ ์œ ํ†ต ๊ตฌ์กฐ ๋ณ€ํ™”"],
88
+ "competitors": ["๋Œ€์ƒ ์ฒญ์ •์›", "์˜ค๋šœ๊ธฐ", "๋†์‹ฌ"]
89
+ }
90
+ }
91
+ },
92
+ {
93
+ "input": {
94
+ "job_title": "A&R",
95
+ "company_name": "ํ•˜์ด๋ธŒ",
96
+ "experience_level": "์ธํ„ด"
97
+ },
98
+ "output": {
99
+ "company_profile": {
100
+ "name": "ํ•˜์ด๋ธŒ (HYBE)",
101
+ "vision_mission": "'We believe in music'์ด๋ผ๋Š” ๋ฏธ์…˜ ์•„๋ž˜, ์Œ์•…์— ๊ธฐ๋ฐ˜ํ•œ ์„ธ๊ณ„ ์ตœ๊ณ ์˜ ์—”ํ„ฐํ…Œ์ธ๋จผํŠธ ๋ผ์ดํ”„์Šคํƒ€์ผ ํ”Œ๋žซํผ ๊ธฐ์—…์„ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.",
102
+ "core_values": ["ํŒฌ ์ค‘์‹ฌ (Fan-centric)", "ํ˜์‹  (Innovation)", "์ง„์ •์„ฑ (Authenticity)", "์ง€์†๊ฐ€๋Šฅ์„ฑ (Sustainability)"],
103
+ "talent_philosophy": "์Œ์•…๊ณผ ์ฝ˜ํ…์ธ ์— ๋Œ€ํ•œ ๊นŠ์€ ์ดํ•ด์™€ ์—ด์ •์„ ๋ฐ”ํƒ•์œผ๋กœ, ์‚ฐ์—…์˜ ๊ฒฝ๊ณ„๋ฅผ ๋„˜์–ด ์ƒˆ๋กœ์šด ๊ฐ€์น˜๋ฅผ ์ฐฝ์ถœํ•˜๋Š” ์ „๋ฌธ๊ฐ€๋ฅผ ์ถ”๊ตฌํ•ฉ๋‹ˆ๋‹ค.",
104
+ "recent_news_summary": "๋ฉ€ํ‹ฐ ๋ ˆ์ด๋ธ” ์ฒด์ œ๋ฅผ ๊ฐ•ํ™”ํ•˜๊ณ , ํŒฌ๋ค ํ”Œ๋žซํผ '์œ„๋ฒ„์Šค(Weverse)'๋ฅผ ํ†ตํ•ด ํŒฌ ๋น„์ฆˆ๋‹ˆ์Šค๋ฅผ ํ™•์žฅํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ๊ฒŒ์ž„, ์›นํˆฐ ๋“ฑ IP(์ง€์ ์žฌ์‚ฐ๊ถŒ)๋ฅผ ํ™œ์šฉํ•œ ์‚ฌ์—… ๋‹ค๊ฐํ™”์— ์ง‘์ค‘ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",
105
+ "main_products_services": ["์Œ๋ฐ˜ ๋ฐ ์Œ์› ์ œ์ž‘/์œ ํ†ต", "์•„ํ‹ฐ์ŠคํŠธ ๋งค๋‹ˆ์ง€๋จผํŠธ (BTS, ์„ธ๋ธํ‹ด ๋“ฑ)", "์œ„๋ฒ„์Šค (Weverse) ํ”Œ๋žซํผ", "๊ณต์—ฐ ๋ฐ MD ์‚ฌ์—…"]
106
+ },
107
+ "position_analysis": {
108
+ "role_summary": "์‹ ์ธ ์•„ํ‹ฐ์ŠคํŠธ ๋ฐœ๊ตด ๋ฐ ๊ธฐ์กด ์•„ํ‹ฐ์ŠคํŠธ์˜ ์•จ๋ฒ” ์ปจ์…‰ ๊ธฐํš, ๊ตญ๋‚ด์™ธ ์ž‘๊ณก๊ฐ€์™€์˜ ํ˜‘์—…์„ ํ†ตํ•œ ๊ณก ์ˆ˜๊ธ‰, ๋…น์Œ ๋“ฑ ์Œ๋ฐ˜ ์ œ์ž‘ ๊ณผ์ • ์ „๋ฐ˜์„ ์ง€์›ํ•˜๋Š” ์—…๋ฌด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค.",
109
+ "required_skills": {
110
+ "hard": ["์Œ์•… ์žฅ๋ฅด ๋ฐ ํŠธ๋ Œ๋“œ์— ๋Œ€ํ•œ ๋†’์€ ์ดํ•ด๋„", "์™ธ๊ตญ์–ด ๋Šฅ๋ ฅ (์˜์–ด/์ผ๋ณธ์–ด ๋“ฑ)", "๊ธฐ๋ณธ์ ์ธ ์ž‘๊ณก/์ž‘์‚ฌ ์ง€์‹ ์šฐ๋Œ€"],
111
+ "soft": ["์ฐฝ์˜์ ์ธ ๊ธฐํš๋ ฅ", "์›ํ™œํ•œ ๋„คํŠธ์›Œํ‚น ๋ฐ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜", "๊ผผ๊ผผํ•จ๊ณผ ์ฑ…์ž„๊ฐ", "๊ฐ•ํ•œ ์—ด์ •"]
112
+ },
113
+ "keywords": ["A&R", "K-POP", "์Œ๋ฐ˜ ๊ธฐํš", "IP ๋น„์ฆˆ๋‹ˆ์Šค", "์œ„๋ฒ„์Šค", "์ฝ˜ํ…์ธ  ์ œ์ž‘"]
114
+ },
115
+ "industry_context": {
116
+ "trends": ["๊ธ€๋กœ๋ฒŒ ํŒฌ๋ค ํ”Œ๋žซํผ์˜ ์ค‘์š”์„ฑ ์ฆ๋Œ€", "IP๋ฅผ ํ™œ์šฉํ•œ ์›์†Œ์Šค ๋ฉ€ํ‹ฐ์œ ์ฆˆ(OSMU) ์ „๋žต ๋ณดํŽธํ™”", "๋ฒ„์ถ”์–ผ ์•„์ด๋Œ ๋“ฑ ๊ธฐ์ˆ  ์œตํ•ฉํ˜• ์ฝ˜ํ…์ธ  ๋“ฑ์žฅ"],
117
+ "competitors": ["SM์—”ํ„ฐํ…Œ์ธ๋จผํŠธ", "JYP์—”ํ„ฐํ…Œ์ธ๋จผํŠธ", "YG์—”ํ„ฐํ…Œ์ธ๋จผํŠธ"]
118
+ }
119
+ }
120
+ }
121
+ ]
122
+ }
123
+ }
jasoseo-context-report/llm_functions.py ADDED
@@ -0,0 +1,282 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import yaml
5
+ from openai import OpenAI
6
+
7
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
8
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9
+
10
+ # ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
11
+ import os
12
+ current_dir = os.path.dirname(os.path.abspath(__file__))
13
+ prompt_path = os.path.join(current_dir, 'prompt.yaml')
14
+ with open(prompt_path, 'r', encoding='utf-8') as f:
15
+ prompt_data = yaml.safe_load(f)
16
+ prompt_template = prompt_data['prompt']
17
+
18
+ def parse_context_report(content):
19
+ """
20
+ AI ์‘๋‹ต์—์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ JSON์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
21
+ """
22
+ try:
23
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ๊ธธ์ด: {len(content)}")
24
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ์ฒซ 200์ž: {repr(content[:200])}")
25
+
26
+ # ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ
27
+ cleaned_content = content.strip()
28
+
29
+ # 1. JSON ์ฝ”๋“œ ๋ธ”๋ก ์ฐพ๊ธฐ (```json ... ``` ํ˜•์‹)
30
+ json_patterns = [
31
+ r'```json\s*(\{.*?\})\s*```',
32
+ r'```\s*(\{.*?\})\s*```',
33
+ r'```json\s*(.*?)\s*```',
34
+ r'```\s*(.*?)\s*```'
35
+ ]
36
+
37
+ for pattern in json_patterns:
38
+ json_match = re.search(pattern, cleaned_content, re.DOTALL)
39
+ if json_match:
40
+ json_str = json_match.group(1).strip()
41
+ print(f"JSON ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
42
+
43
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
44
+ json_str = re.sub(r'\n\s*', ' ', json_str)
45
+ json_str = re.sub(r',\s*}', '}', json_str)
46
+ json_str = re.sub(r',\s*]', ']', json_str)
47
+
48
+ try:
49
+ parsed_json = json.loads(json_str)
50
+ if isinstance(parsed_json, dict) and 'company_profile' in parsed_json:
51
+ return parsed_json
52
+ except json.JSONDecodeError as e:
53
+ print(f"JSON ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
54
+
55
+ # 2. ์ค‘๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ JSON ์ฐพ๊ธฐ
56
+ brace_patterns = [
57
+ r'\{.*?\}'
58
+ ]
59
+
60
+ for pattern in brace_patterns:
61
+ brace_match = re.search(pattern, cleaned_content, re.DOTALL)
62
+ if brace_match:
63
+ json_str = brace_match.group(0).strip()
64
+ print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
65
+
66
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
67
+ json_str = re.sub(r'\n\s*', ' ', json_str)
68
+ json_str = re.sub(r',\s*}', '}', json_str)
69
+ json_str = re.sub(r',\s*]', ']', json_str)
70
+
71
+ try:
72
+ parsed_json = json.loads(json_str)
73
+ if isinstance(parsed_json, dict) and 'company_profile' in parsed_json:
74
+ return parsed_json
75
+ except json.JSONDecodeError as e:
76
+ print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
77
+
78
+ # 3. ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JSON์œผ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
79
+ try:
80
+ # ์ฝ”๋“œ ๋ธ”๋ก ๋งˆ์ปค ์ œ๊ฑฐ
81
+ if cleaned_content.startswith('```'):
82
+ lines = cleaned_content.split('\n')
83
+ start_idx = 1 if lines[0].startswith('```') else 0
84
+ end_idx = len(lines)
85
+ for i in range(len(lines)-1, -1, -1):
86
+ if lines[i].strip() == '```':
87
+ end_idx = i
88
+ break
89
+ cleaned_content = '\n'.join(lines[start_idx:end_idx])
90
+
91
+ cleaned_content = cleaned_content.strip()
92
+ parsed_json = json.loads(cleaned_content)
93
+ if isinstance(parsed_json, dict) and 'company_profile' in parsed_json:
94
+ return parsed_json
95
+ except json.JSONDecodeError as e:
96
+ print(f"์ „์ฒด JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
97
+
98
+ # 4. ๊ธฐ๋ณธ ๊ตฌ์กฐ ๋ฐ˜ํ™˜ (ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ)
99
+ print("JSON ํŒŒ์‹ฑ ์‹คํŒจ, ๊ธฐ๋ณธ ๊ตฌ์กฐ ๋ฐ˜ํ™˜")
100
+ return {
101
+ "company_profile": {
102
+ "name": "ํŒŒ์‹ฑ ์‹คํŒจ",
103
+ "vision_mission": "์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
104
+ "core_values": ["์ •๋ณด ์—†์Œ"],
105
+ "talent_philosophy": "์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
106
+ "recent_news_summary": "์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
107
+ "main_products_services": ["์ •๋ณด ์—†์Œ"]
108
+ },
109
+ "position_analysis": {
110
+ "role_summary": "์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.",
111
+ "required_skills": {
112
+ "hard": ["์ •๋ณด ์—†์Œ"],
113
+ "soft": ["์ •๋ณด ์—†์Œ"]
114
+ },
115
+ "keywords": ["์ •๋ณด ์—†์Œ"]
116
+ },
117
+ "industry_context": {
118
+ "trends": ["์ •๋ณด ์—†์Œ"],
119
+ "competitors": ["์ •๋ณด ์—†์Œ"]
120
+ }
121
+ }
122
+
123
+ except Exception as e:
124
+ print(f"์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ํŒŒ์‹ฑ ์ „์ฒด ์˜ค๋ฅ˜: {e}")
125
+ print(f"ํŒŒ์‹ฑ ์‹คํŒจํ•œ ์ปจํ…์ธ : {repr(content)}")
126
+ return {
127
+ "company_profile": {
128
+ "name": "์˜ค๋ฅ˜ ๋ฐœ์ƒ",
129
+ "vision_mission": f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}",
130
+ "core_values": ["์˜ค๋ฅ˜"],
131
+ "talent_philosophy": f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}",
132
+ "recent_news_summary": f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}",
133
+ "main_products_services": ["์˜ค๋ฅ˜"]
134
+ },
135
+ "position_analysis": {
136
+ "role_summary": f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}",
137
+ "required_skills": {
138
+ "hard": ["์˜ค๋ฅ˜"],
139
+ "soft": ["์˜ค๋ฅ˜"]
140
+ },
141
+ "keywords": ["์˜ค๋ฅ˜"]
142
+ },
143
+ "industry_context": {
144
+ "trends": ["์˜ค๋ฅ˜"],
145
+ "competitors": ["์˜ค๋ฅ˜"]
146
+ }
147
+ }
148
+
149
+ def generate_context_report(job_title, company_name, experience_level):
150
+ """
151
+ OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
152
+ """
153
+ try:
154
+ if not job_title or not company_name or not experience_level:
155
+ return "์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", {}
156
+
157
+ # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
158
+ prompt = prompt_template.format(
159
+ job_title=job_title,
160
+ company_name=company_name,
161
+ experience_level=experience_level
162
+ )
163
+
164
+ # OpenAI Responses API ํ˜ธ์ถœ (Web Search Preview ์‚ฌ์šฉ)
165
+ response = client.responses.create(
166
+ model="gpt-4o-mini",
167
+ tools=[{
168
+ "type": "web_search_preview",
169
+ "search_context_size": "high",
170
+ }],
171
+ input=f"๋‹น์‹ ์€ ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ์„ ์œ„ํ•œ ๊ธฐ์—… ๋ฐ ์ง๋ฌด ๋ถ„์„ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•ด ์ตœ์‹  ๊ธฐ์—… ์ •๋ณด์™€ ์‚ฐ์—… ๋™ํ–ฅ์„ ํ™•์ธํ•˜๊ณ  ์ •ํ™•ํ•œ JSON ํ˜•์‹์œผ๋กœ ๊ตฌ์กฐํ™”๋œ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”.\n\n{prompt}"
172
+ )
173
+
174
+ content = response.output_text
175
+ print(f"=== AI ์‘๋‹ต ์›๋ณธ ===")
176
+ print(content)
177
+ print(f"=== ์ „์ฒด ์‘๋‹ต ๊ฐ์ฒด ===")
178
+ print(response)
179
+
180
+ # ์›น ๊ฒ€์ƒ‰ ์ฐธ๊ณ  ๋งํฌ ์ถœ๋ ฅ
181
+ if hasattr(response, 'web_search_results') and response.web_search_results:
182
+ print(f"=== ์ฐธ๊ณ ํ•œ ์›น ๊ฒ€์ƒ‰ ๋งํฌ ===")
183
+ for i, result in enumerate(response.web_search_results, 1):
184
+ if hasattr(result, 'url'):
185
+ print(f"{i}. {result.url}")
186
+ elif hasattr(result, 'link'):
187
+ print(f"{i}. {result.link}")
188
+
189
+ print(f"=== AI ์‘๋‹ต ๋ ===")
190
+
191
+ report_data = parse_context_report(content)
192
+
193
+ if not report_data or 'company_profile' not in report_data:
194
+ return "์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", {}
195
+
196
+ # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
197
+ result = f"""## ๐Ÿ“Š {company_name} - {job_title} ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ
198
+
199
+ ### ๐Ÿข **๊ธฐ์—… ํ”„๋กœํ•„**
200
+
201
+ **๐ŸŽฏ ๋น„์ „ & ๋ฏธ์…˜**
202
+ {report_data['company_profile']['vision_mission']}
203
+
204
+ **๐Ÿ’Ž ํ•ต์‹ฌ ๊ฐ€์น˜**
205
+ """
206
+ for i, value in enumerate(report_data['company_profile']['core_values'], 1):
207
+ result += f"**{i}.** {value}\n"
208
+
209
+ result += f"""
210
+ **๐Ÿ‘ฅ ์ธ์žฌ์ƒ**
211
+ {report_data['company_profile']['talent_philosophy']}
212
+
213
+ **๐Ÿ“ฐ ์ตœ๊ทผ ๋™ํ–ฅ**
214
+ {report_data['company_profile']['recent_news_summary']}
215
+
216
+ **๐Ÿ›๏ธ ์ฃผ์š” ์ œํ’ˆ/์„œ๋น„์Šค**
217
+ """
218
+ for i, service in enumerate(report_data['company_profile']['main_products_services'], 1):
219
+ result += f"**{i}.** {service}\n"
220
+
221
+ result += f"""
222
+
223
+ ### ๐Ÿ’ผ **์ง๋ฌด ๋ถ„์„**
224
+
225
+ **๐Ÿ“‹ ์—ญํ•  ์š”์•ฝ**
226
+ {report_data['position_analysis']['role_summary']}
227
+
228
+ **๐Ÿ”ง ํ•„์š” ์Šคํ‚ฌ**
229
+
230
+ *ํ•˜๋“œ ์Šคํ‚ฌ:*
231
+ """
232
+ for skill in report_data['position_analysis']['required_skills']['hard']:
233
+ result += f"โ€ข {skill}\n"
234
+
235
+ result += "\n*์†Œํ”„ํŠธ ์Šคํ‚ฌ:*\n"
236
+ for skill in report_data['position_analysis']['required_skills']['soft']:
237
+ result += f"โ€ข {skill}\n"
238
+
239
+ result += f"""
240
+ **๐Ÿท๏ธ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ**
241
+ """
242
+ for keyword in report_data['position_analysis']['keywords']:
243
+ result += f"`{keyword}` "
244
+
245
+ result += f"""
246
+
247
+ ### ๐ŸŒ **์‚ฐ์—… ๋งฅ๋ฝ**
248
+
249
+ **๐Ÿ“ˆ ์ฃผ์š” ํŠธ๋ Œ๋“œ**
250
+ """
251
+ for i, trend in enumerate(report_data['industry_context']['trends'], 1):
252
+ result += f"**{i}.** {trend}\n"
253
+
254
+ result += f"""
255
+ **๐Ÿ† ์ฃผ์š” ๊ฒฝ์Ÿ์‚ฌ**
256
+ """
257
+ for i, competitor in enumerate(report_data['industry_context']['competitors'], 1):
258
+ result += f"**{i}.** {competitor}\n"
259
+
260
+ result += f"""
261
+
262
+ ---
263
+ **๐Ÿ“ ์ž…๋ ฅ ์ •๋ณด:**
264
+ - ํšŒ์‚ฌ: {company_name}
265
+ - ์ง๋ฌด: {job_title}
266
+ - ๊ฒฝ๋ ฅ: {experience_level}
267
+
268
+ *๋ณธ ๋ฆฌํฌํŠธ๋Š” AI๊ฐ€ ์ƒ์„ฑํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ ์ •๋ณด์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์†Œ์„œ ์ž‘์„ฑ ์‹œ ์ฐธ๊ณ ์šฉ์œผ๋กœ ํ™œ์šฉํ•˜์„ธ์š”.*
269
+ """
270
+
271
+ return result, report_data
272
+
273
+ except Exception as e:
274
+ error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
275
+
276
+ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
277
+
278
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
279
+
280
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
281
+ """
282
+ return error_msg, {}
jasoseo-context-report/main.py ADDED
@@ -0,0 +1,314 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from llm_functions import generate_context_report
3
+
4
+ # ์˜ˆ์ œ ๋ฐ์ดํ„ฐ
5
+ example_companies = ["ํ† ์Šค", "์‚ผ์„ฑ์ „์ž", "CJ์ œ์ผ์ œ๋‹น", "ํ•˜์ด๋ธŒ", "ํ˜„๋Œ€๊ฑด์„ค", "์‹ ํ•œ์€ํ–‰", "์นด์นด์˜ค", "๋„ค์ด๋ฒ„", "LG์ „์ž", "SKํ•˜์ด๋‹‰์Šค"]
6
+ example_jobs = ["๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "๊ฒฝ์˜๊ธฐํš", "์‹ํ’ˆ๋งˆ์ผ€ํŒ…", "A&R", "HRM(์ธ์‚ฌ์šด์˜)", "ํ•ด์™ธ์˜์—…", "๋ฐ์ดํ„ฐ ๋ถ„์„", "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…", "๊ธฐํš", "๊ฐœ๋ฐœ"]
7
+ experience_levels = ["์‹ ์ž…", "๊ฒฝ๋ ฅ", "์ธํ„ด", "๊ธฐํƒ€"]
8
+
9
+ def create_info_cards(report_data):
10
+ """
11
+ ๋ฆฌํฌํŠธ ๋ฐ์ดํ„ฐ๋ฅผ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œํ•˜๋Š” HTML ์ƒ์„ฑ
12
+ """
13
+ if not report_data or 'company_profile' not in report_data:
14
+ return "<div style='text-align: center; color: #6B7280; padding: 20px;'>๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>"
15
+
16
+ # ๊ธฐ์—… ํ”„๋กœํ•„ ์นด๋“œ
17
+ company_card = f"""
18
+ <div style="
19
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
20
+ color: white;
21
+ border-radius: 15px;
22
+ padding: 20px;
23
+ margin: 10px 0;
24
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
25
+ ">
26
+ <h3 style="margin: 0 0 15px 0; font-size: 18px;">๐Ÿข ๊ธฐ์—… ํ”„๋กœํ•„</h3>
27
+ <div style="background: rgba(255,255,255,0.1); border-radius: 10px; padding: 15px;">
28
+ <strong>๐ŸŽฏ ๋น„์ „/๋ฏธ์…˜:</strong><br>
29
+ {report_data['company_profile']['vision_mission']}<br><br>
30
+ <strong>๐Ÿ’Ž ํ•ต์‹ฌ ๊ฐ€์น˜:</strong><br>
31
+ {' โ€ข '.join(report_data['company_profile']['core_values'])}<br><br>
32
+ <strong>๐Ÿ‘ฅ ์ธ์žฌ์ƒ:</strong><br>
33
+ {report_data['company_profile']['talent_philosophy']}
34
+ </div>
35
+ </div>
36
+ """
37
+
38
+ # ์ง๋ฌด ๋ถ„์„ ์นด๋“œ
39
+ position_card = f"""
40
+ <div style="
41
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
42
+ color: white;
43
+ border-radius: 15px;
44
+ padding: 20px;
45
+ margin: 10px 0;
46
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
47
+ ">
48
+ <h3 style="margin: 0 0 15px 0; font-size: 18px;">๐Ÿ’ผ ์ง๋ฌด ๋ถ„์„</h3>
49
+ <div style="background: rgba(255,255,255,0.1); border-radius: 10px; padding: 15px;">
50
+ <strong>๐Ÿ“‹ ์—ญํ• :</strong><br>
51
+ {report_data['position_analysis']['role_summary']}<br><br>
52
+ <strong>๐Ÿ”ง ํ•˜๋“œ ์Šคํ‚ฌ:</strong><br>
53
+ {' โ€ข '.join(report_data['position_analysis']['required_skills']['hard'])}<br><br>
54
+ <strong>๐Ÿ’ก ์†Œํ”„ํŠธ ์Šคํ‚ฌ:</strong><br>
55
+ {' โ€ข '.join(report_data['position_analysis']['required_skills']['soft'])}
56
+ </div>
57
+ </div>
58
+ """
59
+
60
+ # ์‚ฐ์—… ๋งฅ๋ฝ ์นด๋“œ
61
+ industry_card = f"""
62
+ <div style="
63
+ background: linear-gradient(135deg, #fc4a1a 0%, #f7b733 100%);
64
+ color: white;
65
+ border-radius: 15px;
66
+ padding: 20px;
67
+ margin: 10px 0;
68
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
69
+ ">
70
+ <h3 style="margin: 0 0 15px 0; font-size: 18px;">๐ŸŒ ์‚ฐ์—… ๋งฅ๋ฝ</h3>
71
+ <div style="background: rgba(255,255,255,0.1); border-radius: 10px; padding: 15px;">
72
+ <strong>๐Ÿ“ˆ ์ฃผ์š” ํŠธ๋ Œ๋“œ:</strong><br>
73
+ {' โ€ข '.join(report_data['industry_context']['trends'])}<br><br>
74
+ <strong>๐Ÿ† ์ฃผ์š” ๊ฒฝ์Ÿ์‚ฌ:</strong><br>
75
+ {' โ€ข '.join(report_data['industry_context']['competitors'])}
76
+ </div>
77
+ </div>
78
+ """
79
+
80
+ # ํ‚ค์›Œ๋“œ ์นด๋“œ
81
+ keywords_html = ""
82
+ if 'keywords' in report_data['position_analysis']:
83
+ keywords_html = "<div style='margin-top: 15px;'><strong>๐Ÿท๏ธ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ:</strong><br>"
84
+ for keyword in report_data['position_analysis']['keywords']:
85
+ keywords_html += f"<span style='background: rgba(255,255,255,0.2); padding: 5px 10px; border-radius: 15px; margin: 2px; display: inline-block; font-size: 12px;'>{keyword}</span>"
86
+ keywords_html += "</div>"
87
+
88
+ return f"""
89
+ <div style="display: flex; flex-direction: column; gap: 10px;">
90
+ {company_card}
91
+ {position_card}
92
+ {industry_card}
93
+ <div style="
94
+ background: linear-gradient(135deg, #a8edea 0%, #fed6e3 100%);
95
+ color: #333;
96
+ border-radius: 15px;
97
+ padding: 20px;
98
+ margin: 10px 0;
99
+ box-shadow: 0 4px 15px rgba(0,0,0,0.1);
100
+ ">
101
+ <h3 style="margin: 0 0 15px 0; font-size: 18px;">๐Ÿท๏ธ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ</h3>
102
+ <div style="display: flex; flex-wrap: wrap; gap: 8px;">
103
+ {''.join([f'<span style="background: #667eea; color: white; padding: 8px 15px; border-radius: 20px; font-size: 14px; font-weight: bold;">{keyword}</span>' for keyword in report_data['position_analysis']['keywords']])}
104
+ </div>
105
+ </div>
106
+ </div>
107
+ """
108
+
109
+ def process_report_generation(job_title, company_name, experience_level):
110
+ """
111
+ ๋ฆฌํฌํŠธ ์ƒ์„ฑ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI์— ํ‘œ์‹œํ•  ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
112
+ """
113
+ try:
114
+ content, report_data = generate_context_report(job_title, company_name, experience_level)
115
+ info_cards = create_info_cards(report_data)
116
+ return content, info_cards
117
+ except Exception as e:
118
+ error_content = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
119
+
120
+ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
121
+
122
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
123
+
124
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
125
+ """
126
+ error_cards = create_info_cards({})
127
+ return error_content, error_cards
128
+
129
+ def create_interface():
130
+ """
131
+ Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
132
+ """
133
+ with gr.Blocks(
134
+ title="๐Ÿ“Š ์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ",
135
+ theme=gr.themes.Soft(),
136
+ css="""
137
+ .main-header {
138
+ text-align: center;
139
+ padding: 20px;
140
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
141
+ color: white;
142
+ border-radius: 10px;
143
+ margin-bottom: 20px;
144
+ }
145
+ .input-section {
146
+ background-color: #f8f9fa;
147
+ padding: 20px;
148
+ border-radius: 8px;
149
+ margin: 10px 0;
150
+ }
151
+ .example-section {
152
+ background-color: #f0f9ff;
153
+ padding: 15px;
154
+ border-radius: 8px;
155
+ margin: 10px 0;
156
+ }
157
+ """
158
+ ) as demo:
159
+
160
+ # ํ—ค๋”
161
+ gr.HTML("""
162
+ <div class="main-header">
163
+ <h1>๐Ÿ“Š ์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ</h1>
164
+ <p>๊ธฐ์—…๊ณผ ์ง๋ฌด์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ๋ถ„์„์œผ๋กœ ์™„๋ฒฝํ•œ ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”</p>
165
+ </div>
166
+ """)
167
+
168
+ # ์„ค๋ช…
169
+ gr.Markdown("""
170
+ ### ๐Ÿš€ **์‚ฌ์šฉ ๋ฐฉ๋ฒ•**
171
+ 1. **์ง๋ฌด**: ์ง€์›ํ•˜๊ณ ์ž ํ•˜๋Š” ์ง๋ฌด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”
172
+ 2. **ํšŒ์‚ฌ๋ช…**: ์ง€์› ํšŒ์‚ฌ๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”
173
+ 3. **๊ฒฝ๋ ฅ ์ˆ˜์ค€**: ์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด/๊ธฐํƒ€ ์ค‘ ์„ ํƒํ•˜์„ธ์š”
174
+ 4. **์ƒ์„ฑ**: '๋ฆฌํฌํŠธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”
175
+
176
+ โœจ **ํŠน์ง•**: ๊ธฐ์—… ํ”„๋กœํ•„, ์ง๋ฌด ๋ถ„์„, ์‚ฐ์—… ๋งฅ๋ฝ์„ ์ข…ํ•ฉ์ ์œผ๋กœ ๋ถ„์„ํ•˜์—ฌ ์ž์†Œ์„œ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
177
+ """)
178
+
179
+ with gr.Row():
180
+ with gr.Column(scale=2):
181
+ # ์ž…๋ ฅ ์„น์…˜
182
+ gr.HTML('<div class="input-section">')
183
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
184
+
185
+ with gr.Row():
186
+ job_input = gr.Textbox(
187
+ label="๐Ÿ’ผ ์ง๋ฌด",
188
+ placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๊ฒฝ์˜๊ธฐํš, ๋งˆ์ผ€ํŒ… ๋“ฑ",
189
+ value="",
190
+ scale=1
191
+ )
192
+
193
+ company_input = gr.Textbox(
194
+ label="๐Ÿข ํšŒ์‚ฌ๋ช…",
195
+ placeholder="์˜ˆ: ํ† ์Šค, ์‚ผ์„ฑ์ „์ž, ์นด์นด์˜ค ๋“ฑ",
196
+ value="",
197
+ scale=1
198
+ )
199
+
200
+ experience_input = gr.Dropdown(
201
+ label="๐Ÿ“ˆ ๊ฒฝ๋ ฅ ์ˆ˜์ค€",
202
+ choices=experience_levels,
203
+ value="์‹ ์ž…",
204
+ interactive=True
205
+ )
206
+
207
+ generate_btn = gr.Button(
208
+ "๐Ÿ“Š ๋ฆฌํฌํŠธ ์ƒ์„ฑ",
209
+ variant="primary",
210
+ size="lg"
211
+ )
212
+ gr.HTML('</div>')
213
+
214
+ with gr.Column(scale=1):
215
+ # ์˜ˆ์ œ ๋ฐ ๊ฐ€์ด๋“œ
216
+ gr.HTML('<div class="example-section">')
217
+ gr.Markdown("### ๐Ÿ’ก **์˜ˆ์ œ ํšŒ์‚ฌ**")
218
+
219
+ company_rows = [example_companies[i:i+2] for i in range(0, len(example_companies), 2)]
220
+ for row in company_rows:
221
+ with gr.Row():
222
+ for company in row:
223
+ example_btn = gr.Button(
224
+ company,
225
+ size="sm",
226
+ variant="secondary",
227
+ scale=1
228
+ )
229
+ example_btn.click(
230
+ fn=lambda x=company: x,
231
+ outputs=company_input
232
+ )
233
+
234
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ์ง๋ฌด**")
235
+
236
+ job_rows = [example_jobs[i:i+2] for i in range(0, len(example_jobs), 2)]
237
+ for row in job_rows:
238
+ with gr.Row():
239
+ for job in row:
240
+ job_btn = gr.Button(
241
+ job,
242
+ size="sm",
243
+ variant="secondary",
244
+ scale=1
245
+ )
246
+ job_btn.click(
247
+ fn=lambda x=job: x,
248
+ outputs=job_input
249
+ )
250
+
251
+ gr.Markdown("### ๐Ÿ“ˆ **๊ฒฝ๋ ฅ ์ˆ˜์ค€**")
252
+ with gr.Row():
253
+ for level in experience_levels:
254
+ level_btn = gr.Button(
255
+ level,
256
+ size="sm",
257
+ variant="secondary",
258
+ scale=1
259
+ )
260
+ level_btn.click(
261
+ fn=lambda x=level: x,
262
+ outputs=experience_input
263
+ )
264
+
265
+ gr.HTML('</div>')
266
+
267
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
268
+ with gr.Row():
269
+ with gr.Column(scale=2):
270
+ gr.Markdown("### ๐Ÿ“‹ **์ƒ์„ธ ๋ฆฌํฌํŠธ**")
271
+ result_output = gr.Markdown(
272
+ value="์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ์ž…๋ ฅํ•˜๊ณ  '๋ฆฌํฌํŠธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
273
+ elem_classes=["result-output"]
274
+ )
275
+
276
+ with gr.Column(scale=1):
277
+ gr.Markdown("### ๐Ÿ“Š **ํ•ต์‹ฌ ์ •๋ณด**")
278
+ info_cards = gr.HTML(
279
+ value="<div style='text-align: center; color: #6B7280; padding: 20px;'>๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>",
280
+ elem_classes=["info-cards"]
281
+ )
282
+
283
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
284
+ generate_btn.click(
285
+ fn=process_report_generation,
286
+ inputs=[job_input, company_input, experience_input],
287
+ outputs=[result_output, info_cards],
288
+ api_name="generate_context_report"
289
+ )
290
+
291
+ # ํ‘ธํ„ฐ
292
+ gr.Markdown("""
293
+ ---
294
+ **๐Ÿ“Š ๋ฆฌํฌํŠธ ๊ตฌ์„ฑ**:
295
+ - **๐Ÿข ๊ธฐ์—… ํ”„๋กœํ•„**: ๋น„์ „/๋ฏธ์…˜, ํ•ต์‹ฌ๊ฐ€์น˜, ์ธ์žฌ์ƒ, ์ตœ๊ทผ๋™ํ–ฅ, ์ฃผ์š”์ œํ’ˆ/์„œ๋น„์Šค
296
+ - **๐Ÿ’ผ ์ง๋ฌด ๋ถ„์„**: ์—ญํ• ์š”์•ฝ, ํ•„์š”์Šคํ‚ฌ(ํ•˜๋“œ/์†Œํ”„ํŠธ), ํ•ต์‹ฌํ‚ค์›Œ๋“œ
297
+ - **๐ŸŒ ์‚ฐ์—… ๋งฅ๋ฝ**: ์ฃผ์š”ํŠธ๋ Œ๋“œ, ๊ฒฝ์Ÿ์‚ฌ ์ •๋ณด
298
+
299
+ ๐Ÿ’ก **ํ™œ์šฉ ํŒ**: ์ƒ์„ฑ๋œ ํ‚ค์›Œ๋“œ์™€ ์ •๋ณด๋ฅผ ์ž์†Œ์„œ ์ž‘์„ฑ ์‹œ ์ ๊ทน ํ™œ์šฉํ•˜์—ฌ ๋งž์ถคํ˜• ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
300
+
301
+ ๐Ÿค– **Powered by**: OpenAI GPT-4o
302
+ """)
303
+
304
+ return demo
305
+
306
+ if __name__ == "__main__":
307
+ # Gradio ์•ฑ ์‹คํ–‰
308
+ demo = create_interface()
309
+ demo.launch(
310
+ server_name="0.0.0.0",
311
+ # server_port=7862,
312
+ share=True,
313
+ show_error=True
314
+ )
jasoseo-context-report/prompt.yaml ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt: |
2
+ ๋‹น์‹ ์€ ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ์„ ์œ„ํ•œ ๊ธฐ์—… ๋ฐ ์ง๋ฌด ๋ถ„์„ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
3
+ ์ฃผ์–ด์ง„ ์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ฐ”ํƒ•์œผ๋กœ ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ์ฒด๊ณ„์ ์œผ๋กœ ์ •๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
4
+
5
+ ### ์ง€์นจ
6
+ 1. **์›น ๊ฒ€์ƒ‰ ํ™œ์šฉ**: ํšŒ์‚ฌ๋ช…์„ ๊ฒ€์ƒ‰ํ•˜์—ฌ ์ตœ์‹  ๊ธฐ์—… ์ •๋ณด, ๋น„์ „/๋ฏธ์…˜, ํ•ต์‹ฌ๊ฐ€์น˜, ์ตœ๊ทผ ๋‰ด์Šค, ์ฑ„์šฉ ์ •๋ณด ๋“ฑ์„ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
7
+ 2. **์ •ํ™•ํ•œ ์ •๋ณด**: ๊ฒ€์ƒ‰๋œ ์ตœ์‹  ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์ •ํ™•ํ•˜๊ณ  ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ๋‚ด์šฉ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
8
+ 3. **์ž์†Œ์„œ ๊ด€์ **: ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ์— ์‹ค์งˆ์ ์œผ๋กœ ๋„์›€์ด ๋˜๋Š” ์ •๋ณด์— ์ง‘์ค‘ํ•ฉ๋‹ˆ๋‹ค.
9
+ 4. **๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ**: ์ง€์ •๋œ JSON ์Šคํ‚ค๋งˆ๋ฅผ ์ •ํ™•ํžˆ ๋”ฐ๋ผ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค.
10
+
11
+ ### ์ž…๋ ฅ ์ •๋ณด
12
+ - **์ง๋ฌด:** {job_title}
13
+ - **ํšŒ์‚ฌ๋ช…:** {company_name}
14
+ - **๊ฒฝ๋ ฅ ์ˆ˜์ค€:** {experience_level}
15
+
16
+ ### ์ถœ๋ ฅ ํ˜•์‹
17
+ ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ JSON ์Šคํ‚ค๋งˆ๋ฅผ ์ •ํ™•ํžˆ ๋”ฐ๋ผ ์‘๋‹ตํ•˜์„ธ์š”:
18
+
19
+ ```json
20
+ {{
21
+ "company_profile": {{
22
+ "name": "ํšŒ์‚ฌ๋ช…",
23
+ "vision_mission": "ํšŒ์‚ฌ์˜ ๋น„์ „๊ณผ ๋ฏธ์…˜ ์„ค๋ช…",
24
+ "core_values": ["ํ•ต์‹ฌ๊ฐ€์น˜1", "ํ•ต์‹ฌ๊ฐ€์น˜2", "ํ•ต์‹ฌ๊ฐ€์น˜3"],
25
+ "talent_philosophy": "์ธ์žฌ์ƒ ๋ฐ ์ธ์žฌ ์ฒ ํ•™",
26
+ "recent_news_summary": "์ตœ๊ทผ ์ฃผ์š” ๋‰ด์Šค ๋ฐ ๋™ํ–ฅ ์š”์•ฝ",
27
+ "main_products_services": ["์ฃผ์š”์ œํ’ˆ1", "์ฃผ์š”์ œํ’ˆ2", "์ฃผ์š”์ œํ’ˆ3"]
28
+ }},
29
+ "position_analysis": {{
30
+ "role_summary": "ํ•ด๋‹น ์ง๋ฌด์˜ ์ฃผ์š” ์—ญํ• ๊ณผ ์ฑ…์ž„ ์š”์•ฝ",
31
+ "required_skills": {{
32
+ "hard": ["ํ•˜๋“œ์Šคํ‚ฌ1", "ํ•˜๋“œ์Šคํ‚ฌ2", "ํ•˜๋“œ์Šคํ‚ฌ3"],
33
+ "soft": ["์†Œํ”„ํŠธ์Šคํ‚ฌ1", "์†Œํ”„ํŠธ์Šคํ‚ฌ2", "์†Œํ”„ํŠธ์Šคํ‚ฌ3"]
34
+ }},
35
+ "keywords": ["ํ‚ค์›Œ๋“œ1", "ํ‚ค์›Œ๋“œ2", "ํ‚ค์›Œ๋“œ3", "ํ‚ค์›Œ๋“œ4", "ํ‚ค์›Œ๋“œ5"]
36
+ }},
37
+ "industry_context": {{
38
+ "trends": ["์‚ฐ์—…ํŠธ๋ Œ๋“œ1", "์‚ฐ์—…ํŠธ๋ Œ๋“œ2", "์‚ฐ์—…ํŠธ๋ Œ๋“œ3"],
39
+ "competitors": ["๊ฒฝ์Ÿ์‚ฌ1", "๊ฒฝ์Ÿ์‚ฌ2", "๊ฒฝ์Ÿ์‚ฌ3"]
40
+ }}
41
+ }}
42
+ ```
43
+
44
+ ### ์„ธ๋ถ€ ์š”๊ตฌ์‚ฌํ•ญ
45
+
46
+ **company_profile (๊ธฐ์—… ํ”„๋กœํ•„)**
47
+ - name: ์ •ํ™•ํ•œ ํšŒ์‚ฌ๋ช…
48
+ - vision_mission: ํšŒ์‚ฌ์˜ ๋น„์ „๊ณผ ๋ฏธ์…˜์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์„ค๋ช… (1-2๋ฌธ์žฅ)
49
+ - core_values: ํšŒ์‚ฌ์˜ ํ•ต์‹ฌ ๊ฐ€์น˜ 3-4๊ฐœ (๋ฐฐ์—ด ํ˜•ํƒœ)
50
+ - talent_philosophy: ํšŒ์‚ฌ๊ฐ€ ์ถ”๊ตฌํ•˜๋Š” ์ธ์žฌ์ƒ๊ณผ ์ธ์žฌ ์ฒ ํ•™ (1-2๋ฌธ์žฅ)
51
+ - recent_news_summary: ์ตœ๊ทผ 1-2๋…„๊ฐ„ ์ฃผ์š” ๋‰ด์Šค๋‚˜ ์‚ฌ์—… ๋™ํ–ฅ ์š”์•ฝ (2-3๋ฌธ์žฅ)
52
+ - main_products_services: ์ฃผ์š” ์ œํ’ˆ์ด๋‚˜ ์„œ๋น„์Šค 3-4๊ฐœ (๋ฐฐ์—ด ํ˜•ํƒœ)
53
+
54
+ **position_analysis (์ง๋ฌด ๋ถ„์„)**
55
+ - role_summary: ํ•ด๋‹น ์ง๋ฌด์˜ ์ฃผ์š” ์—ญํ• ๊ณผ ์ฑ…์ž„์„ ๊ฒฝ๋ ฅ ์ˆ˜์ค€์— ๋งž๊ฒŒ ์„ค๋ช… (2-3๋ฌธ์žฅ)
56
+ - required_skills.hard: ํ•„์š”ํ•œ ํ•˜๋“œ ์Šคํ‚ฌ 3-4๊ฐœ (๊ธฐ์ˆ , ๋„๊ตฌ, ์ž๊ฒฉ์ฆ ๋“ฑ)
57
+ - required_skills.soft: ํ•„์š”ํ•œ ์†Œํ”„ํŠธ ์Šคํ‚ฌ 3-4๊ฐœ (์—ญ๋Ÿ‰, ํƒœ๋„, ์„ฑํ–ฅ ๋“ฑ)
58
+ - keywords: ์ž์†Œ์„œ์— ํ™œ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ 5-6๊ฐœ
59
+
60
+ **industry_context (์‚ฐ์—… ๋งฅ๋ฝ)**
61
+ - trends: ํ•ด๋‹น ์‚ฐ์—…์˜ ์ฃผ์š” ํŠธ๋ Œ๋“œ 3๊ฐœ
62
+ - competitors: ์ฃผ์š” ๊ฒฝ์Ÿ์‚ฌ 3๊ฐœ
63
+
64
+ ### ์˜ˆ์‹œ
65
+ [์ž…๋ ฅ]
66
+ - ์ง๋ฌด: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ
67
+ - ํšŒ์‚ฌ๋ช…: ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)
68
+ - ๊ฒฝ๋ ฅ ์ˆ˜์ค€: ์‹ ์ž…
69
+
70
+ [์ถœ๋ ฅ]
71
+ ```json
72
+ {{
73
+ "company_profile": {{
74
+ "name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
75
+ "vision_mission": "๊ธˆ์œต์˜ ๋ชจ๋“  ์ˆœ๊ฐ„์„ ์‰ฝ๊ณ  ๊ฐ„ํŽธํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๋Š” ๊ธฐ์ˆ  ๊ธฐ๋ฐ˜ ๊ธˆ์œต ํ˜์‹  ๊ธฐ์—…์ž…๋‹ˆ๋‹ค.",
76
+ "core_values": ["Customer Obsession (๊ณ ๊ฐ ์ค‘์‹ฌ)", "Excellence (ํƒ์›”ํ•จ)", "Radical Transparency (๊ทน๋„์˜ ํˆฌ๋ช…์„ฑ)", "Freedom and Responsibility (์ž์œจ๊ณผ ์ฑ…์ž„)"],
77
+ "talent_philosophy": "์Šค์Šค๋กœ ๋ฌธ์ œ๋ฅผ ์ •์˜ํ•˜๊ณ  ํ•ด๊ฒฐํ•˜๋ฉฐ, ๋™๋ฃŒ์™€ ํˆฌ๋ช…ํ•˜๊ฒŒ ์†Œํ†ตํ•˜์—ฌ ์ตœ๊ณ ์˜ ์„ฑ๊ณผ๋ฅผ ๋งŒ๋“œ๋Š” ์ธ์žฌ๋ฅผ ์ง€ํ–ฅํ•ฉ๋‹ˆ๋‹ค.",
78
+ "recent_news_summary": "ํ† ์Šค๋ฑ…ํฌ, ํ† ์Šค์ฆ๊ถŒ ๋“ฑ ์ข…ํ•ฉ ๊ธˆ์œต ํ”Œ๋žซํผ์œผ๋กœ์˜ ํ™•์žฅ์„ ๋„˜์–ด, ์•Œ๋œฐํฐ(MVNO) ์‚ฌ์—… ์ง„์ถœ ๋“ฑ ๋น„๊ธˆ์œต ๋ถ„์•ผ๋กœ ์„œ๋น„์Šค๋ฅผ ๋‹ค๊ฐํ™”ํ•˜๋ฉฐ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ˜์‹ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.",
79
+ "main_products_services": ["ํ† ์Šค (๊ฐ„ํŽธ์†ก๊ธˆ, ๊ฒฐ์ œ)", "ํ† ์Šค๋ฑ…ํฌ", "ํ† ์Šค์ฆ๊ถŒ", "ํ† ์Šคํ”Œ๋ ˆ์ด์Šค"]
80
+ }},
81
+ "position_analysis": {{
82
+ "role_summary": "๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์•ˆ์ •์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ธˆ์œต ์„œ๋น„์Šค์˜ ๋ฐฑ์—”๋“œ API๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. ๋ณต์žกํ•œ ๊ธˆ์œต ๋ฌธ์ œ๋ฅผ ๊ธฐ์ˆ ๋กœ ํ•ด๊ฒฐํ•˜๋ฉฐ ์ตœ๊ณ ์˜ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ์ œ๊ณตํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ•ฉ๋‹ˆ๋‹ค.",
83
+ "required_skills": {{
84
+ "hard": ["Java/Kotlin", "Spring Framework", "RDBMS/NoSQL", "MSA (Microservice Architecture) ์ดํ•ด"],
85
+ "soft": ["๋ฌธ์ œ ํ•ด๊ฒฐ ๋Šฅ๋ ฅ", "ํ˜‘์—… ๋ฐ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜", "ํ•™์Šต ๋ฐ ์„ฑ์žฅ ์˜์ง€", "๋†’์€ ์ˆ˜์ค€์˜ ์ฑ…์ž„๊ฐ"]
86
+ }},
87
+ "keywords": ["ํ•€ํ…Œํฌ", "๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ", "MSA", "๊ฒฐ์ œ ์‹œ์Šคํ…œ", "API", "๊ธˆ์œต ํ”Œ๋žซํผ"]
88
+ }},
89
+ "industry_context": {{
90
+ "trends": ["๊ฐ„ํŽธ๊ฒฐ์ œ ์‹œ์žฅ์˜ ์ง€์†์  ์„ฑ์žฅ", "๋งˆ์ด๋ฐ์ดํ„ฐ ์‚ฌ์—… ํ™œ์„ฑํ™”", "๋น…ํ…Œํฌ ๊ธฐ์—…์˜ ๊ธˆ์œต์—… ์ง„์ถœ ๊ฐ€์†ํ™”"],
91
+ "competitors": ["์นด์นด์˜คํŽ˜์ด", "๋„ค์ด๋ฒ„ํŒŒ์ด๋‚ธ์…œ"]
92
+ }}
93
+ }}
94
+ ```
95
+
96
+ ์ด์ œ ๋‹ค์Œ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
jd-recommendation/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (8.5 kB). View file
 
jd-recommendation/eval.json ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "jd_recommendation_eval": {
3
+ "task_definition": {
4
+ "description": "Generate a representative job description based on the job title, company name, and experience level. The output should be a concise narrative summarizing key responsibilities and qualifications, suitable for preparing a '์ž์†Œ์„œ'.",
5
+ "input": {
6
+ "job_title": "string",
7
+ "company_name": "string",
8
+ "experience_level": "์‹ ์ž… | ๊ฒฝ๋ ฅ | ์ธํ„ด | ๊ธฐํƒ€"
9
+ },
10
+ "output": {
11
+ "recommended_jd": "string"
12
+ }
13
+ },
14
+ "examples": [
15
+ {
16
+ "input": {
17
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
18
+ "company_name": "์‚ผ์„ฑ์ „์ž",
19
+ "experience_level": "์‹ ์ž…"
20
+ },
21
+ "output": {
22
+ "recommended_jd": "์ „์‚ฌ ๊ฒฝ์˜ ๋ชฉํ‘œ ๋‹ฌ์„ฑ์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„, ์‹œ์žฅ ๋ฆฌ์„œ์น˜, ์‚ฌ์—… ์ „๋žต ์ˆ˜๋ฆฝ ์ง€์› ์—…๋ฌด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‹ ๊ทœ ์‚ฌ์—… ๊ธฐํšŒ๋ฅผ ๋ฐœ๊ตดํ•˜๊ณ , ๊ฒฝ์˜์ง„์˜ ์˜์‚ฌ๊ฒฐ์ •์„ ๋•๊ธฐ ์œ„ํ•œ ๋ณด๊ณ ์„œ ์ž‘์„ฑ ๋ฐ ๋ฐœํ‘œ๋ฅผ ๋‹ด๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋›ฐ์–ด๋‚œ ๋ถ„์„๋ ฅ๊ณผ ๊ธฐํš๋ ฅ์„ ๊ฐ–์ถ˜ ์ธ์žฌ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค."
23
+ }
24
+ },
25
+ {
26
+ "input": {
27
+ "job_title": "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…",
28
+ "company_name": "์ฟ ํŒก",
29
+ "experience_level": "์ธํ„ด"
30
+ },
31
+ "output": {
32
+ "recommended_jd": "๋””์ง€ํ„ธ ๋งˆ์ผ€ํŒ… ์บ ํŽ˜์ธ ๊ธฐํš ๋ฐ ์šด์˜์„ ๋ณด์กฐํ•˜๋ฉฐ, ์†Œ์…œ ๋ฏธ๋””์–ด ์ฑ„๋„ ๊ด€๋ฆฌ, ์ฝ˜ํ…์ธ  ์ œ์ž‘, ๊ด‘๊ณ  ์„ฑ๊ณผ ๋ฐ์ดํ„ฐ ๋ถ„์„ ์—…๋ฌด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ตœ์‹  ๋งˆ์ผ€ํŒ… ํŠธ๋ Œ๋“œ๋ฅผ ํ•™์Šตํ•˜๊ณ  ์‹ค์ œ ์—…๋ฌด์— ์ ์šฉํ•˜๋ฉฐ ์„ฑ์žฅํ•  ๊ธฐํšŒ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค."
33
+ }
34
+ },
35
+ {
36
+ "input": {
37
+ "job_title": "HRM(์ธ์‚ฌ์šด์˜)",
38
+ "company_name": "ํ•˜์ด๋ธŒ",
39
+ "experience_level": "๊ฒฝ๋ ฅ"
40
+ },
41
+ "output": {
42
+ "recommended_jd": "๊ตฌ์„ฑ์›์˜ ์„ฑ์žฅ๊ณผ ์กฐ์ง ๋ฌธํ™” ๋ฐœ์ „์„ ์œ„ํ•œ ์ธ์‚ฌ ์šด์˜ ์ „๋ฐ˜์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. ์ฑ„์šฉ, ํ‰๊ฐ€, ๋ณด์ƒ, ๋…ธ๋ฌด ๋“ฑ HRM ํ”„๋กœ์„ธ์Šค๋ฅผ ์ฃผ๋„์ ์œผ๋กœ ๊ฐœ์„ ํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉฐ, ๊ธ€๋กœ๋ฒŒ ์—”ํ„ฐํ…Œ์ธ๋จผํŠธ ์‚ฐ์—…์— ๋งž๋Š” ์ธ์‚ฌ ์ „๋žต์„ ์ˆ˜๋ฆฝํ•ฉ๋‹ˆ๋‹ค. 3๋…„ ์ด์ƒ์˜ ๊ด€๋ จ ๊ฒฝ๋ ฅ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
43
+ }
44
+ },
45
+ {
46
+ "input": {
47
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
48
+ "company_name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
49
+ "experience_level": "์‹ ์ž…"
50
+ },
51
+ "output": {
52
+ "recommended_jd": "ํ† ์Šค ์„œ๋น„์Šค์˜ ์•ˆ์ •์ ์ด๊ณ  ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ์„œ๋ฒ„, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜, API๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์šด์˜ํ•ฉ๋‹ˆ๋‹ค. ๋Œ€์šฉ๋Ÿ‰ ํŠธ๋ž˜ํ”ฝ ์ฒ˜๋ฆฌ ๊ฒฝํ—˜์„ ์Œ“๊ณ , ๊ธˆ์œต ์„œ๋น„์Šค์˜ ๋ณต์žกํ•œ ๋ฌธ์ œ๋ฅผ ๊ธฐ์ˆ ๋กœ ํ•ด๊ฒฐํ•˜๋Š” ๋ฐ ๊ธฐ์—ฌํ•ฉ๋‹ˆ๋‹ค. Java/Kotlin, Spring ํ”„๋ ˆ์ž„์›Œํฌ์— ๋Œ€ํ•œ ์ดํ•ด๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
53
+ }
54
+ },
55
+ {
56
+ "input": {
57
+ "job_title": "ํ•ด์™ธ์˜์—…",
58
+ "company_name": "HMM (์—์ด์น˜์— ์— )",
59
+ "experience_level": "๊ฒฝ๋ ฅ"
60
+ },
61
+ "output": {
62
+ "recommended_jd": "๊ธ€๋กœ๋ฒŒ ํ•ด์šด ์‹œ์žฅ์—์„œ ์‹ ๊ทœ ๊ณ ๊ฐ์‚ฌ๋ฅผ ๋ฐœ๊ตดํ•˜๊ณ  ๊ธฐ์กด ๊ณ ๊ฐ ๊ด€๊ณ„๋ฅผ ๊ด€๋ฆฌํ•˜๋ฉฐ ์˜์—… ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. ํ•ด์™ธ ๋ฒ•์ธ ๋ฐ ํŒŒํŠธ๋„ˆ์‚ฌ์™€์˜ ํ˜‘๋ ฅ์„ ํ†ตํ•ด ์˜์—… ์ „๋žต์„ ์ˆ˜๋ฆฝํ•˜๊ณ , ์šด์ž„ ํ˜‘์ƒ ๋ฐ ๊ณ„์•ฝ ์ฒด๊ฒฐ์„ ์ฃผ๋„ํ•ฉ๋‹ˆ๋‹ค. ๋น„์ฆˆ๋‹ˆ์Šค ์˜์–ด ๋Šฅํ†ต ๋ฐ ๋™์ข… ์—…๊ณ„ ๊ฒฝ๋ ฅ์ž๋ฅผ ์šฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค."
63
+ }
64
+ },
65
+ {
66
+ "input": {
67
+ "job_title": "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…",
68
+ "company_name": "์•„๋ชจ๋ ˆํผ์‹œํ”ฝ",
69
+ "experience_level": "๊ฒฝ๋ ฅ"
70
+ },
71
+ "output": {
72
+ "recommended_jd": "์ž์‚ฌ ๋ธŒ๋žœ๋“œ์˜ ๋””์ง€ํ„ธ ์ฑ„๋„(์ž์‚ฌ๋ชฐ, ์†Œ์…œ๋ฏธ๋””์–ด, ์ด์ปค๋จธ์Šค) ๋งˆ์ผ€ํŒ… ์ „๋žต์„ ์ˆ˜๋ฆฝํ•˜๊ณ  ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํผํฌ๋จผ์Šค ๋งˆ์ผ€ํŒ…, CRM, ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜์˜ ๊ณ ๊ฐ ๋ถ„์„์„ ํ†ตํ•ด ๋ธŒ๋žœ๋“œ ์ธ์ง€๋„ ๋ฐ ๋งค์ถœ ์ฆ๋Œ€์— ๊ธฐ์—ฌํ•ฉ๋‹ˆ๋‹ค. ๋ทฐํ‹ฐ/์†Œ๋น„์žฌ ์‚ฐ์—… ๋งˆ์ผ€ํŒ… ๊ฒฝ๋ ฅ 5๋…„ ์ด์ƒ์„ ์ฐพ์Šต๋‹ˆ๋‹ค."
73
+ }
74
+ },
75
+ {
76
+ "input": {
77
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
78
+ "company_name": "๋„ฅ์Šจ",
79
+ "experience_level": "์ธํ„ด"
80
+ },
81
+ "output": {
82
+ "recommended_jd": "๋ผ์ด๋ธŒ ๊ฒŒ์ž„ ์„œ๋น„์Šค์˜ ๋ฐฑ์—”๋“œ ์‹œ์Šคํ…œ ๊ฐœ๋ฐœ ๋ฐ ์œ ์ง€๋ณด์ˆ˜ ์—…๋ฌด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์„ ๋ฐฐ ๊ฐœ๋ฐœ์ž๋“ค์˜ ๋ฉ˜ํ† ๋ง ํ•˜์— ๊ฒŒ์ž„ ์„œ๋ฒ„ ์ธํ”„๋ผ๋ฅผ ์ดํ•ดํ•˜๊ณ , ๊ฐ์ข… ํˆด ๊ฐœ๋ฐœ์— ์ฐธ์—ฌํ•˜๋ฉฐ ์‹ค๋ฌด ๊ฒฝํ—˜์„ ์Œ“์Šต๋‹ˆ๋‹ค. C++ ๋˜๋Š” Java์— ๋Œ€ํ•œ ๊ธฐ๋ณธ ์ง€์‹์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค."
83
+ }
84
+ },
85
+ {
86
+ "input": {
87
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
88
+ "company_name": "ํ˜„๋Œ€๊ฑด์„ค",
89
+ "experience_level": "๊ธฐํƒ€"
90
+ },
91
+ "output": {
92
+ "recommended_jd": "ํŠน์ • ํ•ด์™ธ ๊ฑด์„ค ํ”„๋กœ์ ํŠธ์˜ ์‚ฌ์—…์„ฑ ๊ฒ€ํ†  ๋ฐ ๊ธฐํš ์—…๋ฌด๋ฅผ ๋‹ด๋‹นํ•  ๊ณ„์•ฝ์ง ์ „๋ฌธ๊ฐ€๋ฅผ ๋ชจ์ง‘ํ•ฉ๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ์ˆ˜์ฃผ๋ฅผ ์œ„ํ•œ ์‹œ์žฅ ๋ถ„์„, ๋ฆฌ์Šคํฌ ๊ด€๋ฆฌ, ์˜ˆ์‚ฐ ์ˆ˜๋ฆฝ ์—…๋ฌด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ฉฐ, ๊ด€๋ จ ๊ฒฝ๋ ฅ 3๋…„ ์ด์ƒ ๋ณด์œ  ๋ฐ ์œ ๊ด€ ์ž๊ฒฉ์ฆ ์†Œ์ง€์ž๋ฅผ ์šฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค."
93
+ }
94
+ },
95
+ {
96
+ "input": {
97
+ "job_title": "HRM(์ธ์‚ฌ์šด์˜)",
98
+ "company_name": "์‹ ํ•œ์€ํ–‰",
99
+ "experience_level": "์‹ ์ž…"
100
+ },
101
+ "output": {
102
+ "recommended_jd": "์€ํ–‰์˜ ์ธ์ ์ž์› ๊ด€๋ฆฌ ๋ฐ ์šด์˜ ์—…๋ฌด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‹ ์ž…ํ–‰์› ์ฑ„์šฉ ๋ฐ ๊ต์œก ํ”„๋กœ๊ทธ๋žจ ๊ธฐํš, ๊ธ‰์—ฌ ๋ฐ ๋ณต๋ฆฌํ›„์ƒ ๊ด€๋ฆฌ, ์กฐ์ง๋ฌธํ™” ํ™œ์„ฑํ™” ํ™œ๋™์„ ์ง€์›ํ•˜๋ฉฐ ๊ธˆ์œต ์ธ์žฌ๋กœ ์„ฑ์žฅํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค."
103
+ }
104
+ },
105
+ {
106
+ "input": {
107
+ "job_title": "ํ•ด์™ธ์˜์—…",
108
+ "company_name": "์‚ผ์„ฑ๋ฐ”์ด์˜ค๋กœ์ง์Šค",
109
+ "experience_level": "์ธํ„ด"
110
+ },
111
+ "output": {
112
+ "recommended_jd": "๊ธ€๋กœ๋ฒŒ ์ œ์•ฝ/๋ฐ”์ด์˜ค ๊ณ ๊ฐ์‚ฌ ๋Œ€์ƒ ์˜์—… ํ™œ๋™์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์‹œ์žฅ ์ž๋ฃŒ ์กฐ์‚ฌ, ์ž ์žฌ ๊ณ ๊ฐ ๋ฆฌ์ŠคํŠธ์—…, ์ œ์•ˆ์„œ ์ž‘์„ฑ ๋ณด์กฐ ๋ฐ ๋‚ด๋ถ€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ์—…๋ฌด๋ฅผ ํ†ตํ•ด ๋ฐ”์ด์˜ค ์‚ฐ์—…์˜ B2B ์˜์—… ํ”„๋กœ์„ธ์Šค๋ฅผ ๊ฒฝํ—˜ํ•ฉ๋‹ˆ๋‹ค."
113
+ }
114
+ }
115
+ ]
116
+ }
117
+ }
jd-recommendation/llm_functions.py ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import re
4
+ import yaml
5
+ from openai import OpenAI
6
+
7
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
8
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
9
+
10
+ # ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ
11
+ import os
12
+ current_dir = os.path.dirname(os.path.abspath(__file__))
13
+ prompt_path = os.path.join(current_dir, 'prompt.yaml')
14
+ with open(prompt_path, 'r', encoding='utf-8') as f:
15
+ prompt_data = yaml.safe_load(f)
16
+ prompt_template = prompt_data['prompt']
17
+
18
+ def parse_jd_recommendation(content):
19
+ """
20
+ AI ์‘๋‹ต์—์„œ ์ง๋ฌด๊ธฐ์ˆ ์„œ JSON์„ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
21
+ """
22
+ try:
23
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ๊ธธ์ด: {len(content)}")
24
+ print(f"ํŒŒ์‹ฑํ•  ์ปจํ…์ธ  ์ฒซ 200์ž: {repr(content[:200])}")
25
+
26
+ # ํ…์ŠคํŠธ ์ „์ฒ˜๋ฆฌ
27
+ cleaned_content = content.strip()
28
+
29
+ # 1. JSON ์ฝ”๋“œ ๋ธ”๋ก ์ฐพ๊ธฐ (```json ... ``` ํ˜•์‹)
30
+ json_patterns = [
31
+ r'```json\s*(\{.*?\})\s*```',
32
+ r'```\s*(\{.*?\})\s*```',
33
+ r'```json\s*(.*?)\s*```',
34
+ r'```\s*(.*?)\s*```'
35
+ ]
36
+
37
+ for pattern in json_patterns:
38
+ json_match = re.search(pattern, cleaned_content, re.DOTALL)
39
+ if json_match:
40
+ json_str = json_match.group(1).strip()
41
+ print(f"JSON ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
42
+
43
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
44
+ json_str = re.sub(r'\n\s*', ' ', json_str)
45
+ json_str = re.sub(r',\s*}', '}', json_str)
46
+
47
+ try:
48
+ parsed_json = json.loads(json_str)
49
+ if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
50
+ return parsed_json['recommended_jd']
51
+ except json.JSONDecodeError as e:
52
+ print(f"JSON ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
53
+
54
+ # 2. ์ค‘๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ JSON ์ฐพ๊ธฐ
55
+ brace_patterns = [
56
+ r'\{.*?\}'
57
+ ]
58
+
59
+ for pattern in brace_patterns:
60
+ brace_match = re.search(pattern, cleaned_content, re.DOTALL)
61
+ if brace_match:
62
+ json_str = brace_match.group(0).strip()
63
+ print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ๋ฐœ๊ฒฌ: {repr(json_str[:100])}")
64
+
65
+ # JSON ๋ฌธ์ž์—ด ์ •๋ฆฌ
66
+ json_str = re.sub(r'\n\s*', ' ', json_str)
67
+ json_str = re.sub(r',\s*}', '}', json_str)
68
+
69
+ try:
70
+ parsed_json = json.loads(json_str)
71
+ if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
72
+ return parsed_json['recommended_jd']
73
+ except json.JSONDecodeError as e:
74
+ print(f"์ค‘๊ด„ํ˜ธ ๋ธ”๋ก ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
75
+
76
+ # 3. ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JSON์œผ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
77
+ try:
78
+ # ์ฝ”๋“œ ๋ธ”๋ก ๋งˆ์ปค ์ œ๊ฑฐ
79
+ if cleaned_content.startswith('```'):
80
+ lines = cleaned_content.split('\n')
81
+ start_idx = 1 if lines[0].startswith('```') else 0
82
+ end_idx = len(lines)
83
+ for i in range(len(lines)-1, -1, -1):
84
+ if lines[i].strip() == '```':
85
+ end_idx = i
86
+ break
87
+ cleaned_content = '\n'.join(lines[start_idx:end_idx])
88
+
89
+ cleaned_content = cleaned_content.strip()
90
+ parsed_json = json.loads(cleaned_content)
91
+ if isinstance(parsed_json, dict) and 'recommended_jd' in parsed_json:
92
+ return parsed_json['recommended_jd']
93
+ except json.JSONDecodeError as e:
94
+ print(f"์ „์ฒด JSON ํŒŒ์‹ฑ ์‹คํŒจ: {e}")
95
+
96
+ # 4. ์ง์ ‘ ํ…์ŠคํŠธ์—์„œ JD ๋‚ด์šฉ ์ถ”์ถœ ์‹œ๋„
97
+ print("์ง์ ‘ ํ…์ŠคํŠธ์—์„œ JD ๋‚ด์šฉ ์ถ”์ถœ ์‹œ๋„")
98
+
99
+ # "recommended_jd" ํ‚ค์›Œ๋“œ ๋’ค์˜ ๋‚ด์šฉ ์ฐพ๊ธฐ
100
+ jd_patterns = [
101
+ r'"recommended_jd"\s*:\s*"([^"]+)"',
102
+ r'recommended_jd["\s]*:\s*["\s]*([^"]+)["\s]*',
103
+ r'์ง๋ฌด๊ธฐ์ˆ ์„œ[:\s]*([^\n]+)',
104
+ ]
105
+
106
+ for pattern in jd_patterns:
107
+ match = re.search(pattern, cleaned_content, re.IGNORECASE)
108
+ if match:
109
+ jd_content = match.group(1).strip()
110
+ if len(jd_content) > 10: # ์ตœ์†Œ ๊ธธ์ด ์ฒดํฌ
111
+ return jd_content
112
+
113
+ # 5. ์ตœํ›„์˜ ์ˆ˜๋‹จ: ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JD๋กœ ๊ฐ„์ฃผ (JSON ๋งˆ์ปค ์ œ๊ฑฐ)
114
+ print("์ „์ฒด ํ…์ŠคํŠธ๋ฅผ JD๋กœ ๊ฐ„์ฃผ")
115
+ cleaned_text = re.sub(r'```[a-z]*\s*', '', cleaned_content)
116
+ cleaned_text = re.sub(r'```\s*', '', cleaned_text)
117
+ cleaned_text = re.sub(r'\{.*?\}', '', cleaned_text, flags=re.DOTALL)
118
+ cleaned_text = cleaned_text.strip()
119
+
120
+ if len(cleaned_text) > 20:
121
+ return cleaned_text
122
+
123
+ return "์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค."
124
+
125
+ except Exception as e:
126
+ print(f"JD ํŒŒ์‹ฑ ์ „์ฒด ์˜ค๋ฅ˜: {e}")
127
+ print(f"ํŒŒ์‹ฑ ์‹คํŒจํ•œ ์ปจํ…์ธ : {repr(content)}")
128
+ return f"ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}"
129
+
130
+ def generate_jd_recommendation(job_title, company_name, experience_level):
131
+ """
132
+ OpenAI API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
133
+ """
134
+ try:
135
+ if not job_title or not company_name or not experience_level:
136
+ return "์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ชจ๋‘ ์ž…๋ ฅํ•ด์ฃผ์„ธ์š”.", ""
137
+
138
+ # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
139
+ prompt = prompt_template.format(
140
+ job_title=job_title,
141
+ company_name=company_name,
142
+ experience_level=experience_level
143
+ )
144
+
145
+ # OpenAI API ํ˜ธ์ถœ
146
+ response = client.chat.completions.create(
147
+ model="gpt-4o",
148
+ messages=[
149
+ {"role": "system", "content": "๋‹น์‹ ์€ ์ฑ„์šฉ ๊ณต๊ณ  ์ž‘์„ฑ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ JSON ํ˜•์‹์œผ๋กœ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ œ๊ณตํ•ด์ฃผ์„ธ์š”."},
150
+ {"role": "user", "content": prompt}
151
+ ],
152
+ temperature=0.3
153
+ )
154
+
155
+ content = response.choices[0].message.content
156
+ print(f"=== AI ์‘๋‹ต ์›๋ณธ ===")
157
+ print(content)
158
+ print(f"=== AI ์‘๋‹ต ๋ ===")
159
+
160
+ jd_content = parse_jd_recommendation(content)
161
+
162
+ if not jd_content or jd_content == "์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.":
163
+ return "์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.", ""
164
+
165
+ # ๊ฒฐ๊ณผ ํฌ๋งทํŒ…
166
+ result = f"""## ๐Ÿ“‹ {company_name} - {job_title} ์ง๋ฌด๊ธฐ์ˆ ์„œ
167
+
168
+ ### ๐Ÿ’ผ **์ถ”์ฒœ ์ง๋ฌด๊ธฐ์ˆ ์„œ**
169
+
170
+ {jd_content}
171
+
172
+ ---
173
+
174
+ ### ๐Ÿ“Š **์ง๋ฌด ์ •๋ณด ์š”์•ฝ**
175
+
176
+ **๐Ÿข ํšŒ์‚ฌ:** {company_name}
177
+ **๐Ÿ’ผ ์ง๋ฌด:** {job_title}
178
+ **๐Ÿ“ˆ ๊ฒฝ๋ ฅ:** {experience_level}
179
+
180
+ ### ๐Ÿ’ก **์ž์†Œ์„œ ์ž‘์„ฑ ํŒ**
181
+
182
+ ์œ„ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋‹ค์Œ ์‚ฌํ•ญ์„ ์ž์†Œ์„œ์— ๋ฐ˜์˜ํ•ด๋ณด์„ธ์š”:
183
+
184
+ 1. **ํ•ต์‹ฌ ์—…๋ฌด**: ์–ธ๊ธ‰๋œ ์ฃผ์š” ์—…๋ฌด์™€ ๊ด€๋ จ๋œ ๊ฒฝํ—˜์ด๋‚˜ ์—ญ๋Ÿ‰์„ ๊ฐ•์กฐ
185
+ 2. **์š”๊ตฌ ์Šคํ‚ฌ**: ํ•„์š”ํ•œ ๊ธฐ์ˆ ์ด๋‚˜ ๋Šฅ๋ ฅ์— ๋Œ€ํ•œ ๋ณธ์ธ์˜ ์ค€๋น„๋„๋ฅผ ์–ดํ•„
186
+ 3. **ํšŒ์‚ฌ ํŠน์„ฑ**: ํ•ด๋‹น ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ์—ฐ๊ด€๋œ ๊ด€์‹ฌ์‚ฌ๋‚˜ ๊ฒฝํ—˜์„ ์–ธ๊ธ‰
187
+ 4. **์„ฑ์žฅ ์˜์ง€**: ์ง๋ฌด์—์„œ ์š”๊ตฌํ•˜๋Š” ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ๊ณผ ํ•™์Šต ์˜์ง€๋ฅผ ํ‘œํ˜„
188
+
189
+ ---
190
+
191
+ *๋ณธ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋Š” AI๊ฐ€ ์ƒ์„ฑํ•œ ๊ฒƒ์œผ๋กœ, ์‹ค์ œ ์ฑ„์šฉ๊ณต๊ณ ์™€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์†Œ์„œ ์ž‘์„ฑ ์‹œ ์ฐธ๊ณ ์šฉ์œผ๋กœ ํ™œ์šฉํ•˜์„ธ์š”.*
192
+ """
193
+
194
+ return result, jd_content
195
+
196
+ except Exception as e:
197
+ error_msg = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
198
+
199
+ ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
200
+
201
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
202
+
203
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
204
+ """
205
+ return error_msg, ""
jd-recommendation/main.py ADDED
@@ -0,0 +1,329 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from llm_functions import generate_jd_recommendation
3
+
4
+ # ์˜ˆ์ œ ๋ฐ์ดํ„ฐ
5
+ example_companies = ["์‚ผ์„ฑ์ „์ž", "์ฟ ํŒก", "ํ•˜์ด๋ธŒ", "ํ† ์Šค", "ํ˜„๋Œ€๊ฑด์„ค", "์‹ ํ•œ์€ํ–‰", "๋„ฅ์Šจ", "์•„๋ชจ๋ ˆํผ์‹œํ”ฝ", "์‚ผ์„ฑ๋ฐ”์ด์˜ค๋กœ์ง์Šค", "HMM"]
6
+ example_jobs = ["๊ฒฝ์˜๊ธฐํš", "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…", "HRM(์ธ์‚ฌ์šด์˜)", "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "ํ•ด์™ธ์˜์—…", "๋ฐ์ดํ„ฐ ๋ถ„์„", "์˜์—…", "๋งˆ์ผ€ํŒ…", "๊ธฐํš", "๊ฐœ๋ฐœ"]
7
+ experience_levels = ["์‹ ์ž…", "๊ฒฝ๋ ฅ", "์ธํ„ด", "๊ธฐํƒ€"]
8
+
9
+ def create_jd_card(jd_content):
10
+ """
11
+ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์นด๋“œ ํ˜•ํƒœ๋กœ ํ‘œ์‹œํ•˜๋Š” HTML ์ƒ์„ฑ
12
+ """
13
+ if not jd_content:
14
+ return "<div style='text-align: center; color: #6B7280; padding: 20px;'>์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>"
15
+
16
+ # ๋ฌธ์žฅ ๋‹จ์œ„๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ ๋” ์ฝ๊ธฐ ์‰ฝ๊ฒŒ ํ‘œ์‹œ
17
+ sentences = jd_content.split('. ')
18
+ formatted_content = ""
19
+
20
+ for i, sentence in enumerate(sentences):
21
+ if sentence.strip():
22
+ # ๋งˆ์ง€๋ง‰ ๋ฌธ์žฅ์ด ์•„๋‹ˆ๋ฉด ๋งˆ์นจํ‘œ ์ถ”๊ฐ€
23
+ if i < len(sentences) - 1 and not sentence.endswith('.'):
24
+ sentence += '.'
25
+ formatted_content += f"<p style='margin: 10px 0; line-height: 1.6;'>{sentence.strip()}</p>"
26
+
27
+ return f"""
28
+ <div style="
29
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
30
+ color: white;
31
+ border-radius: 15px;
32
+ padding: 25px;
33
+ margin: 15px 0;
34
+ box-shadow: 0 8px 25px rgba(0,0,0,0.15);
35
+ border: 1px solid rgba(255,255,255,0.1);
36
+ ">
37
+ <div style="
38
+ display: flex;
39
+ align-items: center;
40
+ margin-bottom: 20px;
41
+ padding-bottom: 15px;
42
+ border-bottom: 1px solid rgba(255,255,255,0.2);
43
+ ">
44
+ <div style="
45
+ background: rgba(255,255,255,0.2);
46
+ border-radius: 50%;
47
+ width: 40px;
48
+ height: 40px;
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ margin-right: 15px;
53
+ font-size: 18px;
54
+ ">๐Ÿ“‹</div>
55
+ <h3 style="margin: 0; font-size: 20px; font-weight: bold;">์ถ”์ฒœ ์ง๋ฌด๊ธฐ์ˆ ์„œ</h3>
56
+ </div>
57
+
58
+ <div style="
59
+ background: rgba(255,255,255,0.1);
60
+ border-radius: 12px;
61
+ padding: 20px;
62
+ backdrop-filter: blur(10px);
63
+ ">
64
+ {formatted_content}
65
+ </div>
66
+
67
+ <div style="
68
+ margin-top: 20px;
69
+ padding-top: 15px;
70
+ border-top: 1px solid rgba(255,255,255,0.2);
71
+ font-size: 14px;
72
+ opacity: 0.8;
73
+ text-align: center;
74
+ ">
75
+ ๐Ÿ’ก ์ด ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ๋งž์ถคํ˜• ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ž‘์„ฑํ•ด๋ณด์„ธ์š”
76
+ </div>
77
+ </div>
78
+ """
79
+
80
+ def create_tips_card():
81
+ """
82
+ ์ž์†Œ์„œ ์ž‘์„ฑ ํŒ ์นด๋“œ ์ƒ์„ฑ
83
+ """
84
+ return """
85
+ <div style="
86
+ background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
87
+ color: white;
88
+ border-radius: 15px;
89
+ padding: 25px;
90
+ margin: 15px 0;
91
+ box-shadow: 0 8px 25px rgba(0,0,0,0.15);
92
+ ">
93
+ <h3 style="margin: 0 0 20px 0; font-size: 18px; display: flex; align-items: center;">
94
+ <span style="margin-right: 10px;">๐Ÿ’ก</span>
95
+ ์ž์†Œ์„œ ์ž‘์„ฑ ํŒ
96
+ </h3>
97
+
98
+ <div style="background: rgba(255,255,255,0.1); border-radius: 10px; padding: 20px;">
99
+ <div style="margin-bottom: 15px;">
100
+ <strong>1. ํ•ต์‹ฌ ์—…๋ฌด ๋งค์นญ</strong><br>
101
+ <span style="font-size: 14px; opacity: 0.9;">์ง๋ฌด๊ธฐ์ˆ ์„œ์˜ ์ฃผ์š” ์—…๋ฌด์™€ ๊ด€๋ จ๋œ ๊ฒฝํ—˜์„ ๊ตฌ์ฒด์ ์œผ๋กœ ์„œ์ˆ </span>
102
+ </div>
103
+
104
+ <div style="margin-bottom: 15px;">
105
+ <strong>2. ์š”๊ตฌ ์—ญ๋Ÿ‰ ์–ดํ•„</strong><br>
106
+ <span style="font-size: 14px; opacity: 0.9;">์–ธ๊ธ‰๋œ ์Šคํ‚ฌ๊ณผ ์—ญ๋Ÿ‰์— ๋Œ€ํ•œ ๋ณธ์ธ์˜ ์ค€๋น„๋„๋ฅผ ์ฆ๋ช…</span>
107
+ </div>
108
+
109
+ <div style="margin-bottom: 15px;">
110
+ <strong>3. ํšŒ์‚ฌ ํŠน์„ฑ ๋ฐ˜์˜</strong><br>
111
+ <span style="font-size: 14px; opacity: 0.9;">ํ•ด๋‹น ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ์—ฐ๊ด€๋œ ๊ด€์‹ฌ์‚ฌ๋‚˜ ๊ฒฝํ—˜์„ ํฌํ•จ</span>
112
+ </div>
113
+
114
+ <div>
115
+ <strong>4. ์„ฑ์žฅ ์˜์ง€ ํ‘œํ˜„</strong><br>
116
+ <span style="font-size: 14px; opacity: 0.9;">์ง๋ฌด์—์„œ ์š”๊ตฌํ•˜๋Š” ์„ฑ์žฅ ๊ฐ€๋Šฅ์„ฑ๊ณผ ํ•™์Šต ์˜์ง€๋ฅผ ๊ฐ•์กฐ</span>
117
+ </div>
118
+ </div>
119
+ </div>
120
+ """
121
+
122
+ def process_jd_generation(job_title, company_name, experience_level):
123
+ """
124
+ ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ๊ฒฐ๊ณผ๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  UI์— ํ‘œ์‹œํ•  ํ˜•ํƒœ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ํ•จ์ˆ˜
125
+ """
126
+ try:
127
+ content, jd_content = generate_jd_recommendation(job_title, company_name, experience_level)
128
+ jd_card = create_jd_card(jd_content)
129
+ tips_card = create_tips_card()
130
+ return content, jd_card + tips_card
131
+ except Exception as e:
132
+ error_content = f"""## โŒ ์˜ค๋ฅ˜ ๋ฐœ์ƒ
133
+
134
+ ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.
135
+
136
+ **์˜ค๋ฅ˜ ๋‚ด์šฉ:** {str(e)}
137
+
138
+ ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”.
139
+ """
140
+ error_card = create_jd_card("")
141
+ return error_content, error_card
142
+
143
+ def create_interface():
144
+ """
145
+ Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
146
+ """
147
+ with gr.Blocks(
148
+ title="๐Ÿ“‹ AI ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ๊ธฐ",
149
+ theme=gr.themes.Soft(),
150
+ css="""
151
+ .main-header {
152
+ text-align: center;
153
+ padding: 20px;
154
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
155
+ color: white;
156
+ border-radius: 10px;
157
+ margin-bottom: 20px;
158
+ }
159
+ .input-section {
160
+ background-color: #f8f9fa;
161
+ padding: 20px;
162
+ border-radius: 8px;
163
+ margin: 10px 0;
164
+ }
165
+ .example-section {
166
+ background-color: #f0f9ff;
167
+ padding: 15px;
168
+ border-radius: 8px;
169
+ margin: 10px 0;
170
+ }
171
+ """
172
+ ) as demo:
173
+
174
+ # ํ—ค๋”
175
+ gr.HTML("""
176
+ <div class="main-header">
177
+ <h1>๐Ÿ“‹ AI ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ๊ธฐ</h1>
178
+ <p>๋งž์ถคํ˜• ์ง๋ฌด๊ธฐ์ˆ ์„œ๋กœ ์™„๋ฒฝํ•œ ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”</p>
179
+ </div>
180
+ """)
181
+
182
+ # ์„ค๋ช…
183
+ gr.Markdown("""
184
+ ### ๐Ÿš€ **์‚ฌ์šฉ ๋ฐฉ๋ฒ•**
185
+ 1. **์ง๋ฌด**: ์ง€์›ํ•˜๊ณ ์ž ํ•˜๋Š” ์ง๋ฌด๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”
186
+ 2. **ํšŒ์‚ฌ๋ช…**: ์ง€์› ํšŒ์‚ฌ๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š”
187
+ 3. **๊ฒฝ๋ ฅ ์ˆ˜์ค€**: ์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด/๊ธฐํƒ€ ์ค‘ ์„ ํƒํ•˜์„ธ์š”
188
+ 4. **์ƒ์„ฑ**: '์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜์„ธ์š”
189
+
190
+ โœจ **ํŠน์ง•**: ์‹ค์ œ ์ฑ„์šฉ๊ณต๊ณ  ์Šคํƒ€์ผ์˜ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•˜์—ฌ ์ž์†Œ์„œ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ํ•ต์‹ฌ ์ •๋ณด๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
191
+ """)
192
+
193
+ with gr.Row():
194
+ with gr.Column(scale=2):
195
+ # ์ž…๋ ฅ ์„น์…˜
196
+ gr.HTML('<div class="input-section">')
197
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
198
+
199
+ with gr.Row():
200
+ job_input = gr.Textbox(
201
+ label="๐Ÿ’ผ ์ง๋ฌด",
202
+ placeholder="์˜ˆ: ๊ฒฝ์˜๊ธฐํš, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ… ๋“ฑ",
203
+ value="",
204
+ scale=1
205
+ )
206
+
207
+ company_input = gr.Textbox(
208
+ label="๐Ÿข ํšŒ์‚ฌ๋ช…",
209
+ placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ",
210
+ value="",
211
+ scale=1
212
+ )
213
+
214
+ experience_input = gr.Dropdown(
215
+ label="๐Ÿ“ˆ ๊ฒฝ๋ ฅ ์ˆ˜์ค€",
216
+ choices=experience_levels,
217
+ value="์‹ ์ž…",
218
+ interactive=True
219
+ )
220
+
221
+ generate_btn = gr.Button(
222
+ "๐Ÿ“‹ ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ",
223
+ variant="primary",
224
+ size="lg"
225
+ )
226
+ gr.HTML('</div>')
227
+
228
+ with gr.Column(scale=1):
229
+ # ์˜ˆ์ œ ๋ฐ ๊ฐ€์ด๋“œ
230
+ gr.HTML('<div class="example-section">')
231
+ gr.Markdown("### ๐Ÿ’ก **์˜ˆ์ œ ํšŒ์‚ฌ**")
232
+
233
+ company_rows = [example_companies[i:i+2] for i in range(0, len(example_companies), 2)]
234
+ for row in company_rows:
235
+ with gr.Row():
236
+ for company in row:
237
+ example_btn = gr.Button(
238
+ company,
239
+ size="sm",
240
+ variant="secondary",
241
+ scale=1
242
+ )
243
+ example_btn.click(
244
+ fn=lambda x=company: x,
245
+ outputs=company_input
246
+ )
247
+
248
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ์ง๋ฌด**")
249
+
250
+ job_rows = [example_jobs[i:i+2] for i in range(0, len(example_jobs), 2)]
251
+ for row in job_rows:
252
+ with gr.Row():
253
+ for job in row:
254
+ job_btn = gr.Button(
255
+ job,
256
+ size="sm",
257
+ variant="secondary",
258
+ scale=1
259
+ )
260
+ job_btn.click(
261
+ fn=lambda x=job: x,
262
+ outputs=job_input
263
+ )
264
+
265
+ gr.Markdown("### ๐Ÿ“ˆ **๊ฒฝ๋ ฅ ์ˆ˜์ค€**")
266
+ with gr.Row():
267
+ for level in experience_levels:
268
+ level_btn = gr.Button(
269
+ level,
270
+ size="sm",
271
+ variant="secondary",
272
+ scale=1
273
+ )
274
+ level_btn.click(
275
+ fn=lambda x=level: x,
276
+ outputs=experience_input
277
+ )
278
+
279
+ gr.HTML('</div>')
280
+
281
+ # ๊ฒฐ๊ณผ ์ถœ๋ ฅ ์„น์…˜
282
+ with gr.Row():
283
+ with gr.Column(scale=2):
284
+ gr.Markdown("### ๐Ÿ“‹ **์ƒ์„ธ ์ง๋ฌด๊ธฐ์ˆ ์„œ**")
285
+ result_output = gr.Markdown(
286
+ value="์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ์ž…๋ ฅํ•˜๊ณ  '์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
287
+ elem_classes=["result-output"]
288
+ )
289
+
290
+ with gr.Column(scale=1):
291
+ gr.Markdown("### ๐Ÿ’ผ **์ง๋ฌด๊ธฐ์ˆ ์„œ & ํŒ**")
292
+ jd_cards = gr.HTML(
293
+ value="<div style='text-align: center; color: #6B7280; padding: 20px;'>์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”</div>",
294
+ elem_classes=["jd-cards"]
295
+ )
296
+
297
+ # ์ƒ์„ฑ ๋ฒ„ํŠผ ํด๋ฆญ ์ด๋ฒคํŠธ
298
+ generate_btn.click(
299
+ fn=process_jd_generation,
300
+ inputs=[job_input, company_input, experience_input],
301
+ outputs=[result_output, jd_cards],
302
+ api_name="generate_jd_recommendation"
303
+ )
304
+
305
+ # ํ‘ธํ„ฐ
306
+ gr.Markdown("""
307
+ ---
308
+ **๐Ÿ“‹ ์ง๋ฌด๊ธฐ์ˆ ์„œ ํŠน์ง•**:
309
+ - **ํ˜„์‹ค์ ์ธ ๋‚ด์šฉ**: ์‹ค์ œ ์ฑ„์šฉ๊ณต๊ณ  ์Šคํƒ€์ผ์˜ ์ง๋ฌด ์„ค๋ช…
310
+ - **๊ฒฝ๋ ฅ๋ณ„ ๋งž์ถค**: ์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด/๊ธฐํƒ€์— ๋”ฐ๋ฅธ ์ ์ ˆํ•œ ์ˆ˜์ค€์˜ ์š”๊ตฌ์‚ฌํ•ญ
311
+ - **ํšŒ์‚ฌ ํŠน์„ฑ ๋ฐ˜์˜**: ํ•ด๋‹น ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ํŠน์„ฑ์„ ๊ณ ๋ คํ•œ ๋‚ด์šฉ
312
+ - **์ž์†Œ์„œ ์—ฐ๊ณ„**: ์ž๊ธฐ์†Œ๊ฐœ์„œ ์ž‘์„ฑ์— ์ง์ ‘ ํ™œ์šฉ ๊ฐ€๋Šฅํ•œ ๊ตฌ์ฒด์  ์ •๋ณด
313
+
314
+ ๐Ÿ’ก **ํ™œ์šฉ ๋ฐฉ๋ฒ•**: ์ƒ์„ฑ๋œ ์ง๋ฌด๊ธฐ์ˆ ์„œ์˜ ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ์™€ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ž์†Œ์„œ์— ๋ฐ˜์˜ํ•˜์—ฌ ๋งž์ถคํ˜• ์ง€์›์„œ๋ฅผ ์ž‘์„ฑํ•˜์„ธ์š”.
315
+
316
+ ๐Ÿค– **Powered by**: OpenAI GPT-4o
317
+ """)
318
+
319
+ return demo
320
+
321
+ if __name__ == "__main__":
322
+ # Gradio ์•ฑ ์‹คํ–‰
323
+ demo = create_interface()
324
+ demo.launch(
325
+ server_name="0.0.0.0",
326
+ server_port=7863,
327
+ share=True,
328
+ show_error=True
329
+ )
jd-recommendation/prompt.yaml ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ prompt: |
2
+ ๋‹น์‹ ์€ ์ฑ„์šฉ ๊ณต๊ณ  ์ž‘์„ฑ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
3
+ ์ฃผ์–ด์ง„ ์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•ด๋‹น ํฌ์ง€์…˜์— ๋Œ€ํ•œ ๋Œ€ํ‘œ์ ์ธ ์ง๋ฌด๊ธฐ์ˆ ์„œ(Job Description)๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
4
+
5
+ ### ์ง€์นจ
6
+ 1. **ํ˜„์‹ค์ ์ธ ๋‚ด์šฉ**: ์‹ค์ œ ์ฑ„์šฉ ๊ณต๊ณ ์—์„œ ๋ณผ ์ˆ˜ ์žˆ๋Š” ํ˜„์‹ค์ ์ด๊ณ  ๊ตฌ์ฒด์ ์ธ ๋‚ด์šฉ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
7
+ 2. **๊ฒฝ๋ ฅ ์ˆ˜์ค€ ๋ฐ˜์˜**: ์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด/๊ธฐํƒ€์— ๋”ฐ๋ผ ์ ์ ˆํ•œ ์ˆ˜์ค€์˜ ์—…๋ฌด์™€ ์š”๊ตฌ์‚ฌํ•ญ์„ ์ œ์‹œํ•ฉ๋‹ˆ๋‹ค.
8
+ 3. **๊ฐ„๊ฒฐํ•˜๊ณ  ๋ช…ํ™•**: ์ž์†Œ์„œ ์ž‘์„ฑ์— ๋„์›€์ด ๋˜๋„๋ก ํ•ต์‹ฌ ๋‚ด์šฉ์„ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ •๋ฆฌํ•ฉ๋‹ˆ๋‹ค.
9
+ 4. **ํšŒ์‚ฌ ํŠน์„ฑ ๋ฐ˜์˜**: ํ•ด๋‹น ํšŒ์‚ฌ์˜ ์‚ฌ์—… ์˜์—ญ๊ณผ ํŠน์„ฑ์„ ๊ณ ๋ คํ•œ ์ง๋ฌด ๋‚ด์šฉ์„ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.
10
+
11
+ ### ์ž…๋ ฅ ์ •๋ณด
12
+ - **์ง๋ฌด:** {job_title}
13
+ - **ํšŒ์‚ฌ๋ช…:** {company_name}
14
+ - **๊ฒฝ๋ ฅ ์ˆ˜์ค€:** {experience_level}
15
+
16
+ ### ์ถœ๋ ฅ ํ˜•์‹
17
+ ๋ฐ˜๋“œ์‹œ ๋‹ค์Œ JSON ํ˜•์‹์œผ๋กœ๋งŒ ์‘๋‹ตํ•˜์„ธ์š”:
18
+
19
+ ```json
20
+ {{
21
+ "recommended_jd": "์ง๋ฌด๊ธฐ์ˆ ์„œ ๋‚ด์šฉ"
22
+ }}
23
+ ```
24
+
25
+ ### ์ž‘์„ฑ ์š”๊ตฌ์‚ฌํ•ญ
26
+
27
+ **์ง๋ฌด๊ธฐ์ˆ ์„œ ๋‚ด์šฉ (recommended_jd)**
28
+ - 2-4๋ฌธ์žฅ์œผ๋กœ ๊ตฌ์„ฑ๋œ ๊ฐ„๊ฒฐํ•œ ์„ค๋ช…
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
+ ```json
64
+ {{
65
+ "recommended_jd": "์ „์‚ฌ ๊ฒฝ์˜ ๋ชฉํ‘œ ๋‹ฌ์„ฑ์„ ์œ„ํ•œ ๋ฐ์ดํ„ฐ ๋ถ„์„, ์‹œ์žฅ ๋ฆฌ์„œ์น˜, ์‚ฌ์—… ์ „๋žต ์ˆ˜๋ฆฝ ์ง€์› ์—…๋ฌด๋ฅผ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์‹ ๊ทœ ์‚ฌ์—… ๊ธฐํšŒ๋ฅผ ๋ฐœ๊ตดํ•˜๊ณ , ๊ฒฝ์˜์ง„์˜ ์˜์‚ฌ๊ฒฐ์ •์„ ๋•๊ธฐ ์œ„ํ•œ ๋ณด๊ณ ์„œ ์ž‘์„ฑ ๋ฐ ๋ฐœํ‘œ๋ฅผ ๋‹ด๋‹นํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋›ฐ์–ด๋‚œ ๋ถ„์„๋ ฅ๊ณผ ๊ธฐํš๋ ฅ์„ ๊ฐ–์ถ˜ ์ธ์žฌ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค."
66
+ }}
67
+ ```
68
+
69
+ ์ด์ œ ๋‹ค์Œ ์ž…๋ ฅ์— ๋Œ€ํ•œ ์ง๋ฌด๊ธฐ์ˆ ์„œ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
jd-recommendation/propmt.yaml ADDED
File without changes
main.py ADDED
@@ -0,0 +1,474 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import sys
4
+ import importlib.util
5
+ from pathlib import Path
6
+ import dotenv
7
+
8
+ dotenv.load_dotenv()
9
+
10
+ # ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ ์„ค์ •
11
+ current_dir = Path(__file__).parent
12
+
13
+ # ๊ฐ ๊ธฐ๋Šฅ๋ณ„ ๋ชจ๋“ˆ ๋กœ๋“œ ํ•จ์ˆ˜
14
+ def load_module_from_path(module_name, file_path):
15
+ """๋™์ ์œผ๋กœ ๋ชจ๋“ˆ์„ ๋กœ๋“œํ•˜๋Š” ํ•จ์ˆ˜"""
16
+ try:
17
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
18
+ module = importlib.util.module_from_spec(spec)
19
+ spec.loader.exec_module(module)
20
+ return module
21
+ except Exception as e:
22
+ print(f"๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ {module_name}: {e}")
23
+ return None
24
+
25
+ # ๊ฐ ๊ธฐ๋Šฅ๋ณ„ ๋ชจ๋“ˆ ๋ฐ ํ•จ์ˆ˜ ๋กœ๋“œ
26
+ modules = {}
27
+ available_features = {}
28
+
29
+ # 1. commonly-asked-question
30
+ try:
31
+ llm_functions_path = current_dir / "commonly-asked-question" / "llm_functions.py"
32
+ if llm_functions_path.exists():
33
+ modules['commonly_asked'] = load_module_from_path("commonly_asked_llm", llm_functions_path)
34
+ available_features['commonly_asked'] = modules['commonly_asked'] is not None
35
+ else:
36
+ available_features['commonly_asked'] = False
37
+ except Exception as e:
38
+ print(f"commonly-asked-question ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
39
+ available_features['commonly_asked'] = False
40
+
41
+ # 2. question-recommendation
42
+ try:
43
+ llm_functions_path = current_dir / "question-recommendation" / "llm_functions.py"
44
+ if llm_functions_path.exists():
45
+ modules['question_rec'] = load_module_from_path("question_rec_llm", llm_functions_path)
46
+ available_features['question_rec'] = modules['question_rec'] is not None
47
+ else:
48
+ available_features['question_rec'] = False
49
+ except Exception as e:
50
+ print(f"question-recommendation ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
51
+ available_features['question_rec'] = False
52
+
53
+ # 3. jd-recommendation
54
+ try:
55
+ llm_functions_path = current_dir / "jd-recommendation" / "llm_functions.py"
56
+ if llm_functions_path.exists():
57
+ modules['jd_rec'] = load_module_from_path("jd_rec_llm", llm_functions_path)
58
+ available_features['jd_rec'] = modules['jd_rec'] is not None
59
+ else:
60
+ available_features['jd_rec'] = False
61
+ except Exception as e:
62
+ print(f"jd-recommendation ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
63
+ available_features['jd_rec'] = False
64
+
65
+ # 4. industry-classification
66
+ try:
67
+ llm_functions_path = current_dir / "industry-classification" / "llm_functions.py"
68
+ if llm_functions_path.exists():
69
+ modules['industry'] = load_module_from_path("industry_llm", llm_functions_path)
70
+ available_features['industry'] = modules['industry'] is not None
71
+ else:
72
+ available_features['industry'] = False
73
+ except Exception as e:
74
+ print(f"industry-classification ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
75
+ available_features['industry'] = False
76
+
77
+ # 5. jasoseo-context-report
78
+ try:
79
+ llm_functions_path = current_dir / "jasoseo-context-report" / "llm_functions.py"
80
+ if llm_functions_path.exists():
81
+ modules['jasoseo'] = load_module_from_path("jasoseo_llm", llm_functions_path)
82
+ available_features['jasoseo'] = modules['jasoseo'] is not None
83
+ else:
84
+ available_features['jasoseo'] = False
85
+ except Exception as e:
86
+ print(f"jasoseo-context-report ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
87
+ available_features['jasoseo'] = False
88
+
89
+ # 6. company-size-classification
90
+ try:
91
+ llm_functions_path = current_dir / "company-size-classification" / "llm_functions.py"
92
+ if llm_functions_path.exists():
93
+ modules['company_size'] = load_module_from_path("company_size_llm", llm_functions_path)
94
+ available_features['company_size'] = modules['company_size'] is not None
95
+ else:
96
+ available_features['company_size'] = False
97
+ except Exception as e:
98
+ print(f"company-size-classification ๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ: {e}")
99
+ available_features['company_size'] = False
100
+
101
+ # OpenAI ๊ด€๋ จ ๋ชจ๋“ˆ
102
+ try:
103
+ from openai import OpenAI
104
+ import yaml
105
+ openai_available = True
106
+ except ImportError:
107
+ openai_available = False
108
+
109
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
110
+ client = None
111
+ if openai_available and os.getenv("OPENAI_API_KEY"):
112
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
113
+
114
+ # ๊ณตํ†ต CSS ์Šคํƒ€์ผ
115
+ common_css = """
116
+ .main-header {
117
+ text-align: center;
118
+ padding: 20px;
119
+ background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
120
+ color: white;
121
+ border-radius: 10px;
122
+ margin-bottom: 20px;
123
+ }
124
+ .input-section {
125
+ background-color: #f8f9fa;
126
+ padding: 20px;
127
+ border-radius: 8px;
128
+ margin: 10px 0;
129
+ }
130
+ .example-section {
131
+ background-color: #f0f9ff;
132
+ padding: 15px;
133
+ border-radius: 8px;
134
+ margin: 10px 0;
135
+ }
136
+ .tab-container {
137
+ margin: 20px 0;
138
+ height: 100%;
139
+ }
140
+ """
141
+
142
+ # ์˜ˆ์ œ ๋ฐ์ดํ„ฐ
143
+ example_companies = ["์‚ผ์„ฑ์ „์ž", "ํ† ์Šค", "์นด์นด์˜ค", "๋„ค์ด๋ฒ„", "LG์ „์ž", "ํ˜„๋Œ€์ž๋™์ฐจ", "CJ์ œ์ผ์ œ๋‹น", "ํ•˜์ด๋ธŒ", "์ฟ ํŒก", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ", "์‹ ํ•œ์€ํ–‰", "SKT", "ํฌ์Šค์ฝ”", "ํ˜„๋Œ€๊ฑด์„ค", "์•„๋ชจ๋ ˆํผ์‹œํ”ฝ"]
144
+ example_jobs = ["๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "ํ”„๋ก ํŠธ์—”๋“œ ๊ฐœ๏ฟฝ๏ฟฝ๏ฟฝ", "๋ฐ์ดํ„ฐ ์‚ฌ์ด์–ธํ‹ฐ์ŠคํŠธ", "๋งˆ์ผ€ํŒ…", "์˜์—…", "๊ธฐํš", "๋””์ž์ธ", "HR", "์žฌ๋ฌด", "A&R", "๊ฒฝ์˜๊ธฐํš", "ํ•ด์™ธ์˜์—…", "์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ…", "์‹ํ’ˆ๋งˆ์ผ€ํŒ…", "HRM(์ธ์‚ฌ์šด์˜)"]
145
+ experience_levels = ["์‹ ์ž…", "๊ฒฝ๋ ฅ", "์ธํ„ด", "๊ธฐํƒ€"]
146
+
147
+ # ๊ณตํ†ต ํ•จ์ˆ˜๋“ค
148
+ def create_example_buttons(companies, jobs, company_input, job_input):
149
+ """์˜ˆ์ œ ๋ฒ„ํŠผ๋“ค์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜"""
150
+
151
+ gr.Markdown("### ๐Ÿ’ก **์˜ˆ์ œ ํšŒ์‚ฌ**")
152
+ with gr.Row():
153
+ for company in companies[:5]:
154
+ btn = gr.Button(company, size="sm", variant="secondary")
155
+ btn.click(fn=lambda x=company: x, outputs=company_input)
156
+
157
+ with gr.Row():
158
+ for company in companies[5:10]:
159
+ btn = gr.Button(company, size="sm", variant="secondary")
160
+ btn.click(fn=lambda x=company: x, outputs=company_input)
161
+
162
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ์ง๋ฌด**")
163
+ with gr.Row():
164
+ for job in jobs[:5]:
165
+ btn = gr.Button(job, size="sm", variant="secondary")
166
+ btn.click(fn=lambda x=job: x, outputs=job_input)
167
+
168
+ with gr.Row():
169
+ for job in jobs[5:10]:
170
+ btn = gr.Button(job, size="sm", variant="secondary")
171
+ btn.click(fn=lambda x=job: x, outputs=job_input)
172
+
173
+ # 1. ์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ ํƒญ
174
+ def create_commonly_asked_tab():
175
+ if not available_features.get('commonly_asked'):
176
+ gr.Markdown("โŒ **์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ)")
177
+ return
178
+ gr.HTML("""
179
+ <div class="main-header">
180
+ <h2>๐ŸŽฏ ์ผ๋ฐ˜์ ์ธ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ</h2>
181
+ <p>ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ AI๊ฐ€ ์ƒ์„ฑํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค</p>
182
+ </div>
183
+ """)
184
+
185
+ with gr.Row():
186
+ with gr.Column(scale=2):
187
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
188
+
189
+ company_input = gr.Textbox(label="๐Ÿข ํšŒ์‚ฌ๋ช…", placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ")
190
+ job_input = gr.Textbox(label="๐Ÿ’ผ ์ง๋ฌด", placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๋งˆ์ผ€ํŒ…, ๊ธฐํš ๋“ฑ")
191
+
192
+ with gr.Row():
193
+ experience_input = gr.Dropdown(label="๐Ÿ“Š ๊ฒฝ๋ ฅ ์ˆ˜์ค€", choices=experience_levels, value="์‹ ์ž…", interactive=True)
194
+ num_questions_input = gr.Dropdown(label="๐Ÿ“‹ ์ƒ์„ฑํ•  ์งˆ๋ฌธ ์ˆ˜", choices=[1, 2, 3, 4, 5], value=3, interactive=True)
195
+
196
+ common_questions_list = [
197
+ "์ž๊ธฐ์†Œ๊ฐœ๋ฅผ ํ•ด๋ณด์„ธ์š”", "์ง€์› ๋™๊ธฐ๊ฐ€ ๋ฌด์—‡์ธ๊ฐ€์š”", "๋ณธ์ธ์˜ ๊ฐ•์ ์€ ๋ฌด์—‡์ธ๊ฐ€์š”",
198
+ "๊ฐ€์žฅ ๋„์ „์ ์ธ ๊ฒฝํ—˜์€ ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ๊ณต ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”", "์‹คํŒจ ๊ฒฝํ—˜์„ ๋งํ•ด์ฃผ์„ธ์š”",
199
+ "์ž…์‚ฌ ํ›„ ํฌ๋ถ€๋Š” ๋ฌด์—‡์ธ๊ฐ€์š”", "์„ฑ๊ฒฉ์˜ ์žฅ๋‹จ์ ์„ ๋งํ•ด์ฃผ์„ธ์š”", "์กด๊ฒฝํ•˜๋Š” ์ธ๋ฌผ์€ ๋ˆ„๊ตฌ์ธ๊ฐ€์š”",
200
+ "๋งˆ์ง€๋ง‰์œผ๋กœ ํ•˜๊ณ  ์‹ถ์€ ๋ง์€?"
201
+ ]
202
+ selected_questions = gr.CheckboxGroup(label="๐Ÿ“ ์ฐธ๊ณ ํ•  ์ผ๋ฐ˜ ์งˆ๋ฌธ (์„ ํƒ)", choices=common_questions_list, value=common_questions_list[:3])
203
+
204
+ generate_btn = gr.Button("๐ŸŽฏ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ", variant="primary", size="lg")
205
+
206
+ with gr.Column(scale=1):
207
+ create_example_buttons(example_companies, example_jobs, company_input, job_input)
208
+
209
+ gr.Markdown("### ๐Ÿ“‹ **์ƒ์„ฑ๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ**")
210
+ result_output = gr.Markdown("์œ„ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.")
211
+
212
+ def process_question_generation(company, job, experience, selected, num):
213
+ try:
214
+ content, _ = modules['commonly_asked'].generate_interview_questions(company, job, experience, selected, num)
215
+ return content
216
+ except Exception as e:
217
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
218
+
219
+ generate_btn.click(
220
+ fn=process_question_generation,
221
+ inputs=[company_input, job_input, experience_input, selected_questions, num_questions_input],
222
+ outputs=result_output
223
+ )
224
+
225
+ # 2. ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ํƒญ
226
+ def create_question_recommendation_tab():
227
+ if not available_features.get('question_rec') or not openai_available:
228
+ gr.Markdown("โŒ **๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ ๋˜๋Š” API ํ‚ค ์—†์Œ)")
229
+ return
230
+ gr.HTML("""
231
+ <div class="main-header">
232
+ <h2>๐Ÿ’ก ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ</h2>
233
+ <p>์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ์ž…๋ ฅํ•˜๋ฉด AI๊ฐ€ ๋งž์ถคํ˜• ์งˆ๋ฌธ์„ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค</p>
234
+ </div>
235
+ """)
236
+
237
+ with gr.Row():
238
+ with gr.Column(scale=2):
239
+ gr.Markdown("### ๐Ÿ“ **์ •๋ณด ์ž…๋ ฅ**")
240
+ job_input = gr.Textbox(label="๐Ÿ’ผ ์ง๋ฌด", placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๋งˆ์ผ€ํŒ…, ๊ธฐํš ๋“ฑ")
241
+ company_input = gr.Textbox(label="๐Ÿข ํšŒ์‚ฌ๋ช…", placeholder="์˜ˆ: ํ† ์Šค, ์‚ผ์„ฑ์ „์ž, ๋„ค์ด๋ฒ„ ๋“ฑ")
242
+ experience_input = gr.Dropdown(label="๐Ÿ“Š ๊ฒฝ๋ ฅ ์ˆ˜์ค€", choices=experience_levels, value="์‹ ์ž…")
243
+ submit_btn = gr.Button("๐Ÿ’ก ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ๋ฐ›๊ธฐ", variant="primary", size="lg")
244
+
245
+ with gr.Column(scale=1):
246
+ create_example_buttons(example_companies, example_jobs, company_input, job_input)
247
+
248
+ gr.Markdown("### ๐Ÿ’ฌ **์ถ”์ฒœ ๋ฉด์ ‘ ์งˆ๋ฌธ**")
249
+ result_output = gr.Textbox(label="", placeholder="์ถ”์ฒœ๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ์ด ์—ฌ๊ธฐ์— ํ‘œ์‹œ๋ฉ๋‹ˆ๋‹ค.", lines=5, show_label=False, interactive=False)
250
+
251
+ def recommend_question(job, company, experience):
252
+ try:
253
+ if not client: return "โŒ OpenAI API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
254
+ prompt_path = current_dir / "question-recommendation" / "prompt.yaml"
255
+ with open(prompt_path, 'r', encoding='utf-8') as f:
256
+ prompts = yaml.safe_load(f)
257
+
258
+ result = modules['question_rec'].generate_question_recommendation(client, prompts, job, company, experience)
259
+ parsed = modules['question_rec'].parse_question_recommendation(result)
260
+ return parsed.get('recommended_question', "์งˆ๋ฌธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.")
261
+ except Exception as e:
262
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
263
+
264
+ submit_btn.click(fn=recommend_question, inputs=[job_input, company_input, experience_input], outputs=result_output)
265
+
266
+ # 3. ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ํƒญ
267
+ def create_jd_recommendation_tab():
268
+ if not available_features.get('jd_rec'):
269
+ gr.Markdown("โŒ **์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ)")
270
+ return
271
+ gr.HTML("""
272
+ <div class="main-header">
273
+ <h2>๐Ÿ“‹ AI ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ๊ธฐ</h2>
274
+ <p>๋งž์ถคํ˜• ์ง๋ฌด๊ธฐ์ˆ ์„œ๋กœ ์™„๋ฒฝํ•œ ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”</p>
275
+ </div>
276
+ """)
277
+
278
+ with gr.Row():
279
+ with gr.Column(scale=2):
280
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
281
+ job_input = gr.Textbox(label="๐Ÿ’ผ ์ง๋ฌด", placeholder="์˜ˆ: ๊ฒฝ์˜๊ธฐํš, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ… ๋“ฑ")
282
+ company_input = gr.Textbox(label="๐Ÿข ํšŒ์‚ฌ๋ช…", placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ")
283
+ experience_input = gr.Dropdown(label="๐Ÿ“ˆ ๊ฒฝ๋ ฅ ์ˆ˜์ค€", choices=experience_levels, value="์‹ ์ž…", interactive=True)
284
+ generate_btn = gr.Button("๐Ÿ“‹ ์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ", variant="primary", size="lg")
285
+
286
+ with gr.Column(scale=1):
287
+ create_example_buttons(example_companies, example_jobs, company_input, job_input)
288
+
289
+ gr.Markdown("### ๐Ÿ“œ **์ƒ์„ฑ๋œ ์ง๋ฌด๊ธฐ์ˆ ์„œ**")
290
+ result_output = gr.Markdown("์œ„ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '์ง๋ฌด๊ธฐ์ˆ ์„œ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.")
291
+
292
+ def process_jd_generation(job, company, experience):
293
+ try:
294
+ content, _ = modules['jd_rec'].generate_jd_recommendation(job, company, experience)
295
+ return content
296
+ except Exception as e:
297
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
298
+
299
+ generate_btn.click(fn=process_jd_generation, inputs=[job_input, company_input, experience_input], outputs=result_output)
300
+
301
+ # 4. ์‚ฐ์—… ๋ถ„๋ฅ˜ ํƒญ
302
+ def create_industry_classification_tab():
303
+ if not available_features.get('industry'):
304
+ gr.Markdown("โŒ **์‚ฐ์—… ๋ถ„๋ฅ˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ)")
305
+ return
306
+ gr.HTML("""
307
+ <div class="main-header">
308
+ <h2>๐Ÿท๏ธ AI ์‚ฐ์—… ๋ถ„๋ฅ˜๊ธฐ</h2>
309
+ <p>์ง๋ฌด์™€ ํšŒ์‚ฌ๋ช…์„ ๋ฐ”ํƒ•์œผ๋กœ ์ •ํ™•ํ•œ ์‚ฐ์—… ๋ถ„์•ผ๋ฅผ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค</p>
310
+ </div>
311
+ """)
312
+
313
+ with gr.Row():
314
+ with gr.Column(scale=2):
315
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
316
+ job_input = gr.Textbox(label="๐Ÿ’ผ ์ง๋ฌด", placeholder="์˜ˆ: ๊ฒฝ์˜๊ธฐํš, ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ์˜จ๋ผ์ธ๋งˆ์ผ€ํŒ… ๋“ฑ")
317
+ company_input = gr.Textbox(label="๐Ÿข ํšŒ์‚ฌ๋ช…", placeholder="์˜ˆ: ์‚ผ์„ฑ์ „์ž, ํ† ์Šค, ์นด์นด์˜ค ๋“ฑ")
318
+ classify_btn = gr.Button("๐Ÿท๏ธ ์‚ฐ์—… ๋ถ„๋ฅ˜", variant="primary", size="lg")
319
+
320
+ with gr.Column(scale=1):
321
+ create_example_buttons(example_companies, example_jobs, company_input, job_input)
322
+
323
+ gr.Markdown("### ๐Ÿ“‘ **๋ถ„๋ฅ˜ ๊ฒฐ๊ณผ**")
324
+ result_output = gr.Markdown("์œ„ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '์‚ฐ์—… ๋ถ„๋ฅ˜' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.")
325
+
326
+ def process_classification(job, company):
327
+ try:
328
+ content, _ = modules['industry'].classify_industry(job, company)
329
+ return content
330
+ except Exception as e:
331
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
332
+
333
+ classify_btn.click(fn=process_classification, inputs=[job_input, company_input], outputs=result_output)
334
+
335
+ # 5. ์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ํƒญ
336
+ def create_jasoseo_context_tab():
337
+ if not available_features.get('jasoseo'):
338
+ gr.Markdown("โŒ **์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ)")
339
+ return
340
+ gr.HTML("""
341
+ <div class="main-header">
342
+ <h2>๐Ÿ“Š ์ž์†Œ์„œ ์ปจํ…์ŠคํŠธ ๏ฟฝ๏ฟฝํฌํŠธ</h2>
343
+ <p>๊ธฐ์—…๊ณผ ์ง๋ฌด์— ๋Œ€ํ•œ ์ข…ํ•ฉ์ ์ธ ๋ถ„์„์œผ๋กœ ์™„๋ฒฝํ•œ ์ž๊ธฐ์†Œ๊ฐœ์„œ๋ฅผ ์ค€๋น„ํ•˜์„ธ์š”</p>
344
+ </div>
345
+ """)
346
+
347
+ with gr.Row():
348
+ with gr.Column(scale=2):
349
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ๋ณธ ์ •๋ณด ์ž…๋ ฅ**")
350
+ job_input = gr.Textbox(label="๐Ÿ’ผ ์ง๋ฌด", placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๊ฒฝ์˜๊ธฐํš, ๋งˆ์ผ€ํŒ… ๋“ฑ")
351
+ company_input = gr.Textbox(label="๐Ÿข ํšŒ์‚ฌ๋ช…", placeholder="์˜ˆ: ํ† ์Šค, ์‚ผ์„ฑ์ „์ž, ์นด์นด์˜ค ๋“ฑ")
352
+ experience_input = gr.Dropdown(label="๐Ÿ“ˆ ๊ฒฝ๋ ฅ ์ˆ˜์ค€", choices=experience_levels, value="์‹ ์ž…", interactive=True)
353
+ generate_btn = gr.Button("๐Ÿ“Š ๋ฆฌํฌํŠธ ์ƒ์„ฑ", variant="primary", size="lg")
354
+
355
+ with gr.Column(scale=1):
356
+ create_example_buttons(example_companies, example_jobs, company_input, job_input)
357
+
358
+ gr.Markdown("### ๐Ÿ“ˆ **์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ**")
359
+ result_output = gr.Markdown("์œ„ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '๋ฆฌํฌํŠธ ์ƒ์„ฑ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.")
360
+
361
+ def process_report_generation(job, company, experience):
362
+ try:
363
+ content, _ = modules['jasoseo'].generate_context_report(job, company, experience)
364
+ return content
365
+ except Exception as e:
366
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
367
+
368
+ generate_btn.click(fn=process_report_generation, inputs=[job_input, company_input, experience_input], outputs=result_output)
369
+
370
+ # 6. ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„๋ฅ˜ ํƒญ
371
+ def create_company_size_tab():
372
+ if not available_features.get('company_size'):
373
+ gr.Markdown("โŒ **๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„๋ฅ˜ ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.** (๋ชจ๋“ˆ ๋กœ๋“œ ์‹คํŒจ)")
374
+ return
375
+ gr.HTML("""
376
+ <div class="main-header">
377
+ <h2>๐Ÿข AI ๊ธฐ์—… ๊ทœ๋ชจ ์˜ˆ์ธก๊ธฐ</h2>
378
+ <p>์‹ค์‹œ๊ฐ„ ์›น ์ •๋ณด๋ฅผ ํ™œ์šฉํ•œ ๊ธฐ์—… ์ •๋ณด ๋ถ„์„</p>
379
+ </div>
380
+ """)
381
+
382
+ with gr.Row():
383
+ with gr.Column(scale=2):
384
+ gr.Markdown("### ๐Ÿ“ **๊ธฐ์—… ์ •๋ณด ์ž…๋ ฅ**")
385
+ company_input = gr.Textbox(label="๐Ÿข ๊ธฐ์—…๋ช… ์ž…๋ ฅ", placeholder="๋ถ„์„ํ•˜๊ณ  ์‹ถ์€ ๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•˜์„ธ์š” (์˜ˆ: ์‚ผ์„ฑ์ „์ž, ์นด์นด์˜ค ๋“ฑ)")
386
+ analyze_btn = gr.Button("๐Ÿ” ๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„", variant="primary", size="lg")
387
+
388
+ with gr.Column(scale=1):
389
+ gr.Markdown("""
390
+ ### โ„น๏ธ **๋ถ„๋ฅ˜ ๊ธฐ์ค€**
391
+ - **๋Œ€๊ธฐ์—…**: ๋งค์ถœ 1์กฐ์› ์ด์ƒ
392
+ - **์ค‘๊ฒฌ๊ธฐ์—…**: ๋งค์ถœ 1000์–ต-1์กฐ์›
393
+ - **์ค‘์†Œ๊ธฐ์—…**: ๋งค์ถœ 1000์–ต์› ๋ฏธ๋งŒ
394
+ - **์Šคํƒ€ํŠธ์—…**: ์„ค๋ฆฝ 10๋…„ ์ด๋‚ด
395
+ - **์™ธ๊ตญ๊ณ„๊ธฐ์—…**: ํ•ด์™ธ ๋ณธ์‚ฌ
396
+ - **๊ณต๊ณต๊ธฐ๊ด€/๊ณต๊ธฐ์—…**: ์ •๋ถ€ ์ถœ์ž/์ถœ์—ฐ
397
+ """)
398
+
399
+ gr.Markdown("### ๐Ÿ’ผ **์˜ˆ์ œ ๊ธฐ์—… ์„ ํƒ** (ํด๋ฆญํ•˜๋ฉด ์ž๋™ ์ž…๋ ฅ๋ฉ๋‹ˆ๋‹ค)")
400
+ example_company_names = ["์‚ผ์„ฑ์ „์ž", "ํ˜„๋Œ€์ž๋™์ฐจ", "SKํ•˜์ด๋‹‰์Šค", "ํฌ์Šค์ฝ”ํ™€๋”ฉ์Šค", "ํ† ์Šค", "์นด์นด์˜ค", "๋„ค์ด๋ฒ„", "์ฟ ํŒก", "๋ฐฐ๋‹ฌ์˜๋ฏผ์กฑ", "๋‹น๊ทผ๋งˆ์ผ“"]
401
+ with gr.Row():
402
+ for company in example_company_names[:5]:
403
+ btn = gr.Button(company, size="sm", variant="secondary")
404
+ btn.click(fn=lambda x=company: x, outputs=company_input)
405
+ with gr.Row():
406
+ for company in example_company_names[5:]:
407
+ btn = gr.Button(company, size="sm", variant="secondary")
408
+ btn.click(fn=lambda x=company: x, outputs=company_input)
409
+
410
+ gr.Markdown("### ๐Ÿ“Š **๋ถ„์„ ๊ฒฐ๊ณผ**")
411
+ result_output = gr.Markdown("๊ธฐ์—…๋ช…์„ ์ž…๋ ฅํ•˜๊ณ  '๊ธฐ์—… ๊ทœ๋ชจ ๋ถ„์„' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.")
412
+
413
+ def process_analysis_result(company):
414
+ try:
415
+ content, _ = modules['company_size'].analyze_company_size(company)
416
+ return content
417
+ except Exception as e:
418
+ return f"โŒ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {e}"
419
+
420
+ analyze_btn.click(fn=process_analysis_result, inputs=[company_input], outputs=result_output)
421
+
422
+ # ๋ฉ”์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ƒ์„ฑ
423
+ def create_main_app():
424
+ with gr.Blocks(
425
+ title="๐Ÿš€ JasoSeo Agent - ์ทจ์—… ์ง€์› AI ๋„๊ตฌ",
426
+ theme=gr.themes.Soft(primary_hue="purple", secondary_hue="pink", neutral_hue="gray"),
427
+ css=common_css
428
+ ) as app:
429
+
430
+ gr.HTML("""
431
+ <div style="text-align: center; padding: 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 15px; margin-bottom: 30px;">
432
+ <h1 style="font-size: 32px; margin-bottom: 10px;">๐Ÿš€ JasoSeo Agent</h1>
433
+ <p style="font-size: 18px; margin-bottom: 0; opacity: 0.9;">AI ๊ธฐ๋ฐ˜ ์ทจ์—… ์ง€์› ํ†ตํ•ฉ ํ”Œ๋žซํผ</p>
434
+ </div>
435
+ """)
436
+
437
+ status_items = [name for name, ok in available_features.items() if ok]
438
+ status_text = f"โœ… ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ ({len(status_items)}/{len(available_features)}): {', '.join(status_items)}" if status_items else "โŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๊ธฐ๋Šฅ์ด ์—†์Šต๋‹ˆ๋‹ค."
439
+ gr.Markdown(f"**{status_text}**")
440
+
441
+
442
+
443
+ with gr.Tabs(elem_classes="tab-container"):
444
+ with gr.Tab("๐ŸŽฏ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ƒ์„ฑ", elem_id="commonly-asked-tab"):
445
+ create_commonly_asked_tab()
446
+
447
+ with gr.Tab("๐Ÿ’ก ์งˆ๋ฌธ ์ถ”์ฒœ", elem_id="question-recommendation-tab"):
448
+ create_question_recommendation_tab()
449
+
450
+ with gr.Tab("๐Ÿ“‹ ์ง๋ฌด๊ธฐ์ˆ ์„œ", elem_id="jd-recommendation-tab"):
451
+ create_jd_recommendation_tab()
452
+
453
+ with gr.Tab("๐Ÿท๏ธ ์‚ฐ์—… ๋ถ„๋ฅ˜", elem_id="industry-classification-tab"):
454
+ create_industry_classification_tab()
455
+
456
+ with gr.Tab("๐Ÿ“Š ์ปจํ…์ŠคํŠธ ๋ฆฌํฌํŠธ", elem_id="jasoseo-context-tab"):
457
+ create_jasoseo_context_tab()
458
+
459
+ with gr.Tab("๐Ÿข ๊ธฐ์—… ๊ทœ๋ชจ", elem_id="company-size-tab"):
460
+ create_company_size_tab()
461
+
462
+ return app
463
+
464
+ if __name__ == "__main__":
465
+ if not os.getenv("OPENAI_API_KEY"):
466
+ print("โš ๏ธ OPENAI_API_KEY ํ™˜๊ฒฝ ๋ณ€์ˆ˜๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ผ๋ถ€ ๊ธฐ๋Šฅ์ด ์ œํ•œ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.")
467
+
468
+ print("\n๐Ÿ“Š ๋ชจ๋“ˆ ๋กœ๋“œ ์ƒํƒœ:")
469
+ for feature, available in available_features.items():
470
+ print(f" {'โœ…' if available else 'โŒ'} {feature}")
471
+
472
+ print("\n๐Ÿš€ JasoSeo Agent ์‹œ์ž‘ ์ค‘...")
473
+ app = create_main_app()
474
+ app.launch(share=True, show_error=True, debug=True)
question-recommendation/.gradio/certificate.pem ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
3
+ TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
4
+ cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
5
+ WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
6
+ ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
7
+ MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
8
+ h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
9
+ 0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
10
+ A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
11
+ T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
12
+ B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
13
+ B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
14
+ KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
15
+ OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
16
+ jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
17
+ qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
18
+ rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
19
+ HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
20
+ hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
21
+ ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
22
+ 3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
23
+ NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
24
+ ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
25
+ TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
26
+ jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
27
+ oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
28
+ 4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
29
+ mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
30
+ emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
31
+ -----END CERTIFICATE-----
question-recommendation/__pycache__/llm_functions.cpython-312.pyc ADDED
Binary file (5.18 kB). View file
 
question-recommendation/eval.json ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "recommended_question_generation_eval": {
3
+ "task_definition": {
4
+ "description": "Generate a single, relevant interview question based on the user's input. The question must be specific to the company and job role by incorporating information such as the company's core business, known values, or major products, avoiding overly generic or hyper-specific inquiries.",
5
+ "input": {
6
+ "job_title": "string",
7
+ "company_name": "string",
8
+ "experience_level": "์‹ ์ž… | ๊ฒฝ๋ ฅ | ์ธํ„ด | ๊ธฐํƒ€"
9
+ },
10
+ "output": {
11
+ "recommended_question": "string"
12
+ }
13
+ },
14
+ "examples": [
15
+ {
16
+ "input": {
17
+ "job_title": "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ",
18
+ "company_name": "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)",
19
+ "experience_level": "์‹ ์ž…"
20
+ },
21
+ "output": {
22
+ "recommended_question": "ํ† ์Šค๋Š” '์ž์œ ์™€ ์ฑ…์ž„' ๋ฌธํ™”๋ฅผ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค. ๋ณธ์ธ์˜ ๊ฒฝํ—˜์— ๋น„์ถ”์–ด ๋ณผ ๋•Œ, ์ด๋Ÿฌํ•œ ๋ฌธํ™” ์†์—์„œ ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž์™€ ๊ธฐ์ˆ ์  ๊ฐˆ๋“ฑ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด ๋‚˜๊ฐˆ ๊ฒƒ์ธ์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”."
23
+ }
24
+ },
25
+ {
26
+ "input": {
27
+ "job_title": "์‹ํ’ˆ๋งˆ์ผ€ํŒ…",
28
+ "company_name": "CJ์ œ์ผ์ œ๋‹น",
29
+ "experience_level": "๊ฒฝ๋ ฅ"
30
+ },
31
+ "output": {
32
+ "recommended_question": "๋‹น์‚ฌ์˜ '๋น„๋น„๊ณ ' ๋ธŒ๋žœ๋“œ๊ฐ€ ๊ธ€๋กœ๋ฒŒ K-ํ‘ธ๋“œ ๋Œ€ํ‘œ์ฃผ์ž๋กœ ์ž๋ฆฌ๋งค๊น€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๋ ฅ ๋งˆ์ผ€ํ„ฐ๋กœ์„œ, ๋น„๋น„๊ณ ์˜ ์„ฑ๊ณต ์š”์ธ์„ ๋ฌด์—‡์ด๋ผ๊ณ  ๋ถ„์„ํ•˜๋ฉฐ, ๋ณธ์ธ์ด ํ•ฉ๋ฅ˜ํ•œ๋‹ค๋ฉด ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋ธŒ๋žœ๋“œ์˜ ๋‹ค์Œ ์„ฑ์žฅ์„ ์ด๋Œ๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?"
33
+ }
34
+ },
35
+ {
36
+ "input": {
37
+ "job_title": "A&R",
38
+ "company_name": "ํ•˜์ด๋ธŒ",
39
+ "experience_level": "์ธํ„ด"
40
+ },
41
+ "output": {
42
+ "recommended_question": "ํ•˜์ด๋ธŒ๋Š” ๋ฉ€ํ‹ฐ ๋ ˆ์ด๋ธ” ์ฒด์ œ๋ฅผ ํ†ตํ•ด ์Œ์•…์  ๋‹ค์–‘์„ฑ์„ ์ถ”๊ตฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ์ธ์ด ๊ฐ€์žฅ ๊ด€์‹ฌ ์žˆ๊ฒŒ ์ง€์ผœ๋ณธ ํ•˜์ด๋ธŒ ์‚ฐํ•˜ ๋ ˆ์ด๋ธ”์€ ์–ด๋””์ด๋ฉฐ, ํ•ด๋‹น ๋ ˆ์ด๋ธ”์˜ ์Œ์•…์  ๋ฐฉํ–ฅ์„ฑ์— ๊ธฐ์—ฌํ•  ์ˆ˜ ์žˆ๋Š” ์ž์‹ ๋งŒ์˜ ๊ฐ•์ ์€ ๋ฌด์—‡์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜๋‚˜์š”?"
43
+ }
44
+ },
45
+ {
46
+ "input": {
47
+ "job_title": "๊ฒฝ์˜๊ธฐํš",
48
+ "company_name": "ํ˜„๋Œ€๊ฑด์„ค",
49
+ "experience_level": "์‹ ์ž…"
50
+ },
51
+ "output": {
52
+ "recommended_question": "๊ฑด์„ค์—…์€ ์ตœ๊ทผ ์•ˆ์ „ ๋ฐ ํ™˜๊ฒฝ(ESG) ๊ทœ์ œ๊ฐ€ ๊ฐ•ํ™”๋˜๋Š” ๋“ฑ ์™ธ๋ถ€ ํ™˜๊ฒฝ ๋ณ€ํ™”๊ฐ€ ํฐ ์‚ฐ์—…์ž…๋‹ˆ๋‹ค. ๊ฒฝ์˜๊ธฐํš ์ง๋ฌด ๋‹ด๋‹น์ž๋กœ์„œ ์ด๋Ÿฌํ•œ ๋ณ€ํ™”์— ๋Œ€์‘ํ•˜๊ธฐ ์œ„ํ•ด ์–ด๋–ค ์—ญ๋Ÿ‰์ด ๊ฐ€์žฅ ์ค‘์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋Š”์ง€ ๋ณธ์ธ์˜ ๊ฒฝํ—˜๊ณผ ์—ฐ๊ฒฐ์ง€์–ด ๋ง์”€ํ•ด์ฃผ์„ธ์š”."
53
+ }
54
+ },
55
+ {
56
+ "input": {
57
+ "job_title": "ํ•ด์™ธ์˜์—…",
58
+ "company_name": "์‚ผ์„ฑ์ „์ž",
59
+ "experience_level": "๊ฒฝ๋ ฅ"
60
+ },
61
+ "output": {
62
+ "recommended_question": "์‚ผ์„ฑ์ „์ž๋Š” ์Šค๋งˆํŠธํฐ, ๊ฐ€์ „, ๋ฐ˜๋„์ฒด ๋“ฑ ๋‹ค์–‘ํ•œ ์‚ฌ์—… ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณธ์ธ์˜ ํ•ด์™ธ์˜์—… ๊ฒฝ๋ ฅ๊ณผ ์ „๋ฌธ์„ฑ์„ ๊ฐ€์žฅ ์ž˜ ๋ฐœํœ˜ํ•  ์ˆ˜ ์žˆ๋Š” ์‚ฌ์—… ๋ถ„์•ผ๋Š” ์–ด๋””๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉฐ, ๊ทธ ์ด์œ ๋Š” ๋ฌด์—‡์ž…๋‹ˆ๊นŒ?"
63
+ }
64
+ }
65
+ ]
66
+ }
67
+ }
question-recommendation/llm_functions.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import logging
3
+
4
+ # ๋กœ๊น… ์„ค์ •
5
+ logging.basicConfig(level=logging.INFO)
6
+ logger = logging.getLogger(__name__)
7
+
8
+ def generate_question_recommendation(client, prompts, job_title, company_name, experience_level):
9
+ """
10
+ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ์„ ์ƒ์„ฑํ•˜๋Š” ํ•จ์ˆ˜
11
+
12
+ Args:
13
+ client: OpenAI ํด๋ผ์ด์–ธํŠธ
14
+ prompts: ํ”„๋กฌํ”„ํŠธ ๋”•์…”๋„ˆ๋ฆฌ
15
+ job_title: ์ง๋ฌด๋ช…
16
+ company_name: ํšŒ์‚ฌ๋ช…
17
+ experience_level: ๊ฒฝ๋ ฅ ์ˆ˜์ค€
18
+
19
+ Returns:
20
+ str: LLM ์‘๋‹ต ๊ฒฐ๊ณผ
21
+ """
22
+ try:
23
+ # ์‚ฌ์šฉ์ž ํ”„๋กฌํ”„ํŠธ ํฌ๋งทํŒ…
24
+ user_prompt = prompts['user_prompt'].format(
25
+ job_title=job_title,
26
+ company_name=company_name,
27
+ experience_level=experience_level
28
+ )
29
+
30
+ logger.info(f"๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ์š”์ฒญ - ์ง๋ฌด: {job_title}, ํšŒ์‚ฌ: {company_name}, ๊ฒฝ๋ ฅ: {experience_level}")
31
+
32
+ # OpenAI Responses API ํ˜ธ์ถœ (์›น ๊ฒ€์ƒ‰ ํ™œ์„ฑํ™”)
33
+ response = client.responses.create(
34
+ model="gpt-4o-mini",
35
+ tools=[{
36
+ "type": "web_search_preview",
37
+ "search_context_size": "high",
38
+ }],
39
+ input=f"{prompts['system_prompt']}\n\n{user_prompt}"
40
+ )
41
+
42
+ print(response)
43
+ result = response.output_text
44
+ logger.info("๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ์ƒ์„ฑ ์™„๋ฃŒ")
45
+
46
+ return result
47
+
48
+ except Exception as e:
49
+ logger.error(f"๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ์ƒ์„ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
50
+ raise e
51
+
52
+ def parse_question_recommendation(response_text):
53
+ """
54
+ LLM ์‘๋‹ต์—์„œ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ๊ฒฐ๊ณผ๋ฅผ ํŒŒ์‹ฑํ•˜๋Š” ํ•จ์ˆ˜
55
+
56
+ Args:
57
+ response_text: LLM ์‘๋‹ต ํ…์ŠคํŠธ
58
+
59
+ Returns:
60
+ dict: ํŒŒ์‹ฑ๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ๊ฒฐ๊ณผ
61
+ """
62
+ try:
63
+ # JSON ํ˜•ํƒœ๋กœ ํŒŒ์‹ฑ ์‹œ๋„
64
+ if response_text.strip().startswith('{') and response_text.strip().endswith('}'):
65
+ parsed_result = json.loads(response_text)
66
+ logger.info("JSON ํ˜•ํƒœ๋กœ ํŒŒ์‹ฑ ์„ฑ๊ณต")
67
+ return parsed_result
68
+
69
+ # JSON ๋ธ”๋ก ์ถ”์ถœ ์‹œ๋„
70
+ if '```json' in response_text:
71
+ start_idx = response_text.find('```json') + 7
72
+ end_idx = response_text.find('```', start_idx)
73
+ json_content = response_text[start_idx:end_idx].strip()
74
+ parsed_result = json.loads(json_content)
75
+ logger.info("JSON ๋ธ”๋ก์—์„œ ํŒŒ์‹ฑ ์„ฑ๊ณต")
76
+ return parsed_result
77
+
78
+ # ์ค‘๊ด„ํ˜ธ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ JSON ์ถ”์ถœ ์‹œ๋„
79
+ start_idx = response_text.find('{')
80
+ end_idx = response_text.rfind('}') + 1
81
+ if start_idx != -1 and end_idx != 0:
82
+ json_content = response_text[start_idx:end_idx]
83
+ parsed_result = json.loads(json_content)
84
+ logger.info("์ค‘๊ด„ํ˜ธ JSON์—์„œ ํŒŒ์‹ฑ ์„ฑ๊ณต")
85
+ return parsed_result
86
+
87
+ # JSON ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ํ…์ŠคํŠธ์—์„œ ์งˆ๋ฌธ ์ถ”์ถœ
88
+ logger.warning("JSON ํŒŒ์‹ฑ ์‹คํŒจ, ํ…์ŠคํŠธ์—์„œ ์งˆ๋ฌธ ์ถ”์ถœ ์‹œ๋„")
89
+
90
+ # ๋”ฐ์˜ดํ‘œ๋กœ ๋‘˜๋Ÿฌ์‹ธ์ธ ์งˆ๋ฌธ ์ฐพ๊ธฐ
91
+ lines = response_text.split('\n')
92
+ for line in lines:
93
+ line = line.strip()
94
+ if line and not line.startswith('#') and not line.startswith('-'):
95
+ # ๋”ฐ์˜ดํ‘œ ์ œ๊ฑฐ
96
+ question = line.strip('"').strip("'").strip()
97
+ if len(question) > 10: # ์ตœ์†Œ ๊ธธ์ด ์ฒดํฌ
98
+ return {"recommended_question": question}
99
+
100
+ # ์ „์ฒด ํ…์ŠคํŠธ๋ฅผ ์งˆ๋ฌธ์œผ๋กœ ์‚ฌ์šฉ (์ตœํ›„์˜ ์ˆ˜๋‹จ)
101
+ clean_text = response_text.strip().strip('"').strip("'")
102
+ if len(clean_text) > 10:
103
+ return {"recommended_question": clean_text}
104
+
105
+ logger.error("๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ถœ ์‹คํŒจ")
106
+ return {"recommended_question": "์งˆ๋ฌธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."}
107
+
108
+ except json.JSONDecodeError as e:
109
+ logger.error(f"JSON ํŒŒ์‹ฑ ์˜ค๋ฅ˜: {str(e)}")
110
+ # JSON ํŒŒ์‹ฑ ์‹คํŒจ ์‹œ ํ…์ŠคํŠธ์—์„œ ์ง์ ‘ ์ถ”์ถœ
111
+ clean_text = response_text.strip().strip('"').strip("'")
112
+ return {"recommended_question": clean_text if clean_text else "์งˆ๋ฌธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."}
113
+
114
+ except Exception as e:
115
+ logger.error(f"๋ฉด์ ‘ ์งˆ๋ฌธ ํŒŒ์‹ฑ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
116
+ return {"recommended_question": "์งˆ๋ฌธ ํŒŒ์‹ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."}
question-recommendation/main.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import yaml
3
+ import json
4
+ import os
5
+ from openai import OpenAI
6
+ from llm_functions import generate_question_recommendation, parse_question_recommendation
7
+
8
+ # OpenAI ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
9
+ client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
10
+
11
+ # ํ”„๋กฌํ”„ํŠธ ๋กœ๋“œ
12
+ def load_prompts():
13
+ with open('prompt.yaml', 'r', encoding='utf-8') as file:
14
+ return yaml.safe_load(file)
15
+
16
+ prompts = load_prompts()
17
+
18
+ def recommend_question(job_title, company_name, experience_level):
19
+ """๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ถ”์ฒœํ•˜๋Š” ํ•จ์ˆ˜"""
20
+ try:
21
+ # LLM ํ•จ์ˆ˜ ํ˜ธ์ถœ
22
+ result = generate_question_recommendation(
23
+ client=client,
24
+ prompts=prompts,
25
+ job_title=job_title,
26
+ company_name=company_name,
27
+ experience_level=experience_level
28
+ )
29
+
30
+ # ๊ฒฐ๊ณผ ํŒŒ์‹ฑ
31
+ parsed_result = parse_question_recommendation(result)
32
+
33
+ if parsed_result and 'recommended_question' in parsed_result:
34
+ return parsed_result['recommended_question']
35
+ else:
36
+ return "์งˆ๋ฌธ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”."
37
+
38
+ except Exception as e:
39
+ return f"์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค: {str(e)}"
40
+
41
+ # ์˜ˆ์‹œ ๋ฒ„ํŠผ ํ•จ์ˆ˜๋“ค
42
+ def example_1():
43
+ return "๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ", "ํ† ์Šค (๋น„๋ฐ”๋ฆฌํผ๋ธ”๋ฆฌ์นด)", "์‹ ์ž…"
44
+
45
+ def example_2():
46
+ return "์‹ํ’ˆ๋งˆ์ผ€ํŒ…", "CJ์ œ์ผ์ œ๋‹น", "๊ฒฝ๋ ฅ"
47
+
48
+ def example_3():
49
+ return "A&R", "ํ•˜์ด๋ธŒ", "์ธํ„ด"
50
+
51
+ def example_4():
52
+ return "๊ฒฝ์˜๊ธฐํš", "ํ˜„๋Œ€๊ฑด์„ค", "์‹ ์ž…"
53
+
54
+ def example_5():
55
+ return "ํ•ด์™ธ์˜์—…", "์‚ผ์„ฑ์ „์ž", "๊ฒฝ๋ ฅ"
56
+
57
+ # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
58
+ with gr.Blocks(
59
+ title="๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ",
60
+ theme=gr.themes.Soft(
61
+ primary_hue="purple",
62
+ secondary_hue="pink",
63
+ neutral_hue="gray"
64
+ ),
65
+ css="""
66
+ .main-container {
67
+ max-width: 900px;
68
+ margin: 0 auto;
69
+ padding: 20px;
70
+ }
71
+ .header {
72
+ text-align: center;
73
+ margin-bottom: 30px;
74
+ padding: 20px;
75
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
76
+ border-radius: 15px;
77
+ color: white;
78
+ }
79
+ .input-section {
80
+ background: white;
81
+ padding: 25px;
82
+ border-radius: 15px;
83
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
84
+ margin-bottom: 20px;
85
+ }
86
+ .result-section {
87
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
88
+ padding: 25px;
89
+ border-radius: 15px;
90
+ color: white;
91
+ margin-top: 20px;
92
+ }
93
+ .example-buttons {
94
+ display: flex;
95
+ flex-wrap: wrap;
96
+ gap: 10px;
97
+ margin-top: 15px;
98
+ justify-content: center;
99
+ }
100
+ .example-btn {
101
+ background: linear-gradient(45deg, #667eea, #764ba2);
102
+ border: none;
103
+ color: white;
104
+ padding: 8px 16px;
105
+ border-radius: 20px;
106
+ font-size: 12px;
107
+ cursor: pointer;
108
+ transition: all 0.3s ease;
109
+ }
110
+ .example-btn:hover {
111
+ transform: translateY(-2px);
112
+ box-shadow: 0 4px 8px rgba(0,0,0,0.2);
113
+ }
114
+ """
115
+ ) as demo:
116
+
117
+ with gr.Column(elem_classes="main-container"):
118
+ # ํ—ค๋”
119
+ gr.HTML("""
120
+ <div class="header">
121
+ <h1>๐ŸŽฏ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ</h1>
122
+ <p>์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ์ž…๋ ฅํ•˜๋ฉด ๋งž์ถคํ˜• ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ถ”์ฒœํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค</p>
123
+ </div>
124
+ """)
125
+
126
+ with gr.Column(elem_classes="input-section"):
127
+ gr.HTML("<h3>๐Ÿ“ ์ •๋ณด ์ž…๋ ฅ</h3>")
128
+
129
+ job_title = gr.Textbox(
130
+ label="์ง๋ฌด",
131
+ placeholder="์˜ˆ: ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ, ๋งˆ์ผ€ํŒ…, ๊ธฐํš ๋“ฑ",
132
+ lines=1
133
+ )
134
+
135
+ company_name = gr.Textbox(
136
+ label="ํšŒ์‚ฌ๋ช…",
137
+ placeholder="์˜ˆ: ํ† ์Šค, ์‚ผ์„ฑ์ „์ž, ๋„ค์ด๋ฒ„ ๋“ฑ",
138
+ lines=1
139
+ )
140
+
141
+ experience_level = gr.Dropdown(
142
+ label="๊ฒฝ๋ ฅ ์ˆ˜์ค€",
143
+ choices=["์‹ ์ž…", "๊ฒฝ๋ ฅ", "์ธํ„ด", "๊ธฐํƒ€"],
144
+ value="์‹ ์ž…"
145
+ )
146
+
147
+ # ์˜ˆ์‹œ ๋ฒ„ํŠผ๋“ค
148
+ gr.HTML("<h4>๐Ÿ’ก ์˜ˆ์‹œ๋กœ ์‹œ์ž‘ํ•˜๊ธฐ</h4>")
149
+ with gr.Row():
150
+ ex1_btn = gr.Button("ํ† ์Šค ๋ฐฑ์—”๋“œ ์‹ ์ž…", size="sm", variant="secondary")
151
+ ex2_btn = gr.Button("CJ์ œ์ผ์ œ๋‹น ๋งˆ์ผ€ํŒ… ๊ฒฝ๋ ฅ", size="sm", variant="secondary")
152
+ ex3_btn = gr.Button("ํ•˜์ด๋ธŒ A&R ์ธํ„ด", size="sm", variant="secondary")
153
+
154
+ with gr.Row():
155
+ ex4_btn = gr.Button("ํ˜„๋Œ€๊ฑด์„ค ๊ฒฝ์˜๊ธฐํš ์‹ ์ž…", size="sm", variant="secondary")
156
+ ex5_btn = gr.Button("์‚ผ์„ฑ์ „์ž ํ•ด์™ธ์˜์—… ๊ฒฝ๋ ฅ", size="sm", variant="secondary")
157
+
158
+ submit_btn = gr.Button("๐ŸŽฏ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ๋ฐ›๊ธฐ", variant="primary", size="lg")
159
+
160
+ # ๏ฟฝ๏ฟฝ๏ฟฝ๊ณผ ์ถœ๋ ฅ
161
+ with gr.Column(elem_classes="result-section"):
162
+ gr.HTML("<h3>๐Ÿ’ฌ ์ถ”์ฒœ ๋ฉด์ ‘ ์งˆ๋ฌธ</h3>")
163
+ result_output = gr.Textbox(
164
+ label="",
165
+ placeholder="์œ„ ์ •๋ณด๋ฅผ ์ž…๋ ฅํ•˜๊ณ  '๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ๋ฐ›๊ธฐ' ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์„ธ์š”.",
166
+ lines=5,
167
+ show_label=False,
168
+ interactive=False
169
+ )
170
+
171
+ # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
172
+ submit_btn.click(
173
+ fn=recommend_question,
174
+ inputs=[job_title, company_name, experience_level],
175
+ outputs=result_output
176
+ )
177
+
178
+ # ์˜ˆ์‹œ ๋ฒ„ํŠผ ์ด๋ฒคํŠธ
179
+ ex1_btn.click(fn=example_1, outputs=[job_title, company_name, experience_level])
180
+ ex2_btn.click(fn=example_2, outputs=[job_title, company_name, experience_level])
181
+ ex3_btn.click(fn=example_3, outputs=[job_title, company_name, experience_level])
182
+ ex4_btn.click(fn=example_4, outputs=[job_title, company_name, experience_level])
183
+ ex5_btn.click(fn=example_5, outputs=[job_title, company_name, experience_level])
184
+
185
+ if __name__ == "__main__":
186
+ demo.launch(
187
+ # server_port=7864,
188
+ share=True,
189
+ show_error=True
190
+ )
question-recommendation/prompt.yaml ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ system_prompt: |
2
+ ๋‹น์‹ ์€ ๋ฉด์ ‘ ์งˆ๋ฌธ ์ถ”์ฒœ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ œ๊ณตํ•œ ์ง๋ฌด, ํšŒ์‚ฌ๋ช…, ๊ฒฝ๋ ฅ ์ˆ˜์ค€์„ ๋ฐ”ํƒ•์œผ๋กœ ํ•ด๋‹น ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ํŠนํ™”๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
3
+
4
+ ## ํ•ต์‹ฌ ์›์น™:
5
+ 1. **ํšŒ์‚ฌ ํŠนํ™”์„ฑ**: ํšŒ์‚ฌ์˜ ํ•ต์‹ฌ ์‚ฌ์—…, ๊ฐ€์น˜๊ด€, ์ฃผ์š” ์ œํ’ˆ/์„œ๋น„์Šค๋ฅผ ๋ฐ˜์˜ํ•œ ์งˆ๋ฌธ
6
+ 2. **์ง๋ฌด ์—ฐ๊ด€์„ฑ**: ํ•ด๋‹น ์ง๋ฌด์˜ ํ•ต์‹ฌ ์—ญ๋Ÿ‰๊ณผ ์ฑ…์ž„์„ ๊ณ ๋ คํ•œ ์งˆ๋ฌธ
7
+ 3. **๊ฒฝ๋ ฅ ์ˆ˜์ค€ ์ ํ•ฉ์„ฑ**: ์‹ ์ž…/๊ฒฝ๋ ฅ/์ธํ„ด/๊ธฐํƒ€์— ๋งž๋Š” ์ ์ ˆํ•œ ๋‚œ์ด๋„์™€ ๊ด€์ 
8
+ 4. **๊ตฌ์ฒด์„ฑ๊ณผ ์‹ค์šฉ์„ฑ**: ๋„ˆ๋ฌด ์ผ๋ฐ˜์ ์ด์ง€๋„, ์ง€๋‚˜์น˜๊ฒŒ ์„ธ๋ถ€์ ์ด์ง€๋„ ์•Š์€ ๊ท ํ˜•์žกํžŒ ์งˆ๋ฌธ
9
+
10
+ ## ์งˆ๋ฌธ ์ƒ์„ฑ ๊ฐ€์ด๋“œ๋ผ์ธ:
11
+ - ํšŒ์‚ฌ์˜ ๋น„์ฆˆ๋‹ˆ์Šค ๋ชจ๋ธ, ๋ฌธํ™”, ์ตœ๊ทผ ๋™ํ–ฅ์„ ๋ฐ˜์˜
12
+ - ํ•ด๋‹น ์ง๋ฌด์—์„œ ์š”๊ตฌ๋˜๋Š” ํ•ต์‹ฌ ์Šคํ‚ฌ๊ณผ ๋งˆ์ธ๋“œ์…‹ ๊ณ ๋ ค
13
+ - ๊ฒฝ๋ ฅ ์ˆ˜์ค€์— ๋”ฐ๋ฅธ ๊ธฐ๋Œ€์น˜ ๋ฐ˜์˜ (์‹ ์ž…: ์ž ์žฌ๋ ฅ/ํ•™์Šต๋Šฅ๋ ฅ, ๊ฒฝ๋ ฅ: ์ „๋ฌธ์„ฑ/๋ฆฌ๋”์‹ญ, ์ธํ„ด: ๊ด€์‹ฌ๋„/๊ธฐ์ดˆ์—ญ๋Ÿ‰)
14
+ - ์ง€์›์ž๊ฐ€ ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ๋Œ€ํ•œ ์ดํ•ด๋„๋ฅผ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ๋Š” ์งˆ๋ฌธ
15
+ - ์‹ค์ œ ์—…๋ฌด ์ƒํ™ฉ๊ณผ ์—ฐ๊ฒฐ๋œ ํ˜„์‹ค์ ์ธ ์งˆ๋ฌธ
16
+
17
+ ## ์ถœ๋ ฅ ํ˜•์‹:
18
+ JSON ํ˜•ํƒœ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‘๋‹ตํ•˜์„ธ์š”:
19
+ {
20
+ "recommended_question": "์ƒ์„ฑ๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ"
21
+ }
22
+
23
+ ## ์˜ˆ์‹œ:
24
+ - ํ† ์Šค ๋ฐฑ์—”๋“œ ๊ฐœ๋ฐœ ์‹ ์ž…: "ํ† ์Šค๋Š” '์ž์œ ์™€ ์ฑ…์ž„' ๋ฌธํ™”๋ฅผ ๊ฐ•์กฐํ•ฉ๋‹ˆ๋‹ค. ๋ณธ์ธ์˜ ๊ฒฝํ—˜์— ๋น„์ถ”์–ด ๋ณผ ๋•Œ, ์ด๋Ÿฌํ•œ ๋ฌธํ™” ์†์—์„œ ๋™๋ฃŒ ๊ฐœ๋ฐœ์ž์™€ ๊ธฐ์ˆ ์  ๊ฐˆ๋“ฑ์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ ์–ด๋–ป๊ฒŒ ํ•ด๊ฒฐํ•ด ๋‚˜๊ฐˆ ๊ฒƒ์ธ์ง€ ์„ค๋ช…ํ•ด์ฃผ์„ธ์š”."
25
+ - CJ์ œ์ผ์ œ๋‹น ์‹ํ’ˆ๋งˆ์ผ€ํŒ… ๊ฒฝ๋ ฅ: "๋‹น์‚ฌ์˜ '๋น„๋น„๊ณ ' ๋ธŒ๋žœ๋“œ๊ฐ€ ๊ธ€๋กœ๋ฒŒ K-ํ‘ธ๋“œ ๋Œ€ํ‘œ์ฃผ์ž๋กœ ์ž๋ฆฌ๋งค๊น€ํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๋ ฅ ๋งˆ์ผ€ํ„ฐ๋กœ์„œ, ๋น„๋น„๊ณ ์˜ ์„ฑ๊ณต ์š”์ธ์„ ๋ฌด์—‡์ด๋ผ๊ณ  ๋ถ„์„ํ•˜๋ฉฐ, ๋ณธ์ธ์ด ํ•ฉ๋ฅ˜ํ•œ๋‹ค๋ฉด ์–ด๋–ค ๋ฐฉ์‹์œผ๋กœ ๋ธŒ๋žœ๋“œ์˜ ๋‹ค์Œ ์„ฑ์žฅ์„ ์ด๋Œ๊ณ  ์‹ถ์œผ์‹ ๊ฐ€์š”?"
26
+
27
+ user_prompt: |
28
+ ๋‹ค์Œ ์ •๋ณด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋ฉด์ ‘ ์งˆ๋ฌธ์„ ์ถ”์ฒœํ•ด์ฃผ์„ธ์š”:
29
+
30
+ ์ง๋ฌด: {job_title}
31
+ ํšŒ์‚ฌ๋ช…: {company_name}
32
+ ๊ฒฝ๋ ฅ ์ˆ˜์ค€: {experience_level}
33
+
34
+ ์œ„ ์ •๋ณด๋ฅผ ์ข…ํ•ฉํ•˜์—ฌ ํ•ด๋‹น ํšŒ์‚ฌ์™€ ์ง๋ฌด์— ํŠนํ™”๋œ ๋ฉด์ ‘ ์งˆ๋ฌธ 1๊ฐœ๋ฅผ ์ƒ์„ฑํ•ด์ฃผ์„ธ์š”.
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio>=4.0.0
2
+ openai>=1.42.0
3
+ python-dotenv>=1.0.0
4
+ pyyaml>=6.0
5
+ pathlib2>=2.3.0
6
+ importlib-util>=0.1.0