From f2ca6e5c282b077e70731ed80e70d342fbfa02af Mon Sep 17 00:00:00 2001 From: Variet Agent Date: Fri, 20 Mar 2026 07:15:51 +0900 Subject: [PATCH] =?UTF-8?q?fix(debate):=20Wiki.js=20=EC=A7=A7=EC=9D=80=20?= =?UTF-8?q?=EC=A0=9C=EB=AA=A9=20=EC=83=9D=EC=84=B1,=20AG=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20=EC=A2=85=EB=A3=8C=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=8C=80=EA=B8=B0=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/debate_handler.py | 92 +++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/handlers/debate_handler.py b/handlers/debate_handler.py index 43b42b9..6157e29 100644 --- a/handlers/debate_handler.py +++ b/handlers/debate_handler.py @@ -48,6 +48,7 @@ FILE_STABLE_DELAY = 5 # 파일 작성 완료 대기 (초) class DebateSession: topic: str = "" topic_slug: str = "" # Wiki 경로용 + wiki_title: str = "" # Wiki 페이지 짧은 제목 round: int = 0 max_rounds: int = MAX_ROUNDS active: bool = False @@ -62,6 +63,7 @@ class DebateHandler: self.bot = bot self.session = DebateSession() self._debate_task: Optional[asyncio.Task] = None + self._response_event = asyncio.Event() # ═══════════════════════════════════════════ # 공개 API @@ -73,17 +75,29 @@ class DebateHandler: await ctx.reply("⚠️ 이미 진행 중. `!debate-stop`으로 먼저 종료.") return - # slug 생성 + # slug 및 짧은 제목 생성 try: + from core.gemini_caller import GeminiCaller + caller = GeminiCaller() + short_title = await caller.call_simple( + f"다음 토론 주제를 3~5단어의 짧고 명확한 제목으로 요약해주세요. 마크다운 기호 없이 텍스트만 출력하세요.\n\n주제: {topic}", + timeout=30 + ) + short_title = short_title.strip() + if len(short_title) > 50: + short_title = short_title[:50] + from tools.wiki_client import WikiClient - slug = WikiClient.slugify(topic) + slug = WikiClient.slugify(short_title) if len(slug) > 80: slug = slug[:80].rstrip("-") - except Exception: - slug = topic[:20].replace(" ", "-").lower() + except Exception as e: + logger.warning(f"짧은 제목 생성 실패: {e}") + short_title = topic[:20] + slug = short_title.replace(" ", "-").lower() self.session = DebateSession( - topic=topic, topic_slug=slug, + topic=topic, topic_slug=slug, wiki_title=short_title, active=True, max_rounds=MAX_ROUNDS, ) @@ -145,9 +159,21 @@ class DebateHandler: self.session.pending_question = "" async def on_agent_message(self, message: discord.Message): - """AG 채널 메시지 감지 — response.md 체크 트리거.""" - # 별도 처리 불필요 — auto_loop에서 파일 폴링으로 처리 - pass + """AG 채널 메시지 감지 — 완료 시그널이면 event set.""" + if not self.session.active: + return + + active_ch_id = DEBATE_AGENTS.get(self.session.current_speaker) + if message.channel.id != active_ch_id: + return + + content = message.content or "" + embed_texts = " ".join([e.description for e in message.embeds if e.description]) + full_text = content + " " + embed_texts + + if "작업 종료" in full_text or "작성 완료" in full_text: + logger.info(f"[{self.session.current_speaker}] 완료 시그널 감지: {full_text[:50]}") + self._response_event.set() # ═══════════════════════════════════════════ # 자동 토론 루프 @@ -275,7 +301,7 @@ class DebateHandler: ) async def _wait_for_response(self, speaker: str) -> str: - """response.md에 내용이 채워질 때까지 폴링.""" + """AG의 Discord 완료 메시지 대기 후 response.md 읽기.""" resp_path = AGENT_PATHS[speaker] / "response.md" # response.md 비우기 resp_path.write_text("", encoding="utf-8") @@ -287,38 +313,30 @@ class DebateHandler: f"`{speaker}` 답변 대기 중..." ) - start = time.time() - last_size = 0 - stable_since = 0 + self._response_event.clear() - while time.time() - start < RESPONSE_TIMEOUT: + try: + # Discord에서 "작업 종료" 메시지가 올 때까지 대기 + await asyncio.wait_for(self._response_event.wait(), timeout=RESPONSE_TIMEOUT) + if not self.session.active: return "" - await asyncio.sleep(FILE_CHECK_INTERVAL) - - if not resp_path.exists(): - continue - - content = resp_path.read_text(encoding="utf-8").strip() - if not content: - continue - - current_size = len(content) - if current_size == last_size and current_size > 0: - # 파일 크기 안정 — 작성 완료로 판단 - if stable_since == 0: - stable_since = time.time() - elif time.time() - stable_since >= FILE_STABLE_DELAY: + # 완료 시그널을 받으면 파일 읽기 + if resp_path.exists(): + content = resp_path.read_text(encoding="utf-8").strip() + if content: return content - else: - last_size = current_size - stable_since = 0 + else: + if ctrl: + await ctrl.send(f"⚠️ `{speaker}` 완료 신호를 받았으나 `response.md`가 비어있습니다.") - # 타임아웃 - if ctrl: - await ctrl.send(f"⏰ `{speaker}` 응답 시간 초과 ({RESPONSE_TIMEOUT}초)") - return "" + return "" + + except asyncio.TimeoutError: + if ctrl: + await ctrl.send(f"⏰ `{speaker}` 응답 시간 초과 ({RESPONSE_TIMEOUT}초)") + return "" # ═══════════════════════════════════════════ # 사회자 로직 (Flash) @@ -503,7 +521,7 @@ class DebateHandler: # Wiki.js에 업로드 await self._wiki_upsert( f"debates/{self.session.topic_slug}/working-document", - f"{self.session.topic} — Working Document", + f"{self.session.wiki_title} — Working Document", updated, ) else: @@ -532,7 +550,7 @@ class DebateHandler: # Wiki.js에 업로드 await self._wiki_upsert( f"debates/{self.session.topic_slug}/round-log", - f"{self.session.topic} — Round Log", + f"{self.session.wiki_title} — Round Log", updated_log, )