2. about CHAIN

이우철·2026년 2월 14일

체인은 여러 컴포넌트(프롬프트, LLM, 파서 등)를 연결하여 복잡한 작업을 수행하는 파이프라인임.

입력 → 프롬프트 → LLM → 출력 파서 → 결과

why?

  • 재사용성: 한 번 만든 체인을 여러 곳에서 사용
  • 가독성: 복잡한 로직을 단순하게 표현
  • 유지보수: 각 단계를 독립적으로 수정 가능

2-1. LCEL 기본 문법
LangChain 1.0부터 도입된 LCEL은 파이프(|) 연산자로 컴포넌트를 연결

basic example :

# week2_01_basic_chain.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.output_parsers import StrOutputParser

# LLM 초기화
llm = OllamaLLM(model="qwen3:1.7b", temperature=0.7)

# 프롬프트 템플릿
prompt = PromptTemplate(
    input_variables=["product"],
    template="{product}의 특징을 3가지만 간단히 설명해주세요."
)

# 출력 파서
output_parser = StrOutputParser()

# 체인 구성 (LCEL 방식)
chain = prompt | llm | output_parser

# 실행
result = chain.invoke({"product": "개발자"})
print(result)

구조 설명:

입력 dict → PromptTemplate → LLM → StrOutputParser → 문자열

2-2. 순차체인(Sequential Chain)

여러 단계를 거쳐 결과를 만드는 체인임.

# week2_02_sequential_chain.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.output_parsers import StrOutputParser

llm = OllamaLLM(model="qwen3:1.7b", temperature=0.7)

# 1단계: 주제 생성
topic_prompt = PromptTemplate(
    input_variables=["interest"],
    template="{interest}에 관련된 블로그 주제를 1개만 제안해주세요. 주제만 출력하세요."
)

# 2단계: 개요 작성
outline_prompt = PromptTemplate(
    input_variables=["topic"],
    template="{topic}에 대한 블로그 글 개요를 3개 항목으로 작성해주세요."
)

# 체인 구성
chain = (
    topic_prompt 
    | llm 
    | StrOutputParser() 
    | {"topic": lambda x: x}  # 결과를 다음 프롬프트의 input으로 전달
    | outline_prompt 
    | llm
)

# 실행
result = chain.invoke({"interest": "인공지능"})
print(result)

응답 :


(llm_env) PS C:\dev\llm> & C:/dev/llm/llm_env/Scripts/python.exe c:/dev/llm/2-2.sequencial_chain.py
### 1. **인공지능의 윤리적 한계: 과학적 탐색과 사회적 논의의 핵심 문제**  
   - **AI의 윤리적 한계**: 기술의 발전이 가져오는 윤리적 갈등을 분석(예: 알고리즘의 편향, 데이터의 민감성, 인간의 권리 보호). 과학적 관점에서 AI의 불확실성과 위험성을 점검.      
   - **사회적 논의의 중요성**: 대중의 인식 부족과 윤리적 인식의 차이가 초래할 수 있는 사회적 문제(예: AI의 대규모 사용에 대한 반발, 기술의 불균형한 확산). 과학적 연구의 사회적 맥락을 반영한 대화의 필요성.
   - **과학적 토론의 한계**: 이론적 모델의 현실성과 실제 적용의 차이, AI의 윤리적 가치 평가에 대한 학계의 갈등. 과학적 탐구의 범위와 제한성에 대한 경고.

---

### 2. **인공지능의 기회: 과학적 진보와 사회적 혁명의 결합**
   - **AI의 혁신적 기능**: 의료 분야(예: 진단 정확도 향상), 환경 문제 해결(예: 탄소 배출 예측), 교육 기회 확대(예: 맞춤형 학습 시스템)에 대한 과학적 기대.
   - **사회적 혁명의 가능성**: AI가 인간의 역할을 재정의할 수 있는 잠재력(예: 자동화로의 전환, 인간의 창의성의 강화). 과학적 발전과 사회적 변화의 동반성을 강조.
   - **공동 혁신의 필요성**: 기술 개발과 윤리적 대화의 조화를 위한 정부, 학계, 대중의 협력. AI의 사회적 기능을 확장하는 데 있어 실용적 접근의 중요성.

---

### 3. **과학적 이해와 사회적 대화의 통합: AI의 윤리적 발전 방향**
   - **과학적 이해의 핵심**: AI의 기초 과학적 원리(예: 머신러닝, 네트워크 분석)를 기반으로 한 이해의 필요성. 과학적 탐구의 결과와 윤리적 적용의 차이를 반영한 분석.
   - **사회적 대화의 역할**: 대중의 참여를 통한 AI 윤리의 합리적 정의(예: 공정성, 편향 최소화). 사회적 대화가 기술의 윤리적 적용에 미치는 영향과 그 예시.
   - **정책과 교육의 조화**: AI 윤리의 정책 수립과 학교 교육의 조정을 통해 대중의 인식 개선. 과학적 연구와 사회적 논의의 통합을 통해 AI의 윤리적 발전의 기반 마련.

흐름:
"인공지능" → 주제 생성 → "AI의 미래" → 개요 작성 → 최종 개요

2-3. 병렬 체인 (Parallel Chain)
여러 작업을 동시에 실행하고 결과를 합침.

# week2_03_parallel_chain.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.runnables import RunnableParallel

llm = OllamaLLM(model="qwen3:1.7b", temperature=0.7)

# 장점 분석 프롬프트
pros_prompt = PromptTemplate(
    input_variables=["topic"],
    template="{topic}의 장점 2가지를 간단히 설명해주세요."
)

# 단점 분석 프롬프트
cons_prompt = PromptTemplate(
    input_variables=["topic"],
    template="{topic}의 단점 2가지를 간단히 설명해주세요."
)

# 병렬 체인 구성
parallel_chain = RunnableParallel(
    pros=pros_prompt | llm,
    cons=cons_prompt | llm
)

# 실행
result = parallel_chain.invoke({"topic": "바이브코딩"})
print("=== 장점 ===")
print(result["pros"])
print("\n=== 단점 ===")
print(result["cons"])

응답

(llm_env) PS C:\dev\llm> & C:/dev/llm/llm_env/Scripts/python.exe c:/dev/llm/2_3.parallel_chain.py
=== 장점 ===
바이브코딩(이하 바이너)의 주요 장점은 다음과 같습니다:  

1. **효율적인 저장 및 전송**
   바이너는 0과 1 두 상태로만 표현되어 데이터의 크기가 작고, 저장 용량이 절약됩니다. 이로 인해 정보 전송 시 전력 소모와 처리 시간이 줄어듭니다.

2. **고정된 구조와 일관성**
   0과 1의 명확한 규칙이 있어 데이터를 쉽게 해석하고 처리할 수 있으며, 다양한 기술과 호환성이 뛰어납니다.

=== 단점 ===
바이브코딩의 단점 중 두 가지는 다음과 같습니다:

1. **복잡한 인코딩 과정**
   생물학적 데이터는 구조화되지 않거나 다양한 형식으로 표현될 수 있어, 디지털 시스템과 호환성을 유지하기 위한 복잡한 알고리즘을 사용해야 하므로, 시간과 자원이 많이 소요됩니다.   

2. **데이터 손실 또는 오류의 위험**
   생물학적 데이터는 쉽게 오염되거나 손실될 수 있어, 인코딩 과정에서 품질을 유지하는 데 어려움이 생기며, 결과적으로 신뢰성을 떨어뜨릴 수 있습니다.

구조:

         ┌─→ 장점 분석 → pros
입력 ──→ ┤
         └─→ 단점 분석 → cons

2-4. 조건부 체인 (Conditional Chain)
입력에 따라 다른 경로로 실행

# week2_04_conditional_chain.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.runnables import RunnableLambda

llm = OllamaLLM(model="qwen3:1.7b", temperature=0.7)

# 기술 질문 프롬프트
tech_prompt = PromptTemplate(
    input_variables=["question"],
    template="기술 전문가로서 답변: {question}"
)

# 일반 질문 프롬프트
general_prompt = PromptTemplate(
    input_variables=["question"],
    template="친절하게 답변: {question}"
)

# 라우팅 함수
def route_question(input_dict):
    question = input_dict["question"].lower()
    # 기술 관련 키워드 체크
    tech_keywords = ["코드", "프로그래밍", "api", "버그", "에러"]
    
    if any(keyword in question for keyword in tech_keywords):
        return tech_prompt | llm
    else:
        return general_prompt | llm

# 조건부 체인
chain = RunnableLambda(route_question)

# 실행
result1 = chain.invoke({"question": "파이썬 코드 에러를 어떻게 디버깅하나요?"})
print("기술 질문 답변:")
print(result1)

print("\n" + "="*50 + "\n")

result2 = chain.invoke({"question": "오늘 서울 날씨가 좋네요."})
print("일반 질문 답변:")
print(result2)

응답 :
(llm_env) PS C:\dev\llm> & C:/dev/llm/llm_env/Scripts/python.exe c:/dev/llm/2_4.conditional_chain.py
기술 질문 답변:
파이썬 코드 디버깅은 오류를 이해하고 해결하는 데 핵심적인 역할을 합니다. 다음은 디버깅을 위한 체계적인 접근 방법과 기법입니다:


1. 오류 메시지 이해

  • 오류 메시지 확인:
    오류 메시지(예: NameError, TypeError, IndexError)는 오류의 유형과 위치를 알려줍니다.
    예:
    print(my_list[5])  # IndexError
  • 추가 정보:
    traceback을 사용해 오류 발생 위치와 호출 경로를 확인합니다.
    import traceback
    traceback.print_exc()

2. 기본적인 디버깅 기법

  • print 사용:
    변수 값이나 함수 호출을 실시간으로 확인합니다.
    print("현재 값:", my_var)
  • logging 모듈:
    더 세부적인 로그를 기록하여 오류 원인을 추적합니다.
    import logging
    logging.basicConfig(level=logging.DEBUG)
    logging.debug("변수 값: %s", my_var)
  • 단계별 실행:
    코드의 실행을 단계로 나누어 보는 방법(예: pdb 드로퍼).

3. 디버깅 도구 활용

  • pdb (Python Debugger):
    명령어로 코드를 실행하고 디버깅합니다.
    import pdb; pdb.set_trace()
  • ipdb:
    더 복잡한 코드에서 사용할 수 있는 편한 인터프리터.
  • step / next:
    코드 실행 단계별로 움직이기 (예: step은 루프 내부를 실행, next는 루프 외부를 실행).

4. 오류 원인 분석

  • 형식 오류:
    예: int("abc")ValueError.
    • 해결: int(input("입력: "))으로 입력 처리.
  • 데이터 유형 오류:
    예: my_list[5]IndexError 발생.
    • 해결: len(my_list) <= 5 검사.
  • 외부 의존성:
    모듈 불러오기 오류(예: ImportError) → import 명령어 확인.

5. 코드 구조 분석

  • 분리된 코드 단위:
    오류가 발생하는 부분을 분리해 확인(예: if 조건, 루프 내부).
  • 모듈/함수 분리:
    복잡한 코드를 작은 단위로 나누어 디버깅.
  • 변수 값 확인:
    print() 또는 logging을 통해 변수 값 추적.

6. 고급 기법

  • __file__ 변수:
    오류 발생 파일을 확인합니다.
    import sys
    print(sys.modules['__main__'].__file__)
  • sys 모듈 사용:
    시스템 설정, 버전, 경로 등을 확인.
  • __traceback__:
    오류 발생 시점의 호출 경로를 확인.

7. 실무 팁

  • 코드 가독성:
    주석을 적절히 사용해 오류 원인을 명확히 표현.
  • 환경 설정:
    PYTHONPATH 등을 확인해 모듈 불러오기 문제를 해결.
  • 테스트:
    단위 테스트(예: unittest)로 오류를 사전에 검증.

8. 예제: 오류 발생 시 디버깅

def calculate_sum(a, b):
    return a + b

try:
    result = calculate_sum(5, "abc")
    print("결과:", result)
except TypeError as e:
    print("타입 오류:", e)
  • 오류 발생 시:
    TypeError: unsupported operand type(s) for +: 'int' and 'str'
    • print(type(a))int 확인.
    • print(a + b)Traceback으로 오류 위치 확인.

결론

디버깅은 오류를 이해하고 해결하는 핵심 과정입니다.

  • 오류 메시지코드 분석도구 활용실무 팁을 체계적으로 적용하면 효과적입니다.
  • 코드 가독성환경 확인은 디버깅의 성공률을 높입니다.

==================================================

일반 질문 답변:
안녕하세요! 오늘 서울의 날씨가 정말 좋네요 😊
고마워요! 오늘은 어울리고 싶은 날이에요.
어떤 일이든 도와드릴 수 있어요! 😊

2-5 실전 복합체엔 예제 : 이메일 자동 분류 및 답변

  • 이메일 처리 시스템
# week2_05_email_system.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.runnables import RunnableParallel, RunnableLambda
from langchain_core.output_parsers import StrOutputParser

llm = OllamaLLM(model="qwen3:1.7b", temperature=0.7)

# 1단계: 이메일 분류
classify_prompt = PromptTemplate(
    input_variables=["email"],
    template="""다음 이메일을 '기술지원', '영업', '일반문의' 중 하나로 분류하세요.
분류만 출력하세요.

이메일: {email}
분류:"""
)

# 2단계: 카테고리별 답변 생성
tech_support_prompt = PromptTemplate(
    input_variables=["email"],
    template="기술지원팀으로서 다음 이메일에 답변하세요 (2문장):\n\n{email}"
)

sales_prompt = PromptTemplate(
    input_variables=["email"],
    template="영업팀으로서 다음 이메일에 답변하세요 (2문장):\n\n{email}"
)

general_prompt = PromptTemplate(
    input_variables=["email"],
    template="고객센터로서 다음 이메일에 답변하세요 (2문장):\n\n{email}"
)

# 분류 결과에 따라 답변 생성
def generate_response(input_dict):
    category = input_dict["category"].strip().lower()
    email = input_dict["email"]
    
    if "기술" in category:
        prompt = tech_support_prompt
    elif "영업" in category:
        prompt = sales_prompt
    else:
        prompt = general_prompt
    
    chain = prompt | llm
    #return chain.invoke({"email": email})
    response = chain.invoke({"email": email})

    # 카테고리와 답변을 함께 반환 
    return { 
        "category": category, 
        "response": response 
    }

# 전체 체인 구성
email_chain = (
    {
        "email": lambda x: x["email"],
        "category": classify_prompt | llm | StrOutputParser()
    }
    | RunnableLambda(generate_response)
)

# 테스트
test_emails = [
    "로그인이 안 됩니다. 비밀번호를 초기화 할 수 있나요.",
    "귀사 솔루션(브릴리언트)의 가격과 할인 정책이 궁금합니다."",
    "영업시간이 어떻게 되나요?"
]

for i, email in enumerate(test_emails, 1):
    print(f"\n{'='*60}")
    print(f"이메일 {i}: {email}")
    print(f"{'='*60}")
    result = email_chain.invoke({"email": email})
    print(f"분류: {result['category']}")
    print(f"답변:\n{result['response']}")

응답 :

(llm_env) PS C:\dev\llm> & C:/dev/llm/llm_env/Scripts/python.exe c:/dev/llm/2_5.email_system.py

============================================================
이메일 1: 로그인이 안 됩니다. 비밀번호를 초기화 할 수 있나요.
============================================================
분류: 기술지원
답변:
로그인 문제는 원인을 확인해 보시면 좋습니다. 비밀번호 초기화는 로그인 페이지에서 '비밀번호 재설정' 또는 '아이디를 이용한 패스워드 재설정' 기능을 통해 진행할 수 있습니다.

============================================================
이메일 2: 귀사 솔루션(브릴리언트)의 가격과 할인 정책이 궁금합니다.
============================================================
분류: sales
답변:
브릴리언트의 솔루션 가격은 고객님의 구매량 및 기간에 따라 달라질 수 있으며, 추가 할인 조건이나 특별한 할인 정책도 제공하고 있습니다. 문의 사항은 고객 지원팀에 연락하시면 더욱 세 부적으로 안내해 드리겠습니다.

============================================================
이메일 3: 영업시간이 어떻게 되나요?
============================================================
분류: 분류: 일반문의
답변:
고객센터 영업시간은 월~금 9시부터 17시까지 운영됩니다. 문의사항은 언제든지 연락해 주세요!

2-6. 실전 디버깅 팁 (체인 디버깅)

# week2_06_debugging.py
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
from langchain_core.runnables import RunnablePassthrough
from langchain_core.runnables import RunnableLambda

llm = OllamaLLM(model="qwen3:1.7b")

prompt = PromptTemplate(
    input_variables=["topic"],
    template="{topic}에 대해 설명해주세요."
)

# 디버깅용 함수
def print_input(x):
    print(f"🔍 중간 결과: {x}")
    return x

# 체인에 디버깅 포인트 추가
chain = (
    prompt 
    | RunnableLambda(print_input)  # 프롬프트 결과 확인
    | llm 
    | RunnableLambda(print_input)  # LLM 결과 확인
)

chain.invoke({"topic": "랭체인"})

응답 :

(llm_env) PS C:\dev\llm> & C:/dev/llm/llm_env/Scripts/python.exe c:/dev/llm/2_6.debug_chain.py
🔍 중간 결과: text='랭체인에 대해 설명해주세요.'
🔍 중간 결과: 랭체인은 게임 **레전드 오브 랜드(Legend of Legends)**에서 사용되는 **랭킹 시스템**의 일환으로, 플레이어가 자신의 랭킹을 점진적으로 상승시키는 과정을 의미합니다. 이 시스템은 플레이어가 게임 내에서의 성능을 기반으로 랭킹을 업데이트하고, 최고 랭킹(1000)에 도달하는 과정을 포함합니다.

### 주요 내용:
1. **랭킹 시스템**:
   - 게임 내에서 플레이어의 성능(예: 스타크래프트, 레전드 오브 랜드 등)을 기준으로 랭킹을 결정합니다. 랭킹은 1에서 1000까지 점진적으로 상승합니다.
   - 랭킹은 플레이어의 기술, 채팅, 스타크래프트, 그리고 게임 내에서의 성능을 종합적으로 평가합니다.

2. **랭체인(랭킹 체인)**:
   - 랭킹 시스템 내에서 플레이어가 점진적으로 상승하는 경로를 의미합니다. 예를 들어, 1 → 2 → 3 → ... → 1000으로 이어지는 과정입니다.
   - 랭킹 체인은 플레이어가 최고 랭킹(1000)에 도달하기 위해 필요한 단계를 포함합니다.

3. **특징**:
   - 플레이어는 랭킹 체인을 통해 게임 내에서의 랭킹을 점진적으로 상승시킬 수 있습니다.
   - 랭킹 체인은 게임의 전반적인 성능과 플레이어의 기술, 전략, 채팅 등을 종합적으로 반영합니다.

### 예시:
- 플레이어가 1 → 2 → 3 → ... → 1000 랭킹으로 상승하는 과정은 랭체인입니다.
- 랭체인은 게임 내에서의 성능과 전략적 능력을 반영하여, 플레이어의 성취도를 평가합니다.

### 결론:
랭체인은 게임 내에서 플레이어가 점진적으로 상승하는 랭킹 경로를 의미하며, 이는 게임의 기술, 전략, 채팅, 그리고 성능을 종합적으로 평가하는 시스템입니다. 최고 랭킹(1000)에 도달하는 과정은 랭체인의 핵심입니다.
profile
개발 정리 공간 - 업무일때도 있고, 공부일때도 있고...

0개의 댓글