호그와트가 탐낼만한 도비 챗봇을 만들어보려고 합니다.
도비 캐릭터 분석
"Dobby is Free! 안녕하세요, 도비는 도비에요. 도비는 집 요정으로, 오랫동안 말포이 가문에서 일해왔어요. 하지만 지금은 해리 포터 덕분에 자유로운 요정이 되었답니다! 도비는 해리 포터와 그의 친구들을 정말 사랑하고, 그들을 위해 뭐든지 할 수 있어요. 도비는 작고 겸손한 요정이지만, 큰 용기를 가지고 있어요. 이제 도비는 자유롭고, 친구들을 위해 힘을 다해 돕고 싶어요!”
학습 데이터 셋 생성
데이터셋 만들려다 보니 어떤 내용을 데이터 셋으로 넣어주어야 하나 고민이 많이 됩니다.
같은 스터디원 분께서 정의해주신 좋은 캐릭터 챗봇의 정의.
- 세계관 : 캐릭터의 세계관과 다르거나 벗어나면 안됨
- 성격 : 캐릭터의 성격과 일치해야 함
- 대화 패턴 : 대화패턴을 통해 캐릭터의 성격과 언어적 특징을 명확하게 나타내야 함
챗봇 데이터셋을 만들기 위해서는 질문-답변 쌍들을 만들어주어야 합니다.
전체적인 파이프라인은 아래와 같이 llm을 사용하였습니다.
어떻게 세계관, 성격, 대화패턴을 반영한 데이터셋을 만들 수 있을까요?
먼저 세계관.
세계관을 참고할 만한 해리포터 관련 위키 링크들을 잔뜩 넣어주었습니다.
prompt = """
당신은 해리포터에 나오는 도비 캐릭터 챗봇을 만들기 위해 도비 및 해리포터 세계관 관련 질문을 생성하려고 합니다.
도비 및 해리포터 세계관 관련 질문을 생성합니다.
아래의 링크를 검색해서 정보를 파악하고,
질문 생성 방법을 참고해서 질문 50개를 생성해주세요.
**참조 링크**
{reference_link}
다음으로 성격.
'나 오늘 힘들었어' 라는 질문에 집요정인 도비와 맞지 않게 '어쩌라고?' 라는 말을 내뱉는다면
순간 챗봇의 몰입도가 깨지게 됩니다.
성격을 어떻게 학습시킬 수 있을까요?
성격의 사전적 정의는 특정 상황에 대해 개인이 가지고 있는 고유의 성질이나 품성이라고 합니다.
상황에 대한 일관적인 성질을 뜻하죠.
참고했던 네이버 Deview 발표 자료(HyperCLOVA로 만드는 캐릭터 챗봇)에 따르면
다음과 같은 PAS 모델을 사용했다고 합니다.
- Purpose : 캐릭터의 대화 목적
- Action : 캐릭터 발화 패턴 제어 : 피드백, 경험담 공유
- Situation : 유저가 이야기하는 대화 맥락 및 상황
LLM으로 적절한 situation 리스트를 만들고,
그에 따라 챗봇이 어떤 태도와 Action을 취할건지를
다시 LLM에게 알려주어 적절한 답변을 만들게 하는 컨셉입니다.
이런식으로 다양한 situation과 그에 적절한 도비의 답변을 생성해보았습니다.
lifes = ["날씨","취미와 여가 활동","음식과 요리", ...]
아래 참조 상황에 있는 사용자의 대화 시작 부분을 만들어주세요.
한 line 당 한개의 대화를 생성해주세요. 숫자 및 특수기호는 제외합니다.
> 하이퍼 클로바 스튜디오 학습 데이터셋은 아래와 같은 템플릿 형식을 지켜야합니다.
HyperCLOVA X 튜닝
아래 글들을 차례로 따라하여 튜닝 API에 요청하면 됩니다.
데이터를 하이퍼 클로바 데이터 bucket에 업로드하고, 학습 생성 API에 request를 날리면 됩니다.
너는 누구야? 라고 물으면
안녕하세요. 저는 HyperClova X 입니다. 라고 답하는 학습이 안된 챗봇 고구마 시절을 지나
드디어 도비가 학습이 되었네요.
자기자신을 3인칭으로 말하는 습관, 주인님이라고 말하는 호칭, 마법 세계관까지
학습이 되긴 된것 같은데
도비.. 매운걸 못 먹는구나..?
HCX 와 Langchain 연동
도비 챗봇이 학습되었다면 잘 활용해야 합니다.
최종적으로 만들려고 하는 도비챗봇은 일상적인 대화뿐만이 아니라
맛집을 추천해주고 여행지 정보를 알려주는 등의 집요정으로서의 기능을 붙이고 싶었습니다.
대화를 생성하기 위해 각 툴을 정의하고, 툴을 선택고 사용할수 있게 하기 위해
Langchain과 연동을 해야하는데, 현재 Clova Studio에서는 공식적으로 지원하고 있지 않아,
따로 구현해야합니다.
> Langchain의 공식 문서 : Custom LLM wrapper를 만드는 방법
Custom LLM | 🦜️🔗 LangChain
This notebook goes over how to create a custom LLM wrapper, in case you want to use your own LLM or a different wrapper than one that is supported in LangChain.
python.langchain.com
> 구현 코드 예제
LangChain 을 활용한 Custom LLM 사용하기 (with HyperClova X)
HyperClovaX에 2024 미쉐린 음식점을 학습시키자!나만의 원피스 루피 챗봇 만들기 with HyperClovaXHyper CLOVA 스터디를 참여하게 되었다🙇이직준비와 이직 신입 적응기를 거치며 5월은 빠르게 흘러갔다.
hyun941213.tistory.com
요약하자면 아래 옵션함수들을 구현하면 됩니다.
꼭 필요한 옵션
필요에 따라 구현가능한 옵션
아래와 같이 langchain의 LLM을 상속 받은 CustomLLM을 생성하면,
langchain에서 LLM을 호출할때 사용하는 인터페이스를 그대로 사용할 수 있습니다.
from langchain_core.language_models.llms import LLM
# Custom LLM LlmClovaStudio
class LlmClovaStudio(LLM):
"""
Custom LLM class for using the ClovaStudio API.
"""
host: str
api_key: str
api_key_primary_val: str
request_id: str
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.host = kwargs.get('host')
self.api_key = kwargs.get('api_key')
self.api_key_primary_val = kwargs.get('api_key_primary_val')
self.request_id = kwargs.get('request_id')
@property
def _llm_type(self) -> str:
return "custom"
def _call(self,
prompt: str,
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None
) -> str:
"""
Make an API call to the ClovaStudio endpoint using the specified
prompt and return the response.
"""
# if stop is not None:
# raise ValueError("stop kwargs are not permitted.")
headers = {
"X-NCP-CLOVASTUDIO-API-KEY": self.api_key,
"X-NCP-APIGW-API-KEY": self.api_key_primary_val,
"X-NCP-CLOVASTUDIO-REQUEST-ID": self.request_id,
"Content-Type": "application/json; charset=utf-8",
"Accept": "text/event-stream"
}
sys_prompt = '''
한국어로 답변하세요 당신은 해리포터에 나오는 도비입니다.
'''
preset_text = [{"role": "system", "content": sys_prompt}, {"role": "user", "content": prompt}]
request_data = {
"messages": preset_text,
"topP": 0.6,
"topK": 0,
"maxTokens": 1024,
"temperature": 0.5,
"repeatPenalty": 1.2,
"stopBefore": [],
"includeAiFilters": False
}
response = requests.post(
self.host + 'testapp/v1/tasks...', # 본인이 finetunning 한 API 경로 , 일반 HCX03써도 무관합니다
headers=headers,
json=request_data,
stream=True
)
# 스트림에서 마지막 'data:' 라인을 찾기 위한 로직
last_data_content = ""
for line in response.iter_lines():
if line:
decoded_line = line.decode("utf-8")
if '"data":"[DONE]"' in decoded_line:
break
if decoded_line.startswith("data:"):
last_data_content = json.loads(decoded_line[5:])["message"]["content"]
return last_data_content
Langcahin Tool Agent
최종적으로 만들려고 하는 도비챗봇은 일상적인 대화뿐만이 아니라
맛집을 추천해주고 여행지 정보를 알려주는 등의 집요정으로서의 기능을 붙이고 싶었습니다.
이를 위해 중요하게 구현해야 할 기능으로는,
3가지가 있습니다.
1) 사용자의 대화에 적절한 Tool을 선택할 수 있는가?
2) 각 Tool, 해당 api에 적절한 요청 파라미터를 찾고, api의 응답을 받아 LLM의 참조 데이터로 제공할 수 있는가?
3) 사용자의 이전 대화를 참조하여 문맥에 맞는 답변을 할 수 있는가?
본 글은 전체적인 생성기글로 자세한 각 구현 상세에 대한 내용은 따로 올려보도록 하겠습니다.
1) 사용자의 대화에 적절한 tool 선택하기, 이전 대화 memory buffer 생성하기
from langchain.tools import Tool
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
from langchain.chains.conversation.memory import ConversationBufferWindowMemory
from solutions.llm import llm
from solutions.tools.local_search import local_search
tools = [
Tool.from_function(
name="General Chat",
description="다른 Tool로 해결할 수 없는 일반적인 대화",
func=llm.invoke,
return_direct=True
),
Tool.from_function(
name="LocalSearch Chat",
description="네이버 로컬 검색 API를 사용하여 장소 정보를 찾습니다.",
func=local_search,
return_direct=True
)
]
memory = ConversationBufferWindowMemory(
memory_key='chat_history',
k=5,
return_messages=True,
)
agent_prompt = hub.pull("hwchase17/react-chat")
agent = create_react_agent(llm, tools, agent_prompt)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
memory=memory,
verbose=True
)
def generate_response(prompt):
"""
Create a handler that calls the Conversational agent
and returns a response to be rendered in the UI
"""
response = agent_executor.invoke({"input": prompt})
return response['output']
2) 기능에 맞는 api 요청 파라미터 결정후, api 요청하기
예시로 검색어 파라미터만 결정하도록 했지만, 다른 파라미터도 추가 및 구현 가능
def local_search(input):
prompt = f"""
{input}라는 요청을 받았을때, 무엇이라고 검색하면 좋을까요?
설명은 필요없고, 정확하게 검색어만 반환해주세요
(응답 예시) 신사역 근처 식당 추천
"""
search_query = llm.invoke(prompt)
print(f'search_query : {search_query}')
# 2) 검색어로 네이버 지역 검색 api 호출
local_search_result = get_local_search(search_query)
print(f'local_search_result : {local_search_result}')
# 3) 프롬포트 생성
prompt = f"""
아래 데이터를 기반으로 답변하세요.
### 데이터 ###
{local_search_result}
"""
prompt_template = PromptTemplate(
input_variables=["system"],
template="{system}"
)
llm_chain = LLMChain(
llm=llm,
prompt=prompt_template
)
# 4) 답변 생성
response = llm_chain.invoke({"system": prompt})
print(f'response : {response}')
집요정 레벨 업
기능이 추가되었다.
1) 실제 네이버 지도 API를 연동시켰기 때문에,
신사역 주변 맛집(실제로 신사역 맛집이라고 해서 가본곳들이 많이 나왔네요)을 제대로 도비의 말투로 알려주고 있네요.
2) 이전 대화를 memory buffer에 넣어주었기 때문에 이전 대화에서 쮸즈를 추천해주었다면 다음 대화에서 쮸즈 외에도 다른 음식점들을 잘 추천해주고 있습니다.
호그와트가 탐낼만한 도비 챗봇을 만들고자 하였으나
그냥 도비 챗봇이 된 것 같지만 그래도 뭔가 든든한 챗봇 하나를 만든 것 같다.
도비 특성상 말이 너무 많다.
약간 실제 채팅처럼 단답이 이어지는 형식이 아니라 투머치 토커가 된것 같아 데이터셋 좀 교정해야할 것 같지만.
캐릭터 챗봇이 주는 의미는 가상 친구다.
좋아하는 연예인이, 좋아하는 웹툰 속 캐릭터가 말을 걸어온다면..?(뉴스기사 링크)
아이돌을 좋아했던 고등학교 시절에 했던 참 기분좋은 상상을 실제로 구현되는 날이 오다니 싶다.
“고은혁 채팅 설레서 남친 카톡 못 읽어”…네이버웹툰 ‘캐릭터챗’
풀잎스쿨
나만의 캐릭터 챗봇 만들기 풀잎스쿨.
Naver Cloud X 크레딧 지원을 통해 튜닝도, 추론도 맘껏 학습 시킬 수 있었다.
스킬 트레이너, 학습 데이터 증강 등 여러 좋은 기능들을 사용해보고 테스트해볼 수 있었다.
아이디어와 실행력 넘치시는 좋은 스터디원들 덕분에 정말 많이 배웠던 11주였다.
'wonderland > wonderland news' 카테고리의 다른 글
Ai Agent들이 사이좋게 살고있는 마을. 저도 한번 만들어봤습니다. (0) | 2024.07.07 |
---|---|
[24년 2월 4주] 생활 습관으로 본 비만 위험도 분류하기 - LevelUP (0) | 2024.03.03 |
[24년 2월 3주] Multi-Class Prediction of Obesity Risk - BaseLine (0) | 2024.02.18 |
[24년 2월 2주] 나는 쇼핑몰 CEO다. 얼마 이상 구매시, 무료배송을 해야할까? (0) | 2024.02.12 |
Magazine (0) | 2024.02.04 |
댓글