[인공지능사관학교: 자연어분석A반] JavaScript (10) / LangChain (3) / 포트폴리오 특강

Suhyeon Lee·2025년 8월 28일

목차

Ⅰ. 오전 수업
	A. 1교시
		1. 지난 시간 복습
		2. fetch api
	B. 2교시
		1. fetch api (cont.)
		2. 카카오지도 api
	C. 3교시
		1. TIPS / FEEDBACK
		2. JS 수업 전체 복습

Ⅱ. 오후 수업
	A. 4교시: LangChain
		1. 지난 시간 복습
		2. 메시지 출력 양식 설정
		3. 한 개의 변수에 두 개 이상의 값을 넣고 싶을 때
	B. 5교시: LangChain
		1. 문서 요약 시스템 만들어보기
		2. 데이터를 효과적으로 전달하는 방법
	C. 6교시: LangChain
		1. 간단 챗봇 만들어 보기
		2. LangChain의 다양한 활용법

Ⅲ. CAREER UP
	A. Goal Tracker, TIL 정리
	B. 포트폴리오 특강

Ⅳ. 하루 돌아보기




Ⅰ. 오전 수업

A. 1교시

1. 지난 시간 복습

  • 통신: 클라이언트의 요청을 받아서 서버가 해당하는 요청을 처리해 응답하는 하나의 구조
    • 카카오톡 메시지를 보낸다는 행위를 함 → 거기에 맞는 응답을 서버가 보내줌
  • 동기 vs. 비동기
    • 화면 전체 바뀌면 동기, 일부만 바뀌면 비동기 이용한 거라 생각하면 쉬움
      • 동기 통신은 서버가 클라이언트에게 넘겨주려는 데이터도 많고 화면 자체가 바뀌어야 하니까 로딩이 발생함
      • 화면 전체가 바뀌다 보니까 화면이 만들어지기 전까지는 사용자가 다른 행위를 할 수 없음
      • 동기 통신을 대표하는 태그가 바로 form 태그
      • 비동기 통신은 특정 부분의 데이터반 받아오기 때문에 적은 양의 데이터를 처리하고 그만큼 빠름
      • 비동기 통신은 요청에 대한 응답이 즉시 처리되지 않아도, 그 대기 시간동안 또 다른 요청을 보낼 수 있음(요청이 진행되더라도 응답을 기다리지 않고 다음 코드 수행)
      • 채팅, 실시간으로 만들어내는 행위들은 모두 비동기 통신으로 구현되는 것

동기 통신은 요청 후 응답을 기다려 다음 작업을 수행하는 반면, 비동기 통신은 요청을 보내놓고 응답을 기다리지 않고 다음 작업을 계속 수행하는 방식입니다(현재 실행중인 작업을 멈추지 않고 다른 작업을 병렬적으로 수행). 동기 통신은 순차적인 진행으로 작업이 끝날 때까지 기다려야 하는 반면, 비동기 통신은 작업이 동시에 진행될 수 있어 효율적입니다.
→ 더 자세한 내용은 inpa 블로그 읽어보기

function taskA(a, b) {
    return setTimeout(() => {
        const res = a + b;
        return res;
    }, 2000);
}

const res = taskA(10, 20);
console.log(res); // Timeout { _idleTimeout: 2000, .. }
  • a와 b를 더한 값이 아닌 Timeout이 반환
    • 반환값이 함수 setTimeout에서 인수로 전달한 콜백 함수가 반환하는게 아니기 때문
function taskA(a, b, cb) {
    // 2. 비동기 작업으로 setTimeout 내부 코드가 2초 뒤에 실행된다. (Non-Blocking)
    setTimeout(() => {
        // 4. 비동기 작업을 수행한다.
        const res = a + b;

        // 5. 비동기 작업이 완료되면 콜백 함수를 호출해 연산의 결과값을 인수로 전달한다.
        cb(res); 
    }, 2000);
}

// 1. taskA 함수를 호출한다. 이때 인수로 화살표 함수로 만든 콜백 함수를 전달한다.
taskA(10, 20, (res) => {
    // 6. taskA의 비동기 작업이 종료되고, 콜백 함수가 호출되어 실행된다.
    console.log("TASK A RESULT: ", res); // 30
});

// 3. taskA 내부 함수가 비동기 작업이므로 해당 작업의 종료를 기다리지 않고 먼저 출력된다.
console.log("task B RESULT");
  • 비동기 작업을 처리하는 setTimeout 안에서 a와 b를 더하고, 더한 결과값인 res를 콜백 함수에서 처리
  • 비동기 작업 함수의 매개변수에 콜백 함수를 전달
  • SPA vs. MPA ★ → 웹 개발에 관심이 있다면 읽어보기
  • jQuery 문법으로 데이터 요청해보기: ajex 함수 비동기 통신 실행
    • jQuery 등장 전에 사용하던 XMLHttpRequest와 비교해 보면 ajax의 장점이 잘 보임
      • jQuery ajax는 객체 하나에 다 묶어서 쓰니까 간단하고 편함
    • error: 자바스크립트 AJAX의 error 함수는 통신 실패 시 실행되며, jqXHR, textStatus, errorThrown 세 가지 주요 파라미터를 받습니다. 이 파라미터들은 통신 실패의 원인을 파악하는 데 사용되는데, jqXHR 객체는 서버 응답과 관련된 정보를 담고 있고, textStatus는 "timeout", "error", "abort", "parsererror"와 같은 상태를, errorThrown은 실제 발생한 예외를 문자열 형태로 제공합니다.
$.ajax({
  type: 'POST',
  url: 'your_server_url',
  data: { param1: 'value1' },
  success: function(response) {
    console.log('성공:', response);
  },
  error: function(jqXHR, textStatus, errorThrown) {
    console.error('AJAX 오류 발생:');
    console.error('jqXHR:', jqXHR);
    console.error('textStatus:', textStatus);
    console.error('errorThrown:', errorThrown);
    // jqXHR.status 등을 통해 HTTP 상태 코드 확인 가능
  }

2. fetch api

  • JS 내부에 있는 자체 라이브러리 → 따로 설치할 필요가 없음!
  • 현재 가장 많이 사용하는 비동기 기법
    • fetch는 JS 순수 문법이라 react, vue와 같은 최신 라이브러리 프레임워크와 연계가 좋다
const getData2 = () => {
    let url = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=★발급받은 KEY 넣기★&targetDt=20250825";
    fetch(url)
    .then((res)=>res.json())
    .then((data)=>getMovieName(data))
}
  • fetch로 데이터를 요청하면 응답하는 데이터는 json이 아님 → 데이터가 한 번 감싸져 있는 상태라 JS에서 바로 사용이 불가능 (바로 데이터를 열어볼 수 없음 → 변환해야 함)
    • fetch라는 요청을 했을 때 받아오는 데이터의 주체 자체가 다름: fetch가 주는 데이터 형식은 promise
      • 프로미스(Promise): 자바스크립트 비동기 처리에 사용되는 객체
      • 프로미스(Promise)를 이용하면 비동기 처리의 결과값을 처리하는 콜백 함수를 비동기 함수로부터 분리할 수 있음
  • 해결책: JSON 데이터로 변환 후 사용 가능
  • then은 특이하게 엔터 쳐도 앞쪽과 연결이 된 상태 → 엔터 있어도 코드가 잘 작동함
    -Q. .then((data)=>getMovieName(data))의 data는 어디서 오는 건가요?
    • A. 앞줄 .then((res)=>res.json())이 끝났을 때 json으로 바뀐 데이터를 자동적으로 매개 변수로 넘겨준 것! (위에서 만든 res.json()이 자동으로 data에 연결된다고 함)

Live Server 실행하면 콘솔창에 나오는 경고문은 왜 나오는 건가요?

→ 웹 페이지는 기본적으로 만들 때 '아이콘'이 있음 (네이버 이름 옆 N 로고 같은 거) → 로컬 환경은 원래 해당 아이콘 경로 설정이 안 되어 있어서 뜨는 것임: 오류 아니니까 신경 쓰지 말기!

  • 응용: 영화명만 출력되게 만들기
const getMovieName = (res) => {
    let data = res.boxOfficeResult.dailyBoxOfficeList
    // json 데이터는 key와 index만 생각하자
    for(let i=0;i<data.length;i++){
        console.log(data[i].movieNm);
    }
}
const getData2 = () => {
    let url = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=aab94063cb3a3a547ae3602a83b8a2de&targetDt=20250825";
    fetch(url)
    .then((res)=>res.json())
    .then((data)=>getMovieName(data)) // 콜백 함수 기법
}

document.getElementById("btn").addEventListener("click",getData2);

B. 2교시

1. fetch api (cont.)

  • fech 함수 더 알아보기
  • MDN Web Docs
    • 웹 개발 공식 문서 → 웹 표준 개발이 정리되어 있음
    • 생성형 AI에게도 MDN Web Docs 기반으로 코드를 만들어달라 요청하면 좋음

2. 카카오지도 api

  • 카카오맵을 내 사이트에 사용하는 기능 api
    • 난이도가 제일 쉬운 api → 카카오: 공개된 예제가 많지 않다
    • 다양한 기능 활용할 때 → google map: 예제가 많다, 해외 지원 BUT 난이도가 어렵다
    • 경로 기반 지도 이용 → tmap api: 예제가 많진 않다
  • API 키 받는 법
    • 카카오맵 api에서 app key 발급 클릭
    • 카카오 개발자사이트 들어가서 앱 생성
    • admin 키는 절대 노출 금지!
    • Web 플랫폼 등록해야 함
      • http://127.0.0.1:5500 입력 → 로컬이라는 뜻임
      • 보통 여러 개의 도메인을 연결한다고 함
  • 카카오지도 api 사용하는 법
  • Sample > 오버레이 > 마커에 인포윈도우 표시하기
    • 코드 복사 - 붙여넣기 - 전체 선택(ctrl+A) 후 alt+shift+F
    • <script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=발급받은 APP KEY를 사용하세요"></script>에서 '발급받은 APP KEY를 사용하세요' 위치에 발급 받은 API KEY 넣기
  • 커스텀 가능한 부분인지 알아보는 법
    • 수정을 원하는 부분에서 마우스 오른쪽 클릭 > 검사
    • 개발자 도구에서 해당 부분 확인하고 내용 복사
    • vs코드에서 검색(ctrl+F)해서 있으면 커스텀할 수 있는(수정할 수 있는) 부분이고 없으면 안 됨
      • 이미 완성된 걸 가지고 패키지로 가지고 오는 개념이라 어쩔 수 없음

C. 3교시

1. TIPS / FEEDBACK

  • Cursor AI 활용해 웹 사이트 만드는 실습도 해 볼 예정
  • 프로젝트 팁
    • '수익성' 보여주기
      • 심사위원 → 기업 대표, 교수님, … → 수익화를 가장 궁금해 함
      • 만약 공익적인 프로젝트라면 "정부"와 연결해 사업성을 어필
    • '차별성' 보여주기
      • 이게 없네? → 만들자! (X) 이게 없네? → 왜 없지? 이유를 찾기 (O)
    • 공모전의 포인트는 "주제"임
      • 큰 주제만 보는 게 아니라 큰 주제 내에서 '어떤 세분화를 할 것인지'를 보자
    • 시장 조사의 중요성
      • 아이템만 보지 말고 실제 사용자 및 사용자가 느꼈을 불편한 점을 개선하는 쪽으로 가기
      • "누가 쓸 것인지"를 항상 먼저 생각하기
      • 아이템만 보지 말고 실제 서비스를 쓸 사람들이 누구인지, 불편한 점은 무엇인지 생각하기
    • ★ 논문 많이 찾아보기 ★
  • 공모전 팁
    • 공모전 심사 기준은 기술력이 아닌 주제에 얼마나 일치하는지
    • 평가 주체는 실제 현업자 → 실제 현업에서 불편하다고 생각하는 점을 찾아서 보완해야 좋아함
      • 실제 업무에 도움이 되는 내용들이 좋은 점수를 얻음
  • 우리 조 미니 프로젝트 피드백
    • HTML
      • "section" 활용 good: 영역을 구분짓는 태그
      • header와 footer가 페이지마다 반복 → 둘을 따로 분리해서 불러오는 방식으로 쓰는 걸 추천 (그래야 header 수정할 일 생겼을 때 페이지마다 들어가서 수정할 일이 없겠죠)
    • JS
      • if로 하드코딩한 것도 나쁘진 않지만 '매핑 테이블'을 이용해 데이터만 따로 관리하는 걸 추천함
  • 다른 조 미니 프로젝트 피드백 (1)
    • preventDefault()
      • form 태그 → 제출 누르는 순간 바로 데이터 전송
      • 데이터를 검증한다던가 데이터를 Python에 넘긴다거나 하는 행위를 동시에 할 수 없음 → preventDefault()로 원래 가지고 있던 기능을 잠시 정지시키겠다는 뜻
const socket = io();
document.getElementById("searchForm").addEventListener("submit",function(e) {
	e.preventDefault();
	document.getElementById("resultDiv").style.display = "block";
	const formData = new FormData(e.target);
  	const data = Object.fromEntries(formData.entries());
  	socket.emit("start_analysis", data);
});
  • 다른 조 미니 프로젝트 피드백 (2)
    • color: var(--muted) in CSS sets the color property of an element to the value of a custom CSS variable named --muted
    • try - catch
    • 코드를 .dev로 변환해 숨기는 걸 추천
      • 현재 코드 → 노출되어 있음
    • .ts TypeScript: 타입이 붙은 자바스크립트 ← 자바의 장점을 가져온 것
      • TypeScript는 마이크로소프트에서 구현한 JavaScript의 슈퍼셋(Superset) 프로그래밍 언어
      • 타입을 지정해 메모리가 효율적이고 개발 시 규약을 줄 수 있음
    • axios: react 버전 fetch 같은 거
  • 리액트에 관하여
    • 리액트는 모든 걸 '컴포넌트' 단위로 쪼개서 관리함
      • 헤더 코드, 풋터 코드, 사이드바 코드, … → 필요할 때 부분 부분 가져와서 처리

2. JS 수업 전체 복습

  • 출력문: document.write(), console.log(), alert()
    • console.log()는 개발자끼리 소통 → 콘솔창에 출력됨
      • .log뿐만 아니라 .error, .warn도 있음
    • alert()는 사용자에게 보내는 알림 메시지 → 단순 메시지 전송, 사용자의 상태가 변화했을 때 알림
  • 입력문: prompt(), confirm()
    • prompt()는 사용자에게 입력 창을 제공
    • confirm()은 사용자에게 두 가지 선택지(확인/취소)를 제공 → 조건문과 연결 생각하기!
  • 변수: 데이터를 저장하는 공간
    • 타입은 지정하지 않아도 되지만 변수의 키워드는 적어야 함(var, let, const)
      • cf. 전통적으로 Java에서는 변수 선언 시 타입을 명시해야 함
    • var의 문제점
      • 똑같은 변수명 사용이 가능 (변수명 중복 가능)
      • 이로 인해 결과가 다를 수 있음
    • let은 변수명이 중복되지 않음 → let을 쓰는 게 표준안
    • const는 한 번 값을 넣으면 수정 불가능(재할당 불가능) & 재선언 불가능
    • 데이터 타입: null vs. undefined
      • null → "없다"라는 의미를 가진 데이터를 넣는다 → 메모리에 할당 (e.g., num = null;)
      • undefined → 아무런 데이터가 없다 → 메모리에 할당 X (e.g., let num;)
  • 형변환: 데이터 타입을 변경하는 방법
    • 문자를 숫자로 변환하는 방법을 가장 많이 사용함: Number()
      • 사용자가 입력한 모든 값은 문자 형태
      • key 값으로 꺼내오는 것도 문자열
    • 비교 연산자: 동등(==) vs. 일치(===)
  • 조건/반복문
    • if(조건1){조건1이 참일 때 실행할 내용}else if(조건2){조건2가 참일 때 실행할 내용}else{그 외의 경우 실행할 내용}
    • for(let i = 0; i < 10; i++){반복할 내용}
      • 자바 반복문(for(int i = 0; i < 10; i++){반복할 내용})과 거의 똑같음
    • while(조건){반복할 내용}
  • 배열: 같은 의미를 가진 데이터를 관리하는 공간
    • 배열은 데이터를 조회할 때 인덱스 번호를 활용ㅇ
      • 인덱스는 0부터 시작
    • 배열 길이 구하고 싶으면 .length
      • 반복문 범위 지정 시 유용
      • 데이터의 유무 검증 시 유용
  • 배열 함수
    • 단순 값 변경
      • push
      • pop
      • unshift
      • shif
    • 값 조회
      • includes
    • 반복을 활용한 배열 함수
      • forEach
      • map
    • 조건을 활용한 배열 함수: 조건문 + 반복문
      • filter
  • 객체: 다른 의미를 가진 데이터를 관리하는 공간
    • 객체는 key로 조회
  • 함수: 자주 쓰는 기능들을 묶어서 관리하는 방법
    • 화살표 함수(arraw function)
  • DOM: HTML 문서를 태그, 속성, 컨텐츠 등 하나하나 쪼개서 관리하는 모델
    • document: 모든 정보를 담고 있는 최상위 객체
    • innerText vs. innerHTML
      • 공통점: 태그가 포함되어 있지 않다면 똑같이 요소 안 컨텐츠를 가져옴
      • 차이점: 태그가 포함된다면 innerText는 인식하지 못하고 innerHTML은 인식함
      • input과 innerHTML → XSS 문제
  • 유사배열(HTMLCollection) vs. 배열
  • 이벤트 처리
    • 인라인 방식
    • 이벤트 리스너 방식
    • 익명 함수
      • 특정 함수를 한 번만 연결할 때
      • 매개 변수를 포함한 함수를 호출할 때 → 콜백 함수(callback function)
  • jQuery
    • 순수 js는 객체의 key 값 기반, jQuery는 함수 기반
      • jQuery는 함수 베이스기 때문에 여러 개를 보내려고 하면 객체로 만들어서 보내야 함
    • 순수 js에서 접근할 때는 .key, jQuery는 기능을 호출
    • 수정할 때 js는 대입(할당), jQuery는 매개 변수 사용(괄호 안에 변경 내용 넣는다)
vanila JSjQuery
요소 접근document.getElementById("id");$("#id");
컨텐츠 접근document.getElementById("id").innerText;$("#id").text();
컨텐츠 수정document.getElementById("id").innerText = "수정";$("#id").text("수정");
스타일 변경 1document.getElementById("id").style.color = "red";$("#id").css("color", "red");
스타일 변경 2document.getElementById("id").style.cssText = "color: blue; font-size: 100px;";$("#id").css({color: "blue", fontSize: "100px"});
함수 연결document.getElementById("btn").addEventListener("click",print);$("#btn").on("click", print);
  • 노드 생성
    • 노드: 웹을 구성하는 가장 작은 단위 → 태그 노드, 컨텐츠 노드, 속성 노드
    • 요소를 생성할 때는 하나하나 세분화해서 관리한다 → 태그, 속성, 컨텐츠
    • 순서 잘 기억하기
      • 1) 태그 생성 2) 컨텐츠, 속성 생성 3) 태그 + 컨텐츠 + 속성 연결 4) 완성된 요소를 화면에 표출
    • 태그 안에 태그 or 컨텐츠를 넣을 때는 자식으로 넣는다 → appendChild
    • 태그 안에 속성을 넣을 때는 자식이 아닌 key 값으로 넣는다.
  • 비동기 통신
    • jQuery ajax
    • fetch api





Ⅱ. 오후 수업

A. 4교시: LangChain

1. 지난 시간 복습

  • 파이프라인으로 체인 생성
    • 순서를 잘 맞춰서 진행하기: prompt → lmm → OutputParser
  • LangChain에서 사용할 수 있는 모델 유형
    • 단순 LLM 모델 클래스(OpenAI)
      • 주로 단일 요청에 대한 출력을 생성
      • 단순 문자열 입력, 출력 → 기본적인 구조
    • Chat 모델 클래스(ChatOpenAI)
      • 사용자와의 상호작용을 통한 연속적인 대화관리에 적합
      • 대화 형태로 상호작용이 필요한 경우 사용
  • Huggingface 모델 연동
    • HuggingFacePipeline.from_model_id class
    • OpenAI가 제공하는 모델뿐만 아니라 Huggingface에 있는 모델을 LangChain에 붙여 호출할 수 있음
      • LangChain은 OpenAI 전용이 아니라 여러 LLM 엔진을 함께 활용 가능함
  • 프롬프트 탬플릿 활용
    • 입력받는 프롬프트의 형식을 미리 정의: {} → 사용자 입력만 바꿔서 반복 생성
  • GPT3.5, GPT 4o 두 개의 모델 비교
    • 3.5는 hallucination이 큼 → 4o로 실습 진행
  • ChatPromptTemplate
    • 단순히 입력 값만 넣는 것이 아니라 llm 모델의 역할(role)을 지정해 훨씬 더 알맞는 답변을 해줄 수 있게 만듦
    • from_messages() 함수를 사용해 role 적용
      • 꼭 지정된 키워드를 사용해야 함: system, human, ai, …
    • placeholder 활용
      • 이전 대화를 유지하면서 추가 질문을 가능하게 하는 구조 설계법
      • {} 안에 대화 이력 삽입
  • 메시지 출력 양식 설정
    • 생성형 AI → 답변 형식이 매번 달라짐(랜덤)
    • 최대한 비슷한 양식에 맞춰 대답할 수 있도록 하기 위해 메시지의 출력 방식을 지정할 수 있음

2. 메시지 출력 양식 설정

# 구글 마운트 및 경로 설정
%cd /content/drive/MyDrive/Colab Notebooks/LangChain

# api key 설정
import os
with open("./key/.openai_api_key",'r') as f:
    api_key = f.read().strip()

os.environ["OPENAI_API_KEY"] = api_key

!pip install langchain_openai

from langchain_core.prompts import PromptTemplate
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate

llm4=ChatOpenAI(model = 'gpt-4o-mini', max_tokens = 500)

# 출력 형식 지정하기
prompt_template = ChatPromptTemplate.from_messages([
    ('system', "너는 여행 전문가이고 여행지의 맛있는 음식에 대해 전문적으로 대답할 수 있어"),
    ('human', '{area}에서 유명한 {topic} {num}가지를 추천해 줘~'),
    ('ai',""" 아래의 형식처럼 답변을 작성해주세요
    ======================
    타이틀
    ======================
    - 응답 1
      - 설명

    - 응답 2
      - 설명

    - 응답 3
      - 설명
    """)
])

# 체인 생성
travel_chain2 = prompt_template | llm4
# 결과 출력(invoke 함수에서 바로 작성)
res = travel_chain2.invoke({
    "area": "일본"
    , "topic": "스시"
    , "num": 5
})
print(res.content)
======================
일본에서 유명한 스시 5가지
======================
- 니기리 스시 (Nigiri Sushi)
  - 신선한 생선을 얇게 썰어 얹은 쌀 덩어리로, 스시에 가장 기본적인 형태입니다. 일반적으로 간장, 와사비와 함께 제공되며, 생선의 신선함이 가장 중요합니다.

- 마키 스시 (Maki Sushi)
  - 해조류(김)로 감싸진 쌀과 다양한 재료가 들어있는 스시입니다. 소스나 야채, 해산물 등을 넣어 롤 형태로 만들어지며, 다양한 맛과 질감을 즐길 수 있습니다.

- 우에바리 스시 (Aburi Sushi)
  - 생선의 표면을 불에 구워서 약간 익히는 스시입니다. 불에 구워서 생선의 맛과 향을 더욱 깊게 만들며, 팬이 많은 인기 메뉴입니다.

- 하카마 스시 (Hakama Sushi)
  - 횟감으로 주로 고등어, 연어 등을 사용하여 생성된 스시로, 신선한 해산물과 밥의 조화가 일품입니다. 주로 지역 특산물로 만들어지는 경우가 많습니다.

- 오마카세 스시 (Omakase Sushi)
  - 셰프에게 요리를 맡기는 스타일의 스시로, 매 시즌, 매 입회마다 신선한 재료로 즉석에서 준비되어 제공됩니다. 특별한 경험을 원하는 이들에게 추천합니다.
prompt_template = ChatPromptTemplate.from_messages([
    ('system', "너는 여행 전문가이고 여행지의 여러 가지 정보에 대해 전문적으로 대답할 수 있어"),
    ('human', '{area}에서 유명한 {topic} {num}가지를 추천해 줘~'),
    ('ai',""" 아래의 형식처럼 답변을 작성해주세요
    ======================
    타이틀
    ======================
    - 응답 1
      - 설명

    - 응답 2
      - 설명

    - 응답 3
      - 설명
    """)
])

travel_chain = prompt_template|llm4
res = travel_chain.invoke({
    "area": "일본"
    , "topic": "겨울 여행지"
    , "num": 5
})
print(res.content)
======================
일본의 유명한 겨울 여행지 5곳
======================
- 홋카이도 (Hokkaido)
  - 일본의 가장 북쪽에 위치한 홋카이도는 겨울철 스키와 스노보드의 명소로 유명합니다. 특히 니세코(Notches)와 후라노(Furano)는 세계적인 수준의 스키 리조트로, 아름다운 눈국경과 함께 다양한 겨울 스포츠를 즐길 수 있습니다. 또한, 삿포로에서는 매년 개최되는 삿포로 눈축제(Sapporo Snow Festival)도 놓칠 수 없는 매력입니다.

- 오사카 (Osaka)
  - 겨울철 오사카에서는 특별한 겨울 조명과 축제가 많이 열립니다. 특히 다카시마야 백화점과 우메다 스카이 빌딩 주변에서는 환상적인 겨울 조명이 반짝이며, 맛있는 겨울음식인 오코노미야키와 타코야키를 즐길 수 있습니다. 겨울철 한정 이벤트로는 크리스마스 마켓과 뉴 연 소음 등이 있습니다.

- 나고야 (Nagoya)
  - 나고야는 겨울철 가족 단위 여행객들에게 적합한 다양한 장소를 제공합니다. 특히 레고랜드 재팬에서는 겨울 특별 이벤트가 열리며, 누가 단체 스케이팅을 경험할 수 있는 핫코리. 나고야 성에서는 겨울 조명이 빛나며 고즈넉한 분위기를 즐길 수 있습니다. 

- 교토 (Kyoto)
  - 겨울철 교토는 조용하고 고즈넉한 분위기로 일본 전통문화를 경험할 수 있는 멋진 장소입니다. 특히 독특한 겨울 풍경을 담은 사찰과 정원들은 더욱 아름다워지는 시기입니다. 기온 거리를 거닐며 전통 가옥과 함께 찻집에서 따뜻한 차 한 잔을 즐기는 시간을 가진다면 잊지 못할 겨울 여행이 될 것입니다.

- 삿포로 (Sapporo)
  - 홋카이도의 수도인 삿포로는 겨울철에 특히 많은 관광객이 몰리는 곳입니다. 삿포로 눈축제(Sapporo Snow Festival)는 세계적으로

→ max-token을 500으로 설정했기 때문에 삿포로는 문장 중간에 끝남

  • max_tokens
    • 한 번에 생성될 수 있는 토큰(=단어, 기호 등 단위)의 최대치를 제한
    • 답변이 길거나 상세할 경우 설정한 값에 도달하면 LLM이 더 이상 출력을 이어가지 않고 중간에 멈춤
  • max_tokens의 동작 방식
    • max_tokens=500을 설정하면, 모델은 system, human, ai 메시지, 입력값, 출력 결과 전체를 합산해서 최대 500토큰만 생성 → system, human, ai의 모든 메시지의 총합이 한 세션의 토큰 수에 포함됨
    • ☆토큰 1개는 영어 단어 기준 3~4자, 한글은 좀 더 짧은 문장 단위로 쪼개지므로 "500글자"와는 다름!☆ → '토큰' 단위: 띄어쓰기, 구두점 포함 분절 단위

3. 한 개의 변수에 두 개 이상의 값을 넣고 싶을 때

  • batch() 함수
    • invoke() 대신 사용하여 한 개의 변수에 여러 개의 값을 전달
    • 리스트 형태로 딕셔너리를 묶어서 값을 전달하기
prompt = ChatPromptTemplate.from_template("{country}의 수도는 어디일까요?")
chain = prompt | llm4
input_list = [
    {"country": "미국"}
    , {"country": "호주"}
    , {"country": "안드로메다"}
]
res2 = chain.batch(input_list)

# content만 출력하기
for text in res2:
    print(text.content)
미국의 수도는 워싱턴 D.C.입니다.
호주의 수도는 캔버라(Canberra)입니다. 캔버라는 호주 연방 정부의 중심지로, 국가의 주요 정부 기관들이 위치하고 있습니다.
안드로메다는 별자리이자 우리 은하인 Milky Way와는 다른 별계에 위치한 은하입니다. 따라서 "안드로메다의 수도"라는 개념은 실제로 존재하지 않습니다. 안드로메다 은하는 M31이라는 이름을 가지며, 지구에서 가장 가까운 대형 은하 중 하나입니다. 과학적 및 천문학적 관점에서 볼 때, 안드로메다에는 인간이 살거나 수도가 존재하지 않지만, 우주 탐사나 SF 소설에서 상상으로 만들어진 세계에서는 다양한 설정이 있을 수 있습니다.

→ chain.batch(input_list)의 결과는 AIMessage 객체 배열(리스트 안에 AIMessage 객체 3개가 들어 있음) → for 문을 통해 AIMessage 객체를 하나씩 꺼내고 거기에 .content를 해서 내용만 출력 가능


B. 5교시: LangChain

1. 문서 요약 시스템 만들어보기

  1. 모델 정의하기
    • gpt-4o-mini 사용
  2. ChatPromptTemplate 작성
    • system, human, ai 메시지를 어떻게 활용할지 고민하기
      • 요약 (몇 자 이내~) → ai 메시지에 넣기
    • 튜플 형태로 넣어야 함: ("system", "당신은 장문을 요약하는 시스템입니다.")
역할권장 지시 사항 위치주요 효과
system대화 전체에 적용할 규칙항상 일관성 유지, 전역 규칙 적용
human개별 질문/입력에 적용요청별로 다르게 제어, 유연성
ai출력 예시, 템플릿포맷/형식 직접 제시, 따라 쓰게 유도
  1. 요약할 뉴스 기사 변수에 저장
  2. 체인 구성 후 호출: .invoke(document)
llm_model = ChatOpenAI(model="gpt-4o-mini", temperature=0, max_tokens=500)
summary_prompt = ChatPromptTemplate.from_messages([
    ("system","당신은 장문을 요약하는 시스템입니다.") # role
    , ("human","입력된 document를 요약하세요: {document}") # 사용자의 입력
    , ("ai","요약 결과는 50 단어 이하로 해주세요. 반드시 필요한 내용을 포함시켜주세요") # 출력 → 출력 관련 내용 넣기
])

document = """
(서울=연합뉴스) 김경희 기자 = '디지털 전환'(DX)에서 '인공지능 전환'(AX)으로 정보통신(ITC) 패러다임 이동이 이뤄지는 가운데 산업계의 이 같은 변화를 실행하는 시스템 통합(SI) 업계 내부에서부터 AI 전진 배치가 급격히 진행되고 있다.

2일 관련업계에 따르면 대기업 계열 SI 기업 대부분이 코딩 등 영역에 AI를 폭넓게 이용하는 가운데 최근에는 업무 전반으로 이를 확산해 업계 화두인 'AI 에이전트'에 선제적으로 대비하는 움직임을 보이고 있다.


LG CNS는 최근 코딩 단계에서만 활용해 온 AI 코딩 플랫폼을 고도화해 분석부터 설계, 코딩, 테스트, 품질진단 전 과정으로 확대했다고 밝혔다.


업그레이드한 플랫폼에는 개발자들이 만들고자 하는 기능에 대한 명령어를 입력하는 것만으로 생성형 AI가 소스코드 생성부터 테스트, 검증까지 자동으로 수행하는 '코딩 에이전트' 기능도 탑재했다.

테스트와 검증 과정에서 오류가 발생할 경우 코딩 에이전트가 자동으로 소스코드를 수정하고 이를 반복 수행해 고품질의 결과물을 얻을 수 있도록 했다.

현대오토에버[307950] 역시 지난해 하반기부터 자체 개발한 생성형 AI 기반 대화형 서비스 '에이치 챗'을 업무에 활용 중이다.

마이크로소프트의 '애저 오픈AI'를 기반으로 임직원의 업무를 보좌한다. 대표적으로는 단위 테스트 코드를 수행해 개발자들이 신뢰성 높은 코드를 쉽게 만들 수 있게 돕는다.

단위 테스트는 작성한 코드가 의도대로 작동하는지 검증하는 절차로, 상당한 시간이 소요되고 작업 난도 역시 높다.

소프트웨어 오류의 원인을 찾아내고 해결 방법까지 제시하는 '트러블 슈팅' 기능도 수행한다. 코드 마이그레이션, 코드 리뷰 기능도 가능하다.

최근 AI를 전면에 내세워 사명을 바꾼 SK AX(옛 SK C&C)는 아예 'AI 퍼스트' 전략을 내세워 개발 현장에 AX 개발 플랫폼 '솔루어'는 물론 다양한 AI코딩 설루션을 활용 중이다.

분석과 설계 단계에서 AI가 방대한 정보를 자연어로 정리하고 복잡한 업무 흐름을 구조화하면, 개발자가 본질적 설계 논리에 집중할 수 있게 하는 식이다.

코딩 단계에서는 AI가 반복적 작업을 대신하고, 개발자의 의도를 반영해 필요한 코드 조각을 제안하는 식의 협업이 이뤄진다.

삼성SDS는 생성형 AI 서비스인 '패브릭스'(FabriX), '브리티 코파일럿'(Brity Copilot), '브리티 오토메이션'(Brity Automation) 등 시스템을 구현 중이다.

삼성SDS는 디지털 물류 플랫폼 첼로스퀘어에도 AI를 적용하고 있다. 분석형 AI를 활용한 '출항일 및 도착 예정일 예측', '환적 및 하역 항구 이슈 조기 감지' 등 기능이 가능하다.

CJ올리브네트웍스 역시 지난해부터 AI 기반 시스템 개발 체계를 도입해 개발 생산성 향상이라는 성과를 거두고 있다고 밝혔다.

회사 측은 AI 코딩 기술을 설계·개발·테스트·품질 검토 등 개발 라이프사이클 전반에 적용하고 있으며, 코드 작성과 리뷰, 테스트 코드 개발까지 전 영역에 AI를 활용하는 방식으로 전환 중이라고 설명했다.

특히 단일 AI가 아닌 글로벌 AI 모델들을 동시에 활용하고, 생성된 코드에 대한 교차 검증(Cross check)까지 진행해 품질을 높이고 있다고 강조했다.
"""

summary_chain = summary_prompt | llm_model
res3 = summary_chain.invoke({"document":document})
print(res3.content)
산업계에서 '디지털 전환'에서 '인공지능 전환'으로의 변화가 진행 중이며, SI 기업들이 AI를 업무 전반에 활용하고 있다. LG CNS, 현대오토에버, SK AX, 삼성SDS, CJ올리브네트웍스 등은 AI 기반 시스템을 도입해 개발 효율성을 높이고 있다.

❓ '요약 결과는 50 단어 이하로 해주세요'를 human에 넣는 것과 ai에 넣는 것의 차이
❗ 출력 결과는 비슷할 수 있지만 모델이 받아들이는 의미가 조금씩 달라짐
→ 출력 형식 지시를 system, human, ai 메시지 각각 어디에 넣느냐에 따라 의미적 역할과 LLM의 반응 방식이 달라짐!

메시지 역할별 차이

system 메시지

  • 목적: 모델 전체 동작의 기본 규칙이나 행동 양식을 지정.
  • 지시어를 넣으면: LLM이 모든 대화에 걸쳐 일관된 요약 방식(예: 언제나 50단어 이하로)을 유지하려고 함.
  • 예시: "항상 요약은 50단어 이하로 해 주세요."

human 메시지

  • 목적: 사용자(즉, 프롬프트 작성자)가 모델에 제공하는 실제 입력이나 요청.
  • 지시어를 넣으면: LLM이 ‘요약해 달라’는 요청과 함께 제한 조건(50단어 이하)을 ‘질문’의 일부로 인식.
  • 예시: “입력된 문서를 50단어 이하로 요약해 주세요.”

ai 메시지

  • 목적: LLM이 예시 답변을 보거나, 원하는 답변 형식을 따르라고 안내(초거시적 output template 역할).
  • 지시어를 넣으면: LLM이 예시 답변의 형식과 원하는 제약(50단어 이하)에 맞춰 출력.
  • 예시: “요약 결과는 50 단어 이하로 해주세요. 반드시 필요한 내용을 포함시켜주세요.”

실제 출력 차이

  • system에 넣으면: 전체 대화에서 일관된 응답 스타일을 원할 때 적합. 한 번에 여러 작업을 하거나 장기적인 일관성이 중요할 때 사용.
  • human에 넣으면: 해당 질문에만 적용되는 제한(예외적인 지시)이 명확히 전달됨. 다양한 요청에 따라 다르게 동작하도록 유연하게 제어할 때 알맞음.
  • ai에 넣으면: 원하는 형식의 샘플 답변(혹은 답안의 포맷 및 제약 조건)을 ‘따라 쓰기’ 방식으로 쉽게 유도함. 출력 예시 효과가 큰 상황에 적합.

결론

  • 일관된 룰 지정: system이 적합.
  • 특정 요청마다 다르게 적용: human이 적합.
  • 출력 예시와 직접 명시: ai가 효과적.
    실제 차이는 크게 드러나지 않을 수 있지만, 복잡한 프롬프트 구조나 다양한 작업을 조합할 때 메시지 배치에 따라 LLM 응답 품질과 일관성이 달라질 수 있습니다.

2. 데이터를 효과적으로 전달하는 방법

RunnablePassthrough()RunnableParallel()

  • RunnablePassthrough() 함수
    • LangChain에서 사용하는 Runnable의 종류 중 하나로 가장 기본적인 방법
      • Runnable: 입력 데이터에 어떤 처리를 해 줄지에 대한 방법 중 하나
    • 입력데이터를 수정하지 않고 바로 그대로 다음 단계로 전달
      • 입력값을 단순히 넘기는 역할: RunnablePassthrough → "입력 데이터를 그대로 전달해"라는 뜻임
from langchain_core.runnables import RunnablePassthrough
# 입력값을 별도 처리 없이 다음 단계로 전달할 때 사용

# 1. 모델 생성 → 위에서 만든 llm_mode 사용
# 2. 프롬프트 생성
prompt = ChatPromptTemplate.from_template("{obj}의 주요 기능에 대해서 설명해 줘")
# 3. chain 구성
chain = {"obj":RunnablePassthrough()} | prompt | llm4
# 4. chain 실행
result = chain.invoke("사과")
print(result.content)
사과는 여러 가지 주요 기능과 효과를 가지고 있는 과일입니다. 다음은 사과의 주요 기능에 대한 설명입니다:

1. **영양소 공급**: 사과는 비타민 C, 식이섬유, 항산화 물질인 폴리페놀 등이 풍부하여 면역력을 증진하고 건강에 도움을 줍니다.

2. **소화 개선**: 사과에 함유된 식이섬유는 소화를 촉진하고 장 건강을 개선하는 데 기여합니다. 특히 펙틴이라는 식이섬유는 장내 유익한 세균의 성장을 도와줍니다.

3. **체중 관리**: 사과는 칼로리가 낮고 식이섬유가 많아 포만감을 주어 체중 관리에 유리합니다. 간식으로 섭취하기 좋은 선택입니다.

4. **심혈관 건강**: 사과의 항산화 물질은 심혈관 질환의 위험을 줄이는 데 도움을 줍니다. 또한, 식이섬유는 콜레스테롤 수치를 낮추는 데 기여할 수 있습니다.

5. **혈당 조절**: 사과는 혈당 지수가 낮아 혈당을 안정적으로 유지하는 데 도움이 됩니다. 특히 당뇨 환자에게 좋은 과일로 알려져 있습니다.

6. **항산화 효과**: 사과에 포함된 다양한 항산화 물질은 세포의 손상을 예방하고 노화 방지에 도움을 줄 수 있습니다.

이 외에도 사과는 다양한 요리에 활용되며, 간편하게 섭취할 수 있는 과일로 많은 사람들에게 사랑받고 있습니다.

obj에게 RunnablePassthrough가 dictionary 형태로 전달해 주기 때문에(RunnablePassthrough를 통해 obj로 들어가기 때문에) 체인을 사용할 때 chain.invoke("사과")와 같이 딕셔너리 형태가 아닌 단순 키워드(문자열)만 사용하면 됨

  • RunnableParallel() 함수
    • 하나의 입력 값으로 여러 체인을 동시에 실행하고 결과를 딕셔너리로 묶음
      • 여러 개의 체인 → 여러 개의 입력 값(capital, area)이 필요한 곳에 하나의 동일한 값(대한민국)을 넣고 싶음 → RunnableParallel: 입력 값을 묶는 역할
    • 같은 입력으로 서로 다른 질문을 받을 때 사용 → 왕복 시간을 줄일 수 있음
from langchain_core.runnables import RunnableParallel

# 모델 생성 → model_llm 그대로 사용
# 프롬프트 작성
prompt1=ChatPromptTemplate.from_template("{capital}의 수도는 어디일까요?")
prompt2=ChatPromptTemplate.from_template("{area}의 면적은?")
# 여러 개의 체인을 작성
chain1 = {"capital": RunnablePassthrough()}|prompt1|llm4
chain2 = {"area": RunnablePassthrough()}|prompt2|llm4

# 체인 사용하기
combined_chain = RunnableParallel({"capital":chain1, "area":chain2})
combined_chain.invoke("대한민국")
{'capital': AIMessage(content='대한민국의 수도는 서울입니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 8, 'prompt_tokens': 16, 'total_tokens': 24, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-C9PtdHm5Kwh7NuHLh4y2YI1eCK3rw', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--de52952a-0eab-4ac2-a68a-691dce12e415-0', usage_metadata={'input_tokens': 16, 'output_tokens': 8, 'total_tokens': 24, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}}),
 'area': AIMessage(content='대한민국의 면적은 약 100,210 제곱킬로미터입니다. 이는 한반도의 남쪽에 위치한 국가로, 대략적인 크기는 미국의 테네시주와 비슷합니다.', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 48, 'prompt_tokens': 14, 'total_tokens': 62, 'completion_tokens_details': {'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmpl-C9PtdosWSmONi3lFIxts9xjQZbRcu', 'service_tier': 'default', 'finish_reason': 'stop', 'logprobs': None}, id='run--f8db0686-828a-43a9-80aa-e35605f59639-0', usage_metadata={'input_tokens': 14, 'output_tokens': 48, 'total_tokens': 62, 'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details': {'audio': 0, 'reasoning': 0}})}
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# 텍스트로부터 FAISS 벡터 저장소를 생성합니다.
vectorstore = FAISS.from_texts(
    [
        "테디는 랭체인 주식회사에서 근무를 하였습니다.",
        "셜리는 테디와 같은 회사에서 근무하였습니다.",
        "테디의 직업은 개발자입니다.",
        "셜리의 직업은 디자이너입니다.",
    ],
    embedding=OpenAIEmbeddings(),
)
# 벡터 저장소를 검색기로 사용합니다.
retriever = vectorstore.as_retriever()
# 템플릿을 정의합니다.
template = """Answer the question based only on the following context:
{context}
Question: {question}
"""
# 템플릿으로부터 채팅 프롬프트를 생성합니다.
prompt = ChatPromptTemplate.from_template(template)
# ChatOpenAI 모델을 초기화합니다.
model = ChatOpenAI(model_name="gpt-4o-mini")
# 문서를 포맷팅하는 함수
def format_docs(docs):
    return "\n".join([doc.page_content for doc in docs])
# 검색 체인을 구성합니다.
retrieval_chain = (
    {"context": retriever | format_docs, "question": RunnablePassthrough()}
    | prompt
    | model
    | StrOutputParser()
)
# 검색 체인을 실행하여 질문에 대한 답변을 얻습니다.
retrieval_chain.invoke("테디의 직업은 무엇입니까?") # '테디의 직업은 개발자입니다.'
# 검색 체인을 실행하여 질문에 대한 답변을 얻습니다.
retrieval_chain.invoke("셜리의 직업은 무엇입니까?") # '셜리의 직업은 디자이너입니다.'

→ 'context'의 값(== 입력 값)이 그대로 답변으로 나옴!

3. 출력 파서(Output Parser)

  • 기능: 출력 포멧 설정
  • 종류: 매우 다양
    • StrOutputParser(): 문자열로 변환해 출력. 기본 타입.
    • JsonOutputParser(): JSON 형태로 변환. 구조적 형태로 가져가고 싶을 때 많이 사용.
    • CommaSeparatedListOutputParser(): CSV로 변환 (데이터를 ,로 구분)
    • DatetimeOutputParser(): 날짜 형식으로 변환

기본 StrOutputParser 사용해보기

from langchain_core.output_parsers import StrOutputParser

# llm_model: 템플릿 지정 → 여행전문가
prompt_template = ChatPromptTemplate.from_messages([
    ('system', "너는 여행 전문가이고 여행지에 대해 전문적으로 대답할 수 있어"),
    ('human', '{area}에서 유명한 {topic} {num}가지를 추천해줘~'),
    ('ai',""" 아래의 형식처럼 답변을 작성해주세요
    ======================
    타이틀
    ======================
    - 응답 1
      - 설명

    - 응답 2
      - 설명

    - 응답 3
      - 설명
    """)
])

# 체인연결
travel_chain3 = prompt_template | llm_model | StrOutputParser()

res3 = travel_chain3.invoke({"area":"일본","topic":"시리카와고","num":5})
print(res3)
# StrOutputParser()를 사용했기 때문에 .content 하지 않아도 바로 string 형태로 예쁘게 나온다!
======================
시리카와고의 매력적인 명소 5가지
======================
- 응답 1: 고샤쿠지 (合掌造り集落)
  - 설명: 시리카와고의 대표적인 전통 마을로, UNESCO 세계문화유산으로 지정되어 있습니다. 독특한 '합장 건축' 양식의 집들이 모여 있어, 아름다운 경관을 자랑합니다. 특히 겨울철에는 눈 덮인 풍경이 환상적입니다.

- 응답 2: 시리카와고 민속촌 (白川郷民俗館)
  - 설명: 이곳은 시리카와고의 전통적인 생활 방식을 체험할 수 있는 장소입니다. 다양한 전통 가옥과 농기구, 생활용품들이 전시되어 있어, 일본의 농촌 문화를 깊이 이해할 수 있습니다.

- 응답 3: 시리카와고 전망대 (白川郷展望台)
  - 설명: 시리카와고의 전경을 한눈에 볼 수 있는 최고의 장소입니다. 특히 일출이나 일몰 시간에 방문하면, 아름다운 경치를 감상할 수 있어 사진 촬영에 적합합니다.

- 응답 4: 시리카와고 온천 (白川郷温泉)
  - 설명: 시리카와고 지역에는 여러 온천이 있어, 여행 중 피로를 풀기에 좋습니다. 자연 속에서 온천욕을 즐기며, 편안한 시간을 보낼 수 있습니다.

- 응답 5: 시리카와고 겨울 축제 (白川郷冬まつり)
  - 설명: 매년 겨울에 열리는 이 축제는 시리카와고의 아름다운 겨울 풍경을 즐길 수 있는 기회입니다. 전통적인 조명과 함께 눈으로 덮인 마을의 경관이 환상적이며, 다양한 문화 행사도 함께 진행됩니다.
  • parser를 사용하지 않았을 경우에는 아래와 같이 AIMessage 객체로 출력됨
    • 위와 같은 경우 사람이 확인하려면 .content를 사용해야 함
  • StrOutputParser()를 사용하면 content에 대해서 str 형태로 출력됨

JsonOutputParser 사용해보기

from langchain_core.output_parsers import JsonOutputParser

prompt3 = """
광주의 유명한 {topic} 5가지를 추천해 주세요.
아래 키 값에 따라 JSON 형태로 응답해 주세요.
* 답변 1
* 답변 2
* 답변 3
* 답변 4
* 답변 5
"""

chain = {"topic": RunnablePassthrough()} | ChatPromptTemplate.from_template(prompt3)|llm_model|JsonOutputParser()
chain.invoke("음식")
{'답변 1': '광주 비빔밥',
 '답변 2': '무등산 막걸리',
 '답변 3': '광주 떡갈비',
 '답변 4': '전라도식 김치찌개',
 '답변 5': '광주 순대'}
  • chain 흐름
res = chain.invoke("음식")
res["답변 1"]
광주 비빔밥

C. 6교시: LangChain

1. 간단 챗봇 만들어 보기

  • 반복문을 사용해 간단한 챗봇 형태를 만들어보자
    • 메모리 기능이 없어 진짜 챗봇은 아님
while (True):
    human_in = input("입력:")

    if human_in == "exit":
        break

    print(llm_model.invoke(human_in).content)
입력:밴드 토킹 헤드에 대해 알려주세요.
토킹 헤드(Talking Heads)는 1975년에 결성된 미국의 록 밴드로, 뉴 웨이브와 아트 록 장르에서 큰 영향을 미친 그룹입니다. 밴드는 데이비드 번(David Byrne), 티나 웨이머스(Tina Weymouth), 제리 해리슨(Jerry Harrison), 크리스 프랜시스(Chris Frantz)로 구성되어 있습니다. 

토킹 헤드는 독특한 음악 스타일과 실험적인 사운드로 유명하며, 그들의 음악은 펑크 록, 아프리카 리듬, 전자 음악 등 다양한 요소를 혼합한 특징이 있습니다. 대표적인 앨범으로는 "Talking Heads: 77", "Fear of Music", "Remain in Light" 등이 있으며, 이들 앨범은 비평가들로부터 높은 평가를 받았습니다.

특히 "Once in a Lifetime"와 "Psycho Killer"와 같은 곡은 그들의 대표적인 히트곡으로, 지금까지도 많은 사랑을 받고 있습니다. 토킹 헤드는 1980년대 초반에 활동을 중단했지만, 그들의 음악은 여전히 많은 아티스트들에게 영향을 미치고 있습니다. 2002년에는 록의 명예의 전당에 헌액되었습니다.
입력:여행지 세 곳 추천해 주세요. 대한민국과 가까운 나라로 부탁해요.
대한민국과 가까운 나라에서 여행하기 좋은 세 곳을 추천해 드릴게요.

1. **일본 (도쿄)**:
   - 도쿄는 현대적인 도시와 전통적인 문화가 조화를 이루는 곳입니다. 신주쿠, 시부야, 아사쿠사 등 다양한 지역에서 쇼핑과 맛있는 음식을 즐길 수 있습니다. 또한, 도쿄 타워와 스카이트리 같은 랜드마크도 방문할 만합니다.

2. **중국 (상하이)**:
   - 상하이는 중국의 경제 중심지로, 현대적인 스카이라인과 전통적인 건축물이 어우러져 있습니다. 번화한 난징루 거리와 유서 깊은 유원지인 위안민가든을 방문해 보세요. 또한, 상하이의 다양한 미식도 놓치지 마세요.

3. **대만 (타이베이)**:
   - 타이베이는 맛있는 길거리 음식과 친절한 사람들로 유명합니다. 타이베이 101, 국립고궁박물관, 스린 야시장 등 다양한 명소가 있습니다. 대만의 자연 경관도 아름다우니 근교 여행도 추천합니다.

이 세 곳은 모두 비행기로 쉽게 접근할 수 있으며, 각기 다른 매력을 가지고 있어 즐거운 여행이 될 것입니다!
입력:아까 물어봤던 밴드 이름이 뭐였죠?
출력: 죄송하지만, 이전 대화 내용을 기억할 수 없어요. 어떤 이름을 말씀하셨는지 다시 알려주실 수 있나요?
입력:exit
  • 마치 챗봇처럼 보이지만 사실 순간 순간의 입력에 대해 답변해 주고 있을 뿐임
    • 따라서 메모리 기능을 활용해 실제로 이전 대화 내용을 기억하도록 만들어야 함

history로 메모리 기능이 없는 부분을 어느 정도 보완 가능

2. LangChain의 다양한 활용법

  • zero-shot prompting
  • few-shot prompting
  • 예시선택(ExampleSelector) → 문서 유사도 확인 → DB 필요
    • 예시란? 프롬프트에 사전 예시를 제공하여 AI가 어떻게 응답해야 하는지 학습시키는 방법
      • 예시를 통해 AI는 제공된 예시의 패턴을 이해하고, 사용자가 기대하는 답변 스타일이나 내용을 쉽게 따라갈 수 있음
      • 특히 Few-Shot Learning이나 맥락 학습과 같은 기법에서 매우 유용하게 사용
      • AI에게 새로운 질문을 제시할 때 일관성 있는 답변을 이끌어낼 수 있는 강력한 도구
    • LangChain에서의 example 활용은 RAG(Retrieval-Augmented Generation) 구조와 밀접함 → 사용자가 질문하면, 그 질문과 의미적으로 가장 비슷한 예시(문서)를 DB(주로 벡터 데이터베이스)에서 찾아내고, 그 예시와 함께 LLM에게 답변을 요청
      • 문서 유사도 확인은 질문 벡터와 DB 내 저장된 문서 벡터를 비교하여, 코사인 유사도 등으로 가장 가까운 문서(예시)를 찾는 과정 → 이 과정에는 반드시 예시 데이터가 담긴 DB(Vector Store, 예: ChromaDB, FAISS 등)가 필요 → DB에서 의미적으로 가까운 문서를 추출해 답변의 신뢰성과 맥락 풍부함을 높임
    • example은 단순히 프롬프트에 넣는 샘플이 아니라, 실제로 질문과 유사한 사례(문서) 자체를 벡터로 뽑아 활용하는 것을 의미하며, 이런 문서 유사도 확인 절차를 통해 context-aware QA 등 다양한 활용이 가능함
  • 스트리밍(streaming)
    • LangChain의 스트리밍 기능은 LLM이 답변을 모두 생성한 뒤 한 번에 반환하지 않고, 생성되는 부분(partial output)을 실시간으로 바로 사용자에게 출력하는 방식
      • 긴 답변을 생성할 때 사용자에게 기다림 없이 순차적으로 텍스트를 보여줄 수 있어, UX가 매우 개선됨
      • LangChain에서는 stream() 또는 astream() 같은 메서드로 동기/비동기 스트리밍을 구현할 수 있음 → 특히 채팅 시스템, 인터랙티브 환경 등에 유용
      • 실용 예시: 웹소켓, 실시간 챗 UI 등에서 LLM이 생성한 시(詩)나 장문의 텍스트를 단어별, 문장별로 빠르게 전달할 수 있음

LangChain에서 예시와 스트리밍은 프롬프트 단순화(제로샷, 퓨샷) 그 이상의 실제적인 예시 데이터와 실시간 사용자 피드백을 포함하여, 현실적인 LLM 앱 구현에 매우 자주 사용되는 패턴

# 구글 마운트 및 경로 설정
%cd /content/drive/MyDrive/Colab Notebooks/LangChain

# api key 설정
import os
with open("./key/.openai_api_key",'r') as f:
    api_key = f.read().strip()

os.environ["OPENAI_API_KEY"] = api_key

# 패키지 설치 (대략 3분 소요)
!pip install -qU openai langchain-openai langchain langchain_community
!pip install -qU tiktoken pypdf chromadb faiss-cpu
!pip install -qU langchain-teddynote
!pip install -qU huggingface_hub langchain_huggingface

# -q : 설치 진행 메시지 최소화
# -U : 이미 설치된 패키지가 있으면 최신 버전으로 업그레이드(아니면 최신 버전 설치)

설치한 패키지 간단 설명

  • openai
    • OpenAI API와 상호 작용하기 위한 공식 Python 라이브러리
  • langchain-openai
    • OpenAI의 LLM을 LangChain과 통합하기 위한 확장 패키지
  • langchain
    • LangChain의 핵심 라이브러리, LLM 기반 애플리케이션 개발을 위한 프레임워크 제공
  • langchain_community
    • 커뮤니티에서 제공하는 LangChain 확장 및 추가 도구 모음
  • tiktoken
    • OpenAI에서 제공하는 토크나이저 라이브러리로, 토큰 수 계산 등에 사용됨
  • pypdf
    • PDF 파일에서 텍스트를 추출하기 위한 라이브러리
  • chromadb
    • 벡터 임베딩 저장 및 검색을 위한 벡터 데이터베이스 (Chroma DB)
  • faiss-cpu
    • 효율적인 벡터 유사도 검색을 위한 FAISS 라이브러리의 CPU 버전
  • langchain-teddynote
    • 테디노트(유튜버)가 만든 랭체인 확장 패키지
  • huggingface_hub
    • Hugging Face의 모델 및 데이터셋 저장소와 상호작용하기 위한 클라이언트 라이브러리
  • langchain_huggingface
    • Hugging Face 모델을 LangChain 프레임워크와 통합하여 활용할 수 있도록 도와주는 확장 패키지

제로샷 프폼트팅(Zero-Shot Prompting)

  • 아무 예시 없이 모델 자체가 새로운 답변을 하는 경우를 지칭
    • 이제까지 진행한 실습이 모두 제로샷 프롬프팅에 해당
  • Zero-Shot: AI 모델이 사전 지식없이 새로운 답변을 출력하는 방식
  • Zero-Shot Prompting: 사전 예시 없이 단순 요청에 모델이 답변을 생성하는 방식
  • 특징
    • 모델은 오로지 프롬프트 템플릿에 포함된 정보만을 기반으로 응답을 생성함
  • 장점
    • 빠름
    • 간단하게 적용 가능
  • 단점
    • 복잡한 문제나 구체적인 형식을 요구하는 작업에서는 모델의 응답이 기대에 못 미칠 수 있음
  • Zero-Shot Prompting 개선방법
  • Few-Shot Prompting 사용
  • memeory 기능 사용
    • 피드백을 통한 개선된 응답
  • 체인 결합
    • 여러 개의 모델을 연결
    • 하나의 체인에 하나의 모델 → 체인을 연결한다 == 여러 모델이 연결된다
  • RAG(Retrieval-Augmented Generation; 검색 증강 생성)
    • 대규모 언어 모델(LLM)의 한계를 극복하기 위해 제안된 새로운 자연어 처리 기술
      • 기존의 언어 모델에 외부 지식을 결합아여 더욱 정확하고 최신의 정보를 제공하는 혁신적인 접근 방법
    • 대규모 언어 모델의 출력을 최적화하여 응답을 생성하기 전에 훈련 데이터 소스 외부의 신뢰할 수 있는 기술 자료를 참조하도록 하는 프로세스




Ⅲ. CARRER UP

A. Goal Tracker, TIL 정리

B. 포트폴리오 특강

  • 모든 문서/자료/설명문에는 기·승·전·결이 꼭 필요함

    • 앞이 재미있어야 계속 볼 수 있음
    • "앞이 재미있다"의 기준? 起!
  • 중요 포인트

    • 왜 기획했는지 / 무엇을 해결하려고 하는지 → '기' 파트
    • 해결을 위해 무멋을 했는지 → 프로젝트를 통해 제공하고자 하는 차별점
    • 가장 중요한 것 하나만 글자 크게
      • 도드라지는 것에만 볼드
      • 나머지 글자는 적게 or 빼기
  • 이미지 위주로 편집 및 설명!

    • 면접관의 흥미 유발 및 마지막까지 읽게 하기 위함
  • 면접관은 내가 어떻게 했는지는 안 궁금함! 결과만 말하기!

  • 미리캔버스에서 템플릿 그대로 가져다 쓰지 마세요
    • 나의 프로젝트를 직관적으로 설명하는 이미지 찾는 데 시간 많이 쓰기




하루 돌아보기

👍 잘한 점

  • 수업 열심히 참여 & 질문 많이 함
  • 포트폴리오 특강 참여

👎 아쉬웠던 점

  • 내가 만들었던 포트폴리오가 사실 못 만든 포트폴리오에 속한다는 사실을… 알아버렸다…
  • 발표 PPT에 글자가 너무 많아서 다시 수정해야 하는데 처음부터 분량 조절을 잘 했다면 두 번 일하는 일은 없었을텐데
    • 하지만 진짜 하고 싶은 말만 넣었는데😅
    • 나는 PPT의 기준을 항상 학교 다닐 때 만들었던 기억으로 맞추는데 프로젝트 발표는 이런 형식이면 곤란하나 봄
      • 본 게 교수님들 것밖에 없으니까 글만 엄청 많고 그림이 없음😧

🔬 개선점

  • 프로젝트 발표용 PPT와 발표 10분에 딱 맞추는 방법을 더 궁리하기
profile
2 B R 0 2 B

0개의 댓글