Yoon-gu Hwang
commited on
Commit
·
1ebd829
1
Parent(s):
00237bb
update langgraph + gradio
Browse files- app_tool.py +420 -0
- pyproject.toml +3 -0
- uv.lock +0 -0
app_tool.py
ADDED
@@ -0,0 +1,420 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from langgraph.graph import StateGraph, END
|
3 |
+
from typing import TypedDict, Annotated
|
4 |
+
import operator
|
5 |
+
from langchain_core.messages import HumanMessage, AIMessage, BaseMessage
|
6 |
+
from langchain_openai import ChatOpenAI
|
7 |
+
import asyncio
|
8 |
+
import time
|
9 |
+
import os
|
10 |
+
|
11 |
+
# 상태 정의
|
12 |
+
class ChatState(TypedDict):
|
13 |
+
messages: Annotated[list[BaseMessage], operator.add]
|
14 |
+
current_response: str
|
15 |
+
agent_type: str
|
16 |
+
context: dict
|
17 |
+
step_info: str
|
18 |
+
|
19 |
+
# LLM 초기화
|
20 |
+
llm = ChatOpenAI(
|
21 |
+
model="gpt-3.5-turbo",
|
22 |
+
temperature=0.7,
|
23 |
+
# api_key="your-openai-api-key" # 실제 API 키를 설정하세요
|
24 |
+
)
|
25 |
+
|
26 |
+
def step1_analyzer_node(state: ChatState) -> ChatState:
|
27 |
+
"""1단계: 메시지 분석 노드"""
|
28 |
+
last_message = state["messages"][-1].content
|
29 |
+
|
30 |
+
# 분석 시뮬레이션 (실제로는 더 복잡한 로직)
|
31 |
+
analysis = {
|
32 |
+
"length": len(last_message),
|
33 |
+
"has_question": "?" in last_message,
|
34 |
+
"language": "korean" if any(ord(c) > 127 for c in last_message) else "english",
|
35 |
+
"sentiment": "positive" if any(word in last_message.lower() for word in ["좋", "감사", "고마워", "thanks", "good"]) else "neutral"
|
36 |
+
}
|
37 |
+
|
38 |
+
return {
|
39 |
+
"messages": [],
|
40 |
+
"current_response": "",
|
41 |
+
"agent_type": "",
|
42 |
+
"context": {"analysis": analysis},
|
43 |
+
"step_info": f"📊 **1단계 완료** - 메시지 분석\n- 길이: {analysis['length']}자\n- 질문 포함: {'예' if analysis['has_question'] else '아니오'}\n- 언어: {analysis['language']}\n- 감정: {analysis['sentiment']}"
|
44 |
+
}
|
45 |
+
|
46 |
+
def step2_classifier_node(state: ChatState) -> ChatState:
|
47 |
+
"""2단계: 의도 분류 노드"""
|
48 |
+
last_message = state["messages"][-1].content.lower()
|
49 |
+
analysis = state["context"]["analysis"]
|
50 |
+
|
51 |
+
# 의도 분류 로직
|
52 |
+
if any(word in last_message for word in ["코드", "프로그래밍", "python", "개발", "함수", "클래스"]):
|
53 |
+
agent_type = "programmer"
|
54 |
+
confidence = 0.9
|
55 |
+
elif any(word in last_message for word in ["날씨", "뉴스", "정보", "검색", "찾아"]):
|
56 |
+
agent_type = "informer"
|
57 |
+
confidence = 0.8
|
58 |
+
elif any(word in last_message for word in ["계산", "수학", "더하기", "빼기", "곱하기", "나누기"]):
|
59 |
+
agent_type = "calculator"
|
60 |
+
confidence = 0.95
|
61 |
+
elif any(word in last_message for word in ["창작", "시", "소설", "이야기", "글"]):
|
62 |
+
agent_type = "creative"
|
63 |
+
confidence = 0.85
|
64 |
+
else:
|
65 |
+
agent_type = "general"
|
66 |
+
confidence = 0.7
|
67 |
+
|
68 |
+
context = state["context"]
|
69 |
+
context["classification"] = {
|
70 |
+
"agent_type": agent_type,
|
71 |
+
"confidence": confidence
|
72 |
+
}
|
73 |
+
|
74 |
+
return {
|
75 |
+
"messages": [],
|
76 |
+
"current_response": "",
|
77 |
+
"agent_type": agent_type,
|
78 |
+
"context": context,
|
79 |
+
"step_info": f"🎯 **2단계 완료** - 의도 분류\n- 분류 결과: {agent_type}\n- 신뢰도: {confidence:.1%}\n- 다음 단계: {'전문 처리' if confidence > 0.8 else '일반 처리'}"
|
80 |
+
}
|
81 |
+
|
82 |
+
def step3_context_enricher_node(state: ChatState) -> ChatState:
|
83 |
+
"""3단계: 컨텍스트 강화 노드"""
|
84 |
+
agent_type = state["agent_type"]
|
85 |
+
|
86 |
+
# 에이전트 타입별 컨텍스트 강화
|
87 |
+
enriched_context = {
|
88 |
+
"programmer": {
|
89 |
+
"system_prompt": "당신은 경험이 풍부한 시니어 개발자입니다. 코드 예시와 함께 명확하고 실용적인 답변을 제공하세요.",
|
90 |
+
"tools": ["코드 실행", "문서 검색", "베스트 프랙티스"],
|
91 |
+
"style": "기술적이고 정확한"
|
92 |
+
},
|
93 |
+
"informer": {
|
94 |
+
"system_prompt": "당신은 정보 전문가입니다. 정확하고 최신의 정보를 구조화된 형태로 제공하세요.",
|
95 |
+
"tools": ["웹 검색", "팩트 체크", "데이터 분석"],
|
96 |
+
"style": "객관적이고 상세한"
|
97 |
+
},
|
98 |
+
"calculator": {
|
99 |
+
"system_prompt": "당신은 수학 전문가입니다. 계산 과정을 단계별로 설명하고 정확한 답을 제공하세요.",
|
100 |
+
"tools": ["수식 계산", "그래프 생성", "통계 분석"],
|
101 |
+
"style": "논리적이고 체계적인"
|
102 |
+
},
|
103 |
+
"creative": {
|
104 |
+
"system_prompt": "당신은 창작 전문가입니다. 상상력이 풍부하고 감성적인 콘텐츠를 제작하세요.",
|
105 |
+
"tools": ["스토리텔링", "시각적 묘사", "감정 표현"],
|
106 |
+
"style": "창의적이고 감성적인"
|
107 |
+
},
|
108 |
+
"general": {
|
109 |
+
"system_prompt": "당신은 친근하고 도움이 되는 AI 어시스턴트입니다. 자연스럽고 이해하기 쉬운 답변을 제공하세요.",
|
110 |
+
"tools": ["일반 대화", "정보 제공", "문제 해결"],
|
111 |
+
"style": "친근하고 자연스러운"
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
context = state["context"]
|
116 |
+
context["enriched"] = enriched_context.get(agent_type, enriched_context["general"])
|
117 |
+
|
118 |
+
return {
|
119 |
+
"messages": [],
|
120 |
+
"current_response": "",
|
121 |
+
"agent_type": agent_type,
|
122 |
+
"context": context,
|
123 |
+
"step_info": f"🔧 **3단계 완료** - 컨텍스트 강화\n- 에이전트: {agent_type}\n- 스타일: {context['enriched']['style']}\n- 활용 도구: {', '.join(context['enriched']['tools'][:2])}"
|
124 |
+
}
|
125 |
+
|
126 |
+
def step4_response_generator_node(state: ChatState) -> ChatState:
|
127 |
+
"""4단계: 응답 생성 노드"""
|
128 |
+
enriched_context = state["context"]["enriched"]
|
129 |
+
system_prompt = enriched_context["system_prompt"]
|
130 |
+
|
131 |
+
# 시스템 프롬프트와 함께 메시지 구성
|
132 |
+
messages = [HumanMessage(content=system_prompt)] + state["messages"]
|
133 |
+
|
134 |
+
try:
|
135 |
+
response = llm.invoke(messages)
|
136 |
+
|
137 |
+
# 에이전트 타입에 따른 아이콘 설정
|
138 |
+
icons = {
|
139 |
+
"programmer": "💻",
|
140 |
+
"informer": "📚",
|
141 |
+
"calculator": "🔢",
|
142 |
+
"creative": "🎨",
|
143 |
+
"general": "💬"
|
144 |
+
}
|
145 |
+
|
146 |
+
icon = icons.get(state["agent_type"], "💬")
|
147 |
+
final_response = f"{icon} **[{state['agent_type'].upper()}]**\n\n{response.content}"
|
148 |
+
|
149 |
+
return {
|
150 |
+
"messages": [response],
|
151 |
+
"current_response": final_response,
|
152 |
+
"agent_type": state["agent_type"],
|
153 |
+
"context": state["context"],
|
154 |
+
"step_info": f"✅ **4단계 완료** - 응답 생성\n- 최종 응답 준비됨\n- 응답 길이: {len(response.content)}자"
|
155 |
+
}
|
156 |
+
|
157 |
+
except Exception as e:
|
158 |
+
error_msg = f"❌ 응답 생성 중 오류 발생: {str(e)}"
|
159 |
+
return {
|
160 |
+
"messages": [AIMessage(content=error_msg)],
|
161 |
+
"current_response": error_msg,
|
162 |
+
"agent_type": state["agent_type"],
|
163 |
+
"context": state["context"],
|
164 |
+
"step_info": f"❌ **4단계 실패** - 오류 발생\n- 오류: {str(e)}"
|
165 |
+
}
|
166 |
+
|
167 |
+
def should_continue_to_classifier(state: ChatState) -> str:
|
168 |
+
return "classifier"
|
169 |
+
|
170 |
+
def should_continue_to_enricher(state: ChatState) -> str:
|
171 |
+
return "enricher"
|
172 |
+
|
173 |
+
def should_continue_to_generator(state: ChatState) -> str:
|
174 |
+
return "generator"
|
175 |
+
|
176 |
+
def should_end(state: ChatState) -> str:
|
177 |
+
return END
|
178 |
+
|
179 |
+
# LangGraph 워크플로우 생성
|
180 |
+
def create_enhanced_workflow():
|
181 |
+
workflow = StateGraph(ChatState)
|
182 |
+
|
183 |
+
# 4개 노드 추가
|
184 |
+
workflow.add_node("analyzer", step1_analyzer_node)
|
185 |
+
workflow.add_node("classifier", step2_classifier_node)
|
186 |
+
workflow.add_node("enricher", step3_context_enricher_node)
|
187 |
+
workflow.add_node("generator", step4_response_generator_node)
|
188 |
+
|
189 |
+
# 시작점 설정
|
190 |
+
workflow.set_entry_point("analyzer")
|
191 |
+
|
192 |
+
# 순차적 엣지 추가
|
193 |
+
workflow.add_conditional_edges(
|
194 |
+
"analyzer",
|
195 |
+
should_continue_to_classifier,
|
196 |
+
{"classifier": "classifier"}
|
197 |
+
)
|
198 |
+
|
199 |
+
workflow.add_conditional_edges(
|
200 |
+
"classifier",
|
201 |
+
should_continue_to_enricher,
|
202 |
+
{"enricher": "enricher"}
|
203 |
+
)
|
204 |
+
|
205 |
+
workflow.add_conditional_edges(
|
206 |
+
"enricher",
|
207 |
+
should_continue_to_generator,
|
208 |
+
{"generator": "generator"}
|
209 |
+
)
|
210 |
+
|
211 |
+
workflow.add_conditional_edges(
|
212 |
+
"generator",
|
213 |
+
should_end,
|
214 |
+
{END: END}
|
215 |
+
)
|
216 |
+
|
217 |
+
return workflow.compile()
|
218 |
+
|
219 |
+
# 글로벌 워크플로우 인스턴스
|
220 |
+
enhanced_workflow = create_enhanced_workflow()
|
221 |
+
|
222 |
+
def stream_chatbot_response(message, history):
|
223 |
+
"""각 단계를 누적해서 실시간 표시"""
|
224 |
+
if not message.strip():
|
225 |
+
yield "", history
|
226 |
+
return
|
227 |
+
|
228 |
+
# 메시지 히스토리를 LangChain 메시지로 변환
|
229 |
+
messages = []
|
230 |
+
for human_msg, ai_msg in history:
|
231 |
+
if human_msg and not "📊" in human_msg and not "🎯" in human_msg:
|
232 |
+
messages.append(HumanMessage(content=human_msg))
|
233 |
+
if ai_msg and not "📊" in ai_msg and not "🎯" in ai_msg and not "⚡" in ai_msg:
|
234 |
+
messages.append(AIMessage(content=ai_msg))
|
235 |
+
|
236 |
+
# 현재 메시지 추가
|
237 |
+
messages.append(HumanMessage(content=message))
|
238 |
+
|
239 |
+
try:
|
240 |
+
# 초기 상태 설정
|
241 |
+
initial_state = {
|
242 |
+
"messages": messages,
|
243 |
+
"current_response": "",
|
244 |
+
"agent_type": "",
|
245 |
+
"context": {},
|
246 |
+
"step_info": ""
|
247 |
+
}
|
248 |
+
|
249 |
+
# 워크플로우를 스트림으로 실행
|
250 |
+
current_history = history.copy()
|
251 |
+
|
252 |
+
# 누적될 단계 정보들
|
253 |
+
accumulated_steps = []
|
254 |
+
accumulated_steps.append("🔄 **AI 처리 과정 시작**\n")
|
255 |
+
|
256 |
+
current_history.append((message, "\n".join(accumulated_steps)))
|
257 |
+
yield "", current_history
|
258 |
+
time.sleep(0.3)
|
259 |
+
|
260 |
+
# 각 노드를 순차적으로 실행하면서 중간 결과를 누적 표시
|
261 |
+
step_counter = 1
|
262 |
+
for step_result in enhanced_workflow.stream(initial_state):
|
263 |
+
node_name = list(step_result.keys())[0]
|
264 |
+
node_result = step_result[node_name]
|
265 |
+
|
266 |
+
if "step_info" in node_result and node_result["step_info"]:
|
267 |
+
# 현재 단계 정보를 누적 리스트에 추가
|
268 |
+
step_info = node_result["step_info"]
|
269 |
+
accumulated_steps.append(f"\n{'='*50}")
|
270 |
+
accumulated_steps.append(step_info)
|
271 |
+
|
272 |
+
# 진행률 표시
|
273 |
+
progress_bar = "▓" * step_counter + "░" * (4 - step_counter)
|
274 |
+
accumulated_steps.append(f"\n📈 **진행률**: [{progress_bar}] {step_counter}/4 단계")
|
275 |
+
|
276 |
+
# 누적된 모든 단계 정보를 표시
|
277 |
+
full_message = "\n".join(accumulated_steps)
|
278 |
+
current_history[-1] = (message, full_message)
|
279 |
+
yield "", current_history
|
280 |
+
time.sleep(0.8) # 시각적 효과를 위한 지연
|
281 |
+
step_counter += 1
|
282 |
+
|
283 |
+
# 최종 응답을 위해 다시 워크플로우 실행 (결과 획득용)
|
284 |
+
final_result = None
|
285 |
+
for step_result in enhanced_workflow.stream(initial_state):
|
286 |
+
final_result = step_result
|
287 |
+
|
288 |
+
if final_result:
|
289 |
+
final_node_result = list(final_result.values())[0]
|
290 |
+
final_response = final_node_result.get("current_response", "응답을 생성할 수 없습니다.")
|
291 |
+
|
292 |
+
# 최종 응답을 누적 정보에 추가
|
293 |
+
accumulated_steps.append(f"\n{'='*50}")
|
294 |
+
accumulated_steps.append("🎉 **최종 응답**")
|
295 |
+
accumulated_steps.append(f"\n{final_response}")
|
296 |
+
|
297 |
+
# 완료된 전체 과정 표시
|
298 |
+
full_message = "\n".join(accumulated_steps)
|
299 |
+
current_history[-1] = (message, full_message)
|
300 |
+
yield "", current_history
|
301 |
+
|
302 |
+
except Exception as e:
|
303 |
+
error_response = f"❌ **오류 발생**\n{str(e)}"
|
304 |
+
current_history = history.copy()
|
305 |
+
current_history.append((message, error_response))
|
306 |
+
yield "", current_history
|
307 |
+
|
308 |
+
def clear_chat():
|
309 |
+
"""채팅 히스토리 초기화"""
|
310 |
+
return []
|
311 |
+
|
312 |
+
# Gradio 인터페이스 생성
|
313 |
+
def create_enhanced_gradio_interface():
|
314 |
+
with gr.Blocks(title="Enhanced LangGraph 챗봇", theme=gr.themes.Soft()) as demo:
|
315 |
+
gr.Markdown(
|
316 |
+
"""
|
317 |
+
# 🚀 Enhanced LangGraph + Gradio 챗봇
|
318 |
+
|
319 |
+
**4단계 처리 과정을 실시간으로 확인할 수 있는 AI 챗봇**
|
320 |
+
|
321 |
+
**처리 단계:**
|
322 |
+
1. 📊 **메시지 분석** - 입력 메시지의 특성 분석
|
323 |
+
2. 🎯 **의도 분류** - 사용자 의도에 따른 에이전트 선택
|
324 |
+
3. 🔧 **컨텍스트 강화** - 전문 도메인별 컨텍스트 설정
|
325 |
+
4. ✅ **응답 생성** - 최적화된 답변 생성
|
326 |
+
|
327 |
+
**지원 에이전트:** 💻 프로그래머 | 📚 정보전문가 | 🔢 계산기 | 🎨 창작가 | 💬 일반대화
|
328 |
+
"""
|
329 |
+
)
|
330 |
+
|
331 |
+
# 챗봇 컴포넌트
|
332 |
+
chatbot = gr.Chatbot(
|
333 |
+
value=[],
|
334 |
+
height=600,
|
335 |
+
show_label=False,
|
336 |
+
container=True,
|
337 |
+
)
|
338 |
+
|
339 |
+
with gr.Row():
|
340 |
+
msg = gr.Textbox(
|
341 |
+
placeholder="메시지를 입력하세요... (각 처리 단계가 실시간으로 표시됩니다)",
|
342 |
+
show_label=False,
|
343 |
+
scale=4,
|
344 |
+
container=False
|
345 |
+
)
|
346 |
+
submit_btn = gr.Button("전송", scale=1, variant="primary")
|
347 |
+
clear_btn = gr.Button("초기화", scale=1, variant="secondary")
|
348 |
+
|
349 |
+
# 상태 표시
|
350 |
+
with gr.Row():
|
351 |
+
gr.Markdown("💡 **팁**: 다양한 주제로 대화해보세요. 각 단계별 처리 과정을 확인할 수 있습니다!")
|
352 |
+
|
353 |
+
# 이벤트 핸들러 - 스트리밍 방식으로 변경
|
354 |
+
submit_btn.click(
|
355 |
+
stream_chatbot_response,
|
356 |
+
inputs=[msg, chatbot],
|
357 |
+
outputs=[msg, chatbot]
|
358 |
+
)
|
359 |
+
|
360 |
+
msg.submit(
|
361 |
+
stream_chatbot_response,
|
362 |
+
inputs=[msg, chatbot],
|
363 |
+
outputs=[msg, chatbot]
|
364 |
+
)
|
365 |
+
|
366 |
+
# 초기화 버튼
|
367 |
+
clear_btn.click(
|
368 |
+
clear_chat,
|
369 |
+
outputs=[chatbot]
|
370 |
+
)
|
371 |
+
|
372 |
+
# 카테고리별 예제 질문들
|
373 |
+
with gr.Row():
|
374 |
+
with gr.Column():
|
375 |
+
gr.Markdown("### 💻 프로그래밍")
|
376 |
+
gr.Examples(
|
377 |
+
examples=[
|
378 |
+
"Python으로 피보나치 수열 함수 만드는 방법?",
|
379 |
+
"딕셔너리와 리스트의 차이점을 알려줘",
|
380 |
+
"클래스와 객체에 대해 설명해줘"
|
381 |
+
],
|
382 |
+
inputs=msg
|
383 |
+
)
|
384 |
+
|
385 |
+
with gr.Column():
|
386 |
+
gr.Markdown("### 🔢 계산/수학")
|
387 |
+
gr.Examples(
|
388 |
+
examples=[
|
389 |
+
"25 곱하기 37은 얼마야?",
|
390 |
+
"복리 계산 방법을 알려줘",
|
391 |
+
"삼각함수에 대해 설명해줘"
|
392 |
+
],
|
393 |
+
inputs=msg
|
394 |
+
)
|
395 |
+
|
396 |
+
with gr.Column():
|
397 |
+
gr.Markdown("### 🎨 창작")
|
398 |
+
gr.Examples(
|
399 |
+
examples=[
|
400 |
+
"봄에 대한 짧은 시를 써줘",
|
401 |
+
"우주 여행 이야기를 만들어줘",
|
402 |
+
"창의적인 아이디어를 제안해줘"
|
403 |
+
],
|
404 |
+
inputs=msg
|
405 |
+
)
|
406 |
+
|
407 |
+
return demo
|
408 |
+
|
409 |
+
if __name__ == "__main__":
|
410 |
+
# 사용 방법:
|
411 |
+
# 1. OpenAI API 키를 설정하세요
|
412 |
+
# os.environ["OPENAI_API_KEY"] = "your-api-key-here"
|
413 |
+
|
414 |
+
# 2. 필요한 패키지 설치:
|
415 |
+
# pip install gradio langgraph langchain-openai langchain-core
|
416 |
+
|
417 |
+
# 3. 인터페이스 실행
|
418 |
+
demo = create_enhanced_gradio_interface()
|
419 |
+
demo.launch(
|
420 |
+
)
|
pyproject.toml
CHANGED
@@ -6,8 +6,11 @@ readme = "README.md"
|
|
6 |
requires-python = ">=3.13"
|
7 |
dependencies = [
|
8 |
"gradio[mcp]==5.37",
|
|
|
9 |
"langchain-mcp-adapters>=0.1.9",
|
|
|
10 |
"langchain[openai]>=0.3.27",
|
11 |
"langgraph>=0.5.4",
|
|
|
12 |
"textblob>=0.19.0",
|
13 |
]
|
|
|
6 |
requires-python = ">=3.13"
|
7 |
dependencies = [
|
8 |
"gradio[mcp]==5.37",
|
9 |
+
"ipython>=9.4.0",
|
10 |
"langchain-mcp-adapters>=0.1.9",
|
11 |
+
"langchain-teddynote>=0.3.45",
|
12 |
"langchain[openai]>=0.3.27",
|
13 |
"langgraph>=0.5.4",
|
14 |
+
"notebook>=7.4.4",
|
15 |
"textblob>=0.19.0",
|
16 |
]
|
uv.lock
CHANGED
The diff for this file is too large to render.
See raw diff
|
|