본문으로 건너뛰기

SOL(Sleep Onset Latency) Chain of Debate (CoD) 시스템 설계 문서

📋 프로젝트 개요

목표: Microsoft AI MAI-DxO 논문에서 영감을 받아 4개의 전문가 에이전트가 Chain of Debate를 통해 사용자의 SOL(Sleep Onset Latency, 0-120분)을 예측하는 시스템을 DTA Wide 플랫폼에 구축

기술 스택: TypeScript + LangGraph.js + NestJS + CQRS + Event-Driven Architecture

🎯 MAI-DxO 핵심 개념 분석

Sequential Diagnosis Benchmark (SDBench)

  • 304개의 NEJM CPC 케이스를 순차적 진단 과제로 변환
  • Gatekeeper 모델이 정보를 제어하여 실제 임상 환경 시뮬레이션
  • 진단 정확도와 비용을 동시에 평가하는 Pareto 프론티어 접근법
  • o3 모델 기준: 기본 78.6% → MAI-DxO 79.9% 정확도, 비용 70% 절감

MAI Diagnostic Orchestrator 아키텍처

  • 가상 의료진 패널: 5개의 전문화된 역할을 가진 AI 에이전트
  • Chain of Debate: 구조화된 토론을 통한 합의 도출
  • 모델 불가지론적 설계: 다양한 LLM에서 일관된 성능 향상 (+11% 평균)

MAI-DxO 5개 전문가 에이전트

  1. Dr. Hypothesis: 베이지안 방식으로 상위 3개 진단 가설 관리
  2. Dr. Test-Chooser: 진단 가설을 최대로 구별하는 3개 검사 선택
  3. Dr. Challenger: 편향 식별 및 반박 증거 제시
  4. Dr. Stewardship: 비용 효율적 대안 옹호
  5. Dr. Checklist: 품질 관리 및 일관성 유지

👥 SOL CoD - 4개 전문가 에이전트 설계

1. 수면잠복기 분석 전문가 (Sleep Latency Analyst)

역할: 수면 데이터의 시계열 패턴과 트렌드를 분석하여 SOL 예측

핵심 책임:

  • 과거 SOL 패턴 분석 (7일, 14일, 30일 추세)
  • 침상 시간(lot)과 실제 수면 시작(ast) 간격 분석
  • 주중/주말 SOL 차이 분석
  • 수면 부채와 SOL 상관관계 분석
  • 개인별 SOL 기준선(baseline) 설정
  • 계절적/환경적 요인과 SOL 연관성 분석

전문성: 수면의학, 수면잠복기 병리학, 시계열 분석

2. 심리상태 분석 전문가 (Psychological State Analyst)

역할: SOL에 영향을 미치는 심리적 요인 분석

핵심 책임:

  • ISI: 불면증 중증도와 SOL 연관성 분석
  • PHQ-9: 우울 증상이 수면잠복기에 미치는 영향
  • GAD-7: 불안 수준과 SOL 상관관계
  • PSS: 스트레스와 수면잠복기 연관성
  • DBAS: 수면에 대한 역기능적 인지가 SOL에 미치는 영향
  • 과각성(hyperarousal) 상태 평가
  • 수면 불안과 SOL 연관성 분석

전문성: 임상심리학, 수면 심리학, 불안-각성 이론

3. CBT-I 수면행동 전문가 (CBT-I Sleep Behavior Specialist)

역할: SOL 개선을 위한 행동 개입 전문가

핵심 책임:

  • 침실 환경이 SOL에 미치는 영향 분석
  • 잠자리에서의 활동 패턴 평가 (스크린 타임, 독서 등)
  • 수면 위생 요소와 SOL 연관성
  • 자극 조절 치료 관점에서 SOL 분석
  • 이완 기법 효과성 평가
  • 수면 제한 치료와 SOL 개선 가능성
  • 카페인, 알코올 등 물질이 SOL에 미치는 영향

전문성: CBT-I, 수면 위생, 자극 조절 치료, 이완 요법

4. 디지털 수면환경 전문가 (Digital Sleep Environment Analyst)

역할: 디지털 환경이 SOL에 미치는 영향 분석

핵심 책임:

  • 잠자리 전 스크린 타임과 SOL 상관관계
  • 블루라이트 노출 시간과 수면잠복기 영향
  • 수면 추적 앱 사용이 SOL에 미치는 역설적 효과 분석
  • 소셜미디어, 뉴스 앱 사용과 각성 상태 연관성
  • 알림 설정과 수면잠복기 방해 요인 분석
  • 디지털 웰빙 지표와 SOL 개선 연관성

전문성: 디지털 수면 건강, 일주기 리듬과 기술 상호작용

🔄 Chain of Debate 워크플로우

1단계: 개별 SOL 분석 (Individual SOL Analysis)

  • 각 전문가가 자신의 전문 영역에서 SOL 예측
  • 예측 범위: 0-120분
  • 신뢰도 및 불확실성 평가
  • 주요 영향 요인 식별

2단계: 교차 요인 분석 토론 (Cross-Factor Analysis Debate)

  • 생리적 요인 vs 심리적 요인 대조
  • 행동적 요인 vs 환경적 요인 연관성 검토
  • 급성 요인 vs 만성 요인 구분 분석

3단계: SOL 예측 검증 토론 (SOL Prediction Validation)

  • 각 전문가의 예측값에 대한 비판적 검토
  • 상충하는 요인들의 우선순위 결정
  • 개인별 특성을 고려한 예측값 조정

4단계: 최종 SOL 예측 및 개선 방안 도출

  • 가중평균을 통한 최종 SOL 예측값 (분 단위)
  • 신뢰구간 및 예측 정확도 제시
  • 개인 맞춤형 SOL 개선 권장사항
  • 후속 모니터링 계획

🏗️ DTA Wide 플랫폼 통합 아키텍처

시스템 요구사항

핵심 입력 데이터:

  • 수면 기록: lot(lights out time), ast(asleep start time) 포함 DTA Wide sleep domain 데이터
  • 설문 응답: questionnaire domain의 ISI, PHQ-9, DBAS, GAD-7, PSS
  • 사용자 행동: user domain의 앱 사용 패턴 및 디지털 행동 데이터
  • TimeMachine 데이터: 시간 기반 분석을 위한 가상 시간 정보

출력:

  • SOL 예측값 (0-120분)
  • 각 전문가의 SOL 분석 근거
  • Chain of Debate 토론 로그
  • SOL 개선 권장사항
  • 예측 신뢰도 지표 및 불확실성 범위

Feature Library 구조

libs/feature/sleep-onset-latency-cod/
├── src/lib/
│ ├── application/
│ │ ├── commands/handlers/
│ │ │ └── predict-sol.handler.ts
│ │ └── queries/handlers/
│ │ └── get-sol-analysis.handler.ts
│ ├── domain/
│ │ ├── entities/
│ │ │ ├── sol-prediction.entity.ts
│ │ │ ├── expert-sol-analysis.entity.ts
│ │ │ └── sol-chain-of-debate.entity.ts
│ │ ├── services/
│ │ │ ├── sleep-latency-analyst.service.ts
│ │ │ ├── psychological-analyst.service.ts
│ │ │ ├── behavior-specialist.service.ts
│ │ │ ├── digital-environment-analyst.service.ts
│ │ │ └── sol-consensus-builder.service.ts
│ │ ├── repositories/
│ │ │ └── sol-cod.repository.interface.ts
│ │ └── exceptions/
│ └── infrastructure/
├── langgraph/
│ ├── sol-cod-workflow.ts
│ ├── expert-agents/
│ │ ├── base-expert-agent.ts
│ │ ├── sleep-latency-analyst.agent.ts
│ │ ├── psychological-analyst.agent.ts
│ │ ├── behavior-specialist.agent.ts
│ │ └── digital-environment-analyst.agent.ts
│ └── prompts/
│ ├── sol-latency-prompts.ts
│ ├── psychological-prompts.ts
│ ├── behavior-prompts.ts
│ └── digital-environment-prompts.ts
└── repositories/
└── prisma-sol-cod.repository.ts

CQRS 패턴 구현

Command Handler:

@CommandHandler(PredictSOLCommand)
export class PredictSOLHandler {
constructor(
private readonly unitOfWork: UnitOfWork,
private readonly solCoDWorkflow: SOLCoDWorkflow,
private readonly timeMachineService: TimeMachineService,
private readonly eventBus: EventBus
) {}

async execute(command: PredictSOLCommand): Promise<SOLPredictionResultDto> {
return await this.unitOfWork.execute(async (repositories) => {
const currentTime = await this.timeMachineService.getCurrentTime(command.userId);

// Execute SOL Chain of Debate workflow
const prediction = await this.solCoDWorkflow.executeChainOfDebate({
userId: command.userId,
sleepData: command.sleepData,
questionnaireResponses: command.questionnaireResponses,
mobileUsageData: command.mobileUsageData,
analysisTime: currentTime
});

// Save SOL prediction
const savedPrediction = await repositories.solCoDRepository.save(prediction);

// Publish domain event
this.eventBus.publish(new SOLPredictionCompletedEvent(
savedPrediction.id,
savedPrediction.userId,
savedPrediction.solValue,
savedPrediction.confidenceLevel,
currentTime
));

return new SOLPredictionResultDto(savedPrediction);
});
}
}

Query Handler:

@QueryHandler(GetSOLAnalysisQuery)
export class GetSOLAnalysisHandler {
constructor(
private readonly solCoDRepository: SOLCoDRepository,
private readonly timeMachineService: TimeMachineService
) {}

async execute(query: GetSOLAnalysisQuery): Promise<SOLAnalysisDto[]> {
const userCurrentTime = await this.timeMachineService.getCurrentTime(query.userId);

const analyses = await this.solCoDRepository.findAnalysesByUserAndDateRange(
query.userId,
query.dateRange || { start: subDays(userCurrentTime, 30), end: userCurrentTime }
);

return analyses.map(analysis => new SOLAnalysisDto(
analysis.id,
analysis.solValue,
analysis.expertAnalyses,
analysis.confidenceLevel,
analysis.recommendations,
analysis.createdAt.toISOString()
));
}
}

Event-Driven Architecture 통합

Domain Events:

export class SOLPredictionCompletedEvent extends DomainEvent {
constructor(
public readonly predictionId: string,
public readonly userId: string,
public readonly solValue: number,
public readonly confidenceLevel: number,
public readonly analysisTime: Date
) {
super('sol.prediction.completed', predictionId);
}
}

export class SOLExpertAnalysisGeneratedEvent extends DomainEvent {
constructor(
public readonly predictionId: string,
public readonly expertType: string,
public readonly analysis: SOLExpertAnalysis,
public readonly userId: string
) {
super('sol.expert.analysis.generated', predictionId);
}
}

export class SOLTrendDetectedEvent extends DomainEvent {
constructor(
public readonly userId: string,
public readonly trendType: 'improving' | 'declining' | 'stable',
public readonly trendStrength: number,
public readonly timeRange: DateRange
) {
super('sol.trend.detected', userId);
}
}

Cross-Domain Event Flow:

  1. Sleep DomainSleepDataRecordedEvent → SOL CoD 실행 트리거
  2. Questionnaire DomainQuestionnaireCompletedEvent → 심리 상태 업데이트
  3. SOL CoDSOLPredictionCompletedEvent → 결과 저장 및 알림
  4. User Domain → SOL 개선 권장사항 제공
  5. Notification Domain → 사용자 알림 발송

💻 TypeScript/LangGraph.js 구현 상세

기술 스택 및 의존성

핵심 기술:

  • LangGraph.js: Chain of Debate 워크플로우 구현
  • TypeScript: 타입 안전성 및 코드 품질 보장
  • NestJS: DI Container 및 CQRS 패턴 지원
  • Prisma: 데이터베이스 ORM
  • Redis: 캐싱 및 세션 관리

AI/ML 통합:

  • OpenAI GPT-4: 주요 언어 모델
  • Claude/Gemini: 백업 및 앙상블 모델
  • LangChain: 프롬프트 관리 및 체이닝

LangGraph.js 워크플로우 구현

// sol-cod-workflow.ts
export class SOLCoDWorkflow {
private sleepLatencyAnalyst: SleepLatencyAnalystAgent;
private psychologicalAnalyst: PsychologicalAnalystAgent;
private behaviorSpecialist: BehaviorSpecialistAgent;
private digitalEnvironmentAnalyst: DigitalEnvironmentAnalystAgent;
private solConsensusBuilder: SOLConsensusBuilder;

constructor(
@Inject('ChatOpenAI') private readonly llm: ChatOpenAI,
@Inject('TimeMachineService') private readonly timeMachine: TimeMachineService
) {
this.initializeAgents();
}

async executeChainOfDebate(input: SOLAnalysisInput): Promise<SOLPrediction> {
try {
// Phase 1: Individual Analysis
const individualAnalyses = await this.conductIndividualAnalysis(input);

// Phase 2: Cross-Expert Review
const debateResults = await this.conductCrossExpertDebate(individualAnalyses);

// Phase 3: Consensus Building
const consensus = await this.buildConsensus(debateResults);

// Phase 4: Final Validation
const finalPrediction = await this.validateAndFinalize(consensus, input);

return finalPrediction;
} catch (error) {
throw new SOLCoDException('SOL Chain of Debate execution failed', error);
}
}

private async conductIndividualAnalysis(input: SOLAnalysisInput): Promise<SOLExpertAnalysis[]> {
const analyses = await Promise.allSettled([
this.sleepLatencyAnalyst.analyze(input.sleepData, input.analysisTime),
this.psychologicalAnalyst.analyze(input.questionnaireResponses, input.analysisTime),
this.behaviorSpecialist.analyze(input.behaviorData, input.analysisTime),
this.digitalEnvironmentAnalyst.analyze(input.mobileUsageData, input.analysisTime)
]);

return analyses
.filter(result => result.status === 'fulfilled')
.map(result => (result as PromiseFulfilledResult<SOLExpertAnalysis>).value);
}

private async conductCrossExpertDebate(analyses: ExpertAnalysis[]): Promise<DebateResult[]> {
const debates: DebateResult[] = [];

// Each expert challenges others' analyses
for (const expert of [this.sleepLatencyAnalyst, this.psychologicalAnalyst, this.behaviorSpecialist, this.digitalEnvironmentAnalyst]) {
const otherAnalyses = analyses.filter(a => a.expertType !== expert.getExpertType());
const debate = await expert.challengeOtherAnalyses(otherAnalyses);
debates.push(debate);
}

return debates;
}

private async buildConsensus(debates: DebateResult[]): Promise<ConsensusResult> {
return await this.solConsensusBuilder.buildConsensus(debates);
}

private async validateAndFinalize(consensus: ConsensusResult, input: SOLAnalysisInput): Promise<SOLPrediction> {
// Additional validation logic
const confidenceLevel = this.calculateConfidenceLevel(consensus);
const recommendations = await this.generateRecommendations(consensus, input);

return new SOLPrediction(
generateId(),
input.userId,
consensus.finalSOLValue,
consensus.expertAnalyses,
confidenceLevel,
recommendations,
consensus.reasoning,
input.analysisTime
);
}
}

전문가 에이전트 구현 구조

Base Expert Agent:

export abstract class BaseExpertAgent {
protected llm: ChatOpenAI;
protected promptManager: PromptManager;
protected tools: Tool[];

constructor(
llm: ChatOpenAI,
promptManager: PromptManager
) {
this.llm = llm;
this.promptManager = promptManager;
this.tools = this.initializeTools();
}

abstract getExpertType(): string;
abstract analyze(data: any, analysisTime: Date): Promise<ExpertAnalysis>;
abstract challengeOtherAnalyses(analyses: ExpertAnalysis[]): Promise<DebateResult>;
abstract buildConsensusView(debates: DebateResult[]): Promise<ConsensusView>;

protected abstract initializeTools(): Tool[];

protected async executeAnalysis(prompt: string, context: any): Promise<string> {
try {
const response = await this.llm.invoke([
{ role: 'system', content: this.getSystemPrompt() },
{ role: 'user', content: prompt }
]);
return response.content as string;
} catch (error) {
throw new ExpertAnalysisException(`${this.getExpertType()} analysis failed`, error);
}
}

protected abstract getSystemPrompt(): string;
}

구체적 전문가 구현:

export class SleepLatencyAnalystAgent extends BaseExpertAgent {
getExpertType(): string {
return 'sleep_latency_analyst';
}

async analyze(sleepData: SleepDataDto, analysisTime: Date): Promise<SOLLatencyAnalysis> {
const prompt = await this.promptManager.buildSOLLatencyAnalysisPrompt({
sleepData,
analysisTime,
historicalData: await this.getHistoricalSleepData(sleepData.userId)
});

const response = await this.executeAnalysis(prompt, sleepData);
return this.parseSOLLatencyResponse(response);
}

async challengeOtherAnalyses(analyses: SOLExpertAnalysis[]): Promise<DebateResult> {
const challengePrompt = await this.promptManager.buildChallengePrompt({
myExpertise: this.getExpertType(),
otherAnalyses: analyses,
challengeFocus: 'physiological_sol_vs_psychological_factors'
});

const response = await this.executeAnalysis(challengePrompt, analyses);
return this.parseChallengeResponse(response);
}

protected getSystemPrompt(): string {
return `당신은 수면의학 전문의이자 수면 패턴 분석가입니다.

전문 영역:
- 수면 구조 및 아키텍처 분석
- 일주기 리듬 평가
- 수면 효율성 및 질 지표 해석
- 수면 장애 패턴 식별

분석 접근법:
1. 객관적 수면 데이터를 우선시
2. 시계열 트렌드 분석 수행
3. 개인별 수면 기준선 고려
4. 생리학적 수면 요구사항 평가

항상 근거 기반 분석을 제공하고, 불확실성이 있는 경우 명시하세요.`;
}

protected initializeTools(): Tool[] {
return [
new SleepDataAnalysisTool(),
new CircadianRhythmTool(),
new SleepEfficiencyCalculatorTool(),
new HistoricalTrendAnalysisTool()
];
}

private async getHistoricalSleepData(userId: string): Promise<HistoricalSleepData[]> {
// Implementation to fetch historical sleep data
return [];
}

private parseSleepPatternResponse(response: string): SleepPatternAnalysis {
// Parse LLM response into structured analysis
const parsed = JSON.parse(response);
return new SleepPatternAnalysis({
qualityScore: parsed.qualityScore,
sleepEfficiency: parsed.sleepEfficiency,
sleepLatency: parsed.sleepLatency,
awakeLengths: parsed.awakeLengths,
deepSleepPercentage: parsed.deepSleepPercentage,
remSleepPercentage: parsed.remSleepPercentage,
circadianAlignment: parsed.circadianAlignment,
trendAnalysis: parsed.trendAnalysis,
confidenceLevel: parsed.confidenceLevel,
reasoning: parsed.reasoning,
recommendations: parsed.recommendations
});
}

private parseChallengeResponse(response: string): DebateResult {
// Parse challenge response
const parsed = JSON.parse(response);
return new DebateResult({
challengingExpert: this.getExpertType(),
challenges: parsed.challenges,
supportingEvidence: parsed.supportingEvidence,
conflictingFindings: parsed.conflictingFindings,
proposedResolution: parsed.proposedResolution
});
}
}

개발 단계별 마일스톤

Phase 1 (Week 1-2): 기반 구조 구축

  • NestJS Feature Module 생성 및 DI 설정
  • LangGraph.js 워크플로우 기본 구조 구현
  • BaseExpertAgent 추상 클래스 구현
  • 프롬프트 관리 시스템 구축
  • 기본 도메인 엔티티 정의
  • CQRS Command/Query 구조 설정

Phase 2 (Week 3-4): 전문가 에이전트 구현

  • SleepPatternAnalystAgent 구현 및 테스트
  • PsychometricExpertAgent 구현 및 테스트
  • CBTISpecialistAgent 구현 및 테스트
  • MobileAnalyticsExpertAgent 구현 및 테스트
  • 각 에이전트별 프롬프트 템플릿 최적화
  • 에이전트별 도구(Tools) 구현

Phase 3 (Week 5-6): Chain of Debate 구현

  • 에이전트 간 토론 로직 구현
  • ConsensusBuilder 서비스 구현
  • 합의 도출 알고리즘 구현 (가중평균, 앙상블)
  • 결과 신뢰도 계산 시스템 구축
  • 토론 로그 저장 및 추적 시스템

Phase 4 (Week 7-8): 통합 및 최적화

  • DTA Wide CQRS 패턴 완전 통합
  • Event-Driven Architecture 이벤트 연동
  • TimeMachine 서비스 통합
  • Redis 캐싱 전략 구현
  • 성능 최적화 및 병렬 처리 개선
  • 에러 핸들링 및 복구 로직 구현

Phase 5 (Week 9-10): 테스트 및 검증

  • 단위 테스트 (각 에이전트 및 워크플로우)
  • 통합 테스트 (전체 시스템)
  • E2E 테스트 (API 엔드포인트)
  • 의료진 피드백 수집 및 반영
  • 성능 벤치마킹 및 부하 테스트
  • 프로덕션 배포 준비

📈 성공 지표 및 평가 기준

정량적 지표

  • SOL 예측 정확도: 실제 SOL 대비 예측 정확도 (목표: MAPE < 20%)
  • 응답 시간: SOL Chain of Debate 실행 시간 (목표: 25초 이내)
  • 합의 일관성: 동일 데이터에 대한 반복 실행시 SOL 예측 일관성 (목표: 90% 이상)
  • SOL 개선 효과: 권장사항 적용 후 SOL 개선률 (목표: 30% 이상)

정성적 지표

  • 의료진 신뢰도: 의료 전문가의 SOL 예측 결과 신뢰도 평가
  • 사용자 만족도: SOL 개선 권장사항의 실용성 및 수용도
  • 시스템 안정성: 오류 발생률 및 복구 시간
  • 확장 가능성: 새로운 전문가 에이전트 추가 용이성

⚠️ 리스크 및 완화 방안

기술적 리스크

  1. LLM 응답 불안정성

    • 완화: 여러 모델 앙상블, 응답 검증 로직, 재시도 메커니즘
  2. 토큰 비용 증가

    • 완화: 프롬프트 최적화, 캐싱 전략, 응답 길이 제한
  3. 응답 시간 지연

    • 완화: 병렬 처리, 응답 캐싱, 비동기 처리

의료 안전성 리스크

  1. 잘못된 권장사항 제공

    • 완화: 의료진 검토 프로세스, 면책 조항, 추가 상담 권유
  2. 개인정보 보호

    • 완화: 데이터 암호화, GDPR 준수, 최소 데이터 수집

비즈니스 리스크

  1. 사용자 수용도 저조

    • 완화: 점진적 롤아웃, 사용자 피드백 반영, UI/UX 개선
  2. 의료 규제 변경

    • 완화: 규제 모니터링, 유연한 아키텍처 설계, 컴플라이언스 검토

📅 일정 및 마일스톤 요약

Phase기간주요 산출물완료 기준
Phase 1Week 1-2기반 아키텍처기본 구조 및 DI 완료
Phase 2Week 3-44개 전문가 에이전트모든 에이전트 단위 테스트 통과
Phase 3Week 5-6Chain of Debate 로직전체 워크플로우 동작
Phase 4Week 7-8시스템 통합DTA Wide 플랫폼 완전 통합
Phase 5Week 9-10테스트 및 최적화프로덕션 배포 준비 완료

문서 버전: v1.0
작성일: 2025-08-31
다음 업데이트 예정: Phase 1 완료 후
담당자: Sleep Quality CoD Team