개발일지/Gemini

Gemini로 간단한 Node.js 프롬프트 만들기

cotnmin 2024. 7. 14. 19:52

어느 날 유튜브를 보던 중 조코딩 채널에서 해커톤을 진행한다고 해서 도전해 보기로 했습니다. ㅎㅎ

수상을 노리기보다는 재밌어 보여 진행하는 느낌으로 부담 없이 참여해보려 합니다.

(게다가 대회 참여만 해도 피자를 준답니다...)

Google에서 출시한 Gemini API를 활용한 서비스나 애플리케이션을 만들어 출품하면 되는데, 개발을 모르는 분이어도 간단하게 따라 만들 수 있는 부분인 것 같습니다!

저는 간단한 애플리케이션을 만들어 출품해 볼 생각입니다! 그전에 간단하게 공부해 보면서 Gemini를 소개해보려고 합니다.

조코딩 해커톤 안내 (유튜브 커뮤니티)

Google Gemini 란?

최근 많은 LLM들이 주목받고 있습니다. 가장 유명한 것으로는 OpenAI사의 Chat GPT가 있습니다.

하지만 이제는 LLM을 넘어서 LMM의 시대가 찾아왔습니다.

그중 미국 최고의 빅테크 기업이라고 할 수 있는 Google에서 출시한 LMM인 Gemini는 어떤 특징이 있을까요?

  1. 다른 LLM에서 LMM으로 진화한 인공지능과 다르게 처음부터 LMM으로 설계되어 만들어졌습니다. 멀티모달 쪽에서 다른 모델에 비해 빠르고 정확한 결과를 보여준다고 합니다.
  2. 구글의 많은 정보(구글 검색, 유튜브 등)와 양질의 정보(구글 논문, 북스 등)를 당사의 수많은 서비스에서 학습해 높은 성능을 보여줍니다.
  3. 상황에 따라 4가지 모델을 사용할 수 있습니다. Ultra, Pro, Flash, Nano 4가지 모델을 지원하는 듯합니다. https://deepmind.google/technologies/gemini/
    1. Ultra: 최고의 성능, 크기를 자랑하는 모델입니다. 위의 공식홈페이지를 보면 인간 전문가를 이기는 성능을 보여줍니다. 다만 아직 최종버전은 1.0로 아직 1.5가 출시되지 않았습니다. 오히려 1.5 Pro버전이 더 좋은 성능을 보여주는 것 같습니다.
    2. Pro: 현재 Gemini에서 발표된 모델 중 가장 높은 성능을 보여주는 것 같습니다. 높은 성능의 LMM이 필요한 경우 사용하면 좋을 것 같습니다.
    3. Flash: 가장 최근에 발표된 모델로 최고의 효율로 가볍고 빠른 성능을 보여주는 모델로 보입니다. 성능 자체는 Pro에 조금 밀리는 것 같지만, Flash만으로도 높은 성능을 보여주는 것으로 보입니다.
    4. Nano: 온디바이스용 AI 모델로 삼성의 첫 AI폰인 갤럭시 S24에 탑재된 on-device AI가 이 모델입니다!

특징은 간단히 여기까지만 알아보고 간단하게 콘솔에서 사용할 수 있는 Node.js 프롬프트를 만들어 보겠습니다.

Gemini API키 및 환경 준비하기

저는 맥 OS에서 진행했습니다! 일단 콘솔을 켜고 아래의 명령어를 입력합니다.

node -v

버전이 18 미만이거나 Node.js가 설치되어있지 않다면, Nvm을 통해 18 이상의 버전으로 변경하거나 새로 설치합니다.

18 버전 이상의 노드가 설치된 것을 확인하였다면 빈 폴더 하나를 만들고 해당 폴더로 이동해 줍니다.

npm install @google/generative-ai

빈 폴더에서 위 명령어를 실행해 줍니다. npm이 아니라 yarn이나 다른 패키지 매니저를 사용해도 문제없습니다. (나중에 새로운 앱이나 패키지를 만들고 싶은 거라면 원하는 라이브러리 환경을 만들거나 npm init으로 package.json을 생성해 주시면 됩니다.)

이제 https://aistudio.google.com/app/apikey에서 Gemini API키를 발급받아준 뒤 키를 복사해 콘솔에 아래처럼 입력해 줍니다. (Github나 블로그 등에 절대 키를 노출하지 않도록 주의하세요! 레포지토리를 만들어서 쓰고 싶다면 Codespaces secrets 기능을 이용하는 식으로 API 키를 꼭 숨겨서 사용하세요!!!)

export GOOGLE_GEMINI_API_KEY=키를 여기에 붙여넣습니다.

실습 1: 기본 택스트 생성 실습하기

위 준비가 다 끝났다면 이제 편하게 코드 작성을 해주면 됩니다.

위에서 @google/generative-ai를 설치해 둔 폴더에 test1.js를 만들어줍니다. (파일이름은 상관없습니다. index.js도 좋습니다.)

그리고 아래 코드를 넣어줍니다.

const { GoogleGenerativeAI } = require("@google/generative-ai");

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GEMINI_API_KEY);

async function run() {
  const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash"});

  const result = await model.generateContent('안녕 너는 누구니?');
  const response = result.response;
  const text = response.text();
  console.log(text);
}

run();
node test1.js

다시 콘솔에서 위 명령어를 실행해 주면 됩니다.
저는 아래와 같은 대답이 나왔네요!

실습 1 실행화면: 안녕하세요! 저는 구글에서 개발한 대규모 언어 모델입니다. 무엇을 도와드릴까요?

하지만 이렇게 되면 한번 말 걸고 끝나는 거니 좀 더 대화라는 게 이루어지도록 만들어보겠습니다.

실습 2: 기본 채팅 기능 만들기

이번엔 같은 폴더에 test2.js를 만들어줍니다.

const { GoogleGenerativeAI } = require("@google/generative-ai");

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GEMINI_API_KEY);
async function run() {
  const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash"});

  const chat = model.startChat({
    history: [
      {
        role: "user",
        parts: [{ text: "안녕, 내 이름은 cotnmin이야" }],
      },
      {
        role: "model",
        parts: [{ text: "안녕하세요! cotnmin씨" }],
      },
    ],
  });

  const result = await chat.sendMessage('내 이름이 뭐라고?');
  const response = result.response;
  const text = response.text();
  console.log(text);
}

run();

실습 2 실행화면: 죄송합니다만, 저는 당신의 이름이 "cotnmin"이라고 기억하고 있습니다. 다른 이름을 말씀하신 건가요? 혹시 실수로 입력하신 건 아닐까요?

잘 기억하고 있다고 칭찬해주고 싶지만 다시 실행하면 모델이 초기화되어 칭찬할 수 없네요...

그렇다면 마지막으로 readline를 이용해서 콘솔에서 채팅이 가능하게 만들어보겠습니다.

실습 3: readline로 실시간 대화하기

또 같은 폴더에 test3.js를 만들어줍니다.

const { GoogleGenerativeAI } = require("@google/generative-ai");
const readline = require("readline");

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash"});
const chat = model.startChat();
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

async function run(line = '') {
  const result = await chat.sendMessage(line);
  const response = result.response;
  const text = response.text();
  process.stdout.write('GEMINI : ' + text + '\n');
}

rl.on("line", async function(line) {
  if (!line) {
    rl.close();
    return;
  }
  run(line);
}).on("close", function() {
  process.exit();
});
node test3.js

위 명령어 입력 후 원하는 말을 입력하면 Gemini가 대답을 해줍니다!
아래와 같이 대화해 봤습니다.

실습 3 실행화면: 날씨를 물어보고 답변받는 과정

답변을 잘 보니 줄 때 Markdown 형태로 주는 거 같네요 ㅎㅎ
그럼 마지막으로 LMM의 장점을 살려 이미지를 분석하는 기능을 추가해 보겠습니다.

실습 4: 이미지 링크를 통해 이미지를 보내고 대화하기

마지막으로 같은 폴더에 test4.js를 만들어줍니다. 그리고 테스트에 사용할 이미지를 같은 경로에 넣어줍니다. (꼭 같은 경로일 필요는 없습니다.)

저는 햄스터 이미지(hamster.jpeg)와 실습 3: readline로 실시간 대화하기 코드를 캡처한 이미지(code.png)를 가지고 테스트했습니다.

const { GoogleGenerativeAI } = require("@google/generative-ai");
const readline = require("readline");
const fs = require("fs");

const genAI = new GoogleGenerativeAI(process.env.GOOGLE_GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash"});
const chat = model.startChat();
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
const getImage = (path, mimeType) => ({
  inlineData: {
    data: Buffer.from(fs.readFileSync(path)).toString("base64"),
    mimeType,
  },
});

let isReadyImage = false;
let images = [];
async function run(line = '') {
  let result;
  if (images.length > 0) {
    result = await chat.sendMessage([line, ...images]);
  } else {
    result = await chat.sendMessage(line);
  }
  const response = result.response;
  const text = response.text();
  process.stdout.write('GEMINI : ' + text + '\n');
}
async function addImage(line = '') {
  const imageMatch = line.match(/\.(png|jpg|jpeg|gif)$/i);;
  if (!imageMatch) {
    process.stdout.write('GEMINI : 이미지 파일이 아닙니다.\n');
    return;
  }
  const type = `image/${imageMatch[1]}`;
  const image = getImage(line, type);
  images.push(image);
  process.stdout.write('GEMINI : 이미지를 추가했습니다.\n');
  isReadyImage = false;
  return;
}

rl.on("line", async function(line) {
  if (!line) {
    rl.close();
    return;
  }
  if (line === "image") {
    isReadyImage = true;
    process.stdout.write('GEMINI : 이미지 파일 경로를 입력해주세요.\n');
    return;
  }
  if (isReadyImage) {
    addImage(line);
    return;
  }
  run(line);
}).on("close", function() {
  process.exit();
});

코드의 내용은 콘솔에 image라고 입력하면 이미지를 입력받고 다음 텍스트를 입력하면 입력했던 이미지들을 함께 전송하는 기능입니다.

같은 경로에 넣었던 두 가지 이미지를 테스트해 보겠습니다.

1. 동물 이미지 분석 테스트

입력한 햄스터 입니다. 출처) 위키백과 https://ko.wikipedia.org/wiki/%ED%96%84%EC%8A%A4%ED%84%B0
실습 4 실행화면: 햄스터 사진 분석하기

2. 이미지로 코드 분석 테스트

입력한 code.png 입니다.
실습 4 실행화면: 코드 이미지로 분석 하기

위 대화 이후 코드 분석을 요청한 결과 아래 같이 답변이 왔네요

**단점:**
* 에러 처리가 부족합니다. 예를 들어 API 호출 중 에러가 발생하면 프로그램이 종료될 수 있습니다.
* 코드가 간단하여 실제 챗봇에 적용하기에는 기능이 부족합니다. 예를 들어, 사용자의 입력을 분석하고 맥락을 이해하는 기능이 없습니다.
* 코드 스타일이 일관성이 부족합니다. 변수 이름, 들여쓰기, 주석 등이 일관되지 않아 가독성이 떨어집니다.

**개선할 점:**
* 에러 처리를 추가하여 프로그램 안정성을 높여야 합니다.
* 챗봇 기능을 확장하여 사용자 입력을 분석하고 맥락을 이해할 수 있도록 개선해야 합니다.
* 코드 스타일을 일관성 있게 개선하여 가독성을 높여야 합니다.

 

간단한 예제라 대충 만들어 그런지 예상했던 단점을 분석해 주네요 그래서 제미나이에게 다시 써달라고 부탁했습니다.

 

실습 3의 Gemini버전입니다.

const { GoogleGenerativeAI } = require("@google/generative-ai");
const readline = require("readline");

const API_KEY = process.env.GOOGLE_GEMINI_API_KEY;
if (!API_KEY) {
  console.error("Error: GOOGLE_GEMINI_API_KEY environment variable not set");
  process.exit(1);
}

const genAI = new GoogleGenerativeAI({
  apiKey: API_KEY,
});

const model = await genAI.getGenerativeModel({
  model: "gemini-1.5-flash",
});

const chat = model.startChat();

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

async function run(line) {
  try {
    const result = await chat.sendMessage(line);
    const response = result.response;
    const text = response.text();
    process.stdout.write(`GEMINI: ${text}\n`);
  } catch (error) {
    console.error("Error sending message:", error);
  }
}

rl.on("line", async function (line) {
  if (!line) {
    rl.close();
    return;
  }
  await run(line);
});

rl.on("close", function () {
  process.exit(0);
});

run(""); // Run an empty message to start the chat

네... 분석은 잘하지만 코딩 실력은 아직 조금 부족한 것 같네요...

  • GoogleGenerativeAI 객체 생성 시 apiKey전달 방식오류
  • Top level에 await 사용 오류 (심지어 getGenerativeModel 은 프로미스도 아닙니다.)

이 두 가지 문제가 있네요. 아직은 AI가 준 코드 그대로 쓰면 탈 나는 거 같네요.

마무리;

이번에 조코딩 해커톤Gemini API 개발자 대회에 앞서 Gemini API를 이용한 간단한 실습으로 학습을 해보았는데요.

API를 활용하니 그냥 LLM들과 대화로 하는 것 이상으로 즐거운 경험이네요.

다음에는 뭔가 그럴듯한 APP으로 돌아와 보겠습니다.