fix(debate): idle-state 처리 + slug 탐색 — AG 대기 규칙, list-debates 커맨드, handler 시그널 개선

This commit is contained in:
2026-03-22 19:33:05 +09:00
parent a73774d63e
commit 30a9f6b465
9 changed files with 130 additions and 17 deletions

View File

@@ -2,7 +2,7 @@
> AI 에이전트는 **세션 시작 시** 이 문서를 읽어 전체 맥락을 파악합니다. > AI 에이전트는 **세션 시작 시** 이 문서를 읽어 전체 맥락을 파악합니다.
> **세션 종료 시** 변경사항을 이 문서에 반영합니다. > **세션 종료 시** 변경사항을 이 문서에 반영합니다.
> Last updated: 2026-03-20 > Last updated: 2026-03-22
## 시스템 개요 ## 시스템 개요
@@ -70,10 +70,11 @@ Discord 메시지
| 에이전트 워크플로우 | `.agent/` | ✅ | STATUS.md + 수칙 업데이트 | | 에이전트 워크플로우 | `.agent/` | ✅ | STATUS.md + 수칙 업데이트 |
| Wiki.js | `tools/wiki_client.py` | ✅ | GraphQL CRUD + 대시보드 + singleByPath | | Wiki.js | `tools/wiki_client.py` | ✅ | GraphQL CRUD + 대시보드 + singleByPath |
| AI Debate Room | `handlers/debate_handler.py` | 🚧 | Wiki.js 기반 분산 토론 (AG 분리) | | AI Debate Room | `handlers/debate_handler.py` | 🚧 | Wiki.js 기반 분산 토론 (AG 분리) |
| Debate AG 프로젝트 | `debate-agent/` | 🚧 | gemini/opus AG 스캐폴딩 + wiki_debate.py | | Debate AG 프로젝트 | `debate-agent/` | 🚧 | idle-state 처리 + slug 탐색 + list-debates |
## 최근 마일스톤 ## 최근 마일스톤
- **2026-03-22**: debate-agent idle-state 처리 — 대기 규칙 + slug 탐색 + list-debates + handler 시그널 개선
- **2026-03-21**: debate-agent 분산 토론 시스템 — Wiki.js 기반 통신, AG 프로젝트 스캐폴딩, handler 리팩토링 - **2026-03-21**: debate-agent 분산 토론 시스템 — Wiki.js 기반 통신, AG 프로젝트 스캐폴딩, handler 리팩토링
- **2026-03-20**: AI Debate Room v2 — 파일 기반 자동 토론 + Flash 합의 판정 + Wiki.js 동기화 - **2026-03-20**: AI Debate Room v2 — 파일 기반 자동 토론 + Flash 합의 판정 + Wiki.js 동기화
- **2026-03-18**: Nextcloud 4모듈 + NC핸들러 + AI Foreman v0.1 + unified 분류→라우팅 구현 - **2026-03-18**: Nextcloud 4모듈 + NC핸들러 + AI Foreman v0.1 + unified 분류→라우팅 구현

View File

@@ -1,5 +1,5 @@
--- ---
description: 토론 세션을 시작합니다. 사회자의 input을 읽고 response를 작성합니다. description: 토론 세션을 시작합니다. 활성 토론이 없으면 대기합니다.
--- ---
# 토론 참여 시작 # 토론 참여 시작
@@ -12,15 +12,26 @@ description: 토론 세션을 시작합니다. 사회자의 input을 읽고 resp
`GEMINI.md`를 읽어 토론 참여자 행동 규칙을 확인합니다. `GEMINI.md`를 읽어 토론 참여자 행동 규칙을 확인합니다.
### 2. 사회자 지시 읽기 ### 2. 활성 토론 탐색
```powershell
python tools/wiki_debate.py list-debates
```
- 활성 토론이 **없으면** → "진행 중인 토론이 없습니다. 사회자의 시그널을 기다리세요."라고 응답하고 **여기서 종료합니다. 이후 단계를 실행하지 마세요.**
- 활성 토론이 **있으면** → 해당 slug를 사용하여 아래 단계를 진행합니다.
- ⚠️ slug를 추측하거나 임의로 만들지 마세요. 반드시 위 명령의 출력에서 가져오세요.
### 3. 사회자 지시 읽기
```powershell ```powershell
python tools/wiki_debate.py read debates/{slug}/input-gemini python tools/wiki_debate.py read debates/{slug}/input-gemini
``` ```
> `{slug}`는 현재 진행 중인 토론 slug입니다. `input-gemini` 페이지가 비어있으면 아직 사회자가 input을 작성하지 않은 것이니 대기하세요. > `{slug}`는 2단계에서 발견한 실제 토론 slug로 치환합니다.
> 페이지가 비어있거나 `[오류]`가 반환되면 → "사회자의 지시를 기다리는 중입니다"라고 응답하고 **종료합니다.**
### 3. 합의 상태 확인 ### 4. 합의 상태 확인
```powershell ```powershell
python tools/wiki_debate.py read debates/{slug}/working-document python tools/wiki_debate.py read debates/{slug}/working-document
@@ -28,7 +39,7 @@ python tools/wiki_debate.py read debates/{slug}/working-document
사회자가 정리한 현재 합의 상태를 확인합니다. 사회자가 정리한 현재 합의 상태를 확인합니다.
### 4. 답변 작성 및 업로드 ### 5. 답변 작성 및 업로드
답변을 작성한 후 response 페이지에 업로드: 답변을 작성한 후 response 페이지에 업로드:
@@ -42,6 +53,6 @@ python tools/wiki_debate.py write debates/{slug}/response-gemini "답변 전문"
python tools/wiki_debate.py write-file debates/{slug}/response-gemini response_draft.md python tools/wiki_debate.py write-file debates/{slug}/response-gemini response_draft.md
``` ```
### 5. Discord 완료 시그널 ### 6. Discord 완료 시그널
Discord에 "response 작성 완료"를 게시하여 사회자에게 알립니다. Discord에 "response 작성 완료"를 게시하여 사회자에게 알립니다.

View File

@@ -30,13 +30,22 @@
6. **근거 명시** — 기술적 근거, 사례, 레퍼런스를 포함하세요 6. **근거 명시** — 기술적 근거, 사례, 레퍼런스를 포함하세요
7. **합의 가능 시 인정** — 상대 의견이 맞으면 솔직히 인정하고 발전시키세요 7. **합의 가능 시 인정** — 상대 의견이 맞으면 솔직히 인정하고 발전시키세요
## 대기 상태 (Idle Mode)
- 진행 중인 토론이 없으면 → **아무것도 하지 마세요.** "진행 중인 토론이 없습니다"라고 답변만 하세요.
- `input-gemini` 페이지가 비어있거나 존재하지 않으면 → **"사회자의 지시를 기다리는 중입니다"라고 답하세요.**
- 사회자가 Discord 시그널을 보내면 그때 `/start`를 다시 실행하여 시작합니다.
- **절대로** 토론 문서가 없다고 해서 자체적으로 해결을 시도하지 마세요.
## 금지 사항 ## 금지 사항
- ❌ 상대방의 `input-opus` 또는 `response-opus` 페이지 접근 - ❌ 상대방의 `input-opus` 또는 `response-opus` 페이지 접근
-`working-document` 수정 (읽기만 — 사회자만 편집) -`working-document` 수정 (읽기만 — 사회자만 편집)
- ❌ 소스 코드 작성 (문서 수준 논의만) - ❌ 소스 코드 작성/수정 (문서 수준 논의만)
- ❌ 사회자 지시 무시 - ❌ 사회자 지시 무시
- ❌ 주제에서 벗어난 발언 - ❌ 주제에서 벗어난 발언
- ❌ 근거 없는 주장 - ❌ 근거 없는 주장
- ❌ "이하생략" 또는 답변 축약 - ❌ "이하생략" 또는 답변 축약
- ❌ Discord에 긴 답변 직접 게시 (반드시 Wiki.js response 페이지에) - ❌ Discord에 긴 답변 직접 게시 (반드시 Wiki.js response 페이지에)
- ❌ 토론 문서가 없을 때 자체적으로 해결 시도 (소스 수정, 파일 생성, 설정 변경 등)
- ❌ slug를 추측하거나 임의로 생성

View File

@@ -66,6 +66,28 @@ async def _query(query: str, variables: dict = None) -> dict:
return data.get("data", {}) return data.get("data", {})
async def list_debates() -> list[str]:
"""Wiki.js에서 debates/ 하위의 활성 토론 slug 목록을 조회."""
query = """
query {
pages { list(orderBy: UPDATED, orderByDirection: DESC) {
id, path, updatedAt
}}
}
"""
data = await _query(query)
pages = data.get("pages", {}).get("list", [])
slugs = set()
for p in pages:
path = p.get("path", "")
# debates/{slug}/input-* 형태의 페이지에서 slug 추출
if path.startswith("debates/") and "/input-" in path:
parts = path.split("/")
if len(parts) >= 3:
slugs.add(parts[1])
return sorted(slugs)
async def read_page(path: str) -> str: async def read_page(path: str) -> str:
"""경로로 페이지 읽기 (singleByPath).""" """경로로 페이지 읽기 (singleByPath)."""
query = """ query = """
@@ -143,6 +165,18 @@ async def main():
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
args = sys.argv[1:] args = sys.argv[1:]
# list-debates 커맨드는 인수 불필요
if args and args[0] == "list-debates":
slugs = await list_debates()
if slugs:
print("활성 토론 목록:")
for s in slugs:
print(f" - {s}")
else:
print("진행 중인 토론이 없습니다.")
return
if len(args) < 2: if len(args) < 2:
print(__doc__) print(__doc__)
return return

View File

@@ -1,5 +1,5 @@
--- ---
description: 토론 세션을 시작합니다. 사회자의 input을 읽고 response를 작성합니다. description: 토론 세션을 시작합니다. 활성 토론이 없으면 대기합니다.
--- ---
# 토론 참여 시작 # 토론 참여 시작
@@ -12,15 +12,26 @@ description: 토론 세션을 시작합니다. 사회자의 input을 읽고 resp
`GEMINI.md`를 읽어 토론 참여자 행동 규칙을 확인합니다. `GEMINI.md`를 읽어 토론 참여자 행동 규칙을 확인합니다.
### 2. 사회자 지시 읽기 ### 2. 활성 토론 탐색
```powershell
python tools/wiki_debate.py list-debates
```
- 활성 토론이 **없으면** → "진행 중인 토론이 없습니다. 사회자의 시그널을 기다리세요."라고 응답하고 **여기서 종료합니다. 이후 단계를 실행하지 마세요.**
- 활성 토론이 **있으면** → 해당 slug를 사용하여 아래 단계를 진행합니다.
- ⚠️ slug를 추측하거나 임의로 만들지 마세요. 반드시 위 명령의 출력에서 가져오세요.
### 3. 사회자 지시 읽기
```powershell ```powershell
python tools/wiki_debate.py read debates/{slug}/input-opus python tools/wiki_debate.py read debates/{slug}/input-opus
``` ```
> `{slug}`는 현재 진행 중인 토론 slug입니다. `input-opus` 페이지가 비어있으면 아직 사회자가 input을 작성하지 않은 것이니 대기하세요. > `{slug}`는 2단계에서 발견한 실제 토론 slug로 치환합니다.
> 페이지가 비어있거나 `[오류]`가 반환되면 → "사회자의 지시를 기다리는 중입니다"라고 응답하고 **종료합니다.**
### 3. 합의 상태 확인 ### 4. 합의 상태 확인
```powershell ```powershell
python tools/wiki_debate.py read debates/{slug}/working-document python tools/wiki_debate.py read debates/{slug}/working-document
@@ -28,7 +39,7 @@ python tools/wiki_debate.py read debates/{slug}/working-document
사회자가 정리한 현재 합의 상태를 확인합니다. 사회자가 정리한 현재 합의 상태를 확인합니다.
### 4. 답변 작성 및 업로드 ### 5. 답변 작성 및 업로드
답변을 작성한 후 response 페이지에 업로드: 답변을 작성한 후 response 페이지에 업로드:
@@ -42,6 +53,6 @@ python tools/wiki_debate.py write debates/{slug}/response-opus "답변 전문"
python tools/wiki_debate.py write-file debates/{slug}/response-opus response_draft.md python tools/wiki_debate.py write-file debates/{slug}/response-opus response_draft.md
``` ```
### 5. Discord 완료 시그널 ### 6. Discord 완료 시그널
Discord에 "response 작성 완료"를 게시하여 사회자에게 알립니다. Discord에 "response 작성 완료"를 게시하여 사회자에게 알립니다.

View File

@@ -30,13 +30,22 @@
6. **근거 명시** — 기술적 근거, 사례, 레퍼런스를 포함하세요 6. **근거 명시** — 기술적 근거, 사례, 레퍼런스를 포함하세요
7. **합의 가능 시 인정** — 상대 의견이 맞으면 솔직히 인정하고 발전시키세요 7. **합의 가능 시 인정** — 상대 의견이 맞으면 솔직히 인정하고 발전시키세요
## 대기 상태 (Idle Mode)
- 진행 중인 토론이 없으면 → **아무것도 하지 마세요.** "진행 중인 토론이 없습니다"라고 답변만 하세요.
- `input-opus` 페이지가 비어있거나 존재하지 않으면 → **"사회자의 지시를 기다리는 중입니다"라고 답하세요.**
- 사회자가 Discord 시그널을 보내면 그때 `/start`를 다시 실행하여 시작합니다.
- **절대로** 토론 문서가 없다고 해서 자체적으로 해결을 시도하지 마세요.
## 금지 사항 ## 금지 사항
- ❌ 상대방의 `input-gemini` 또는 `response-gemini` 페이지 접근 - ❌ 상대방의 `input-gemini` 또는 `response-gemini` 페이지 접근
-`working-document` 수정 (읽기만 — 사회자만 편집) -`working-document` 수정 (읽기만 — 사회자만 편집)
- ❌ 소스 코드 작성 (문서 수준 논의만) - ❌ 소스 코드 작성/수정 (문서 수준 논의만)
- ❌ 사회자 지시 무시 - ❌ 사회자 지시 무시
- ❌ 주제에서 벗어난 발언 - ❌ 주제에서 벗어난 발언
- ❌ 근거 없는 주장 - ❌ 근거 없는 주장
- ❌ "이하생략" 또는 답변 축약 - ❌ "이하생략" 또는 답변 축약
- ❌ Discord에 긴 답변 직접 게시 (반드시 Wiki.js response 페이지에) - ❌ Discord에 긴 답변 직접 게시 (반드시 Wiki.js response 페이지에)
- ❌ 토론 문서가 없을 때 자체적으로 해결 시도 (소스 수정, 파일 생성, 설정 변경 등)
- ❌ slug를 추측하거나 임의로 생성

View File

@@ -66,6 +66,28 @@ async def _query(query: str, variables: dict = None) -> dict:
return data.get("data", {}) return data.get("data", {})
async def list_debates() -> list[str]:
"""Wiki.js에서 debates/ 하위의 활성 토론 slug 목록을 조회."""
query = """
query {
pages { list(orderBy: UPDATED, orderByDirection: DESC) {
id, path, updatedAt
}}
}
"""
data = await _query(query)
pages = data.get("pages", {}).get("list", [])
slugs = set()
for p in pages:
path = p.get("path", "")
# debates/{slug}/input-* 형태의 페이지에서 slug 추출
if path.startswith("debates/") and "/input-" in path:
parts = path.split("/")
if len(parts) >= 3:
slugs.add(parts[1])
return sorted(slugs)
async def read_page(path: str) -> str: async def read_page(path: str) -> str:
"""경로로 페이지 읽기 (singleByPath).""" """경로로 페이지 읽기 (singleByPath)."""
query = """ query = """
@@ -143,6 +165,18 @@ async def main():
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace") sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
args = sys.argv[1:] args = sys.argv[1:]
# list-debates 커맨드는 인수 불필요
if args and args[0] == "list-debates":
slugs = await list_debates()
if slugs:
print("활성 토론 목록:")
for s in slugs:
print(f" - {s}")
else:
print("진행 중인 토론이 없습니다.")
return
if len(args) < 2: if len(args) < 2:
print(__doc__) print(__doc__)
return return

View File

@@ -1,3 +1,4 @@
| # | 시간 | 작업 | 커밋 | 상태 | | # | 시간 | 작업 | 커밋 | 상태 |
|----|------|------|------|------| |----|------|------|------|------|
| 001 | 09:05 | debate-agent .env 생성 (gemini/opus Wiki.js API 키) | - | ✅ | | 001 | 09:05 | debate-agent .env 생성 (gemini/opus Wiki.js API 키) | - | ✅ |
| 002 | 19:15 | debate-agent 대기 상태 처리 — idle-mode 규칙 + slug 탐색 + 금지 사항 강화 (7파일) | - | ✅ |

View File

@@ -323,8 +323,11 @@ class DebateHandler:
if not ch: if not ch:
return return
rd = self.session.round rd = self.session.round
slug = self.session.topic_slug
await ch.send( await ch.send(
f"📥 **Round {rd}** — `input.md`를 읽고 `response.md`에 답변을 작성하세요." f"📥 **Round {rd}** — slug: `{slug}`\n"
f"Wiki.js에서 `debates/{slug}/input-{speaker}`를 읽고 "
f"`debates/{slug}/response-{speaker}`에 답변을 작성하세요."
) )
async def _wait_for_response(self, speaker: str) -> str: async def _wait_for_response(self, speaker: str) -> str: