[LangChain.js] 2. 모델과 프롬프트

LangChain.js 카테고리에 포함된 포스트들에서는 React/JS 를 이용해서 LangChain 을 구현하는 예제들을 포함하고 있습니다.

여기서는 LangChain 의 javascript 구현체를 기반으로 내용이 구성되어 있습니다. LangChain 아키텍처 자체에 대해서는 python 을 기반으로 한 강좌에서 더욱 자세히 다루고 있습니다. 아래 링크의 강좌를 참고하세요.

이번 포스트에서는 예제에서 사용된 model 과 prompt 에 대해서 더 자세히 알아봅니다.



Model & Prompt


이번 포스트에서 사용하는 예제는 아래에서 다운로드 받을 수 있습니다.


Chap 1 에서 사용한 예제처럼 번역 화면을 출력합니다. 하지만 이번에는 사용자가 입력한 텍스트를 원하는 언어로 번역할 수 있도록 언어를 선택하는 콤보박스가 추가되었습니다.


원하는 언어로 변환하기 위해서는 LLM 모델에게 어떤 언어로 번역할지 알려줘야 합니다. 이번 예제는 Prompt 를 이용해서 이걸 어떻게 처리하는지 보여줍니다.

아울러 앞서 사용했던 OpenAI 의 gpt 모델 대신 DeepSeek 의 모델을 사용하도록 변경하면서 모델 사용법에 대해서도 살펴보겠습니다.

UI 의 변경 내용은 여기서는 다루지 않습니다. App.tsx 파일을 참고하세요.



DeepSeek 모델 사용


DeepSeek 를 사용하기 위해서는 package.json@langchain/deepseek 를 추가해야 합니다. 그리고 아래 명령으로 패키지를 install 해주세요.

  • npm install
  "dependencies": {
    "@langchain/core": "^0.3.28",
    "@langchain/openai": "^0.3.16",
    "@langchain/deepseek": "^0.0.1",
    "langchain": "^0.3.11",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },

DeepSeek API 를 사용하기 위해서는 DeepSeek platform 에 가입 후 API key 를 생성하고 Billing 을 추가해야 합니다.

API key 가 준비되었다면 프로젝트 root 폴더에 .env 파일을 생성하고 VITE_CHATMODEL_API_KEY 항목에 값을 넣어줍니다.

VITE_CHATMODEL_API_KEY=<your_deepseek_api_key>

여기까지 준비되었다면 npm run start 로 실행해서 번역기능을 테스트 해볼 수 있습니다.

우리가 분석할 LLM 호출과 번역기능 코드를 살펴보겠습니다. 관련 코드는 모두 chatService.ts 에 있습니다.

import { ChatDeepSeek } from '@langchain/deepseek'

const apiKey = import.meta.env.VITE_CHATMODEL_API_KEY
if (!apiKey) {
  throw new Error('API key is not defined in environment variables')
}

// 1. OpenAI 모델 대신 DeepSeek 모델을 사용합니다.
// const model = new ChatOpenAI({ model: 'gpt-4o-mini', temperature: 0, apiKey })

const model = new ChatDeepSeek({
  model: 'deepseek-chat',
  temperature: 0,
  maxTokens: 10000,
  maxRetries: 2,
  apiKey,
})

코드를 보면 Chap 1 에서 사용했던 ChatOpenAI 가 주석처리되고, 대신 ChatDeepSeek 가 사용되었습니다. LangChain 에서 제공하는 LLM 모델 클래스는 이름이 Chat 으로 시작합니다.

클래스만 바뀌었을 뿐 클래스에 사용되는 속성은 표준화되어 있어서 동일하게 사용됩니다. 위 코드에서는 model, temperature, maxTokens, maxRetries, apiKey 등이 사용되었습니다.

대부분의 속성은 이름만으로도 기능이 이해가 갈 것입니다. 다만 temperature 가 궁금할 수 있겠습니다. temperature 는 AI 의 창의성을 제어하는 수치로 값이 낮을수록 정확한 답변을 요구합니다. 1에 가까울수록 AI 는 창의적이고 예측할 수 없는 답변을 합니다.

챗 모델에 사용할 수 있는 속성들은 아래 링크에 자세히 소개되어 있습니다. (“표준 매개변수” 참고)

이렇게 생성된 model 인스턴스의 invoke 또는 stream 메서드를 호출하면 LLM API 를 통해 입력한 프롬프트에 대한 응답을 출력해줍니다.



Prompt 설정


이제 사용자가 입력한 텍스트를 선택한 언어로 번역해야 합니다.

즉, LLM 모델을 호출할 때 사용자가 선택한 언어와 번역할 문장을 포함한 Prompt 를 구성해서 넘겨줘야 합니다. 이 과정이 streamChatResponse() 함수에 포함되어 있습니다.

export async function streamChatResponse(
  targetLanguage: string,
  userMessage: string,
  onMessage: (message: string) => void,
) {
  // 2. 템플릿을 생성합니다.
  const systemTemplate = 'Translate the following from Korean into {language}'
  const promptTemplate = ChatPromptTemplate.fromMessages([
    ['system', systemTemplate],
    ['user', '{text}'],
  ])

  // 3. 프롬프트를 생성합니다.
  const prompt = await promptTemplate.invoke({
    language: targetLanguage,
    text: userMessage,
  })
  // 3-1. 동일한 기능을 하는 다른 코드
  /* const prompt = await promptTemplate.formatMessages({
    language: 'English',
    text: userMessage,
  }) */

  // 4. 프롬프트를 모델에 전달하여 대화를 생성합니다.
  const stream = await model.stream(prompt)

  // 5. 대화 스트림을 이용하여 메시지를 처리합니다.
  const chunks = []
  for await (const chunk of stream) {
    const content = parseContent(chunk.content)
    chunks.push(content)
    onMessage(content)
  }

  // 6. 대화 스트림이 종료되면 결과를 출력합니다.
  console.log(chunks.join(''))
}

코드를 분석하기 전 LangChain 의 message 구조를 알아두는 것이 좋습니다.

LangChain 에서는 LLM 모델과의 통신에 사용되는 기본 단위를 메시지(message) 라고 부릅니다. 메시지는 다음의 정보들을 포함하고 있습니다.

  • 역할(Role): 메시지의 역할(예: “user”, “system”, “assistant” …)
  • 내용(Content): 메시지의 내용(예: 텍스트, 다중 모드 데이터)
  • 추가 메타데이터: ID, 이름, 토큰 사용량 및 기타 모델별 메타데이터
  • 참고자료 링크

우리는 LLM 에게 번역할 문장과 함께 특정 언어로 번역해 달라고 요청하는 프롬프트를 작성해야 합니다. 이걸 메시지를 이용해서 표현하면 다음과 같습니다.

  • Prompt
    • Message 1 (role: system)
      • LLM 이 어떤 역할을 수행해야 하는지를 지정
      • content: 텍스트 (번역할 언어를 {language} 변수로 포함)
    • Message 2 (role: user)
      • 사용자가 입력한 텍스트
      • content: 텍스트 (사용자가 입력한 텍스트를 {text} 변수로 포함)

이 구조를 코드로 작성한 부분이 [2], [3] 입니다.

  • 2. ChatPromptTemplate 을 이용해서 프롬프트 템플릿을 작성합니다.
    • 이 템플릿은 system, user 메시지를 포함하고 있습니다.
    • system, user 메시지는 language, text 변수를 포함하고 있습니다.
  • 3. 템플릿에 변수값을 지정해서 invoke 메서드를 호출하면 LLM 에게 전달할 프롬프트가 작성됩니다.
    • 사실 프롬프트 템플릿은 streamChatResponse() 함수 호출때마다 생성할 필요가 없습니다. 이름처럼 템플릿을 생성해두고 필요할 때마다 invoke() 를 호출해서 사용하면 됩니다.
  • 4. LLM API 호출
  • 5. stream 처리



LangChain 의 장점

LLM 마다 특성이 다르고 지원하는 기능도 천차만별입니다. 따라서 다양한 LLM 모델을 서비스에 접합하기 위해서는 LLM 특성에 맞는 코드를 작성해야 하는 부담이 생기기 마련입니다. LangChain 은 이런 상황에서 다양한 LLM 을 서비스에 접목하는 표준적인 방법을 제시해줍니다.

당장은 모델과 메시지와 같은 기본적인 구성요소를 이용해서 LangChain 의 유용함을 맛봤지만, 앞으로는 LangChain 이 가진 더 강력한 기능들을 하나씩 살펴볼 것입니다.



참고자료

You may also like...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

This site uses Akismet to reduce spam. Learn how your comment data is processed.