File size: 26,253 Bytes
37fcd19
 
 
 
 
 
 
 
 
 
c4610c0
 
37fcd19
 
 
 
 
 
 
2a06dbe
37fcd19
 
2a06dbe
 
 
 
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4610c0
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f8a23d4
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47d1891
37fcd19
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5437d1d
37fcd19
47d1891
37fcd19
 
 
5437d1d
37fcd19
 
 
 
 
5437d1d
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4610c0
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c4610c0
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2a06dbe
 
 
 
 
 
37fcd19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
#!/usr/bin/env python3
"""
κ²°ν•©(bundle-v3) μ‹œλ‚˜λ¦¬μ˜€ μΉ΄νƒˆλ‘œκ·Έ 생성기.

단일 TAXONOMY μ •μ˜(μ†ŒμŠ€ 였브 트루슀)λ‘œλΆ€ν„°
  - intents.json          : L3 Intent μΉ΄νƒˆλ‘œκ·Έ (id/name/L1/L2/inference_type/features)
  - actions.json          : Intent별 3채널(push / call_center / agent) ν™œμš© μ˜ˆμ‹œ
  - behavior_intents.json  : behaviors.json entity β†’ 직접 μ‹ ν˜Έ Intent λ§€ν•‘
μ„Έ νŒŒμΌμ„ μΌκ΄€λ˜κ²Œ μƒμ„±ν•œλ‹€.

μ‹œλ‚˜λ¦¬μ˜€_κ²°ν•©:
  1.1 κ²°ν•© Domain Intent Taxonomy / 1.2 Metadata / 4.2 ν™œμš© λ°©μ•ˆ.

μ‹€ν–‰:
    cd roadshow-server-v3
    python scripts/build_bundle_scenario.py
"""
from __future__ import annotations

import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from scripts._scenario_common import emit_layers  # noqa: E402

SCENARIO_ID = "bundle-v3"
OUT_DIR = Path(__file__).parent.parent / "scenarios" / SCENARIO_ID

# ── L1 / L2 메타 ─────────────────────────────────────────────
L1 = {
    "1": ("INT-B1000", "κ°€μž… ν™•λŒ€"),
    "2": ("INT-B2000", "할인 μ΅œμ ν™”"),
    "3": ("INT-B3000", "νšŒμ„ /μ„œλΉ„μŠ€ ν™•μž₯"),
    "4": ("INT-B4000", "μœ μ§€/락인"),
    "5": ("INT-B5000", "μ΄νƒˆ κ²€ν† "),
}
L2 = {
    "11": ("INT-B1100", "κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 탐색"),
    "12": ("INT-B1200", "κ²°ν•© μƒν’ˆ 탐색"),
    "13": ("INT-B1300", "κ°€μž… ν˜œνƒ 탐색"),
    "14": ("INT-B1400", "κ°€μž… μ‹€ν–‰ κ²€ν† "),
    "21": ("INT-B2100", "ν˜„μž¬ ν˜œνƒ 점검"),
    "22": ("INT-B2200", "μΆ”κ°€ ν˜œνƒ 탐색"),
    "23": ("INT-B2300", "ν˜œνƒ ν™œμš© μ΅œμ ν™”"),
    "31": ("INT-B3100", "κ²°ν•© λ²”μœ„ ν™•λŒ€"),
    "32": ("INT-B3200", "λ””λ°”μ΄μŠ€/IoT ν™•μž₯"),
    "33": ("INT-B3300", "κ²°ν•© ꡬ쑰 μž¬κ΅¬μ„±"),
    "41": ("INT-B4100", "μž¬μ•½μ • κ²€ν† "),
    "42": ("INT-B4200", "μž₯κΈ° 고객 ν˜œνƒ 확인"),
    "43": ("INT-B4300", "κ²°ν•© μœ μ§€ 영ν–₯ 확인"),
    "51": ("INT-B5100", "κ²½μŸμ‚¬ 비ꡐ"),
    "52": ("INT-B5200", "ν˜œνƒ/λΉ„μš© 뢈만"),
    "53": ("INT-B5300", "ν’ˆμ§ˆ 뢈만"),
    "54": ("INT-B5400", "ν•΄μ§€ κ²€ν† "),
}


# ── L3 Intent μ •μ˜ ───────────────────────────────────────────
# 각 ν•­λͺ©:
#   key       : L3 μ½”λ“œ 3자리 (μ•ž 2μžλ¦¬κ°€ L2)
#   name      : Intent ν•œκΈ€λͺ…
#   type      : Rule | Model
#   features  : μΆ”λ‘  핡심 batch feature λͺ… (Index/Score)
#   entities  : 이 Intent둜 직접 μ‹ ν˜Έλ₯Ό μ£ΌλŠ” behavior entity (behavior_intents)
#   push      : μ•± Push 문ꡬ
#   cc        : (상황, μ•ˆλ‚΄) 상담사 μ»¨ν…μŠ€νŠΈ
#   agent     : AI Agent λ°œν™”
TAX: list[dict] = [
    # ── L1=1 κ°€μž… ν™•λŒ€ ───────────────────────────────────────
    dict(key="111", name="κ°€μ‘± κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 확인", type="Rule",
         features=["family_line_count", "Bundle Opportunity Index"],
         entities=["bundle_eligibility", "family_bundle"],
         push="κ°€μ‘± κ²°ν•© μ‹œ 받을 수 μžˆλŠ” 할인 ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("κ°€μ‘± κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 탐색", "κ°€μ‘± κ²°ν•© κ°€λŠ₯ 여뢀와 μ˜ˆμƒ 할인 κΈˆμ•‘μ„ μ•ˆλ‚΄"),
         agent="κ°€μ‘± νšŒμ„ μ„ κ²°ν•©ν•˜λ©΄ μ–Όλ§ˆλ‚˜ μ ˆμ•½λ˜λŠ”μ§€ κ³„μ‚°ν•΄λ³ΌκΉŒμš”?"),
    dict(key="112", name="인터넷/IPTV κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 확인", type="Rule",
         features=["service_coverage_ratio", "Bundle Opportunity Index"],
         entities=["internet_iptv_bundle"],
         push="인터넷·TV κ²°ν•© μ‹œ μΆ”κ°€ ν˜œνƒμ„ 받을 수 μžˆμ–΄μš”",
         cc=("인터넷/IPTV κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 탐색", "ν˜„μž¬ 이용 μƒν’ˆ κΈ°μ€€ κ²°ν•© κ°€λŠ₯ μƒν’ˆμ„ μ•ˆλ‚΄"),
         agent="인터넷과 TVλ₯Ό ν•¨κ»˜ μ΄μš©ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μƒκΈ°λŠ”μ§€ μ•Œμ•„λ³ΌκΉŒμš”?"),
    dict(key="113", name="ν˜„μž¬ μš”κΈˆμ œ 기반 κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 확인", type="Rule",
         features=["plan_tier", "Bundle Opportunity Index"],
         entities=[],
         push="μ§€κΈˆ μš”κΈˆμ œλ‘œ 받을 수 μžˆλŠ” κ²°ν•© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("ν˜„μž¬ μš”κΈˆμ œ 기반 κ²°ν•© κ°€λŠ₯ μ—¬λΆ€ 확인", "ν˜„μž¬ μš”κΈˆμ œ κΈ°μ€€ 적용 κ°€λŠ₯ν•œ 결합을 μ•ˆλ‚΄"),
         agent="μ§€κΈˆ μ“°μ‹œλŠ” μš”κΈˆμ œλ‘œ κ²°ν•©ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μžˆλŠ”μ§€ λ³ΌκΉŒμš”?"),
    dict(key="121", name="할인 쀑심 κ²°ν•© μƒν’ˆ 탐색", type="Model",
         features=["non_mobile_cost_gap", "Benefit Optimization Index"],
         entities=["discount_calc", "total_bundle"],
         push="할인 ν˜œνƒμ΄ 큰 κ²°ν•© μƒν’ˆμ„ μΆ”μ²œν•΄λ“œλ¦½λ‹ˆλ‹€",
         cc=("할인 쀑심 κ²°ν•© μƒν’ˆ 탐색", "κ³ κ°λ‹˜ 쑰건에 λ§žλŠ” κ²°ν•© μƒν’ˆμ„ μ•ˆλ‚΄"),
         agent="κ°€μž₯ μœ λ¦¬ν•œ κ²°ν•© μƒν’ˆμ„ ν•¨κ»˜ μ°Ύμ•„λ³ΌκΉŒμš”?"),
    dict(key="122", name="데이터 ν˜œνƒ 쀑심 κ²°ν•© μƒν’ˆ 탐색", type="Model",
         features=["content_view_mode", "Benefit Engagement Index"],
         entities=[],
         push="데이터 ν˜œνƒμ΄ 큰 κ²°ν•© μƒν’ˆμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("데이터 ν˜œνƒ 쀑심 κ²°ν•© μƒν’ˆ 탐색", "데이터 ν™œμš©μ΄ λ§Žμ€ 고객 λŒ€μƒ 결합을 μ•ˆλ‚΄"),
         agent="데이터λ₯Ό 더 λ„‰λ„‰νžˆ μ“Έ 수 μžˆλŠ” 결합을 μ°Ύμ•„λ³ΌκΉŒμš”?"),
    dict(key="123", name="프리미엄 ν˜œνƒ 쀑심 κ²°ν•© μƒν’ˆ 탐색", type="Model",
         features=["plan_tier", "Benefit Engagement Index"],
         entities=[],
         push="프리미엄 ν˜œνƒ 쀑심 κ²°ν•© μƒν’ˆμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("프리미엄 ν˜œνƒ 쀑심 κ²°ν•© μƒν’ˆ 탐색", "μƒμœ„ μš”κΈˆμ œ 기반 프리미엄 결합을 μ•ˆλ‚΄"),
         agent="프리미엄 ν˜œνƒκΉŒμ§€ ν¬ν•¨λœ 결합을 μ‚΄νŽ΄λ³ΌκΉŒμš”?"),
    dict(key="131", name="μ‹ κ·œ κ²°ν•© κ°€μž… ν˜œνƒ 확인", type="Model",
         features=["Bundle Opportunity Index", "Benefit Optimization Index"],
         entities=["internet_signup_benefit"],
         push="μ‹ κ·œ κ°€μž… 고객 μ „μš© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μ‹ κ·œ κ°€μž… ν˜œνƒ 탐색", "ν˜„μž¬ κ°€μž… κ°€λŠ₯ν•œ ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="κ°€μž… μ‹œ 받을 수 μžˆλŠ” ν˜œνƒμ„ μ •λ¦¬ν•΄λ³ΌκΉŒμš”?"),
    dict(key="141", name="온라인 κ²°ν•© κ°€μž… κ²€ν† ", type="Rule",
         features=["Bundle Opportunity Index", "Acquisition Score"],
         entities=["bundle_apply"],
         push="온라인으둜 κ°„νŽΈν•˜κ²Œ κ°€μž…ν•΄λ³΄μ„Έμš”",
         cc=("온라인 κ°€μž… κ²€ν† ", "온라인 κ°€μž… 절차λ₯Ό μ•ˆλ‚΄"),
         agent="κ°€μž…κΉŒμ§€ ν•„μš”ν•œ 단계λ₯Ό μ•ˆλ‚΄ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="142", name="상담 기반 κ²°ν•© κ°€μž… κ²€ν† ", type="Rule",
         features=["Bundle Opportunity Index", "Acquisition Score"],
         entities=["support_consult", "bundle_apply"],
         push="상담사와 ν•¨κ»˜ κ°€μž…μ„ μ§„ν–‰ν•΄λ³΄μ„Έμš”",
         cc=("상담 기반 κ°€μž… κ²€ν†  (상담 μΈμž…)", "고객 쑰건 확인 ν›„ 맞좀 κ²°ν•© κ°€μž…μ„ μ•ˆλ‚΄"),
         agent="상담사 μ—°κ²° 전에 ν•„μš”ν•œ 정보λ₯Ό 미리 μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="143", name="λ§€μž₯ λ°©λ¬Έ κ²°ν•© κ°€μž… κ²€ν† ", type="Model",
         features=["Bundle Opportunity Index", "Acquisition Score"],
         entities=[],
         push="κ°€κΉŒμš΄ λ§€μž₯μ—μ„œ κ°€μž… 상담을 λ°›μ•„λ³΄μ„Έμš”",
         cc=("λ§€μž₯ λ°©λ¬Έ κ°€μž… κ²€ν† ", "λ°©λ¬Έ κ°€λŠ₯ λ§€μž₯κ³Ό μ€€λΉ„ μ„œλ₯˜λ₯Ό μ•ˆλ‚΄"),
         agent="λ°©λ¬Έ κ°€μž… μ‹œ ν•„μš”ν•œ 쀀비물을 μ•ˆλ‚΄ν•΄λ“œλ¦΄κΉŒμš”?"),

    # ── L1=2 할인 μ΅œμ ν™” ─────────────────────────────────────
    dict(key="211", name="ν˜„μž¬ κ²°ν•© ν˜œνƒ 확인", type="Rule",
         features=["Benefit Engagement Index", "benefit_utilization"],
         entities=["current_bundle_benefit"],
         push="ν˜„μž¬ λ°›κ³  μžˆλŠ” 할인·결합 ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("ν˜„μž¬ 적용 ν˜œνƒ 확인", "ν˜„μž¬ 적용 쀑인 할인 내역을 μ•ˆλ‚΄"),
         agent="μ§€κΈˆ λ°›κ³  μžˆλŠ” ν˜œνƒμ„ ν•œ λ²ˆμ— μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="221", name="μΆ”κ°€ κ²°ν•© 할인 κ°€λŠ₯ μ—¬λΆ€ 확인", type="Rule",
         features=["Benefit Optimization Index", "non_mobile_cost_gap"],
         entities=["discount_calc"],
         push="μΆ”κ°€λ‘œ 받을 수 μžˆλŠ” 할인 ν˜œνƒμ΄ μžˆλŠ”μ§€ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μΆ”κ°€ ν˜œνƒ 탐색", "적용 κ°€λŠ₯ν•œ 할인 ν•­λͺ©μ„ μ•ˆλ‚΄"),
         agent="ν˜„μž¬ μ‘°κ±΄μ—μ„œ μΆ”κ°€ 할인이 κ°€λŠ₯ν•œμ§€ ν™•μΈν•΄λ³ΌκΉŒμš”?"),
    dict(key="231", name="데이터 ν˜œνƒ ν™œμš© κ²€ν† ", type="Model",
         features=["Benefit Engagement Index", "content_view_mode"],
         entities=[],
         push="아직 ν™œμš©ν•˜μ§€ μ•Šμ€ 데이터 ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("데이터 ν˜œνƒ ν™œμš© κ²€ν† ", "λ―Έν™œμš© 데이터 ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="λ†“μΉ˜κ³  μžˆλŠ” 데이터 ν˜œνƒμ΄ μžˆλŠ”μ§€ ν•¨κ»˜ λ³ΌκΉŒμš”?"),
    dict(key="234", name="λ―Έμ‚¬μš© κ²°ν•© ν˜œνƒ 확인", type="Rule",
         features=["Benefit Engagement Index", "benefit_utilization"],
         entities=["unused_benefit"],
         push="아직 μ‚¬μš©ν•˜μ§€ μ•Šμ€ ν˜œνƒμ΄ μžˆμŠ΅λ‹ˆλ‹€",
         cc=("λ―Έμ‚¬μš© ν˜œνƒ 확인", "λ―Έμ‚¬μš© ν˜œνƒμ„ 정리해 μ•ˆλ‚΄"),
         agent="λ†“μΉ˜κ³  μžˆλŠ” ν˜œνƒμ΄ μžˆλŠ”μ§€ ν•¨κ»˜ ν™•μΈν•΄λ³ΌκΉŒμš”?"),

    # ── L1=3 νšŒμ„ /μ„œλΉ„μŠ€ ν™•μž₯ ────────────────────────────────
    dict(key="311", name="κ°€μ‘± νšŒμ„  μΆ”κ°€ κ²€ν† ", type="Rule",
         features=["family_line_count", "Bundle Opportunity Index"],
         entities=["family_bundle"],
         push="κ°€μ‘± νšŒμ„ μ„ μΆ”κ°€ν•˜λ©΄ 할인 ν˜œνƒμ΄ 더 컀질 수 μžˆμ–΄μš”",
         cc=("κ°€μ‘± νšŒμ„  μΆ”κ°€ κ²€ν† ", "κ°€μ‘± νšŒμ„  μΆ”κ°€ μ‹œ μ˜ˆμƒ ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="κ°€μ‘± νšŒμ„ μ„ μΆ”κ°€ν•˜λ©΄ μ–Όλ§ˆλ‚˜ μ ˆμ•½λ˜λŠ”μ§€ κ³„μ‚°ν•΄λ³ΌκΉŒμš”?"),
    dict(key="312", name="인터넷 κ²°ν•© μΆ”κ°€ κ²€ν† ", type="Rule",
         features=["Home Service Expansion Index", "service_coverage_ratio"],
         entities=["internet"],
         push="인터넷 κ²°ν•© μ‹œ μΆ”κ°€ ν˜œνƒμ„ 받을 수 μžˆμ–΄μš”",
         cc=("인터넷 κ²°ν•© μΆ”κ°€ κ²€ν† ", "인터넷 κ²°ν•© μƒν’ˆμ„ μ•ˆλ‚΄"),
         agent="인터넷을 ν•¨κ»˜ μ΄μš©ν•˜λ©΄ μ–Όλ§ˆλ‚˜ μ ˆμ•½λ˜λŠ”μ§€ μ•Œμ•„λ³ΌκΉŒμš”?"),
    dict(key="313", name="IPTV κ²°ν•© μΆ”κ°€ κ²€ν† ", type="Rule",
         features=["Home Service Expansion Index", "content_view_mode"],
         entities=["iptv"],
         push="IPTV κ²°ν•© μ‹œ μ½˜ν…μΈ  ν˜œνƒμ„ 받을 수 μžˆμ–΄μš”",
         cc=("IPTV κ²°ν•© μΆ”κ°€ κ²€ν† ", "IPTV κ²°ν•© μƒν’ˆκ³Ό μ½˜ν…μΈ  ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="TVκΉŒμ§€ κ²°ν•©ν•˜λ©΄ μ–΄λ–€ μ½˜ν…μΈ  ν˜œνƒμ΄ μƒκΈ°λŠ”μ§€ λ³ΌκΉŒμš”?"),
    dict(key="314", name="인터넷+IPTV 톡합 이용 κ²€ν† ", type="Rule",
         features=["Home Service Expansion Index", "service_coverage_ratio"],
         entities=["internet_iptv_bundle"],
         push="인터넷+TV 톡합 κ²°ν•© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("인터넷+IPTV 톡합 이용 κ²€ν† ", "톡합 κ²°ν•© μ‹œ ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="인터넷과 TVλ₯Ό 묢으면 μ–Όλ§ˆλ‚˜ μœ λ¦¬ν•œμ§€ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
    dict(key="315", name="ν™ˆ WiFi μΆ”κ°€ κ²€ν† ", type="Rule",
         features=["Home Service Expansion Index", "household_change"],
         entities=["wifi"],
         push="ν™ˆ WiFi μΆ”κ°€ μ‹œ 더 νŽΈλ¦¬ν•˜κ²Œ μ΄μš©ν•  수 μžˆμ–΄μš”",
         cc=("ν™ˆ WiFi μΆ”κ°€ κ²€ν† ", "ν™ˆ WiFi κ²°ν•© ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="ν™ˆ WiFiλ₯Ό μΆ”κ°€ν•˜λ©΄ μ–΄λ–€ 점이 μ’‹μ•„μ§€λŠ”μ§€ μ•Œλ €λ“œλ¦΄κΉŒμš”?"),
    dict(key="321", name="μ›ŒμΉ˜ νšŒμ„  μΆ”κ°€ κ²€ν† ", type="Model",
         features=["plan_tier", "Service Expansion Score"],
         entities=[],
         push="μ›ŒμΉ˜ μ „μš© νšŒμ„  ν˜œνƒμ„ μ•ˆλ‚΄ν•΄λ“œλ¦΄κ²Œμš”",
         cc=("μ›ŒμΉ˜ νšŒμ„  μΆ”κ°€ κ²€ν† ", "μ›ŒμΉ˜ μ „μš© νšŒμ„  ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="μŠ€λ§ˆνŠΈμ›ŒμΉ˜ μ—°κ²° μ‹œ 이용 κ°€λŠ₯ν•œ ν˜œνƒμ„ ν™•μΈν•΄λ³ΌκΉŒμš”?"),
    dict(key="322", name="νƒœλΈ”λ¦Ώ κ²°ν•© μΆ”κ°€ κ²€ν† ", type="Model",
         features=["plan_tier", "Service Expansion Score"],
         entities=[],
         push="νƒœλΈ”λ¦Ώ κ²°ν•© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("νƒœλΈ”λ¦Ώ κ²°ν•© μΆ”κ°€ κ²€ν† ", "νƒœλΈ”λ¦Ώ κ²°ν•© νšŒμ„  ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="νƒœλΈ”λ¦Ώμ„ κ²°ν•©ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μžˆλŠ”μ§€ λ³ΌκΉŒμš”?"),
    dict(key="323", name="μ„Έμ»¨λ“œ λ””λ°”μ΄μŠ€ μ—°κ²° κ²€ν† ", type="Model",
         features=["plan_tier", "Service Expansion Score"],
         entities=[],
         push="μ„Έμ»¨λ“œ λ””λ°”μ΄μŠ€ μ—°κ²° ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μ„Έμ»¨λ“œ λ””λ°”μ΄μŠ€ μ—°κ²° κ²€ν† ", "μΆ”κ°€ λ””λ°”μ΄μŠ€ μ—°κ²° ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="μΆ”κ°€ λ””λ°”μ΄μŠ€λ₯Ό μ—°κ²°ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μžˆλŠ”μ§€ μ•Œμ•„λ³ΌκΉŒμš”?"),
    dict(key="331", name="κ°€μ‘± νšŒμ„  톡합 κ²€ν† ", type="Model",
         features=["family_line_count", "Bundle Opportunity Index"],
         entities=[],
         push="κ°€μ‘± νšŒμ„ μ„ ν†΅ν•©ν•˜λ©΄ 더 μœ λ¦¬ν•œ 결합을 받을 수 μžˆμ–΄μš”",
         cc=("κ°€μ‘± νšŒμ„  톡합 κ²€ν† ", "κ°€μ‘± νšŒμ„  톡합 μ‹œ κ²°ν•© ꡬ성을 μ•ˆλ‚΄"),
         agent="κ°€μ‘± νšŒμ„ μ„ ν†΅ν•©ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μƒκΈ°λŠ”μ§€ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
    dict(key="332", name="κ°€μ‘± νšŒμ„  뢄리 κ²€ν† ", type="Model",
         features=["family_line_count", "household_change"],
         entities=[],
         push="κ°€μ‘± νšŒμ„  뢄리 μ‹œ λ³€λ™λ˜λŠ” ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("κ°€μ‘± νšŒμ„  뢄리 κ²€ν† ", "νšŒμ„  뢄리 μ‹œ ν˜œνƒ 변동을 μ•ˆλ‚΄"),
         agent="νšŒμ„ μ„ λΆ„λ¦¬ν•˜λ©΄ ν˜œνƒμ΄ μ–΄λ–»κ²Œ λ‹¬λΌμ§€λŠ”μ§€ ν™•μΈν•΄λ³ΌκΉŒμš”?"),
    dict(key="333", name="λͺ…μ˜ λ³€κ²½ κ²€ν† ", type="Rule",
         features=["household_change", "family_line_count"],
         entities=[],
         push="κ²°ν•© ꡬ쑰λ₯Ό λ³€κ²½ν•˜λ©΄ 더 μœ λ¦¬ν•œ ν˜œνƒμ„ 받을 수 μžˆμŠ΅λ‹ˆλ‹€",
         cc=("λͺ…μ˜ λ³€κ²½ κ²€ν† ", "λͺ…μ˜ λ³€κ²½ κ°€λŠ₯ν•œ κ²°ν•© ꡬ성을 μ•ˆλ‚΄"),
         agent="ν˜„μž¬ 상황에 맞게 결합을 λ‹€μ‹œ κ΅¬μ„±ν•΄λ³ΌκΉŒμš”?"),
    dict(key="334", name="λŒ€ν‘œ νšŒμ„  λ³€κ²½ κ²€ν† ", type="Rule",
         features=["household_change", "family_line_count"],
         entities=[],
         push="λŒ€ν‘œ νšŒμ„  λ³€κ²½ μ‹œ κ²°ν•© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("λŒ€ν‘œ νšŒμ„  λ³€κ²½ κ²€ν† ", "λŒ€ν‘œ νšŒμ„  λ³€κ²½ μ‹œ ν˜œνƒ 변동을 μ•ˆλ‚΄"),
         agent="λŒ€ν‘œ νšŒμ„ μ„ λ°”κΎΈλ©΄ κ²°ν•© ν˜œνƒμ΄ μ–΄λ–»κ²Œ λ‹¬λΌμ§€λŠ”μ§€ λ³ΌκΉŒμš”?"),
    dict(key="335", name="νšŒμ„  이동 κ²€ν† ", type="Model",
         features=["household_change", "family_line_count"],
         entities=[],
         push="νšŒμ„  이동 μ‹œ κ²°ν•© ꡬ성을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("νšŒμ„  이동 κ²€ν† ", "νšŒμ„  이동 μ‹œ κ²°ν•© ꡬ성을 μ•ˆλ‚΄"),
         agent="νšŒμ„ μ„ μ΄λ™ν•˜λ©΄ 결합이 μ–΄λ–»κ²Œ λ°”λ€ŒλŠ”μ§€ ν™•μΈν•΄λ³ΌκΉŒμš”?"),

    # ── L1=4 μœ μ§€/락인 ───────────────────────────────────────
    dict(key="411", name="κ²°ν•© μ•½μ • μƒνƒœΒ·μ—°μž₯ ν˜œνƒ 확인", type="Rule",
         features=["contract_status", "Retention Readiness Index"],
         entities=["contract_status"],
         push="μ•½μ • μƒνƒœμ™€ μ—°μž₯ μ‹œ 받을 수 μžˆλŠ” ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μ•½μ • μƒνƒœ 및 μ—°μž₯ ν˜œνƒ 확인", "ν˜„μž¬ μ•½μ • μƒνƒœμ™€ μ—°μž₯ ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="μ•½μ • μ—°μž₯ μ‹œ 받을 수 μžˆλŠ” ν˜œνƒμ„ μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="412", name="κ²°ν•© μž¬μ•½μ • ν˜œνƒ 비ꡐ", type="Model",
         features=["Retention Readiness Index", "Benefit Engagement Index"],
         entities=["renewal_benefit", "renewal_consult"],
         push="μž¬μ•½μ • μ‹œ 받을 수 μžˆλŠ” ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μž¬μ•½μ • κ²€ν†  (상담 μΈμž…)", "ν˜„μž¬ μž¬μ•½μ • κ°€λŠ₯ ν˜œνƒμ„ 비ꡐ μ•ˆλ‚΄"),
         agent="μž¬μ•½μ •κ³Ό ν˜„μž¬ 쑰건을 λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
    dict(key="421", name="κ²°ν•© μž₯κΈ° 고객 ν˜œνƒ 확인", type="Rule",
         features=["tenure_group", "Retention Readiness Index"],
         entities=["long_term_benefit"],
         push="μž₯κΈ° 고객 μ „μš© ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μž₯κΈ° 고객 ν˜œνƒ 확인", "고객이 받을 수 μžˆλŠ” μž₯κΈ° ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="μž₯κΈ° 이용 고객 ν˜œνƒμ„ μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="431", name="μš”κΈˆμ œ λ³€κ²½ μ‹œ κ²°ν•© 영ν–₯ 확인", type="Model",
         features=["non_mobile_cost_gap", "Retention Readiness Index"],
         entities=[],
         push="μš”κΈˆμ œ λ³€κ²½ μ‹œ κ²°ν•© ν˜œνƒ 변동을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μš”κΈˆμ œ λ³€κ²½ 영ν–₯ 확인", "μš”κΈˆμ œ λ³€κ²½ μ‹œ κ²°ν•© ν˜œνƒ 변동을 μ•ˆλ‚΄"),
         agent="μš”κΈˆμ œλ₯Ό λ°”κΎΈλ©΄ κ²°ν•© ν˜œνƒμ— μ–΄λ–€ 영ν–₯이 μžˆλŠ”μ§€ λ³ΌκΉŒμš”?"),
    dict(key="432", name="κ²°ν•© ν•΄μ§€ 영ν–₯ 확인", type="Rule",
         features=["Retention Readiness Index", "Churn Risk Index"],
         entities=["penalty"],
         push="κ²°ν•© ν•΄μ§€ μ‹œ λ³€κ²½λ˜λŠ” ν˜œνƒμ„ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("κ²°ν•© μœ μ§€ 영ν–₯ 확인", "κ²°ν•© ν•΄μ§€ μ‹œ λ³€λ™λ˜λŠ” 할인 ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="결합을 ν•΄μ§€ν•˜λ©΄ μ–΄λ–€ 영ν–₯이 μžˆλŠ”μ§€ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),

    # ── L1=5 μ΄νƒˆ κ²€ν†  ───────────────────────────────────────
    dict(key="511", name="타사 κ²°ν•© ν˜œνƒ/가격 비ꡐ", type="Model",
         features=["Churn Risk Index", "non_mobile_cost_gap"],
         entities=["competitor_compare"],
         push="ν˜„μž¬ ν˜œνƒκ³Ό 타사 ν˜œνƒμ„ λΉ„κ΅ν•΄λ³΄μ„Έμš”",
         cc=("κ²½μŸμ‚¬ 비ꡐ", "κ²½μŸμ‚¬ μƒν’ˆκ³Ό 비ꡐ μ•ˆλ‚΄"),
         agent="ν˜„μž¬ ν˜œνƒκ³Ό 타사 ν˜œνƒμ„ λ‚˜λž€νžˆ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
    dict(key="512", name="λ²ˆν˜Έμ΄λ™ 기반 ν˜œνƒ 비ꡐ", type="Rule",
         features=["Churn Risk Index", "contract_status"],
         entities=[],
         push="λ²ˆν˜Έμ΄λ™ μ‹œ 받을 수 μžˆλŠ” ν˜œνƒμ„ λΉ„κ΅ν•΄λ³΄μ„Έμš”",
         cc=("λ²ˆν˜Έμ΄λ™ 기반 ν˜œνƒ 비ꡐ", "λ²ˆν˜Έμ΄λ™ μ‹œ ν˜œνƒμ„ 비ꡐ μ•ˆλ‚΄"),
         agent="λ²ˆν˜Έμ΄λ™ μ‹œ ν˜œνƒμ„ ν˜„μž¬μ™€ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
    dict(key="521", name="κ²°ν•© ν˜œνƒ λΆ€μ‘± 체감", type="Model",
         features=["dissatisfaction_factor", "Benefit Optimization Index"],
         entities=[],
         push="더 받을 수 μžˆλŠ” ν˜œνƒμ΄ μžˆλŠ”μ§€ ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("ν˜œνƒ λΆ€μ‘± 체감", "μΆ”κ°€ 적용 κ°€λŠ₯ν•œ ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="μ§€κΈˆ 받을 수 μžˆλŠ” ν˜œνƒμ„ ν•œ λ²ˆμ— μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="522", name="κ²°ν•© μœ μ§€ κ°€μΉ˜ ν•˜λ½", type="Model",
         features=["dissatisfaction_factor", "Retention Readiness Index"],
         entities=[],
         push="κ²°ν•© μœ μ§€ μ‹œ 받을 수 μžˆλŠ” κ°€μΉ˜λ₯Ό ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("κ²°ν•© μœ μ§€ κ°€μΉ˜ ν•˜λ½", "κ²°ν•© μœ μ§€ κ°€μΉ˜λ₯Ό μž¬μ•ˆλ‚΄"),
         agent="결합을 μœ μ§€ν•˜λ©΄ μ–΄λ–€ 점이 μœ λ¦¬ν•œμ§€ λ‹€μ‹œ μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="523", name="κ²°ν•© 톡신비 λΆ€λ‹΄ 증가", type="Model",
         features=["non_mobile_cost_gap", "dissatisfaction_factor"],
         entities=[],
         push="톡신비λ₯Ό 쀄일 수 μžˆλŠ” 방법을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("톡신비 λΆ€λ‹΄ 증가", "λΉ„μš© 뢀담을 쀄일 수 μžˆλŠ” μƒν’ˆμ„ μ•ˆλ‚΄"),
         agent="μ§€κΈˆλ³΄λ‹€ 톡신비λ₯Ό μ ˆμ•½ν•  수 μžˆλŠ” 방법을 μ°Ύμ•„λ³ΌκΉŒμš”?"),
    dict(key="531", name="인터넷 ν’ˆμ§ˆ 뢈만", type="Rule",
         features=["dissatisfaction_factor", "Churn Risk Index"],
         entities=[],
         push="인터넷 ν’ˆμ§ˆ κ°œμ„  방법을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("인터넷 ν’ˆμ§ˆ 뢈만", "ν˜„μž¬ 인터넷 ν’ˆμ§ˆ μƒνƒœλ₯Ό 점검 μ•ˆλ‚΄"),
         agent="인터넷 이용 ν™˜κ²½μ„ ν•¨κ»˜ μ κ²€ν•΄λ³ΌκΉŒμš”?"),
    dict(key="532", name="IPTV ν’ˆμ§ˆ 뢈만", type="Rule",
         features=["dissatisfaction_factor", "Churn Risk Index"],
         entities=[],
         push="IPTV ν’ˆμ§ˆ κ°œμ„  방법을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("IPTV ν’ˆμ§ˆ 뢈만", "IPTV ν’ˆμ§ˆ μƒνƒœλ₯Ό 점검 μ•ˆλ‚΄"),
         agent="TV 이용 ν™˜κ²½μ„ ν•¨κ»˜ μ κ²€ν•΄λ³ΌκΉŒμš”?"),
    dict(key="533", name="λͺ¨λ°”일 데이터 ν’ˆμ§ˆ 뢈만", type="Model",
         features=["dissatisfaction_factor", "Churn Risk Index"],
         entities=[],
         push="λͺ¨λ°”일 데이터 ν’ˆμ§ˆ κ°œμ„  방법을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("λͺ¨λ°”일 데이터 ν’ˆμ§ˆ 뢈만", "데이터 ν’ˆμ§ˆ μƒνƒœλ₯Ό 점검 μ•ˆλ‚΄"),
         agent="데이터 이용 ν™˜κ²½μ„ ν•¨κ»˜ μ κ²€ν•΄λ³ΌκΉŒμš”?"),
    dict(key="541", name="κ²°ν•© ν•΄μ§€ 절차 확인", type="Rule",
         features=["Churn Risk Index", "Retention Readiness Index"],
         entities=["cancel_process", "penalty"],
         push="ν•΄μ§€ μ „ μ ˆμ°¨μ™€ 변동 ν˜œνƒμ„ 미리 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("ν•΄μ§€ κ²€ν† ", "ν•΄μ§€ μ ˆμ°¨μ™€ ν•΄μ§€ μ‹œ 변동 사항을 μ•ˆλ‚΄"),
         agent="ν•΄μ§€ μ‹œ λ³€κ²½λ˜λŠ” 뢀뢄을 미리 μ •λ¦¬ν•΄λ“œλ¦΄κΉŒμš”?"),
    dict(key="542", name="κ²°ν•© ν•΄μ§€ μœ„μ•½κΈˆ 확인", type="Rule",
         features=["Churn Risk Index", "contract_status"],
         entities=["penalty"],
         push="ν•΄μ§€ μ „ μœ„μ•½κΈˆ 정보λ₯Ό 미리 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("μœ„μ•½κΈˆ 확인", "ν•΄μ§€ μ‹œ λ°œμƒ κ°€λŠ₯ν•œ μœ„μ•½κΈˆμ„ μ•ˆλ‚΄"),
         agent="μ§€κΈˆ ν•΄μ§€ν•˜λ©΄ μœ„μ•½κΈˆμ΄ μ–Όλ§ˆλ‚˜ λ°œμƒν•˜λŠ”μ§€ ν™•μΈν•΄λ³ΌκΉŒμš”?"),
    dict(key="543", name="κ²°ν•© ν•΄μ§€ μ‹œ μž”μ—¬ νšŒμ„  영ν–₯ 확인", type="Rule",
         features=["Churn Risk Index", "Retention Readiness Index"],
         entities=[],
         push="ν•΄μ§€ μ‹œ λ³€λ™λ˜λŠ” ν˜œνƒκ³Ό 영ν–₯을 ν™•μΈν•΄λ³΄μ„Έμš”",
         cc=("ν•΄μ§€ 영ν–₯ 확인", "ν•΄μ§€ μ‹œ λ³€λ™λ˜λŠ” κ²°ν•© ν˜œνƒμ„ μ•ˆλ‚΄"),
         agent="ν•΄μ§€ν•˜λ©΄ μ–΄λ–€ ν˜œνƒμ΄ μ‚¬λΌμ§€λŠ”μ§€ λΉ„κ΅ν•΄λ³ΌκΉŒμš”?"),
]


def _l1_of(key: str) -> str:
    return key[0]


def _l2_of(key: str) -> str:
    return key[:2]


def build_intents() -> dict:
    intents = []
    for t in TAX:
        key = t["key"]
        l1_id, l1_name = L1[_l1_of(key)]
        l2_id, l2_name = L2[_l2_of(key)]
        intents.append({
            "id":             f"INT-B{key}0",
            "name":           t["name"],
            "L1_id":          l1_id,
            "L1_name":        l1_name,
            "L2_id":          l2_id,
            "L2_name":        l2_name,
            "inference_type": t["type"],
            "features":       t["features"],
        })
    return {
        "scenario_id": SCENARIO_ID,
        "version":     "0.1.0",
        "description": "κ²°ν•© μƒν’ˆ ν™œμ„±ν™” μ‹œλ‚˜λ¦¬μ˜€ Intent Taxonomy",
        "intents":     intents,
    }


CHANNELS = [
    {"id": "push", "name": "μ•± Push", "icon": "πŸ“±",
     "characteristic": "κ³ κ°μ—κ²Œ 직접 λ…ΈμΆœλ˜λŠ” μ•Œλ¦Ό λ©”μ‹œμ§€ β€” 행동 μœ λ„Β·ν˜œνƒ μ•ˆλ‚΄"},
    {"id": "call_center", "name": "고객센터 상담사 μ»¨ν…μŠ€νŠΈ", "icon": "πŸ“ž",
     "characteristic": "μ½œΒ·μ±„νŒ… μΈμž… μ‹œ 상담사 ν™”λ©΄ μ»¨ν…μŠ€νŠΈ β€” 상황 + μ•ˆλ‚΄ κ°€μ΄λ“œ(고객 λŒ€μƒ λ¬Έμž₯ μ•„λ‹˜)"},
    {"id": "agent", "name": "AI Agent", "icon": "πŸ€–",
     "characteristic": "AI Agentκ°€ κ³ κ°μ—κ²Œ λ˜μ§€λŠ” κ°œμΈν™” μ œμ•ˆΒ·λΉ„κ΅Β·λΆ„μ„ λ°œν™”"},
]


def build_actions() -> dict:
    actions = {}
    for t in TAX:
        iid = f"INT-B{t['key']}0"
        situation, guidance = t["cc"]
        actions[iid] = {
            "push": t["push"],
            "call_center": {"situation": situation, "guidance": guidance},
            "agent": t["agent"],
        }
    return {
        "scenario_id": SCENARIO_ID,
        "version":     "0.1.0",
        "description": "κ²°ν•© μ‹œλ‚˜λ¦¬μ˜€ Intent별 3채널 ν™œμš© μ˜ˆμ‹œ",
        "channels":    CHANNELS,
        "actions":     actions,
    }


# Step1(λͺ©μ ν˜•) behavior entity β†’ λŒ€ν‘œ L3 Intent κ·Έλ£Ή.
# Step1 클릭도 μ˜λ„λ₯Ό λŒμ–΄μ˜¬λ¦¬λ„λ‘(behaviors.json step1 entity 컀버).
STEP1_ENTITY_INTENTS = {
    "benefit_check":         ["INT-B2110", "INT-B2210", "INT-B2340"],
    "bundle_discount_check": ["INT-B1110", "INT-B1210", "INT-B3110"],
    "home_service_explore":  ["INT-B1120", "INT-B3120", "INT-B3130", "INT-B3140"],
    "contract_benefit_check": ["INT-B4110", "INT-B4120", "INT-B4210"],
    "competitor_explore":    ["INT-B5110", "INT-B5410", "INT-B5420"],
}


def build_behavior_intents() -> dict:
    entity_intents: dict[str, list[str]] = {}
    for t in TAX:
        iid = f"INT-B{t['key']}0"
        for ent in t["entities"]:
            entity_intents.setdefault(ent, []).append(iid)
    # Step1 λͺ©μ ν˜• entity λ§€ν•‘ 병합
    for ent, iids in STEP1_ENTITY_INTENTS.items():
        entity_intents.setdefault(ent, [])
        for iid in iids:
            if iid not in entity_intents[ent]:
                entity_intents[ent].append(iid)
    # behaviors.json 곡톡(BACK/EXIT) entityλŠ” 무효과
    entity_intents.setdefault("back_to_step1", [])
    entity_intents.setdefault("session_end", [])
    return {
        "scenario_id": SCENARIO_ID,
        "version":     "0.1.0",
        "description": "κ²°ν•© behaviors.json entity β†’ 직접 μ‹ ν˜Έ Intent λ§€ν•‘",
        "entity_intents": entity_intents,
    }


def main() -> None:
    emit_layers(
        OUT_DIR / "engine",
        taxonomy=build_intents(),
        context_library=build_actions(),
        behavior_signals=build_behavior_intents()["entity_intents"],
    )

    # μš”μ•½
    intents = build_intents()["intents"]
    from collections import Counter
    print("\n── L1 뢄포 ──")
    for (lid, lname), c in sorted(Counter((i["L1_id"], i["L1_name"]) for i in intents).items()):
        print(f"  {lid} {lname}: {c}")
    print("── inference_type ──")
    print(" ", Counter(i["inference_type"] for i in intents))
    bi = build_behavior_intents()["entity_intents"]
    print(f"── behavior entities mapped: {len([k for k,v in bi.items() if v])} ──")


if __name__ == "__main__":
    main()