[LLM 개발기초] 5. LangChain 활용 #2


앞선 예제들에서는 로컬 PC에 설치된 LLM 모델을 ollama 와 연동하는 방식으로 사용했습니다. 하지만 PC가 GPU 가속을 쓸 수 없어 느리거나, 업무/배포용 프로젝트를 진행하는 경우는 상용으로 개발된 API 를 원격으로 접속해서 사용하는 것이 더 합당합니다.

이번 실습에서는 OpenAI 에서 제공하는 LLM 모델 중 GPT-4o mini 모델을 이용해서 챗팅을 구현하도록 하겠습니다. GPT-4o mini 모델은 가성비가 매우 뛰어나므로 개발용/테스트용으로 좋습니다.

OpenAI 에서 제공하는 API 를 사용하기 위해서는 OpenAI 에 회원가입 후 API Key 를 생성해야 합니다. 사용자는 자신의 API Key 를 통해 원격에서 접속할 수 있습니다. 사용량에 따른 과금과 관련되어 있으므로 API Key 는 외부에 노출되지 않도록 해야합니다. 아래 링크를 통해 OpenAI API Key를 생성하세요.

API Key 를 발급받았다면 OpenAI 를 사용할 준비는 끝났습니다.



실습 1: OpenAI 서버를 이용한 AI 채팅 (API 연동방식)

먼저 OpenAI 에서 배포하는 라이브러리와 LangChain 연동 라이브러리를 설치해야 합니다. 추가로 API Key 가 코드에 노출되지 않도록 하기 위하여 dotenv 도 설치하겠습니다.

pip install openai langchain_openai dotenv

라이브러리 설치가 끝나면 코드를 준비하세요. [05_01_use_openai_api.py]

그리고 코드가 저장된 위치와 같은 곳에 [.env] 파일을 생성합니다. 해당 파일을 텍스트 에디터로 열어서 OPENAI_API_KEY 우측에 발급받은 키 값을 넣어주세요.

OPENAI_API_KEY='발급받은 API Key'

OPENAI_API_KEY 값을 소스코드와 분리된 위치에 저장해서 코드가 공유되더라도 키가 노출되지 않도록 해줬습니다. 이제 코드를 잠시 보겠습니다.

import os
from dotenv import load_dotenv
from typing import Iterable
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableGenerator
from langchain_core.messages import AIMessageChunk

load_dotenv() # .env 파일 로드

# 1. 대화내용 저장을 위한 ChatPromptTemplate 설정
prompt = ChatPromptTemplate.from_messages([
    ("system", "당신은 유능한 기상학자입니다. 답변은 200자 이내로 하세요."),
    MessagesPlaceholder("chat_history"), # 1-1. 프롬프트에 대화 기록용 chat_history 추가
    ("user", "{question}")
])

# 2. 모델 초기화
openai_api_key = os.getenv("OPENAI_API_KEY")    # 2-1. OpenAI API 키 가져오기
llm = ChatOpenAI(temperature=0,             # 2-2. 정확성 <-> 창의성 (0.0 ~ 2.0) 
                 max_tokens=2048,           # 2-3. 최대 토큰수
                 model_name='gpt-4o-mini',  # 2-4. 모델명
                 openai_api_key = openai_api_key # 2-5. API Key 설정
                 )

# 3. 스트림 출력 파서 생성 🌪️
def replace_word_with_emoji(text: str) -> str: # 문자열에서 태풍을 이모지로 바꿔주는 함수
    return text.replace("태풍", "🌪️")

def streaming_parse(chunks: Iterable[AIMessageChunk]) -> Iterable[str]: # type: ignore
    buffer = ""
    for chunk in chunks:
        buffer += chunk.content # 3-1. OpenAI 를 연동하면 AIMessageChunk 형식으로 온다. 그래서 텍스트만 추출
        while " " in buffer: # 단어가 완성될 때까지 모아서 처리
            word, buffer = buffer.split(" ", 1)
            yield replace_word_with_emoji(word) + " "
    if buffer:
        yield replace_word_with_emoji(buffer)

streaming_parser = RunnableGenerator(streaming_parse)

# 4. chain 연결 (LCEL)
chain = prompt | llm | streaming_parser

# 5. 채팅 기록 초기화
chat_history = []

# 6. chain 실행 및 결과 출력을 반복
while True:
    # 6-1. 사용자의 입력을 기다림
    user_input = input("\n\n당신: ")
    if user_input == "끝":
        break

    # 6-2. 체인을 실행하고 결과를 stream 형태로 출력
    result = ""
    for chunk in chain.stream({"question": user_input, "chat_history": chat_history}):
        print(chunk, end="", flush=True)
        result += chunk

    # 6-3. 채팅 기록 업데이트
    chat_history.append(("user", user_input))
    chat_history.append(("assistant", result))

앞선 예제를 수정한 코드이므로 변경된 부분만 보겠습니다.

  1. 변경내용 없음
  2. ollama 대신 OpenAI 서버에 연결하도록 설정합니다. 이를 위해 LangChain 에서 ChatOpenAI 를 제공합니다.
    • 2-1. .env 에 저장된 환경변수 값 중 OPENAI_API_KEY 값을 가져옵니다.
    • 2-1. temperature : 정확성과 창의성의 정도를 조절할 수 있습니다. (ollama 도 제공)
    • 2-2. 입력 가능한 토큰(단어) 수
    • 2-3. 사용할 모델명
    • 2-4. API Key 설정 : API Key 값이 여기서 전달됩니다.
  3. 출력파서 설정
    • 3-1. OpenAI 를 연동하면 string 이 아니라 AIMessageChunk 형식으로 받습니다. 그래서 chunk.content 를 통해 string 만 사용합니다.
  4. LCEL 을 이용해서 chain 을 생성합니다.
    • OpenAI 를 사용하더라도 사용법은 동일합니다.
  5. 변경내용 없음
  6. 변경내용 없음
$ python -u "c:\___Workspace\Llama\test\llama_test\LLM_test\05_01_custom_output_parser.py"


당신: 1년 동안 평균 몇 개의 태풍이 발생하지?
1년 동안 평균적으로 약 25개의 🌪️이  발생합니다. 이 중 약 10개가 일본, 한국, 중국 등 동아시아 지역에 영향을 미치는 경우가 많습니다.

당신:

PC GPU 의 성능이 그다지 좋지 않은 상태에서 ollama 를 사용해왔다면, 이번 예제에서 응답이 굉장히 빠른 것을 체감하셨을겁니다.

비용이나 보안의 문제만 없다면 상용 AI 들을 API 형태로 사용하는 것이 여러모로 좋다는 점은 부정할 수 없습니다.



실습 2: AI 를 이용한 기사 요약

이제까지는 사람들에게 가장 익숙한 AI 인터페이스인 채팅 형태로 구현했습니다. 이번에는 조금 다른 AI 기능을 테스트 해보겠습니다. 아래 BBC 기사를 AI 에게 요약해 달라고 요청해 보겠습니다.

물론 뉴스 URL 을 입력해서 텍스트를 추출하고 이걸 AI 에게 요약하라고 시키는 것이 효율적이겠죠. 하지만 이런 시나리오는 심화과정에서 다시 다룰 것이기 때문에 당장은 미리 준비된 text 데이터를 던져줘서 요약이 잘 되는지만 확인하겠습니다.

LLM 모델은 위에서 사용한 OpenAI: GPT-4o mini 를 그대로 사용하겠습니다. 강의를 잘 따라오신 분이라면 이전에 실습한 코드를 참고해서 ollama 와 llama3.1 을 이용하도록 쉽게 바꿀 수 있을겁니다.

실습파일 [05_02_openai_summarize_news.py]

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.prompts import PromptTemplate

news_text = """
"시간당 100mm의 호우는 정말 흔하지 않은데, 거의 매년 오고 있어요."
기후학자 포스텍 민승기 교수의 눈에도 거의 매년 반복되는 시간당 100mm의 폭우는 심상치 않다.
지난 17일 오전 의정부 신곡동엔 시간당 103mm의 비가, 파주시엔 시간당 101mm의 비가 내렸다. 앞선 10일 군산 어청도엔 기상 관측 이래 최대인 시간당 145.5mm의 비가 내렸다.
확률적으로는 수십 년에서 수백 년에 한 번 내려야 할 비가 이처럼 반복되면서 한반도의 강우 양상이 바뀐 것 아니냐는 관측이 나온다.
극한 호우가 왜 잦아졌냐는 질문에 대한 학계의 대답은 '아직 모른다'이다.
장 교수는 "현재의 관측 수준으로는 상공에서 대기가 정확히 어떤 구조를 가지고 있는지 입체적으로 파악하기가 쉽지 않다"며 "워낙에 다양한 변수가 있어 원인 규명이 쉽지 않다"고 답했다.
그러면서도 "해수면 온도 상승이 원인 중 하나일 거라고는 보고 있다"고 말한다.
"서해안은 온난화 속도가 전 지구 해역 중에서도 굉장히 빠른 쪽에 들어가요. 직접적으로 단정짓기는 어렵지만, 분명히 영향을 줬을 거라고 생각은 하고 있어요."
국립수산과학원에 따르면 서해의 표층 수온은 지난 1968년부터 2022년까지 약 1.19도가 올랐다. 같은 기간 전 지구 평균 표층 수온이 약 0.52상승한 데 비해 두 배 넘게 오른 셈이다.
포스텍 환경공학부 민승기 교수는 "시간당 100mm의 호우는 정말 흔하지 않은 건데, 거의 매년 이런 호우가 내린다는 건 우연 같지는 않다"며, "온난화가 대기 순환 패턴도 건드리고 있는 건 아닌가 의심을 하고 있다"고 밝혔다.
미국의 권위적인 기후 관련 학술지 ‘저널 오브 클라이미트'의 편집위원인 민 교수는 최근의 극한 호우는 "단순히 온도가 올라가서 수증기가 늘어난 걸로 설명이 안 되는 수준"이라고 한다.
그는 "일반적으로 지구 평균 온도가 1도가 올라가면 대기 중 수증기의 양은 평균 7%가 증가한다는 걸 기반으로 계산을 한다"면서, "그러나 현재는 그런 식의 계산을 벗어난 정도로 강우 형태가 크게 변했다"고 분석했다.
"3~4년 전만 해도 이렇게 시간당 100mm씩 내리는 비는 잘 없었는데, 그 사이에 전지구 온도가 3~4도가 올라간 건 아니잖아요."
민 교수는 "먼 미래가 되면 기상현상이 어떻게 변화할지를 예측했는데, 비슷한 일들이 지금 생기고 있으니까 학계에서도 뭘 놓치고 있었는지를 고민하는 상태"라고 말한다.
그러면서 "저희가 놓치고 있었던 배경 중 하나를 최근 몇 년간 비정상적으로 뜨거워진 바다라고 보고 있다"고 밝혔다.
지난해부터 강하게 영향을 미친 엘니뇨로 인해 전지구 평균 해수면 온도는 지난해 3월부터 역대 최고치를 계속해서 갱신해오다 이번 달에 들어서야 처음으로 전년도보다 낮아졌다.
민 교수는 "뜨겁게 유지된 바다가 역할을 하면서 조금 먼 미래의 상태가 온 것으로 보인다"며, 앞으로 기후변화가 지금처럼 진행됐을 때 "지금과 같이 짧은 시간 동안 많은 비가 내리는 경향성이 더 강해질 수 있다"고 우려했다.
시간당 100mm 이상의 비가 일상이 되면 수해를 대비하는 기준 자체도 높아져야 한다는 목소리가 나온다.
지난 3월 행안전부는 도시지역 소하천 설계 기준을 기존 100년 빈도의 홍수에서, 200년 빈도의 홍수를 대비할 수준으로 2배 상향했다.
이처럼 변화하는 강우패턴에 대해 각계에서 대책을 마련하고 있지만, 짧은 시간에 많은 비를 퍼붓는 최근의 강우 패턴을 대비하기에는 역부족이란 지적도 나온다.
열린사이버대 소방방재학과의 백승주 교수는 "앞으로는 여태까지 접해보지 않은 기후 상황을 계속 갱신하게 될 것"이라며 "일반적인 설계 기준처럼 최근 몇 년의 발생 빈도를 기준으로 한다든지 하는 방식은 계속 문제를 일으킬 수밖에 없다"고 말한다.
백 교수는 그러면서 상습 침수 지역에 대해서는 보다 과감하고 높은 기준의 방재 조건을 적용해야 한다고 지적했다.
"선진국들은 기존의 하수 관로로 늘어나는 비를 감당을 못 하니까 지하에 대규모 배수터널과 배수지 등을 건설했다"면서 "한국은 이런 시설이 아직 서울 신월동에 한 곳 뿐"이라고 지적했다.
지난 2022년 서울에 기록적인 폭우가 내린 이후 서울시는 상습 침수 지역에 대심도 빗물터널 6개를 더 짓겠다고 발표한 바 있다. 그러나 현재까지 고금리와 건설경기 불황 등으로 아직 한 곳도 착공을 못 한 상황이다.
"""

load_dotenv() # .env 파일 로드

# 1. 프롬프트 정의: 뉴스 원문 포함
prompt = PromptTemplate(
    input_variables=["news"],  # 아래 template 에서 사용된 변수들의 목록
    template="다음 뉴스 기사를 요약해주세요.\n{news}"  # AI 에게 전달할 내용, {변수} 형태로 변수를 삽입
)

# 2. 모델 초기화
openai_api_key = os.getenv("OPENAI_API_KEY")    # OpenAI API 키 가져오기
llm = ChatOpenAI(temperature=0,             # 정확성 <-> 창의성 (0.0 ~ 2.0) 
                 max_tokens=1024*8,         # 2-1. 최대 토큰수
                 model_name='gpt-4o-mini',  # 모델명
                 openai_api_key = openai_api_key # API Key 설정
                 )

# 3. chain 연결 (LCEL)
chain = prompt | llm

# 4. chain 실행 및 결과 출력
for chunk in chain.stream({"news": news_text}):
    print(chunk.content, end="", flush=True)

상단 news_text 변수에 뉴스 기사의 텍스트를 넣어뒀습니다.

  1. 프롬프트 정의
    • 앞선 예제들과는 다르게 PromptTemplate 를 이용했습니다.
    • template 을 정의해주고, 뉴스 원문이 들어갈 자리에 {news} 를 넣어줬습니다
    • input_variables 템플릿 내에서 사용될 변수의 이름을 리스트 형태로 지정합니다. “news” 는 템플릿 내에서 {news} 부분이 추후 채워질 것을 의미합니다.
    • chain 을 실행할 때 news 에 기사 원문을 넣어주면 됩니다.
  2. 모델 초기화
    • 2-1. 긴 뉴스 원문이 LLM 모델에 입력되어야 합니다. 그래서 입력 토큰을 1024 * 8 로 넉넉하게 잡았습니다.
    • 여기서 토큰이란 문자를 처리하는 최소 단위로 쉽게 단어라 봐도 됩니다. 하지만 복합어 applepie 같은 경우, 명사에 조사가 붙어있는 경우는 두 개 이상의 토큰이 됩니다.
  3. chain 을 연결합니다.
    • 기존에 사용하던 output parser 등은 모두 제거하였습니다.
  4. chain 실행 및 결과 출력
    • OpenAI 를 사용하는 경우 chunk.content 를 출력해야합니다.

코드를 실행하면 아래처럼 나올겁니다.

$ python -u "c:\___Workspace\Llama\test\llama_test\LLM_test\05_02_openai_summarize_news.py"

최근 한국에서 시간당 100mm 이상의 폭우가 거의 매년 발생하고 있어 기후 변화에 대한 우려가 커지고 있다. 기후학자들은 이러한 극한 호우가 빈번해진 이유를 명확히 규명하지 못하고 있지만, 해수면 온도 상승이 주요 원인 중 하나로 지목되고 있다. 서해의 표층 수온은 지난 50년간 약 1.19도 상승했으며, 이는 전 세계 평균보다 두 배 이상 높은 수치다.

민승기 교수는 최근의 강우 패턴 변화가 단순히 온도 상승으로 설명할 수 없는 수준이라고 강조하며, 비정상적으로 뜨거워진 바다가 기후 변화에 영향을 미쳤다고 분석했다. 이러한 변화에 대응하기 위해 한국 정부는 도시 소하천 설계 기준을 100년 빈도에서 200년 빈도로 상향 조정했지만, 여전히 짧은 시간에 많은 비를 처리하기에는 부족하다는 지적이 있다.

전문가들은 상습 침수 지역에 대해 더 높은 방재 기준을 적용해야 하며, 선진국들이 대규모 배수 시설을 건설한 사례를 참고할 필요가 있다고 강조하
고 있다. 서울시는 상습 침수 지역에 대심도 빗물터널을 추가로 건설할 계획을 세웠지만, 아직 착공이 이루어지지 않고 있다.

$

이와 유사한 방식으로 번역 실력도 테스트가 가능합니다. 프롬프트를 수정해가며 다양한 시도를 해보세요.



참고자료


You may also like...

답글 남기기

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

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.