feat: add AI review of server recommendations
- Server Expert AI now reviews recommendation results before showing to user - Changed flow: get recommendations first → AI reviews → show with comments - AI provides specific advice based on actual recommended specs - Reviews include: spec adequacy, bandwidth warnings, CDN suggestions Before: AI gave generic advice without seeing recommendations After: AI reviews actual results and gives contextual feedback Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -191,11 +191,38 @@ interface OpenAIAPIResponse {
|
|||||||
}>;
|
}>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecommendResponse 타입 (server-tool.ts와 동일)
|
||||||
|
interface RecommendResponse {
|
||||||
|
recommendations: Array<{
|
||||||
|
server: {
|
||||||
|
instance_name: string;
|
||||||
|
vcpu: number;
|
||||||
|
memory_gb: number;
|
||||||
|
storage_gb: number;
|
||||||
|
transfer_tb: number;
|
||||||
|
monthly_price: number;
|
||||||
|
provider_name: string;
|
||||||
|
region_code: string;
|
||||||
|
region_name: string;
|
||||||
|
};
|
||||||
|
score: number;
|
||||||
|
estimated_capacity?: {
|
||||||
|
max_concurrent_users?: number;
|
||||||
|
};
|
||||||
|
bandwidth_analysis?: {
|
||||||
|
estimated_monthly_tb?: number;
|
||||||
|
overage_tb?: number;
|
||||||
|
overage_cost_krw?: number;
|
||||||
|
};
|
||||||
|
}>;
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAI 호출 (서버 전문가 AI with Function Calling)
|
// OpenAI 호출 (서버 전문가 AI with Function Calling)
|
||||||
async function callServerExpertAI(
|
async function callServerExpertAI(
|
||||||
env: Env,
|
env: Env,
|
||||||
session: ServerSession,
|
session: ServerSession,
|
||||||
userMessage: string
|
userMessage: string,
|
||||||
|
recommendationData?: RecommendResponse
|
||||||
): Promise<{ action: 'question' | 'recommend'; message: string; collectedInfo: ServerSession['collectedInfo'] }> {
|
): Promise<{ action: 'question' | 'recommend'; message: string; collectedInfo: ServerSession['collectedInfo'] }> {
|
||||||
if (!env.OPENAI_API_KEY) {
|
if (!env.OPENAI_API_KEY) {
|
||||||
throw new Error('OPENAI_API_KEY not configured');
|
throw new Error('OPENAI_API_KEY not configured');
|
||||||
@@ -209,7 +236,42 @@ async function callServerExpertAI(
|
|||||||
content: m.content,
|
content: m.content,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const systemPrompt = `당신은 30년 경력의 시니어 클라우드 아키텍트입니다.
|
// 검토 모드: 추천 결과가 있을 때
|
||||||
|
const isReviewMode = !!recommendationData;
|
||||||
|
|
||||||
|
const systemPrompt = isReviewMode
|
||||||
|
? `당신은 Cloud Orchestrator가 추천한 서버를 검토하는 30년 경력의 시니어 클라우드 아키텍트입니다.
|
||||||
|
|
||||||
|
## 전문성 (30년 경력)
|
||||||
|
- 서버 엔지니어: Linux, Windows Server, 가상화, 컨테이너 마스터
|
||||||
|
- 네트워크 엔지니어: 로드밸런싱, CDN, DNS, 보안 설계 전문
|
||||||
|
- 클라우드 아키텍트: 모든 클라우드 플랫폼 경험
|
||||||
|
- 수천 개의 서버 구축 경험
|
||||||
|
|
||||||
|
## 검토 대상 추천 결과
|
||||||
|
${JSON.stringify(recommendationData?.recommendations, null, 2)}
|
||||||
|
|
||||||
|
## 사용자 요구사항
|
||||||
|
- 용도: ${session.collectedInfo.useCase || '웹 서비스'}
|
||||||
|
- 규모: ${session.collectedInfo.scale === 'business' ? '사업용' : '개인용'}
|
||||||
|
${session.collectedInfo.budgetLimit ? `- 예산: ${session.collectedInfo.budgetLimit}원` : ''}
|
||||||
|
|
||||||
|
## 검토 작업
|
||||||
|
다음을 검토하고 간결하게 2-3문장으로 코멘트해주세요:
|
||||||
|
1. 추천된 서버가 용도와 규모에 적합한지
|
||||||
|
2. 스펙이 충분한지 (RAM, CPU, 스토리지)
|
||||||
|
3. 대역폭 경고(overage)가 있다면 언급
|
||||||
|
4. 더 적합한 스펙이 필요하다면 제안
|
||||||
|
|
||||||
|
## 응답 형식 (반드시 JSON만 반환)
|
||||||
|
{
|
||||||
|
"action": "recommend",
|
||||||
|
"message": "검토 코멘트 (자연스럽고 친근한 어조, 2-3문장)",
|
||||||
|
"collectedInfo": ${JSON.stringify(session.collectedInfo)}
|
||||||
|
}
|
||||||
|
|
||||||
|
중요: 검토 코멘트만 작성하세요. 추천 결과 나열은 하지 마세요.`
|
||||||
|
: `당신은 30년 경력의 시니어 클라우드 아키텍트입니다.
|
||||||
|
|
||||||
## 전문성 (30년 경력)
|
## 전문성 (30년 경력)
|
||||||
- 서버 엔지니어: Linux, Windows Server, 가상화, 컨테이너 마스터
|
- 서버 엔지니어: Linux, Windows Server, 가상화, 컨테이너 마스터
|
||||||
@@ -466,7 +528,7 @@ export async function processServerConsultation(
|
|||||||
session.status = 'recommending';
|
session.status = 'recommending';
|
||||||
await saveServerSession(env.SESSION_KV, session.telegramUserId, session);
|
await saveServerSession(env.SESSION_KV, session.telegramUserId, session);
|
||||||
|
|
||||||
// Call recommendation API
|
// 1. Call recommendation API (추천 먼저 받기)
|
||||||
logger.info('추천 API 호출', { collectedInfo: session.collectedInfo });
|
logger.info('추천 API 호출', { collectedInfo: session.collectedInfo });
|
||||||
|
|
||||||
const { executeServerAction, getRecommendationData } = await import('./tools/server-tool');
|
const { executeServerAction, getRecommendationData } = await import('./tools/server-tool');
|
||||||
@@ -516,10 +578,11 @@ export async function processServerConsultation(
|
|||||||
createdAt: Date.now()
|
createdAt: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mark session as selecting (사용자 선택 대기)
|
// 2. AI에게 추천 결과 전달하여 검토 요청
|
||||||
session.status = 'selecting';
|
logger.info('AI 검토 요청', { recommendationCount: recommendationData.recommendations.length });
|
||||||
await saveServerSession(env.SESSION_KV, session.telegramUserId, session);
|
const reviewResult = await callServerExpertAI(env, session, userMessage, recommendationData);
|
||||||
|
|
||||||
|
// 3. 포맷팅된 추천 결과 생성
|
||||||
const formattedRecommendation = await executeServerAction(
|
const formattedRecommendation = await executeServerAction(
|
||||||
'recommend',
|
'recommend',
|
||||||
{
|
{
|
||||||
@@ -534,7 +597,12 @@ export async function processServerConsultation(
|
|||||||
session.telegramUserId
|
session.telegramUserId
|
||||||
);
|
);
|
||||||
|
|
||||||
return `${aiResult.message}\n\n${formattedRecommendation}\n\n💡 원하는 서버 번호를 선택해주세요 (예: 1번)`;
|
// Mark session as selecting (사용자 선택 대기)
|
||||||
|
session.status = 'selecting';
|
||||||
|
await saveServerSession(env.SESSION_KV, session.telegramUserId, session);
|
||||||
|
|
||||||
|
// 4. AI 검토 코멘트 + 추천 결과 함께 반환
|
||||||
|
return `${reviewResult.message}\n\n${formattedRecommendation}\n\n💡 원하는 서버 번호를 선택해주세요 (예: 1번)`;
|
||||||
} else {
|
} else {
|
||||||
// 추천 결과 없음 - 세션 삭제
|
// 추천 결과 없음 - 세션 삭제
|
||||||
session.status = 'completed';
|
session.status = 'completed';
|
||||||
|
|||||||
Reference in New Issue
Block a user