์ด๋ํ
[TEST] bundle/worker ๋๊ท๋ชจ ์คํธ๋ ์ค ์๋ฎฌ๋ ์ดํฐ ์ถ๊ฐ (cs ๊ฒ์ฆ ์ด์)
07f059a | from __future__ import annotations | |
| """ | |
| ๊ฒฐํฉ(bundle-v3) ๋๊ท๋ชจ ๋๋ค ์คํธ๋ ์ค ์๋ฎฌ๋ ์ด์ โ ์๋ ๋ณํ ์ ์ ์ฑยท์ด์ ์ผ์ด์ค ํ์ง. | |
| - ์ค๋ฌธ: ๋ฌด์์ N_SURVEY๊ฑด (์ ์ 1,870๋ง์ ๋ถ๊ฐ โ ๋๊ท๋ชจ ์ํ) | |
| - ํ๋: N_SESS ์ธ์ ร ์ธ์ ๋น ์ต๋ MAX_ACT ํด๋ฆญ (ํด๋ฆญ ๊ธฐ๋ฐ ์๋์ฐ ๊ฐ์ ) | |
| - ์งํ๋ฐ๋ /tmp/cs_sim_progress.txt ์ in-place ๊ธฐ๋ก (tail -f ๋ก ๊ด๋) | |
| - ์์ฝ์ stdout, ์์ธ๋ /tmp/cs_sim_report.json | |
| ํ์ง ์ด์: | |
| E1 ๋ฒ์/NaN (scoreโ[0,1]) | |
| E2 ๋ฒ์ฉ intent(AI์ถ์ฒ๋ฅ) top-5 ๋์ถ | |
| E3 ์์ ๋์ (top-1๊ณผ ๋๋ฅ ์ด top-5 ๋ด 3๊ฐ ์ด์) | |
| E4 ํ๋ ๋ฌด๋ฐ์ (ํด๋ฆญ entity์ ์ ํธ intent๊ฐ top-10์๋ ๋ชป ๋ฆ) | |
| E5 stuck (top-1์ด โฅSTUCK_N ์ฐ์ ๊ณ ์ ) | |
| + ๋ถํฌ ๊ฑด์ ์ฑ(top-1 ๋ค์์ฑ/HHI), entity๋ณ ๋ฐ์๋ฅ | |
| """ | |
| import json, random, sys, time | |
| from collections import Counter, defaultdict | |
| from pathlib import Path | |
| sys.path.insert(0, str(Path(__file__).parent.parent)) | |
| from core.engines import config # noqa: E402 | |
| from core.extractor import get_extractor # noqa: E402 | |
| from core.inference import infer_batch, infer_with_behavior # noqa: E402 | |
| SID = "bundle-v3" | |
| N_SURVEY = 30_000 | |
| N_SESS = 8_000 | |
| MAX_ACT = 30 | |
| STUCK_N = 15 | |
| GENERIC = set() # bundle: ๋ฒ์ฉ intent ์์ (E2 N/A) | |
| PROG = "/tmp/bundle_sim_progress.txt" | |
| REPORT = "/tmp/bundle_sim_report.json" | |
| tax = config.get_taxonomy(SID)["intents"] | |
| NM = {i["id"]: i["name"] for i in tax} | |
| SURVEY = config.get_survey(SID)["questions"] | |
| OPTS = {q["id"]: [o["code"] for o in q["options"]] for q in SURVEY} | |
| SIG = config.get_behavior_signals(SID) | |
| # ํ๋ ํธ๋ฆฌ | |
| _bc = config.get_behaviors(SID) | |
| STEP1 = [(b["event_type"], b["entity"], b["id"]) for b in _bc["step1"]["behaviors"]] | |
| STEP2 = {} | |
| for parent, items in _bc["step2"]["by_parent"].items(): | |
| STEP2[parent] = [(b["event_type"], b["entity"], b["id"]) for b in items] | |
| def _bar(frac, label): | |
| n = int(frac * 30) | |
| with open(PROG, "w") as f: | |
| f.write(f"[{'#'*n}{'.'*(30-n)}] {frac*100:5.1f}% {label}") | |
| def _rand_survey(rng): | |
| return {q: rng.choice(OPTS[q]) for q in OPTS} | |
| def _top(scores, k): | |
| return sorted(scores, key=lambda s: s.final_score, reverse=True)[:k] | |
| def _check_common(scores, anom, tag): | |
| # E1 ๋ฒ์/NaN | |
| for s in scores: | |
| v = s.final_score | |
| if v != v or v < -1e-9 or v > 1.0001: | |
| anom["E1"].append((tag, s.intent_id, v)); break | |
| top = _top(scores, 5) | |
| top_ids = [s.intent_id for s in top] | |
| # E2 ๋ฒ์ฉ ๋์ถ | |
| g = GENERIC & set(top_ids) | |
| if g: | |
| anom["E2"].append((tag, list(g))) | |
| # E3 ์์ ๋์ | |
| tv = round(top[0].final_score, 2) | |
| tie = sum(1 for s in top if round(s.final_score, 2) == tv) | |
| if tie >= 3: | |
| anom["E3"].append((tag, tv, tie, top_ids[:tie])) | |
| return top_ids | |
| def run(): | |
| ext = get_extractor() | |
| rng = random.Random(20240601) | |
| t0 = time.time() | |
| anom = defaultdict(list) | |
| # ์ค๋ฌธ sweep | |
| top1_survey = Counter() | |
| for i in range(N_SURVEY): | |
| a = _rand_survey(rng) | |
| try: | |
| _, sc = infer_batch(a, SID) | |
| except Exception as e: | |
| anom["E1"].append(("survey", "EXCEPTION", str(e)[:80])); continue | |
| top_ids = _check_common(sc, anom, "survey") | |
| top1_survey[top_ids[0]] += 1 | |
| if i % 500 == 0: | |
| _bar(i / (N_SURVEY + N_SESS), f"์ค๋ฌธ {i:,}/{N_SURVEY:,}") | |
| # ํ๋ sweep | |
| ent_rise = defaultdict(lambda: [0, 0]) # entity โ [์ ํธ์์น, ์๋] | |
| stuck_runs = [] | |
| top1_beh = Counter() | |
| for j in range(N_SESS): | |
| a = _rand_survey(rng) | |
| sess = f"__st_{j}" | |
| ext.reset(sess) | |
| cur_parent = None | |
| last_top1 = None; run_len = 0; max_run = 0 | |
| n_act = rng.randint(5, MAX_ACT) | |
| for _ in range(n_act): | |
| # ํธ๋ฆฌ ๋ค๋น๊ฒ์ด์ | |
| r = rng.random() | |
| if cur_parent is None or r < 0.35: | |
| et, en, bid = rng.choice(STEP1); cur_parent = bid | |
| elif r < 0.5 and cur_parent: | |
| cur_parent = None; ext.add_event(sess, "navigate_back", "back_to_step1"); continue | |
| else: | |
| items = STEP2.get(cur_parent) or STEP1 | |
| et, en, bid = rng.choice(items) | |
| ext.add_event(sess, et, en) | |
| try: | |
| _, sc = infer_with_behavior(a, sess, SID) | |
| except Exception as e: | |
| anom["E1"].append((f"beh_{j}", "EXCEPTION", str(e)[:80])); break | |
| top_ids = _check_common(sc, anom, f"beh_{j}") | |
| # E4 ๋ฐ์์ฑ (์ ํธ entity๊ฐ top-10์) | |
| targets = SIG.get(en, []) | |
| if targets: | |
| ent_rise[en][1] += 1 | |
| if set(targets) & set([s.intent_id for s in _top(sc, 10)]): | |
| ent_rise[en][0] += 1 | |
| # E5 stuck | |
| if top_ids[0] == last_top1: | |
| run_len += 1; max_run = max(max_run, run_len) | |
| else: | |
| last_top1 = top_ids[0]; run_len = 1 | |
| top1_beh[top_ids[0]] += 1 | |
| if max_run >= STUCK_N: | |
| stuck_runs.append((f"beh_{j}", last_top1, max_run)) | |
| ext.reset(sess) | |
| if j % 150 == 0: | |
| _bar((N_SURVEY + j) / (N_SURVEY + N_SESS), f"ํ๋ {j:,}/{N_SESS:,}") | |
| _bar(1.0, "์๋ฃ") | |
| dt = time.time() - t0 | |
| # โโ ์ง๊ณ/์์ฝ โโ | |
| n_surv_top1 = sum(top1_survey.values()) | |
| hhi = sum((c / n_surv_top1) ** 2 for c in top1_survey.values()) if n_surv_top1 else 0 | |
| low_ent = {e: f"{v[0]}/{v[1]}={v[0]/v[1]*100:.0f}%" for e, v in ent_rise.items() if v[1] and v[0]/v[1] < 0.9} | |
| summary = { | |
| "elapsed_sec": round(dt, 1), | |
| "n_survey": N_SURVEY, "n_sess": N_SESS, "max_act": MAX_ACT, | |
| "E1_range_nan": len(anom["E1"]), | |
| "E2_generic_leak": len(anom["E2"]), | |
| "E3_top_tie(>=3)": len(anom["E3"]), | |
| "E5_stuck(>=%d)" % STUCK_N: len(stuck_runs), | |
| "survey_top1_distinct": len(top1_survey), | |
| "survey_top1_HHI": round(hhi, 4), | |
| "survey_top1_top8": [(NM[i][:14], f"{c/n_surv_top1*100:.1f}%") for i, c in top1_survey.most_common(8)], | |
| "entity_low_responsiveness(<90%)": low_ent, | |
| "stuck_samples": stuck_runs[:10], | |
| "E1_samples": anom["E1"][:5], | |
| "E2_samples": anom["E2"][:5], | |
| "E3_samples": anom["E3"][:5], | |
| } | |
| json.dump({"summary": summary, | |
| "anom_E1": anom["E1"][:200], "anom_E2": anom["E2"][:200], | |
| "anom_E3": anom["E3"][:200], "stuck": stuck_runs[:200]}, | |
| open(REPORT, "w"), ensure_ascii=False, indent=1) | |
| print("=" * 64) | |
| print(f"๊ฒฐํฉ ์คํธ๋ ์ค ์๋ฎฌ ์๋ฃ โ {dt/60:.1f}๋ถ (์ค๋ฌธ {N_SURVEY:,} + ํ๋ {N_SESS:,}รโค{MAX_ACT})") | |
| print("=" * 64) | |
| print(f" [E1] ๋ฒ์/NaN/์์ธ : {len(anom['E1'])} ๊ฑด") | |
| print(f" [E2] ๋ฒ์ฉ intent ๋์ถ : {len(anom['E2'])} ๊ฑด") | |
| print(f" [E3] ์์ ๋์ (โฅ3) : {len(anom['E3'])} ๊ฑด") | |
| print(f" [E5] stuck(top1 โฅ{STUCK_N}์ฐ์): {len(stuck_runs)} ์ธ์ ") | |
| print(f" ์ค๋ฌธ top-1 ๋ค์์ฑ: {len(top1_survey)}์ข , HHI={hhi:.3f}") | |
| print(f" ์ค๋ฌธ top-1 ์ต๋น: " + ", ".join(f"{NM[i][:10]} {c/n_surv_top1*100:.0f}%" for i, c in top1_survey.most_common(5))) | |
| print(f" ํ๋ ๋ฐ์๋ฅ <90% entity: {low_ent if low_ent else '์์ โ'}") | |
| if stuck_runs: | |
| print(" stuck ์ํ:", [(s[1], s[2]) for s in stuck_runs[:5]]) | |
| print(f"\n์์ธ ๋ฆฌํฌํธ: {REPORT}") | |
| if __name__ == "__main__": | |
| run() | |