ableman82 commited on
Commit
fa38e76
ยท
verified ยท
1 Parent(s): e7efcac

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +215 -29
app.py CHANGED
@@ -36,7 +36,10 @@ CLAUDE_MODEL = "claude-sonnet-4-6" # 2026๋…„ 5์›” ํ˜„์žฌ ๊ถŒ์žฅ ๋ชจ๋ธ
36
  # ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ์„ 2๋ฒˆ ์ˆ˜ํ–‰ (2-hop)
37
 
38
  def build_training_data_v2(n_normal=200, n_fraud=50, seed=42):
39
- """4์„ธ๋Œ€ GNN์šฉ ํ™•์žฅ ํ•™์Šต ๋ฐ์ดํ„ฐ (250๊ฑด)"""
 
 
 
40
  np.random.seed(seed)
41
  normal_arr = np.column_stack([
42
  np.random.normal(100, 50, n_normal),
@@ -179,20 +182,22 @@ gnn_model = train_gnn()
179
  FEATURES = ['๊ธˆ์•ก', '์‹œ๊ฐ„', '์‹ ๊ทœ์ˆ˜์ทจ์ธ', '๊ธˆ์•ก๋น„์œจ']
180
 
181
  def build_training_data():
182
- """ํ•™์Šต ๋ฐ์ดํ„ฐ ์ƒ์„ฑ (์žฌํ˜„ ๊ฐ€๋Šฅํ•˜๋„๋ก ์‹œ๋“œ ๊ณ ์ •)"""
 
 
183
  np.random.seed(42)
184
  normal = pd.DataFrame({
185
- '๊ธˆ์•ก': np.random.normal(100, 50, 20),
186
- '์‹œ๊ฐ„': np.random.normal(14, 4, 20),
187
- '์‹ ๊ทœ์ˆ˜์ทจ์ธ': np.random.binomial(1, 0.1, 20),
188
- '๊ธˆ์•ก๋น„์œจ': np.random.normal(1, 0.5, 20),
189
  '๋ผ๋ฒจ': 0
190
  })
191
  fraud = pd.DataFrame({
192
- '๊ธˆ์•ก': np.random.normal(500, 200, 5),
193
- '์‹œ๊ฐ„': np.random.normal(3, 2, 5),
194
- '์‹ ๊ทœ์ˆ˜์ทจ์ธ': np.random.binomial(1, 0.8, 5),
195
- '๊ธˆ์•ก๋น„์œจ': np.random.normal(10, 5, 5),
196
  '๋ผ๋ฒจ': 1
197
  })
198
  return pd.concat([normal, fraud], ignore_index=True)
@@ -268,6 +273,47 @@ def card_header(gen_label, title, decision_text, bg_color, text_color, sub):
268
  def formula_box(html):
269
  return f"""<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:'Courier New',monospace; font-size:12px; margin-bottom:10px; line-height:1.6;">{html}</div>"""
270
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
271
  CARD_STYLE = ("background:#fff; border:0.5px solid rgba(0,0,0,0.15); "
272
  "border-radius:12px; padding:16px 20px; margin-bottom:14px;")
273
 
@@ -294,9 +340,22 @@ def render_gen1(amount, hour, new_payee_bin, ratio):
294
  <td style="text-align:right; padding:6px 4px; font-weight:500; color:{td_color};">+{applied}</td>
295
  </tr>"""
296
 
 
 
 
 
 
 
 
 
 
 
 
 
297
  return f"""
298
  <div style="{CARD_STYLE}">
299
  {card_header("GEN 1 ยท RULE-BASED", "๊ทœ์น™ ๊ธฐ๋ฐ˜ ํŒ๋‹จ", dec, bg, fg, f"๋ˆ„์  {score}์  / 100์ ")}
 
300
  {formula_box("์ด์  = ฮฃ (๋ฐœ๋™๋œ ๋ฃฐ์˜ ๊ฐ€์ค‘์น˜) โ†’ ์ž„๊ณ„๊ฐ’ ๋น„๊ต (โ‰ฅ70 ์ฐจ๋‹จ / โ‰ฅ40 ์ถ”๊ฐ€์ธ์ฆ)")}
301
  <table style="width:100%; font-size:13px; border-collapse:collapse;">
302
  <thead>
@@ -351,9 +410,23 @@ def render_gen2(amount, hour, new_payee_bin, ratio):
351
  f"<span style='font-weight:500;'>{prob:.4f} โ†’ {prob*100:.2f}%</span>"
352
  )
353
 
 
 
 
 
 
 
 
 
 
 
 
 
 
354
  return f"""
355
  <div style="{CARD_STYLE}">
356
- {card_header("GEN 2 ยท LOGISTIC REGRESSION", "๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
 
357
  {formula_box("z = wโ‚ยท๊ธˆ์•ก + wโ‚‚ยท์‹œ๊ฐ„ + wโ‚ƒยท์‹ ๊ทœ์ˆ˜์ทจ์ธ + wโ‚„ยท๊ธˆ์•ก๋น„์œจ + b<br>P(์‚ฌ๊ธฐ) = 1 / (1 + e<sup>-z</sup>)")}
358
  <table style="width:100%; font-size:13px; border-collapse:collapse;">
359
  <thead>
@@ -395,30 +468,113 @@ def render_gen3(amount, hour, new_payee_bin, ratio):
395
  <span style="text-align:right; font-family:monospace; color:#666;">{imp:.3f}</span>
396
  </div>"""
397
 
398
- # ๋Œ€ํ‘œ ํŠธ๋ฆฌ ๋ถ„๊ธฐ ๊ฒฝ๋กœ (์ž…๋ ฅ๊ฐ’ ๊ธฐ๋ฐ˜)
399
- if ratio < 4.8:
400
- leaf_val = "-0.18" if amount < 320 else "+0.12"
401
- path_lines = [
402
- f"[๊ธˆ์•ก๋น„์œจ &lt; 4.8] โ†’ <b>Yes</b> (์ž…๋ ฅ {ratio:.1f})",
403
- f" โ””โ”€ [๊ธˆ์•ก &lt; 320] โ†’ {'Yes' if amount < 320 else 'No'} โ†’ leaf=<b>{leaf_val}</b> โ† ๋ณธ ๊ฑฐ๋ž˜ ๋„๋‹ฌ",
404
- ]
405
- else:
406
- leaf_val = "+0.41" if new_payee_bin == 1 else "+0.08"
407
- path_lines = [
408
- f"[๊ธˆ์•ก๋น„์œจ &lt; 4.8] โ†’ <b>No</b> (์ž…๋ ฅ {ratio:.1f})",
409
- f" โ””โ”€ [์‹ ๊ทœ์ˆ˜์ทจ์ธ &lt; 0.5] โ†’ {'Yes' if new_payee_bin == 0 else 'No'} โ†’ leaf=<b>{leaf_val}</b> โ† ๋ณธ ๊ฑฐ๋ž˜ ๋„๋‹ฌ",
410
- ]
411
- tree_html = "<br>".join(path_lines)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
 
413
  return f"""
414
  <div style="{CARD_STYLE}">
415
- {card_header("GEN 3 ยท XGBOOST (TREE ENSEMBLE)", "๊ทธ๋ž˜๋””์–ธํŠธ ๋ถ€์ŠคํŒ… ํŠธ๋ฆฌ", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
 
416
  {formula_box("F(x) = ฮฃ<sub>k=1..K</sub> f<sub>k</sub>(x), &nbsp; f<sub>k</sub> โˆˆ ํŠธ๋ฆฌ ๊ณต๊ฐ„<br>P(์‚ฌ๊ธฐ) = sigmoid(F(x))&nbsp;&nbsp;[K=10, max_depth=3, lr=0.1]")}
417
  <p style="font-size:13px; color:#666; margin:12px 0 6px;">ํ”ผ์ฒ˜ ์ค‘์š”๋„ (Gain ๊ธฐ๋ฐ˜)</p>
418
  <div style="display:flex; flex-direction:column; gap:6px; font-size:13px;">{imp_bars}</div>
419
- <p style="font-size:13px; color:#666; margin:14px 0 6px;">๋Œ€ํ‘œ ํŠธ๋ฆฌ (Tree #1) - ๋ณธ ๊ฑฐ๋ž˜ ๋ถ„๊ธฐ ๊ฒฝ๋กœ</p>
420
- {formula_box(tree_html)}
421
- <p style="font-size:12px; color:#888; margin:10px 0 0; font-style:italic;">ํŠธ๋ฆฌ 10๊ฐœ์˜ leaf ๊ฐ’์„ ํ•ฉ์‚ฐํ•œ raw score๋ฅผ ์‹œ๊ทธ๋ชจ์ด๋“œ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์ตœ์ข… ํ™•๋ฅ  ์‚ฐ์ถœ</p>
 
 
 
422
  </div>
423
  """
424
 
@@ -652,9 +808,24 @@ def render_gen4(amount, hour, new_payee_bin, ratio, prob3):
652
  </table>
653
  """
654
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
655
  return prob, f"""
656
  <div style="{CARD_STYLE}">
657
  {card_header("GEN 4 ยท GNN (GRAPH NEURAL NETWORK)", "๊ทธ๋ž˜ํ”„ ์‹ ๊ฒฝ๋ง (numpy ํ•™์Šต)", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
 
658
  {formula_box("h<sub>v</sub><sup>(l+1)</sup> = ReLU(W<sup>(l)</sup> ยท AGG({{h<sub>u</sub><sup>(l)</sup> : u โˆˆ N(v)}}))<br>P(์‚ฌ๊ธฐ) = sigmoid(W<sub>mlp</sub> ยท h<sub>๊ฑฐ๋ž˜</sub><sup>(2)</sup>) &nbsp; [2-hop ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ, 250๊ฑด ํ•™์Šต๋จ]")}
659
  <p style="font-size:13px; color:#666; margin:12px 0 6px;">2-hop ์ด์›ƒ ๊ทธ๋ž˜ํ”„</p>
660
  {svg}
@@ -874,9 +1045,24 @@ def render_gen5_card(amount, hour, new_payee_bin, ratio, prior_avg, prior_dec,
874
  f"{judgment_text}"
875
  )
876
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
  return f"""
878
  <div style="{CARD_STYLE}">
879
  {card_header("GEN 5 ยท FOUNDATION MODEL (LLM)", f"์ดˆ๊ฑฐ๋Œ€ ์ถ”๋ก  ๋ชจ๋ธ ({source_label})", dec, bg, fg, f"์˜์‹ฌ๋„ {prob*100:.0f}%")}
 
880
  <p style="font-size:13px; color:#666; margin:4px 0 6px;">์ปจํ…์ŠคํŠธ ํ† ํฐํ™”</p>
881
  <div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:monospace; font-size:11px; line-height:1.7; margin-bottom:12px;">{context_html}</div>
882
  <p style="font-size:13px; color:#666; margin:4px 0 6px;">์ถ”๋ก  ์ฒด์ธ (Chain-of-Thought)</p>
 
36
  # ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ์„ 2๋ฒˆ ์ˆ˜ํ–‰ (2-hop)
37
 
38
  def build_training_data_v2(n_normal=200, n_fraud=50, seed=42):
39
+ """4์„ธ๋Œ€ GNN์šฉ ํ•™์Šต ๋ฐ์ดํ„ฐ (250๊ฑด, 1-3์„ธ๋Œ€์™€ ๋™์ผ ๋ถ„ํฌยท๋™์ผ ๊ทœ๋ชจ).
40
+ 1-3์„ธ๋Œ€์˜ build_training_data()์™€ ๊ฐ™์€ ์ •๊ทœ๋ถ„ํฌ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ
41
+ ๋ชจ๋“  ์„ธ๋Œ€๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ ๊ทœ๋ชจ์™€ ๋ถ„ํฌ๋กœ ํ•™์Šต๋˜๋„๋ก ๋ณด์žฅ.
42
+ """
43
  np.random.seed(seed)
44
  normal_arr = np.column_stack([
45
  np.random.normal(100, 50, n_normal),
 
182
  FEATURES = ['๊ธˆ์•ก', '์‹œ๊ฐ„', '์‹ ๊ทœ์ˆ˜์ทจ์ธ', '๊ธˆ์•ก๋น„์œจ']
183
 
184
  def build_training_data():
185
+ """ํ•™์Šต ๋ฐ์ดํ„ฐ ์ƒ์„ฑ (250๊ฑด: ์ •์ƒ 200 + ์‚ฌ๊ธฐ 50)
186
+ 1-3์„ธ๋Œ€ ๋ชจ๋‘ ๋™์ผํ•œ 250๊ฑด์œผ๋กœ ํ•™์Šต๋˜์–ด, 4์„ธ๋Œ€ GNN๊ณผ ๋ฐ์ดํ„ฐ ๊ทœ๋ชจ๊ฐ€ ๊ฐ™์Œ.
187
+ """
188
  np.random.seed(42)
189
  normal = pd.DataFrame({
190
+ '๊ธˆ์•ก': np.random.normal(100, 50, 200),
191
+ '์‹œ๊ฐ„': np.random.normal(14, 4, 200),
192
+ '์‹ ๊ทœ์ˆ˜์ทจ์ธ': np.random.binomial(1, 0.1, 200),
193
+ '๊ธˆ์•ก๋น„์œจ': np.random.normal(1, 0.5, 200),
194
  '๋ผ๋ฒจ': 0
195
  })
196
  fraud = pd.DataFrame({
197
+ '๊ธˆ์•ก': np.random.normal(500, 200, 50),
198
+ '์‹œ๊ฐ„': np.random.normal(3, 2, 50),
199
+ '์‹ ๊ทœ์ˆ˜์ทจ์ธ': np.random.binomial(1, 0.8, 50),
200
+ '๊ธˆ์•ก๋น„์œจ': np.random.normal(10, 5, 50),
201
  '๋ผ๋ฒจ': 1
202
  })
203
  return pd.concat([normal, fraud], ignore_index=True)
 
273
  def formula_box(html):
274
  return f"""<div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:'Courier New',monospace; font-size:12px; margin-bottom:10px; line-height:1.6;">{html}</div>"""
275
 
276
+ def feature_setup_box(actor_label, actor_color, items, explanation):
277
+ """๊ฐ ์„ธ๋Œ€ ์นด๋“œ ์ƒ๋‹จ์— 'Feature/Rule์ด ์–ด๋–ป๊ฒŒ ๊ฒฐ์ •๋๋Š”๊ฐ€'๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ณตํ†ต ๋ฐ•์Šค.
278
+
279
+ actor_label: '์‚ฌ๋žŒ์ด ๊ฒฐ์ •' / '๋ชจ๋ธ์ด ํ•™์Šต' / 'ํ•˜์ด๋ธŒ๋ฆฌ๋“œ'
280
+ actor_color: ๋ฐฐ์ง€ ์ƒ‰ ('blue' | 'orange' | 'mixed')
281
+ items: [(ํ•ญ๋ชฉ๋ช…, ๊ฒฐ์ • ์ฃผ์ฒด, ์„ค๋ช…)] ๋ฆฌ์ŠคํŠธ
282
+ explanation: ๋ฐ•์Šค ํ•˜๋‹จ์˜ ํ•œ ์ค„ ๊ฐ•์กฐ ๋ฉ”์‹œ์ง€
283
+ """
284
+ color_map = {
285
+ 'human': ('#E6F1FB', '#0C447C'), # ํŒŒ๋ž‘ = ์‚ฌ๋žŒ
286
+ 'model': ('#FAECE7', '#993C1D'), # ์ฃผํ™ฉ = ๋ชจ๋ธ
287
+ 'mixed': ('#F1EFE8', '#5F5E5A'), # ํšŒ์ƒ‰ = ํ˜ผํ•ฉ
288
+ }
289
+ badge_bg, badge_fg = color_map.get(actor_color, color_map['mixed'])
290
+
291
+ rows = ""
292
+ for name, actor, desc in items:
293
+ a_bg, a_fg = color_map.get(actor, color_map['mixed'])
294
+ rows += (
295
+ f"<tr>"
296
+ f"<td style='padding:5px 8px; color:#444; width:30%;'>{name}</td>"
297
+ f"<td style='padding:5px 8px; width:20%;'>"
298
+ f"<span style='background:{a_bg}; color:{a_fg}; font-size:10px; padding:2px 8px; border-radius:6px; font-weight:500;'>{actor}</span>"
299
+ f"</td>"
300
+ f"<td style='padding:5px 8px; color:#666; font-size:12px;'>{desc}</td>"
301
+ f"</tr>"
302
+ )
303
+
304
+ return f"""
305
+ <div style="background:#FAFAF7; border:0.5px solid rgba(0,0,0,0.08); border-radius:8px; padding:10px 14px; margin-bottom:14px;">
306
+ <div style="display:flex; align-items:center; gap:10px; margin-bottom:8px;">
307
+ <p style="font-size:12px; font-weight:500; color:#444; margin:0;">โš™๏ธ FeatureยทRule ๊ฒฐ์ • ๋ฐฉ์‹</p>
308
+ <span style="background:{badge_bg}; color:{badge_fg}; font-size:10px; padding:3px 10px; border-radius:6px; font-weight:500;">{actor_label}</span>
309
+ </div>
310
+ <table style="width:100%; font-size:13px; border-collapse:collapse;">
311
+ <tbody>{rows}</tbody>
312
+ </table>
313
+ <p style="font-size:11px; color:#888; margin:8px 0 0; font-style:italic; line-height:1.5;">{explanation}</p>
314
+ </div>
315
+ """
316
+
317
  CARD_STYLE = ("background:#fff; border:0.5px solid rgba(0,0,0,0.15); "
318
  "border-radius:12px; padding:16px 20px; margin-bottom:14px;")
319
 
 
340
  <td style="text-align:right; padding:6px 4px; font-weight:500; color:{td_color};">+{applied}</td>
341
  </tr>"""
342
 
343
+ gen1_setup = feature_setup_box(
344
+ actor_label="100% ์‚ฌ๋žŒ ๊ฒฐ์ •",
345
+ actor_color='human',
346
+ items=[
347
+ ("์ž…๋ ฅ Feature 4๊ฐœ", "์‚ฌ๋žŒ", "๋„๋ฉ”์ธ ์ „๋ฌธ๊ฐ€๊ฐ€ '๊ธˆ์•กยท์‹œ๊ฐ„ยท์‹ ๊ทœ์ˆ˜์ทจ์ธยท๊ธˆ์•ก๋น„์œจ'์„ ์‚ฌ๊ธฐ ํŒ๋‹จ ๊ธฐ๏ฟฝ๏ฟฝ๏ฟฝ์œผ๋กœ ์„ ์ •"),
348
+ ("๋ฃฐ ์กฐ๊ฑด (์ž„๊ณ„๊ฐ’)", "์‚ฌ๋žŒ", "โ‰ฅ500๋งŒ์›, โ‰ค6์‹œ ๋˜๋Š” โ‰ฅ22์‹œ, =1, โ‰ฅ5๋ฐฐ โ€” ๋ชจ๋‘ ์‚ฌ๋žŒ์ด ์ง์ ‘ ๊ฒฐ์ •"),
349
+ ("๋ฃฐ๋ณ„ ๊ฐ€์ค‘์น˜", "์‚ฌ๋žŒ", "40 / 30 / 20 / 10์  โ€” ๋„๋ฉ”์ธ ๊ฒฝํ—˜์— ๋”ฐ๋ผ ์‚ฌ๋žŒ์ด ๋ถ€์—ฌ"),
350
+ ("ํŒ์ • ์ž„๊ณ„๊ฐ’", "์‚ฌ๋žŒ", "70์  ์ด์ƒ ์ฐจ๋‹จ, 40์  ์ด์ƒ ์ถ”๊ฐ€์ธ์ฆ โ€” ์šด์˜ํŒ€์ด ๋น„์ฆˆ๋‹ˆ์Šค ํŒ๋‹จ์œผ๋กœ ๊ฒฐ์ •"),
351
+ ],
352
+ explanation="๋ชจ๋“  ๊ฒฐ์ •์ด ์‚ฌ๋žŒ์˜ ๋„๋ฉ”์ธ ์ง€์‹์— ์˜์กด. ํ•™์Šต ๋ฐ์ดํ„ฐ๋Š” ์‚ฌ์šฉํ•˜์ง€ ์•Š์Œ. ์ƒˆ ์‚ฌ๊ธฐ ํŒจํ„ด ๋“ฑ์žฅ ์‹œ ์‚ฌ๋žŒ์ด ๋ฃฐ์„ ์ถ”๊ฐ€ํ•ด์•ผ ํ•จ."
353
+ )
354
+
355
  return f"""
356
  <div style="{CARD_STYLE}">
357
  {card_header("GEN 1 ยท RULE-BASED", "๊ทœ์น™ ๊ธฐ๋ฐ˜ ํŒ๋‹จ", dec, bg, fg, f"๋ˆ„์  {score}์  / 100์ ")}
358
+ {gen1_setup}
359
  {formula_box("์ด์  = ฮฃ (๋ฐœ๋™๋œ ๋ฃฐ์˜ ๊ฐ€์ค‘์น˜) โ†’ ์ž„๊ณ„๊ฐ’ ๋น„๊ต (โ‰ฅ70 ์ฐจ๋‹จ / โ‰ฅ40 ์ถ”๊ฐ€์ธ์ฆ)")}
360
  <table style="width:100%; font-size:13px; border-collapse:collapse;">
361
  <thead>
 
410
  f"<span style='font-weight:500;'>{prob:.4f} โ†’ {prob*100:.2f}%</span>"
411
  )
412
 
413
+ gen2_setup = feature_setup_box(
414
+ actor_label="ํ”ผ์ฒ˜๋Š” ์‚ฌ๋žŒ, ๊ฐ€์ค‘์น˜๋Š” ๋ชจ๋ธ",
415
+ actor_color='mixed',
416
+ items=[
417
+ ("์ž…๋ ฅ Feature 4๊ฐœ", "์‚ฌ๋žŒ", "1์„ธ๋Œ€์™€ ๋™์ผํ•œ 4๊ฐœ ์ปฌ๋Ÿผ์„ ์‚ฌ๋žŒ์ด ์„ ์ • (ํ”ผ์ฒ˜ ์—”์ง€๋‹ˆ์–ด๋ง)"),
418
+ ("ํ•™์Šต ๋ฐ์ดํ„ฐ", "์‚ฌ๋žŒ", "250๊ฑด์˜ ๊ฑฐ๋ž˜์— ์‚ฌ๊ธฐ/์ •์ƒ ๋ผ๋ฒจ์„ ์‚ฌ๋žŒ์ด ๋ถ€์—ฌ"),
419
+ ("๊ฐ€์ค‘์น˜ wโ‚~wโ‚„", "๋ชจ๋ธ", "fit() ํ˜ธ์ถœ ์‹œ L-BFGS ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์ž๋™ ํ•™์Šต"),
420
+ ("์ ˆํŽธ b", "๋ชจ๋ธ", "๋ฐ์ดํ„ฐ์˜ ์‚ฌ๊ธฐ ๋น„์œจ(50/250=20%)์— ๋งž์ถฐ ์ž๋™ ์กฐ์ •"),
421
+ ("ํŒ์ • ์ž„๊ณ„๊ฐ’", "์‚ฌ๋žŒ", "0.5(์ถ”๊ฐ€์ธ์ฆ) / 0.7(์ฐจ๋‹จ) โ€” ์šด์˜ํŒ€์ด ๊ฒฐ์ •"),
422
+ ],
423
+ explanation="ํ”ผ์ฒ˜๋Š” ์—ฌ์ „ํžˆ ์‚ฌ๋žŒ์ด ์ •์˜. ๋ชจ๋ธ์ด ํ•™์Šตํ•˜๋Š” ๊ฑด '4๊ฐœ ํ”ผ์ฒ˜์— ์–ด๋–ค ๊ฐ€์ค‘์น˜๋ฅผ ๊ณฑํ•ด์•ผ ์‚ฌ๊ธฐ๋ฅผ ์ž˜ ๋งž์ถ”๋Š”๊ฐ€'๋ฟ. ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ”๋€Œ๋ฉด ๊ฐ€์ค‘์น˜๋Š” ์ž๋™ ์žฌํ•™์Šต๋˜์ง€๋งŒ, ํ”ผ์ฒ˜ ์ž์ฒด๋Š” ์‚ฌ๋žŒ์ด ๋‹ค์‹œ ์„ค๊ณ„ํ•ด์•ผ ํ•จ."
424
+ )
425
+
426
  return f"""
427
  <div style="{CARD_STYLE}">
428
+ {card_header("GEN 2 ยท LOGISTIC REGRESSION", "๋กœ์ง€์Šคํ‹ฑ ํšŒ๊ท€ (์„ ํ˜• ๋ชจ๋ธ)", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
429
+ {gen2_setup}
430
  {formula_box("z = wโ‚ยท๊ธˆ์•ก + wโ‚‚ยท์‹œ๊ฐ„ + wโ‚ƒยท์‹ ๊ทœ์ˆ˜์ทจ์ธ + wโ‚„ยท๊ธˆ์•ก๋น„์œจ + b<br>P(์‚ฌ๊ธฐ) = 1 / (1 + e<sup>-z</sup>)")}
431
  <table style="width:100%; font-size:13px; border-collapse:collapse;">
432
  <thead>
 
468
  <span style="text-align:right; font-family:monospace; color:#666;">{imp:.3f}</span>
469
  </div>"""
470
 
471
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
472
+ # ์ง„์งœ ํ•™์Šต๋œ ํŠธ๋ฆฌ 10๊ฐœ ์ „์ฒด์—์„œ ๋ณธ ๊ฑฐ๋ž˜๊ฐ€ ๋„๋‹ฌํ•œ leaf ๊ฐ’์„ ์ถ”์ถœ
473
+ # โ†’ ๋ถ€์ŠคํŒ…์˜ ๋ณธ์งˆ(ํŠธ๋ฆฌ๋“ค์ด ๋ˆ„์ ๋˜์–ด ์ ์ˆ˜๋ฅผ ๋งŒ๋“ ๋‹ค)์„ ์ •์งํ•˜๊ฒŒ ๋…ธ์ถœ
474
+ # โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
475
+ booster = gen3_model.get_booster()
476
+ trees_df = booster.trees_to_dataframe()
477
+ input_values = {'๊ธˆ์•ก': amount, '์‹œ๊ฐ„': hour, '์‹ ๊ทœ์ˆ˜์ทจ์ธ': new_payee_bin, '๊ธˆ์•ก๋น„์œจ': ratio}
478
+
479
+ tree_traces = [] # ๊ฐ ํŠธ๋ฆฌ์˜ (๋ถ„๊ธฐ ๊ฒฝ๋กœ, leaf ๊ฐ’)์„ ์ €์žฅ
480
+ for tree_id in range(10):
481
+ tree = trees_df[trees_df['Tree'] == tree_id].set_index('ID')
482
+ # ๋ฃจํŠธ ๋…ธ๋“œ๋ถ€ํ„ฐ leaf๊นŒ์ง€ ๋”ฐ๋ผ๊ฐ€๊ธฐ
483
+ current_id = f"{tree_id}-0"
484
+ path = []
485
+ leaf_val = 0.0
486
+ while True:
487
+ row = tree.loc[current_id]
488
+ if row['Feature'] == 'Leaf':
489
+ leaf_val = float(row['Gain'])
490
+ break
491
+ feature = row['Feature']
492
+ split = float(row['Split'])
493
+ input_v = input_values[feature]
494
+ if input_v < split:
495
+ path.append(f"[{feature} &lt; {split:.2f}] Yes")
496
+ current_id = row['Yes']
497
+ else:
498
+ path.append(f"[{feature} &lt; {split:.2f}] No")
499
+ current_id = row['No']
500
+ tree_traces.append((path, leaf_val))
501
+
502
+ # raw_score ํ•ฉ์‚ฐ (์‹ค์ œ XGBoost ๋‚ด๋ถ€ ๋™์ž‘ ์žฌํ˜„)
503
+ raw_score = sum(leaf for _, leaf in tree_traces)
504
+ # XGBoost ๋‚ด๋ถ€ base_score ์ถ”๊ฐ€ (๊ธฐ๋ณธ 0.5 โ†’ logit 0)
505
+ # logit ํ•ฉ์‚ฐ ํ›„ sigmoid โ†’ predict_proba์™€ ๊ฑฐ์˜ ์ผ์น˜ํ•ด์•ผ ํ•จ
506
+
507
+ # ํŠธ๋ฆฌ๋ณ„ leaf ๊ฐ’ ๋ˆ„์  ํ…Œ์ด๋ธ”
508
+ tree_rows = ""
509
+ cumulative = 0.0
510
+ for tree_id, (path, leaf) in enumerate(tree_traces):
511
+ cumulative += leaf
512
+ path_text = " โ†’ ".join(path) + f" โ†’ <b>leaf={leaf:+.4f}</b>"
513
+ leaf_color = "#4A1B0C" if leaf > 0 else "#04342C"
514
+ leaf_bg = "#FAECE7" if leaf > 0 else "#E1F5EE"
515
+ tree_rows += (
516
+ f"<tr style='background:{leaf_bg};'>"
517
+ f"<td style='padding:5px 6px; font-family:monospace;'>#{tree_id}</td>"
518
+ f"<td style='padding:5px 6px; font-size:11px; color:#555;'>{path_text}</td>"
519
+ f"<td style='text-align:right; padding:5px 6px; font-family:monospace; color:{leaf_color}; font-weight:500;'>{leaf:+.4f}</td>"
520
+ f"<td style='text-align:right; padding:5px 6px; font-family:monospace; color:#666;'>{cumulative:+.4f}</td>"
521
+ f"</tr>"
522
+ )
523
+
524
+ sigmoid_result = 1 / (1 + np.exp(-raw_score))
525
+
526
+ tree_table = f"""
527
+ <table style="width:100%; font-size:12px; border-collapse:collapse;">
528
+ <thead>
529
+ <tr style="background:#f5f5f0; border-bottom:0.5px solid rgba(0,0,0,0.15);">
530
+ <th style="text-align:left; padding:6px; font-weight:500; color:#666; width:8%;">ํŠธ๋ฆฌ</th>
531
+ <th style="text-align:left; padding:6px; font-weight:500; color:#666; width:60%;">๋ณธ ๊ฑฐ๋ž˜์˜ ๋ถ„๊ธฐ ๊ฒฝ๋กœ โ†’ ๋„๋‹ฌํ•œ leaf</th>
532
+ <th style="text-align:right; padding:6px; font-weight:500; color:#666; width:14%;">leaf ๊ฐ’</th>
533
+ <th style="text-align:right; padding:6px; font-weight:500; color:#666; width:18%;">๋ˆ„์  raw score</th>
534
+ </tr>
535
+ </thead>
536
+ <tbody>{tree_rows}
537
+ <tr style="background:#F1EFE8; font-weight:500; border-top:1px solid rgba(0,0,0,0.3);">
538
+ <td colspan="3" style="text-align:right; padding:6px;">์ตœ์ข… raw score (10๊ฐœ ํŠธ๋ฆฌ ํ•ฉ์‚ฐ)</td>
539
+ <td style="text-align:right; padding:6px; font-family:monospace;">{raw_score:+.4f}</td>
540
+ </tr>
541
+ </tbody>
542
+ </table>
543
+ """
544
+
545
+ final_calc = formula_box(
546
+ f"P(์‚ฌ๊ธฐ) = sigmoid({raw_score:+.4f}) "
547
+ f"= 1 / (1 + e<sup>{-raw_score:+.4f}</sup>) "
548
+ f"= <b>{sigmoid_result:.4f}</b> ({sigmoid_result*100:.2f}%)"
549
+ )
550
+
551
+ gen3_setup = feature_setup_box(
552
+ actor_label="ํ”ผ์ฒ˜๋Š” ์‚ฌ๋žŒ, ํŠธ๋ฆฌ ๊ตฌ์กฐ๋Š” ๋ชจ๋ธ",
553
+ actor_color='mixed',
554
+ items=[
555
+ ("์ž…๋ ฅ Feature 4๊ฐœ", "์‚ฌ๋žŒ", "1ยท2์„ธ๋Œ€์™€ ์™„์ „ํžˆ ๋™์ผํ•œ 4๊ฐœ ์ปฌ๋Ÿผ"),
556
+ ("ํ•™์Šต ๋ฐ์ดํ„ฐ", "์‚ฌ๋žŒ", "1ยท2์„ธ๋Œ€์™€ ๋™์ผํ•œ 250๊ฑด"),
557
+ ("ํŠธ๋ฆฌ ๋ถ„๊ธฐ ์ž„๊ณ„๊ฐ’", "๋ชจ๋ธ", "Gain ์ตœ๋Œ€ํ™”๋กœ ์ž๋™ ๊ฒฐ์ • (์˜ˆ: ์‹œ๊ฐ„ < 8.69, ๊ธˆ์•ก๋น„์œจ < 1.77)"),
558
+ ("๊ฐ leaf ๊ฐ’", "๋ชจ๋ธ", "๊ฐ leaf์— ๋„๋‹ฌํ•œ ์ƒ˜ํ”Œ๋“ค์˜ ์ž”์ฐจ๋กœ ์ž๋™ ๊ณ„์‚ฐ"),
559
+ ("ํŠธ๋ฆฌ ๊ฐœ์ˆ˜ / ๊นŠ์ด", "์‚ฌ๋žŒ", "n_estimators=10, max_depth=3 (ํ•˜์ดํผํŒŒ๋ผ๋ฏธํ„ฐ)"),
560
+ ("ํŒ์ • ์ž„๊ณ„๊ฐ’", "์‚ฌ๋žŒ", "0.5(์ถ”๊ฐ€์ธ์ฆ) / 0.7(์ฐจ๋‹จ)"),
561
+ ],
562
+ explanation="2์„ธ๋Œ€๋ณด๋‹ค ํ•™์Šต๋˜๋Š” ๋ถ€๋ถ„์ด ํ›จ์”ฌ ๋งŽ์•„์ง. ๋ถ„๊ธฐ ์ž„๊ณ„๊ฐ’(์™œ 4.8์ด ์•„๋‹ˆ๋ผ 1.77์ธ๊ฐ€)๊ณผ leaf ๊ฐ’ ๋ชจ๋‘ ๋ฐ์ดํ„ฐ์—์„œ ์ž๋™ ๋ฐœ๊ฒฌ. ๋‹จ ์ž…๋ ฅ ํ”ผ์ฒ˜ ์ž์ฒด๋Š” ์—ฌ์ „ํžˆ ์‚ฌ๋žŒ์ด ์ •ํ•œ 4๊ฐœ."
563
+ )
564
 
565
  return f"""
566
  <div style="{CARD_STYLE}">
567
+ {card_header("GEN 3 ยท XGBOOST (TREE ENSEMBLE)", "XGBoost (ํŠธ๋ฆฌ 10๊ฐœ ์•™์ƒ๋ธ”)", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
568
+ {gen3_setup}
569
  {formula_box("F(x) = ฮฃ<sub>k=1..K</sub> f<sub>k</sub>(x), &nbsp; f<sub>k</sub> โˆˆ ํŠธ๋ฆฌ ๊ณต๊ฐ„<br>P(์‚ฌ๊ธฐ) = sigmoid(F(x))&nbsp;&nbsp;[K=10, max_depth=3, lr=0.1]")}
570
  <p style="font-size:13px; color:#666; margin:12px 0 6px;">ํ”ผ์ฒ˜ ์ค‘์š”๋„ (Gain ๊ธฐ๋ฐ˜)</p>
571
  <div style="display:flex; flex-direction:column; gap:6px; font-size:13px;">{imp_bars}</div>
572
+ <p style="font-size:13px; color:#666; margin:14px 0 6px;">๐ŸŒณ ํ•™์Šต๋œ ํŠธ๋ฆฌ 10๊ฐœ์˜ leaf ๊ฐ’ ๋ˆ„์  (๋ถ€์ŠคํŒ… ํ•ต์‹ฌ)</p>
573
+ <p style="font-size:12px; color:#888; margin:0 0 8px; font-style:italic;">๊ฐ ํŠธ๋ฆฌ๊ฐ€ ์ด์ „ ํŠธ๋ฆฌ์˜ ์ž”์ฐจ๋ฅผ ๋ณด๏ฟฝ๏ฟฝํ•˜๋ฉฐ leaf ๊ฐ’์„ ๋”ํ•ด๊ฐ โ†’ ๋ˆ„์ ๋œ raw score๋ฅผ sigmoid๋กœ ๋ณ€ํ™˜</p>
574
+ {tree_table}
575
+ <p style="font-size:13px; color:#666; margin:14px 0 6px;">๐Ÿงฎ ์ตœ์ข… ํ™•๋ฅ  ๊ณ„์‚ฐ</p>
576
+ {final_calc}
577
+ <p style="font-size:12px; color:#888; margin:10px 0 0; font-style:italic;">๊ฐ•์ : ๋น„์„ ํ˜• ํŒจํ„ดยทํ”ผ์ฒ˜ ์ƒํ˜ธ์ž‘์šฉ ์ž๋™ ํ•™์Šต. ํ•œ๊ณ„: ํ•™์Šต ๋ฐ์ดํ„ฐ ๋ถ„ํฌ ๋ฐ–์˜ ์ผ€์ด์Šค(์˜ˆ: ์ „์„ธ ์ž”๊ธˆ)๋Š” ์—ฌ์ „ํžˆ ๋ชป ์žก์Œ</p>
578
  </div>
579
  """
580
 
 
808
  </table>
809
  """
810
 
811
+ gen4_setup = feature_setup_box(
812
+ actor_label="๊ตฌ์กฐ๋Š” ์‚ฌ๋žŒ, ์ž„๋ฒ ๋”ฉ์€ ๋ชจ๋ธ ์ƒ์„ฑ",
813
+ actor_color='model',
814
+ items=[
815
+ ("๊ทธ๋ž˜ํ”„ ๊ตฌ์กฐ ์ •์˜", "์‚ฌ๋žŒ", "๋…ธ๋“œ ์ข…๋ฅ˜(๊ฑฐ๋ž˜ยท์†ก๊ธˆ์ธยท์ˆ˜์ทจ์ธยท๋‹จ๋ง๊ธฐ)์™€ ์—ฃ์ง€ ๊ด€๊ณ„๋Š” ์‚ฌ๋žŒ์ด ์„ค๊ณ„"),
816
+ ("ํ•™์Šต ๋ฐ์ดํ„ฐ", "์‚ฌ๋žŒ", "1-3์„ธ๋Œ€์™€ ๋™์ผํ•œ 250๊ฑด ๊ฑฐ๋ž˜์— ๋ผ๋ฒจ ๋ถ€์—ฌ"),
817
+ ("๋…ธ๋“œ ์ดˆ๊ธฐ ์ž„๋ฒ ๋”ฉ", "์‚ฌ๋žŒ", "๊ฐ ๋…ธ๋“œ์˜ 4์ฐจ์› ์ดˆ๊ธฐ๊ฐ’์€ ์‚ฌ๋žŒ์ด ์ธ์ฝ”๋”ฉ ๊ทœ์น™ ์ž‘์„ฑ"),
818
+ ("์€๋‹‰์ธต 16์ฐจ์› Feature", "๋ชจ๋ธ", "์‚ฌ๋žŒ์ด ์ •์˜ ์•ˆ ํ•จ. ๋ชจ๋ธ์ด ํ•™์Šต์œผ๋กœ 16๊ฐœ ์ต๋ช… ์ฐจ์›์„ ์ž๋™ ์ƒ์„ฑ"),
819
+ ("๊ฐ€์ค‘์น˜ Wโ‚, Wโ‚‚, W_mlp", "๋ชจ๋ธ", "337๊ฐœ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ backpropagation์œผ๋กœ ์ž๋™ ํ•™์Šต"),
820
+ ("ํŒ์ • ์ž„๊ณ„๊ฐ’", "์‚ฌ๋žŒ", "0.5(์ถ”๊ฐ€์ธ์ฆ) / 0.7(์ฐจ๋‹จ)"),
821
+ ],
822
+ explanation="๊ฒฐ์ •์  ์ฐจ์ด: 1-3์„ธ๋Œ€๋Š” ์‚ฌ๋žŒ์ด ์ •ํ•œ 4๊ฐœ ํ”ผ์ฒ˜๋งŒ ๋ดค์ง€๋งŒ, 4์„ธ๋Œ€๋Š” ๋ชจ๋ธ์ด 16+16=32๊ฐœ์˜ ์ƒˆ ์ต๋ช… ํ”ผ์ฒ˜๋ฅผ ์Šค์Šค๋กœ ๋งŒ๋“ค์–ด๋ƒ„. ์ด ์ต๋ช… ํ”ผ์ฒ˜๋“ค์˜ ์˜๋ฏธ๋Š” ์‚ฌ๋žŒ์ด ์‚ฌํ›„์—๋„ ๋Œ€๋ถ€๋ถ„ ํ•ด์„ ๋ถˆ๊ฐ€."
823
+ )
824
+
825
  return prob, f"""
826
  <div style="{CARD_STYLE}">
827
  {card_header("GEN 4 ยท GNN (GRAPH NEURAL NETWORK)", "๊ทธ๋ž˜ํ”„ ์‹ ๊ฒฝ๋ง (numpy ํ•™์Šต)", dec, bg, fg, f"์‚ฌ๊ธฐ ํ™•๋ฅ  {prob*100:.2f}%")}
828
+ {gen4_setup}
829
  {formula_box("h<sub>v</sub><sup>(l+1)</sup> = ReLU(W<sup>(l)</sup> ยท AGG({{h<sub>u</sub><sup>(l)</sup> : u โˆˆ N(v)}}))<br>P(์‚ฌ๊ธฐ) = sigmoid(W<sub>mlp</sub> ยท h<sub>๊ฑฐ๋ž˜</sub><sup>(2)</sup>) &nbsp; [2-hop ๋ฉ”์‹œ์ง€ ํŒจ์‹ฑ, 250๊ฑด ํ•™์Šต๋จ]")}
830
  <p style="font-size:13px; color:#666; margin:12px 0 6px;">2-hop ์ด์›ƒ ๊ทธ๋ž˜ํ”„</p>
831
  {svg}
 
1045
  f"{judgment_text}"
1046
  )
1047
 
1048
+ gen5_setup = feature_setup_box(
1049
+ actor_label="ํ”„๋กฌํ”„ํŠธ๋งŒ ์‚ฌ๋žŒ, ์ถ”๋ก ์€ ์ „์ ์œผ๋กœ ๋ชจ๋ธ",
1050
+ actor_color='model',
1051
+ items=[
1052
+ ("์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ", "์‚ฌ๋žŒ", "'๋‹น์‹ ์€ FDS ๋ถ„์„๊ฐ€์ž…๋‹ˆ๋‹ค' ๋“ฑ์˜ ์—ญํ•  ๋ถ€์—ฌ๋งŒ ์‚ฌ๋žŒ์ด ์ž‘์„ฑ"),
1053
+ ("ํ•™์Šต ๋ฐ์ดํ„ฐ", "๋ชจ๋ธ", "Anthropic์ด ์ธํ„ฐ๋„ท ๊ทœ๋ชจ ๋ฐ์ดํ„ฐ๋กœ ์‚ฌ์ „ ํ•™์Šต (์ˆ˜์กฐ ํ† ํฐ)"),
1054
+ ("๋„๋ฉ”์ธ ์ง€์‹", "๋ชจ๋ธ", "๋ณด์ด์Šคํ”ผ์‹ฑ ํŒจํ„ด, ๋ถ€๋™์‚ฐ ๊ฑฐ๋ž˜ ์ •ํ˜• ๋“ฑ์„ ์‚ฌ์ „ ํ•™์Šต์œผ๋กœ ๋ณด์œ "),
1055
+ ("์ถ”๋ก  ๋‹จ๊ณ„ (CoT)", "๋ชจ๋ธ", "๊ฐ ๋‹จ๊ณ„์—์„œ ๋ฌด์—‡์— ์ฃผ๋ชฉํ• ์ง€ ๋ชจ๋ธ์ด ์Šค์Šค๋กœ ๊ฒฐ์ •"),
1056
+ ("์ตœ์ข… ํŒ๋‹จ ๋ฌธ์žฅ", "๋ชจ๋ธ", "์ž์—ฐ์–ด๋กœ ์ž๋™ ์ƒ์„ฑ"),
1057
+ ("ํŒ์ • ์ž„๊ณ„๊ฐ’", "์‚ฌ๋žŒ", "0.5(์ถ”๊ฐ€์ธ์ฆ) / 0.7(์ฐจ๋‹จ)"),
1058
+ ],
1059
+ explanation="๊ฐ€์žฅ ๊ทน์ ์ธ ๋ณ€ํ™”: ์‚ฌ๋žŒ์ด ์ž…๋ ฅ์œผ๋กœ ์ฃผ๋Š” ๊ฑด '๊ฑฐ๋ž˜ ์ •๋ณด + ์‚ฌ์ „ ํŒ๋‹จ'๋ฟ. ๋ชจ๋ธ์ด ์‚ฌ์ „ ํ•™์Šต๋œ ๋„๋ฉ”์ธ ์ง€์‹์œผ๋กœ '์™œ ์‚ฌ๊ธฐ์ธ์ง€/์•„๋‹Œ์ง€'๋ฅผ ์ž์—ฐ์–ด๋กœ ์ถ”๋ก . 4์„ธ๋Œ€๊นŒ์ง€์˜ 250๊ฑด ํ•™์Šต๊ณผ๋Š” ์ฐจ์›์ด ๋‹ค๋ฅธ ๊ทœ๋ชจ์˜ ์‚ฌ์ „ ํ•™์Šต์ด ๊น”๋ ค ์žˆ์Œ."
1060
+ )
1061
+
1062
  return f"""
1063
  <div style="{CARD_STYLE}">
1064
  {card_header("GEN 5 ยท FOUNDATION MODEL (LLM)", f"์ดˆ๊ฑฐ๋Œ€ ์ถ”๋ก  ๋ชจ๋ธ ({source_label})", dec, bg, fg, f"์˜์‹ฌ๋„ {prob*100:.0f}%")}
1065
+ {gen5_setup}
1066
  <p style="font-size:13px; color:#666; margin:4px 0 6px;">์ปจํ…์ŠคํŠธ ํ† ํฐํ™”</p>
1067
  <div style="background:#f5f5f0; padding:10px 12px; border-radius:6px; font-family:monospace; font-size:11px; line-height:1.7; margin-bottom:12px;">{context_html}</div>
1068
  <p style="font-size:13px; color:#666; margin:4px 0 6px;">์ถ”๋ก  ์ฒด์ธ (Chain-of-Thought)</p>