# Codex 봇 개선 2건 (1팀/헤르메스)

**위임 대상**: 개발1팀 (헤르메스)
**작업 레벨**: Lv.1~2 (파일 특정 + 로직 파악 필요)
**대상 서비스**: `/home/jay/workspace/services/multimodel-bot/`

---

## 항목 1: Codex 에러 메시지에 시스템 프롬프트 노출 방지

### 문제 상황
Codex CLI가 에러를 반환할 때, `stderr_text[-300:]`을 그대로 사용자에게 보여주고 있음.
이로 인해 **시스템 프롬프트(페르소나 지시문)**가 채팅방에 노출되는 보안 문제 발생.

### 대상 파일
- `/home/jay/workspace/services/multimodel-bot/engine.py`

### 현재 코드 (engine.py, call_codex 함수, 약 172~176행)
```python
if proc.returncode != 0:
    stderr_lower = stderr_text.lower()
    if "login" in stderr_lower or "auth" in stderr_lower:
        return f"🔑 Codex 로그인이 필요합니다. `codex login`을 실행해주세요.\n(상세: {stderr_text[-300:]})"
    return f"❌ Codex CLI 에러 (exit {proc.returncode}): {stderr_text[-300:]}"
```

### 수정 요구사항

1. **사용량 한도 초과 감지**: stderr에 `"usage limit"` 또는 `"hit your"` 가 포함되어 있으면:
   - 반환: `"⚠️ Codex 사용량 한도 초과. 잠시 후 다시 시도해주세요."`
   - stderr 내용을 사용자에게 절대 노출하지 않음

2. **기타 에러**: stderr 전체를 보여주지 말고, `"ERROR:"` 또는 `"error"` (대소문자 무시)가 포함된 줄만 필터링해서 보여줌
   - `stderr_text`를 줄 단위로 분리
   - 각 줄에서 `re.search(r"error", line, re.IGNORECASE)` 매칭되는 줄만 수집
   - 필터링된 줄이 없으면 `"❌ Codex CLI 에러 (exit {returncode}): 상세 내용 없음"` 반환
   - 필터링된 줄이 있으면 합쳐서 반환 (300자 제한 유지)

3. **call_claude() 함수 점검**: 현재 call_claude는 stderr를 사용자에게 반환하지 않지만(stdout만 반환), proc.returncode != 0인 경우에 대한 처리가 아예 없음. 에러 시에도 빈 stdout이 반환될 수 있으므로:
   - `proc.returncode != 0`이고 `output`이 비어있을 때: stderr에서 에러 줄만 필터링하여 반환 (위 Codex와 동일 로직)
   - 역시 stderr 전문 노출 금지

### 검증 방법
- 의도적으로 잘못된 프롬프트를 보내거나, 사용량 한도 초과 시뮬레이션
- 반환 메시지에 시스템 프롬프트 내용이 포함되지 않는지 확인

---

## 항목 2: 집단지성 토론 질서 문제 (동시 응답 방지)

### 문제 상황
멀티모델 토론 봇은 라운드 로빈(순서대로 발언)으로 설계되어 있음.
이전에는 각 봇이 이전 봇의 발언을 읽고 사려 깊은 응답을 했으나, 현재 메시지가 혼란스럽게/동시에 들어오는 현상 발생.

### 조사 대상 파일
- `/home/jay/workspace/services/multimodel-bot/main_bot.py` — `trigger_next_bot_response()` (115~183행)
- `/home/jay/workspace/services/multimodel-bot/discussion_manager.py` — 턴 관리 전체
- `/home/jay/workspace/services/multimodel-bot/codex_bot.py`
- `/home/jay/workspace/services/multimodel-bot/claude_bot.py`
- `/home/jay/workspace/services/multimodel-bot/gemini_bot.py`

### 현재 구조 분석
`trigger_next_bot_response()` (main_bot.py 155~170행)에서:
```python
next_next = dm.on_bot_response(next_bot_username)
if next_next:
    asyncio.create_task(
        trigger_next_bot_response(
            next_next, chat_id, result, next_bot_username,
        )
    )
```

`asyncio.create_task()`는 **비동기로 즉시 반환**됨. 현재 봇의 Telegram 메시지 전송(`replace_thinking_message`)이 완전히 완료된 후 다음 봇이 트리거되는 것처럼 보이지만, 다음 상황에서 레이스 컨디션 가능:

1. 유저가 토론 중 새 메시지를 보내면 `on_user_message()`가 호출되어 턴이 리셋될 수 있음
2. 각 봇의 `handle_message()`에서도 독립적으로 `trigger_next_bot_response`를 호출할 가능성
3. `remaining_turns` 제거(최근 변경)로 턴 제한이 없어진 점도 확인 필요

### 점검 항목

1. **순차 보장 확인**: 다음 봇은 현재 봇의 메시지가 Telegram에 **완전히 전송된 후**에만 시작해야 함.
   - `replace_thinking_message` → `memory.add_message` → `on_bot_response` → 다음 `trigger_next_bot_response` 순서가 `await` 체인으로 보장되는가?

2. **레이스 컨디션 점검**: `trigger_next_bot_response`가 동시에 여러 번 호출될 수 있는 경로가 있는지 확인
   - 유저 메시지 핸들러(`_handle_all_messages`)와 봇 턴 트리거가 동시에 동작할 때
   - 각 봇별 `handle_message` 내부에서 독립적으로 트리거하는 경로가 있는지

3. **대화 맥락 전달 확인**: `memory.format_context()`가 이전 봇의 발언을 포함한 최신 맥락을 제공하는지
   - `add_message` → `format_context` 사이에 타이밍 이슈 없는지

4. **`remaining_turns` 제거 영향**: 최근 커밋에서 `remaining_turns` 관련 로직이 제거되었다면, 이것이 턴 관리에 어떤 영향을 미쳤는지 git log/diff로 확인

### 수정 방향

- **락(Lock) 도입**: `trigger_next_bot_response`에 `asyncio.Lock`을 사용하여 동시 실행 방지
  ```python
  _turn_lock = asyncio.Lock()

  async def trigger_next_bot_response(...):
      async with _turn_lock:
          # 기존 로직
  ```
- **유저 메시지 중 턴 리셋 방지**: 토론 진행 중 유저가 메시지를 보내도, 현재 진행 중인 봇 응답이 완료될 때까지 턴을 변경하지 않도록 가드 추가
- **create_task 대신 await 고려**: 다음 턴 트리거를 `create_task` 대신 `await`로 변경하면 순차 보장이 더 확실하지만, Telegram 응답 지연이 누적될 수 있으므로 트레이드오프 분석 필요
- 수정 후 3봇이 깔끔하게 순서대로 응답하는지 실제 토론 테스트로 확인

### 검증 방법
- 3봇 토론을 시작하고, 각 봇의 응답 순서가 정확히 라운드 로빈인지 확인
- 토론 중 유저가 끼어들었을 때 순서가 깨지지 않는지 확인
- 로그에서 `trigger_next_bot_response`가 동시 다발적으로 호출되는 패턴이 없는지 확인