fix(bot): unified prompt 분류 -> 라우팅 흐름 구현
This commit is contained in:
@@ -155,6 +155,31 @@ async def _agent_call(text: str, history: str, project_path: str) -> str:
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_unified_response(raw: str) -> dict:
|
||||||
|
"""Gemini unified prompt 응답에서 JSON 추출."""
|
||||||
|
import re as _re
|
||||||
|
|
||||||
|
# 1) ```json ... ``` 블록
|
||||||
|
m = _re.search(r"```json\s*\n(.+?)```", raw, _re.DOTALL)
|
||||||
|
if m:
|
||||||
|
try:
|
||||||
|
return json.loads(m.group(1))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 2) { ... } 직접
|
||||||
|
m = _re.search(r"\{[\s\S]*\"mode\"[\s\S]*?\}", raw)
|
||||||
|
if m:
|
||||||
|
try:
|
||||||
|
return json.loads(m.group(0))
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 3) 파싱 실패 → chat 모드 폴백
|
||||||
|
logger.warning(f"unified 응답 JSON 파싱 실패: {raw[:200]}")
|
||||||
|
return {"mode": "chat", "response": raw}
|
||||||
|
|
||||||
|
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
# 이벤트 핸들러
|
# 이벤트 핸들러
|
||||||
# ──────────────────────────────────────────────
|
# ──────────────────────────────────────────────
|
||||||
@@ -330,30 +355,32 @@ async def on_message(message: discord.Message):
|
|||||||
await message.reply("실행 중인 작업이 없습니다.")
|
await message.reply("실행 중인 작업이 없습니다.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 에이전트 호출 (CLI 도구 자율 실행)
|
# 통합 분류 → 라우팅 (unified prompt → NC handler / chat / agent)
|
||||||
channel_id = message.channel.id
|
channel_id = message.channel.id
|
||||||
if channel_id in _running_tasks and not _running_tasks[channel_id].done():
|
if channel_id in _running_tasks and not _running_tasks[channel_id].done():
|
||||||
await message.reply("⚠️ 이미 작업이 실행 중입니다. `취소` 후 다시 요청하세요.")
|
await message.reply("⚠️ 이미 작업이 실행 중입니다. `취소` 후 다시 요청하세요.")
|
||||||
return
|
return
|
||||||
|
|
||||||
async def _tracked_agent():
|
async def _classify_and_route():
|
||||||
progress_msg = None
|
progress_msg = None
|
||||||
try:
|
try:
|
||||||
# 진행 표시
|
|
||||||
progress_msg = await message.channel.send(
|
progress_msg = await message.channel.send(
|
||||||
embed=discord.Embed(
|
embed=discord.Embed(
|
||||||
title="🤖 에이전트 처리 중...",
|
title="🤖 처리 중...",
|
||||||
description=f"```{user_text[:200]}```",
|
description=f"```{user_text[:200]}```",
|
||||||
color=0xF39C12,
|
color=0xF39C12,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
async with message.channel.typing():
|
async with message.channel.typing():
|
||||||
# 일반 → Gemini agent 모드 (도구 자동 호출)
|
# 1단계: unified prompt로 분류
|
||||||
|
gemini = GeminiCaller()
|
||||||
history = await _get_channel_history(message.channel, limit=10)
|
history = await _get_channel_history(message.channel, limit=10)
|
||||||
response = await _agent_call(user_text, history, ws.path)
|
classify_input = f"{history}## User Message\n{user_text}"
|
||||||
|
raw = await gemini.call("unified", classify_input, timeout=60)
|
||||||
|
|
||||||
logger.info(f"에이전트 응답: \"{user_text[:50]}\" -> {len(response)}자")
|
# JSON 파싱
|
||||||
|
parsed = _parse_unified_response(raw)
|
||||||
|
|
||||||
# 진행 메시지 삭제
|
# 진행 메시지 삭제
|
||||||
if progress_msg:
|
if progress_msg:
|
||||||
@@ -362,18 +389,59 @@ async def on_message(message: discord.Message):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not response:
|
mode = parsed.get("mode", "chat")
|
||||||
await message.reply("응답을 생성하지 못했습니다.")
|
logger.info(f"분류 결과: mode={mode} — \"{user_text[:50]}\"")
|
||||||
return
|
|
||||||
|
|
||||||
# 응답 전송
|
# ── 라우팅 ──
|
||||||
|
|
||||||
|
if mode == "nextcloud":
|
||||||
|
# NC 핸들러로 직접 라우팅
|
||||||
|
await _nc_handler.handle(parsed, message.channel)
|
||||||
|
|
||||||
|
elif mode == "chat":
|
||||||
|
# 즉시 응답
|
||||||
|
response = parsed.get("response", "")
|
||||||
|
if response:
|
||||||
if len(response) <= 2000:
|
if len(response) <= 2000:
|
||||||
await message.reply(response)
|
await message.reply(response)
|
||||||
else:
|
else:
|
||||||
for i in range(0, len(response), 4000):
|
for i in range(0, len(response), 4000):
|
||||||
chunk = response[i:i + 4000]
|
embed = discord.Embed(description=response[i:i + 4000], color=0x3498DB)
|
||||||
embed = discord.Embed(description=chunk, color=0x3498DB)
|
|
||||||
await message.channel.send(embed=embed)
|
await message.channel.send(embed=embed)
|
||||||
|
else:
|
||||||
|
await message.reply("응답을 생성하지 못했습니다.")
|
||||||
|
|
||||||
|
elif mode == "clarify":
|
||||||
|
question = parsed.get("question", "무엇을 도와드릴까요?")
|
||||||
|
await message.reply(
|
||||||
|
embed=discord.Embed(
|
||||||
|
title="🤔 확인이 필요합니다",
|
||||||
|
description=question,
|
||||||
|
color=0xF39C12,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
elif mode == "anime":
|
||||||
|
# 기존 anime 핸들러 호출
|
||||||
|
from handlers.anime_handler import handle_anime_action
|
||||||
|
await handle_anime_action(parsed, message.channel)
|
||||||
|
|
||||||
|
elif mode == "task":
|
||||||
|
# 에이전트 모드 (파일 작업 필요)
|
||||||
|
async with message.channel.typing():
|
||||||
|
response = await _agent_call(user_text, history, ws.path)
|
||||||
|
if response:
|
||||||
|
if len(response) <= 2000:
|
||||||
|
await message.reply(response)
|
||||||
|
else:
|
||||||
|
for i in range(0, len(response), 4000):
|
||||||
|
embed = discord.Embed(description=response[i:i + 4000], color=0x3498DB)
|
||||||
|
await message.channel.send(embed=embed)
|
||||||
|
else:
|
||||||
|
await message.reply("응답을 생성하지 못했습니다.")
|
||||||
|
|
||||||
|
else:
|
||||||
|
await message.reply(f"알 수 없는 모드: `{mode}`")
|
||||||
|
|
||||||
except asyncio.CancelledError:
|
except asyncio.CancelledError:
|
||||||
await message.channel.send(
|
await message.channel.send(
|
||||||
@@ -386,12 +454,12 @@ async def on_message(message: discord.Message):
|
|||||||
except GeminiCallError as e:
|
except GeminiCallError as e:
|
||||||
await message.reply(f"⚠️ AI 호출 오류: {str(e)[:200]}")
|
await message.reply(f"⚠️ AI 호출 오류: {str(e)[:200]}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"에이전트 호출 오류: {e}", exc_info=True)
|
logger.error(f"분류/라우팅 오류: {e}", exc_info=True)
|
||||||
await message.reply(f"❌ 오류: {str(e)[:200]}")
|
await message.reply(f"❌ 오류: {str(e)[:200]}")
|
||||||
finally:
|
finally:
|
||||||
_running_tasks.pop(channel_id, None)
|
_running_tasks.pop(channel_id, None)
|
||||||
|
|
||||||
task = asyncio.create_task(_tracked_agent())
|
task = asyncio.create_task(_classify_and_route())
|
||||||
_running_tasks[channel_id] = task
|
_running_tasks[channel_id] = task
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user