fix(debate): Wiki.js 짧은 제목 생성, AG 작업 종료 이벤트 대기로 변경

This commit is contained in:
2026-03-20 07:15:51 +09:00
parent 87d534cfcc
commit f2ca6e5c28

View File

@@ -48,6 +48,7 @@ FILE_STABLE_DELAY = 5 # 파일 작성 완료 대기 (초)
class DebateSession: class DebateSession:
topic: str = "" topic: str = ""
topic_slug: str = "" # Wiki 경로용 topic_slug: str = "" # Wiki 경로용
wiki_title: str = "" # Wiki 페이지 짧은 제목
round: int = 0 round: int = 0
max_rounds: int = MAX_ROUNDS max_rounds: int = MAX_ROUNDS
active: bool = False active: bool = False
@@ -62,6 +63,7 @@ class DebateHandler:
self.bot = bot self.bot = bot
self.session = DebateSession() self.session = DebateSession()
self._debate_task: Optional[asyncio.Task] = None self._debate_task: Optional[asyncio.Task] = None
self._response_event = asyncio.Event()
# ═══════════════════════════════════════════ # ═══════════════════════════════════════════
# 공개 API # 공개 API
@@ -73,17 +75,29 @@ class DebateHandler:
await ctx.reply("⚠️ 이미 진행 중. `!debate-stop`으로 먼저 종료.") await ctx.reply("⚠️ 이미 진행 중. `!debate-stop`으로 먼저 종료.")
return return
# slug 생성 # slug 및 짧은 제목 생성
try: 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 from tools.wiki_client import WikiClient
slug = WikiClient.slugify(topic) slug = WikiClient.slugify(short_title)
if len(slug) > 80: if len(slug) > 80:
slug = slug[:80].rstrip("-") slug = slug[:80].rstrip("-")
except Exception: except Exception as e:
slug = topic[:20].replace(" ", "-").lower() logger.warning(f"짧은 제목 생성 실패: {e}")
short_title = topic[:20]
slug = short_title.replace(" ", "-").lower()
self.session = DebateSession( self.session = DebateSession(
topic=topic, topic_slug=slug, topic=topic, topic_slug=slug, wiki_title=short_title,
active=True, max_rounds=MAX_ROUNDS, active=True, max_rounds=MAX_ROUNDS,
) )
@@ -145,9 +159,21 @@ class DebateHandler:
self.session.pending_question = "" self.session.pending_question = ""
async def on_agent_message(self, message: discord.Message): async def on_agent_message(self, message: discord.Message):
"""AG 채널 메시지 감지 — response.md 체크 트리거.""" """AG 채널 메시지 감지 — 완료 시그널이면 event set."""
# 별도 처리 불필요 — auto_loop에서 파일 폴링으로 처리 if not self.session.active:
pass 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: async def _wait_for_response(self, speaker: str) -> str:
"""response.md에 내용이 채워질 때까지 폴링.""" """AG의 Discord 완료 메시지 대기 후 response.md 읽기."""
resp_path = AGENT_PATHS[speaker] / "response.md" resp_path = AGENT_PATHS[speaker] / "response.md"
# response.md 비우기 # response.md 비우기
resp_path.write_text("", encoding="utf-8") resp_path.write_text("", encoding="utf-8")
@@ -287,38 +313,30 @@ class DebateHandler:
f"`{speaker}` 답변 대기 중..." f"`{speaker}` 답변 대기 중..."
) )
start = time.time() self._response_event.clear()
last_size = 0
stable_since = 0 try:
# Discord에서 "작업 종료" 메시지가 올 때까지 대기
await asyncio.wait_for(self._response_event.wait(), timeout=RESPONSE_TIMEOUT)
while time.time() - start < RESPONSE_TIMEOUT:
if not self.session.active: if not self.session.active:
return "" return ""
await asyncio.sleep(FILE_CHECK_INTERVAL) # 완료 시그널을 받으면 파일 읽기
if resp_path.exists():
if not resp_path.exists(): content = resp_path.read_text(encoding="utf-8").strip()
continue if content:
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:
return content return content
else: else:
last_size = current_size if ctrl:
stable_since = 0 await ctrl.send(f"⚠️ `{speaker}` 완료 신호를 받았으나 `response.md`가 비어있습니다.")
# 타임아웃 return ""
if ctrl:
await ctrl.send(f"⏰ `{speaker}` 응답 시간 초과 ({RESPONSE_TIMEOUT}초)") except asyncio.TimeoutError:
return "" if ctrl:
await ctrl.send(f"⏰ `{speaker}` 응답 시간 초과 ({RESPONSE_TIMEOUT}초)")
return ""
# ═══════════════════════════════════════════ # ═══════════════════════════════════════════
# 사회자 로직 (Flash) # 사회자 로직 (Flash)
@@ -503,7 +521,7 @@ class DebateHandler:
# Wiki.js에 업로드 # Wiki.js에 업로드
await self._wiki_upsert( await self._wiki_upsert(
f"debates/{self.session.topic_slug}/working-document", f"debates/{self.session.topic_slug}/working-document",
f"{self.session.topic} — Working Document", f"{self.session.wiki_title} — Working Document",
updated, updated,
) )
else: else:
@@ -532,7 +550,7 @@ class DebateHandler:
# Wiki.js에 업로드 # Wiki.js에 업로드
await self._wiki_upsert( await self._wiki_upsert(
f"debates/{self.session.topic_slug}/round-log", f"debates/{self.session.topic_slug}/round-log",
f"{self.session.topic} — Round Log", f"{self.session.wiki_title} — Round Log",
updated_log, updated_log,
) )