# Prompt Template

## LangChain: Prompt Templete

```python
import os
from dotenv import load_dotenv  

load_dotenv()
api_key = os.getenv("OPENAI_API_KEY")
```

```python
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
```

### 1. Prompt Templete

1. `PromptTemplate`: 일반적인 프롬프트 템플릿을 생성할때 활용
2. `ChatPromptTemplate`: LLM에 프롬프트를 전달하여 활용에 특화된 프롬프트 템플릿

```python
chat_inst = OpenAI(
    model_name="gpt-3.5-turbo-instruct", 
    openai_api_key=api_key,
    max_tokens=1000
)
```

```python
from langchain.prompts import PromptTemplate, ChatPromptTemplate

# {subject} 문자열로 변환하는 매개변수 지정
string_prompt = PromptTemplate.from_template(
    "tell me a joke about {subject}"
)

# 매개변수 결과를 string_prompt_value에 할당
string_prompt_value = string_prompt.format_prompt(subject="soccer")

# string_prompt는 LLM과 대화 시 필요한 프롬프트
string_prompt_value
```

```
StringPromptValue(text='tell me a joke about soccer')
```

```python
# to_string() 함수는 prompt template으로 생성한 문장 raw_text를 반환

print(string_prompt_value.to_string())
```

```
tell me a joke about soccer
```

```python
chat_prompt = ChatPromptTemplate.from_template(
    "tell me a joke about {subject}"
)

# chat_prompte는 Human Message를 지정
chat_prompt_value = chat_prompt.format_prompt(subject="soccer")
chat_prompt_value
```

```
ChatPromptValue(messages=[HumanMessage(content='tell me a joke about soccer')])
```

```python
chat_prompt_value.to_string()
```

```
'Human: tell me a joke about soccer'
```

### 2. PromptTemplate 기본

```python
from langchain.prompts.prompt import PromptTemplate

template = """
너는 요리사야. 내가 가진 재료들을 갖고 만들 수 있는 요리를 추천하고, 그 요리의 레시피를 제시해줘.
내가 가진 재료는 아래와 같아.

<재료>
{재료}
"""
prompt_template = PromptTemplate(
    input_variables=['재료'],
    template=template
)
```

```python
print(prompt_template.format(재료='양파, 계란, 사과, 빵'))
```

```
너는 요리사야. 내가 가진 재료들을 갖고 만들 수 있는 요리를 추천하고, 그 요리의 레시피를 제시해줘.
내가 가진 재료는 아래와 같아.

<재료>
양파, 계란, 사과, 빵
```

### 3. OpenAI `prompte_tempate` 사용

```python
print(
    prompt_template.format(
        재료 = '양파, 계란, 사과, 빵'
    )
))
```

```
네가 가진 재료로 만들 수 있는 요리 중 하나는 "양파 계란 스크램블 토스트"일 것 같아. 아래는 그 레시피야.

<레시피>
1. 양파를 다진다.
2. 계란을 풀어서 소금과 후추로 간을 한다.
3. 팬에 기름을 두르고 양파를 볶는다.
4. 양파가 익으면 계란을 부어서 섞는다.
5. 계란이 익으면 빵 위에 올려서 토스트한다.
6. 사과를 슬라이스해서 곁들인다.

맛있는 요리가 될 거야. 즐거운 식사 되길 바래!
```

### 4. ChatOpenAI `ChatPrompteTemplete`사용

```python
from langchain.prompts import (
    ChatPromptTemplate,
    PromptTemplate,
    SystemMessagePromptTemplate,
    AIMessagePromptTemplate,
    HumanMessagePromptTemplate,
)

from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage
)
```

```python
chatgpt = ChatOpenAI(temperature=0)

# SystemMessagePromptTemplate로 역할 부여
system_message_prompt = SystemMessagePromptTemplate.from_template(template)

# 사용자가 입력할 매개변수 { } 변수를 선언하고 HumanMessagePromptTemplate로 역할 부여
human_template = "{재료}"
human_message_prompt = HumanMessagePromptTemplate.from_template(human_template)

# ChatPromptTemplate에 system message와 human message 템플릿 삽입
chat_prompt = ChatPromptTemplate.from_messages([
    system_message_prompt, human_message_prompt
])

# ChatPromptTemplate의 구성요소인 system message와 human message를 전달받아 LLM은 대답을 생성
answer = chatgpt(
    chat_prompt.format_prompt(
        재료="양파, 계란, 사과, 빵"
    ).to_messages()
)

print(answer.content)
```

```
제가 추천하는 요리는 "양파 계란 토스트"입니다. 이 요리는 간단하면서도 맛있는 아침 식사나 간식으로 딱 좋아요. 아래는 "양파 계란 토스트"의 레시피입니다.

**양파 계란 토스트 레시피:**

**재료:**
- 빵 4조각
- 계란 2개
- 양파 1개
- 소금
- 후추
- 식용유

**만드는 법:**
1. 양파를 깍둑썰기로 썰어줍니다.
2. 팬에 식용유를 두르고 양파를 볶아 투명해질 때까지 볶아줍니다.
3. 볶은 양파에 소금과 후추를 넣고 볶아줍니다.
4. 다른 팬에 식용유를 두르고 계란을 풀어 스크램블 에그를 만듭니다.
5. 빵을 굽거나 토스터기에 구워 노릇하게 해줍니다.
6. 구운 빵 위에 양파를 골고루 올려줍니다.
7. 그 위에 스크램블 에그를 올립니다.
8. 다른 빵을 얹어 완성합니다.

맛있는 "양파 계란 토스트"가 완성되었습니다. 맛있게 드세요!
```

### 5. `FewshotPrompteTemplete` 사용

* Few-shot은 결과물을 출력할 때 예시 결과물을 제시하여 결과물을 유도하는 방법론
* 원하는 결과물의 형태가 특수하거나, 구조화된 답변을 원할 경우, 결과물의 예시를 수 개 제시하여 결과물을 한정시킴

```python
from langchain.prompts.few_shot import FewShotPromptTemplate
from langchain.prompts.prompt import PromptTemplate

examples = [
  {
    "question": "아이유로 삼행시 만들어줘",
    "answer":
"""
아: 아이유는
이: 이런 강의를 들을 이
유: 유가 없다.
"""
  },

  {
    "question": "김민수로 삼행시 만들어줘",
    "answer":
"""
김: 김치는 맛있다
민: 민달팽이도 좋아하는 김치!
수: 수억을 줘도 김치는 내꺼!
"""
  }
]
```

```python
example_prompt = PromptTemplate(
    input_variables=[
        "question", "answer"], 
    template="Question: {question}\n{answer}"
)

print(example_prompt.format(**examples[0]))
```

```
Question: 아이유로 삼행시 만들어줘

아: 아이유는
이: 이런 강의를 들을 이
유: 유가 없다.
```

```python
prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="Question: {input}",
    input_variables=["input"]
)

print(prompt.format(
    input="호날두로 삼행시 만들어줘")
     )
```

```
Question: 아이유로 삼행시 만들어줘

아: 아이유는
이: 이런 강의를 들을 이
유: 유가 없다.


Question: 김민수로 삼행시 만들어줘

김: 김치는 맛있다
민: 민달팽이도 좋아하는 김치!
수: 수억을 줘도 김치는 내꺼!


Question: 호날두로 삼행시 만들어줘
```

```python
print(chat_inst.predict(
    "호날두로 삼행시 만들어줘")
     )
```

```
짜릿한 날씨와 함께하는
수요일 밤, 리버풀의 전설
호날두는 그 이름만으로도
많은 축구 팬들의 마음을 설레게 해

그는 축구의 천재, 마치 마법사
공을 다루는 그 손짓은 누구나 못 따라해
무대 위에서 그는 늘 빛나는 별
우리 모두의 마음을 사로잡는 그의 춤과 같아

그의 발길이 닿는 곳엔 행복이 넘쳐
리버풀을 위해 그가 뛰는 모습은
마치 우리 모두의 꿈을 이뤄주는 것 같아
호날두, 너의 덕분에 우리는 더 행복해

오늘도 리버풀의 빨간 유니폼을 입고
그는 우리의 자랑, 영원한 전설이야
호날두, 우리의 사랑은 끝나지 않아
우리 함께라면 언제나 이길 수 있어!
```

```python
print(chat_inst(
    prompt.format(
        input="호날두로 삼행시 만들어줘")
))
```

```
호: 호주에서 태어나
날: 날개 달고
두: 두 발로 많은 골을 넣는 호날두!
```

### 6. `example_selector`: 동적 Few-shot Learning

* Few-shot을 동적으로 입력하고 싶은 경우, Example Selector를 활용
* LLM이 여러 작업을 수행하도록 만들되 내가 원하는 범위의 대답을 출력하도록 하려면 사용자의 입력에 동적으로 반응 필요
* 이와 동시에적절한 예시만 포함하도록 함으로써 입력 prompt의 길이를 제한하여오류가 발생하지 않도록 조정

```python
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
from langchain.prompts import FewShotPromptTemplate, PromptTemplate

example_prompt = PromptTemplate(
    input_variables=["input", "output"],
    template="Input: {input}\nOutput: {output}",
)

examples = [
    {"input": "행복", "output": "슬픔"},
    {"input": "흥미", "output": "지루"},
    {"input": "불안", "output": "안정"},
    {"input": "긴 기차", "output": "짧은 기차"},
    {"input": "큰 공", "output": "작은 공"},
]
```

```python
# VectorDB로 ChromaDB 사용
#%pip install chromadb
#%pip install tiktoken
```

```python
example_selector = SemanticSimilarityExampleSelector.from_examples(
    examples,
    OpenAIEmbeddings(),
    Chroma,
    k=1
)

similar_prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    prefix="주어진 입력에 대해 반대의 의미를 가진 단어를 출력해줘",
    suffix="Input: {단어}\nOutput:",
    input_variables=["단어"],
)
```

```python
print(similar_prompt.format(
    단어="무서운"))
```

```
주어진 입력에 대해 반대의 의미를 가진 단어를 출력해줘

Input: 불안
Output: 안정

Input: 무서운
Output:
```

```python
print(similar_prompt.format(
    단어="큰 비행기"))
```

```
주어진 입력에 대해 반대의 의미를 가진 단어를 출력해줘

Input: 긴 기차
Output: 짧은 기차

Input: 큰 비행기
Output:
```

```python
query = "큰 비행기"

print(chat_inst(
    similar_prompt.format(단어=query)
))
```

```
 작은 비행기
```

### 7. `output_parser`: 출력값 조정

* LLM output을 `output_parser` 함수를 활용하여 고정
* List, json 형태 등 다양한 형식의 답변을 고정하여 출력 지정

```python
from langchain.output_parsers import CommaSeparatedListOutputParser
from langchain.prompts import PromptTemplate, ChatPromptTemplate, HumanMessagePromptTemplate
from langchain.llms import OpenAI
from langchain.chat_models import ChatOpenAI
```

```python
output_parser = CommaSeparatedListOutputParser()
```

```python
format_instructions = output_parser.get_format_instructions()
format_instructions
```

```
'Your response should be a list of comma separated values, eg: `foo, bar, baz`'
```

```python
prompt = PromptTemplate(
    template="{주제} 5개를 추천해줘.\n{format_instructions}",
    input_variables=["주제"],
    partial_variables={"format_instructions": format_instructions}
)
```

```python
model = OpenAI(temperature=0, 
               model='gpt-3.5-turbo-instruct')
```

```python
_input = prompt.format(주제="영화")
output = model(_input)

print(output)
```

```
1. Parasite
2. The Shawshank Redemption
3. Inception
4. The Godfather
5. Pulp Fiction
```

```python
output_parser.parse(output)
```

```
['1. Parasite\n2. The Shawshank Redemption\n3. Inception\n4. The Godfather\n5. Pulp Fiction']
```

```python
```
