Discord 비동기 인바운드 워커 계획
목표
Discord 리스너 타임아웃을 사용자 대면 실패 모드에서 제거하기 위해 인바운드 Discord 턴을 비동기로 만듭니다:
- 게이트웨이 리스너가 인바운드 이벤트를 빠르게 수용하고 정규화.
- Discord 실행 큐가 현재 사용하는 것과 동일한 순서 경계로 키가 지정된 직렬화된 작업을 저장.
- 워커가 Carbon 리스너 수명 외부에서 실제 에이전트 턴을 실행.
- 실행 완료 후 원본 채널이나 스레드로 응답 전달.
이것은 에이전트 실행 자체가 진행 중인데도 channels.discord.eventQueue.listenerTimeout에서 큐에 대기 중인 Discord 실행이 타임아웃되는 문제의 장기 수정입니다.
현재 상태
이 계획은 부분적으로 구현되었습니다.
완료된 사항:
- Discord 리스너 타임아웃과 Discord 실행 타임아웃이 별도 설정으로 분리.
- 수용된 인바운드 Discord 턴이
src/discord/monitor/inbound-worker.ts에 큐잉. - 워커가 Carbon 리스너 대신 장시간 실행 턴을 소유.
- 큐 키에 의한 기존 경로별 순서 유지.
- Discord 워커 경로에 대한 타임아웃 회귀 커버리지 존재.
쉬운 말로:
- 프로덕션 타임아웃 버그가 수정됨
- 장시간 실행 턴이 더 이상 Discord 리스너 예산 만료만으로 죽지 않음
- 워커 아키텍처는 아직 완성되지 않음
여전히 부족한 사항:
DiscordInboundJob이 아직 부분적으로만 정규화되었고 실시간 런타임 참조를 포함- 명령 의미론 (
stop,new,reset, 미래 세션 제어)이 아직 워커 네이티브로 완전히 전환되지 않음 - 워커 관측성과 운영자 상태가 아직 최소
- 재시작 내구성이 아직 없음
이 문서가 필요한 이유
현재 동작은 전체 에이전트 턴을 리스너 수명에 연결:
src/discord/monitor/listeners.ts가 타임아웃과 중단 경계를 적용.src/discord/monitor/message-handler.ts가 큐에 대기 중인 실행을 해당 경계 내에 유지.src/discord/monitor/message-handler.process.ts가 미디어 로딩, 라우팅, 디스패치, 타이핑, 드래프트 스트리밍, 최종 응답 전달을 인라인으로 수행.
이 아키텍처에는 두 가지 나쁜 속성이 있습니다:
- 정상적이지만 긴 턴이 리스너 감시독에 의해 중단될 수 있음
- 다운스트림 런타임이 응답을 생성했음에도 사용자에게 응답이 표시되지 않을 수 있음
타임아웃을 높이면 도움이 되지만 실패 모드를 바꾸지 않습니다.
비목표
- 이번 패스에서 비-Discord 채널을 재설계하지 않음.
- 첫 구현에서 범용 전채널 워커 프레임워크로 확장하지 않음.
- 공유 크로스 채널 인바운드 워커 추상화를 아직 추출하지 않음; 중복이 명백할 때만 저수준 프리미티브를 공유.
- 안전하게 출시하기 위해 필요하지 않는 한 첫 패스에서 내구성 있는 충돌 복구를 추가하지 않음.
- 이 계획에서 라우트 선택, 바인딩 의미론, ACP 정책을 변경하지 않음.
목표 아키텍처
1. 리스너 단계
DiscordMessageListener가 진입점으로 유지되지만, 역할이 다음으로 변경:
- 사전 검사 및 정책 확인 실행
- 수용된 입력을 직렬화 가능한
DiscordInboundJob으로 정규화 - 세션별 또는 채널별 비동기 큐에 작업 큐잉
- 큐잉 성공 후 즉시 Carbon에 반환
리스너는 더 이상 종단 간 LLM 턴 수명을 소유하지 않아야 합니다.
2. 정규화된 작업 페이로드
나중에 턴을 실행하는 데 필요한 데이터만 포함하는 직렬화 가능한 작업 디스크립터를 도입합니다.
최소 형태:
- 라우트 식별
agentIdsessionKeyaccountIdchannel
- 전달 식별
- 대상 채널 ID
- 응답 대상 메시지 ID
- 스레드 ID (있을 경우)
- 발신자 식별
- 발신자 ID, 라벨, 사용자명, 태그
- 채널 컨텍스트
- 길드 ID
- 채널 이름 또는 슬러그
- 스레드 메타데이터
- 해석된 시스템 프롬프트 오버라이드
- 정규화된 메시지 본문
- 기본 텍스트
- 유효 메시지 텍스트
- 첨부 파일 디스크립터 또는 해석된 미디어 참조
- 게이팅 결정
- 멘션 요구 결과
- 명령 인증 결과
- 바인딩된 세션 또는 에이전트 메타데이터 (해당 시)
작업 페이로드는 실시간 Carbon 객체나 변경 가능한 클로저를 포함하지 않아야 합니다.
3. 워커 단계
다음을 담당하는 Discord 전용 워커 러너 추가:
DiscordInboundJob에서 턴 컨텍스트 재구성- 실행에 필요한 미디어 및 추가 채널 메타데이터 로딩
- 에이전트 턴 디스패치
- 최종 응답 페이로드 전달
- 상태 및 진단 업데이트
4. 순서 모델
주어진 라우트 경계에 대해 오늘과 동일한 순서를 유지해야 합니다.
권장 키:
resolveDiscordRunQueueKey(...)와 동일한 큐 키 로직 사용
5. 타임아웃 모델
전환 후 두 가지 별도 타임아웃 클래스가 있습니다:
- 리스너 타임아웃
- 정규화와 큐잉만 커버
- 짧아야 함
- 실행 타임아웃
- 선택적, 워커 소유, 명시적, 사용자에게 표시
- Carbon 리스너 설정에서 우연히 상속되지 않아야 함
테스트 계획
기존 타임아웃 재현 커버리지 유지:
src/discord/monitor/message-handler.queue.test.ts
새 테스트 추가:
- 전체 턴을 기다리지 않고 큐잉 후 리스너 반환
- 경로별 순서 유지
- 다른 채널은 동시 실행 가능
- 응답이 원본 메시지 대상으로 전달
stop이 워커 소유 활성 실행을 취소- 워커 실패가 이후 작업을 차단하지 않고 가시적 진단 생성
- ACP 바인딩된 Discord 채널이 워커 실행에서 올바르게 라우팅
위험 및 완화
- 위험: 현재 동기 동작에서 명령 의미론 드리프트 완화: 동일 전환에서 명령 상태 배관을 함께 출시
- 위험: 응답 전달이 스레드 또는 reply-to 컨텍스트를 잃음
완화:
DiscordInboundJob에서 전달 식별을 일급으로 - 위험: 재시도 또는 큐 재시작 중 중복 전송 완화: 첫 패스는 인메모리만, 또는 지속성 전에 명시적 전달 멱등성 추가
- 위험: 마이그레이션 중
message-handler.process.ts가 추론하기 어려워짐 완화: 워커 전환 전이나 도중에 정규화, 실행, 전달 헬퍼로 분리
수락 기준
계획은 다음이 완료될 때 완료:
- Discord 리스너 타임아웃이 정상적인 장시간 실행 턴을 더 이상 중단하지 않음.
- 리스너 수명과 에이전트 턴 수명이 코드에서 별도 개념.
- 기존 세션별 순서 유지.
- ACP 바인딩된 Discord 채널이 동일한 워커 경로를 통해 동작.
stop이 이전 리스너 소유 호출 스택 대신 워커 소유 실행을 대상으로.- 타임아웃과 전달 실패가 조용한 리스너 드롭이 아닌 명시적 워커 결과가 됨.