본문 바로가기
공부 기록

Langchain으로 AI Agent 만들기 (1) : LLM 기본 Chain 만들기

by hihi0000 2025. 8. 27.
728x90

목차

     

     

     

    1. Langchain(랭체인)이란?

      • LLM(Large Language Model)을 실제 애플리케이션에 쉽게 연결하고 확장할 수 있도록 해주는 프레임워크  
      • 'Chain'이란? 여러 단계를 연결하는 것 (프롬프트 → LLM → 후처리 등)
      • DocumentLoader, TextSplitter, Retriever 등을 조합하여 RAG 등을 구현할 수 있음

    (출처: https://brunch.co.kr/@ywkim36/147)

    Langchain의 핵심 모듈

    이름 설명
    Prompt LLM 입력을 구조화
    - PromptTemplate: 변수 기반 프롬프트 템플릿
    - ChatPromptTemplate: 다중 메시지(시스템/유저/AI) 기반 템플릿
    ➡️동적으로 사용자 입력을 변수로 사용 가능
    LLM 텍스트 생성 엔진(언어 모델)
    - 클래스: ChatOpenAI, ChatAnthropic, ChatGoogleGenerativeAI 등 
    - 파라미터(온도, 최대 토큰 수 등) 제어 가능 
    Chains 여러 단계를 묶은 실행 흐름 
    - LLMChain: Prompt + LLM + Output 처리
    - SequentialChain: 여러 체인을 순서대로 실행 
    - RouterChain: 조건에 따라 다른 체인을 실행 
    Agents LLM이 상황에 따라 어떤 Tool을 사용할지 판단하고 실행 
    - Policy: 어떤 Tool을 언제 호출할지 결정(ReAct 패턴 등) 
    Tools Agent가 쓰는 외부 기능
    - 웹 검색(Tavily, DuckDuckGo), 계산기, 데이터베이스 조회, API 호출 등
    Memory 대화/컨텍스트 저장
    - ConversationBufferMemory: 그대로 기록
    - ConversationSummaryMemory: 요약하여 기록
    - VectorStoreRetrieverMemory: 임베딩 기반 검색
    ➡️멀티턴 대화에서 맥락 유지에 필수
    Retrievers & Vectorstores 검색 기반 RAG
    -
    Retrievers: 질의(query)에 맞는 문서를 찾아주는 모듈
    - Vectorstores: 문서를 벡터 임베딩으로 저장/검색하는 저장소(Chroma, FAISS 등 지원)
    Output Parsers 결과물 구조화
    - LLM의 자유로운 텍스트 출력을 원하는 형식(JSON, 리스트, 구조체)으로 변환
    - StrOutputParser: 문자열 그대로
    - JsonOutputParser: JSON 강제 변환
    - PydanticOutputParser: Pydantic 모델 기반 구조화

     

     

     

     

    2. Prompt Template

    from langchain_core.prompts import PromptTemplate
    
    template = "당신의 이름은 {name}이고, 직업은 {job}입니다. 자기소개를 해주세요."
    prompt = PromptTemplate.from_template(template)
    • {name}, {job}: 변수를 위한 자리 표시자(placeholder)
    • from_template: 자동으로 변수 이름을 추출해 input_variables를 생성
    prompt.format(name="홍길동", job="개발자")
    출력:
    당신의 이름은 홍길동이고, 직업은 개발자입니다. 자기소개를 해주세요.

     

     

     

    Chat Prompt Template

    • 대화형(Chat) 모델 전용 프롬프트
    • ChatGPT와 같은 모델은 "역할(role) + 메시지(content)"와 같은 구조를 이해
    • system: 모델의 성격, 규칙 설정
    • user: 사용자 입력
    • assistant: 이전 모델 응답(선택)

    ➡️ 단순 문자열 입력이 아닌, 리스트로 구조화된 입력을 만들 수 있도록 해주는 것이 Chat Prompt Template

    from langchain_core.prompts import ChatPromptTemplate
    
    # 대화형 프롬프트 템플릿 정의
    chat_prompt = ChatPromptTemplate.from_messages([
        ("system", "당신은 친절한 한국어 선생님입니다."),
        ("user", "다음 문장을 영어로 번역해 주세요: {sentence}")
    ])
    
    # 변수 채우기
    final_prompt = chat_prompt.format_messages(sentence="오늘 날씨가 참 좋네요.")
    print(final_prompt)

     

    실제 실행 예시 (Gemini API 연결)

    import os
    os.environ["GOOGLE_API_KEY"] = "API KEY 입력"
    
    from langchain_google_genai import ChatGoogleGenerativeAI
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    
    chat_prompt = ChatPromptTemplate.from_messages([
        ("system", "당신은 친절한 AI 자기소개 도우미입니다."),
        ("user", "이름: {name}, 직업: {job} → 자기소개를 작성해 주세요.")
    ])
    
    # Gemini 모델 선택: "gemini-1.5-flash" (빠름/저렴) or "gemini-1.5-pro" (정확도↑)
    llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0.7)
    
    parser = StrOutputParser()
    chain = chat_prompt | llm | parser
    
    print(chain.invoke({"name": "홍길동", "job": "개발자"}))
    출력:
    안녕하세요, 저는 홍길동입니다. 개발자로서 소프트웨어 개발에 대한 열정을 가지고 있습니다.  (이후 경력이나 특정 분야, 혹은 강점을 추가하면 좋습니다. 예시는 아래와 같습니다.)
    
    **예시 1 (경력 중심):**  저는 [경력기간] 동안 [업계/분야]에서 개발자로 일해 왔으며, [주요 프로젝트 또는 기술] 경험을 바탕으로 [자신의 강점 또는 성취]를 이루었습니다.  최근에는 [최근 프로젝트 또는 관심 분야]에 집중하고 있으며,  끊임없는 학습과 성장을 통해 더욱 발전된 개발자가 되기 위해 노력하고 있습니다.
    
    **예시 2 (기술 중심):** 저는 [주요 사용 언어/기술]을 활용하여 [웹/모바일/데스크탑 등] 개발에 능숙합니다. 특히 [특정 기술 또는 분야]에 대한 전문성을 가지고 있으며,  [구체적인 프로젝트 경험 또는 성과]를 통해 실력을 증명해왔습니다.  새로운 기술을 배우고 적용하는 것을 즐기며, 효율적이고 효과적인 코드 작성을 중요하게 생각합니다.
    
    **예시 3 (개인적인 특성 강조):** 저는 문제 해결 능력이 뛰어나고, 꼼꼼하고 체계적인 개발 프로세스를 중요하게 생각합니다.  팀워크를 중시하며, 동료와의 협력을 통해 최고의 결과를 만들어내는 것을 목표로 합니다.  끊임없이 배우고 성장하며,  더 나은 개발자가 되기 위해 노력하는 열정적인 개발자입니다.
    
    
    자신에게 맞는 예시를 선택하여  괄호 안에 구체적인 내용을 채워 넣으면 더욱 효과적인 자기소개가 될 것입니다.  어떤 자리에 자기소개를 할 것인지에 따라 내용을 조정하는 것도 중요합니다.

     

     

     

     

    3. Chain 실행

    • 단일 LLM 호출 체인: "Prompt  → LLM → Parser (선택)" 의 직렬 파이프라인을 한 번 실행해 결과를 얻는 구조
    단계 설명
    Prompt 입력 변수를 끼워 넣어 LLM이 이해하기 쉬운 문자열을 만듦
    LLM 한 번 호출(invoke)로 답변 생성
    Parser (선택) LLM 출력을 문자열/JSON/스키마 등으로 확실한 타입으로 변환

    ➡️실행 흐름은 보통 prompt | llm | parser 형태의 파이프 연산으로 표현

     

    import os
    os.environ["GOOGLE_API_KEY"] = "API KEY 입력"
    
    #from langchain_openai import ChatOpenAI
    from langchain_google_genai import ChatGoogleGenerativeAI
    from langchain_core.prompts import ChatPromptTemplate
    from langchain_core.output_parsers import StrOutputParser
    
    # 1) 프롬프트
    prompt = ChatPromptTemplate.from_messages([
        ("system", "당신은 간결하고 친절한 한국어 설명가입니다."),
        ("user", "주제: {topic}\n한 문단으로 핵심만 설명해 주세요.")
    ])
    
    # 2) LLM (온도 낮게: 일관된 출력)
    llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0.0)
    
    # 3) 파서 (문자열 그대로 받기)
    parser = StrOutputParser()
    
    # 4) 단일 호출 체인
    chain = prompt | llm | parser
    
    # chain.invoke(input_dict): 단일 LLM 호출
    print(chain.invoke({"topic": "강화학습"}))
    출력:
    강화학습은 에이전트가 환경과 상호작용하며 보상을 최대화하는 방법을 학습하는 머신러닝 기법입니다.  에이전트는 시행착오를 통해 최적의 행동 전략을 찾아가며,  보상을 많이 받는 행동을 더 자주 선택하도록 학습합니다.  마치 사람이 훈련을 통해 특정 행동을 익히는 것과 유사합니다.

     

     

    Few-Shot 예시 포함 단일 호출

    fewshot = ChatPromptTemplate.from_messages([
        ("system", "당신은 용어를 비전문가에게 비유 중심으로 쉽게 설명합니다."),
        ("user", "용어: 오토리그레시브(autoregressive)"),
        ("assistant", "한 글자씩 이어 쓰는 시처럼, 이전 글을 바탕으로 다음 글자를 결정하는 방식입니다."),
        ("user", "용어: {term}")
    ])
    
    (llm := ChatGoogleGenerativeAI(model="gemini-1.5-flash", temperature=0.7))
    chain = fewshot | llm | StrOutputParser()
    print(chain.invoke({"term": "어텐션(attention)"}))
    출력:
    어텐션은 마치 사람이 책을 읽을 때처럼, 중요한 부분에 집중하는 능력이라고 생각하면 됩니다.  책 전체를 다 똑같이 보는 게 아니라, 현재 읽고 있는 문장을 이해하는 데 필요한 단어나 문장에 더 많은 '주의'를 기울이는 거죠.  컴퓨터도 마찬가지로, 많은 정보 중에서 현재 작업에 가장 중요한 정보에 집중해서 처리하는 기술이 어텐션입니다.  예를 들어, 번역을 할 때 어텐션은 문장의 특정 단어에 집중하여 번역의 정확도를 높입니다.  마치 중요한 단어에 형광펜을 긋고 읽는 것과 같다고 생각하면 쉬워요.

     

     

    에러, 품질 체크 방법

    • 온도(temperature): 0에 가까울 수록 일관성이 높고 창의력이 낮음
    • 토큰 길이: max_tokens를 적절히 지정(너무 작으면 중간에 끊김)
    • 가드레일: 스키마 파서, 예/아니오 등 제약 지시문을 프롬프트에 명확히 하기
    • 디버그: print(prompt.format(...))로 실제 프롬프트 확인

    ➡️상태(State), 분기, 반복, 툴 호출 등 플로우 제어가 필요한 경우 LanGraph 사용

     

     

     

     

     

    728x90

    '공부 기록' 카테고리의 다른 글

    Langchain으로 AI Agent 만들기 (2) : Tool 연결하기  (1) 2025.08.27