File size: 8,351 Bytes
2b267d0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import os
import random
import datetime
import json
import multiprocessing
import sys
from tqdm import tqdm

# μƒμœ„ λ””λ ‰ν† λ¦¬μ˜ utils.pyλ₯Ό importν•˜κΈ° μœ„ν•΄ 경둜 μΆ”κ°€
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from utils import track_api_cost
from llm_functions import generate_answer_flow

# ν…ŒμŠ€νŠΈλ₯Ό μœ„ν•œ ν™˜κ²½ λ³€μˆ˜ λ‘œλ“œ (ν•„μš”μ‹œ)
try:
    from dotenv import load_dotenv
    load_dotenv()
except ImportError:
    pass

# 예제 데이터
example_companies = ["μ‚Όμ„±μ „μž", "카카였", "넀이버", "쿠팑", "ν† μŠ€", "ν˜„λŒ€μžλ™μ°¨", "CJμ œμΌμ œλ‹Ή", "ν•˜μ΄λΈŒ"]
example_jobs_jds = {
    "λ°±μ—”λ“œ 개발자": "Spring Boot, JPA, MySQL κ²½ν—˜μž. MSA ν™˜κ²½ κ²½ν—˜ μš°λŒ€. ν΄λΌμš°λ“œ(AWS) ν™˜κ²½ 배포 및 운영 κ²½ν—˜μž.",
    "ν”„λ‘ νŠΈμ—”λ“œ 개발자": "React, TypeScript, Redux μ‚¬μš©. λ°˜μ‘ν˜• μ›Ή 개발 κ²½ν—˜ ν•„μˆ˜. UI/UX에 λŒ€ν•œ 이해도가 높은 λΆ„.",
    "데이터 뢄석가": "SQL, Python(Pandas, Scikit-learn) ν™œμš© λŠ₯λ ₯. 톡계적 지식과 데이터 μ‹œκ°ν™”(Tableau λ“±) λŠ₯λ ₯.",
    "ν”„λ‘œλ•νŠΈ λ§€λ‹ˆμ €(PM)": "IT ν”„λ‘œλ•νŠΈ 기획 및 관리 κ²½ν—˜. μ‚¬μš©μž μŠ€ν† λ¦¬ μž‘μ„±, 백둜그 관리. 데이터 기반 μ˜μ‚¬κ²°μ • λŠ₯λ ₯.",
    "λ§ˆμΌ€ν„°": "λ””μ§€ν„Έ λ§ˆμΌ€νŒ…(SA, DA, SEO) κ²½ν—˜. μ½˜ν…μΈ  기획 및 μ œμž‘, μ„±κ³Ό 뢄석. SNS 채널 운영 κ²½ν—˜."
}
example_questions = {
    "지원동기": "{company_name}에 μ§€μ›ν•œ 동기에 λŒ€ν•΄ κΈ°μˆ ν•΄μ£Όμ‹­μ‹œμ˜€.",
    "μ„±μž₯κ³Όμ •": "본인의 μ„±μž₯과정을 κ°„λž΅νžˆ κΈ°μˆ ν•˜λ˜ ν˜„μž¬μ˜ μžμ‹ μ—κ²Œ κ°€μž₯ 큰 영ν–₯을 끼친 사건, 인물 등을 ν¬ν•¨ν•˜μ—¬ κΈ°μˆ ν•΄μ£Όμ‹­μ‹œμ˜€.",
    "μ§λ¬΄μ—­λŸ‰": "{job_title} 직무 μˆ˜ν–‰μ— ν•„μš”ν•œ μ—­λŸ‰μ€ 무엇이라고 μƒκ°ν•˜λ©°, 이λ₯Ό κ°–μΆ”κΈ° μœ„ν•΄ μ–΄λ–€ λ…Έλ ₯을 ν•΄μ™”λŠ”μ§€ κΈ°μˆ ν•΄μ£Όμ‹­μ‹œμ˜€.",
    "μž…μ‚¬ν›„ν¬λΆ€": "μž…μ‚¬ ν›„ 10λ…„ λ™μ•ˆμ˜ νšŒμ‚¬μƒν™œ μ‹œλ‚˜λ¦¬μ˜€μ™€ 그것을 μΆ”κ΅¬ν•˜λŠ” 이유λ₯Ό κΈ°μˆ ν•΄μ£Όμ‹­μ‹œμ˜€."
}
experience_levels = ["μ‹ μž…", "κ²½λ ₯", "인턴"]
example_conversations = [
    "User: μ•ˆλ…•ν•˜μ„Έμš”, μžκΈ°μ†Œκ°œμ„œ μ»¨μ„€νŒ…μ„ λ°›κ³  μ‹ΆμŠ΅λ‹ˆλ‹€.\nAI: λ„€, μ–΄λ–€ 직무와 νšŒμ‚¬μ— μ§€μ›ν•˜μ‹œλ‚˜μš”?\nUser: 쿠팑의 PM μ§λ¬΄μž…λ‹ˆλ‹€. 제 강점은 데이터 뢄석 λŠ₯λ ₯μž…λ‹ˆλ‹€.",
    "User: μ„±μž₯κ³Όμ • ν•­λͺ©μ€ μ–΄λ–»κ²Œ μ“°λŠ” 게 μ’‹μ„κΉŒμš”?\nAI: 직무와 κ΄€λ ¨λœ κ²½ν—˜μ„ μ€‘μ‹¬μœΌλ‘œ, κ·Έ κ²½ν—˜μ„ 톡해 무엇을 배우고 μ–΄λ–»κ²Œ μ„±μž₯ν–ˆλŠ”μ§€ ꡬ체적인 사둀λ₯Ό λ“€μ–΄ μž‘μ„±ν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€.",
    "User: 제 κ²½ν—˜ 쀑 μ–΄λ–€ 것을 κ°•μ‘°ν•΄μ•Ό ν• μ§€ λͺ¨λ₯΄κ² μ–΄μš”.\nAI: μ§€μ›ν•˜μ‹œλŠ” 직무의 JD(μ§λ¬΄κΈ°μˆ μ„œ)λ₯Ό 보면 μ–΄λ–€ μ—­λŸ‰μ„ μ€‘μš”ν•˜κ²Œ μƒκ°ν•˜λŠ”μ§€ μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. 그와 κ΄€λ ¨λœ κ²½ν—˜μ„ μš°μ„ μ μœΌλ‘œ μ–΄ν•„ν•΄λ³΄μ„Έμš”."
]

NUM_TESTS = 100
NUM_PROCESSES = 10
MODEL_NAME = "gpt-4o-mini"

def run_test(test_input_with_id):
    """κ°œλ³„ ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό μ‹€ν–‰ν•˜λŠ” μ›Œμ»€ ν•¨μˆ˜"""
    test_id, test_input = test_input_with_id
    total_cost = 0
    error_message = ""
    status = "βœ… Success"

    try:
        parsed_flow, response = generate_answer_flow(**test_input)
        
        if response and hasattr(response, 'usage'):
            total_cost = track_api_cost(response, MODEL_NAME, None)
        
        if "error" in parsed_flow or "flow" not in parsed_flow or not parsed_flow["flow"]:
            status = "❌ Failure"
            error_message = str(parsed_flow.get("error", "Invalid flow format"))

        return {
            "id": test_id,
            "input": test_input,
            "parsed_result": parsed_flow,
            "error": error_message,
            "cost": total_cost,
            "status": status,
        }
    except Exception as e:
        return {
            "id": test_id,
            "input": test_input,
            "parsed_result": {},
            "error": f"Exception: {str(e)}",
            "cost": total_cost,
            "status": "❌ Error"
        }

def main():
    """ν…ŒμŠ€νŠΈλ₯Ό μ€€λΉ„, μ‹€ν–‰ν•˜κ³  λ³΄κ³ μ„œλ₯Ό μƒμ„±ν•˜λŠ” 메인 ν•¨μˆ˜"""
    print(f"총 {NUM_TESTS}개의 ν…ŒμŠ€νŠΈλ₯Ό {NUM_PROCESSES}개 ν”„λ‘œμ„ΈμŠ€λ‘œ 병렬 μ‹€ν–‰ν•©λ‹ˆλ‹€...")

    test_inputs = []
    for i in range(NUM_TESTS):
        company = random.choice(example_companies)
        job_title, jd = random.choice(list(example_jobs_jds.items()))
        question_template = random.choice(list(example_questions.values()))
        question = question_template.format(company_name=company, job_title=job_title)
        experience = random.choice(experience_levels)
        conversation = random.choice(example_conversations)
        
        test_input = {
            "company_name": company,
            "jd": jd,
            "question": question,
            "experience_level": experience,
            "conversation": conversation
        }
        test_inputs.append((i + 1, test_input))

    results = []
    with multiprocessing.Pool(processes=NUM_PROCESSES) as pool:
        with tqdm(total=NUM_TESTS, desc="λ‹΅λ³€ 흐름 생성 ν…ŒμŠ€νŠΈ") as pbar:
            for result in pool.imap_unordered(run_test, test_inputs):
                results.append(result)
                pbar.update()
    
    results.sort(key=lambda x: x['id'])
    print("λͺ¨λ“  ν…ŒμŠ€νŠΈκ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
    
    now = datetime.datetime.now()
    report_filename = f"answer_flow_report_{now.strftime('%Y%m%d_%H%M%S')}.html"
    
    failure_count = sum(1 for r in results if "Success" not in r["status"])
    total_cost = sum(r['cost'] for r in results)
    failure_rate = (failure_count / NUM_TESTS) * 100 if NUM_TESTS > 0 else 0

    html_template = """
    <!DOCTYPE html><html lang="ko"><head><meta charset="UTF-8"><title>λ‹΅λ³€ 흐름 생성 ν…ŒμŠ€νŠΈ λ³΄κ³ μ„œ</title>
    <style>body{{font-family:sans-serif;margin:20px;}} h1,h2{{text-align:center;}} .summary{{border:1px solid #ddd;padding:20px;margin-bottom:20px;}} table{{width:100%;border-collapse:collapse;}} th,td{{border:1px solid #ddd;padding:8px;text-align:left;vertical-align:top;}} th{{background-color:#f2f2f2;}} .status-success{{color:green;font-weight:bold;}} .status-failure{{color:red;font-weight:bold;}} pre{{white-space:pre-wrap;word-wrap:break-word;background-color:#f9f9f9;padding:10px;border:1px solid #ddd;}} .container{{max-width:1400px;margin:auto;}}</style>
    </head><body><div class="container"><h1>λ‹΅λ³€ 흐름 생성 ν…ŒμŠ€νŠΈ λ³΄κ³ μ„œ</h1><div class="summary"><h2>μš”μ•½</h2><p><strong>ν…ŒμŠ€νŠΈ μ‹œκ°„:</strong> {now}</p><p><strong>총 ν…ŒμŠ€νŠΈ 수:</strong> {total_tests}</p><p><strong>성곡:</strong> {success_count}</p><p><strong>μ‹€νŒ¨/μ—λŸ¬:</strong> {failure_count}</p><p><strong>μ‹€νŒ¨μœ¨:</strong> <span class="{status_class}">{failure_rate:.2f}%</span></p><p><strong>총 μ˜ˆμƒ λΉ„μš©:</strong> ${total_cost:.6f}</p></div>
    <h2>상세 κ²°κ³Ό</h2><table><thead><tr><th>ID</th><th>μž…λ ₯ (Input)</th><th>νŒŒμ‹± κ²°κ³Ό (Parsed)</th><th>μ—λŸ¬</th><th>λΉ„μš©</th><th>μƒνƒœ</th></tr></thead><tbody>{table_rows}</tbody></table></div></body></html>
    """

    table_rows_html = ""
    for res in results:
        status_class = "status-success" if "Success" in res["status"] else "status-failure"
        table_rows_html += f"""
            <tr>
                <td>{res['id']}</td>
                <td><pre>{json.dumps(res['input'], indent=2, ensure_ascii=False)}</pre></td>
                <td><pre>{json.dumps(res['parsed_result'], indent=2, ensure_ascii=False)}</pre></td>
                <td><pre>{res['error']}</pre></td>
                <td>${res['cost']:.6f}</td>
                <td class="{status_class}">{res['status']}</td>
            </tr>
    """

    final_html = html_template.format(
        now=now.strftime('%Y-%m-%d %H:%M:%S'),
        total_tests=NUM_TESTS,
        success_count=NUM_TESTS - failure_count,
        failure_count=failure_count,
        status_class="status-success" if failure_rate < 10 else "status-failure",
        failure_rate=failure_rate,
        total_cost=total_cost,
        table_rows=table_rows_html
    )

    with open(report_filename, "w", encoding="utf-8") as f:
        f.write(final_html)

    print(f"'{report_filename}' 파일둜 λ³΄κ³ μ„œκ°€ μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.")
    print(f"총 μ˜ˆμƒ λΉ„μš©: ${total_cost:.6f}")

if __name__ == "__main__":
    main()