πŸ”₯ LangGraph Code Assistant

μ •κΈ°μš©Β·2025λ…„ 2μ›” 25일
0

LangChain, LangGraph

λͺ©λ‘ 보기
2/2
post-thumbnail

πŸ“ ν”„λ‘œμ νŠΈ κ°œμš”

LangGraph?

λ‚΄κ°€ LangGraphλ₯Ό μ’‹μ•„ν•˜λŠ” μ΄μœ λŠ” ν•„μš”ν•œ λͺ¨λ“ˆμ„ μ˜¨λΌμΈμ—μ„œ 가져와 λ‚΄ λ§ˆμŒλŒ€λ‘œ μ»€μŠ€ν„°λ§ˆμ΄μ§•ν•˜κΈ° νŽΈν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. LangChain처럼 λͺ¨λ“  κΈ°λŠ₯이 μΌμ²΄ν˜•μœΌλ‘œ λ¬Άμ—¬ μžˆμ§€ μ•Šκ³ , Node λ‹¨μœ„λ‘œ κ΅¬μ„±λ˜μ–΄ μžˆμ–΄ μ½”λ“œ μˆ˜μ •κ³Ό ν™•μž₯이 훨씬 μœ μ—°ν•˜λ‹€.

λ‹€λ₯Έ μ‚¬λžŒλ“€μ˜ μ½”λ“œλ₯Ό μ°Έκ³ ν•˜λ©° λ§Žμ€ 도움을 받은 만큼, λ‚˜λ„ λˆ„κ΅°κ°€μ˜ ν”„λ‘œμ νŠΈμ— μž‘μ€ κΈ°μ—¬λ₯Ό ν•  수 있으면 μ’‹κ² λ‹€λŠ” 생각이 λ“€μ—ˆλ‹€. λ¬Όλ‘  μ½”λ“œ μ–΄μ‹œμŠ€ν„΄νŠΈ μ˜ˆμ œλŠ” 이미 LangGraph 곡식 λ¬Έμ„œμ— λ‚˜μ™€ μžˆμ§€λ§Œ, ν•œκ΅­μ–΄ μžλ£ŒλŠ” λΆ€μ‘±ν•˜κ³  곡식 λ¬Έμ„œλŠ” λ‚΄μš©μ΄ λ‹€μ†Œ 무겁게 느껴질 수 μžˆλ‹€. κ·Έλž˜μ„œ 이 κΈ€μ—μ„œλŠ” 처음 μ ‘ν•˜λŠ” μ‚¬λžŒλ„ λΆ€λ‹΄ 없이 이해할 수 μžˆλ„λ‘ 더 가볍고 μ‹€μš©μ μΈ μ½”λ“œ 쿑뢁을 λ§Œλ“€μ–΄λ΄€λ‹€. 특히 데이터 μ‹œκ°ν™” 츑면에 μ΄ˆμ μ„ 맞좰 μ½”λ“œλ₯Ό μ§œλ΄€λ‹€.

이 μ½”λ“œκ°€ λˆ„κ΅°κ°€μ˜ ν”„λ‘œμ νŠΈμ— μΈμš©λœλ‹€λ©΄ 정말 λΏŒλ“―ν•  것 κ°™λ‹€.
그럼 μ‹œμž‘ν•΄λ³΄μž! πŸš€


🧩 LangGraph의 κΈ°λ³Έ μš”μ†Œ

Agentic Code Generatorλ₯Ό λ§Œλ“€κΈ° 전에, LangGraph의 핡심 κ°œλ…λΆ€ν„° κ°€λ³κ²Œ ν›‘μ–΄λ³΄μž.

  1. μƒνƒœ(State): μž‘μ—… 쀑 데이터λ₯Ό μ €μž₯ν•˜λŠ” 곡간이야. 예λ₯Ό λ“€μ–΄ μ‚¬μš©μž μž…λ ₯, μ „μ²˜λ¦¬ κ²°κ³Ό, μ½”λ“œ μ‹€ν–‰ κ²°κ³Ό 같은 게 여기에 λ‹΄κΈ΄λ‹€.
  2. λ…Έλ“œ(Node): νŠΉμ • μž‘μ—…μ„ μ²˜λ¦¬ν•˜λŠ” λ‹¨μœ„λ‹€. 데이터 λ‘œλ“œ, μ „μ²˜λ¦¬, μ½”λ“œ 생성, μ‹œκ°ν™” λ“± 단계별 μž‘μ—…μ΄ μ—¬κΈ°μ„œ 이뀄진닀.
  3. κ°„μ„ (Edge): λ…Έλ“œλ“€λΌλ¦¬ μ—°κ²°ν•΄μ£ΌλŠ” 닀리 같은 μ‘΄μž¬λ‹€. 이걸둜 μž‘μ—… μˆœμ„œλ‚˜ 쑰건 λΆ„κΈ°λ₯Ό μ„€μ •ν•  수 μžˆμ–΄.

이 ꡬ쑰 덕뢄에 μž‘μ—… 흐름을 μ‹œκ°μ μœΌλ‘œ νŒŒμ•…ν•˜κΈ°λ„ 쉽고, μ½”λ“œ 관리도 훨씬 νŽΈν•˜λ‹€. πŸ™Œ


πŸ› οΈ Agentic Code Generator ν”Œλ‘œμš° 섀계

Agentic Code GeneratorλŠ” μ•„λž˜ νλ¦„μœΌλ‘œ λŒμ•„κ°„λ‹€.

  1. 데이터 λ‘œλ“œ
  2. 데이터 μ „μ²˜λ¦¬
  3. μ‚¬μš©μž μš”κ΅¬μ‚¬ν•­ 뢄석 및 μ½”λ“œ 생성
  4. μ½”λ“œ μ‹€ν–‰ 및 κ²°κ³Ό 확인
  5. μ—λŸ¬ λ°œμƒ μ‹œ μ½”λ“œ μž¬μƒμ„± 및 μž¬μ‹€ν–‰

πŸ” ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨

λ‹€μ΄μ–΄κ·Έλž¨

μœ„μ˜ 그림은 Agentic Code Generator의 ν”Œλ‘œμš° λ‹€μ΄μ–΄κ·Έλž¨μ΄λ‹€. μ΅œλŒ€ν•œ κ°„μ†Œν™”ν•΄μ„œ λ§Œλ“€μ–΄λ³΄μ•˜λ‹€.

flowchart LR
    A(START) --> B[데이터 λ‘œλ“œ]
    B --> C[데이터 μ „μ²˜λ¦¬]
    C --> D[μ½”λ“œ 생성]
    D --> E[μ‹œκ°ν™”]
    E -->|μ—λŸ¬ λ°œμƒ?| D
    E -->|μ—λŸ¬ μ—†μŒ| F(END)

μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ μžλ™μœΌλ‘œ μ½”λ“œ 생성 λ‹¨κ³„λ‘œ λŒμ•„κ°€ 반볡 μ‹€ν–‰ν•œλ‹€. πŸ”„


πŸ’» μ½”λ“œ κ΅¬ν˜„ κ³Όμ •

1️⃣ μƒνƒœ(State) μ •μ˜

λ¨Όμ € μž‘μ—…μ— ν•„μš”ν•œ 데이터λ₯Ό μ €μž₯ν•  곡간을 λ§Œλ“€μ–΄λ³΄μž.

from typing import TypedDict

class State(TypedDict):
    input: str
    data: str
    processed_data: str
    code: str
    is_error: bool
    result: str
  • input: μ‚¬μš©μžμ˜ μš”μ²­ (input query)
  • data: λ‘œλ“œλœ 데이터
  • processed_data: μ „μ²˜λ¦¬λœ 데이터
  • code: μƒμ„±λœ μ½”λ“œ
  • is_error: μ—λŸ¬ λ°œμƒ μ—¬λΆ€
  • result: μ‹€ν–‰ κ²°κ³Ό

각각의 μ“°μž„μƒˆκ°€ μžˆλŠ”λ°, μ•„λž˜μ—μ„œ μ‘°κΈˆμ”© μ„€λͺ…해보겠닀.


2️⃣ λ…Έλ“œ(Node) ν•¨μˆ˜ μ •μ˜

각 μž‘μ—… λ‹¨κ³„λ³„λ‘œ ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄λ³΄μž.

πŸ“₯ 데이터 λ‘œλ“œ λ…Έλ“œ

import pandas as pd

def load_data(state: State) -> State:
    data = pd.read_csv("data/example.csv")
    return {**state, "data": data}

데이터λ₯Ό κ°€μ Έμ˜€λŠ” κ°„λ‹¨ν•œ μ½”λ“œλ‹€. 데이터 μ‹œκ°ν™”λ₯Ό μœ„ν•΄μ„œ CSV νŒŒμΌμ„ κ°€μ Έμ˜¨λ‹€. 참고둜 ν•΄λ‹Ή example.csv νŒŒμΌμ€ Kaggle의 Titanic μƒμ‘΄μž 예츑 데이터λ₯Ό μ‚¬μš©ν–ˆλ‹€.

🧹 데이터 μ „μ²˜λ¦¬ λ…Έλ“œ

def preprocess_data(state: State) -> State:
    processed_data = state["data"].head(100)
    return {**state, "processed_data": processed_data}

데이터가 λ„ˆλ¬΄ 많으면 처리 μ‹œκ°„μ΄ κΈΈμ–΄μ§€λ‹ˆκΉŒ μƒμœ„ 100개만 μΆ”λ €λ³΄μž.

🧠 μ½”λ“œ 생성 λ…Έλ“œ

from langchain_openai import ChatOpenAI

def analyze_data(state: State) -> State:
    model = ChatOpenAI(model="gpt-4o-mini")
    prompt = f"{state['input']} 데이터: {state['processed_data']} 단, 였직 Code만 좜λ ₯ν•˜μ„Έμš”."
    response = model.invoke(prompt)
    return {**state, "code": response.content}

λͺ¨λΈμ—κ²Œ μ½”λ“œ 생성을 μš”μ²­ν•˜λ©΄, μ‚¬μš©μž μž…λ ₯κ³Ό μ „μ²˜λ¦¬λœ 데이터λ₯Ό 기반으둜 μ½”λ“œλ₯Ό λšλ”± λ§Œλ“€μ–΄μ€€λ‹€.

λ§Œμ•½ μ½”λ“œλ₯Ό ν•œλ²ˆλ§Œ λ§Œλ“ λ‹€λ©΄ μœ„μ²˜λŸΌ μ •μ˜ν•˜λ©΄ λ˜κ² μ§€λ§Œ, μš°λ¦¬λŠ” λ‹€μ΄μ–΄κ·Έλž¨μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ λ‹€μ‹œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜κΈ°λ‘œ ν–ˆμœΌλ‹ˆκΉŒ μ½”λ“œλ₯Ό 쑰금 더 μΆ”κ°€ν•΄μ•Ό ν•œλ‹€.

def analyze_data(state: State) -> State:
    # 데이터 뢄석 둜직 κ΅¬ν˜„
    model = ChatOpenAI(model="gpt-4o-mini")
    if state["is_error"] is False:
        # 일반 μ½”λ“œ μ œμž‘ ν”„λ‘¬ν”„νŠΈ
        response = model.invoke(
            (
                f"{state['input']} "
                "단, 였직 Code만 좜λ ₯ν•˜μ„Έμš”. "
                f"데이터: {state['processed_data']} "
            )
        )
    else:
        # Visualization λ…Έλ“œμ—μ„œ μ½”λ“œ μ—λŸ¬ λ°œμƒμ‹œ λ‹€μ‹œ μ½”λ“œλ₯Ό μƒμ„±ν•˜λŠ” ν”„λ‘¬ν”„νŠΈ
        response = model.invoke(
            (
                f"{state['input']} "
                "단, 였직 Code만 좜λ ₯ν•˜μ„Έμš”. "
                f"데이터: {state['processed_data']} "
                "====================== "
                f"[AI] {state['code']} "
                f"{state['result']} "
                "====================== "
                "μ‹€ν–‰ 쀑 λ°œμƒν•œ 버그λ₯Ό μ°Έκ³ ν•˜μ—¬ λ‹€μ‹œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ£Όμ„Έμš”."
            )
        )
    code = response.content
    return State(code=code)

λ§Œμ•½ 처음으둜 μ½”λ“œλ₯Ό μƒμ„±ν•˜λŠ” μƒνƒœλΌλ©΄ state["is_error"]κ°€ Falseμ΄λ―€λ‘œ 첫번째 if λ¬Έμ—μ„œ "일반 μ½”λ“œ μ œμž‘ ν”„λ‘¬ν”„νŠΈ"둜 LLM을 ν˜ΈμΆœν•œλ‹€.

ν•˜μ§€λ§Œ visualization λ…Έλ“œμ—μ„œ μ½”λ“œλ₯Ό μ‹€ν–‰ν–ˆλŠ”λ° errorκ°€ 뜬 상황이라면 state["is_error"]κ°€ Trueμ΄λ―€λ‘œ else λ¬Έμ—μ„œ "λ‹€μ‹œ μ½”λ“œλ₯Ό μƒμ„±ν•˜λŠ” ν”„λ‘¬ν”„νŠΈ"둜 LLM을 ν˜ΈμΆœν•œλ‹€. μ—¬κΈ°μ—λŠ” μ—λŸ¬ λ©”μ‹œμ§€μ™€ λ‹€μ‹œ μ½”λ“œλ₯Ό μž‘μ„±ν•˜λΌλŠ” 문ꡬ가 ν¬ν•¨λ˜μ–΄ μžˆλŠ” 것을 λ³Ό 수 μžˆλ‹€.

πŸ“Š μ‹œκ°ν™” λ…Έλ“œ

def visualization(state: State) -> State:
    import seaborn as sns
    import matplotlib.pyplot as plt
    from langchain_experimental.tools.python.tool import PythonAstREPLTool

    code = state["code"]
    repl_tool = PythonAstREPLTool(locals={"sns": sns, "plt": plt})
    
    try:
        exec_result = repl_tool.invoke(code)
        return {**state, "result": exec_result, "is_error": False}
    except Exception as e:
        return {**state, "result": str(e), "is_error": True}

AIκ°€ μƒμ„±ν•œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•΄μ„œ μ‹œκ°ν™”λ₯Ό 좜λ ₯ν•œλ‹€. μ‹€ν–‰ 쀑 λ¬Έμ œκ°€ 있으면 μ—λŸ¬ λ©”μ‹œμ§€κ°€ λ°˜ν™˜λœλ‹€.

πŸš€ Tips

PythonAstREPLTool이 μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜λŠ” Tool이닀. PythonREPLTool과의 차이점은 λ³΄μ•ˆμ΄ 쑰금 더 κ°•ν•˜λ‹€λŠ” 점이닀. PythonAstREPLTool은 μ½”λ“œ μ‹€ν–‰ 전에 좔상 ꡬ문 트리(Abstract Syntax Tree, AST) 뢄석을 톡해 잠재적으둜 μœ„ν—˜ν•œ μ½”λ“œλ‚˜ κΈˆμ§€λœ 연산을 사전에 차단할 수 μžˆλ‹€. 이λ₯Ό 톡해 μ™ΈλΆ€ 라이브러리 μ ‘κ·Ό, 파일 μ‹œμŠ€ν…œ μ‘°μž‘, λ¬΄ν•œ 루프 μ‹€ν–‰ λ“± μ˜λ„μΉ˜ μ•Šμ€ λ™μž‘μ„ λ°©μ§€ν•˜λ©° 보닀 μ•ˆμ „ν•œ μ½”λ“œ μ‹€ν–‰ ν™˜κ²½μ„ μ œκ³΅ν•œλ‹€. λ”°λΌμ„œ, μ‚¬μš©μž μž…λ ₯을 기반으둜 μ½”λ“œλ₯Ό μ‹€ν–‰ν•΄μ•Ό ν•˜λŠ” μ‹œλ‚˜λ¦¬μ˜€μ—μ„œ PythonAstREPLTool은 μ•ˆμ •μ„±κ³Ό λ³΄μ•ˆμ„ λͺ¨λ‘ 확보할 수 μžˆλŠ” μ μ ˆν•œ 선택지이닀.


3️⃣ ν”Œλ‘œμš° κ΅¬μ„±ν•˜κΈ°

각 λ…Έλ“œλ₯Ό μ—°κ²°ν•΄ 흐름을 μ™„μ„±ν•˜μž.

from langgraph.graph import StateGraph, START, END

def build_flow():
    flow = StateGraph(State)
    
    flow.add_node("load_data", load_data)
    flow.add_node("preprocess_data", preprocess_data)
    flow.add_node("analyze_data", analyze_data)
    flow.add_node("visualization", visualization)

    flow.add_edge(START, "load_data")
    flow.add_edge("load_data", "preprocess_data")
    flow.add_edge("preprocess_data", "analyze_data")
    flow.add_edge("analyze_data", "visualization")

    flow.add_conditional_edges(
        "visualization",
        lambda state: "analyze_data" if state["is_error"] else "end",
        {"analyze_data": "analyze_data", "end": END},
    )

    return flow.compile()

μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ μžλ™μœΌλ‘œ μ½”λ“œ 생성 λ‹¨κ³„λ‘œ λŒμ•„κ°€ μž¬μ‹€ν–‰ν•  수 μžˆλ‹€. flow.add_conditional_edgesμ—μ„œ "visualization" λ…Έλ“œλ‘œλΆ€ν„° μ΄μ–΄μ§€λŠ” conditional edgesλ₯Ό μ •ν•œλ‹€. state["is_error"]κ°€ True라면 "analyze_data" λ…Έλ“œλ‘œ 이어지고, False라면 END 둜 이어진닀.

πŸš€ Tips

LangGraph의 State κ°μ²΄λŠ” 직렬화λ₯Ό 거치기 λ•Œλ¬Έμ— pandas.DataFrame처럼 직렬화가 κΉŒλ‹€λ‘œμš΄ κ°μ²΄λŠ” 직접 μ €μž₯ν•˜κΈ° μ–΄λ €μšΈ 수 μžˆλ‹€. 특히 ν•΄λ‹Ή Graphλ₯Ό SubGraph둜 μΆ”κ°€ν•  λ•Œ 직렬화와 κ΄€λ ¨λœ Pydantic Errorκ°€ λ°œμƒν•œλ‹€λ©΄ μ•„λž˜ μ†”λ£¨μ…˜μ„ μ‹œλ„ν•΄λ³΄μž.
βœ… ν•΄κ²° 방법:

  • CSV, JSON λ“± λ¬Έμžμ—΄ ν˜•νƒœλ‘œ λ³€ν™˜ν•˜μ—¬ μ €μž₯:
    state["data"] = df.to_json()  # μ €μž₯ μ‹œ
    df = pd.read_json(state["data"])  # 뢈러올 λ•Œ
    μ΄λ ‡κ²Œ ν•˜λ©΄ μƒνƒœ μ „ν™˜ μ‹œ 데이터 손싀을 λ°©μ§€ν•˜λ©΄μ„œλ„ μ•ˆμ •μ μΈ μ²˜λ¦¬κ°€ κ°€λŠ₯ν•˜λ‹€.

4️⃣ μ‹€ν–‰ 및 κ²°κ³Ό 확인

if __name__ == "__main__":
    flow = build_flow()
    initial_state = State(
        input="Titanic 데이터λ₯Ό μ‹œκ°μ μœΌλ‘œ λ©‹μ§€κ²Œ λ³΄μ—¬μ€˜!",
        data="",
        processed_data="",
        code="",
        is_error=False,
        result=""
    )

    for event in flow.stream(initial_state):
        for step, output in event.items():
            print(f"\nπŸš€ STEP: {step}\n{output}\n")

βœ… μ‹€ν–‰ κ²°κ³Ό μ˜ˆμ‹œ

πŸš€ STEP별 진행 둜그

πŸš€ STEP: load_data
데이터 λ‘œλ“œ μ™„λ£Œ!

πŸš€ STEP: preprocess_data
μƒμœ„ 100개 데이터 μ „μ²˜λ¦¬ μ™„λ£Œ!

πŸš€ STEP: analyze_data
μ½”λ“œ 생성 성곡! πŸ“

πŸš€ STEP: visualization
μ‹œκ°ν™” 성곡 πŸŽ‰ (μ—λŸ¬ μ—†μŒ)

πŸ“„ μ‹€μ œ State 데이터 (Dump)

πŸ“₯ STEP: load_data

데이터 λ‘œλ“œ ν›„ State에 μ €μž₯된 λ‚΄μš©:

{
  'data': 
      PassengerId  Survived  Pclass                                               Name     Sex  ...  Fare Cabin  Embarked
0              1         0       3                            Braund, Mr. Owen Harris    male  ...  7.2500   NaN      S
1              2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...  female  ... 71.2833   C85      C
2              3         1       3                             Heikkinen, Miss. Laina  female  ...  7.9250   NaN      S
...          ...       ...     ...                                                ...     ...  ...     ...   ...    ...
890          891         0       3                                Dooley, Mr. Patrick    male  ...  7.7500   NaN      Q

[891 rows x 12 columns]
}

🧹 STEP: preprocess_data

μƒμœ„ 100개 데이터 μ „μ²˜λ¦¬ ν›„ State에 μ €μž₯된 λ‚΄μš©:

{
  'processed_data': 
      PassengerId  Survived  Pclass                                               Name  ...     Fare  Cabin Embarked
0              1         0       3                            Braund, Mr. Owen Harris  ...    7.2500   NaN       S
1              2         1       1  Cumings, Mrs. John Bradley (Florence Briggs Th...)  ...   71.2833    C85      C
2              3         1       3                             Heikkinen, Miss. Laina  ...    7.9250   NaN       S
...          ...       ...     ...                                                ...  ...      ...    ...     ...
99           100         0       2                                  Kantor, Mr. Sinai  ...   26.0000    NaN       S

[100 rows x 12 columns]
}

πŸ“Š STEP: analyze_data

뢄석 μ½”λ“œ 생성 ν›„ State에 μ €μž₯된 λ‚΄μš©:

{
  'code': 
"""
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# 데이터 ν”„λ ˆμž„ 생성
data = {
    'PassengerId': range(1, 101),
    'Survived': [0, 1, 1, 1, 0] * 20,
    'Pclass': [3, 1, 3, 1, 3] * 20,
    'Fare': [7.25, 71.2833, 7.925, 53.1, 8.05] * 20,
    'Embarked': ['S', 'C', 'S', 'S', 'S'] * 20
}
df = pd.DataFrame(data)

# μ‹œκ°ν™”
plt.figure(figsize=(12, 8))

# 1. μƒμ‘΄μž 수
plt.subplot(2, 2, 1)
sns.countplot(data=df, x='Survived', palette='pastel')
plt.title('Survival Count')

# 2. ν΄λž˜μŠ€λ³„ μƒμ‘΄μœ¨
plt.subplot(2, 2, 2)
sns.barplot(data=df, x='Pclass', y='Survived', palette='pastel')
plt.title('Survival Rate by Class')

# 3. μš”κΈˆ 뢄포
plt.subplot(2, 2, 3)
sns.histplot(data=df, x='Fare', kde=True, bins=30, color='purple')
plt.title('Fare Distribution')

# 4. νƒ‘μŠΉ 항ꡬ별 μƒμ‘΄μž 수
plt.subplot(2, 2, 4)
sns.countplot(data=df, x='Embarked', hue='Survived', palette='pastel')
plt.title('Survival Count by Embarked')

plt.tight_layout()
plt.show()
"""
}

πŸ–ΌοΈ STEP: visualization

μ‹œκ°ν™” κ²°κ³Ό 및 μ‹€ν–‰ μƒνƒœ:

{
  'result': '',
  'is_error': False
}

βœ… πŸ“ˆ μ‹œκ°ν™”λœ Figure:
μ‹œκ°ν™” κ²°κ³Ό

Titanic 데이터 μ‹œκ°ν™” κ²°κ³Ό:
πŸ§β€β™‚οΈ μƒμ‘΄μž 수 비ꡐ | 🏷️ ν΄λž˜μŠ€λ³„ μƒμ‘΄μœ¨
πŸ’° μš”κΈˆ 뢄포 | 🚒 νƒ‘μŠΉ 항ꡬ별 μƒμ‘΄μž 수


🌊 ν”Œλ‘œμš° μ‹œκ°ν™”

from IPython.display import display, Image
from main import build_flow

flow = build_flow()
display(Image(flow.get_graph(xray=False).draw_mermaid_png()))

  • 각 단계(load_data β†’ preprocess_data β†’ analyze_data β†’ visualization)μ—μ„œ 처리 κ²°κ³Όκ°€ State에 μ €μž₯λœλ‹€.
  • μ‹œκ°ν™” λ‹¨κ³„μ—μ„œ μ—λŸ¬κ°€ λ°œμƒν•˜λ©΄ analyze_data λ‹¨κ³„λ‘œ λ˜λŒμ•„κ°€ μ½”λ“œκ°€ μžλ™μœΌλ‘œ μˆ˜μ • 및 μž¬μ‹€ν–‰λœλ‹€.
  • μ •μƒμ μœΌλ‘œ 싀행될 경우 μœ„μ™€ 같은 μ‹œκ°ν™” κ²°κ³Όκ°€ 좜λ ₯λœλ‹€.

전체 μ½”λ“œ

from typing import TypedDict
import pandas as pd

from langchain_openai import ChatOpenAI
from langchain_experimental.tools.python.tool import PythonAstREPLTool
from langgraph.graph import StateGraph, START, END


# State μ •μ˜
class State(TypedDict):
    input: str
    data: str
    processed_data: str
    code: str
    is_error: bool
    result: str


# 각 Node에 ν•΄λ‹Ήν•˜λŠ” ν•¨μˆ˜ μ •μ˜
def load_data(state: State) -> State:
    # 데이터 λ‘œλ“œ 둜직 κ΅¬ν˜„
    data = pd.read_csv("./data/example.csv")
    return State(data=data)


def preprocess_data(state: State) -> State:
    # 데이터 μ „μ²˜λ¦¬ 둜직 κ΅¬ν˜„
    data = state["data"]
    processed_data = data.head(100)
    return State(processed_data=processed_data)


def analyze_data(state: State) -> State:
    # 데이터 뢄석 둜직 κ΅¬ν˜„
    model = ChatOpenAI(model="gpt-4o-mini")
    if state["is_error"] is False:
        response = model.invoke(
            (
                f"{state['input']} "
                "단, 였직 Code만 좜λ ₯ν•˜μ„Έμš”. "
                f"데이터: {state['processed_data']} "
            )
        )
    else:
        response = model.invoke(
            (
                f"{state['input']} "
                "단, 였직 Code만 좜λ ₯ν•˜μ„Έμš”. "
                f"데이터: {state['processed_data']} "
                "====================== "
                f"[AI] {state['code']} "
                f"{state['result']} "
                "====================== "
                "μ‹€ν–‰ 쀑 λ°œμƒν•œ 버그λ₯Ό μ°Έκ³ ν•˜μ—¬ λ‹€μ‹œ μ½”λ“œλ₯Ό μž‘μ„±ν•΄μ£Όμ„Έμš”."
            )
        )
    code = response.content
    return State(code=code)


def visualization(state: State) -> State:
    # μ½”λ“œ μ‹€ν–‰
    import seaborn as sns
    import matplotlib.pyplot as plt

    code = state["code"]
    processed_code = None
    if code.startswith("```"):
        processed_code = "\n".join(code.split("\n")[1:-1])
    else:
        processed_code = code

    python_repl_tool = PythonAstREPLTool(locals={"sns": sns, "plt": plt})
    try:
        exec_result = str(python_repl_tool.invoke(processed_code))
        return State(result=exec_result, is_error=False)
    except Exception as e:
        return State(result=e, is_error=True)


def build_flow():
    # flow init
    flow = StateGraph(State)

    # node μ •μ˜
    flow.add_node("load_data", load_data)
    flow.add_node("preprocess_data", preprocess_data)
    flow.add_node("analyze_data", analyze_data)
    flow.add_node("visualization", visualization)

    # edge μ—°κ²°
    flow.add_edge(START, "load_data")
    flow.add_edge("load_data", "preprocess_data")
    flow.add_edge("preprocess_data", "analyze_data")
    flow.add_edge("analyze_data", "visualization")

    # codintional edge μ—°κ²°
    def is_error_check(state: State) -> str:
        return "analyze_data" if state["is_error"] else "end"

    flow.add_conditional_edges(
        "visualization",
        is_error_check,
        {"analyze_data": "analyze_data", "end": END},
    )

    # compile
    return flow.compile()


if __name__ == "__main__":
    flow = build_flow()
    initial_state = State(
        input="λ‹€μŒ 데이터λ₯Ό μ „λ¬Έμ μœΌλ‘œ μ‹œκ°ν™”ν•˜μ—¬ ν•˜λ‚˜μ˜ Figure둜 ν‘œν˜„ν•΄μ£Όμ„Έμš”.",
        is_error=False,
        code="",
        reulst="",
    )
    for event in flow.stream(initial_state):
        for node_name, value in event.items():
            print(f"\n==============\nSTEP: {node_name}\n==============\n")
            print(value)

πŸ”” 마무리

이번 κΈ€μ—μ„œλŠ” LangGraphλ₯Ό ν™œμš©ν•œ 데이터 처리 νŒŒμ΄ν”„λΌμΈ ꡬ좕 방법과 단계별 μ‹œκ°ν™” 과정을 λ‹€λ€˜λ‹€.
각 단계별 μ—λŸ¬ 처리 흐름, 쑰건뢀 ν”Œλ‘œμš° ꡬ성, 그리고 State 직렬화 팁 등을 포함해 μ‹€μ „ ν”„λ‘œμ νŠΈμ— μ μš©ν•  수 μžˆλŠ” λ‚΄μš©μ„ μ •λ¦¬ν–ˆλ‹€. πŸš€


πŸ”— μΆ”κ°€ 자료 & μ½”λ“œ ν™•μΈν•˜κΈ°

πŸ“‚ 전체 μ½”λ“œ GitHubμ—μ„œ ν™•μΈν•˜κΈ°:
πŸ”— GitHub - Agentic Code Generator

πŸ“š μ°Έκ³  자료:
πŸ”— LangGraph Code Assistant Tutorial


πŸ’¬ μ½μ–΄μ£Όμ…”μ„œ κ°μ‚¬ν•©λ‹ˆλ‹€!
πŸ™Œ 도움이 λ˜μ…¨λ‹€λ©΄ ⭐️ μŠ€νƒ€λ₯Ό λˆŒλŸ¬μ£Όμ‹œλ©΄ 큰 힘이 λ©λ‹ˆλ‹€.
βœ… μΆ”κ°€λ‘œ 닀뀄보면 쒋을 μ£Όμ œλ‚˜ κΆκΈˆν•œ λ‚΄μš©μ΄ μžˆλ‹€λ©΄ λŒ“κΈ€λ‘œ μ•Œλ €μ£Όμ„Έμš”. κ΄€λ ¨ 글을 μž‘μ„±ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 😊

0개의 λŒ“κΈ€

κ΄€λ ¨ μ±„μš© 정보