Gemma 2를 사용한 텍스트 기반 어드벤처 게임 개발

8월 09, 2024
Ju-yeong Ji Gemma DevRel

Gemma는 Gemini 모델을 만드는 데 사용되는 것과 동일한 연구와 기술로 개발된 개방형 모델 제품군입니다. 명령에 맞게 조정된 모델은 방대한 명령 데이터 세트에서 미세 조정되므로, 질문에 답변하기와 텍스트 요약 등 명령을 준수해야 하는 작업에 특히 적합합니다.

이 블로그 게시물에서는 명령에 맞게 조정된 Gemma 2 모델을 사용하여 텍스트 기반 어드벤처 게임을 만드는 과정을 안내합니다. 개발자 스스로 직접 설정할 수도 있겠지만 저희가 기발한 탐색 여정을 골라보았습니다. Storyteller라는 친절하고 유용한 AI 동반자의 안내를 받게 됩니다. Storyteller가 매혹적인 풍경을 묘사하고, 호기심 많은 캐릭터를 소개하며, 개발자 여러분의 선택에 따라 이야기를 엮어나갈 것입니다! AI와의 상호작용에 따라 게임의 결과가 결정되는 스릴 넘치는 모험에 나설 준비를 하세요!


데모를 살펴보겠습니다.

Link to Youtube Video (visible only when JS is disabled)

이 매력 넘치는 게임에서 플레이어는 어느 외딴섬에서 잠을 자다 깨어납니다. 유일한 안내자이자 동반자인 Storyteller AI는 플레이어가 마주치는 온갖 역경을 헤쳐 나가면서 섬에서 탈출할 방법을 찾을 수 있도록 도와줍니다.


필요한 사항

하드웨어

  • Gemma를 로컬에서 실행할 수 있는 로컬 환경

  • [선택 사항] Vertex AI에 Gemma 배포

Software

kaggle.com에서 Gemma에 액세스하고 Kaggle API 키를 생성하려면 'Gemma 설정'을 참조하세요.

로컬 Gemma 실행에 적합한 강력한 시스템이 없는 경우, Vertex AI를 다운스트림 애플리케이션으로 활용하여 Gemma 모델을 배포하는 것이 좋습니다. Vertex AI는 사내에 MLOps 전문 기술을 갖추고 있지 않더라도 머신러닝 프로젝트를 신속하게 개발하고 확장할 수 있는 관리형 플랫폼을 제공합니다.


[선택 사항] Vertex AI에 Gemma 배포

먼저 Model Garden에서 Gemma 2 모델 카드를 찾습니다. 'Deploy' 버튼을 클릭하면 모델 변형과 배포 위치를 선택할 수 있습니다.

Deploy Gemma in Vertex AI

배포가 완료되면 여기에서 엔드포인트를 찾을 수 있습니다.

활성 엔드포인트에서 예측을 획득하기 위한 스크립트를 얻으려면 'Sample Request'로 이동하여 'Python'을 선택하세요.

sample request in Gemma

챗봇 개발

다음은 로컬 Gemma 챗봇 래퍼 클래스의 예입니다. 'Gemma를 사용한 챗봇 개발'에서 더 자세히 알아볼 수 있습니다.

데모에서는 Gemma 2 2B 모델을 사용했습니다. 하지만 하드웨어 능력이 충분하다면 9B 또는 27B와 같은 Gemma 모델을 선택하여 보다 강력한 결과를 얻을 수 있습니다.

# Gemma local
 
import keras
import keras_nlp
 
model_name = "gemma2_instruct_2b_en"
 
class GemmaBot():
  __START_TURN_USER__ = "<start_of_turn>user\n"
  __START_TURN_MODEL__ = "<start_of_turn>model\n"
  __END_TURN__ = "<end_of_turn>\n"
 
  def __init__(self, system=""):
    self.model = keras_nlp.models.GemmaCausalLM.from_preset(model_name)
    self.system = system
    self.history = []
 
  def add_to_history_as_user(self, message):
      self.history.append(self.__START_TURN_USER__ + message + self.__END_TURN__)
 
  def add_to_history_as_model(self, message):
      self.history.append(self.__START_TURN_MODEL__ + message)
 
  def get_history(self):
      return "".join([*self.history])
 
  def get_full_prompt(self):
    prompt = self.get_history() + self.__START_TURN_MODEL__
    if len(self.system)>0:
      prompt = self.system + "\n" + prompt
    return prompt
 
  def ask(self, message):
    self.add_to_history_as_user(message)
    prompt = self.get_full_prompt()
    response = self.model.generate(prompt, max_length=2048)
    result = response.replace(prompt, "")  # 새 응답
    self.add_to_history_as_model(result)
    반환 결과만 추출

Vertex AI에 Gemma를 배포한 경우 Github의 샘플 코드를 복사하여 봇 파일에 붙여 넣고 아래 코드를 대신 사용하세요. Google Cloud에서 실행 중이므로 'self.model' 변수를 초기화할 필요가 없습니다.

def __init__(self, system=""):
    self.system = system
    self.history = []
 
:
<-- skip -->
:
 
  def ask(self, message):
    self.add_to_history_as_user(message)
    prompt = self.get_full_prompt()
    response = predict_custom_trained_model_sample(
      project="<YOUR_PROJECT_ID>",
      endpoint_id="<YOUR_ENDPONT_ID>",
      location="<YOUR_LOCATION>",
      api_endpoint="<YOUR_LOCATION>-aiplatform.googleapis.com",
      instances={ "inputs": prompt, "max_tokens": 2048 }
      )
    result = response.replace(prompt, "")  # 새 응답
    self.add_to_history_as_model(result)
    반환 결과만 추출

봇을 테스트하려면 다음의 간단한 코드를 사용하세요. (위의 코드를 'gemma.py'로 저장했다고 가정)

from gemma import GemmaBot
gemma_bot = GemmaBot("당신은 이 외딴섬의 안내자 Storyteller입니다.")
text = gemma_bot.ask("당신은 누구죠?")
print(text)

다음은 봇의 출력 예입니다.

Output 1 - build a text based adventure

게임 루프 만들기

게임 루프를 잘 모르는 분들을 위해 간단히 소개해 드리자면 게임 루프란 게임의 기능과 상호작용성을 주도하는 핵심 프로세스입니다. 게임 플레이 중에 내내 반복되는 연속적인 사이클로, 게임 상태가 업데이트되고 입력 정보가 처리되며 화면에 비주얼이 렌더링되도록 해줍니다.

저희가 보여드리는 간단한 텍스트 기반 어드벤처 게임에서는 다음과 같은 기본 게임 루프를 구현했습니다.

import StageIntro
current_stage = StageIntro.StageIntro()
 
def game_loop():
    if current_stage.bot is None:
        # stage type that has no bot
        current_stage.process()
        return
 
    resp = ""
    print("-"*80)
    print(f"Type \"{current_stage.cmd_exit}\" if you want to end the conversation.")
    print("-"*80)
 
    if current_stage.preamble != "":
        print(current_stage.preamble)
 
    while resp != current_stage.cmd_exit:
        if resp == "":
            text = current_stage.intro
            current_stage.bot.add_to_history_as_model(text)
        else:
            text = current_stage.bot.ask(resp)
 
        print(text)
        resp = input("\n> ")
 
    prompt(admin_bot.judge(current_stage.bot.get_history()))
 
def check_end_condition():
    if not current_stage.pass_check():
        check = admin_bot.end_of_game()
        print("탈출에 성공하셨나요?")
        print(check)
        if check.lower().startswith("true"):
            print("축하합니다! 섬을 탈출하셨습니다.")
            exit(0)
 
    next_stage = current_stage.next()
    if next_stage is None:
        print("게임 종료")
        exit(0)
 
    return next_stage
 
while True:
    game_loop()
    current_stage = check_end_condition()
    input("더 진행하려면 ENTER 키를 누르세요")

프로세스를 간소화하기 위해 기본 스테이지 인터페이스 클래스를 디자인했습니다. 새로운 스테이지를 생성하여 이 클래스를 확장하고 자신만의 맞춤형 스테이지 설정을 확정할 수 있습니다.

예를 들어 아래처럼 강철 문이 있는 새 스테이지를 만들 수 있습니다.

from stage import IStage
from gemma import GemmaBot
 
class StageGate(IStage):
    def __init__(self):
        self.preamble = "눈앞에 모습을 드러내는 강철 문이 막강한 위용을 자랑하며 앞을 가로막습니다. 녹슨 쇠봉에 덩굴이 어지럽게 감겨 있고 강력한 전류가 흐르는 듯 주변의 공기가 윙윙 울립니다.\n이때 Storyteller가 어른거리는 햇살 속에서 반짝이는 모습으로 당신 옆에 나타납니다."
        self.intro = "안녕하세요! 이 섬의 안내자, Storyteller라고 해요.\n우린 출입구까지 왔는데, 어떻게 하실래요?"
        self.bot = GemmaBot("당신은 신비의 섬에서 플레이어를 안내하는 친절하고 유용한 AI 가이드 Storyteller입니다. 플레이어가 역경을 헤쳐 나가며 섬에서 탈출할 방법을 찾도록 돕는 것이 당신의 역할이죠. 당신은 현재 커다란 강철 문 앞에 서 있습니다.")

요약

여러분은 Gemma를 사용하여 텍스트 기반 어드벤처 게임 제작 방법을 완전히 익혔습니다. 이와 동일한 구조를 적용하여 스팀펑크부터 판타지까지 다양한 테마로 다양한 게임을 제작할 수 있습니다.

더 나아가 스킬 기반 진행, 관계 구축 또는 미니 게임과 같은 핵심적인 게임 메커니즘에 대한 자신만의 개념을 통합할 수 있습니다.

이는 기본적인 출발점 역할을 할 뿐임을 명심하세요. 이러한 요소를 마음껏 혼합하고 조정하고 확장하여 독창적이고 매력 넘치는 텍스트 기반 어드벤처 게임 경험을 만들어 보세요.

Google 개발자 커뮤니티의 Discord 서버에 가입하여 프로젝트를 소개하고 관심사가 비슷한 열정적인 동료들과 교류해 보세요.

읽어주셔서 감사합니다.