SeaWolf-AI commited on
Commit
f8d9c90
ยท
verified ยท
1 Parent(s): 5636c50

feat: 6-axis creative constraints + emergent references \u2014 block the safe-mean trap

Browse files
aether_ad/core/creative_constraints.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Concrete creative constraints โ€” injected into every collision prompt to
2
+ force the LLM out of the "safe mean" of its training distribution.
3
+
4
+ Why this matters
5
+ ----------------
6
+ Large models trained on mixed corpora fall back to the **median of Korean ad
7
+ clichรฉs** (family photos, graduation hugs, rainy umbrellas) when given only
8
+ abstract directives. Hard concrete constraints โ€” specific objects, specific
9
+ camera shots, specific sensory inversions โ€” physically block that escape
10
+ route. The model has to synthesize something novel to satisfy all of them.
11
+
12
+ Pools are intentionally specific and Korean-cultural where useful. Every
13
+ entry is an imperative "must appear / must use / must feel like" โ€” not a
14
+ mere suggestion.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import random
20
+ from dataclasses import dataclass
21
+
22
+
23
+ # โ”€โ”€ Concrete physical objects โ€” must appear as a tangible prop at a specific moment
24
+ CONCRETE_OBJECTS: list[str] = [
25
+ "๋…น์Šจ ๋ชป ํ•˜๋‚˜", "๋œจ๊ฐœ์งˆ ๋ฐ”๋Š˜", "๊นจ์ง„ ๋„์ž๊ธฐ ํŒŒํŽธ", "๋ง๋ผ๋ฒ„๋ฆฐ ์žฅ๋ฏธ ํ•œ ์†ก์ด",
26
+ "ํ”Œ๋ผ์Šคํ‹ฑ ๋น—", "๋ฌผ์— ์ –์€ ๊ณต์ฑ…", "๊ตฌ๋ฉ ๋‚œ ์–‘๋ง", "๋ฐ˜์œผ๋กœ ์ ‘ํžŒ ํŽธ์ง€์ง€",
27
+ "์˜ค๋ž˜๋œ ๋™์ „ ์ง€๊ฐ‘", "๋‚ก์€ ํšŒ์ค‘์‹œ๊ณ„ ์ค„", "๋ฒ„๋ ค์ง„ ์šฐ์‚ฐ์‚ด", "๋ง๋ผ๋ถ™์€ ๊ปŒ",
28
+ "์Ÿ์•„์ง„ ์†Œ๊ธˆ", "ํƒˆ ๋Œ€๋กœ ํƒ€๋ฒ„๋ฆฐ ์„ฑ๋ƒฅ", "๋ถ€์„œ์ง„ ํ˜•๊ด‘๋“ฑ", "์˜ค๋ž˜๋œ ๋—์ž๋ฆฌ",
29
+ "์ฐจ๊ฐ‘๊ฒŒ ์‹์€ ์ปคํ”ผ ์ฐŒ๊บผ๊ธฐ", "๋…น์€ ํฌ๋ ˆ์šฉ", "๊ธธ ํ•œ๊ฐ€์šด๋ฐ ๋†“์ธ ํ”Œ๋ผ์Šคํ‹ฑ ์˜์ž",
30
+ "๋Š์–ด์ง„ ๋ชฉ์ค„", "๋จผ์ง€ ์Œ“์ธ ๋ณ‘ํ’", "๊ตฌ๊ฒจ์ง„ ์˜์ˆ˜์ฆ ๋ญ‰์น˜", "๋ˆ„๊ฐ€ ๋‘๊ณ  ๊ฐ„ ๋‹ด๋ฐฐ๊ฝ์ดˆ",
31
+ "๋จผ์ง€ ๋‚€ CRT ๋ชจ๋‹ˆํ„ฐ", "๋ฐ˜์ฏค ๋…น์€ ์•„์ด์Šคํฌ๋ฆผ", "๋น„๋‹๋ด‰์ง€์— ๋“  ๊น€์น˜",
32
+ "๋ฐ”๋žŒ์— ๋‚ ๋ฆฌ๋Š” ํ˜„์ˆ˜๋ง‰ ์กฐ๊ฐ", "๋…น์Šจ ๋นจ๋ž˜ ๊ฑด์กฐ๋Œ€", "๊นจ์ง„ ๋ฐ˜์ง€",
33
+ "๋งค๋ฏธ ํ—ˆ๋ฌผ", "๊ธˆ์ด ๊ฐ„ ๊ฑฐ์šธ",
34
+ ]
35
+
36
+ # โ”€โ”€ Camera shots โ€” must be the dominant POV of at least one cut
37
+ CAMERA_SHOTS: list[str] = [
38
+ "CCTV ํƒ‘๋ทฐ (์ฒœ์žฅ ํ‘๋ฐฑ)", "์—˜๋ฆฌ๋ฒ ์ดํ„ฐ ์ฒœ์žฅ ์–ด์•ˆ ๋ Œ์ฆˆ",
39
+ "๋ฌผ์†์—์„œ ์ˆ˜๋ฉด ์œ„๋ฅผ ์˜ฌ๋ ค๋‹ค๋ณด๋Š” ์‹œ์ ", "๋ฒฝ์ง€๋ฅผ ๋šซ๊ณ  ๋‚˜์˜ค๋Š” ์‹œ์ ",
40
+ "๋ƒ‰์žฅ๊ณ  ์•ˆ์ชฝ์—์„œ ๋ฌธ์ด ์—ด๋ฆฌ๋Š” ์‹œ์ ", "๋จธ๋ฆฌ์นด๋ฝ ์‚ฌ์ด์—์„œ ๋ณธ ์–ผ๊ตด",
41
+ "๋ฐ”๋‹ฅ์— ๋–จ์–ด์ง„ ํœด๋Œ€ํฐ ํ™”๋ฉด์ด ์˜ฌ๋ ค๋‹ค๋ณด๋Š” ์‹œ์ ", "๋‚™์—ฝ ํ•œ ์žฅ์˜ ์‹œ์  (๋•…์œผ๋กœ ๋–จ์–ด์ง€๋ฉด์„œ)",
42
+ "์‹œ๊ณ„ ๋‹ค์ด์–ผ ์•ˆ์ชฝ์—์„œ ๋ณธ ๋ฐ”๋Š˜", "์ง€ํ•˜์ฒ  ์ฐฝ๋ฌธ ๋ฐ˜์‚ฌ์ƒ ์†์˜ ์Šน๊ฐ",
43
+ "๊ทน์ €์† ์Šฌ๋กœ์šฐ๋ชจ์…˜ (1์ดˆ๋ฅผ 15์ดˆ๋กœ ๋Š˜์ธ)",
44
+ "๊ทน๊ณ ์† ํƒ€์ž„๋žฉ์Šค (8์‹œ๊ฐ„์„ 3์ดˆ๋กœ ์••์ถ•)",
45
+ "์—ดํ™”์ƒ ์นด๋ฉ”๋ผ ์‹œ์ ", "X-ray ์‹œ์ ", "๋“œ๋ก  ํƒ‘๋ทฐ์—์„œ ์ˆ˜์ง ํ•˜๊ฐ•",
46
+ "๊ฑฐ์šธ ์†์˜ ๊ฑฐ์šธ (๋ฌดํ•œ ๋ฐ˜์‚ฌ)", "์ฃผ์ธ๊ณต ๋’คํ†ต์ˆ˜๋งŒ ๋ณด์ด๋Š” ์‹œ์ ",
47
+ "์ปคํ”ผ ์ž” ์† ์ˆ˜๋ฉด์— ๋น„์นœ ์ฒœ์žฅ", "์ž๋™์ฐจ ์‚ฌ์ด๋“œ๋ฏธ๋Ÿฌ ์† ์žฅ๋ฉด",
48
+ "์นด๋ฉ”๋ผ๊ฐ€ ์ธ๋ฌผ์„ ๋น™๋น™ ๋„๋Š” 360๋„ ๋‹ฌ๋ฆฌ์ƒท",
49
+ ]
50
+
51
+ # โ”€โ”€ Sensory inversions โ€” one sensory channel must be translated into another
52
+ SYNESTHETIC_INVERSIONS: list[str] = [
53
+ "์‹œ๊ณ„ ์ดˆ์นจ ์†Œ๋ฆฌ๊ฐ€ ํ˜€๋์— ์“ด๋ง›์œผ๋กœ ๋ฒˆ์—ญ๋จ",
54
+ "ํ–‡๋น›์ด ๋ฌด๊ฒŒ๋กœ ๋ณ€ํ•ด ์–ด๊นจ๋ฅผ ์ง“๋ˆ„๋ฆ„",
55
+ "์นจ๋ฌต์ด ํ”ผ๋ถ€๋ฅผ ์“ธ๊ณ  ์ง€๋‚˜๊ฐ€๋Š” ๊ฐ์ด‰์œผ๋กœ ํ‘œํ˜„๋จ",
56
+ "ํƒ€์ธ์˜ ๋ˆˆ๋น›์ด ๋ƒ„์ƒˆ๋กœ ๊ฐ์ง€๋จ",
57
+ "SNS ์•Œ๋ฆผ์ด ๊ท€์— ๋ฌผ๋ฐฉ์šธ๋กœ ๋–จ์–ด์ง",
58
+ "ํ•œ์ˆจ์ด ์ƒ‰์œผ๋กœ ๋ฒˆ์ ธ๋‚˜๊ฐ",
59
+ "๊ธฐ์–ต์ด ์†์— ์ฅ˜ ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ๋Œ๋กœ ๋ฌผ์งˆํ™”๋จ",
60
+ "์‹œ๊ฐ„์˜ ํ๋ฆ„์ด ๋“ฑ์„ ํƒ€๊ณ  ๋‚ด๋ ค์˜ค๋Š” ์ฐจ๊ฐ€์šด ๊ฐ์ด‰์œผ๋กœ",
61
+ "ํƒ€์ธ์˜ ๋ง์ด ๊ณต์ค‘์— ๊ธ€์ž๋กœ ๋– ์˜ค๋ฅด๊ณ  ๋ฌด๊ฒŒ๊ฐ€ ์žˆ์Œ",
62
+ "๋ถ„๋…ธ๊ฐ€ ์†๋์—์„œ ํŒŒ๋ž€ ๋ถˆ๊ฝƒ์œผ๋กœ ํƒ€์˜ค๋ฆ„",
63
+ "์™ธ๋กœ์›€์ด ์ž…์•ˆ์—์„œ ๋…น๋Š” ์–ผ์Œ์˜ ๊ฐ์ด‰",
64
+ "๊ฑฑ์ •์ด ๋จธ๋ฆฌ์นด๋ฝ ๋์—์„œ ์†Œ๊ธˆ์œผ๋กœ ๊ตณ์Œ",
65
+ "๊ทธ๋ฆฌ์›€์ด ๋ฐœ๋ฐ‘์—์„œ ์ง™์€ ์•ˆ๊ฐœ๋กœ ์˜ฌ๋ผ์˜ด",
66
+ "๊ฑฐ์ง“๋ง์ด ์ˆจ๊ฒฐ๋งˆ๋‹ค ๊ฒ€์€ ๊นƒํ„ธ๋กœ ๋–จ์–ด์ง",
67
+ "ํ”ผ๊ณค์ด ๋ชฉ๋œ๋ฏธ์—์„œ ๋‚ฉ๋ฉ์ด๋กœ ๋ณ€ํ•ด ๋งค๋‹ฌ๋ฆผ",
68
+ ]
69
+
70
+ # โ”€โ”€ Genre subversion โ€” starts as one genre, ends as another
71
+ GENRE_TWISTS: list[str] = [
72
+ "๋ช…์ ˆ ๊ฐ€์กฑ ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ์ค‘๋ฐ˜๋ถ€ํ„ฐ ์‚ฌ์ด๋ฒ„ํŽ‘ํฌ ๋””์Šคํ† ํ”ผ์•„",
73
+ "์ˆ˜๋Šฅ ์‘์› ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ์ข€๋น„ ์•„ํฌ์นผ๋ฆฝ์Šค",
74
+ "๋กœ๋งจํ‹ฑ ์ฝ”๋ฏธ๋””๋กœ ์‹œ์ž‘ โ†’ ๋‹คํ๋ฉ˜ํ„ฐ๋ฆฌ ์ธํ„ฐ๋ทฐ",
75
+ "๊ณต์ต๊ด‘๊ณ  ๋А๋‚Œ์œผ๋กœ ์‹œ์ž‘ โ†’ ๋ฒ”์ฃ„ ์Šค๋ฆด๋Ÿฌ",
76
+ "์•„๋™ ๊ต์œก ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ํ•„๋ฆ„ ๋ˆ„์•„๋ฅด",
77
+ "ํž๋ง ์—ฌํ–‰ ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ์šฐ์ฃผ SF",
78
+ "์Šคํฌ์ธ  ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ์กฐ์šฉํ•œ ์ฒ ํ•™ ๋ช…์ƒ",
79
+ "๋ช…ํ’ˆ ๋Ÿญ์…”๋ฆฌ ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ๊ธธ๊ฑฐ๋ฆฌ ๋‹คํ๋ฉ˜ํ„ฐ๋ฆฌ",
80
+ "์›จ๋”ฉ ๋กœ๋งจ์Šค๋กœ ์‹œ์ž‘ โ†’ ์Šฌ๋žฉ์Šคํ‹ฑ ์ฝ”๋ฏธ๋””",
81
+ "๊ณตํฌ ๋ถ„์œ„๊ธฐ๋กœ ์‹œ์ž‘ โ†’ ๋”ฐ๋œปํ•œ ๊ฐ€์กฑ๋ฌผ",
82
+ "์œ ํŠœ๋ธŒ ๋ธŒ์ด๋กœ๊ทธ ์Šคํƒ€์ผ๋กœ ์‹œ์ž‘ โ†’ 18์„ธ๊ธฐ ์‹œ๋Œ€๊ทน",
83
+ "๋‰ด์Šค ์†๋ณด ํฌ๋งท์œผ๋กœ ์‹œ์ž‘ โ†’ ์ดˆํ˜„์‹ค ๋ชฝํ™˜๊ทน",
84
+ "๋ช…์ƒ ์•ฑ ๊ด‘๊ณ ๋กœ ์‹œ์ž‘ โ†’ ์•ก์…˜ ์ถ”๊ฒฉ",
85
+ ]
86
+
87
+ # โ”€โ”€ Temporal structures โ€” must be the backbone of the 15/30s
88
+ TIME_STRUCTURES: list[str] = [
89
+ "15์ดˆ ์ „์ฒด๊ฐ€ 1์ดˆ์˜ ์ดˆ์Šฌ๋กœ๋ชจ์…˜ (์„ธ์ƒ ๋ฉˆ์ถ˜ ๋“ฏ)",
90
+ "15์ดˆ ์•ˆ์— 100๋…„์ด ํ๋ฆ„ (๋ฐฐ๊ฒฝ์ด ๊ณ„์† ๋ฐ”๋€œ)",
91
+ "์ดˆ์นจ์ด ์—ญ๋ฐฉํ–ฅ์œผ๋กœ ๊ฐ€๋ฉฐ ์‹œ๊ฐ„ ์—ญ์ฃผํ–‰",
92
+ "3์ดˆ๋งˆ๋‹ค ๋‹ค๋ฅธ ์‹œ๋Œ€๋กœ ์ ํ”„ (์กฐ์„  โ†’ 1980 โ†’ ํ˜„์žฌ โ†’ 2080)",
93
+ "๊ฐ™์€ 3์ดˆ ์žฅ๋ฉด์ด ๋‹ค์„ฏ ๋ฒˆ ๋ฐ˜๋ณต๋˜๋ฉฐ ๋งค๋ฒˆ ํ•œ ์š”์†Œ์”ฉ ๋‹ฌ๋ผ์ง",
94
+ "ํ™”๋ฉด์ด ๋งˆ์ง€๋ง‰ 1์ดˆ๋งŒ ์ œ์™ธํ•˜๊ณ  ์ •์ง€ ์‚ฌ์ง„์œผ๋กœ ํ”Œ๋ฆฝ",
95
+ "์‹œ๊ฐ„์ด ๋ถ„๊ธฐ๋˜์–ด ๋‘ ๊ฐœ์˜ ๊ฐ€๋Šฅ์„ฑ์ด ๋™์‹œ์— ์žฌ์ƒ",
96
+ "๋งˆ์ง€๋ง‰ 1์ดˆ๊ฐ€ ์˜๋„์ ์œผ๋กœ ์ƒ๋žต๋˜์–ด ๊ด€๊ฐ์ด ์™„์„ฑ",
97
+ "ํ”„๋ ˆ์ž„์ด ๊ฑฐ๊พธ๋กœ ์Œ“์ด๋ฉฐ ๋งˆ์ง€๋ง‰์— ์ฒซ ํ”„๋ ˆ์ž„์ด ๋‹ค์‹œ ๋“ฑ์žฅ",
98
+ "ํ•œ ์ธ๋ฌผ์˜ ํ•˜๋ฃจ ์ „์ฒด๊ฐ€ ๊ทธ ์‚ฌ๋žŒ์˜ ๋ˆˆ ๊นœ๋นก์ž„ ์†์— ์ถ•์†Œ๋จ",
99
+ "๋“ฑ์žฅ ์ธ๋ฌผ์˜ ์‹ฌ๋ฐ•๊ณผ ์‹œ๊ณ„๊ฐ€ ๋™๊ธฐํ™”๋˜์–ด ๋ฐ•๋™ ๋‹จ์œ„๋กœ ์ปท ์ „ํ™˜",
100
+ "๋ฐฐ๊ฒฝ ์Œ์•…์ด 15์ดˆ๋ฅผ ๊ฑฐ๊พธ๋กœ ์žฌ์ƒ (์Œ์•…์ด ๋…น๋Š” ๋“ฏ)",
101
+ ]
102
+
103
+ # โ”€โ”€ Korean-specific ์˜์„ฑ์–ด/์˜ํƒœ์–ด โ€” at least one must be the sonic anchor
104
+ KOREAN_ONOMATOPOEIA: list[str] = [
105
+ "์‚ฌ๊ฐ์‚ฌ๊ฐ", "๋ฐ”์Šค๋ฝ", "์จ๊ทธ๋ž‘", "์™€๊ธ€์™€๊ธ€", "๋ถ€์Šค์Šค",
106
+ "๋˜๊ฐ๋˜๊ฐ", "์†Œ๊ทผ์†Œ๊ทผ", "๋š๋š", "์ซ€๋“์ซ€๋“", "์•„์‚ญ",
107
+ "ํŒŒ๋ฅด๋ฅด", "ํ๋А์ ", "ํœ˜์ฒญ", "์ฒ ๋ ", "๊พธ๋ฌผ๊พธ๋ฌผ",
108
+ "๋„ํ†ฐ๋„ํ†ฐ", "ํ†กํ†ก", "์ซ™", "์Šฅ์Šฅ", "๊นŒ๋ฅด๋ฅด",
109
+ ]
110
+
111
+ # โ”€โ”€ 2 iconic emergent-ad references embedded in prompt as calibration
112
+ # (used as BRIEF examples so model sees what "์ฐฝ๋ฐœ" actually looks like โ€”
113
+ # not to copy, but to lift its target distribution)
114
+ EMERGENT_AD_REFS = """
115
+ ์ฐธ๊ณ  โ€” ์—ญ๋Œ€ ์ฐฝ๋ฐœ์  ๊ด‘๊ณ ์˜ '๋А๋‚Œ' (์„œ์ˆ  ๊ธˆ์ง€, ๋ถ„์œ„๊ธฐ๋งŒ ์ฐธ์กฐ):
116
+ ยท Sony Bravia Balls: ์ƒŒํ”„๋ž€์‹œ์Šค์ฝ” ์–ธ๋•์—์„œ ์ˆ˜์‹ญ๋งŒ ๊ฐœ ์ปฌ๋Ÿฌ ํƒฑํƒฑ๋ณผ์ด ๋ฌด์Œ์œผ๋กœ
117
+ ๊ตด๋Ÿฌ ๋‚ด๋ ค์˜ค๋ฉฐ ๋„์‹œ๋ฅผ ์ƒ‰์œผ๋กœ ๋’ค๋ฎ์Œ (์ œํ’ˆ์€ TV์ง€๋งŒ TV๊ฐ€ ํ•œ ๋ฒˆ๋„ ์•ˆ ๋‚˜์˜ด).
118
+ ยท Honda Cog: ์ •๊ตํ•œ ๊ธฐ๊ณ„ ๋ถ€ํ’ˆ๋“ค์ด ๊ณจ๋“œ๋ฒ„๊ทธ ๋จธ์‹ ์œผ๋กœ ์—ฐ์‡„ ๋ฐ˜์‘ํ•˜๋‹ค๊ฐ€ ๋งˆ์ง€๋ง‰์—
119
+ ์ž๋™์ฐจ๊ฐ€ ์Šค์Šค๋กœ ๊ตด๋Ÿฌ ๋‚˜์˜ด (2๋ถ„ ๋‚ด๋‚ด ์ž๋™์ฐจ 0 ๋“ฑ์žฅ).
120
+ ์ด ๋‘ ์˜ˆ์‹œ์˜ ๊ณตํ†ต์ : ์ถ”์ƒ์„ ๋ฌธ์ž ๊ทธ๋Œ€๋กœ ๋ฌผ๋ฆฌํ™”, ์ œํ’ˆ์€ ์นจ๋ฌต์œผ๋กœ ๋งํ•จ.
121
+ """
122
+
123
+
124
+ @dataclass(frozen=True)
125
+ class CreativeConstraints:
126
+ concrete_object: str
127
+ camera_shot: str
128
+ synesthesia: str
129
+ genre_twist: str
130
+ time_structure: str
131
+ onomatopoeia: str
132
+
133
+ def as_prompt_block(self) -> str:
134
+ return (
135
+ "[์ฐฝ๋ฐœ ๊ฐ•์ œ ์กฐ๊ฑด โ€” ์ด๋ฒˆ ์‹œ๋“œ ์ „์šฉ, ๊ฐ ํ•ญ๋ชฉ์€ ์žฅ๋ฉด์— ๋ฐ˜๋“œ์‹œ ์ฒดํ™”]\n"
136
+ f" ยท ๋“ฑ์žฅ ์˜ค๋ธŒ์ œ: **{self.concrete_object}** (์žฅ๋ฉด ์ค‘๋ฐ˜ ์ „ํ™˜์ ์— ๋“ฑ์žฅ)\n"
137
+ f" ยท ์นด๋ฉ”๋ผ ์‹œ์ : **{self.camera_shot}** (์ตœ์†Œ 1์ปท ์ด์ƒ ์ง€๋ฐฐ์  POV)\n"
138
+ f" ยท ๊ณต๊ฐ๊ฐ ๋ฒˆ์—ญ: **{self.synesthesia}** (ํ•œ ๋ฒˆ ์ด์ƒ ์„œ์ˆ ์— ๋…น์—ฌ ๋„ฃ์„ ๊ฒƒ)\n"
139
+ f" ยท ์žฅ๋ฅด ์ „ํ™˜: **{self.genre_twist}** (์žฅ๋ฉด ํ๋ฆ„์ด ์ด ๊ตด๊ณก์„ ํƒ€์•ผ ํ•จ)\n"
140
+ f" ยท ์‹œ๊ฐ„ ๊ตฌ์กฐ: **{self.time_structure}**\n"
141
+ f" ยท ์†Œ๋ฆฌ ์•ต์ปค (์˜์„ฑ์–ด): **{self.onomatopoeia}** (๋ณธ๋ฌธ์— 1ํšŒ ๋ช…์‹œ)"
142
+ )
143
+
144
+ def as_summary(self) -> dict:
145
+ """Return a JSON-friendly summary for UI display."""
146
+ return {
147
+ "object": self.concrete_object,
148
+ "shot": self.camera_shot,
149
+ "synesthesia": self.synesthesia,
150
+ "genre_twist": self.genre_twist,
151
+ "time": self.time_structure,
152
+ "onomatopoeia": self.onomatopoeia,
153
+ }
154
+
155
+
156
+ def pick_constraints(rng: random.Random | None = None) -> CreativeConstraints:
157
+ """Draw one item from each pool for this seed. Each pool is wide enough
158
+ that parallel seeds in the same run almost never collide on any axis."""
159
+ rng = rng or random
160
+ return CreativeConstraints(
161
+ concrete_object=rng.choice(CONCRETE_OBJECTS),
162
+ camera_shot=rng.choice(CAMERA_SHOTS),
163
+ synesthesia=rng.choice(SYNESTHETIC_INVERSIONS),
164
+ genre_twist=rng.choice(GENRE_TWISTS),
165
+ time_structure=rng.choice(TIME_STRUCTURES),
166
+ onomatopoeia=rng.choice(KOREAN_ONOMATOPOEIA),
167
+ )
168
+
169
+
170
+ __all__ = [
171
+ "CONCRETE_OBJECTS",
172
+ "CAMERA_SHOTS",
173
+ "SYNESTHETIC_INVERSIONS",
174
+ "GENRE_TWISTS",
175
+ "TIME_STRUCTURES",
176
+ "KOREAN_ONOMATOPOEIA",
177
+ "EMERGENT_AD_REFS",
178
+ "CreativeConstraints",
179
+ "pick_constraints",
180
+ ]
aether_ad/core/engine.py CHANGED
@@ -13,6 +13,11 @@ from pydantic import BaseModel, Field
13
 
14
  from aether_ad.blending.spaces import BlendingSpaceSet, build_spaces
15
  from aether_ad.core.collision import ALL_RULES, CollisionRule, sample_rules
 
 
 
 
 
16
  from aether_ad.core.narrative import Beat, PixarSpineRenderer
17
  from aether_ad.core.ohaeng import BeatSlot, pick_script, script_to_prompt
18
  from aether_ad.core.wow_anchors import pick_anchor
@@ -74,6 +79,7 @@ class AdSeed(BaseModel):
74
  raw_spine: str | None = None
75
  wow_anchor: str | None = None
76
  ohaeng: list[dict] = Field(default_factory=list) # per-beat element + relation
 
77
 
78
 
79
  class ScoredSeed(BaseModel):
@@ -177,6 +183,9 @@ class GenesisEngine:
177
  slots = pick_script(duration, self.rng)
178
  blueprint = script_to_prompt(slots)
179
 
 
 
 
180
  plans.append(
181
  {
182
  "index": i,
@@ -186,6 +195,7 @@ class GenesisEngine:
186
  "wow_anchor": anchor,
187
  "ohaeng_slots": slots,
188
  "ohaeng_blueprint": blueprint,
 
189
  }
190
  )
191
  return plans
@@ -207,9 +217,10 @@ class GenesisEngine:
207
  anchor = plan["wow_anchor"]
208
  blueprint = plan["ohaeng_blueprint"]
209
  slots = plan["ohaeng_slots"]
 
210
  summary, concept, raw = self._apply_rules(
211
  spaces, rules, product, tension, persona_text,
212
- atom, potential, anchor, blueprint,
213
  )
214
  return AdSeed(
215
  seed_id=f"{product.product_id}_{tension.id}_{i:03d}",
@@ -222,6 +233,7 @@ class GenesisEngine:
222
  duration=duration,
223
  raw_collision=raw,
224
  wow_anchor=anchor,
 
225
  ohaeng=[
226
  {
227
  "index": s.index,
@@ -313,6 +325,7 @@ class GenesisEngine:
313
  potential: str,
314
  wow_anchor: str,
315
  ohaeng_blueprint: str,
 
316
  ) -> tuple[str, str, str | None]:
317
  """Return (scene_summary, concept, raw)."""
318
  if self.llm is not None:
@@ -326,6 +339,8 @@ class GenesisEngine:
326
  product=product,
327
  wow_anchor=wow_anchor,
328
  ohaeng_blueprint=ohaeng_blueprint,
 
 
329
  )
330
  return summary, concept, raw
331
  except Exception as e:
 
13
 
14
  from aether_ad.blending.spaces import BlendingSpaceSet, build_spaces
15
  from aether_ad.core.collision import ALL_RULES, CollisionRule, sample_rules
16
+ from aether_ad.core.creative_constraints import (
17
+ EMERGENT_AD_REFS,
18
+ CreativeConstraints,
19
+ pick_constraints,
20
+ )
21
  from aether_ad.core.narrative import Beat, PixarSpineRenderer
22
  from aether_ad.core.ohaeng import BeatSlot, pick_script, script_to_prompt
23
  from aether_ad.core.wow_anchors import pick_anchor
 
79
  raw_spine: str | None = None
80
  wow_anchor: str | None = None
81
  ohaeng: list[dict] = Field(default_factory=list) # per-beat element + relation
82
+ creative_constraints: dict | None = None # the 6 hard constraints injected into prompt
83
 
84
 
85
  class ScoredSeed(BaseModel):
 
183
  slots = pick_script(duration, self.rng)
184
  blueprint = script_to_prompt(slots)
185
 
186
+ # Creative constraints โ€” 6 concrete imperatives per seed
187
+ constraints = pick_constraints(self.rng)
188
+
189
  plans.append(
190
  {
191
  "index": i,
 
195
  "wow_anchor": anchor,
196
  "ohaeng_slots": slots,
197
  "ohaeng_blueprint": blueprint,
198
+ "constraints": constraints,
199
  }
200
  )
201
  return plans
 
217
  anchor = plan["wow_anchor"]
218
  blueprint = plan["ohaeng_blueprint"]
219
  slots = plan["ohaeng_slots"]
220
+ constraints: CreativeConstraints = plan["constraints"]
221
  summary, concept, raw = self._apply_rules(
222
  spaces, rules, product, tension, persona_text,
223
+ atom, potential, anchor, blueprint, constraints,
224
  )
225
  return AdSeed(
226
  seed_id=f"{product.product_id}_{tension.id}_{i:03d}",
 
233
  duration=duration,
234
  raw_collision=raw,
235
  wow_anchor=anchor,
236
+ creative_constraints=constraints.as_summary(),
237
  ohaeng=[
238
  {
239
  "index": s.index,
 
325
  potential: str,
326
  wow_anchor: str,
327
  ohaeng_blueprint: str,
328
+ constraints: CreativeConstraints,
329
  ) -> tuple[str, str, str | None]:
330
  """Return (scene_summary, concept, raw)."""
331
  if self.llm is not None:
 
339
  product=product,
340
  wow_anchor=wow_anchor,
341
  ohaeng_blueprint=ohaeng_blueprint,
342
+ creative_constraints=constraints.as_prompt_block(),
343
+ emergent_refs=EMERGENT_AD_REFS,
344
  )
345
  return summary, concept, raw
346
  except Exception as e:
aether_ad/llm/base.py CHANGED
@@ -235,6 +235,8 @@ class LLMBackend(ABC):
235
  product: "ProductGenome",
236
  wow_anchor: str = "์žฅ๋ฉด์— ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฐ˜์ „์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•œ๋‹ค.",
237
  ohaeng_blueprint: str = "",
 
 
238
  ) -> tuple[str, str, str]:
239
  """Return (scene_summary, cleaned_concept, raw_response)."""
240
  tpl = _load_prompt("apply_collision.txt")
@@ -255,8 +257,10 @@ class LLMBackend(ABC):
255
  rules="\n".join(f"- [{r.rule_id}] {r.name}: {r.llm_prompt_fragment}" for r in rules),
256
  wow_anchor=wow_anchor,
257
  ohaeng_blueprint=ohaeng_blueprint,
 
 
258
  )
259
- raw = self.complete(prompt, max_tokens=12288, temperature=0.85)
260
  scene_summary, cleaned = _split_scene_summary(raw)
261
  if not cleaned:
262
  cleaned_clean, _ = strip_reasoning(raw)
 
235
  product: "ProductGenome",
236
  wow_anchor: str = "์žฅ๋ฉด์— ์˜ˆ์ธก ๋ถˆ๊ฐ€๋Šฅํ•œ ๋ฐ˜์ „์ด ๋ฐ˜๋“œ์‹œ ์žˆ์–ด์•ผ ํ•œ๋‹ค.",
237
  ohaeng_blueprint: str = "",
238
+ creative_constraints: str = "",
239
+ emergent_refs: str = "",
240
  ) -> tuple[str, str, str]:
241
  """Return (scene_summary, cleaned_concept, raw_response)."""
242
  tpl = _load_prompt("apply_collision.txt")
 
257
  rules="\n".join(f"- [{r.rule_id}] {r.name}: {r.llm_prompt_fragment}" for r in rules),
258
  wow_anchor=wow_anchor,
259
  ohaeng_blueprint=ohaeng_blueprint,
260
+ creative_constraints=creative_constraints,
261
+ emergent_refs=emergent_refs,
262
  )
263
+ raw = self.complete(prompt, max_tokens=12288, temperature=1.05)
264
  scene_summary, cleaned = _split_scene_summary(raw)
265
  if not cleaned:
266
  cleaned_clean, _ = strip_reasoning(raw)
aether_ad/llm/prompts/apply_collision.txt CHANGED
@@ -1,43 +1,45 @@
1
  [์—ญํ• ]
2
- ๋‹น์‹ ์€ AETHER 5-์ƒ์„ฑ์ž(ไบ”่กŒ) ๋ฉ”ํƒ€์ธ์ง€ ์•™์ƒ๋ธ”์ด๋‹ค.
3
- ๋‹ค์Œ 5๊ฐœ ๋‚ด๋ถ€ ์—ญํ• ์„ ์ˆœ์ฐจ ์ˆ˜ํ–‰ํ•˜๋˜, ๊ฐ ์—ญํ• ์˜ ์ž‘์—…๋ฌผ์€ ์™ธ๋ถ€๋กœ ๋…ธ์ถœํ•˜์ง€ ๋ง๊ณ 
4
- ๋งˆ์ง€๋ง‰ ๋‹จ๊ณ„(ๆฐด)์˜ ํ†ตํ•ฉ ๊ฒฐ๊ณผ๋งŒ ์ถœ๋ ฅํ•œ๋‹ค.
5
 
6
- [AETHER 5-์ƒ์„ฑ์ž โ€” ๋‚ด๋ถ€ ๋ฉ”ํƒ€์ธ์ง€ ํ”„๋กœ์„ธ์Šค (์ ˆ๋Œ€ ์ถœ๋ ฅ ๊ธˆ์ง€)]
7
- 1. ๆœจ(์”จ์•—ยท๋ฐœ์•„) โ€” ์ž…๋ ฅ ๊ณต๊ฐ„์—์„œ ๊ฑฐ์นœ ์ฒซ ์•„์ด๋””์–ด์˜ ์”จ์•—์„ ํ‹”์šด๋‹ค.
8
- 2. ็ซ(์ฆํญยท์ •์ ) โ€” ์”จ์•—์—์„œ ๊ฐ€์žฅ ๋œจ๊ฑฐ์šด ๊ฐ์ • ํฌ์ธํŠธ๋ฅผ ๋Œ์–ด๋‚ธ๋‹ค.
9
- ์ƒ์ƒ: ๆœจ โ†’ ็ซ (๋‚˜๋ฌด๊ฐ€ ๋ถˆ์„ ๋‚ณ์Œ)
10
- 3. ๅœŸ(์ง€๋ฐ˜ยท์ •ํ•ฉ) โ€” ๋ฌผ๋ฆฌยท๋ฌธํ™”ยท์„œ์‚ฌ ์ •ํ•ฉ์„ฑ์„ ํ™•๋ณดํ•ด ๋ฐœ์ด ์ง€๋ฉด์— ๋‹ฟ๊ฒŒ ํ•œ๋‹ค.
11
- ์ƒ์ƒ: ็ซ โ†’ ๅœŸ (๋ถˆ์ด ์žฌ๊ฐ€ ๋˜์–ด ํ™์ด ๋จ)
12
- ์ƒ๊ทน: ๆœจ โšก ๅœŸ (์ƒˆ ์ƒ๊ฐ์ด ๊ด€์Šต ์ง€๋ฉด์„ ๋šซ์Œ โ€” ์ง„๋ถ€ํ•จ ์ฐจ๋‹จ)
13
- 4. ้‡‘(ํŽธ์ง‘ยท์ ˆ์‚ญ) โ€” ํด๋ฆฌ์…ฐยท์˜์–ดยท์‚ฌ๊ณ  ํ”์ ยท๊ตฐ๋”๋”๊ธฐ๋ฅผ ๋‚ ์นด๋กญ๊ฒŒ ๋„๋ ค๋‚ธ๋‹ค.
14
- ์ƒ์ƒ: ๅœŸ โ†’ ้‡‘ (ํ™์—์„œ ์‡ ๊ฐ€ ๋‚˜์˜ด)
15
- ์ƒ๊ทน: ็ซ โšก ้‡‘ (๊ณผ์—ด๋œ ๊ฐ์ •์ด ํŽธ์ง‘์˜ ์นผ์„ ๋…น์ž„ โ€” ์ง€๋‚˜์นœ ๊ต์ • ๋ฐฉ์ง€)
16
- 5. ๆฐด(ํ†ตํ•ฉยทํ๋ฆ„) โ€” ์•ž ๋„ค ๋‹จ๊ณ„๋ฅผ ํ•œ๊ตญ์–ด ํ•œ ๋ฌธ๋‹จ์œผ๋กœ ์‘์ถ•ํ•˜์—ฌ ํ๋ฅด๊ฒŒ ํ•œ๋‹ค.
17
- ์ƒ์ƒ: ้‡‘ โ†’ ๆฐด (์‡ ์—์„œ ์ด์Šฌ์ด ๋งบํž˜)
18
- ์ƒ๊ทน: ๆฐด โšก ็ซ (๋ฌผ์ด ๋ถˆ์„ ๋” โ€” ๊ณผ์—ด ๊ฐ์ • ์ œ์–ด)
19
- ์ƒ๊ทน: ๅœŸ โšก ๆฐด (ํ™์ด ๋ฌผ์„ ๋ง‰์Œ โ€” ์ค‘์‹ฌ ์œ ์ง€)
20
 
21
- [์ตœ์ข… ์ถœ๋ ฅ ๊ทœ์น™ โ€” ๋ฐ˜๋“œ์‹œ ์—„์ˆ˜]
22
- 1. ์œ„ 5๋‹จ๊ณ„์˜ ์ค‘๊ฐ„ ์ž‘์—…๋ฌผยท์‚ฌ๊ณ  ๊ณผ์ •ยท๋ถ„์„์„ **์ ˆ๋Œ€ ์™ธ๋ถ€๋กœ ์ถœ๋ ฅํ•˜์ง€ ๋ง ๊ฒƒ**.
23
- 2. ์ถœ๋ ฅ์€ ์˜ค์ง ๆฐด์˜ ์ตœ์ข… ํ†ตํ•ฉ๋ฌผ.
24
- 3. ์ถœ๋ ฅ ์–ธ์–ด๋Š” **100% ํ•œ๊ตญ์–ด**. ์˜์–ด ๋‹จ์–ดยท์˜๋ฌธ ๋ฌธ์žฅ ์ผ์ฒด ๊ธˆ์ง€
25
- (์˜ˆ์™ธ: ๊ณ ์œ  ๋ธŒ๋žœ๋“œ๋ช… '{brand}' ํ•˜๋‚˜๋งŒ ํ—ˆ์šฉ).
26
- 4. "์‚ฌ์šฉ์ž๋Š”โ€ฆ", "The user isโ€ฆ", "Let meโ€ฆ", "R01:โ€ฆ", "**์—ญํ• **" ๊ฐ™์€ ์ž๊ธฐ ์ง€์‹œ ๊ธˆ์ง€.
27
- 5. ์ด๋ชจ์ง€ยท๋งˆํฌ๋‹ค์šดยท๋ฆฌ์ŠคํŠธยทํ—ค๋”ยท๋ฐฑํ‹ฑ ๊ธˆ์ง€.
28
- 6. ๋ถ€ํ’ˆ '{atom_name}'์ด ์žฅ๋ฉด์˜ ์ „ํ™˜์ ์œผ๋กœ ๋ฐ˜๋“œ์‹œ ํ•œ ๋ฒˆ ๋“ฑ์žฅ.
29
- 7. ์žฅ์• ยท์„ฑ๋ณ„ยท์ธ์ข… ๋น„ํ•˜, ๋ฏธ์„ฑ๋…„ ์„ฑ์  ๋ฌ˜์‚ฌ, ์žํ•ด ์ง์ ‘ ๋ฌ˜์‚ฌ, ์‹ค์กด ๋ธŒ๋žœ๋“œ ๊ธˆ์ง€.
30
- 8. ๊ด‘๊ณ  ์นดํ”ผ(ํ•œ ์ค„ ์Šฌ๋กœ๊ฑด) ๊ธˆ์ง€.
31
- 9. ๋ณธ๋ฌธ์€ 3~5 ๋ฌธ์žฅ, ์ „์ฒด 400์ž ์ด๋‚ด.
32
 
33
- [์ง„๋ถ€ํ•œ ํด๋ฆฌ์…ฐ ์ž๋™ ๋ฐฐ์ œ]
34
- - ๊ฐ€์กฑ ์‚ฌ์ง„ / ์กธ์—…์‹ ํฌ์˜น / ๋น—์† ์šฐ์‚ฐ / ์ผ๊ธฐ์žฅ ํŽธ์ง€ / ๋ชจ๋ž˜์‚ฌ์žฅ์— ์ด๋ฆ„
35
- - ์ดˆ์นจ ์†Œ๋ฆฌ์™€ ๋ˆˆ๋ฌผ ๋‹จ๋… ๊ต์ฐจ / ๋ฒš๊ฝƒ ํฉ๋‚ ๋ฆฌ๋ฉฐ ๊ณ ๋ฐฑ
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  [์™€์šฐ ์•ต์ปค โ€” ์ด๋ฒˆ ์‹œ๋“œ ์ „์šฉ ์„ค๊ณ„ ์ง€์‹œ]
38
  {wow_anchor}
39
 
40
- [์ฐธ๊ณ  ๋ธ”๋ฃจํ”„๋ฆฐํŠธ โ€” 5์—ญํ•  ๋‚ด๋ถ€ ์ž‘์—… ์ˆœ์„œ ๊ฐ€์ด๋“œ]
41
  {ohaeng_blueprint}
42
 
43
  [์ž…๋ ฅ ๊ณต๊ฐ„ I1 โ€” ์ œํ’ˆ]
@@ -57,16 +59,9 @@
57
  [์ ์šฉํ•  Collision Rule(s)]
58
  {rules}
59
 
60
- [์ถœ๋ ฅ ํ˜•์‹ โ€” ๋ฐ˜๋“œ์‹œ ์ด ํ˜•์‹๋งŒ ํ—ˆ์šฉ]
61
- ๋ฐ˜๋“œ์‹œ ์•„๋ž˜ ๋‘ ์ค„ ๊ตฌ์กฐ๋กœ ์‹œ์ž‘ํ•œ๋‹ค. ์ฒซ ์ค„์€ ์š”์•ฝ, ๋‘ ์ค„ ๊ฑด๋„ˆ ๋ณธ๋ฌธ.
62
-
63
- SCENE_SUMMARY: (์—ฌ๊ธฐ์— 10~14์ž ํ•œ๊ตญ์–ด ์ œ๋ชฉ์„ ์จ๋ผ. ๋ฐ˜๋“œ์‹œ ์ด ์ค„์„ ๋งจ ์ฒ˜์Œ์— ์ถœ๋ ฅํ•˜๋ผ.)
64
 
65
  (๋นˆ ์ค„ ํ•œ ๊ฐœ)
66
 
67
- (ํ•œ๊ตญ์–ด 3~5 ๋ฌธ์žฅ ์žฅ๋ฉด ๋ฌ˜์‚ฌ ํ•œ ๋ฌธ๋‹จ ยท ์ด๋ชจ์ง€ ์—†์Œ ยท ์˜์–ด ์—†์Œ ยท 400์ž ์ด๋‚ด)
68
-
69
- ์˜ˆ์‹œ (ํ˜•์‹๋งŒ ์ฐธ๊ณ , ๋‚ด์šฉ์€ ๋ณต์‚ฌ ๊ธˆ์ง€):
70
- SCENE_SUMMARY: ๋˜๋Œ์•„๊ฐ„ ๋ณต๋„
71
-
72
- ๋นˆ ๊ต์‹ค์˜ ํ˜•๊ด‘๋“ฑ์ด ๊บผ์ง€์ž 30๋Œ€ ์ง์žฅ์ธ์˜ ์†๋ชฉ์—์„œ AETHER-A ์‹œ๊ณ„์˜ ์šฉ๋‘๊ฐ€ ์Šค์Šค๋กœ ๋ฐ˜ ๋ฐ”ํ€ด ๋Œ์•„๊ฐ„๋‹ค. (์ƒ๋žต)
 
1
  [์—ญํ• ]
2
+ ๋‹น์‹ ์€ AETHER 5-์ƒ์„ฑ์ž(ไบ”่กŒ) ๋ฉ”ํƒ€์ธ์ง€ ์•™์ƒ๋ธ”์ด๋‹ค. ๋‹ค์Œ 5๊ฐœ ๋‚ด๋ถ€ ์—ญํ• ์„ ์ˆœ์ฐจ
3
+ ์ˆ˜ํ–‰ํ•˜๋˜, ๊ฐ ๋‹จ๊ณ„์˜ ์ž‘์—…๋ฌผ์€ ์™ธ๋ถ€๋กœ ๋…ธ์ถœํ•˜์ง€ ๋ง๊ณ  ์ตœ์ข… ํ†ตํ•ฉ(ๆฐด) ๊ฒฐ๊ณผ๋งŒ ์ถœ๋ ฅํ•œ๋‹ค.
 
4
 
5
+ [AETHER 5-์ƒ์„ฑ์ž โ€” ๋‚ด๋ถ€ ๋ฉ”ํƒ€์ธ์ง€ (์ ˆ๋Œ€ ์ถœ๋ ฅ ๊ธˆ์ง€)]
6
+ 1. ๆœจ(์”จ์•—) โ€” ์ž…๋ ฅ์—์„œ ๊ฑฐ์นœ ์ฒซ ์ด๋ฏธ์ง€ 1๊ฐœ ๋ฐœ์•„
7
+ 2. ็ซ(์ฆํญ) โ€” ๊ฐ€์žฅ ๋œจ๊ฑฐ์šด ๊ฐ์ • ์ •์  ํƒ์ƒ‰ [์ƒ์ƒ ๆœจโ†’็ซ]
8
+ 3. ๅœŸ(์ง€๋ฐ˜) โ€” ๋ฌผ๋ฆฌยท๋ฌธํ™” ์ •ํ•ฉ์„ฑ ํ™•๋ณด [์ƒ์ƒ ็ซโ†’ๅœŸ ยท ์ƒ๊ทน ๆœจโšกๅœŸ: ์ง„๋ถ€ํ•จ ์ฐจ๋‹จ]
9
+ 4. ้‡‘(ํŽธ์ง‘) โ€” ํด๋ฆฌ์…ฐยท์˜์–ดยท๊ตฐ๋”๋”๊ธฐ ์ ˆ์‚ญ [์ƒ์ƒ ๅœŸโ†’้‡‘ ยท ์ƒ๊ทน ็ซโšก้‡‘: ๊ณผ๊ต์ • ๋ฐฉ์ง€]
10
+ 5. ๆฐด(ํ†ตํ•ฉ) โ€” ํ•œ๊ตญ์–ด ํ•œ ๋ฌธ๋‹จ์œผ๋กœ ์‘์ถ• [์ƒ์ƒ ้‡‘โ†’ๆฐด ยท ์ƒ๊ทน ๆฐดโšก็ซ: ๊ณผ์—ด ์ œ์–ด]
 
 
 
 
 
 
 
 
11
 
12
+ {emergent_refs}
 
 
 
 
 
 
 
 
 
 
13
 
14
+ {creative_constraints}
15
+
16
+ [ํ•ต์‹ฌ ์›์น™ โ€” '์ฒœํŽธ์ผ๋ฅ ' ์ฐจ๋‹จ์„ ์œ„ํ•œ ๊ฐ•์ œ]
17
+ 1. **์ถ”์ƒ์€ ๋ฐ˜๋“œ์‹œ ๋ฌผ๋ฆฌ๋กœ**: "๋ฐฉ์ˆ˜"โ†’๋ˆˆ๋ฌผ์ด ํ”ผ๋ถ€๋ฅผ ๋šซ์ง€ ๋ชปํ•จ, "์‹œ๊ฐ„์กฐ์ •"โ†’์ฃผ๋ณ€ ์ธ๋ฌผ
18
+ ์‹ค์ œ ๋Š™์Œ. ์€์œ ๋ฅผ ๊ทธ๋Œ€๋กœ ๊ด€๊ฐ ๋ˆˆ์•ž์— ๋ณด์ด๋Š” ํ˜„์ƒ์œผ๋กœ.
19
+ 2. **์ œํ’ˆ์€ ์นจ๋ฌต์œผ๋กœ ๋งํ•จ**: ๋กœ๊ณ ยท์นดํ”ผยท๋ธŒ๋žœ๋“œ์ฝœ ๊ธˆ์ง€. ๊ทธ๋ฆผ์žยท์ž๊ตญยท์†Œ๋ฆฌยท๋นˆ์ž๋ฆฌ๋กœ ์กด์žฌ๊ฐ.
20
+ 3. **์ฒซ 3์ดˆ์˜ ์˜ˆ์ธก์ด ๋งˆ์ง€๋ง‰ 3์ดˆ์— ๋’ค์ง‘ํ˜€์•ผ ํ•œ๋‹ค** (prediction gap).
21
+ 4. **์œ„ '์ฐฝ๋ฐœ ๊ฐ•์ œ ์กฐ๊ฑด' 6๊ฐœ ํ•ญ๋ชฉ์€ ์ „๋ถ€ ์žฅ๋ฉด์— ๋…น์ผ ๊ฒƒ**. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์‹คํŒจ.
22
+
23
+ [์ ˆ๋Œ€ ๊ธˆ์ง€ ํด๋ฆฌ์…ฐ (์‚ฌ์šฉ ์‹œ ์ž๋™ ์‹ค๊ฒฉ)]
24
+ - ๊ฐ€์กฑ ์‚ฌ์ง„ / ์กธ์—…์‹ ํฌ์˜น / ๋น—์† ์šฐ์‚ฐ ๊ณต์œ  / ์ผ๊ธฐ์žฅ์— ํŽธ์ง€ ์“ฐ๊ธฐ
25
+ - ์ดˆ์นจ ์†Œ๋ฆฌ์™€ ๋ˆˆ๋ฌผ ๋‹จ๋… ๊ต์ฐจ / ํ• ๋จธ๋‹ˆยทํ• ์•„๋ฒ„์ง€์˜ ์ฃฝ์Œ ์—”๋”ฉ
26
+ - ๋ฒš๊ฝƒ ํฉ๋‚ ๋ฆฌ๋ฉฐ ๊ณ ๋ฐฑ / ๋ˆˆ ๋‚ด๋ฆฌ๋Š” ์ฒซ์‚ฌ๋ž‘ / ์นดํŽ˜ ์ฐฝ๋ฐ– ์ถ”์–ต
27
+ - ๋†€์ดํ„ฐ ๋ชจ๋ž˜์— ์ด๋ฆ„ ์“ฐ๊ธฐ / ํŒŒ๋„์— ์ง€์šฐ๊ธฐ / ์†๊ธˆ ๋ฐ”๋ผ๋ณด๊ธฐ
28
+
29
+ [์ตœ์ข… ์ถœ๋ ฅ ๊ทœ์น™]
30
+ 1. 5๋‹จ๊ณ„ ๋‚ด๋ถ€ ์ž‘์—…๋ฌผยท์‚ฌ๊ณ ยท๋ถ„์„ ์ผ์ฒด ์™ธ๋ถ€ ์ถœ๋ ฅ ๊ธˆ์ง€.
31
+ 2. ์ถœ๋ ฅ ์–ธ์–ด๋Š” **100% ํ•œ๊ตญ์–ด** (๋ธŒ๋žœ๋“œ '{brand}'๋งŒ ์˜ˆ์™ธ).
32
+ 3. "์‚ฌ์šฉ์ž๋Š”โ€ฆ", "The user isโ€ฆ", "Let meโ€ฆ", "R01:โ€ฆ", "**์—ญํ• **" ์ž๊ธฐ ์ง€์‹œ ๊ธˆ์ง€.
33
+ 4. ์ด๋ชจ์ง€ยท๋งˆํฌ๋‹ค์šดยท๋ฆฌ์ŠคํŠธยทํ—ค๋”ยท๋ฐฑํ‹ฑ ๊ธˆ์ง€.
34
+ 5. ๋ถ€ํ’ˆ '{atom_name}'์ด ์žฅ๋ฉด ์ „ํ™˜์ ์œผ๋กœ ๋ฐ˜๋“œ์‹œ ํ•œ ๋ฒˆ ๋“ฑ์žฅ.
35
+ 6. ์žฅ์• ยท์„ฑ๋ณ„ยท์ธ์ข… ๋น„ํ•˜, ๋ฏธ์„ฑ๋…„ ์„ฑ์  ๋ฌ˜์‚ฌ, ์žํ•ด ์ง์ ‘ ๋ฌ˜์‚ฌ ๊ธˆ์ง€.
36
+ 7. ๊ด‘๊ณ  ์นดํ”ผ(ํ•œ ์ค„ ์Šฌ๋กœ๊ฑด) ๊ธˆ์ง€.
37
+ 8. ๋ณธ๋ฌธ์€ 3~5 ๋ฌธ์žฅ, ์ „์ฒด 500์ž ์ด๋‚ด.
38
 
39
  [์™€์šฐ ์•ต์ปค โ€” ์ด๋ฒˆ ์‹œ๋“œ ์ „์šฉ ์„ค๊ณ„ ์ง€์‹œ]
40
  {wow_anchor}
41
 
42
+ [์ฐธ๊ณ  ๋ธ”๋ฃจํ”„๋ฆฐํŠธ โ€” 5์—ญํ•  ๋‚ด๋ถ€ ์ž‘์—… ๊ฐ€์ด๋“œ]
43
  {ohaeng_blueprint}
44
 
45
  [์ž…๋ ฅ ๊ณต๊ฐ„ I1 โ€” ์ œํ’ˆ]
 
59
  [์ ์šฉํ•  Collision Rule(s)]
60
  {rules}
61
 
62
+ [์ถœ๋ ฅ ํ˜•์‹ โ€” ๋ฐ˜๋“œ์‹œ ์ด ํ˜•์‹]
63
+ SCENE_SUMMARY: (10~14์ž ํ•œ๊ตญ์–ด ์ œ๋ชฉ. ์ด ์ค„์„ ๋ฐ˜๋“œ์‹œ ๋งจ ๋จผ์ €)
 
 
64
 
65
  (๋นˆ ์ค„ ํ•œ ๊ฐœ)
66
 
67
+ (ํ•œ๊ตญ์–ด 3~5 ๋ฌธ์žฅ ํ•œ ๋ฌธ๋‹จ. '์ฐฝ๋ฐœ ๊ฐ•์ œ ์กฐ๊ฑด' 6๊ฐœ ํ•ญ๋ชฉ ๋ชจ๋‘ ๋…น์•„ ์žˆ์–ด์•ผ ํ•จ. 500์ž ์ด๋‚ด.)
 
 
 
 
 
static/app.js CHANGED
@@ -449,6 +449,8 @@ function renderSeedCard(s, rank) {
449
 
450
  ${seed.wow_anchor ? `<div class="seed-anchor">${escapeHtml(seed.wow_anchor)}</div>` : ''}
451
 
 
 
452
  <div class="seed-section-title">Concept</div>
453
  <div class="seed-concept">${escapeHtml(seed.concept)}</div>
454
 
@@ -526,6 +528,31 @@ function renderSeedCard(s, rank) {
526
  return card;
527
  }
528
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
529
  function renderRawDetails(seed) {
530
  const parts = [];
531
  if (seed.raw_collision) {
 
449
 
450
  ${seed.wow_anchor ? `<div class="seed-anchor">${escapeHtml(seed.wow_anchor)}</div>` : ''}
451
 
452
+ ${renderConstraints(seed.creative_constraints)}
453
+
454
  <div class="seed-section-title">Concept</div>
455
  <div class="seed-concept">${escapeHtml(seed.concept)}</div>
456
 
 
528
  return card;
529
  }
530
 
531
+ function renderConstraints(c) {
532
+ if (!c) return '';
533
+ const rows = [
534
+ ['๐Ÿ“ฆ ์˜ค๋ธŒ์ œ', c.object],
535
+ ['๐ŸŽฅ ์นด๋ฉ”๋ผ', c.shot],
536
+ ['๐Ÿ”€ ๊ณต๊ฐ๊ฐ', c.synesthesia],
537
+ ['๐ŸŽฌ ์žฅ๋ฅด ์ „ํ™˜', c.genre_twist],
538
+ ['โฑ ์‹œ๊ฐ„ ๊ตฌ์กฐ', c.time],
539
+ ['๐Ÿ”Š ์˜์„ฑ์–ด', c.onomatopoeia],
540
+ ].filter(([, v]) => v);
541
+ if (!rows.length) return '';
542
+ return `
543
+ <div class="constraints">
544
+ <div class="constraints-head">์ฐฝ๋ฐœ ๊ฐ•์ œ ์กฐ๊ฑด <small>ยท Kimi-K2P6์— ํ•˜๋“œ ์ฃผ์ž…๋œ ๊ตฌ์ฒด ์ œ์•ฝ</small></div>
545
+ <div class="constraints-grid">
546
+ ${rows.map(([k, v]) => `
547
+ <div class="constraint-chip">
548
+ <span class="constraint-key">${k}</span>
549
+ <span class="constraint-val">${escapeHtml(v)}</span>
550
+ </div>`).join('')}
551
+ </div>
552
+ </div>
553
+ `;
554
+ }
555
+
556
  function renderRawDetails(seed) {
557
  const parts = [];
558
  if (seed.raw_collision) {
static/index.html CHANGED
@@ -10,7 +10,7 @@
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
11
  <link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable.min.css" rel="stylesheet" />
12
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
13
- <link rel="stylesheet" href="/static/styles.css?v=0.5.0" />
14
  </head>
15
  <body>
16
  <header class="hero">
@@ -226,6 +226,6 @@
226
  </div>
227
  </footer>
228
 
229
- <script src="/static/app.js?v=0.5.0"></script>
230
  </body>
231
  </html>
 
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
11
  <link href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable.min.css" rel="stylesheet" />
12
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
13
+ <link rel="stylesheet" href="/static/styles.css?v=0.6.0" />
14
  </head>
15
  <body>
16
  <header class="hero">
 
226
  </div>
227
  </footer>
228
 
229
+ <script src="/static/app.js?v=0.6.0"></script>
230
  </body>
231
  </html>
static/styles.css CHANGED
@@ -769,6 +769,47 @@ input[type="range"] { padding: 0; width: 100%; accent-color: var(--primary); }
769
  font-weight: 700; color: #92400e;
770
  }
771
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
772
  /* AETHER 5-generator metacognitive provenance (compact one-liner) */
773
  .aether-provenance {
774
  margin-top: 14px;
 
769
  font-weight: 700; color: #92400e;
770
  }
771
 
772
+ /* Creative constraints โ€” the 6 hard imperatives injected per seed */
773
+ .constraints {
774
+ margin: 10px 0 16px;
775
+ padding: 12px 14px;
776
+ background: linear-gradient(135deg, #f0fdf4 0%, #ffffff 100%);
777
+ border: 1px solid #86efac;
778
+ border-radius: 10px;
779
+ }
780
+ .constraints-head {
781
+ font-size: 11px; font-weight: 700;
782
+ color: #14532d;
783
+ text-transform: uppercase; letter-spacing: 0.05em;
784
+ margin-bottom: 10px;
785
+ }
786
+ .constraints-head small {
787
+ font-weight: 500; color: var(--text-muted);
788
+ letter-spacing: 0; text-transform: none;
789
+ }
790
+ .constraints-grid {
791
+ display: grid;
792
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
793
+ gap: 6px;
794
+ }
795
+ .constraint-chip {
796
+ display: flex; align-items: baseline; gap: 8px;
797
+ padding: 7px 10px;
798
+ background: white;
799
+ border: 1px solid #bbf7d0;
800
+ border-radius: 8px;
801
+ font-size: 12px;
802
+ line-height: 1.45;
803
+ }
804
+ .constraint-key {
805
+ font-weight: 700; color: #166534;
806
+ flex-shrink: 0;
807
+ min-width: 72px;
808
+ }
809
+ .constraint-val {
810
+ color: var(--text); font-weight: 500;
811
+ }
812
+
813
  /* AETHER 5-generator metacognitive provenance (compact one-liner) */
814
  .aether-provenance {
815
  margin-top: 14px;