랭체인

0201 강의 Part 2 - Ch1 Prompt [FewShotTemplate, suffix, example selector, FewShotChatMessagePromptTemplate, LangChainHub]

유방울 2025. 1. 27. 00:23

FewShotPromptTemplate

one shot 하나의 답변예시

few shot 두가지 이상 답변예시

zero shot 예시 제시 x -> 성능 안 좋음

gpt는 예시 주면 답변 더 잘 함

 

unpacking operator 

딕셔너리를 함수의 인자로 전달할 때 사용

딕셔너리의 키-값 쌍을 함수의 매개변수로 자동으로 전달하는 역할

# 프롬프트 생성
example_prompt = PromptTemplate.from_template(

    "Question:\n{question}\nAnswer:\n{answer}"

)

# example의 0번째로 format
print(example_prompt.format(**examples[0]))

Question:
스티브 잡스와 아인슈타인 중 누가 더 오래 살았나요?
Answer:
이 질문에 추가 질문이 필요한가요: 예.

추가 질문: 스티브 잡스는 몇 살에 사망했나요?

중간 답변: 스티브 잡스는 56세에 사망했습니다.

추가 질문: 아인슈타인은 몇 살에 사망했나요?

중간 답변: 아인슈타인은 76세에 사망했습니다.

최종 답변은: 아인슈타인

suffix

 

:생성할 프롬프트의 마지막에 추가되는 텍스트로, 주로 모델이 답변을 시작해야 할 위치나 방식을 설정하는 데 사용

: 템플릿의 마지막 부분을 구성

:새로운 질문이 들어올 때 그 질문을 어떻게 제시하고 그에 대한 답변이 어떻게 나와야 하는지에 대한 포맷을 결정

 

이 질문에 추가 질문이 필요한가요: 예. 추가 질문: 스티브 잡스는 몇 살에 사망했나요? 중간 답변: 스티브 잡스는 56세에 사망했습니다. 추가 질문: 아인슈타인은 몇 살에 사망했나요? 중간 답변: 아인슈타인은 76세에 사망했습니다. 최종 답변은: 아인슈타인

이렇게 example이 있으면 앞에 추가 질문, 중간 답변이 처음에 들어가고 

마지막에 포맷을 최종 답변으로 설정하는 역할

 

아래는 example로 format 해서 모든 예시가 전부 출력된 것임

# suffix 유저가 질문이 들어오면 이전까지의 흐름으로 답변하면 되는 것을 학습하고
# 새로운 Q에 답변해줌
prompt = FewShotPromptTemplate(

    examples=examples,

    example_prompt=example_prompt,

    suffix="Question:\n{question}\nAnswer:",

    input_variables=["question"],

)


question = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"

final_prompt = prompt.format(question=question)

print(final_prompt)

Question:
스티브 잡스와 아인슈타인 중 누가 더 오래 살았나요?
Answer:
이 질문에 추가 질문이 필요한가요: 예.

추가 질문: 스티브 잡스는 몇 살에 사망했나요?

중간 답변: 스티브 잡스는 56세에 사망했습니다.

추가 질문: 아인슈타인은 몇 살에 사망했나요?

중간 답변: 아인슈타인은 76세에 사망했습니다.

최종 답변은: 아인슈타인



Question:
네이버의 창립자는 언제 태어났나요?
Answer:
이 질문에 추가 질문이 필요한가요: 예.

추가 질문: 네이버의 창립자는 누구인가요?

중간 답변: 네이버는 이해진에 의해 창립되었습니다.

추가 질문: 이해진은 언제 태어났나요?

중간 답변: 이해진은 1967년 6월 22일에 태어났습니다.

최종 답변은: 1967년 6월 22일



Question:
율곡 이이의 어머니가 태어난 해의 통치하던 왕은 누구인가요?
Answer:
이 질문에 추가 질문이 필요한가요: 예.

추가 질문: 율곡 이이의 어머니는 누구인가요?

중간 답변: 율곡 이이의 어머니는 신사임당입니다.

추가 질문: 신사임당은 언제 태어났나요?

중간 답변: 신사임당은 1504년에 태어났습니다.

추가 질문: 1504년에 조선을 통치한 왕은 누구인가요?

중간 답변: 1504년에 조선을 통치한 왕은 연산군입니다.

최종 답변은: 연산군



Question:
올드보이와 기생충의 감독이 같은 나라 출신인가요?
Answer:
이 질문에 추가 질문이 필요한가요: 예.

추가 질문: 올드보이의 감독은 누구인가요?

중간 답변: 올드보이의 감독은 박찬욱입니다.

추가 질문: 박찬욱은 어느 나라 출신인가요?

중간 답변: 박찬욱은 대한민국 출신입니다.

추가 질문: 기생충의 감독은 누구인가요?

중간 답변: 기생충의 감독은 봉준호입니다.

추가 질문: 봉준호는 어느 나라 출신인가요?

중간 답변: 봉준호는 대한민국 출신입니다.

최종 답변은: 예



Question:
Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?
Answer:

Example에 만들어준 거 넣기

prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="Question:\n{question}\nAnswer:",
    input_variables=["question"],
)

# chain 생성
chain = prompt | llm | StrOutputParser()

# 결과 출력
answer = chain.stream(
    {"question": "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"}
)
stream_response(answer)

prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    suffix="Question:\n{question}\nAnswer:",
    input_variables=["question"],
)

# chain 생성
chain = prompt | llm | StrOutputParser()

# 결과 출력
answer = chain.stream(
    {"question": "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"}
)
stream_response(answer)

Example Selector

OpenAIEmbeddings()

prompt하나씩 쓸 때마다 돈임

길어질수록 답변의 길이가 짧아질 수 있어서 효율적으로 운영해야 함

입력이 전부 프롬프트로 들어가서 1070토큰을 씀
입력으로 넣은 거 다 돈임
쓰지 않아도 될 비용을 많이 지출함

그래서 example selector할 수 있음

즉 질문과의 유사도를 계산해서 높은 유사도인 부분만 셀랙해서 넣어줌
from langchain_core.example_selectors import (
    MaxMarginalRelevanceExampleSelector,
    SemanticSimilarityExampleSelector , # 의미 유사도 검색
)
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

# Vector DB 생성 (저장소 이름, 임베딩 클래스)

chroma = Chroma("example_selector", OpenAIEmbeddings())

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 여기에는 선택 가능한 예시 목록이 있습니다.
    examples , 
    # 여기에는 의미적 유사성을 측정하는 데 사용되는 임베딩을 생성하는 임베딩 클래스가 있습니다.
    OpenAIEmbeddings() , # 문장 대 문장의 유사성을 계산하는 것임
    # 여기에는 임베딩을 저장하고 유사성 검색을 수행하는 데 사용되는 VectorStore 클래스가 있습니다.
    Chroma,
    # 생성할 예시의 수입니다.
    k=1,
)

question = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"

# 입력과 가장 유사한 예시를 선택합니다.
selected_examples = example_selector.select_examples({"question": question})

print(f"입력에 가장 유사한 예시:\n{question}\n")
for example in selected_examples:
    print(f'question:\n{example["question"]}')
    print(f'answer:\n{example["answer"]}')
    
from langchain_core.example_selectors import (
    MaxMarginalRelevanceExampleSelector,
    SemanticSimilarityExampleSelector , # 의미 유사도 검색
)
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

# Vector DB 생성 (저장소 이름, 임베딩 클래스)

chroma = Chroma("example_selector", OpenAIEmbeddings())

example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 여기에는 선택 가능한 예시 목록이 있습니다.
    examples , 
    # 여기에는 의미적 유사성을 측정하는 데 사용되는 임베딩을 생성하는 임베딩 클래스가 있습니다.
    OpenAIEmbeddings() , # 문장 대 문장의 유사성을 계산하는 것임
    # 여기에는 임베딩을 저장하고 유사성 검색을 수행하는 데 사용되는 VectorStore 클래스가 있습니다.
    Chroma,
    # 생성할 예시의 수입니다.
    k=1,
)

question = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"

# 입력과 가장 유사한 예시를 선택합니다.
selected_examples = example_selector.select_examples({"question": question})

print(f"입력에 가장 유사한 예시:\n{question}\n")
for example in selected_examples:
    print(f'question:\n{example["question"]}')
    print(f'answer:\n{example["answer"]}')

 

prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    suffix="Question:\n{question}\nAnswer:",
    input_variables=["question"],
)

question = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"
example_selector_prompt = prompt.format(question=question)
print(example_selector_prompt)

prompt = FewShotPromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
    suffix="Question:\n{question}\nAnswer:",
    input_variables=["question"],
)

question = "Google이 창립된 연도에 Bill Gates의 나이는 몇 살인가요?"
example_selector_prompt = prompt.format(question=question)
print(example_selector_prompt)

 


FewShotChatMessagePromptTemplate

# 회의록 작성 전문가, 문장 요약 전문가, 문장 교정 전문가 example이 있음
from langchain_core.example_selectors import (
    SemanticSimilarityExampleSelector, # 유사도계산하는 알고리즘
)

 

- 유사도로만 예시를 가지고 오면 문제가 있음

- 예시로 다양한 내용을 넣을 수 있음 질문과 유사도가 높지만 다양한 것을 가져오고 싶음

- 그래서 Max Margianl Relevance (MMR) 알고리즘 (테디노트도 많이 씀)
- 취향과 메뉴의 다양성을 고려함 Relavnce, diversity를 봄
- 가장 관령성 높은 항목 선택 후 차별화된 항목을 찾아서 선택함
- lambda 계수가 클수록 관련성, 작을수로 차별성 고려함

 

주의점 : key값 일치

#chat이 붙으면 대화형을 쓰겠다는 의미임
# prompt와 example의 키 값이 동일해야 함
from langchain_core.prompts import ChatPromptTemplate, FewShotChatMessagePromptTemplate
from langchain_core.example_selectors import (
    SemanticSimilarityExampleSelector, # 유사도계산하는 알고리즘
)
from langchain_openai import OpenAIEmbeddings
from langchain_chroma import Chroma

chroma = Chroma("fewshot_chat", OpenAIEmbeddings())

# 항상 튜플 형태
# input 3개
example_prompt = ChatPromptTemplate.from_messages(
    [
        ("human", "{instruction}:\n{input}"),
        ("ai", "{answer}"),
    ]
)

# import하고 example 코드시 method 그대로 복붙하기
example_selector = SemanticSimilarityExampleSelector.from_examples(
    # 여기에는 선택 가능한 예시 목록이 있습니다.
    examples,
    # 여기에는 의미적 유사성을 측정하는 데 사용되는 임베딩을 생성하는 임베딩 클래스가 있습니다.
    OpenAIEmbeddings(),
    # 여기에는 임베딩을 저장하고 유사성 검색을 수행하는 데 사용되는 VectorStore 클래스가 있습니다.
    chroma,
    # 이것은 생성할 예시의 수입니다.
    k=1,
)

few_shot_prompt = FewShotChatMessagePromptTemplate(
    example_selector=example_selector,
    example_prompt=example_prompt,
)

유사도 계산해서 일치하는 example 선택

# 여기서 instruction에서 회의록을 작성과 example의 당신은 회의록 작성 전문가
# 이 두가지의 유사도가 높아서 example이 select됨
# 현실에서는 이렇지 않은 경우가 다반사
question = {
    "instruction": "회의록을 작성해 주세요",
    "input": "2023년 12월 26일, ABC 기술 회사의 제품 개발 팀은 새로운 모바일 애플리케이션 프로젝트에 대한 주간 진행 상황 회의를 가졌다. 이 회의에는 프로젝트 매니저인 최현수, 주요 개발자인 황지연, UI/UX 디자이너인 김태영이 참석했다. 회의의 주요 목적은 프로젝트의 현재 진행 상황을 검토하고, 다가오는 마일스톤에 대한 계획을 수립하는 것이었다. 각 팀원은 자신의 작업 영역에 대한 업데이트를 제공했고, 팀은 다음 주까지의 목표를 설정했다.",
}

example_selector.select_examples(question)

[{'answer': '\n회의록: XYZ 회사 마케팅 전략 회의\n일시: 2023년 12월 25일\n장소: XYZ 회사 회의실\n참석자: 김수진 (마케팅 팀장), 박지민 (디지털 마케팅 담당자), 이준호 (소셜 미디어 관리자)\n\n1. 개회\n   - 회의는 김수진 팀장의 개회사로 시작됨.\n   - 회의의 목적은 2024년 상반기 마케팅 전략 수립 및 새로운 소셜 미디어 캠페인 아이디어 논의.\n\n2. 시장 동향 개요 (김수진)\n   - 김수진 팀장은 최근 시장 동향에 대한 분석을 제시.\n   - 소비자 행동 변화와 경쟁사 전략에 대한 통찰 공유.\n\n3. 디지털 마케팅 전략 (박지민)\n   - 박지민은 디지털 마케팅 전략에 대해 발표.\n   - 온라인 광고와 SEO 최적화 방안에 중점을 둠.\n\n4. 소셜 미디어 캠페인 (이준호)\n   - 이준호는 새로운 소셜 미디어 캠페인에 대한 아이디어를 제안.\n   - 인플루언서 마케팅과 콘텐츠 전략에 대한 계획을 설명함.\n\n5. 종합 논의\n   - 팀원들 간의 아이디어 공유 및 토론.\n   - 각 전략에 대한 예산 및 자원 배분에 대해 논의.\n\n6. 마무리\n   - 다음 회의 날짜 및 시간 확정.\n   - 회의록 정리 및 배포는 박지민 담당.\n',
  'input': '2023년 12월 25일, XYZ 회사의 마케팅 전략 회의가 오후 3시에 시작되었다. 회의에는 마케팅 팀장인 김수진, 디지털 마케팅 담당자인 박지민, 소셜 미디어 관리자인 이준호가 참석했다. 회의의 주요 목적은 2024년 상반기 마케팅 전략을 수립하고, 새로운 소셜 미디어 캠페인에 대한 아이디어를 논의하는 것이었다. 팀장인 김수진은 최근 시장 동향에 대한 간략한 개요를 제공했으며, 이어서 각 팀원이 자신의 분야에서의 전략적 아이디어를 발표했다.',
  'instruction': '당신은 회의록 작성 전문가 입니다. 주어진 정보를 바탕으로 회의록을 작성해 주세요'}]

 

final_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "You are a helpful assistant.",
        ),
        few_shot_prompt,
        ("human", "{instruction}\n{input}"),
    ]
)

# chain 생성
chain = final_prompt | llm

# 실행 및 결과 출력
answer = chain.stream(question)
stream_response(answer)

 


LangChainHub 

prmopt 잘 작성해야 llm 답변을 잘 받을 수 있음

그 hub에 이미 그런 prompt가 있고 카테고리별로 나눠져 있고 등록됨

top view, top download 필터링 많이 씀

-> 질좋은 prompt hub가 많음

 

꿀팁!

rlm이란 분이 rag-prompt 공식 아이디임

rlm이 작성한 prompt는 공식으로 등록되어서 ->  다운로드수가 높은 것임

이분 말고 그 다음 순위가 좋을 수도 있음

 

사용법

1. 복사해서 prompttemplate에 붙여넣으면 됨

2. id만 기억하면 쉽게 가지고올 수 있음

from langchain import hub

# 가장 최신 버전의 프롬프트를 가져옵니다.
prompt = hub.pull("rlm/rag-prompt")

버전 선택가능 : hash 

commit에 버전이 여러개가 있고 개선될 수 있음

# 특정 버전의 프롬프트를 가져오려면 버전 해시를 지정하세요
prompt = hub.pull("rlm/rag-prompt:50442af1")

 

prompt hub에 자신의 프롬프트 등록 가능

근데 나도 프롬프트 등록할 수 있는거면

필터링을 해서 사용하는 게 좋을 듯