From e65e2250a38f7abd92523066cdc23e2cb3a6a071 Mon Sep 17 00:00:00 2001 From: CD Date: Sat, 7 Mar 2026 01:26:32 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20Planner=20direct=20=EB=AA=A8=EB=93=9C?= =?UTF-8?q?=20+=20Reviewer=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - direct 모드: Planner 직접 처리 → Reviewer 검증 → 완료 (2단계) - tasks 모드: 기존대로 full pipeline (5단계) - 대화 히스토리 embed 내용 포함 (clarify 맥락 유지) --- api/discord_bot.py | 43 +++++++++++++++++++++++++++++++++++++++++-- prompts/planner.md | 42 +++++++++++++++++++++++++++++++----------- 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/api/discord_bot.py b/api/discord_bot.py index d9e2bb2..189272b 100644 --- a/api/discord_bot.py +++ b/api/discord_bot.py @@ -382,12 +382,51 @@ async def _handle_task(message: discord.Message, text: str, ws): ) pipeline.setup() - # 1. Plan - embed.title = "🔍 작업 분해 중..." + # 1. Plan (direct 모드면 Planner가 직접 처리) + embed.title = "🔍 분석 중..." embed.color = 0xF39C12 await status_msg.edit(embed=embed) plan = await pipeline.plan(text) + + # ── Direct 모드: Planner가 직접 처리 완료 ── + is_direct = plan.get("direct", False) + if isinstance(is_direct, str): + is_direct = is_direct.lower() in ("true", "yes") + + if is_direct: + result_text = plan.get("result", plan.get("summary", "완료")) + direct_embed = discord.Embed( + title="✅ " + plan.get("summary", "처리 완료"), + description=result_text[:2000], + color=0x2ECC71, + ) + await safe_send_embed(message.channel, direct_embed) + + # Reviewer 검증 + review_embed = discord.Embed( + title="🔍 리뷰어 검토 중...", + color=0xF39C12, + ) + review_msg = await message.channel.send(embed=review_embed) + + direct_tasks = [{"title": plan.get("summary", "직접 처리"), "description": result_text}] + review = await pipeline.batch_review(direct_tasks, [result_text]) + + passed = review.get("passed", True) + if isinstance(passed, str): + passed = passed.lower() in ("true", "yes", "pass") + + review_embed.title = f"{'✅' if passed else '⚠️'} 리뷰 결과" + review_embed.description = review.get("summary", str(review))[:500] + review_embed.color = 0x2ECC71 if passed else 0xE74C3C + review_embed.set_footer(text=f"ID: {task_id} | {ws.name} | direct") + await review_msg.edit(embed=review_embed) + + pipeline.docs.record_session(text, {"summary": result_text}, plan) + return + + # ── Tasks 모드: Coder에게 분배 ── tasks = plan.get("tasks", []) if tasks: diff --git a/prompts/planner.md b/prompts/planner.md index b93da6a..b36e901 100644 --- a/prompts/planner.md +++ b/prompts/planner.md @@ -1,20 +1,28 @@ -You are a **Planner** — 사용자 요청을 분석하여 태스크로 변환합니다. +You are a **Planner** — 사용자 요청을 분석하여 직접 처리하거나 태스크로 변환합니다. ## 역할 사용자의 요청과 프로젝트 컨텍스트를 보고: 1. 무엇을 해야 하는지 분석 -2. **가능한 한 적은 수의 태스크**로 구성 -3. 각 태스크의 구현 내용을 상세히 기술 +2. **직접 처리할 수 있으면 직접 처리** (파일 삭제, 정리, 간단한 수정 등) +3. 복잡한 작업만 태스크로 분배 -## 태스크 분할 원칙 +## 직접 처리 (direct 모드) -**기본 원칙: 1개로 충분하면 반드시 1개만 만드세요.** +다음과 같은 경우 당신이 직접 처리하세요: +- 파일/폴더 삭제, 정리, 이름 변경 +- 간단한 설정 변경, 한두 줄 수정 +- 프로젝트 구조 확인, 현황 파악 +- 코더에게 넘기기엔 너무 단순한 작업 -여러 태스크로 쪼개는 것은 **서로 독립적인 기능이 2개 이상**일 때만 합니다. -예를 들어: -- "로그인 페이지 만들어줘" → **1개** (한 기능) -- "로그인 페이지와 결제 시스템 만들어줘" → 2개 (독립 기능) +직접 처리 시 에이전트 도구로 파일을 직접 수정한 뒤 결과를 보고하세요. + +## 태스크 분배 (tasks 모드) + +코딩이 필요한 복잡한 작업만 태스크로 만드세요. +**1개로 충분하면 반드시 1개만 만드세요.** + +여러 태스크는 **서로 독립적인 기능이 2개 이상**일 때만. 절대 하지 말 것: - 하나의 기능을 "파일 생성", "스타일 추가", "로직 구현"으로 쪼개기 @@ -27,14 +35,25 @@ review_feedback이 주어지면, 이전 시도에서 실패한 원인을 분석 ## Output Format +### 직접 처리한 경우: ```json { - "summary": "작업 요약 (한국어)", + "summary": "처리 결과 요약", + "direct": true, + "result": "구체적으로 무엇을 했는지" +} +``` + +### 태스크 분배가 필요한 경우: +```json +{ + "summary": "작업 요약", + "direct": false, "tasks": [ { "id": 1, "title": "태스크 제목", - "description": "구현할 내용을 상세히 기술. 에이전트가 이것만 보고 작업합니다.", + "description": "구현 세부사항. 에이전트가 이것만 보고 작업합니다.", "type": "create|modify|delete" } ], @@ -46,3 +65,4 @@ review_feedback이 주어지면, 이전 시도에서 실패한 원인을 분석 - description에 모든 구현 세부사항을 적으세요 - 한국어로 작성하세요 +- 단순한 일을 복잡하게 만들지 마세요