13b756911f
- 병렬 처리, 비동기 병렬 처리 랭그래프 - 워크플로 프리임워크 state, Node, Edge 구조 - 조건부 엣지, 루프, 자가 검토
1196 lines
40 KiB
Plaintext
1196 lines
40 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "markdown",
|
||
"source": [
|
||
"#### LangGraph\n",
|
||
"\n",
|
||
"- 워크플로 프레임워크\n",
|
||
"- LCEL 선형 (A -> B -> C) / LangGraph 순환 ( A-> B -> A -> C -> B) 흐름 지원\n",
|
||
"- 개념\n",
|
||
" - Node : 실행할 함수\n",
|
||
" - Edge : Node 간의 연결\n",
|
||
" - State : Node 간의 전체 상태를 담는 딕셔너리\n",
|
||
"- 조건부 엣지, Agent 루프, 자기 수정, 멀티 에이전트 등 복잡한 팬턴 구현"
|
||
],
|
||
"id": "4f173862ffa5000b"
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T10:59:55.228433274Z",
|
||
"start_time": "2026-06-08T10:59:55.202226030Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": "# !pip install langgraph grandalf",
|
||
"id": "1ecece0e830ff1b5",
|
||
"outputs": [],
|
||
"execution_count": 1
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.004421946Z",
|
||
"start_time": "2026-06-08T10:59:55.236638820Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"from langgraph.graph import StateGraph, START, END\n",
|
||
"from typing import TypedDict\n",
|
||
"\n",
|
||
"import os\n",
|
||
"\n",
|
||
"from cohere.types import summarize_request_extractiveness\n",
|
||
"from google.protobuf.timestamp import from_current_time\n",
|
||
"from langchain_classic.chains.sequential import SequentialChain\n",
|
||
"from langchain_openai import ChatOpenAI\n",
|
||
"from langchain_ibm import WatsonxEmbeddings\n",
|
||
"from langchain_ibm import ChatWatsonx\n",
|
||
"from langchain.agents import create_agent\n",
|
||
"from langchain_ollama import OllamaEmbeddings\n",
|
||
"from langchain_ollama import ChatOllama\n",
|
||
"from langchain_ollama import OllamaEmbeddings, ChatOllama\n",
|
||
"from dotenv import load_dotenv\n",
|
||
"\n",
|
||
"from langchain_community.utilities import GoogleSerperAPIWrapper\n",
|
||
"from langchain_core.tools import Tool\n",
|
||
"from langchain_core.tools import tool\n",
|
||
"\n",
|
||
"from langchain_core.prompts import PromptTemplate, ChatPromptTemplate, MessagesPlaceholder\n",
|
||
"from langchain_core.output_parsers import StrOutputParser, JsonOutputParser, PydanticOutputParser\n",
|
||
"from langchain_core.runnables import RunnablePassthrough, RunnableParallel, RunnableLambda\n",
|
||
"from langgraph import graph\n",
|
||
"from pygments.unistring import combine\n",
|
||
"from sklearn import pipeline"
|
||
],
|
||
"id": "424747aac7874d9",
|
||
"outputs": [
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"/home/cooney/Source/.venv/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
|
||
" from .autonotebook import tqdm as notebook_tqdm\n",
|
||
"/tmp/ipykernel_239115/245101980.py:18: DeprecationWarning: `langchain-community` is being sunset and is no longer actively maintained. See https://github.com/langchain-ai/langchain-community/issues/674 for details and migration guidance toward standalone integration packages.\n",
|
||
" from langchain_community.utilities import GoogleSerperAPIWrapper\n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 2
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "markdown",
|
||
"source": [
|
||
"#### TypedDIct / Pydantic\n",
|
||
"- TypedDict : 딕셔너리 키 , 값 타입을 정의할 수 있게 도와줌, 단순한 State 구현 시 사용, 기본값 줄 수 없음\n",
|
||
"- Pydantic : validation 검사(입력값), 검증이 필요한 State 인 경우 사용"
|
||
],
|
||
"id": "ab7dfbff952ba846"
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.372424934Z",
|
||
"start_time": "2026-06-08T11:00:03.059989505Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 데이터 저장소 생성\n",
|
||
"# message 상태관리 할거야 => 공유\n",
|
||
"class MyState(TypedDict):\n",
|
||
" message:str\n",
|
||
"\n",
|
||
"# 작업 함수 생성(노드)\n",
|
||
"def say_hello(state):\n",
|
||
" # state 값의 변화\n",
|
||
" return {\"message\": \"Hello, LangGraph!\"}\n",
|
||
"\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(MyState)\n",
|
||
"graph.add_node(\"hello\", say_hello)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"hello\")\n",
|
||
"graph.add_edge(\"hello\", END)\n",
|
||
"\n",
|
||
"# 실행\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"message\": \"\"})\n",
|
||
"print(result)\n",
|
||
"\n",
|
||
"# 그래프 시각화(참고)\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "977a81b9df795373",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'message': 'Hello, LangGraph!'}\n",
|
||
"+-----------+ \n",
|
||
"| __start__ | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +-------+ \n",
|
||
" | hello | \n",
|
||
" +-------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 3
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "markdown",
|
||
"source": [
|
||
"#### State\n",
|
||
"- 상태를 중심으로 동작\n",
|
||
"- TypeDict, Pydantic Base Model 을 사용하여 정의\n",
|
||
"- 그래프 실행 중 지속적으로 업데이트 됨\n",
|
||
"- 노드 간의 전환은 조건부 엣지를 통해 제어 가능하며 복잡한 의사결정 프로세스 모델링 간으\n",
|
||
"- 재귀적 실행 지원\n",
|
||
"\n",
|
||
"#### Node\n",
|
||
"- 실제 작업을 수행하는 기본단위\n",
|
||
"- 함수기반\n",
|
||
"- 상태중심 : 현재 상태를 입력으로 받아 처리\n",
|
||
"- 독립적 실행 : 각 노드는 독립적으로 실행\n",
|
||
"- 조합 가능 : 여러 노드를 연결하여 복잡한 워크플로 가능\n",
|
||
"\n",
|
||
"### Edge\n",
|
||
"- 노드간의 연결과 실행 흐름을 정의\n"
|
||
],
|
||
"id": "65888b5803542435"
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.485999045Z",
|
||
"start_time": "2026-06-08T11:00:03.389852542Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 카운터 공유\n",
|
||
"\n",
|
||
"class CounterState(TypedDict):\n",
|
||
" count: int\n",
|
||
"\n",
|
||
"# 증가 함수\n",
|
||
"def increment(state):\n",
|
||
" print(f\"현재 카운트 : {state['count']}\")\n",
|
||
" new_count = state['count'] + 1\n",
|
||
" print(f\"새로운 카운트 : {new_count}\")\n",
|
||
"\n",
|
||
" # state 값 변경(return)\n",
|
||
" return {\"count\": new_count}\n",
|
||
"\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(CounterState)\n",
|
||
"graph.add_node(\"increment\", increment)\n",
|
||
"\n",
|
||
"# 그래프 연결(그래프가 실행될 것인가?)\n",
|
||
"graph.add_edge(START, \"increment\")\n",
|
||
"graph.add_edge(\"increment\", END)\n",
|
||
"\n",
|
||
"# 그래프를 실행 가능한 형태로 변경\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"count\": 0})\n",
|
||
"print(f\"최종 결과 {result}\")\n",
|
||
"\n",
|
||
"# 그래프 시각화(참고)\n",
|
||
"app.get_graph().print_ascii()\n",
|
||
"\n"
|
||
],
|
||
"id": "3acb2357ac1825d7",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"현재 카운트 : 0\n",
|
||
"새로운 카운트 : 1\n",
|
||
"최종 결과 {'count': 1}\n",
|
||
"+-----------+ \n",
|
||
"| __start__ | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
"+-----------+ \n",
|
||
"| increment | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 4
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.537019587Z",
|
||
"start_time": "2026-06-08T11:00:03.491230609Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 2개의 노드\n",
|
||
"\n",
|
||
"def first_increment(state):\n",
|
||
" return {\"count\":state['count']+1}\n",
|
||
"\n",
|
||
"def second_increment(state):\n",
|
||
" return {\"count\":state['count']+10}\n",
|
||
"\n",
|
||
"class CounterState(TypedDict):\n",
|
||
" count: int\n",
|
||
"\n",
|
||
"# START => first => second => END\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(CounterState)\n",
|
||
"graph.add_node(\"first\",first_increment)\n",
|
||
"graph.add_node(\"second\", second_increment)\n",
|
||
"\n",
|
||
"# 그래프 연결(그래프가 실행될 것인가?)\n",
|
||
"graph.add_edge(START, \"first\")\n",
|
||
"graph.add_edge(\"first\", 'second')\n",
|
||
"graph.add_edge('second', END)\n",
|
||
"\n",
|
||
"# 그래프를 실행 가능한 형태로 변경\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"count\": 0})\n",
|
||
"print(f\"최종 결과 {result}\")\n",
|
||
"\n",
|
||
"# 그래프 시각화(참고)\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "d0b60e65d488a596",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"최종 결과 {'count': 11}\n",
|
||
"+-----------+ \n",
|
||
"| __start__ | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +-------+ \n",
|
||
" | first | \n",
|
||
" +-------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +--------+ \n",
|
||
" | second | \n",
|
||
" +--------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 5
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "markdown",
|
||
"source": [
|
||
"- 조건부 엣지\n",
|
||
" - 런타임 상태에 따라 동적으로 실행 경로 결정\n",
|
||
" - add_conditional_edges()"
|
||
],
|
||
"id": "c516f3c13d3ab6e7"
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.599429285Z",
|
||
"start_time": "2026-06-08T11:00:03.548093598Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 입력숫자 > 10 => big\n",
|
||
"# 입력숫자 > 10 => small\n",
|
||
"\n",
|
||
"# State : 숫자, 결과\n",
|
||
"class NumberState(TypedDict):\n",
|
||
" number : int\n",
|
||
" result : str\n",
|
||
"\n",
|
||
"# 노드(result 값 변경)\n",
|
||
"def handle_big_number(state):\n",
|
||
" return {'result':f\"{state['number']}는 큰 숫자입니다.\"}\n",
|
||
"\n",
|
||
"def handle_small_number(state):\n",
|
||
" return {'result':f\"{state['number']}는 작은 숫자입니다.\"}\n",
|
||
"\n",
|
||
"# 라우터(조건함수)\n",
|
||
"def check_size(state):\n",
|
||
" if state['number'] > 10:\n",
|
||
" return \"big\"\n",
|
||
" else:\n",
|
||
" return \"small\"\n",
|
||
"\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(NumberState)\n",
|
||
"graph.add_node(\"big_handler\", handle_big_number)\n",
|
||
"graph.add_node(\"small_handler\", handle_small_number)\n",
|
||
"\n",
|
||
"# 엣지\n",
|
||
"graph.add_edge(\"big_handler\", END)\n",
|
||
"graph.add_edge(\"small_handler\", END)\n",
|
||
"\n",
|
||
"# 조건부 엣지(number 값에 따라 big? small?)\n",
|
||
"graph.add_conditional_edges(START, check_size, {'big':'big_handler', \"small\":\"small_handler\"})\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"number\": 15, \"result\" : \"\"})\n",
|
||
"print(f\"큰 숫자 {result}\")\n",
|
||
"\n",
|
||
"result = app.invoke({\"number\": 5, \"result\" : \"\"})\n",
|
||
"print(f\"작은 숫자 {result}\")\n",
|
||
"\n",
|
||
"# 그래프 시각화(참고)\n",
|
||
"app.get_graph().print_ascii()\n"
|
||
],
|
||
"id": "acf3f40b4d866477",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"큰 숫자 {'number': 15, 'result': '15는 큰 숫자입니다.'}\n",
|
||
"작은 숫자 {'number': 5, 'result': '5는 작은 숫자입니다.'}\n",
|
||
" +-----------+ \n",
|
||
" | __start__ | \n",
|
||
" +-----------+ \n",
|
||
" .. .. \n",
|
||
" .. .. \n",
|
||
" .. .. \n",
|
||
"+-------------+ +---------------+ \n",
|
||
"| big_handler | | small_handler | \n",
|
||
"+-------------+ +---------------+ \n",
|
||
" ** ** \n",
|
||
" ** ** \n",
|
||
" ** ** \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 6
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.650967440Z",
|
||
"start_time": "2026-06-08T11:00:03.606842460Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 홀, 짝\n",
|
||
"\n",
|
||
"# State : 숫자, 결과\n",
|
||
"class NumberState(TypedDict):\n",
|
||
" number : int\n",
|
||
" result : str\n",
|
||
"\n",
|
||
"# 노드(result 값 변경)\n",
|
||
"def even_node(state):\n",
|
||
" return {'result':f\"{state['number']}는 짝수입니다.\"}\n",
|
||
"\n",
|
||
"def odd_node(state):\n",
|
||
" return {'result':f\"{state['number']}는 홀수입니다.\"}\n",
|
||
"\n",
|
||
"# 라우터(조건함수)\n",
|
||
"def check_number(state):\n",
|
||
" if state['number'] % 2 == 0:\n",
|
||
" return \"even\"\n",
|
||
" else:\n",
|
||
" return \"odd\"\n",
|
||
"\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(NumberState)\n",
|
||
"graph.add_node(\"even_handler\", even_node)\n",
|
||
"graph.add_node(\"odd_handler\", odd_node)\n",
|
||
"\n",
|
||
"# 엣지\n",
|
||
"graph.add_edge(\"even_handler\", END)\n",
|
||
"graph.add_edge(\"odd_handler\", END)\n",
|
||
"\n",
|
||
"# 조건부 엣지(number 값에 따라 even? odd)\n",
|
||
"graph.add_conditional_edges(START, check_number, {'even':'even_handler', \"odd\":\"odd_handler\"})\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"number\": 15, \"result\" : \"\"})\n",
|
||
"print(f\"홀수 {result}\")\n",
|
||
"\n",
|
||
"result = app.invoke({\"number\": 6, \"result\" : \"\"})\n",
|
||
"print(f\"짝수 {result}\")\n",
|
||
"\n",
|
||
"# 그래프 시각화(참고)\n",
|
||
"app.get_graph().print_ascii()\n"
|
||
],
|
||
"id": "6900d17fc36539a3",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"홀수 {'number': 15, 'result': '15는 홀수입니다.'}\n",
|
||
"짝수 {'number': 6, 'result': '6는 짝수입니다.'}\n",
|
||
" +-----------+ \n",
|
||
" | __start__ | \n",
|
||
" +-----------+ \n",
|
||
" .. .. \n",
|
||
" .. .. \n",
|
||
" .. .. \n",
|
||
"+--------------+ +-------------+ \n",
|
||
"| even_handler | | odd_handler | \n",
|
||
"+--------------+ +-------------+ \n",
|
||
" ** ** \n",
|
||
" ** ** \n",
|
||
" ** ** \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 7
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.705048993Z",
|
||
"start_time": "2026-06-08T11:00:03.656007893Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"class ScoreState(TypedDict):\n",
|
||
" score:int\n",
|
||
"\n",
|
||
"# 노드 return => 상태값 변경\n",
|
||
"# return 을 안하면 None\n",
|
||
"def grade_a(state):\n",
|
||
" print(\"A 학점\")\n",
|
||
" # score = None\n",
|
||
" return {}\n",
|
||
"\n",
|
||
"def grade_b(state):\n",
|
||
" print(\"B 학점\")\n",
|
||
" return {}\n",
|
||
"\n",
|
||
"def grade_c(state):\n",
|
||
" print(\"C 학점\")\n",
|
||
" return {}\n",
|
||
"\n",
|
||
"# >=90 A, >=80 B, 나머지C,\n",
|
||
"# 라우터(조건함수)\n",
|
||
"def route_grade(state):\n",
|
||
" if state['score'] >= 90:\n",
|
||
" return \"A\"\n",
|
||
" elif state['score'] >= 80:\n",
|
||
" return \"B\"\n",
|
||
" else:\n",
|
||
" return \"C\"\n",
|
||
"\n",
|
||
"graph = StateGraph(ScoreState)\n",
|
||
"graph.add_node(\"grade_a\", grade_a)\n",
|
||
"graph.add_node(\"grade_b\", grade_b)\n",
|
||
"graph.add_node(\"grade_c\", grade_c)\n",
|
||
"\n",
|
||
"graph.add_edge(\"grade_a\", END)\n",
|
||
"graph.add_edge(\"grade_b\", END)\n",
|
||
"graph.add_edge(\"grade_c\", END)\n",
|
||
"\n",
|
||
"graph.add_conditional_edges(START, route_grade, {'A':'grade_a', \"B\":\"grade_b\", \"C\":\"grade_c\"})\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"score\": 95, \"result\" : \"\"})\n",
|
||
"print(f\"학점 {result}\")\n",
|
||
"\n",
|
||
"result = app.invoke({\"score\": 88, \"result\" : \"\"})\n",
|
||
"print(f\"학점 {result}\")\n",
|
||
"\n",
|
||
"# app.get_graph().print_ascii()"
|
||
],
|
||
"id": "3ec626fba58ea942",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"A 학점\n",
|
||
"학점 {'score': 95}\n",
|
||
"B 학점\n",
|
||
"학점 {'score': 88}\n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 8
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.767196417Z",
|
||
"start_time": "2026-06-08T11:00:03.711901025Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 상태관리 : text, sentiment, result\n",
|
||
"# text : 오늘 기분이 너무 좋아\n",
|
||
"# analyze_sentiment() : 감정평가 text 좋 positive / 싫 negative / x neutral\n",
|
||
"# positive_node() : 긍정 의견 / negative_node() : 부정 의견 / neutral_node() : 중립 의견 => result 업데이트\n",
|
||
"# route_sentiment : return state['sentiment']\n",
|
||
"\n",
|
||
"# START => analyze => positive / negative / neutral => route_sentiment => positive_node / negative_node / neutral_node => END\n",
|
||
"\n",
|
||
"class SentimentState(TypedDict):\n",
|
||
" text : str # 입력 문장\n",
|
||
" sentiment : str # 감정 결과 (positive / negative / neutral)\n",
|
||
" result : str # 최종 출력 메시지\n",
|
||
"\n",
|
||
"def analyze_sentiment(state):\n",
|
||
" text = state[\"text\"]\n",
|
||
"\n",
|
||
" if \"좋\" in text:\n",
|
||
" sentiment = \"positive\"\n",
|
||
" elif \"싫\" in text:\n",
|
||
" sentiment = \"negative\"\n",
|
||
" else:\n",
|
||
" sentiment = \"neutral\"\n",
|
||
"\n",
|
||
" return {\"sentiment\": sentiment}\n",
|
||
"\n",
|
||
"def positive_node(state):\n",
|
||
" return {\"result\": \"긍정 의견\"}\n",
|
||
"\n",
|
||
"def negative_node(state):\n",
|
||
" return {\"result\": \"부정 의견\"}\n",
|
||
"\n",
|
||
"def neutral_node(state):\n",
|
||
" return {\"result\": \"중립 의견\"}\n",
|
||
"\n",
|
||
"# 라우터(조건함수)\n",
|
||
"def route_sentiment(state):\n",
|
||
" return state['sentiment']\n",
|
||
"\n",
|
||
"graph = StateGraph(SentimentState)\n",
|
||
"graph.add_node(\"analyze\", analyze_sentiment)\n",
|
||
"graph.add_node(\"positive\", positive_node)\n",
|
||
"graph.add_node(\"negative\", negative_node)\n",
|
||
"graph.add_node(\"neutral\", neutral_node)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"analyze\")\n",
|
||
"graph.add_edge(\"positive\", END)\n",
|
||
"graph.add_edge(\"negative\", END)\n",
|
||
"graph.add_edge(\"neutral\", END)\n",
|
||
"\n",
|
||
"graph.add_conditional_edges(\"analyze\", route_sentiment, {'positive':'positive', \"negative\":\"negative\", \"neutral\":\"neutral\"}) # 라우터 결과가 'positive'면 ->️ 'positive' 노드로 가라!\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"text\": \"좋\"})\n",
|
||
"print(f\"{result}\")\n",
|
||
"\n",
|
||
"result = app.invoke({\"text\": \"싫\"})\n",
|
||
"print(f\"{result}\")\n",
|
||
"\n",
|
||
"result = app.invoke({\"text\": \"밥\"})\n",
|
||
"print(f\"{result}\")\n",
|
||
"\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "94202953b8b8be5f",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"{'text': '좋', 'sentiment': 'positive', 'result': '긍정 의견'}\n",
|
||
"{'text': '싫', 'sentiment': 'negative', 'result': '부정 의견'}\n",
|
||
"{'text': '밥', 'sentiment': 'neutral', 'result': '중립 의견'}\n",
|
||
" +-----------+ \n",
|
||
" | __start__ | \n",
|
||
" +-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | analyze | \n",
|
||
" ...+---------+.... \n",
|
||
" .... . .... \n",
|
||
" .... . .... \n",
|
||
" .. . .. \n",
|
||
"+----------+ +---------+ +----------+ \n",
|
||
"| negative | | neutral | | positive | \n",
|
||
"+----------+**** +---------+ ***+----------+ \n",
|
||
" **** * **** \n",
|
||
" **** * **** \n",
|
||
" ** * ** \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 9
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:03.794644656Z",
|
||
"start_time": "2026-06-08T11:00:03.778761416Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# .env 내용 가져오기\n",
|
||
"load_dotenv()\n",
|
||
"\n",
|
||
"apikey = os.getenv(\"WATSONX_API_KEY\")\n",
|
||
"project_id = os.getenv(\"WATSONX_PROJECT_ID\")\n",
|
||
"watsonx_ai_url = os.getenv(\"WATSONX_URL\")\n",
|
||
"hf_token = os.getenv(\"HF_TOKEN\")\n",
|
||
"COHERE_API_KEY = os.getenv(\"COHERE_API_KEY\")\n",
|
||
"SERPER_API_KEY = os.getenv(\"SERPER_API_KEY\")\n",
|
||
"GEMINI_API_KEY = os.getenv(\"GEMINI_API_KEY\")\n",
|
||
"LANGSMITH_API_KEY = os.getenv(\"LANGSMITH_API_KEY\")"
|
||
],
|
||
"id": "c2b2990c27f33682",
|
||
"outputs": [],
|
||
"execution_count": 10
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:00:11.384010221Z",
|
||
"start_time": "2026-06-08T11:00:03.798592437Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"watson_llm = ChatWatsonx(\n",
|
||
" model_id=\"ibm/granite-4-h-small\",\n",
|
||
" url=f\"{watsonx_ai_url}\",\n",
|
||
" api_key=f\"{apikey}\",\n",
|
||
" project_id=f\"{project_id}\",\n",
|
||
" max_tokens=2000,\n",
|
||
" params={\n",
|
||
" \"temperature\": 0\n",
|
||
" }\n",
|
||
")\n",
|
||
"\n",
|
||
"watson_embedding = WatsonxEmbeddings(\n",
|
||
" model_id=\"ibm/granite-embedding-278m-multilingual\",\n",
|
||
" url=f\"{watsonx_ai_url}\",\n",
|
||
" api_key=f\"{apikey}\",\n",
|
||
" project_id=f\"{project_id}\"\n",
|
||
")\n",
|
||
"\n",
|
||
"hugging_llm = ChatOpenAI(\n",
|
||
" model=\"Qwen/Qwen2.5-7B-Instruct:together\",\n",
|
||
" api_key=hf_token,\n",
|
||
" base_url=\"https://router.huggingface.co/v1\",\n",
|
||
" temperature=0,\n",
|
||
")\n",
|
||
"\n",
|
||
"ollama_embedding = OllamaEmbeddings(model=\"nomic-embed-text-v2-moe\")\n",
|
||
"\n",
|
||
"# 로컬 LLM\n",
|
||
"qwen_llm = ChatOllama(model=\"qwen3.5:4b\", temperature=0)\n",
|
||
"exaone_llm = ChatOllama(model=\"exaone3.5:2.4b\", temperature=0)\n",
|
||
"gemma_llm = ChatOllama(model=\"gemma4:e2b\")"
|
||
],
|
||
"id": "f5fdb0bea4930484",
|
||
"outputs": [],
|
||
"execution_count": 11
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:03:06.386132558Z",
|
||
"start_time": "2026-06-08T11:03:04.111106960Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"parser = StrOutputParser()\n",
|
||
"\n",
|
||
"translate_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"다음 텍스트를 한국어로 번역하세요. 번역문만 출력:\\n{text}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"summarize_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"다음 텍스트를 3문장으로 요약하세요.:\\n{text}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"sentiment_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"다음 텍스트를 의 감정을 긍정/부정/중립 중 하나로만 답하세요.:\\n{summary}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"# State 정의\n",
|
||
"class AnalysisState(TypedDict):\n",
|
||
" text: str\n",
|
||
" translated: str\n",
|
||
" summary: str\n",
|
||
" sentiment: str\n",
|
||
" done: bool\n",
|
||
"\n",
|
||
"# node 정의\n",
|
||
"def translated_node(state):\n",
|
||
" result = translate_chain.invoke({\"text\":state['text']})\n",
|
||
" return {\"translated\":result}\n",
|
||
"\n",
|
||
"def summary_node(state):\n",
|
||
" result = summarize_chain.invoke({'text':state['translated']})\n",
|
||
" return {\"summary\":result}\n",
|
||
"\n",
|
||
"def sentiment_node(state):\n",
|
||
" result = sentiment_chain.invoke({'summary':state['summary']})\n",
|
||
" return {\"sentiment\":result}\n",
|
||
"\n",
|
||
"# 그래프 생성\n",
|
||
"graph = StateGraph(AnalysisState)\n",
|
||
"graph.add_node(\"translate\", translated_node)\n",
|
||
"graph.add_node(\"summarize\", summary_node)\n",
|
||
"graph.add_node(\"sentiment\", sentiment_node)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"translate\")\n",
|
||
"graph.add_edge(\"translate\", \"summarize\")\n",
|
||
"graph.add_edge(\"summarize\", \"sentiment\")\n",
|
||
"graph.add_edge(\"sentiment\", END)\n",
|
||
"\n",
|
||
"# 그래프 실행\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({'text': 'Python is grate!!', \"done\":False})\n",
|
||
"print(\"번역 : \", result['translated'])\n",
|
||
"print(\"요약 : \", result['summary'])\n",
|
||
"print(\"감정 : \", result['sentiment'])\n",
|
||
"\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "75b0102055a3b951",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"번역 : 파이썬은 대단해요!!\n",
|
||
"요약 : 파이썬은 대단한 프로그래밍 언어입니다. 파이썬은 간결하고 읽기 쉬운 문법을 가지고 있어 초보자에게도 적합합니다. 또한 다양한 라이브러리와 프레임워크를 제공하여 다양한 분야에서 활용될 수 있습니다.\n",
|
||
"감정 : 긍정\n",
|
||
"+-----------+ \n",
|
||
"| __start__ | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
"+-----------+ \n",
|
||
"| translate | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
"+-----------+ \n",
|
||
"| summarize | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
"+-----------+ \n",
|
||
"| sentiment | \n",
|
||
"+-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | __end__ | \n",
|
||
" +---------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 14
|
||
},
|
||
{
|
||
"metadata": {
|
||
"ExecuteTime": {
|
||
"end_time": "2026-06-08T11:07:53.195678876Z",
|
||
"start_time": "2026-06-08T11:07:51.827957635Z"
|
||
}
|
||
},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# 조건부엣지 : summary >= 100자 이상 good / 100자 미만 poor\n",
|
||
"\n",
|
||
"class SummaryState(TypedDict):\n",
|
||
" text:str\n",
|
||
" summary:str\n",
|
||
" quality:str\n",
|
||
" retries:int\n",
|
||
"\n",
|
||
"def summary_node(state):\n",
|
||
" result = summarize_chain.invoke({'text':state['text']})\n",
|
||
" return {\"summary\":result}\n",
|
||
"\n",
|
||
"# quality\n",
|
||
"def check_quality_node(state):\n",
|
||
" # summary >= 100 자 이상 good / 100자 미만 poor\n",
|
||
" quality = \"good\" if len(state['summary']) >= 100 else \"poor\"\n",
|
||
" return {\"quality\":quality}\n",
|
||
"\n",
|
||
"def retry_node(state):\n",
|
||
" return {\"text\":state['text']+\"\\n 더 자세히 요약하세요\", \"retries\":state['retries'] + 1}\n",
|
||
"\n",
|
||
"def route_by_quality(state):\n",
|
||
" if state['quality'] == 'poor' and state['retries'] < 3:\n",
|
||
" return \"retry\"\n",
|
||
" return \"done\"\n",
|
||
"\n",
|
||
"graph = StateGraph(SummaryState)\n",
|
||
"graph.add_node(\"summary\", summary_node)\n",
|
||
"graph.add_node(\"check_quality\", check_quality_node)\n",
|
||
"graph.add_node(\"retry\", retry_node)\n",
|
||
"\n",
|
||
"# START -> summary -> check_quality\n",
|
||
"graph.add_edge(START, \"summary\")\n",
|
||
"graph.add_edge(\"summary\", \"check_quality\")\n",
|
||
"graph.add_conditional_edges(\"check_quality\", route_by_quality, {\"retry\": \"retry\", \"done\" : END})\n",
|
||
"graph.add_edge(\"retry\", \"summary\")\n",
|
||
"\n",
|
||
"# 그래프 실행\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({'text': '잛은 글'})\n",
|
||
"print(f\"최종요약({len(result['summary'])}자): {result['summary'][:100]}\")\n",
|
||
"\n",
|
||
"app.get_graph().print_ascii()\n"
|
||
],
|
||
"id": "b5a849ba6c73ae41",
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"최종요약(150자): 제공된 텍스트는 질문이 없는 한 문장으로, 요약할 내용이 없습니다. 그러나 이 문장을 3문장으로 요약하면 다음과 같습니다:\n",
|
||
"\n",
|
||
"1. 이 프롬프트는 요약을 요청하는 것입니다.\n",
|
||
"2. 하\n",
|
||
" +-----------+ \n",
|
||
" | __start__ | \n",
|
||
" +-----------+ \n",
|
||
" * \n",
|
||
" * \n",
|
||
" * \n",
|
||
" +---------+ \n",
|
||
" | summary | \n",
|
||
" +---------+ \n",
|
||
" *** *** \n",
|
||
" * * \n",
|
||
" ** *** \n",
|
||
"+---------------+ * \n",
|
||
"| check_quality | * \n",
|
||
"+---------------+. * \n",
|
||
" . ..... * \n",
|
||
" . ... * \n",
|
||
" . ... * \n",
|
||
" +---------+ +-------+ \n",
|
||
" | __end__ | | retry | \n",
|
||
" +---------+ +-------+ \n"
|
||
]
|
||
}
|
||
],
|
||
"execution_count": 17
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"class VerifyState(TypedDict):\n",
|
||
" question:str\n",
|
||
" answer:str\n",
|
||
" feedback:str\n",
|
||
" is_verified:bool\n",
|
||
" attempt:int\n",
|
||
"\n",
|
||
"def generate(state):\n",
|
||
" \"\"\"답변을 생성합니다. 이전 피드백이 있으면 반영합니다.\"\"\"\n",
|
||
" prompt = f\"질문 : {state['question']}\"\n",
|
||
"\n",
|
||
" if state['feedback']:\n",
|
||
" prompt += f\"\\n\\n이전 답변의 피드백 : {state['feedback']}\\n위 피드백을 반영하여 개선된 답변을 작성하세요\"\n",
|
||
"\n",
|
||
" result = watson_llm.invoke(prompt)\n",
|
||
" return {'answer':result.content, \"attempt\":state['attempt'] + 1}\n",
|
||
"\n",
|
||
"def verify(state):\n",
|
||
" \"\"\"답변의 정확성과 완전성을 검증합니다.\"\"\"\n",
|
||
" verification = watson_llm.invoke(f\"\"\"\n",
|
||
"다음 답변의 정확성과 완정성을 검증하세요\n",
|
||
"\n",
|
||
"질문 :\n",
|
||
"{state['question']}\n",
|
||
"\n",
|
||
"답변 :\n",
|
||
"{state['answer']}\n",
|
||
"\n",
|
||
"정확하고 완전하면 첫 줄에 'PASS' 를, 수정이 필요하면 첫줄에 'FAIL' 을 쓰고 구체적인 개선사항을 설명하세요.\n",
|
||
"\"\"\")\n",
|
||
"\n",
|
||
" content = verification.content\n",
|
||
" is_pass = content.strip().startswith('PASS')\n",
|
||
"\n",
|
||
" return {'is_verified':is_pass, 'feedback':content}\n",
|
||
"\n",
|
||
"# router 개념\n",
|
||
"def should_retry(state):\n",
|
||
" \"\"\"검증 통과 또는 최대 횟수 도달시 종료\"\"\"\n",
|
||
" if state['is_verified'] or state['attempt'] >= 3:\n",
|
||
" return END\n",
|
||
" return 'generate'\n",
|
||
"\n",
|
||
"# 질문 => LLM 답변 생성 => LLM 답변 검증 => 검증통과\n",
|
||
"# 검증 미통과 => LLM 답변 생성\n",
|
||
"\n",
|
||
"graph = StateGraph(VerifyState)\n",
|
||
"graph.add_node(\"generate\", generate)\n",
|
||
"graph.add_node(\"verify\", verify)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"generate\")\n",
|
||
"graph.add_edge(\"generate\", \"verify\")\n",
|
||
"graph.add_conditional_edges(\"verify\", should_retry, {\"generate\": \"generate\", END: END})\n",
|
||
"\n",
|
||
"# 그래프 실행\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({'question': '파이썬에서 GIL이 무엇이며 멀티쓰레딩에 어떤 영향을 주는지 설명해줘', \"answer\":\"\",\"feedback\":\"\", \"is_verified\":False, \"attempt\":0})\n",
|
||
"\n",
|
||
"print(\"시도 횟수\", result['attempt'])\n",
|
||
"print(\"검증통과\", result['is_verified'])\n",
|
||
"print(\"최종답변\", result['answer'][:300])\n",
|
||
"\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "686634fe1625b815",
|
||
"outputs": [],
|
||
"execution_count": null
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"import time\n",
|
||
"from langchain_core.runnables import RunnableParallel\n",
|
||
"\n",
|
||
"class AnalysisState(TypedDict):\n",
|
||
" text:str\n",
|
||
" translated:str\n",
|
||
" summary:str\n",
|
||
" keywords:list[str]\n",
|
||
"\n",
|
||
"translate_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"다음 텍스트를 한국어로 번역하세요. 번역문만 출력:\\n{text}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"summarize_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"다음 텍스트를 3문장으로 번역하세요.:\\n{text}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"keyword_chain = ChatPromptTemplate.from_messages([\n",
|
||
" (\"system\", \"키워드 5개 추출:\\n{text}.\"),\n",
|
||
"]) | watson_llm | parser\n",
|
||
"\n",
|
||
"def translate_node(state):\n",
|
||
" print(\"번역 시작\")\n",
|
||
" time.sleep(3)\n",
|
||
" print(\"번역 종료\")\n",
|
||
" return {\"translated\": translate_chain.invoke({\"text\":state['text']})}\n",
|
||
"\n",
|
||
"def summarize_node(state):\n",
|
||
" print(\"요약 시작\")\n",
|
||
" time.sleep(3)\n",
|
||
" print(\"요약 종료\")\n",
|
||
" return {\"summary\": summarize_chain.invoke({\"text\":state['text']})}\n",
|
||
"\n",
|
||
"def keyword_node(state):\n",
|
||
" print(\"키워드 추출 시작\")\n",
|
||
" time.sleep(3)\n",
|
||
" print(\"키워드 추출 종료\")\n",
|
||
" return {\"keywords\": keyword_chain.invoke({\"text\":state['text']})}\n",
|
||
"\n",
|
||
"graph = StateGraph(AnalysisState)\n",
|
||
"graph.add_node('translated', translated_node)\n",
|
||
"graph.add_node('summary', summarize_node)\n",
|
||
"graph.add_node('keywords', keyword_node)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"translated\")\n",
|
||
"graph.add_edge(START, \"summary\")\n",
|
||
"graph.add_edge(START, \"keywords\")\n",
|
||
"\n",
|
||
"graph.add_edge(\"translated\", END)\n",
|
||
"graph.add_edge(\"summary\", END)\n",
|
||
"graph.add_edge(\"keywords\", END)\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"text = {'text' : 'Python is a versatile language used in AI and web development'}\n",
|
||
"result = app.invoke(text)\n",
|
||
"\n",
|
||
"print(\"번역\", result['translated'][:100])\n",
|
||
"print(\"요약\", result['summary'][:100])\n",
|
||
"print(\"키워드\", result['keywords'][:100])\n",
|
||
"\n",
|
||
"app.get_graph().print_ascii()"
|
||
],
|
||
"id": "956e5a8f89675559",
|
||
"outputs": [],
|
||
"execution_count": null
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "markdown",
|
||
"source": [
|
||
"- rag LangGraph\n",
|
||
" - 사용자 질문 => retrieve => generate => END"
|
||
],
|
||
"id": "e9270ebbd217d878"
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# !pip install langchain_community\n",
|
||
"from langchain_community.document_loaders import PyPDFLoader, CSVLoader, WebBaseLoader, DirectoryLoader\n",
|
||
"from youtube_transcript_api import YouTubeTranscriptApi\n",
|
||
"from langchain_core.documents import Document\n",
|
||
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
|
||
"from langchain_ollama import OllamaEmbeddings\n",
|
||
"from langchain_ibm import WatsonxEmbeddings\n",
|
||
"from langchain_chroma import Chroma\n",
|
||
"from langchain_community.vectorstores import FAISS"
|
||
],
|
||
"id": "d03e2b9989d53cb2",
|
||
"outputs": [],
|
||
"execution_count": null
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"# STEP 1 : 문서로드\n",
|
||
"loader = PyPDFLoader(\"./data/Summary of ChatGPTGPT-4 Research.pdf\")\n",
|
||
"# STEP 2 : 문서분할\n",
|
||
"splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)\n",
|
||
"chunks = splitter.split_documents(loader.load())\n",
|
||
"print(f\"chunks 수 {chunks}\")\n",
|
||
"# STEP 3 : 인덱싱 - 임베딩\n",
|
||
"# STEP 4 : 벡터스토어(Chroma or FAISS)\n",
|
||
"vectorstore = Chroma.from_documents(chunks, watson_embedding, persist_directory=\"./db/chroma_db\", collection_name=\"research\")\n",
|
||
"# STEP 5 : as_retriever() : Vector Store 를 Retriever 형태로 변환하여 LangChain 에 연결\n",
|
||
"retriever = vectorstore.as_retriever(search_type=\"similarity\", search_kwargs={\"k\":3})"
|
||
],
|
||
"id": "1f0fda34f40177ea",
|
||
"outputs": [],
|
||
"execution_count": null
|
||
},
|
||
{
|
||
"metadata": {},
|
||
"cell_type": "code",
|
||
"source": [
|
||
"from typing import List\n",
|
||
"from langchain_core.documents import Document\n",
|
||
"\n",
|
||
"class RAGState(TypedDict):\n",
|
||
" query:str\n",
|
||
" retriever_docs:list[Document]\n",
|
||
" answer:str\n",
|
||
"\n",
|
||
"def retrieve(state):\n",
|
||
" # 기존의 벡터스토어에 질의\n",
|
||
" vectorstore = Chroma(collection_name=\"research\", embedding_function=watson_embedding, persist_directory=\"./db/chroma_db\")\n",
|
||
"\n",
|
||
" docs = vectorstore.similarity_search(state['query'], k=3)\n",
|
||
"\n",
|
||
" return {\"retriever_docs\" : docs}\n",
|
||
"\n",
|
||
"def generate(state):\n",
|
||
" context = \"\\n\\n\".join(doc.page_content for doc in state['retriever_docs'])\n",
|
||
"\n",
|
||
" prompt = \"\"\"\\\n",
|
||
"다음 컨텍스트를 참고하여 질문에 답하세요.\n",
|
||
"컨텍스트에 없는 내용은 모른다고 답하세요.\n",
|
||
"\n",
|
||
"컨텍스트:\n",
|
||
"{context}\n",
|
||
"질문:\n",
|
||
"{query}\n",
|
||
"\"\"\"\n",
|
||
" response = watson_llm.invoke(prompt.format(context=context, query=state['query']))\n",
|
||
" return {\"answer\":response.content}\n",
|
||
"\n",
|
||
"graph = StateGraph(RAGState)\n",
|
||
"graph.add_node(\"retrieve\", retrieve)\n",
|
||
"graph.add_node(\"generate\", generate)\n",
|
||
"\n",
|
||
"graph.add_edge(START, \"retrieve\")\n",
|
||
"graph.add_edge(\"retrieve\", \"generate\")\n",
|
||
"graph.add_edge(\"generate\", END)\n",
|
||
"\n",
|
||
"app = graph.compile()\n",
|
||
"result = app.invoke({\"query\":\"where can i use chatGPT?\"})\n",
|
||
"print(result['answer'])"
|
||
],
|
||
"id": "bcf8f539b91ab2b7",
|
||
"outputs": [],
|
||
"execution_count": null
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Python 3",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 2
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython2",
|
||
"version": "2.7.6"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|