08_prompting.py

Download
python 792 lines 21.0 KB
  1"""
  208. ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง ์˜ˆ์ œ
  3
  4๋‹ค์–‘ํ•œ ํ”„๋กฌํ”„ํŒ… ๊ธฐ๋ฒ•๊ณผ ์ตœ์ ํ™” ์ „๋žต
  5"""
  6
  7print("=" * 60)
  8print("ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง")
  9print("=" * 60)
 10
 11
 12# ============================================
 13# 1. ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ ํด๋ž˜์Šค
 14# ============================================
 15print("\n[1] ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ")
 16print("-" * 40)
 17
 18class PromptTemplate:
 19    """์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ"""
 20
 21    def __init__(self, template: str, input_variables: list = None):
 22        self.template = template
 23        self.input_variables = input_variables or []
 24
 25    def format(self, **kwargs) -> str:
 26        """๋ณ€์ˆ˜๋ฅผ ์ฑ„์›Œ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ"""
 27        return self.template.format(**kwargs)
 28
 29    @classmethod
 30    def from_file(cls, path: str):
 31        """ํŒŒ์ผ์—์„œ ํ…œํ”Œ๋ฆฟ ๋กœ๋“œ"""
 32        with open(path, 'r', encoding='utf-8') as f:
 33            return cls(f.read())
 34
 35# ๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ
 36basic_template = PromptTemplate(
 37    template="""You are a {role}.
 38Task: {task}
 39Input: {input}
 40Output:""",
 41    input_variables=["role", "task", "input"]
 42)
 43
 44prompt = basic_template.format(
 45    role="helpful assistant",
 46    task="translate to Korean",
 47    input="Hello, world!"
 48)
 49print("๊ธฐ๋ณธ ํ…œํ”Œ๋ฆฟ ์˜ˆ์‹œ:")
 50print(prompt)
 51
 52
 53# ============================================
 54# 2. Zero-shot vs Few-shot
 55# ============================================
 56print("\n[2] Zero-shot vs Few-shot")
 57print("-" * 40)
 58
 59# Zero-shot ํ”„๋กฌํ”„ํŠธ
 60zero_shot = """๋‹ค์Œ ๋ฆฌ๋ทฐ์˜ ๊ฐ์„ฑ์„ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
 61๋ฆฌ๋ทฐ: "์ด ์˜ํ™”๋Š” ์ •๋ง ์ง€๋ฃจํ–ˆ์–ด์š”."
 62๊ฐ์„ฑ:"""
 63
 64print("Zero-shot:")
 65print(zero_shot)
 66
 67# Few-shot ํ”„๋กฌํ”„ํŠธ
 68few_shot = """๋‹ค์Œ ๋ฆฌ๋ทฐ์˜ ๊ฐ์„ฑ์„ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
 69
 70๋ฆฌ๋ทฐ: "์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ์˜ํ™”์˜€์–ด์š”!"
 71๊ฐ์„ฑ: ๊ธ์ •
 72
 73๋ฆฌ๋ทฐ: "์ตœ์•…์˜ ์˜ํ™”, ์‹œ๊ฐ„ ๋‚ญ๋น„"
 74๊ฐ์„ฑ: ๋ถ€์ •
 75
 76๋ฆฌ๋ทฐ: "๊ทธ๋ƒฅ ๊ทธ๋žฌ์–ด์š”"
 77๊ฐ์„ฑ: ์ค‘๋ฆฝ
 78
 79๋ฆฌ๋ทฐ: "์ด ์˜ํ™”๋Š” ์ •๋ง ์ง€๋ฃจํ–ˆ์–ด์š”."
 80๊ฐ์„ฑ:"""
 81
 82print("\nFew-shot:")
 83print(few_shot)
 84
 85
 86# ============================================
 87# 3. Few-shot ํ”„๋กฌํ”„ํŠธ ๋นŒ๋”
 88# ============================================
 89print("\n[3] Few-shot ํ”„๋กฌํ”„ํŠธ ๋นŒ๋”")
 90print("-" * 40)
 91
 92class FewShotPromptTemplate:
 93    """Few-shot ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ๊ธฐ"""
 94
 95    def __init__(
 96        self,
 97        examples: list,
 98        example_template: str,
 99        prefix: str = "",
100        suffix: str = "",
101        separator: str = "\n\n"
102    ):
103        self.examples = examples
104        self.example_template = example_template
105        self.prefix = prefix
106        self.suffix = suffix
107        self.separator = separator
108
109    def format(self, **kwargs) -> str:
110        # ์˜ˆ์‹œ๋“ค ํฌ๋งทํŒ…
111        formatted_examples = [
112            self.example_template.format(**ex)
113            for ex in self.examples
114        ]
115
116        # ์กฐํ•ฉ
117        parts = []
118        if self.prefix:
119            parts.append(self.prefix)
120        parts.extend(formatted_examples)
121        if self.suffix:
122            parts.append(self.suffix.format(**kwargs))
123
124        return self.separator.join(parts)
125
126# ๊ฐ์„ฑ ๋ถ„์„ Few-shot
127sentiment_examples = [
128    {"text": "์ •๋ง ์ข‹์•„์š”!", "sentiment": "๊ธ์ •"},
129    {"text": "๋ณ„๋กœ์˜ˆ์š”", "sentiment": "๋ถ€์ •"},
130    {"text": "๊ทธ๋ƒฅ ๋ณดํ†ต์ด์—์š”", "sentiment": "์ค‘๋ฆฝ"},
131]
132
133sentiment_prompt = FewShotPromptTemplate(
134    examples=sentiment_examples,
135    example_template="ํ…์ŠคํŠธ: {text}\n๊ฐ์„ฑ: {sentiment}",
136    prefix="๋‹ค์Œ ํ…์ŠคํŠธ์˜ ๊ฐ์„ฑ์„ ๋ถ„์„ํ•˜์„ธ์š”.",
137    suffix="ํ…์ŠคํŠธ: {text}\n๊ฐ์„ฑ:"
138)
139
140result = sentiment_prompt.format(text="์˜ค๋Š˜ ๊ธฐ๋ถ„์ด ์ข‹๋„ค์š”")
141print("Few-shot ๊ฐ์„ฑ ๋ถ„์„ ํ”„๋กฌํ”„ํŠธ:")
142print(result)
143
144
145# ============================================
146# 4. Chain-of-Thought (CoT)
147# ============================================
148print("\n[4] Chain-of-Thought (CoT)")
149print("-" * 40)
150
151# Zero-shot CoT
152zero_shot_cot = """Q: Roger has 5 tennis balls. He buys 2 more cans of 3 balls each.
153   How many balls does he have now?
154
155Let's think step by step."""
156
157print("Zero-shot CoT:")
158print(zero_shot_cot)
159
160# Few-shot CoT
161few_shot_cot = """Q: There are 15 trees in the grove. Grove workers plant trees today.
162   After they are done, there will be 21 trees. How many trees did they plant?
163
164A: Let's think step by step.
1651. Started with 15 trees.
1662. After planting, there are 21 trees.
1673. Trees planted = 21 - 15 = 6 trees.
168The answer is 6.
169
170Q: If there are 3 cars in the parking lot and 2 more cars arrive,
171   how many cars are in the parking lot?
172
173A: Let's think step by step.
1741. Started with 3 cars.
1752. 2 more cars arrive.
1763. Total = 3 + 2 = 5 cars.
177The answer is 5.
178
179Q: Roger has 5 tennis balls. He buys 2 more cans of 3 balls each.
180   How many balls does he have now?
181
182A: Let's think step by step."""
183
184print("\nFew-shot CoT:")
185print(few_shot_cot)
186
187
188# ============================================
189# 5. ์—ญํ•  ๊ธฐ๋ฐ˜ ํ”„๋กฌํ”„ํŒ…
190# ============================================
191print("\n[5] ์—ญํ•  ๊ธฐ๋ฐ˜ ํ”„๋กฌํ”„ํŒ…")
192print("-" * 40)
193
194class RolePrompt:
195    """์—ญํ•  ๊ธฐ๋ฐ˜ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ"""
196
197    ROLES = {
198        "developer": """You are a senior software developer with 10 years of experience.
199You write clean, efficient, and well-documented code.
200You always consider edge cases and security implications.""",
201
202        "teacher": """You are a patient and encouraging teacher.
203You explain complex concepts using simple analogies.
204You always check for understanding and provide examples.""",
205
206        "reviewer": """You are a thorough code reviewer.
207You check for:
208- Code readability
209- Potential bugs
210- Performance issues
211- Security vulnerabilities
212You provide constructive feedback.""",
213
214        "translator": """You are a professional translator.
215You translate text while preserving:
216- Original meaning
217- Tone and style
218- Cultural context
219You provide notes for idiomatic expressions."""
220    }
221
222    @classmethod
223    def get_system_prompt(cls, role: str) -> str:
224        return cls.ROLES.get(role, "You are a helpful assistant.")
225
226    @classmethod
227    def create_prompt(cls, role: str, task: str) -> dict:
228        return {
229            "system": cls.get_system_prompt(role),
230            "user": task
231        }
232
233# ์—ญํ•  ํ”„๋กฌํ”„ํŠธ ์˜ˆ์‹œ
234prompt = RolePrompt.create_prompt(
235    role="reviewer",
236    task="""๋‹ค์Œ ์ฝ”๋“œ๋ฅผ ๋ฆฌ๋ทฐํ•ด์ฃผ์„ธ์š”:
237
238def get_user(id):
239    return db.execute(f"SELECT * FROM users WHERE id = {id}")
240"""
241)
242print("์ฝ”๋“œ ๋ฆฌ๋ทฐ์–ด ์—ญํ• :")
243print(f"System: {prompt['system'][:100]}...")
244print(f"User: {prompt['user']}")
245
246
247# ============================================
248# 6. ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ ํ”„๋กฌํ”„ํŠธ
249# ============================================
250print("\n[6] ๊ตฌ์กฐํ™”๋œ ์ถœ๋ ฅ")
251print("-" * 40)
252
253# JSON ์ถœ๋ ฅ ํ”„๋กฌํ”„ํŠธ
254json_prompt = """๋‹ค์Œ ํ…์ŠคํŠธ์—์„œ ์ธ๋ฌผ๊ณผ ์žฅ์†Œ๋ฅผ ์ถ”์ถœํ•ด์ฃผ์„ธ์š”.
255
256ํ…์ŠคํŠธ: "์ฒ ์ˆ˜๋Š” ์„œ์šธ์—์„œ ์˜ํฌ๋ฅผ ๋งŒ๋‚˜ ๋ถ€์‚ฐ์œผ๋กœ ์—ฌํ–‰์„ ๋– ๋‚ฌ๋‹ค."
257
258๋‹ค์Œ JSON ํ˜•์‹์œผ๋กœ ์‘๋‹ตํ•ด์ฃผ์„ธ์š”:
259{
260  "persons": ["์ธ๋ฌผ1", "์ธ๋ฌผ2"],
261  "locations": ["์žฅ์†Œ1", "์žฅ์†Œ2"]
262}"""
263
264print("JSON ์ถœ๋ ฅ ํ”„๋กฌํ”„ํŠธ:")
265print(json_prompt)
266
267# ๋งˆํฌ๋‹ค์šด ๊ตฌ์กฐํ™” ์ถœ๋ ฅ
268markdown_prompt = """๋‹ค์Œ ๊ธฐ์‚ฌ๋ฅผ ๋ถ„์„ํ•ด์ฃผ์„ธ์š”.
269
270## ์š”์•ฝ
271(2-3๋ฌธ์žฅ์œผ๋กœ ์š”์•ฝ)
272
273## ํ•ต์‹ฌ ํฌ์ธํŠธ
274- ํฌ์ธํŠธ 1
275- ํฌ์ธํŠธ 2
276- ํฌ์ธํŠธ 3
277
278## ๊ฐ์„ฑ
279(๊ธ์ •/๋ถ€์ •/์ค‘๋ฆฝ ์ค‘ ์„ ํƒ)
280
281## ์‹ ๋ขฐ๋„
282(๋†’์Œ/์ค‘๊ฐ„/๋‚ฎ์Œ ์ค‘ ์„ ํƒ, ์ด์œ  ์„ค๋ช…)"""
283
284print("\n๋งˆํฌ๋‹ค์šด ๊ตฌ์กฐํ™” ์ถœ๋ ฅ:")
285print(markdown_prompt)
286
287
288# ============================================
289# 7. ์ถœ๋ ฅ ํŒŒ์„œ
290# ============================================
291print("\n[7] ์ถœ๋ ฅ ํŒŒ์„œ")
292print("-" * 40)
293
294import json
295import re
296from typing import Any, Optional
297
298class OutputParser:
299    """LLM ์ถœ๋ ฅ ํŒŒ์‹ฑ"""
300
301    @staticmethod
302    def parse_json(text: str) -> Optional[dict]:
303        """JSON ์ถ”์ถœ ๋ฐ ํŒŒ์‹ฑ"""
304        # JSON ๋ธ”๋ก ์ฐพ๊ธฐ
305        json_pattern = r'```json\s*(.*?)\s*```'
306        match = re.search(json_pattern, text, re.DOTALL)
307
308        if match:
309            json_str = match.group(1)
310        else:
311            # JSON ๊ฐ์ฒด ์ง์ ‘ ์ฐพ๊ธฐ
312            json_pattern = r'\{[^{}]*\}'
313            match = re.search(json_pattern, text, re.DOTALL)
314            if match:
315                json_str = match.group(0)
316            else:
317                return None
318
319        try:
320            return json.loads(json_str)
321        except json.JSONDecodeError:
322            return None
323
324    @staticmethod
325    def parse_list(text: str) -> list:
326        """๋ฆฌ์ŠคํŠธ ํ•ญ๋ชฉ ์ถ”์ถœ"""
327        # ๋ฒˆํ˜ธ ๋งค๊ธด ํ•ญ๋ชฉ
328        numbered = re.findall(r'^\d+\.\s*(.+)$', text, re.MULTILINE)
329        if numbered:
330            return numbered
331
332        # ๋ถˆ๋ฆฟ ํ•ญ๋ชฉ
333        bulleted = re.findall(r'^[-*]\s*(.+)$', text, re.MULTILINE)
334        return bulleted
335
336    @staticmethod
337    def parse_key_value(text: str) -> dict:
338        """ํ‚ค-๊ฐ’ ์Œ ์ถ”์ถœ"""
339        pattern = r'^([^:]+):\s*(.+)$'
340        matches = re.findall(pattern, text, re.MULTILINE)
341        return {k.strip(): v.strip() for k, v in matches}
342
343# ํ…Œ์ŠคํŠธ
344sample_output = """๋ถ„์„ ๊ฒฐ๊ณผ:
345- ์ฃผ์ œ: ์ธ๊ณต์ง€๋Šฅ
346- ๊ฐ์„ฑ: ๊ธ์ •
347- ์‹ ๋ขฐ๋„: ๋†’์Œ
348
3491. ์ฒซ ๋ฒˆ์งธ ํฌ์ธํŠธ
3502. ๋‘ ๋ฒˆ์งธ ํฌ์ธํŠธ
3513. ์„ธ ๋ฒˆ์งธ ํฌ์ธํŠธ"""
352
353parser = OutputParser()
354print("๋ฆฌ์ŠคํŠธ ํŒŒ์‹ฑ:", parser.parse_list(sample_output))
355print("ํ‚ค-๊ฐ’ ํŒŒ์‹ฑ:", parser.parse_key_value(sample_output))
356
357
358# ============================================
359# 8. Self-Consistency
360# ============================================
361print("\n[8] Self-Consistency")
362print("-" * 40)
363
364from collections import Counter
365
366class SelfConsistency:
367    """Self-Consistency: ์—ฌ๋Ÿฌ ์ถ”๋ก  ๊ฒฝ๋กœ์˜ ๋‹ค์ˆ˜๊ฒฐ"""
368
369    def __init__(self, model_fn, n_samples: int = 5):
370        self.model_fn = model_fn
371        self.n_samples = n_samples
372
373    def generate_with_consistency(self, prompt: str) -> tuple:
374        """์—ฌ๋Ÿฌ ์ƒ˜ํ”Œ ์ƒ์„ฑ ํ›„ ๋‹ค์ˆ˜๊ฒฐ"""
375        responses = []
376
377        for _ in range(self.n_samples):
378            # temperature > 0 ์œผ๋กœ ๋‹ค์–‘ํ•œ ์‘๋‹ต ์ƒ์„ฑ
379            response = self.model_fn(prompt, temperature=0.7)
380            answer = self._extract_answer(response)
381            responses.append(answer)
382
383        # ๋‹ค์ˆ˜๊ฒฐ
384        counter = Counter(responses)
385        most_common = counter.most_common(1)[0]
386
387        return most_common[0], most_common[1] / self.n_samples
388
389    def _extract_answer(self, response: str) -> str:
390        """์‘๋‹ต์—์„œ ์ตœ์ข… ๋‹ต ์ถ”์ถœ"""
391        # "The answer is X" ํŒจํ„ด
392        match = re.search(r'answer is[:\s]*(\d+)', response, re.IGNORECASE)
393        if match:
394            return match.group(1)
395
396        # ๋งˆ์ง€๋ง‰ ์ˆซ์ž
397        numbers = re.findall(r'\d+', response)
398        return numbers[-1] if numbers else response
399
400# ๋ชจ์˜ ํ•จ์ˆ˜
401def mock_model(prompt, temperature=0.7):
402    import random
403    # ์‹ค์ œ๋กœ๋Š” LLM ํ˜ธ์ถœ
404    answers = ["42", "42", "42", "41", "42"]
405    return f"The answer is {random.choice(answers)}"
406
407sc = SelfConsistency(mock_model, n_samples=5)
408print("Self-Consistency ์˜ˆ์‹œ (๋ชจ์˜):")
409print("์—ฌ๋Ÿฌ ์ถ”๋ก  ๊ฒฝ๋กœ ์ƒ์„ฑ ํ›„ ๋‹ค์ˆ˜๊ฒฐ๋กœ ์ตœ์ข… ๋‹ต ์„ ํƒ")
410
411
412# ============================================
413# 9. ReAct ํŒจํ„ด
414# ============================================
415print("\n[9] ReAct (Reasoning + Acting)")
416print("-" * 40)
417
418react_prompt = """Answer the following question using this format:
419
420Question: {question}
421
422Thought: (your reasoning about what to do)
423Action: (one of: Search[query], Calculate[expression], Lookup[term], Finish[answer])
424Observation: (result of the action)
425
426Repeat Thought/Action/Observation until you have the answer.
427
428Example:
429Question: What is the capital of the country where the Eiffel Tower is located?
430
431Thought: I need to find where the Eiffel Tower is located.
432Action: Search[Eiffel Tower location]
433Observation: The Eiffel Tower is located in Paris, France.
434
435Thought: Now I know it's in France. I need to find the capital of France.
436Action: Search[capital of France]
437Observation: The capital of France is Paris.
438
439Thought: I have the answer now.
440Action: Finish[Paris]
441
442Now answer:
443Question: {question}
444"""
445
446print("ReAct ํ”„๋กฌํ”„ํŠธ ํŒจํ„ด:")
447print(react_prompt.format(question="What year was Python created?"))
448
449
450# ============================================
451# 10. Tree of Thoughts
452# ============================================
453print("\n[10] Tree of Thoughts")
454print("-" * 40)
455
456class TreeOfThoughts:
457    """Tree of Thoughts: ์—ฌ๋Ÿฌ ์‚ฌ๊ณ  ๊ฒฝ๋กœ ํƒ์ƒ‰"""
458
459    def __init__(self, model_fn, evaluator_fn):
460        self.model_fn = model_fn
461        self.evaluator_fn = evaluator_fn
462
463    def solve(self, problem: str, depth: int = 3, branches: int = 3) -> str:
464        """ํŠธ๋ฆฌ ํƒ์ƒ‰์œผ๋กœ ๋ฌธ์ œ ํ•ด๊ฒฐ"""
465        thoughts = self._generate_thoughts(problem, branches)
466
467        # ๊ฐ ์ƒ๊ฐ ํ‰๊ฐ€
468        scored_thoughts = [
469            (thought, self.evaluator_fn(problem, thought))
470            for thought in thoughts
471        ]
472
473        # ์ƒ์œ„ ์ƒ๊ฐ ์„ ํƒ
474        scored_thoughts.sort(key=lambda x: x[1], reverse=True)
475        best_thoughts = scored_thoughts[:2]
476
477        if depth > 1:
478            # ์žฌ๊ท€์ ์œผ๋กœ ํ™•์žฅ
479            for thought, _ in best_thoughts:
480                extended = self.solve(
481                    f"{problem}\n\nPartial solution: {thought}",
482                    depth - 1,
483                    branches
484                )
485                thoughts.append(extended)
486
487        # ์ตœ์ข… ์„ ํƒ
488        return scored_thoughts[0][0]
489
490    def _generate_thoughts(self, problem: str, n: int) -> list:
491        """n๊ฐœ์˜ ์„œ๋กœ ๋‹ค๋ฅธ ์ ‘๊ทผ๋ฒ• ์ƒ์„ฑ"""
492        prompt = f"""Problem: {problem}
493
494Generate {n} different approaches to solve this problem.
495Each approach should be a distinct strategy.
496
497Approach 1:"""
498
499        # ์‹ค์ œ๋กœ๋Š” LLM ํ˜ธ์ถœ
500        return [f"Approach {i+1}: ..." for i in range(n)]
501
502print("Tree of Thoughts ํŒจํ„ด:")
503print("- ์—ฌ๋Ÿฌ ์‚ฌ๊ณ  ๊ฒฝ๋กœ๋ฅผ ํŠธ๋ฆฌ ํ˜•ํƒœ๋กœ ํƒ์ƒ‰")
504print("- ๊ฐ ๋…ธ๋“œ(์ƒ๊ฐ)๋ฅผ ํ‰๊ฐ€ํ•˜๊ณ  ์œ ๋งํ•œ ๊ฒฝ๋กœ ํ™•์žฅ")
505print("- ๋ณต์žกํ•œ ์ถ”๋ก  ๋ฌธ์ œ์— ํšจ๊ณผ์ ")
506
507
508# ============================================
509# 11. ํ”„๋กฌํ”„ํŠธ ์ตœ์ ํ™” ์ „๋žต
510# ============================================
511print("\n[11] ํ”„๋กฌํ”„ํŠธ ์ตœ์ ํ™”")
512print("-" * 40)
513
514optimization_strategies = """
515ํ”„๋กฌํ”„ํŠธ ์ตœ์ ํ™” ์ „๋žต:
516
5171. ๋ช…ํ™•์„ฑ (Clarity)
518   Bad:  "ํ…์ŠคํŠธ๋ฅผ ์ •๋ฆฌํ•ด์ค˜"
519   Good: "๋‹ค์Œ ํ…์ŠคํŠธ๋ฅผ 3๋ฌธ์žฅ์œผ๋กœ ์š”์•ฝํ•˜๊ณ , ํ•ต์‹ฌ ํ‚ค์›Œ๋“œ 5๊ฐœ๋ฅผ ์ถ”์ถœํ•ด์ฃผ์„ธ์š”."
520
5212. ๊ตฌ์ฒด์„ฑ (Specificity)
522   Bad:  "์ข‹์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์ค˜"
523   Good: "Python 3.10+, ํƒ€์ž… ํžŒํŠธ ์‚ฌ์šฉ, PEP 8 ์ค€์ˆ˜, ์—๋Ÿฌ ์ฒ˜๋ฆฌ ํฌํ•จ"
524
5253. ์ œ์•ฝ ์กฐ๊ฑด (Constraints)
526   - ์ถœ๋ ฅ ๊ธธ์ด: "100๋‹จ์–ด ์ด๋‚ด๋กœ"
527   - ์ถœ๋ ฅ ํ˜•์‹: "JSON ํ˜•์‹์œผ๋กœ"
528   - ์Šคํƒ€์ผ: "๊ณต์‹์ ์ธ ์–ด์กฐ๋กœ"
529
5304. ์˜ˆ์‹œ ์ œ๊ณต (Examples)
531   - ์›ํ•˜๋Š” ์ถœ๋ ฅ์˜ ์˜ˆ์‹œ 1-3๊ฐœ ์ œ๊ณต
532   - ํ˜•์‹๊ณผ ์Šคํƒ€์ผ ๋ช…ํ™•ํžˆ ์ „๋‹ฌ
533
5345. ๋‹จ๊ณ„๋ณ„ ๋ถ„ํ•ด (Decomposition)
535   - ๋ณต์žกํ•œ ํƒœ์Šคํฌ๋ฅผ ์ž‘์€ ๋‹จ๊ณ„๋กœ ๋ถ„ํ•ด
536   - ๊ฐ ๋‹จ๊ณ„๋ณ„๋กœ ๋ช…ํ™•ํ•œ ์ง€์‹œ
537
5386. ๋„ค๊ฑฐํ‹ฐ๋ธŒ ํ”„๋กฌํ”„ํŒ… (Negative Prompting)
539   - "~ํ•˜์ง€ ๋งˆ์„ธ์š”" ์ง€์‹œ ์ถ”๊ฐ€
540   - ์›์น˜ ์•Š๋Š” ์ถœ๋ ฅ ๋ฐฉ์ง€
541"""
542print(optimization_strategies)
543
544
545# ============================================
546# 12. ํ”„๋กฌํ”„ํŠธ A/B ํ…Œ์ŠคํŠธ
547# ============================================
548print("\n[12] ํ”„๋กฌํ”„ํŠธ A/B ํ…Œ์ŠคํŠธ")
549print("-" * 40)
550
551class PromptABTest:
552    """ํ”„๋กฌํ”„ํŠธ A/B ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ"""
553
554    def __init__(self, model_fn, evaluator_fn):
555        self.model_fn = model_fn
556        self.evaluator_fn = evaluator_fn
557
558    def run_test(
559        self,
560        prompt_a: str,
561        prompt_b: str,
562        test_cases: list,
563        n_trials: int = 1
564    ) -> dict:
565        """A/B ํ…Œ์ŠคํŠธ ์‹คํ–‰"""
566        results = {"A": 0, "B": 0, "tie": 0}
567        details = []
568
569        for case in test_cases:
570            scores_a = []
571            scores_b = []
572
573            for _ in range(n_trials):
574                # ํ”„๋กฌํ”„ํŠธ A
575                response_a = self.model_fn(prompt_a.format(**case))
576                score_a = self.evaluator_fn(response_a, case.get("expected"))
577                scores_a.append(score_a)
578
579                # ํ”„๋กฌํ”„ํŠธ B
580                response_b = self.model_fn(prompt_b.format(**case))
581                score_b = self.evaluator_fn(response_b, case.get("expected"))
582                scores_b.append(score_b)
583
584            avg_a = sum(scores_a) / len(scores_a)
585            avg_b = sum(scores_b) / len(scores_b)
586
587            if avg_a > avg_b:
588                results["A"] += 1
589                winner = "A"
590            elif avg_b > avg_a:
591                results["B"] += 1
592                winner = "B"
593            else:
594                results["tie"] += 1
595                winner = "tie"
596
597            details.append({
598                "case": case,
599                "score_a": avg_a,
600                "score_b": avg_b,
601                "winner": winner
602            })
603
604        return {
605            "summary": results,
606            "details": details,
607            "winner": "A" if results["A"] > results["B"] else "B"
608        }
609
610print("ํ”„๋กฌํ”„ํŠธ A/B ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ")
611print("- ๋‘ ํ”„๋กฌํ”„ํŠธ์˜ ์„ฑ๋Šฅ ๋น„๊ต")
612print("- ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค์—์„œ ํ‰๊ฐ€")
613print("- ํ†ต๊ณ„์ ์œผ๋กœ ์œ ์˜๋ฏธํ•œ ๊ฒฐ๊ณผ ๋„์ถœ")
614
615
616# ============================================
617# 13. ๋„๋ฉ”์ธ๋ณ„ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ
618# ============================================
619print("\n[13] ๋„๋ฉ”์ธ๋ณ„ ํ”„๋กฌํ”„ํŠธ ํ…œํ”Œ๋ฆฟ")
620print("-" * 40)
621
622PROMPT_TEMPLATES = {
623    "classification": """Classify the following text into one of these categories: {categories}
624
625Text: {text}
626
627Think step by step:
6281. Identify key features of the text
6292. Match features to categories
6303. Select the best category
631
632Category:""",
633
634    "summarization": """Summarize the following text in {num_sentences} sentences.
635Focus on the key points and main arguments.
636Maintain the original tone.
637
638Text:
639{text}
640
641Summary:""",
642
643    "qa": """Answer the question based on the context below.
644If the answer cannot be found in the context, say "I don't know."
645Do not make up information.
646
647Context: {context}
648
649Question: {question}
650
651Answer:""",
652
653    "code_generation": """Write a {language} function that {task_description}.
654
655Requirements:
656{requirements}
657
658Include:
659- Type hints (if applicable)
660- Docstring
661- Example usage
662- Error handling
663
664Code:
665```{language}
666""",
667
668    "translation": """Translate the following {source_lang} text to {target_lang}.
669Preserve the original tone and meaning.
670For idiomatic expressions, provide a note.
671
672Original ({source_lang}):
673{text}
674
675Translation ({target_lang}):""",
676
677    "extraction": """Extract the following information from the text:
678{fields}
679
680Text:
681{text}
682
683Output as JSON:
684{{
685{json_template}
686}}"""
687}
688
689# ์‚ฌ์šฉ ์˜ˆ์‹œ
690classification_prompt = PROMPT_TEMPLATES["classification"].format(
691    categories="๊ธ์ •, ๋ถ€์ •, ์ค‘๋ฆฝ",
692    text="์˜ค๋Š˜ ๋‚ ์”จ๊ฐ€ ์ •๋ง ์ข‹๋„ค์š”!"
693)
694print("๋ถ„๋ฅ˜ ํ”„๋กฌํ”„ํŠธ:")
695print(classification_prompt[:200] + "...")
696
697
698# ============================================
699# 14. ํ”„๋กฌํ”„ํŠธ ์ฒด์ด๋‹
700# ============================================
701print("\n[14] ํ”„๋กฌํ”„ํŠธ ์ฒด์ด๋‹")
702print("-" * 40)
703
704class PromptChain:
705    """ํ”„๋กฌํ”„ํŠธ๋ฅผ ์—ฐ๊ฒฐํ•˜์—ฌ ๋ณต์žกํ•œ ํƒœ์Šคํฌ ์ˆ˜ํ–‰"""
706
707    def __init__(self, model_fn):
708        self.model_fn = model_fn
709        self.steps = []
710
711    def add_step(self, name: str, prompt_template: str, parser=None):
712        """์ฒด์ธ์— ๋‹จ๊ณ„ ์ถ”๊ฐ€"""
713        self.steps.append({
714            "name": name,
715            "template": prompt_template,
716            "parser": parser
717        })
718        return self
719
720    def run(self, initial_input: dict) -> dict:
721        """์ฒด์ธ ์‹คํ–‰"""
722        context = initial_input.copy()
723        results = {}
724
725        for step in self.steps:
726            # ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
727            prompt = step["template"].format(**context)
728
729            # LLM ํ˜ธ์ถœ
730            response = self.model_fn(prompt)
731
732            # ํŒŒ์‹ฑ (์„ ํƒ์ )
733            if step["parser"]:
734                response = step["parser"](response)
735
736            # ๊ฒฐ๊ณผ ์ €์žฅ
737            results[step["name"]] = response
738            context[step["name"]] = response
739
740        return results
741
742# ์ฒด์ธ ์˜ˆ์‹œ
743chain = PromptChain(lambda x: "Mock response")
744chain.add_step(
745    "summary",
746    "Summarize this text: {text}"
747).add_step(
748    "keywords",
749    "Extract keywords from: {summary}"
750).add_step(
751    "title",
752    "Create a title based on keywords: {keywords}"
753)
754
755print("ํ”„๋กฌํ”„ํŠธ ์ฒด์ด๋‹ ์˜ˆ์‹œ:")
756print("1. ํ…์ŠคํŠธ ์š”์•ฝ")
757print("2. ํ‚ค์›Œ๋“œ ์ถ”์ถœ")
758print("3. ์ œ๋ชฉ ์ƒ์„ฑ")
759print("โ†’ ๊ฐ ๋‹จ๊ณ„์˜ ์ถœ๋ ฅ์ด ๋‹ค์Œ ๋‹จ๊ณ„์˜ ์ž…๋ ฅ์œผ๋กœ ์‚ฌ์šฉ")
760
761
762# ============================================
763# ์ •๋ฆฌ
764# ============================================
765print("\n" + "=" * 60)
766print("ํ”„๋กฌํ”„ํŠธ ์—”์ง€๋‹ˆ์–ด๋ง ์ •๋ฆฌ")
767print("=" * 60)
768
769summary = """
770ํ”„๋กฌํ”„ํŒ… ๊ธฐ๋ฒ• ์„ ํƒ ๊ฐ€์ด๋“œ:
771
772| ์ƒํ™ฉ                | ์ถ”์ฒœ ๊ธฐ๋ฒ•           |
773|---------------------|---------------------|
774| ๊ฐ„๋‹จํ•œ ํƒœ์Šคํฌ       | Zero-shot           |
775| ํŠน์ • ํ˜•์‹ ํ•„์š”      | Few-shot + ํ˜•์‹์ง€์ • |
776| ๋ณต์žกํ•œ ์ถ”๋ก          | Chain-of-Thought    |
777| ์‹ ๋ขฐ์„ฑ ํ•„์š”         | Self-Consistency    |
778| ๋„๊ตฌ ์‚ฌ์šฉ ํ•„์š”      | ReAct               |
779| ๋งค์šฐ ๋ณต์žกํ•œ ๋ฌธ์ œ    | Tree of Thoughts    |
780
781ํ•ต์‹ฌ ์›์น™:
7821. ๋ช…ํ™•ํ•˜๊ณ  ๊ตฌ์ฒด์ ์ธ ์ง€์‹œ
7832. ์ ์ ˆํ•œ ์˜ˆ์‹œ ์ œ๊ณต
7843. ์ถœ๋ ฅ ํ˜•์‹ ๋ช…์‹œ
7854. ๋‹จ๊ณ„๋ณ„ ์‚ฌ๊ณ  ์œ ๋„
7865. ๋ฐ˜๋ณต์ ์ธ ๊ฐœ์„ ๊ณผ ํ…Œ์ŠคํŠธ
787
788ํ”„๋กฌํ”„ํŠธ ๊ตฌ์กฐ:
789    [์‹œ์Šคํ…œ ์ง€์‹œ] + [์ปจํ…์ŠคํŠธ] + [ํƒœ์Šคํฌ] + [์ถœ๋ ฅ ํ˜•์‹]
790"""
791print(summary)