aiqtech commited on
Commit
bbe1d5b
Β·
verified Β·
1 Parent(s): 3eeac02

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +644 -184
app.py CHANGED
@@ -1,8 +1,17 @@
 
 
 
 
 
 
1
  import os
2
  import json
3
  import asyncio
4
- from typing import Optional, List, Dict
 
5
  from contextlib import asynccontextmanager
 
 
6
 
7
  import requests
8
  import uvicorn
@@ -10,9 +19,24 @@ from fastapi import FastAPI, HTTPException
10
  from fastapi.middleware.cors import CORSMiddleware
11
  from pydantic import BaseModel, Field
12
  import gradio as gr
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
 
15
- # Pydantic λͺ¨λΈ μ •μ˜
16
  class Message(BaseModel):
17
  role: str
18
  content: str
@@ -25,22 +49,87 @@ class ChatRequest(BaseModel):
25
  temperature: float = Field(default=0.6, ge=0, le=2)
26
  top_p: float = Field(default=1.0, ge=0, le=1)
27
  top_k: int = Field(default=40, ge=1, le=100)
28
- presence_penalty: float = Field(default=0, ge=-2, le=2)
29
- frequency_penalty: float = Field(default=0, ge=-2, le=2)
 
 
 
 
 
30
 
31
 
32
- class ChatResponse(BaseModel):
33
- response: str
34
- model: str
35
- tokens_used: Optional[int] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
 
 
 
37
 
38
- # Fireworks API ν΄λΌμ΄μ–ΈνŠΈ
39
  class FireworksClient:
40
  def __init__(self, api_key: Optional[str] = None):
41
  self.api_key = api_key or os.getenv("FIREWORKS_API_KEY")
42
  if not self.api_key:
43
- raise ValueError("API key is required. Set FIREWORKS_API_KEY environment variable.")
44
 
45
  self.base_url = "https://api.fireworks.ai/inference/v1/chat/completions"
46
  self.headers = {
@@ -49,17 +138,15 @@ class FireworksClient:
49
  "Authorization": f"Bearer {self.api_key}"
50
  }
51
 
52
- def chat(self, request: ChatRequest) -> Dict:
53
- """Fireworks API에 μ±„νŒ… μš”μ²­μ„ λ³΄λƒ…λ‹ˆλ‹€."""
54
  payload = {
55
- "model": request.model,
56
- "max_tokens": request.max_tokens,
57
- "top_p": request.top_p,
58
- "top_k": request.top_k,
59
- "presence_penalty": request.presence_penalty,
60
- "frequency_penalty": request.frequency_penalty,
61
- "temperature": request.temperature,
62
- "messages": [msg.dict() for msg in request.messages]
63
  }
64
 
65
  try:
@@ -67,159 +154,503 @@ class FireworksClient:
67
  self.base_url,
68
  headers=self.headers,
69
  data=json.dumps(payload),
70
- timeout=30
71
  )
72
  response.raise_for_status()
73
- return response.json()
74
- except requests.exceptions.RequestException as e:
75
- raise HTTPException(status_code=500, detail=f"API request failed: {str(e)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
 
 
 
 
 
 
77
 
78
- # Gradio μ•± 생성
79
- def create_gradio_app(client: FireworksClient):
80
- """Gradio μΈν„°νŽ˜μ΄μŠ€λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
- def chat_with_llm(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
  message: str,
84
  history: List[List[str]],
85
- model: str,
 
 
86
  temperature: float,
87
- max_tokens: int,
88
- top_p: float,
89
- top_k: int
90
  ):
91
- """Gradio μ±„νŒ… ν•¨μˆ˜"""
92
- if not message:
93
- return "", history
94
-
95
- # λŒ€ν™” 기둝을 Message ν˜•μ‹μœΌλ‘œ λ³€ν™˜
96
- messages = []
97
- for user_msg, assistant_msg in history:
98
- if user_msg:
99
- messages.append(Message(role="user", content=user_msg))
100
- if assistant_msg:
101
- messages.append(Message(role="assistant", content=assistant_msg))
102
 
103
- # ν˜„μž¬ λ©”μ‹œμ§€ μΆ”κ°€
104
- messages.append(Message(role="user", content=message))
105
 
106
- # API μš”μ²­
107
  try:
108
- request = ChatRequest(
109
- messages=messages,
110
- model=model,
111
- temperature=temperature,
112
- max_tokens=max_tokens,
113
- top_p=top_p,
114
- top_k=top_k
 
 
 
 
 
 
 
 
 
115
  )
116
 
117
- response = client.chat(request)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
 
119
- # μ‘λ‹΅μ—μ„œ ν…μŠ€νŠΈ μΆ”μΆœ
120
- if "choices" in response and len(response["choices"]) > 0:
121
- assistant_response = response["choices"][0]["message"]["content"]
122
- else:
123
- assistant_response = "응닡을 받을 수 μ—†μŠ΅λ‹ˆλ‹€."
124
 
125
  # νžˆμŠ€ν† λ¦¬ μ—…λ°μ΄νŠΈ
126
- history.append([message, assistant_response])
127
- return "", history
 
128
 
129
  except Exception as e:
130
- error_msg = f"였λ₯˜ λ°œμƒ: {str(e)}"
131
  history.append([message, error_msg])
132
- return "", history
133
 
134
- # Gradio μΈν„°νŽ˜μ΄μŠ€ ꡬ성
135
- with gr.Blocks(title="LLM Chat Interface") as demo:
136
- gr.Markdown("# πŸš€ Fireworks LLM Chat Interface")
137
- gr.Markdown("Qwen3-235B λͺ¨λΈμ„ μ‚¬μš©ν•œ μ±„νŒ… μΈν„°νŽ˜μ΄μŠ€μž…λ‹ˆλ‹€.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
 
139
  with gr.Row():
 
140
  with gr.Column(scale=3):
141
  chatbot = gr.Chatbot(
142
  height=500,
143
- label="μ±„νŒ… μ°½"
 
144
  )
 
145
  msg = gr.Textbox(
146
- label="λ©”μ‹œμ§€ μž…λ ₯",
147
- placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš”...",
148
- lines=2
149
  )
 
150
  with gr.Row():
151
- submit = gr.Button("전솑", variant="primary")
152
- clear = gr.Button("λŒ€ν™” μ΄ˆκΈ°ν™”")
 
 
 
 
 
 
 
 
153
 
 
154
  with gr.Column(scale=1):
155
  gr.Markdown("### βš™οΈ μ„€μ •")
156
- model = gr.Textbox(
157
- label="λͺ¨λΈ",
158
- value="accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
159
- interactive=True
160
- )
161
- temperature = gr.Slider(
162
- minimum=0,
163
- maximum=2,
164
- value=0.6,
165
- step=0.1,
166
- label="Temperature"
167
- )
168
- max_tokens = gr.Slider(
169
- minimum=100,
170
- maximum=8192,
171
- value=4096,
172
- step=100,
173
- label="Max Tokens"
174
- )
175
- top_p = gr.Slider(
176
- minimum=0,
177
- maximum=1,
178
- value=1.0,
179
- step=0.1,
180
- label="Top P"
181
- )
182
- top_k = gr.Slider(
183
- minimum=1,
184
- maximum=100,
185
- value=40,
186
- step=1,
187
- label="Top K"
188
- )
 
 
 
 
 
 
 
 
 
 
 
 
189
 
190
- # 이벀트 ν•Έλ“€λŸ¬
 
 
 
 
 
 
 
 
 
 
 
 
191
  submit.click(
192
- chat_with_llm,
193
- inputs=[msg, chatbot, model, temperature, max_tokens, top_p, top_k],
194
- outputs=[msg, chatbot]
 
195
  )
196
 
197
  msg.submit(
198
- chat_with_llm,
199
- inputs=[msg, chatbot, model, temperature, max_tokens, top_p, top_k],
200
- outputs=[msg, chatbot]
 
201
  )
202
 
203
- clear.click(lambda: None, None, chatbot, queue=False)
 
 
 
 
204
 
205
  return demo
206
 
207
 
208
- # FastAPI μ•± μ„€μ •
 
 
 
209
  @asynccontextmanager
210
  async def lifespan(app: FastAPI):
211
- """μ•± μ‹œμž‘/μ’…λ£Œ μ‹œ μ‹€ν–‰λ˜λŠ” ν•¨μˆ˜"""
212
- # μ‹œμž‘ μ‹œ
213
- print("πŸš€ Starting FastAPI + Gradio server...")
 
214
  yield
215
- # μ’…λ£Œ μ‹œ
216
- print("πŸ‘‹ Shutting down server...")
217
 
218
 
219
  app = FastAPI(
220
- title="LLM API with Gradio Interface",
221
- description="Fireworks LLM API with Gradio testing interface",
222
- version="1.0.0",
223
  lifespan=lifespan
224
  )
225
 
@@ -229,16 +660,19 @@ app.add_middleware(
229
  allow_origins=["*"],
230
  allow_credentials=True,
231
  allow_methods=["*"],
232
- allow_headers=["*"],
233
  )
234
 
235
- # Fireworks ν΄λΌμ΄μ–ΈνŠΈ μ΄ˆκΈ°ν™”
236
  try:
237
- fireworks_client = FireworksClient()
238
- except ValueError as e:
239
- print(f"⚠️ Warning: {e}")
240
- print("API endpoints will not work without a valid API key.")
241
- fireworks_client = None
 
 
 
242
 
243
 
244
  # API μ—”λ“œν¬μΈνŠΈ
@@ -246,87 +680,113 @@ except ValueError as e:
246
  async def root():
247
  """루트 μ—”λ“œν¬μΈνŠΈ"""
248
  return {
249
- "message": "LLM API Server is running",
250
- "endpoints": {
251
- "api": "/chat",
252
- "gradio": "/gradio",
253
- "docs": "/docs"
254
- }
255
  }
256
 
257
 
258
- @app.post("/chat", response_model=ChatResponse)
259
- async def chat(request: ChatRequest):
260
- """μ±„νŒ… API μ—”λ“œν¬μΈνŠΈ"""
261
- if not fireworks_client:
262
- raise HTTPException(status_code=500, detail="API key not configured")
263
 
264
  try:
265
- response = fireworks_client.chat(request)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
 
267
- # 응닡 νŒŒμ‹±
268
- if "choices" in response and len(response["choices"]) > 0:
269
- content = response["choices"][0]["message"]["content"]
270
- tokens = response.get("usage", {}).get("total_tokens")
271
-
272
- return ChatResponse(
273
- response=content,
274
- model=request.model,
275
- tokens_used=tokens
276
- )
277
- else:
278
- raise HTTPException(status_code=500, detail="Invalid response from API")
279
-
280
- except HTTPException:
281
- raise
282
  except Exception as e:
283
  raise HTTPException(status_code=500, detail=str(e))
284
 
285
 
286
  @app.get("/health")
287
  async def health_check():
288
- """ν—¬μŠ€ 체크 μ—”λ“œν¬μΈνŠΈ"""
289
  return {
290
  "status": "healthy",
291
- "api_configured": fireworks_client is not None
 
 
 
 
 
292
  }
293
 
294
 
295
- # Gradio μ•± 마운트
296
- if fireworks_client:
297
- gradio_app = create_gradio_app(fireworks_client)
298
- app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
299
 
300
 
 
301
  # 메인 μ‹€ν–‰
 
 
302
  if __name__ == "__main__":
303
- import sys
 
 
 
 
 
 
 
304
 
305
  # API ν‚€ 확인
306
  if not os.getenv("FIREWORKS_API_KEY"):
307
- print("⚠️ κ²½κ³ : FIREWORKS_API_KEY ν™˜κ²½λ³€μˆ˜κ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
308
- print("μ„€μ • 방법:")
309
- print(" Linux/Mac: export FIREWORKS_API_KEY='your-api-key'")
310
- print(" Windows: set FIREWORKS_API_KEY=your-api-key")
311
- print("")
312
-
313
- # μ„ νƒμ μœΌλ‘œ API ν‚€ μž…λ ₯λ°›κΈ°
314
- api_key = input("API ν‚€λ₯Ό μž…λ ₯ν•˜μ„Έμš” (Enterλ₯Ό λˆ„λ₯΄λ©΄ κ±΄λ„ˆλœλ‹ˆλ‹€): ").strip()
315
- if api_key:
316
- os.environ["FIREWORKS_API_KEY"] = api_key
317
- fireworks_client = FireworksClient(api_key)
318
- gradio_app = create_gradio_app(fireworks_client)
319
- app = gr.mount_gradio_app(app, gradio_app, path="/gradio")
 
 
 
 
 
 
320
 
321
- # μ„œλ²„ μ‹œμž‘
322
- print("\nπŸš€ μ„œλ²„λ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€...")
323
- print("πŸ“ API λ¬Έμ„œ: http://localhost:7860/docs")
324
- print("πŸ’¬ Gradio UI: http://localhost:7860/gradio")
325
- print("πŸ”§ API μ—”λ“œν¬μΈνŠΈ: http://localhost:7860/chat")
 
 
 
 
326
 
327
  uvicorn.run(
328
  app,
329
  host="0.0.0.0",
330
- port=7860,
331
- reload=False
 
332
  )
 
1
+ """
2
+ Multi-Agent RAG-Enhanced LLM System
3
+ κ°λ…μž(Supervisor) -> μ°½μ˜μ„± μƒμ„±μž(Creative) -> λΉ„ν‰μž(Critic) -> κ°λ…μž(Final)
4
+ 4단계 νŒŒμ΄ν”„λΌμΈμ„ ν†΅ν•œ κ³ ν’ˆμ§ˆ λ‹΅λ³€ 생성 μ‹œμŠ€ν…œ
5
+ """
6
+
7
  import os
8
  import json
9
  import asyncio
10
+ import time
11
+ from typing import Optional, List, Dict, Any, Tuple
12
  from contextlib import asynccontextmanager
13
+ from datetime import datetime
14
+ from enum import Enum
15
 
16
  import requests
17
  import uvicorn
 
19
  from fastapi.middleware.cors import CORSMiddleware
20
  from pydantic import BaseModel, Field
21
  import gradio as gr
22
+ from dotenv import load_dotenv
23
+
24
+ # ν™˜κ²½λ³€μˆ˜ λ‘œλ“œ
25
+ load_dotenv()
26
+
27
+
28
+ # ============================================================================
29
+ # 데이터 λͺ¨λΈ μ •μ˜
30
+ # ============================================================================
31
+
32
+ class AgentRole(Enum):
33
+ """μ—μ΄μ „νŠΈ μ—­ν•  μ •μ˜"""
34
+ SUPERVISOR = "supervisor"
35
+ CREATIVE = "creative"
36
+ CRITIC = "critic"
37
+ FINALIZER = "finalizer"
38
 
39
 
 
40
  class Message(BaseModel):
41
  role: str
42
  content: str
 
49
  temperature: float = Field(default=0.6, ge=0, le=2)
50
  top_p: float = Field(default=1.0, ge=0, le=1)
51
  top_k: int = Field(default=40, ge=1, le=100)
52
+ use_search: bool = Field(default=True)
53
+
54
+
55
+ class AgentResponse(BaseModel):
56
+ role: AgentRole
57
+ content: str
58
+ metadata: Optional[Dict] = None
59
 
60
 
61
+ class FinalResponse(BaseModel):
62
+ final_answer: str
63
+ agent_responses: List[AgentResponse]
64
+ search_results: Optional[List[Dict]] = None
65
+ processing_time: float
66
+
67
+
68
+ # ============================================================================
69
+ # Brave Search ν΄λΌμ΄μ–ΈνŠΈ
70
+ # ============================================================================
71
+
72
+ class BraveSearchClient:
73
+ def __init__(self, api_key: Optional[str] = None):
74
+ self.api_key = api_key or os.getenv("BRAVE_SEARCH_API_KEY")
75
+ if not self.api_key:
76
+ print("⚠️ Warning: Brave Search API key not found. Search disabled.")
77
+
78
+ self.base_url = "https://api.search.brave.com/res/v1/web/search"
79
+ self.headers = {
80
+ "Accept": "application/json",
81
+ "X-Subscription-Token": self.api_key
82
+ } if self.api_key else {}
83
+
84
+ def search(self, query: str, count: int = 5) -> List[Dict]:
85
+ """μ›Ή 검색 μˆ˜ν–‰"""
86
+ if not self.api_key:
87
+ return []
88
+
89
+ params = {
90
+ "q": query,
91
+ "count": count,
92
+ "text_decorations": False,
93
+ "search_lang": "ko",
94
+ "country": "KR"
95
+ }
96
+
97
+ try:
98
+ response = requests.get(
99
+ self.base_url,
100
+ headers=self.headers,
101
+ params=params,
102
+ timeout=10
103
+ )
104
+ response.raise_for_status()
105
+ data = response.json()
106
+
107
+ results = []
108
+ if "web" in data and "results" in data["web"]:
109
+ for item in data["web"]["results"][:count]:
110
+ results.append({
111
+ "title": item.get("title", ""),
112
+ "url": item.get("url", ""),
113
+ "description": item.get("description", ""),
114
+ "age": item.get("age", "")
115
+ })
116
+
117
+ return results
118
+
119
+ except Exception as e:
120
+ print(f"Search error: {str(e)}")
121
+ return []
122
+
123
 
124
+ # ============================================================================
125
+ # Fireworks LLM ν΄λΌμ΄μ–ΈνŠΈ
126
+ # ============================================================================
127
 
 
128
  class FireworksClient:
129
  def __init__(self, api_key: Optional[str] = None):
130
  self.api_key = api_key or os.getenv("FIREWORKS_API_KEY")
131
  if not self.api_key:
132
+ raise ValueError("FIREWORKS_API_KEY is required!")
133
 
134
  self.base_url = "https://api.fireworks.ai/inference/v1/chat/completions"
135
  self.headers = {
 
138
  "Authorization": f"Bearer {self.api_key}"
139
  }
140
 
141
+ def chat(self, messages: List[Dict], **kwargs) -> str:
142
+ """LLMκ³Ό λŒ€ν™”"""
143
  payload = {
144
+ "model": kwargs.get("model", "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507"),
145
+ "messages": messages,
146
+ "max_tokens": kwargs.get("max_tokens", 4096),
147
+ "temperature": kwargs.get("temperature", 0.7),
148
+ "top_p": kwargs.get("top_p", 1.0),
149
+ "top_k": kwargs.get("top_k", 40)
 
 
150
  }
151
 
152
  try:
 
154
  self.base_url,
155
  headers=self.headers,
156
  data=json.dumps(payload),
157
+ timeout=60
158
  )
159
  response.raise_for_status()
160
+ data = response.json()
161
+
162
+ if "choices" in data and len(data["choices"]) > 0:
163
+ return data["choices"][0]["message"]["content"]
164
+ return "응닡을 생성할 수 μ—†μŠ΅λ‹ˆλ‹€."
165
+
166
+ except Exception as e:
167
+ return f"였λ₯˜ λ°œμƒ: {str(e)}"
168
+
169
+
170
+ # ============================================================================
171
+ # λ©€ν‹° μ—μ΄μ „νŠΈ μ‹œμŠ€ν…œ
172
+ # ============================================================================
173
+
174
+ class MultiAgentSystem:
175
+ """4단계 λ©€ν‹° μ—μ΄μ „νŠΈ 처리 μ‹œμŠ€ν…œ"""
176
+
177
+ def __init__(self, llm_client: FireworksClient, search_client: BraveSearchClient):
178
+ self.llm = llm_client
179
+ self.search = search_client
180
+ self.agent_configs = self._initialize_agent_configs()
181
+
182
+ def _initialize_agent_configs(self) -> Dict:
183
+ """각 μ—μ΄μ „νŠΈλ³„ μ„€μ • μ΄ˆκΈ°ν™”"""
184
+ return {
185
+ AgentRole.SUPERVISOR: {
186
+ "temperature": 0.3,
187
+ "system_prompt": """당신은 κ°λ…μž μ—μ΄μ „νŠΈμž…λ‹ˆλ‹€.
188
+ μ‚¬μš©μžμ˜ 질문과 검색 κ²°κ³Όλ₯Ό λΆ„μ„ν•˜μ—¬ λ‹΅λ³€μ˜ 전체적인 λ°©ν–₯μ„±κ³Ό ꡬ쑰λ₯Ό μ œμ‹œν•΄μ•Ό ν•©λ‹ˆλ‹€.
189
+
190
+ μ—­ν• :
191
+ 1. 질문의 핡심 μ˜λ„ νŒŒμ•…
192
+ 2. 검색 κ²°κ³Όμ—μ„œ 핡심 정보 μΆ”μΆœ
193
+ 3. 닡변이 포함해야 ν•  μ£Όμš” μš”μ†Œλ“€ μ •μ˜
194
+ 4. 논리적 흐름과 ꡬ쑰 μ œμ‹œ
195
+
196
+ 좜λ ₯ ν˜•μ‹:
197
+ - 질문 뢄석: [핡심 μ˜λ„]
198
+ - μ£Όμš” 포함 사항: [ν•­λͺ©λ“€]
199
+ - λ‹΅λ³€ ꡬ쑰: [논리적 흐름]
200
+ - 검색 κ²°κ³Ό ν™œμš© λ°©μ•ˆ: [μ–΄λ–€ 정보λ₯Ό μ–΄λ–»κ²Œ ν™œμš©ν• μ§€]"""
201
+ },
202
+
203
+ AgentRole.CREATIVE: {
204
+ "temperature": 0.9,
205
+ "system_prompt": """당신은 μ°½μ˜μ„± μƒμ„±μž μ—μ΄μ „νŠΈμž…λ‹ˆλ‹€.
206
+ κ°λ…μžμ˜ 지침을 λ°”νƒ•μœΌλ‘œ 창의적이고 ν₯미둜운 닡변을 생성해야 ν•©λ‹ˆλ‹€.
207
+
208
+ μ—­ν• :
209
+ 1. κ°λ…μžμ˜ ꡬ쑰λ₯Ό λ”°λ₯΄λ˜ 창의적으둜 ν™•μž₯
210
+ 2. μ˜ˆμ‹œ, λΉ„μœ , μŠ€ν† λ¦¬ν…”λ§ ν™œμš©
211
+ 3. μ‚¬μš©μž κ΄€μ μ—μ„œ μ΄ν•΄ν•˜κΈ° μ‰¬μš΄ μ„€λͺ… μΆ”κ°€
212
+ 4. μ‹€μš©μ μ΄κ³  ꡬ체적인 μ‘°μ–Έ 포함
213
+ 5. 독창적인 관점과 톡찰 제곡
214
+
215
+ μ£Όμ˜μ‚¬ν•­:
216
+ - 정확성을 ν•΄μΉ˜μ§€ μ•ŠλŠ” μ„ μ—μ„œ μ°½μ˜μ„± 발휘
217
+ - 검색 κ²°κ³Όλ₯Ό 창의적으둜 μž¬κ΅¬μ„±
218
+ - μ‚¬μš©μž μ°Έμ—¬λ₯Ό μœ λ„ν•˜λŠ” λ‚΄μš© 포함"""
219
+ },
220
+
221
+ AgentRole.CRITIC: {
222
+ "temperature": 0.2,
223
+ "system_prompt": """당신은 λΉ„ν‰μž μ—μ΄μ „νŠΈμž…λ‹ˆλ‹€.
224
+ μ°½μ˜μ„± μƒμ„±μžμ˜ 닡변을 κ²€ν† ν•˜κ³  κ°œμ„ μ μ„ μ œμ‹œν•΄μ•Ό ν•©λ‹ˆλ‹€.
225
+
226
+ μ—­ν• :
227
+ 1. 사싀 관계 검증
228
+ 2. 논리적 일관성 확인
229
+ 3. μ˜€ν•΄μ˜ μ†Œμ§€κ°€ μžˆλŠ” ν‘œν˜„ 지적
230
+ 4. λˆ„λ½λœ μ€‘μš” 정보 확인
231
+ 5. κ°œμ„  λ°©ν–₯ ꡬ체적 μ œμ‹œ
232
 
233
+ 평가 κΈ°μ€€:
234
+ - μ •ν™•μ„±: 사싀과 λ°μ΄ν„°μ˜ μ •ν™•μ„±
235
+ - μ™„μ „μ„±: μ§ˆλ¬Έμ— λŒ€ν•œ μΆ©λΆ„ν•œ λ‹΅λ³€ μ—¬λΆ€
236
+ - λͺ…ν™•μ„±: μ΄ν•΄ν•˜κΈ° μ‰¬μš΄ μ„€λͺ…인지
237
+ - μœ μš©μ„±: μ‹€μ œλ‘œ 도움이 λ˜λŠ” 정보인지
238
+ - μ‹ λ’°μ„±: 검증 κ°€λŠ₯ν•œ 좜처 포함 μ—¬λΆ€
239
 
240
+ 좜λ ₯ ν˜•μ‹:
241
+ βœ… 긍정적 μΈ‘λ©΄: [잘된 점듀]
242
+ ⚠️ κ°œμ„  ν•„μš”: [문제점과 κ°œμ„  λ°©μ•ˆ]
243
+ πŸ’‘ μΆ”κ°€ μ œμ•ˆ: [보완할 λ‚΄μš©]"""
244
+ },
245
+
246
+ AgentRole.FINALIZER: {
247
+ "temperature": 0.5,
248
+ "system_prompt": """당신은 μ΅œμ’… κ°λ…μžμž…λ‹ˆλ‹€.
249
+ λͺ¨λ“  μ—μ΄μ „νŠΈμ˜ μ˜κ²¬μ„ μ’…ν•©ν•˜μ—¬ μ΅œμ’… 닡변을 생성해야 ν•©λ‹ˆλ‹€.
250
+
251
+ μ—­ν• :
252
+ 1. μ°½μ˜μ„± μƒμ„±μžμ˜ 닡변을 기반으둜
253
+ 2. λΉ„ν‰μžμ˜ ν”Όλ“œλ°±μ„ λ°˜μ˜ν•˜μ—¬
254
+ 3. κ°λ…μžμ˜ 초기 ꡬ쑰λ₯Ό μœ μ§€ν•˜λ©°
255
+ 4. 논리적이고 μ΄ν•΄ν•˜κΈ° μ‰¬μš΄ μ΅œμ’… λ‹΅λ³€ 생성
256
+
257
+ μ΅œμ’… λ‹΅λ³€ κΈ°μ€€:
258
+ - μ •ν™•μ„±κ³Ό μ°½μ˜μ„±μ˜ κ· ν˜•
259
+ - λͺ…ν™•ν•œ ꡬ쑰와 논리적 흐름
260
+ - μ‹€μš©μ μ΄κ³  μœ μš©ν•œ 정보
261
+ - μ‚¬μš©μž μΉœν™”μ μΈ 톀
262
+ - 검색 κ²°κ³Ό 좜처 λͺ…μ‹œ
263
+
264
+ οΏ½οΏ½λ“œμ‹œ 포함할 μš”μ†Œ:
265
+ 1. 핡심 λ‹΅λ³€ (직접적인 응닡)
266
+ 2. 상세 μ„€λͺ… (λ°°κ²½κ³Ό λ§₯락)
267
+ 3. μ‹€μš©μ  μ‘°μ–Έ (ν•΄λ‹Ή μ‹œ)
268
+ 4. μ°Έκ³  자료 (검색 κ²°κ³Ό 기반)"""
269
+ }
270
+ }
271
+
272
+ def _format_search_results(self, results: List[Dict]) -> str:
273
+ """검색 κ²°κ³Ό ν¬λ§·νŒ…"""
274
+ if not results:
275
+ return "검색 κ²°κ³Ό μ—†μŒ"
276
+
277
+ formatted = []
278
+ for i, result in enumerate(results, 1):
279
+ formatted.append(f"""
280
+ [검색결과 {i}]
281
+ 제λͺ©: {result.get('title', 'N/A')}
282
+ URL: {result.get('url', 'N/A')}
283
+ λ‚΄μš©: {result.get('description', 'N/A')}
284
+ κ²Œμ‹œ: {result.get('age', 'N/A')}""")
285
+
286
+ return "\n".join(formatted)
287
 
288
+ async def process_with_agents(
289
+ self,
290
+ query: str,
291
+ search_results: List[Dict],
292
+ config: Dict
293
+ ) -> FinalResponse:
294
+ """λ©€ν‹° μ—μ΄μ „νŠΈ νŒŒμ΄ν”„λΌμΈ μ‹€ν–‰"""
295
+
296
+ start_time = time.time()
297
+ agent_responses = []
298
+ search_context = self._format_search_results(search_results)
299
+
300
+ # 1단계: κ°λ…μž - λ°©ν–₯μ„± μ œμ‹œ
301
+ supervisor_prompt = f"""
302
+ μ‚¬μš©μž 질문: {query}
303
+
304
+ 검색 κ²°κ³Ό:
305
+ {search_context}
306
+
307
+ μœ„ 정보λ₯Ό λ°”νƒ•μœΌλ‘œ λ‹΅λ³€μ˜ λ°©ν–₯μ„±κ³Ό ꡬ쑰λ₯Ό μ œμ‹œν•˜μ„Έμš”."""
308
+
309
+ supervisor_response = self.llm.chat(
310
+ messages=[
311
+ {"role": "system", "content": self.agent_configs[AgentRole.SUPERVISOR]["system_prompt"]},
312
+ {"role": "user", "content": supervisor_prompt}
313
+ ],
314
+ temperature=self.agent_configs[AgentRole.SUPERVISOR]["temperature"],
315
+ max_tokens=config.get("max_tokens", 1000)
316
+ )
317
+
318
+ agent_responses.append(AgentResponse(
319
+ role=AgentRole.SUPERVISOR,
320
+ content=supervisor_response
321
+ ))
322
+
323
+ # 2단계: μ°½μ˜μ„± μƒμ„±μž - 창의적 λ‹΅λ³€ 생성
324
+ creative_prompt = f"""
325
+ μ‚¬μš©μž 질문: {query}
326
+
327
+ κ°λ…μž μ§€μΉ¨:
328
+ {supervisor_response}
329
+
330
+ 검색 κ²°κ³Ό:
331
+ {search_context}
332
+
333
+ μœ„ μ§€μΉ¨κ³Ό 정보λ₯Ό λ°”νƒ•μœΌλ‘œ 창의적이고 μœ μš©ν•œ 닡변을 μƒμ„±ν•˜μ„Έμš”."""
334
+
335
+ creative_response = self.llm.chat(
336
+ messages=[
337
+ {"role": "system", "content": self.agent_configs[AgentRole.CREATIVE]["system_prompt"]},
338
+ {"role": "user", "content": creative_prompt}
339
+ ],
340
+ temperature=self.agent_configs[AgentRole.CREATIVE]["temperature"],
341
+ max_tokens=config.get("max_tokens", 2000)
342
+ )
343
+
344
+ agent_responses.append(AgentResponse(
345
+ role=AgentRole.CREATIVE,
346
+ content=creative_response
347
+ ))
348
+
349
+ # 3단계: λΉ„ν‰μž - κ²€ν†  및 κ°œμ„ μ  μ œμ‹œ
350
+ critic_prompt = f"""
351
+ 원본 질문: {query}
352
+
353
+ μ°½μ˜μ„± μƒμ„±μžμ˜ λ‹΅λ³€:
354
+ {creative_response}
355
+
356
+ 검색 κ²°κ³Ό:
357
+ {search_context}
358
+
359
+ μœ„ 닡변을 κ²€ν† ν•˜κ³  κ°œμ„ μ μ„ μ œμ‹œν•˜μ„Έμš”."""
360
+
361
+ critic_response = self.llm.chat(
362
+ messages=[
363
+ {"role": "system", "content": self.agent_configs[AgentRole.CRITIC]["system_prompt"]},
364
+ {"role": "user", "content": critic_prompt}
365
+ ],
366
+ temperature=self.agent_configs[AgentRole.CRITIC]["temperature"],
367
+ max_tokens=config.get("max_tokens", 1000)
368
+ )
369
+
370
+ agent_responses.append(AgentResponse(
371
+ role=AgentRole.CRITIC,
372
+ content=critic_response
373
+ ))
374
+
375
+ # 4단계: μ΅œμ’… κ°λ…μž - μ’…ν•© 및 μ΅œμ’… λ‹΅λ³€
376
+ final_prompt = f"""
377
+ μ‚¬μš©μž 질문: {query}
378
+
379
+ μ°½μ˜μ„± μƒμ„±μžμ˜ λ‹΅λ³€:
380
+ {creative_response}
381
+
382
+ λΉ„ν‰μžμ˜ ν”Όλ“œλ°±:
383
+ {critic_response}
384
+
385
+ 초기 κ°λ…μž μ§€μΉ¨:
386
+ {supervisor_response}
387
+
388
+ 검색 κ²°κ³Ό:
389
+ {search_context}
390
+
391
+ λͺ¨λ“  μ˜κ²¬μ„ μ’…ν•©ν•˜μ—¬ μ΅œμ’… 닡변을 μƒμ„±ν•˜μ„Έμš”.
392
+ λΉ„ν‰μžμ˜ ν”Όλ“œλ°±μ„ λ°˜μ˜ν•˜μ—¬ κ°œμ„ λœ 버전을 λ§Œλ“€μ–΄μ£Όμ„Έμš”."""
393
+
394
+ final_response = self.llm.chat(
395
+ messages=[
396
+ {"role": "system", "content": self.agent_configs[AgentRole.FINALIZER]["system_prompt"]},
397
+ {"role": "user", "content": final_prompt}
398
+ ],
399
+ temperature=self.agent_configs[AgentRole.FINALIZER]["temperature"],
400
+ max_tokens=config.get("max_tokens", 3000)
401
+ )
402
+
403
+ agent_responses.append(AgentResponse(
404
+ role=AgentRole.FINALIZER,
405
+ content=final_response
406
+ ))
407
+
408
+ processing_time = time.time() - start_time
409
+
410
+ return FinalResponse(
411
+ final_answer=final_response,
412
+ agent_responses=agent_responses,
413
+ search_results=search_results,
414
+ processing_time=processing_time
415
+ )
416
+
417
+
418
+ # ============================================================================
419
+ # Gradio UI
420
+ # ============================================================================
421
+
422
+ def create_gradio_interface(multi_agent_system: MultiAgentSystem, search_client: BraveSearchClient):
423
+ """Gradio μΈν„°νŽ˜μ΄μŠ€ 생성"""
424
+
425
+ async def process_query(
426
  message: str,
427
  history: List[List[str]],
428
+ use_search: bool,
429
+ show_agent_thoughts: bool,
430
+ search_count: int,
431
  temperature: float,
432
+ max_tokens: int
 
 
433
  ):
434
+ """쿼리 처리 ν•¨μˆ˜"""
 
 
 
 
 
 
 
 
 
 
435
 
436
+ if not message:
437
+ return "", history, "", ""
438
 
 
439
  try:
440
+ # 검색 μˆ˜ν–‰
441
+ search_results = []
442
+ if use_search and search_client.api_key:
443
+ search_results = search_client.search(message, count=search_count)
444
+
445
+ # μ„€μ •
446
+ config = {
447
+ "temperature": temperature,
448
+ "max_tokens": max_tokens
449
+ }
450
+
451
+ # λ©€ν‹° μ—μ΄μ „νŠΈ 처리
452
+ response = await multi_agent_system.process_with_agents(
453
+ query=message,
454
+ search_results=search_results,
455
+ config=config
456
  )
457
 
458
+ # μ—μ΄μ „νŠΈ 사고 κ³Όμ • ν¬λ§·νŒ…
459
+ agent_thoughts = ""
460
+ if show_agent_thoughts:
461
+ agent_thoughts = "## πŸ€– μ—μ΄μ „νŠΈ 사고 κ³Όμ •\n\n"
462
+
463
+ for agent_resp in response.agent_responses:
464
+ role_emoji = {
465
+ AgentRole.SUPERVISOR: "πŸ‘”",
466
+ AgentRole.CREATIVE: "🎨",
467
+ AgentRole.CRITIC: "πŸ”",
468
+ AgentRole.FINALIZER: "βœ…"
469
+ }
470
+
471
+ role_name = {
472
+ AgentRole.SUPERVISOR: "κ°λ…μž (초기 ꡬ쑰화)",
473
+ AgentRole.CREATIVE: "μ°½μ˜μ„± μƒμ„±μž",
474
+ AgentRole.CRITIC: "λΉ„ν‰μž",
475
+ AgentRole.FINALIZER: "μ΅œμ’… κ°λ…μž"
476
+ }
477
+
478
+ agent_thoughts += f"### {role_emoji[agent_resp.role]} {role_name[agent_resp.role]}\n"
479
+ agent_thoughts += f"{agent_resp.content[:500]}...\n\n"
480
+
481
+ # 검색 κ²°κ³Ό ν¬λ§·νŒ…
482
+ search_display = ""
483
+ if search_results:
484
+ search_display = "## πŸ“š μ°Έκ³  자료\n\n"
485
+ for i, result in enumerate(search_results, 1):
486
+ search_display += f"**{i}. [{result['title']}]({result['url']})**\n"
487
+ search_display += f" {result['description'][:100]}...\n\n"
488
 
489
+ # 처리 μ‹œκ°„ μΆ”κ°€
490
+ final_answer = response.final_answer
491
+ final_answer += f"\n\n---\n⏱️ *처리 μ‹œκ°„: {response.processing_time:.2f}초*"
 
 
492
 
493
  # νžˆμŠ€ν† λ¦¬ μ—…λ°μ΄νŠΈ
494
+ history.append([message, final_answer])
495
+
496
+ return "", history, agent_thoughts, search_display
497
 
498
  except Exception as e:
499
+ error_msg = f"❌ 였λ₯˜ λ°œμƒ: {str(e)}"
500
  history.append([message, error_msg])
501
+ return "", history, "", ""
502
 
503
+ # Gradio μΈν„°νŽ˜μ΄μŠ€
504
+ with gr.Blocks(
505
+ title="Multi-Agent RAG System",
506
+ theme=gr.themes.Soft(),
507
+ css="""
508
+ .gradio-container {
509
+ max-width: 1400px !important;
510
+ margin: auto !important;
511
+ }
512
+ #chatbot {
513
+ height: 600px !important;
514
+ }
515
+ """
516
+ ) as demo:
517
+ gr.Markdown("""
518
+ # 🧠 Multi-Agent RAG System
519
+ ### 4단계 μ—μ΄μ „νŠΈ ν˜‘μ—…μ„ ν†΅ν•œ κ³ ν’ˆμ§ˆ λ‹΅λ³€ 생성
520
+
521
+ **처리 κ³Όμ •:** κ°λ…μž(ꡬ쑰화) β†’ μ°½μ˜μ„± μƒμ„±μž(창의적 λ‹΅λ³€) β†’ λΉ„ν‰μž(검증) β†’ μ΅œμ’… κ°λ…μž(μ’…ν•©)
522
+ """)
523
 
524
  with gr.Row():
525
+ # 메인 μ±„νŒ… μ˜μ—­
526
  with gr.Column(scale=3):
527
  chatbot = gr.Chatbot(
528
  height=500,
529
+ label="πŸ’¬ λŒ€ν™”",
530
+ elem_id="chatbot"
531
  )
532
+
533
  msg = gr.Textbox(
534
+ label="질문 μž…λ ₯",
535
+ placeholder="μ§ˆλ¬Έμ„ μž…λ ₯ν•˜μ„Έμš”... (λ©€ν‹° μ—μ΄μ „νŠΈκ°€ ν˜‘μ—…ν•˜μ—¬ λ‹΅λ³€ν•©λ‹ˆλ‹€)",
536
+ lines=3
537
  )
538
+
539
  with gr.Row():
540
+ submit = gr.Button("πŸš€ 전솑", variant="primary")
541
+ clear = gr.Button("πŸ”„ μ΄ˆκΈ°ν™”")
542
+
543
+ # μ—μ΄μ „νŠΈ 사고 κ³Όμ •
544
+ with gr.Accordion("πŸ€– μ—μ΄μ „νŠΈ 사고 κ³Όμ •", open=False):
545
+ agent_thoughts = gr.Markdown()
546
+
547
+ # 검색 κ²°κ³Ό
548
+ with gr.Accordion("πŸ“š 검색 μ†ŒμŠ€", open=False):
549
+ search_sources = gr.Markdown()
550
 
551
+ # μ„€μ • νŒ¨λ„
552
  with gr.Column(scale=1):
553
  gr.Markdown("### βš™οΈ μ„€μ •")
554
+
555
+ with gr.Group():
556
+ use_search = gr.Checkbox(
557
+ label="πŸ” μ›Ή 검색 μ‚¬μš©",
558
+ value=True
559
+ )
560
+
561
+ show_agent_thoughts = gr.Checkbox(
562
+ label="🧠 μ—μ΄μ „νŠΈ 사고과정 ν‘œμ‹œ",
563
+ value=True
564
+ )
565
+
566
+ search_count = gr.Slider(
567
+ minimum=1,
568
+ maximum=10,
569
+ value=5,
570
+ step=1,
571
+ label="검색 κ²°κ³Ό 수"
572
+ )
573
+
574
+ temperature = gr.Slider(
575
+ minimum=0,
576
+ maximum=1,
577
+ value=0.6,
578
+ step=0.1,
579
+ label="Temperature"
580
+ )
581
+
582
+ max_tokens = gr.Slider(
583
+ minimum=500,
584
+ maximum=4000,
585
+ value=2000,
586
+ step=100,
587
+ label="Max Tokens"
588
+ )
589
+
590
+ gr.Markdown("""
591
+ ### πŸ“Š μ‹œμŠ€ν…œ 정보
592
+
593
+ **μ—μ΄μ „νŠΈ μ—­ν• :**
594
+ - πŸ‘” **κ°λ…μž**: ꡬ쑰 섀계
595
+ - 🎨 **μ°½μ˜μ„±**: 창의적 생성
596
+ - πŸ” **λΉ„ν‰μž**: 검증/κ°œμ„ 
597
+ - βœ… **μ΅œμ’…**: μ’…ν•©/μ™„μ„±
598
+ """)
599
 
600
+ # 예제
601
+ gr.Examples(
602
+ examples=[
603
+ "μ–‘μž μ»΄ν“¨ν„°μ˜ 원리λ₯Ό μ΄ˆλ“±ν•™μƒλ„ 이해할 수 있게 μ„€λͺ…ν•΄μ€˜",
604
+ "2024λ…„ AI 기술 νŠΈλ Œλ“œμ™€ 미래 전망은?",
605
+ "효과적인 ν”„λ‘œκ·Έλž˜λ° ν•™μŠ΅ 방법을 λ‹¨κ³„λ³„λ‘œ μ•Œλ €μ€˜",
606
+ "κΈ°ν›„ οΏ½οΏ½ν™”κ°€ ν•œκ΅­ κ²½μ œμ— λ―ΈμΉ˜λŠ” 영ν–₯ λΆ„μ„ν•΄μ€˜",
607
+ "μŠ€νƒ€νŠΈμ—… μ°½μ—… μ‹œ κ³ λ €ν•΄μ•Ό ν•  핡심 μš”μ†Œλ“€μ€?"
608
+ ],
609
+ inputs=msg
610
+ )
611
+
612
+ # 이벀트 바인딩
613
  submit.click(
614
+ process_query,
615
+ inputs=[msg, chatbot, use_search, show_agent_thoughts,
616
+ search_count, temperature, max_tokens],
617
+ outputs=[msg, chatbot, agent_thoughts, search_sources]
618
  )
619
 
620
  msg.submit(
621
+ process_query,
622
+ inputs=[msg, chatbot, use_search, show_agent_thoughts,
623
+ search_count, temperature, max_tokens],
624
+ outputs=[msg, chatbot, agent_thoughts, search_sources]
625
  )
626
 
627
+ clear.click(
628
+ lambda: (None, None, None),
629
+ None,
630
+ [chatbot, agent_thoughts, search_sources]
631
+ )
632
 
633
  return demo
634
 
635
 
636
+ # ============================================================================
637
+ # FastAPI μ•±
638
+ # ============================================================================
639
+
640
  @asynccontextmanager
641
  async def lifespan(app: FastAPI):
642
+ """μ•± 생λͺ…μ£ΌκΈ° 관리"""
643
+ print("\n" + "="*60)
644
+ print("πŸš€ Multi-Agent RAG System Starting...")
645
+ print("="*60)
646
  yield
647
+ print("\nπŸ‘‹ Shutting down...")
 
648
 
649
 
650
  app = FastAPI(
651
+ title="Multi-Agent RAG System API",
652
+ description="4-Stage Agent Collaboration System with RAG",
653
+ version="3.0.0",
654
  lifespan=lifespan
655
  )
656
 
 
660
  allow_origins=["*"],
661
  allow_credentials=True,
662
  allow_methods=["*"],
663
+ allow_headers=["*"]
664
  )
665
 
666
+ # ν΄λΌμ΄μ–ΈνŠΈ μ΄ˆκΈ°ν™”
667
  try:
668
+ llm_client = FireworksClient()
669
+ search_client = BraveSearchClient()
670
+ multi_agent_system = MultiAgentSystem(llm_client, search_client)
671
+ except Exception as e:
672
+ print(f"⚠️ Initialization error: {e}")
673
+ llm_client = None
674
+ search_client = None
675
+ multi_agent_system = None
676
 
677
 
678
  # API μ—”λ“œν¬μΈνŠΈ
 
680
  async def root():
681
  """루트 μ—”λ“œν¬μΈνŠΈ"""
682
  return {
683
+ "name": "Multi-Agent RAG System",
684
+ "version": "3.0.0",
685
+ "status": "running",
686
+ "ui": "http://localhost:8000/ui",
687
+ "docs": "http://localhost:8000/docs"
 
688
  }
689
 
690
 
691
+ @app.post("/api/chat")
692
+ async def chat_endpoint(request: ChatRequest):
693
+ """λ©€ν‹° μ—μ΄μ „νŠΈ μ±„νŒ… API"""
694
+ if not multi_agent_system:
695
+ raise HTTPException(status_code=500, detail="System not initialized")
696
 
697
  try:
698
+ # 검색 μˆ˜ν–‰
699
+ search_results = []
700
+ if request.use_search and search_client.api_key:
701
+ last_message = request.messages[-1].content if request.messages else ""
702
+ search_results = search_client.search(last_message, count=5)
703
+
704
+ # λ©€ν‹° μ—μ΄μ „νŠΈ 처리
705
+ response = await multi_agent_system.process_with_agents(
706
+ query=request.messages[-1].content,
707
+ search_results=search_results,
708
+ config={
709
+ "temperature": request.temperature,
710
+ "max_tokens": request.max_tokens
711
+ }
712
+ )
713
+
714
+ return response
715
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
716
  except Exception as e:
717
  raise HTTPException(status_code=500, detail=str(e))
718
 
719
 
720
  @app.get("/health")
721
  async def health_check():
722
+ """ν—¬μŠ€ 체크"""
723
  return {
724
  "status": "healthy",
725
+ "timestamp": datetime.now().isoformat(),
726
+ "services": {
727
+ "llm": "ready" if llm_client else "not configured",
728
+ "search": "ready" if search_client and search_client.api_key else "not configured",
729
+ "multi_agent": "ready" if multi_agent_system else "not configured"
730
+ }
731
  }
732
 
733
 
734
+ # Gradio 마운트
735
+ if multi_agent_system:
736
+ gradio_app = create_gradio_interface(multi_agent_system, search_client)
737
+ app = gr.mount_gradio_app(app, gradio_app, path="/ui")
738
 
739
 
740
+ # ============================================================================
741
  # 메인 μ‹€ν–‰
742
+ # ============================================================================
743
+
744
  if __name__ == "__main__":
745
+ print("""
746
+ ╔══════════════════════════════════════════════════════════════╗
747
+ β•‘ 🧠 Multi-Agent RAG-Enhanced LLM System 🧠 β•‘
748
+ β•‘ β•‘
749
+ β•‘ κ°λ…μž β†’ μ°½μ˜μ„± μƒμ„±μž β†’ λΉ„ν‰μž β†’ μ΅œμ’… κ°λ…μž β•‘
750
+ β•‘ 4단계 ν˜‘μ—…μ„ ν†΅ν•œ κ³ ν’ˆμ§ˆ λ‹΅λ³€ 생성 β•‘
751
+ β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
752
+ """)
753
 
754
  # API ν‚€ 확인
755
  if not os.getenv("FIREWORKS_API_KEY"):
756
+ print("\n⚠️ FIREWORKS_API_KEYκ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
757
+ key = input("Fireworks API Key μž…λ ₯: ").strip()
758
+ if key:
759
+ os.environ["FIREWORKS_API_KEY"] = key
760
+ llm_client = FireworksClient(key)
761
+
762
+ if not os.getenv("BRAVE_SEARCH_API_KEY"):
763
+ print("\n⚠️ BRAVE_SEARCH_API_KEYκ°€ μ„€μ •λ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.")
764
+ print(" (선택사항: 검색 κΈ°λŠ₯을 μ‚¬μš©ν•˜λ €λ©΄ μž…λ ₯)")
765
+ key = input("Brave Search API Key μž…λ ₯ (Enter=κ±΄λ„ˆλ›°κΈ°): ").strip()
766
+ if key:
767
+ os.environ["BRAVE_SEARCH_API_KEY"] = key
768
+ search_client = BraveSearchClient(key)
769
+
770
+ # μ‹œμŠ€ν…œ μž¬μ΄ˆκΈ°ν™”
771
+ if llm_client:
772
+ multi_agent_system = MultiAgentSystem(llm_client, search_client)
773
+ gradio_app = create_gradio_interface(multi_agent_system, search_client)
774
+ app = gr.mount_gradio_app(app, gradio_app, path="/ui")
775
 
776
+ print("\n" + "="*60)
777
+ print("βœ… μ‹œμŠ€ν…œ μ€€λΉ„ μ™„λ£Œ!")
778
+ print("="*60)
779
+ print("\nπŸ“ 접속 μ£Όμ†Œ:")
780
+ print(" 🎨 Gradio UI: http://localhost:8000/ui")
781
+ print(" πŸ“š API Docs: http://localhost:8000/docs")
782
+ print(" πŸ”§ Chat API: POST http://localhost:8000/api/chat")
783
+ print("\nπŸ’‘ Ctrl+Cλ₯Ό 눌러 μ’…λ£Œ")
784
+ print("="*60 + "\n")
785
 
786
  uvicorn.run(
787
  app,
788
  host="0.0.0.0",
789
+ port=8000,
790
+ reload=False,
791
+ log_level="info"
792
  )