From 881565413761c92bcd2a5186e06c00bf8967cc29 Mon Sep 17 00:00:00 2001 From: kappa Date: Tue, 27 Jan 2026 11:15:22 +0900 Subject: [PATCH] feat: distinguish DAU from concurrent users in Server Expert AI - Add expectedDau and expectedConcurrent fields to ServerSession - Update system prompts to explain DAU vs concurrent users concept - AI now asks for clarification when users mention visitor counts - Use concurrent users (5-10% of DAU) for server recommendations - Update inference rules: personal=10, business=50 concurrent users Co-Authored-By: Claude Opus 4.5 --- src/server-agent.ts | 61 ++++++++++++++++++++++++++++++++++----------- src/types.ts | 2 ++ 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/server-agent.ts b/src/server-agent.ts index 037bda3..40b1174 100644 --- a/src/server-agent.ts +++ b/src/server-agent.ts @@ -162,10 +162,12 @@ function inferTechStack(useCase: string): string[] { } // Expected users inference from scale +// Returns concurrent users (not DAU) function inferExpectedUsers(scale: string): number { - if (scale === 'personal') return 100; - if (scale === 'business') return 500; - return 100; // Default to personal + // DAU → 동시접속자 변환 (5-10% 비율 적용) + if (scale === 'personal') return 10; // DAU 100명 → 동접 10명 + if (scale === 'business') return 50; // DAU 500명 → 동접 50명 + return 10; // Default to personal } // OpenAI API 응답 타입 @@ -254,14 +256,22 @@ ${JSON.stringify(recommendationData?.recommendations, null, 2)} ## 사용자 요구사항 - 용도: ${session.collectedInfo.useCase || '웹 서비스'} - 규모: ${session.collectedInfo.scale === 'business' ? '사업용' : '개인용'} +${session.collectedInfo.expectedDau ? `- 일일 방문자(DAU): ${session.collectedInfo.expectedDau}명` : ''} +${session.collectedInfo.expectedConcurrent ? `- 동시접속자: ${session.collectedInfo.expectedConcurrent}명` : ''} ${session.collectedInfo.budgetLimit ? `- 예산: ${session.collectedInfo.budgetLimit}원` : ''} +## 사용자 수 관련 참고사항 +- DAU(일일 활성 사용자)와 동시접속자는 다른 개념입니다 +- 일반적으로 동시접속자는 DAU의 5-10% 수준입니다 +- 서버 스펙은 동시접속자 기준으로 계산됩니다 + ## 검토 작업 다음을 검토하고 간결하게 2-3문장으로 코멘트해주세요: 1. 추천된 서버가 용도와 규모에 적합한지 2. 스펙이 충분한지 (RAM, CPU, 스토리지) -3. 대역폭 경고(overage)가 있다면 언급 -4. 더 적합한 스펙이 필요하다면 제안 +3. DAU/동시접속자 기준이 적절한지 +4. 대역폭 경고(overage)가 있다면 언급 +5. 더 적합한 스펙이 필요하다면 제안 ## 응답 형식 (반드시 JSON만 반환) { @@ -300,21 +310,35 @@ ${session.collectedInfo.budgetLimit ? `- 예산: ${session.collectedInfo.budgetL ## 대화 흐름 1. 용도 파악: "어떤 서비스를 운영하실 건가요? (예: 블로그, 쇼핑몰, 커뮤니티)" 2. 규모 파악: "개인용인가요, 사업용인가요?" -3. 정보가 충분하면 즉시 추천 (추가 질문 없이) +3. 사용자 수 확인 (필요 시): "방문자나 사용자 수는 어느 정도 예상하시나요?" +4. 정보가 충분하면 즉시 추천 (추가 질문 없이) ## 핵심 규칙 (반드시 준수) -- 기술 스택, 동시접속자 수, 트래픽 패턴은 절대 묻지 않음 (30년 경험으로 알아서 추론) +- 기술 스택, 트래픽 패턴은 절대 묻지 않음 (30년 경험으로 알아서 추론) +- 사용자 수를 언급하면 DAU인지 동시접속자인지 반드시 한 번 확인 +- "방문자 1000명", "유저 500명" 등 언급 시 → "말씀하신 방문자는 일일 방문자(DAU)인가요, 동시접속자인가요?" +- DAU와 동시접속자를 구분해서 설명: "일반적으로 동시접속자는 일일 방문자의 5-10% 정도입니다" - "모르겠어요", "아무거나", "글쎄요" → 즉시 action="recommend" (기본값: 개인용 웹서비스) - 용도+규모 한번에 말하면 → 즉시 action="recommend" - 용도만 말해도 → 개인용으로 가정하고 action="recommend" 가능 - 질문은 최대 2번까지, 그 이후는 무조건 action="recommend" +## 사용자 수 관련 용어 정리 +- **DAU (일일 활성 사용자)**: 하루 동안 서비스를 사용하는 전체 사용자 수 +- **동시접속자 (Concurrent Users)**: 같은 시간에 동시에 접속해 있는 사용자 수 +- **중요**: 서버 스펙은 동시접속자를 기준으로 계산해야 합니다 +- **일반 공식**: 동시접속자 = DAU × 5-10% + +예시: +- "하루 방문자 1000명" → DAU 1000명 → 동시접속자 50-100명 +- "동시 접속 100명" → 그대로 동시접속자 100명 사용 + ## 추론 규칙 (30년 경험 기반) -- 블로그 → WordPress, 1GB RAM이면 충분 -- 쇼핑몰 → 2GB+ RAM, DB 분리 고려 +- 블로그 → WordPress, 1GB RAM이면 충분, DAU 100명 (동시접속자 10명) +- 쇼핑몰 → 2GB+ RAM, DB 분리 고려, DAU 500명 (동시접속자 50명) - 커뮤니티 → PHP+MySQL, 트래픽에 따라 2~4GB - 게임서버 → 고사양 CPU, 낮은 레이턴시 리전 -- 규모: personal→100명, business→500명 +- 규모: personal→DAU 100명 (동접 10명), business→DAU 500명 (동접 50명) ## 현재 수집된 정보 ${JSON.stringify(session.collectedInfo, null, 2)} @@ -325,7 +349,9 @@ ${JSON.stringify(session.collectedInfo, null, 2)} "message": "사용자에게 보여줄 메시지 (도구에서 얻은 정보를 자연스럽게 포함)", "collectedInfo": { "useCase": "용도 (없으면 '웹서비스')", - "scale": "personal 또는 business (없으면 'personal')" + "scale": "personal 또는 business (없으면 'personal')", + "expectedDau": "일일 방문자 수 (사용자가 명시한 경우)", + "expectedConcurrent": "동시접속자 수 (사용자가 명시하거나 DAU에서 계산)" } } @@ -537,9 +563,16 @@ export async function processServerConsultation( ? inferTechStack(session.collectedInfo.useCase) : ['web']; - const expectedUsers = session.collectedInfo.scale - ? inferExpectedUsers(session.collectedInfo.scale) - : 100; + // 동시접속자 우선 사용, 없으면 scale 기반 추론 + let expectedUsers = 10; // Default + if (session.collectedInfo.expectedConcurrent) { + expectedUsers = session.collectedInfo.expectedConcurrent; + } else if (session.collectedInfo.expectedDau) { + // DAU가 있으면 10% 비율로 동시접속자 계산 + expectedUsers = Math.ceil(session.collectedInfo.expectedDau * 0.1); + } else if (session.collectedInfo.scale) { + expectedUsers = inferExpectedUsers(session.collectedInfo.scale); + } const recommendationData = await getRecommendationData( { diff --git a/src/types.ts b/src/types.ts index ab234d1..290292b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -230,6 +230,8 @@ export interface ServerSession { scale?: 'personal' | 'business'; budgetLimit?: number; regionPreference?: string[]; + expectedDau?: number; // 일일 활성 사용자 (Daily Active Users) + expectedConcurrent?: number; // 동시접속자 (Concurrent Users) }; messages: Array<{ role: 'user' | 'assistant'; content: string }>; createdAt: number;