LangGraph 및 Granite로 워크플로 구축

작성자

Vrunda Gadesha

AI Advocate | Technical Content Author

최신 AI 시스템은 단순한 프롬프트-응답 상호 작용을 넘어 진화하고 있습니다. 오늘날의 AI 에이전트는 체계적이고 다단계적인 추론과 의사 결정과 복잡한 작업을 자율적으로 조정할 수 있습니다. 이 새로운 기능은 워크플로로 알려져 있으며, 에이전트가 문제를 보다 효과적으로 해결하기 위해 일련의 논리적 단계를 통해 작동하는 머신 러닝의 강력한 변화입니다.

이 튜토리얼에서는 그래프 기반 추론 경로를 구성하기 위한 프레임워크인 LangGraph와 이 구조를 보완하는 강력한 모델인 IBM® Granite 모델이라는 두 가지 주요 도구를 사용하여 이러한 AI 에이전틱 워크플로를 구축하는 방법을 살펴봅니다. '노드'라고 하는 워크플로의 각 단계는 일반적으로 대규모 언어 모델을 기반으로 하는 에이전트에 의해 처리됩니다. 이러한 에이전트는 모델 아웃풋 또는 조건부 논리를 기반으로 상태 간에 이동하여 동적인 의사 결정 중심 그래프를 형성합니다.

이러한 에이전틱 워크플로를 실현하기 위해 LangGraph와 Granite 모델이라는 두 가지 필수 구성 요소를 자세히 살펴보겠습니다.

LangGraph 이해하기

확장 가능한 AI 기반 워크플로를 위한 프레임워크

LangGraph는 계산 그래프 내에서 AI 모델을 상태 저장 에이전트로 표현하여 AI 기반 워크플로 개발을 간소화하도록 설계된 강력한 프레임워크입니다. 이를 통해 개발자는 각 동작 또는 의사 결정 지점이 그래프의 노드로 정의되는 확장 가능한 모듈식 시스템을 구축할 수 있습니다.

LangGraph를 사용하면 다음을 수행할 수 있습니다.

  • 각 에이전트 행동을 고유한 노드로 정의
  • 알고리즘 또는 모델 아웃풋을 사용하여 다음 단계 결정
  • 메모리와 컨텍스트를 보존하기 위해 노드 간에 상태 전달
  • 추론의 흐름을 쉽게 시각화, 디버깅 및 제어

LangGraph와 같은 다중 에이전트 시스템 및 프레임워크는 생성형 AI 작업에 적용될 때 일반적으로 작업 실행을 순차적 또는 조건부 워크플로로 구성합니다. LangChain, IBM Granite, OpenAI의 GPT 모델 또는 기타 인공 지능 도구를 사용하든 관계없이 LangGraph는 워크플로를 최적화하여 확장성과 성능을 향상시킵니다.

복잡한 워크플로 자동화를 위한 LangGraph의 주요 구성 요소

LangGraph는 복잡한 워크플로를 모듈식 지능형 구성 요소로 세분화하여 AI를 조율하는 기술에 대한 최신 접근 방식을 소개합니다. 기존 자동화나 RPA(Robotic Process Automation)와 달리 LangGraph는 실시간 논리와 메모리를 사용하여 동적이고 상황에 맞는 작업 실행을 가능하게 합니다. 이 프레임워크를 구동하는 4가지 주요 구성 요소는 다음과 같습니다.

  • 노드: 노드는 AI 도구 호출, 데이터 소스 쿼리 또는 특정 작업 수행과 같은 개별 논리 또는 작업 단위를 나타냅니다. 이러한 솔루션은 대규모 비즈니스 프로세스 내에서 반복적인 작업을 자동화하는 데 이상적입니다.
  • 엣지: 엣지: 엣지는 노드 간의 흐름을 정의하여 작업이 연결되고 실행되는 방식을 안내합니다. 이 구조는 유연한 의사 결정 프로세스를 지원하며 결과에 따라 워크플로를 조정할 수 있습니다.
  • 조건부 엣지(순환 그래프): 순환 그래프는 루프 및 조건부 분기를 활성화하여 시스템이 논리 또는 모델 아웃풋을 기반으로 노드를 다시 방문할 수 있도록 합니다. 이 기능은 동적 환경에서 반복적인 작업을 처리하고 정보에 입각한 결정을 내리는 데 중요합니다.
  • 상태(상태 저장 그래프): 상태는 공유 메모리 역할을 하여 컨텍스트를 보존하고 노드 간에 실시간 데이터를 사용할 수 있도록 합니다. 이러한 기능을 통해 LangGraph는 정적인 흐름을 넘어 워크플로 자동화의 적응적이고 지능적인 발전을 지원할 수 있습니다.

LangGraph는 이러한 구성 요소를 함께 사용하여 조직이 AI 기반 워크플로를 설계하고 실행하는 방식을 혁신하여 AI 도구와 실제 비즈니스 프로세스 간의 격차를 해소할 수 있습니다.

복잡한 워크플로 자동화를 위한 LangGraph의 주요 구성 요소 그림 1 복잡한 워크플로 자동화를 위한 LangGraph의 주요 구성 요소

Granite 모델 - 실제 문제 해결을 위한 경량 LLM

IBM® Research에서 개발한 Granite-4.0-Tiny-Preview는 복잡한 문제와 실용적인 자연어 처리(NLP) 작업을 해결하기 위해 설계된 가볍지만 성능이 뛰어난 오픈 소스 언어 모델입니다. Granite는 GPT-4와 같은 상용 모델보다 크기는 작지만 빠르고 효율적이며 Hugging Face와 완벽하게 호환됩니다. 따라서 성능을 희생하지 않고도 운영 효율성을 추구하는 개발자에게 훌륭한 선택입니다.

Granite는 다음과 같은 이점을 제공합니다.

  • 의도 분류 - 챗봇 또는 작업 기반 시스템에서 사용자 목표 식별
  • 창의적 생성 - 요약, 대화 또는 짧은 형식의 콘텐츠 제작
  • 추론 및 요약 - RAG 또는 데이터 분석과 관련된 워크플로에 이상적

이 튜토리얼에서 Granite 모델은 에이전틱 워크플로의 다양한 단계에서 중요한 역할을 하며 문제 해결과 콘텐츠 생성을 모두 지원합니다. 경량 설계로 인해 인간의 개입이 제한될 수 있고 확장 가능한 설계 패턴이 다양한 데이터 세트 및 공급자에 걸쳐 강력한 AI 솔루션을 구축하는 데 필수적인 실제 애플리케이션에 적합합니다.

사용 사례

이 튜토리얼에서는 짧은 애니메이션 시나리오를 작성하기 위한 크리에이티브 어시스턴트 역할을 하는 워크플로를 구축합니다.

목표

사용자의 스토리 아이디어가 주어지면 에이전트는 다음을 수행합니다.

  • 스토리에 적합한 장르와 어조 파악
  • 간단한 플롯 개요 생성
  • 주요 장면으로 확장(예: 클라이맥스 또는 전환점)
  • 시나리오 형식으로 해당 장면에 대한 대화 작성

이 사용 사례는 LangGraph의 구성 워크플로를 통해 구조화된 언어 모델의 추론 및 생성 기능을 모두 보여주기 위해 설계되었습니다.

워크플로 작동 방식

다음 각 단계는 LangGraph 노드로 구현됩니다.

  • 사용자 입력: 사용자는 워크플로를 시작하기 위해 개략적인 스토리 아이디어를 제공합니다.
  • 장르 감지(node_name - select_genre): LLM이 입력을 분석하여 스토리에 적합한 장르와 어조를 추론합니다.
  • 개요 생성(node_name - generate_outline): LLM은 선택한 장르를 기반으로 짧은 플롯 요약을 생성합니다.
  • 장면 작성(node_name - generate_scene): LM은 중요한 장면을 산문으로 작성하여 스토리에 생동감을 불어넣습니다.
  • 대화 쓰기(node_name - write_dialogue): LLM은 장면을 프로덕션 또는 추가 편집에 적합한 형식화된 시나리오 대화로 다시 작성합니다.

이러한 노드는 LangGraph에 순차적으로 연결되며 모델은 변경 가능한 상태 사전을 전달하면서 노드를 통과합니다.

이 워크플로는 창의적인 생성과 구조 계획 사이의 균형을 유지합니다. 이는 다음을 보여줍니다.

  • LangGraph를 통한 LLM 조정
  • 최소한의 수동 개입으로 다단계 스토리텔링
  • 인간의 상상력이 필수적인 영역에서의 창의적인 자동화

또한 확장성이 뛰어나며 개정 단계, 여러 장면 생성기 또는 캐릭터 기반 분기를 추가하여 쉽게 확장할 수 있습니다.

전제조건

watsonx.ai 프로젝트를 생성하려면 IBM® Cloud 계정이 필요합니다.

단계

1단계. 환경 설정

여러 툴 중에서 선택할 수 있지만, 이 튜토리얼에서는 Jupyter Notebook을 사용하기 위해 IBM 계정을 설정하는 방법을 안내합니다.

  1. IBM Cloud 계정을 사용하여 watsonx.ai에 로그인합니다.
  2. watsonx.ai 프로젝트를 생성합니다. 프로젝트 내에서 프로젝트 ID를 가져올 수 있습니다. 관리 탭을 클릭합니다. 그런 다음 일반 페이지의 세부 정보 섹션에서 프로젝트 ID를 복사합니다. 이 튜토리얼에는 이 ID가 필요합니다.
  3. Jupyter Notebook을 만듭니다.

이 단계에서는 이 튜토리얼의 코드를 복사할 수 있는 Notebook 환경이 열립니다. 또는 이 노트북을 로컬 시스템에 다운로드하여 watsonx.ai 프로젝트에 에셋으로 업로드할 수 있습니다. 더 많은 Granite 튜토리얼을 보려면 IBM Granite 커뮤니티를 확인하세요. 이 튜토리얼은 GitHub에서도 보실 수 있습니다.

2단계. watsonx.ai 런타임 서비스 및 API 키 설정

  1. watsonx.ai 런타임 서비스 인스턴스를 만듭니다(무료 인스턴스인 Lite 요금제 선택).
  2. 애플리케이션 프로그래밍 인터페이스 키 (API 키)를 생성합니다.
  3. watsonx.ai 런타임 서비스를 watsonx.ai에서 생성한 프로젝트에 연결합니다.

3단계. 필요한 라이브러리 설치

이 셀은 Hugging Face에 호스팅된 IBM Granite 모델을 사용하는 데 필요한 핵심 라이브러리를 설치합니다.

  • transformers: 다음을 포함하여 사전 학습된 언어 모델을 로드하고 상호 작용하기 위한 기본 라이브러리입니다.granite-4.0-tiny-preview .
  • accelerate:  효율적인 모델 로딩 및 디바이스 배치에 도움이 되며, 특히 GPU를 원활하게 사용하는 데 유용합니다.

이러한-q 플래그는 설치를 조용히 실행하여 더 깔끔한 노트북 인터페이스를 위해 자세한 아웃풋을 억제합니다. 이러한 라이브러리는 튜토리얼에서 모델을 다운로드하고 추론을 효율적으로 처리하는 데 필수적입니다.

참고: 가상 환경에서 이 튜토리얼을 실행 중이고 langgrapg가 사전 설치되어 있지 않은 경우 pip install langgraph를 사용하여 로컬 환경에 설치하세요.

!pip install -q transformers accelerate
새 버전의 PiP를 알리는 터미널의 스크린샷 아웃풋 스니펫: 에이전틱 워크플로 구축 - 라이브러리 설치

4단계. 필요한 라이브러리 가져오기

이 셀은 워크플로를 빌드하고 실행하는 데 필요한 모든 핵심 라이브러리를 가져옵니다.

AutoTokenizerAutoModelForCausalLM (출처:transformers Granite 모델을 로드하고 생성을 위해 프롬프트를 토큰화하는 데 사용됩니다.

torch : 모델 추론에 필요한 GPU 가속 및 텐서 연산을 제공합니다.

time: 시간 추적 및 성능 모니터링(선택 사항)을 활성화합니다.

StateGraphEND (출처:langgraph.graph : 에이전트 워크플로 그래프를 정의하고 컴파일하는 데 사용됩니다.

IPython.display ,base64: 아웃풋을 깔끔하게 렌더링하고 Jupyter Notebook에서 생성된 콘텐츠에 대한 선택적 다운로드 기능을 활성화하는 데 사용됩니다.

이러한 가져오기를 통해 모델 상호 작용, 워크플로 구조화 및 아웃풋 프레젠테이션을 위한 환경을 준비할 수 있습니다.

# Import libraries
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import time
from langgraph.graph import StateGraph, END
from IPython.display import display, HTML
import base64

5단계. granite 모델 및 토큰화 도구 로드

이 셀은 Hugging Face에서 가져온 IBM의granite-4.0-tiny-preview 모델과 해당 토크나이저를 로드합니다.

model_id: Hugging Face에서 호스팅되는 사전 학습된 모델의 식별자를 지정합니다.

AutoTokenizer.from_pretrained(model_id): Granite 모델과 연결된 토크나이저를 로드합니다. 토크나이저는 사람이 읽을 수 있는 텍스트를 모델의 입력 토큰으로 변환하는 역할을 합니다.

AutoModelForCausalLM.from_pretrained(...): 인과적(즉, 생성적) 언어 모델링을 위한 실제 언어 모델을 로드합니다. 이 모델은 입력을 기반으로 텍스트 아웃풋을 예측하고 생성할 수 있습니다.

이러한torch_dtype=torch.float32 인수는 데이터 유형을 메모리 효율이 높고 광범위하게 호환되는 float32로 명시적으로 설정하며, 특히 GPU 제약이 있는 환경에서 유용합니다.

이 단계에서는 Granite 모델을 에이전트 워크플로 뒤에 있는 "추론 엔진"으로 효과적으로 초기화합니다.

#Load Granite-4.0-Tiny-Preview model and tokenizer from Hugging Face
model_id = "ibm-granite/granite-4.0-tiny-preview"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, torch_dtype=torch.float32)
빠른 경로를 사용할 수 없다는 아웃풋 스니펫 아웃풋 스니펫: 워크플로 구축 - Granite 모델 로드

6단계. Granite로 텍스트를 생성하는 유틸리티 함수

generate_with_granite 함수는 Granite 모델을 사용하여 텍스트 생성 프로세스를 래핑합니다. 모델의 응답을 안내하는 입력 텍스트인 프롬프트를 수락합니다.

max_tokens : 아웃풋에서 생성할 최대 토큰 수입니다(기본값: 200).

use_gpu : GPU에서 추론을 실행할지 여부를 나타내는 플래그입니다(사용 가능한 경우).

주요 세부 정보:

device = torch.device(...): 요청되고 사용 가능한 경우 GPU(cuda)를 동적으로 선택합니다. 그렇지 않으면 기본값은 CPU입니다.

model.to(device): 적시에 적절한 디바이스에 모델을 로드하여 메모리를 절약합니다.

tokenizer(prompt, return_tensors="pt"): 모델 처리를 위해 입력 문자열을 토큰 텐서로 변환합니다.

model.generate(...): 다음과 같은 샘플링 전략으로 텍스트 생성을 시작합니다.

  • do_sample=True: 보다 창의적인 아웃풋을 위해 무작위성을 활성화합니다.

  • temperature=0.7top_p=0.9: 생성된 텍스트의 다양성을 제어합니다.

tokenizer.decode(...): 생성된 토큰을 읽을 수 있는 텍스트로 다시 변환하여 특수 토큰을 제거합니다.

이 함수는 워크플로 전반에 걸쳐 재사용되어 다양한 의사 결정 또는 생성 노드에서 Granite 모델을 호출합니다.

def generate_with_granite(prompt: str, max_tokens: int = 200, use_gpu: bool = False) -> str:
    device = torch.device("cuda" if use_gpu and torch.cuda.is_available() else "cpu")

    # Move model to device only at generation time
    model.to(device)
    inputs = tokenizer(prompt, return_tensors="pt").to(device)

    outputs = model.generate(
        **inputs,
        max_new_tokens=max_tokens,
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        pad_token_id=tokenizer.eos_token_id
    )

    return tokenizer.decode(outputs[0], skip_special_tokens=True).strip()

 

7단계. 장르 및 어조 선택을 위한 노드 생성(node-1)

select_genre_node 은(는) 에이전트 시나리오 생성 워크플로의 첫 노드를 정의합니다. Granite 모델을 사용하여 사용자의 입력에 따라 스토리의 장르와 어조를 결정합니다기

입력:state , 다음을 포함하는 사전"user_input" (예: "저는 아이들을 위한 기발한 판타지 스토리를 쓰고 싶습니다").

프롬프트 구성: 프롬프트는 모델에 다음을 요청합니다.

  • 크리에이티브 어시스턴트 역할을 합니다.
  • 사용자의 입력을 분석합니다.
  • 특정 아웃풋 형식을 사용하여 장르와 어조를 추천합니다.Genre: <genre>Tone: <tone>

텍스트 생성: 프롬프트가generate_with_granite() 유틸리티 함수에 전달되어 창의적인 응답을 생성할 수 있습니다.

아웃풋 구문 분석: 간단한 루프를 통해 라인 접두사를 기반으로 모델의 응답에서 장르와 어조를 추출합니다("Genre:""Tone:" ).

상태 업데이트: 추출된genretone 값은 다음 노드로 전달될 상태 사전에 다시 삽입됩니다.

이 노드는 창의적인 분류기 역할을 하여 후속 노드가 장르와 어조을 기본 매개변수로 사용하여 상황에 맞게 정렬된 개요, 구조 및 장면을 생성할 수 있도록 합니다.

def select_genre_node(state: dict) -> dict:
    prompt = f"""
You are a creative assistant. The user wants to write a short animated story.
Based on the following input, suggest a suitable genre and tone for the story.
User Input: {state['user_input']}

Respond in this format:
Genre: <genre>
Tone: <tone>
""".strip()

    response = generate_with_granite(prompt)

    # Basic parsing of output
    genre, tone = None, None
    for line in response.splitlines():
        if "Genre:" in line:
            genre = line.split("Genre:")[1].strip()
        elif "Tone:" in line:
            tone = line.split("Tone:")[1].strip()

    # Update state
    state["genre"] = genre
    state["tone"] = tone
    return state

8단계. 플롯 개요를 생성하기 위한 노드 생성(node-2)

이러한generate_outline_node 함수는 시나리오 생성 워크플로에서 두 번째 노드를 정의합니다. 선택한 장르와 어조를 기반으로 스토리에 대한 간결한 줄거리 개요를 생성합니다.

입력: 함수는 다음을 포함하는 상태 사전을 수신합니다.

  • user_input: 독창적인 스토리 아이디어입니다.
  • 장르: 이전 노드에서 선택되었습니다.
  • 어조: 이전 노드에서 선택되었습니다.

프롬프트 구성: 모델에 다음을 지시합니다.

  • 창의적인 글쓰기 어시스턴트 역할을 합니다.
  • 사용자의 아이디어, 장르 및 어조를 고려합니다.
  • 짧은 애니메이션 시나리오에 적합한 간단한 줄거리 개요(3~5문장)를 생성합니다.

텍스트 생성: 프롬프트가generate_with_granite() 에 더 높은token limit (max_tokens=250) (으)로 전송되어 여러 문장 개요를 위한 공간을 허용합니다.

상태 업데이트: 생성된 플롯 개요가 키"outline" 아래의 상태에 추가되어 구조 확장의 다음 단계에서 사용할 수 있습니다.

이 노드는 추상적인 창작 의도를 내러티브 스케치로 변환하여 뒤에 이어지는 세부적인 3막 구조를 위한 발판을 제공합니다. 이를 통해 다운스트림 노드가 일관되고 상상력이 풍부한 기준선에서 작동하도록 보장합니다.

def generate_outline_node(state: dict) -> dict:
    prompt = f"""
You are a creative writing assistant helping to write a short animated screenplay.
The user wants to write a story with the following details:
Genre: {state.get('genre')}
Tone: {state.get('tone')}
Idea: {state.get('user_input')}

Write a brief plot outline (3–5 sentences) for the story.
""".strip()

    response = generate_with_granite(prompt, max_tokens=250)
    state["outline"] = response
    return state

9단계. 개요에서 주요 장면을 생성하기 위한 노드 생성(node - 3)

이러한generate_scene_node 함수는 플롯 개요가 풍부한 내러티브 장면으로 변환되는 워크플로의 세 번째 단계를 정의합니다. 이 장면은 스토리의 전환점을 생생하게 극화하여 아이디어를 요약에서 스토리텔링으로 옮깁니다.

입력: 노드는 이제 다음을 포함하는 상태 사전을 사용합니다.

  • genretone
  • 이러한plot outline 이전 노드에서

프롬프트 구성: 모델에 다음을 지시합니다.

  • 시나리오 작가 역할
  • 스토리의 구성 요소를 사용하여 전환점 또는 클라이맥스 장면을 생성합니다.genre ,toneoutline
  • 가독성애니메이션 친화적인 설명을 유지하기 위해 산문 형식으로 작성

장면 요구 사항:

  • 생생하고 설명적(애니메이션에 적합)
  • 감정 또는 내러티브 아크의 중심(예: 발견, 갈등 또는 해결)

텍스트 생성:generate_with_granite 은(는)max_tokens=300 (으)로 호출되어 몰입형 장면 구축을 위한 충분한 공간을 제공합니다.

상태 업데이트: 생성된 장면은"scene" 키 아래의 상태 사전에 추가됩니다.

워크플로에 내러티브 몰입과 시각적 스토리텔링을 도입합니다. 이 노드는 단순히 스토리를 요약하는 대신 애니메이션 단편 스크립트 제작에 필수적인 감각적이고 감정적인 디테일로 스토리에 생동감을 불어넣습니다.

def generate_scene_node(state: dict) -> dict:
    prompt = f"""
You are a screenwriter.
Based on the following plot outline, write a key scene from the story.
Focus on a turning point or climax moment. Make the scene vivid, descriptive, and suitable for an animated short film.

Genre: {state.get('genre')}
Tone: {state.get('tone')}
Outline: {state.get('outline')}

Write the scene in prose format (not screenplay format).
""".strip()

    response = generate_with_granite(prompt, max_tokens=300)
    state["scene"] = response
    return state

10단계. 시나리오 형식으로 캐릭터 대화를 작성하기 위한 노드 생성(node - 4)

이러한write_dialogue_node 함수는 스토리텔링 워크플로의 네 번째 창의적 단계인 내러티브 장면을 형식이 지정된 캐릭터 대화로 변환하는 작업을 정의합니다. 이 단계는 산문과 화면에 적합한 대본 작성 사이의 간극을 메워 캐릭터에 목소리를 부여합니다.

입력: 노드는 상태scene 을(를) 예상하여 생생한 스토리 순간(일반적으로 전환점이나 클라이맥스)을 담을 수 있습니다.

프롬프트 구성: 모델은 다음과 같이 안내됩니다.

  • 대화 작가 역할
  • 장면에서 대화 추출 및 조정
  • 다음을 사용하여 시나리오 스타일로 아웃풋 형식을 지정합니다.CHARACTER: Dialogue line

대화 지침:

  • 짧고 표현력 있게 작성
  • 애니메이션에 적합한지 확인(시각적, 감성적, 간결함)
  • 명확성을 위해 필요한 경우 이름 만들기

생성:generate_with_granite() 호출은max_tokens=300 을(를) 사용하며, 이는 표현력과 간결함의 균형을 유지하므로 짧은 애니메이션 스크립트에 이상적입니다.

상태 업데이트: 대화가 향후 단계(예: 표시 또는 편집)를 위해dialogue 키 아래 상태에 저장됩니다.

def write_dialogue_node(state: dict) -> dict:
    prompt = f"""
You are a dialogue writer for an animated screenplay.

Below is a scene from the story:
{state.get('scene')}

Write the dialogue between the characters in screenplay format.
Keep it short, expressive, and suitable for a short animated film.

Use character names (you may invent them if needed), and format as:

CHARACTER:
Dialogue line

CHARACTER:
Dialogue line
""".strip()

    response = generate_with_granite(prompt, max_tokens=300)
    state["dialogue"] = response
    return state

11단계. 각 노드에 진행 상황 보고 추가

이 헬퍼 함수with_progress 은(는) 워크플로 노드를 실시간 진행 표시기로 래핑하도록 설계되었습니다. 원래 함수의 논리를 변경하지 않고 실행 상태와 타이밍을 추적하고 인쇄하여 긴 워크플로를 보다 투명하게 만듭니다.

함수 목적: 다음을 기록하는 데코레이터로 노드(예: generate_scene_node)를 래핑합니다.

  • 실행 중인 단계
  • 단계 수 인덱스 및 총 개수
  • 단계에 걸리는 시간

매개변수:

  • fn : 실제 노드 함수(예: write_dialogue_node)
  • label : 해당 함수에 대한 사람이 읽을 수 있는 레이블
  • index : 시퀀스의 단계 번호(예: 2)
  • total : 워크플로의 총 단계 수

내부 래퍼:

  • 시작 메시지 인쇄
  • 시작 시간 기록
  • 원래 함수 호출
  • 경과 기간이 있는 완료 메시지 인쇄

반환: 진행 메시지를 추가하지만 그 외에는 동일하게 동작하는 함수의 수정된 버전입니다.

워크플로가 증가함에 따라, 특히 생성이나 편집과 같은 일부 단계가 더 오래 걸리거나 메모리 과부하와 같은 문제를 일으킬 수 있는 경우 어떤 단계가 실행되고 있는지 추적하는 것이 중요해집니다. 이 진행 래퍼는 투명성을 보장하고 디버깅런타임 진단에 유용합니다.

# Wrap with progress reporting

def with_progress(fn, label, index, total):
    def wrapper(state):
        print(f"\n[{index}/{total}] Starting: {label}")
        start = time.time()
        result = fn(state)
        duration = time.time() - start
        print(f"[{index}/{total}] Completed: {label} in {duration:.2f} seconds")
        return result
    return wrapper

12단계. LangGraph 워크플로 정의

이 셀은 LLM 워크플로용으로 설계된 컴포지션 그래프 기반 프로그래밍 프레임워크인 LangGraph를 사용하여 짧은 애니메이션 스토리를 생성하기 위한 워크플로 논리를 정의합니다. 그래프의 각 단계는 창의적인 작업을 나타내며 최종 시나리오를 생성하기 위해 특정 순서로 실행됩니다.

워크플로의 구성 요소:StateGraph(dict)  - 사전 기반 상태로 워크플로를 초기화합니다(즉, 작업 메모리는 노드에서 노드로 전달되는 사전입니다).

진행 상황 추적이 있는 노드 등록: 각 단계(장르 선택, 개요 생성, 장면 작성, 대화 작성)는 with_progress() 래퍼가 있는 노드로 추가됩니다. 

graph.add_node("select_genre", with_progress(select_genre_node, "Select Genre", 1, 4)) 

이 접근 방식은 각 노드가 실행될 때 런타임과 진행 상황을 기록하도록 합니다.

워크플로 엣지(노드 시퀀싱): 크리에이티브 파이프라인의 순서는 다음과 같이 명확하게 정의되어 있습니다.

select_genre → generate_outline → generate_scene → write_dialogue

 

set_entry_point()set_finish_point(): 워크플로의 시작 및 종료 노드를 정의합니다.

graph.compile(): 워크플로를 이제 초기 상태로 호출할 수 있는 실행 가능한 양식(워크플로)으로 컴파일합니다.

이 구조는 모듈식이고 읽기 쉽고 디버깅 가능한 LLM 워크플로를 가능하게 합니다. 크리에이티브 프로세스의 각 단계는 개별적으로 프로파일링할 수 있으며 나중에 교체하거나 확장할 수 있습니다(예: '장면 수정' 단계 또는 '아웃풋 요약' 노드 추가).workflow 은(는) 이제 프롬프트와 함께 실행할 준비가 되었으며 크리에이티브 파이프라인을 단계별로 실행하여 실시간 진행 상황을 보여줍니다.

# Define LangGraph

graph = StateGraph(dict)
graph.add_node("select_genre", with_progress(select_genre_node, "Select Genre", 1, 4))
graph.add_node("generate_outline", with_progress(generate_outline_node, "Generate Outline", 2, 4))
graph.add_node("generate_scene", with_progress(generate_scene_node, "Generate Scene", 3, 4))
graph.add_node("write_dialogue", with_progress(write_dialogue_node, "Write Dialogue", 4, 4))

graph.set_entry_point("select_genre")
graph.add_edge("select_genre", "generate_outline")
graph.add_edge("generate_outline", "generate_scene")
graph.add_edge("generate_scene", "write_dialogue")
graph.set_finish_point("write_dialogue")

workflow = graph.compile()

 

13단계. LangGraph 워크플로 실행 및 아웃풋 표시

이 최종 코드 셀은 전체 크리에이티브 워크플로를 실행하고 스토리 생성의 각 단계별 결과를 표시하는 곳입니다.

initial_state: 이 명령은 사용자가 창의적인 아이디어나 테마를 제공하는 워크플로의 시작점입니다. user_input은 스토리 파이프라인의 시드 역할을 합니다.

final_state: 이 명령은 전체 LangGraph 파이프라인을 트리거합니다. 입력은 등록된 각 노드(select_genre, generate_outline, generate_scene 및 write_dialogue)를 순차적으로 통과합니다.

결과 표시: 최종 상태 사전에 다양한 노드가 포함된 키가 포함됩니다.

  • "genre": 사용자 입력에 따라 식별된 장르.
  • "tone": 스토리 어조 품질.
  • "outline": 3~5개의 문장으로 구성된 플롯 요약.
  • "scene": 산문으로 쓰여진 생생한 전환점 장면.
  • "dialogue": 시나리오 형식의 캐릭터 대화.

이 섹션에서는 단계별 모듈식 LLM 워크플로를 통해 사용자 의도가 완전한 미니스크립트로 변환되는 과정을 보여줍니다. 이는 대화형, 해석 및 사용자 지정이 가능한 엔드투엔드 크리에이티브 파이프라인입니다.

참고: GPU 또는 TPU를 사용하는 경우 코드를 실행하는 데 약 15~17분이 소요됩니다. 로컬 가상 환경에서 cede를 실행하는 데 사용된 인프라를 기반으로 아웃풋을 실행하고 생성하는 데 약 65~70분이 걸립니다.

# Run workflow
initial_state = {
    "user_input": "I want to write a whimsical fantasy story for children about a lost dragon finding its home."
}
final_state = workflow.invoke(initial_state)

# Display Results
print("\n=== Final Output ===")
print("Genre:", final_state.get("genre"))
print("Tone:", final_state.get("tone"))
print("\nPlot Outline:\n", final_state.get("outline"))
print("\nKey Scene:\n", final_state.get("scene"))
print("\nDialogue:\n", final_state.get("dialogue"))
각 단계에 소요된 시간(90~260초)을 기록하는 아웃풋 스니펫
판타지 스토리의 줄거리 개요 및 주요 장면 아웃풋
판타지 스토리의 아웃풋 스니펫
판타지 스토리의 아웃풋 스니펫
판타지 스토리의 아웃풋 스니펫

"잃어버린 용이 집을 찾는다는 어린이용 기발한 판타지 스토리를 쓰고 싶어요"라는 사용자의 프롬프트를 시스템이 어떻게 완전한 애니메이션 스토리로 변환하는지 알아봅시다. 각 단계는 이전 단계를 기반으로 하며, 워크플로의 크리에이티브 노드에 의해 안내되고 Granite 모델에 의해 구동됩니다.

1. 장르 및 어조. 워크플로는 길을 잃은 용이 집을 찾는 기발한 판타지 이야기를 어린이를 위해 쓰고 싶습니다라는 내용의 사용자의 원래 프롬프트를 해석하는 것으로 시작됩니다. 이 입력에 따라 select_genre_node는 내러티브를 기발한 판타지로 올바르게 분류하고 적절한 매혹적이고 따뜻한 어조를 식별합니다. '기발한', '어린이를 위한 ', '길을 잃은 용이 집을 찾는'과 같은 문구의 사용이 마법적이면서도 부드러운 스토리텔링 스타일을 분명히 나타내기 때문에 정확하고 상황에 맞게 조정됩니다. 장르와 어조는 워크플로의 모든 후속 생성 단계를 형성하는 기본 매개변수 역할을 합니다.

2. 플롯 개요와 등장인물 설명. 다음 단계에서 모델은 식별된 장르, 톤 및 사용자의 독창적인 아이디어를 기반으로 플롯 개요를 작성하도록 요청받습니다. 아웃풋에는 3~5문장의 스토리 요약뿐만 아니라 프롬프트 누수 또는 이전 반복의 지침 형식 유지로 인한 보너스 캐릭터 설명도 포함됩니다.

줄거리 개요는 릴리라는 소녀가 상처 입은 용을 발견하고 늙은 약초꾼의 안내를 받아 마법에 걸린 숲으로 돌아가도록 도와준다는 내용입니다. 이 스토리라인은 치유, 소속감, 우정에 대한 감성적인 요소가 가미된 어린이 친화적인 마법 여행에 초점을 맞춘 사용자의 의도를 정확히 반영합니다. 용, 릴리, 약초꾼의 캐릭터 스케치는 막연한 아이디어를 역할, 성격, 내러티브 책임이 명확한 구조화된 콘셉트로 전환하여 깊이를 더합니다. 이 단계는 스토리가 추상적인 의도에서 화면 각색에 적합한 구체적인 구조로 전환되도록 합니다.

3. 주요 장면. 전체 플롯 개요를 고려할 때generate_scene_node 은(는) 스토리의 전환점을 포착하는 생생한 장면을 산문 형식으로 작성하며, 이는 단편 애니메이션 스크립트를 개발하려는 사용자의 원래 의도에서 명시적으로 요청된 또 다른 요소입니다.

선택한 순간은 릴리가 마법에 걸린 숲에서 부상당한 용을 돌보는 장면으로, 캐릭터 간의 감정적 교감과 상호 이해를 구축하는 장면입니다. 이 순간은 용의 귀환을 향해 스토리의 중심을 잡는 중요한 순간입니다. 이 장면은 풍부한 이미지와 감정으로 '기발하고' '따뜻한' 제약 조건을 고수하는 동시에 시각적 표현이 풍부하여 짧은 애니메이션 형식에 완벽하게 적합합니다.

단계 전반에 걸쳐 어조와 장르 일관성을 유지하는 모델의 능력은 LangGraph의 상태 전달 워크플로와 Granite 모델의 추론 기능의 가치를 보여줍니다.

4. 시나리오 형식의 대화. 마지막으로,write_dialogue_node 은(는) 산문 장면을 시나리오 형식의 구조화된 대화로 변환합니다. 애니메이션용 스토리 제작에 초점을 맞춘 사용자 프롬프트는 내러티브가 제작 또는 시각화할 수 있는 형식으로 제공될 것을 암묵적으로 요구합니다. 이 노드는 릴리, 용, 늙은 약초꾼이 오두막집과 마법에 걸린 숲이라는 두 가지 장면에서 잘 구성된 형식의 대화를 전달함으로써 이 목표를 달성합니다. 대화는 감정적 단서와 캐릭터 의도를 유지하며 애니메이션 스크립트 작성 규칙에 일치하는 형식으로 작성되었습니다(예: 대문자의 캐릭터 이름, 장면 제목[INT. LILY’S COTTAGE - DAY]) . 아웃풋은 짧고 표현력이 풍부하며 정서적으로 공감을 불러일으키는데, 이는 애니메이션 환경에서 어린 관객의 참여를 유도하는 데 필수적입니다.

워크플로의 각 단계에서는 원래 프롬프트인 "길 잃은 용이 집을 찾는 어린이를 위한 기발한 판타지 스토리"를 체계적이고 창의적이며 표현력이 풍부한 내러티브 결과물로 변환합니다. 장르 선택부터 대화 형식에 이르기까지 시스템은 일관된 스토리텔링 아크를 점진적으로 구축합니다. LangGraph 프레임워크는 작업 간의 전환이 논리적으로 연결되도록 하며, IBM Granite 모델은 일관된 어조로 상황에 맞는 텍스트를 생성할 수 있도록 합니다. 그 결과 한 줄의 사용자 입력만으로 화면에서 바로 볼 수 있는 짧은 단편 애니메이션 스토리가 탄생하여 창의적인 AI 애플리케이션에서 에이전트 워크플로의 실질적인 힘을 보여줍니다.

스토리텔링 경험을 더욱 매력적으로 만들기 위해 생성된 스토리 요소(장르, 어조, 플롯, 장면 및 대화)를 아름답게 포맷하는 간단한 HTML 기반 시각화가 있습니다. 또한, 클릭 한 번으로 전체 스크립트를 텍스트 파일로 다운로드하여 나중에 사용하거나 공유할 수 있습니다. 스토리를 화면으로 생생하게 전달해 보세요!

# Beautification and Downloadable option creation for user

def display_output_with_download(final_state):
    genre = final_state.get("genre", "")
    tone = final_state.get("tone", "")
    outline = final_state.get("outline", "")
    scene = final_state.get("scene", "")
    dialogue = final_state.get("dialogue", "")

    # Combine scene and dialogue into a full script
    script = f"""Genre: {genre}
Tone: {tone}
Outline:
{outline}
Scene:
{scene}
Dialogue:
{dialogue}
"""

    # Encode to base64
    b64_script = base64.b64encode(script.encode()).decode()

    # Create downloadable HTML content
    html = f"""
    <h2>Genre & Tone</h2>
    <p><strong>Genre:</strong> {genre}</p>
    <p><strong>Tone:</strong> {tone}</p>

    <h2>Outline</h2>
    <pre>{outline}</pre>

    <h2>Scene</h2>
    <pre>{scene}</pre>

    <h2>Dialogue</h2>
    <pre>{dialogue}</pre>

    <a download="screenplay_script.txt" href="data:text/plain;base64,{b64_script}">
        <button style="margin-top: 20px; padding: 10px 20px; font-size: 16px;">Download Script as .txt</button>
    </a>
    """
    display(HTML(html))

# Run this after workflow.invoke()
display_output_with_download(final_state)

GPU 사용 및 인프라 참고 사항

이 튜토리얼은 텍스트 생성을 위해 Granite-4.0-Tiny-Preview 모델을 사용합니다. Granite 제품군의 소규모 모델 중 하나이지만, 특히 LangGraph 워크플로에서 여러 노드를 실행할 때 효율적으로 실행하려면 여전히 GPU 지원 환경이 필요합니다.

권장 설정:

  • NVIDIA V100 또는 A100 GPU 1개 이상
  • 안정적인 성능을 위한 16GB GPU 메모리 이상
  • torch, transformers 및 langgraph가 설치된 Python 3.8 이상

성능 참고 사항:

  • GPU 메모리를 지우지 않고 모든 노드를 순차적으로 실행하면 CUDA 메모리 부족 오류가 발생할 수 있습니다.
  • 메모리 문제를 최소화하려면 다음을 수행합니다.
    • 모델을 생성하는 동안에만 GPU로 이동하고 이후에는 다시 CPU로 이동합니다.
    • 생성 토큰을 적당히 유지합니다(max_tokens=200–300).
    • 선택적으로 수정 또는 세부 장면 확장과 같은 노드를 제거하거나 단순화할 수 있습니다.

이 튜토리얼을 호스팅된 노트북 환경(예: IBM watsonx.ai 또는 Google Colab Pro)에서 실행하는 경우 런타임 설정에서 GPU가 활성화되어 있는지 확인하세요.

리소스가 부족한 환경의 경우 다음을 고려합니다.

  • 모델 추론을 Hugging Face Inference API로 오프로드합니다.
  • 워크플로 복잡성 감소.
  • CPU 사용(더 긴 생성 시간).

요약

이 튜토리얼에서는 LangGraph와 IBM의 granite-4.0-Tiny-Preview 언어 모델을 사용하여 모듈식 에이전트 스토리텔링 워크플로를 구축했습니다. 간단한 크리에이티브 프롬프트에서 시작하여 장르와 어조를 분류하고 줄거리 개요를 생성하고 주요 장면을 작성하고 시나리오 스타일의 대화로 마무리하는 단계별 파이프라인을 구축했습니다. 그 과정에서 방법을 시연해 보았습니다.

  • LangGraph를 사용하여 동적 워크플로 구성
  • 창의적인 추론을 위해 Granite와 같은 경량 LLM 통합
  • 임시 모델 로딩으로 GPU 메모리 제약 처리
  • 사용하기 쉬운 형식으로 스토리 아웃풋 표시 및 내보내기

이 에이전트 프레임워크는 시나리오 작성에 강력할 뿐만 아니라 광범위한 창의적 또는 작업 라우팅 사용 사례로 확장될 수 있습니다. 단 몇 개의 노드만으로 기발한 아이디어를 대본에 바로 사용할 수 있는 스토리로 바꿀 수 있는 미니어처 글쓰기 어시스턴트를 구축했습니다.

개발자, 스토리텔러, 연구원이든 이 튜토리얼을 통해 크리에이티브 영역에서 LLM 기반 워크플로 엔지니어링을 살펴볼 수 있는 실용적인 기초를 다질 수 있습니다.

자체 에이전트를 구축할 준비가 되셨나요? IBM Granite 모델과 IBM watsonx Orchestrate를 사용하여 창의성을 마음껏 발휘해 보세요.

관련 솔루션
비즈니스용 AI 에이전트

생성형 AI로 워크플로와 프로세스를 자동화하는 강력한 AI 어시스턴트 및 에이전트를 구축, 배포, 관리하세요.

    watsonx Orchestrate 살펴보기
    IBM AI 에이전트 솔루션

    믿을 수 있는 AI 솔루션으로 비즈니스의 미래를 설계하세요.

    AI 에이전트 솔루션 살펴보기
    IBM Consulting AI 서비스

    IBM Consulting AI 서비스는 기업이 AI 활용 방식을 재구상하여 혁신을 달성하도록 지원합니다.

    인공 지능 서비스 살펴보기
    다음 단계 안내

    사전 구축된 앱과 스킬을 사용자 정의하든, AI 스튜디오를 사용하여 맞춤형 에이전틱 서비스를 구축하고 배포하든, IBM watsonx 플랫폼이 모든 것을 지원합니다.

    watsonx Orchestrate 살펴보기 watsonx.ai 살펴보기
    각주

    1 Lang Cao. 2024년. GraphReason: Enhancing Reasoning Capabilities of Large Language Models through A Graph-Based Verification Approach. In Proceedings of the 2nd Workshop on Natural Language Reasoning and Structured Explanations (@ACL 2024), 1~12페이지, 태국 방콕. Association for Computational Linguistics.