# 도메인/예치금 에이전트 리팩토링 구현 계획 > **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. **Goal:** domain-tool.ts와 deposit-agent.ts를 server-agent 패턴의 세션 기반 AI 에이전트로 리팩토링 **Architecture:** 모든 도메인/예치금 요청이 에이전트를 통과하고 AI가 의도를 파악. 도메인은 작업 단위 세션, 예치금은 입금 신고만 세션 유지. **Tech Stack:** TypeScript, Cloudflare Workers, D1, OpenAI GPT-4o-mini --- ## Phase 1: 기반 작업 ### Task 1.1: 마이그레이션 SQL 작성 **Files:** - Create: `migrations/006_add_agent_sessions.sql` **Step 1: 마이그레이션 파일 작성** ```sql -- 006_add_agent_sessions.sql -- Domain Agent Sessions CREATE TABLE IF NOT EXISTS domain_sessions ( user_id TEXT PRIMARY KEY, status TEXT NOT NULL CHECK(status IN ('gathering', 'suggesting', 'confirming', 'setting_ns', 'completed')), collected_info TEXT, -- JSON: {keywords, purpose, suggestions[]} target_domain TEXT, -- 등록/NS변경 대상 도메인 messages TEXT, -- JSON: 대화 히스토리 created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, expires_at INTEGER NOT NULL -- TTL 1시간 ); CREATE INDEX IF NOT EXISTS idx_domain_sessions_expires ON domain_sessions(expires_at); -- Deposit Agent Sessions CREATE TABLE IF NOT EXISTS deposit_sessions ( user_id TEXT PRIMARY KEY, status TEXT NOT NULL CHECK(status IN ('collecting_amount', 'collecting_name', 'confirming', 'completed')), collected_info TEXT, -- JSON: {amount, depositor_name} messages TEXT, -- JSON: 대화 히스토리 created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, expires_at INTEGER NOT NULL -- TTL 30분 ); CREATE INDEX IF NOT EXISTS idx_deposit_sessions_expires ON deposit_sessions(expires_at); ``` **Step 2: 로컬 D1에 적용 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && wrangler d1 execute telegram-conversations --local --file=migrations/006_add_agent_sessions.sql` Expected: 테이블 생성 성공 **Step 3: 커밋** ```bash git add migrations/006_add_agent_sessions.sql git commit -m "chore: add domain/deposit agent session tables migration" ``` --- ### Task 1.2: agents 디렉토리 생성 및 server-agent 이동 **Files:** - Create: `src/agents/` 디렉토리 - Move: `src/server-agent.ts` → `src/agents/server-agent.ts` - Modify: `src/openai-service.ts` (import 경로 수정) - Modify: `src/tools/server-tool.ts` (import 경로 수정) - Modify: `src/index.ts` (import 경로 수정) **Step 1: 디렉토리 생성 및 파일 이동** ```bash mkdir -p src/agents mv src/server-agent.ts src/agents/server-agent.ts ``` **Step 2: openai-service.ts import 수정** 기존: ```typescript import { getServerSession, processServerConsultation } from './server-agent'; ``` 변경: ```typescript import { getServerSession, processServerConsultation } from './agents/server-agent'; ``` **Step 3: tools/server-tool.ts import 수정** 기존: ```typescript import { getServerSession, saveServerSession, deleteServerSession } from '../server-agent'; ``` 변경: ```typescript import { getServerSession, saveServerSession, deleteServerSession } from '../agents/server-agent'; ``` **Step 4: index.ts import 수정 (있다면)** 필요 시 import 경로 수정 **Step 5: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npm run dev` Expected: 에러 없이 실행 **Step 6: 커밋** ```bash git add src/agents/ src/openai-service.ts src/tools/server-tool.ts git commit -m "refactor: move server-agent to agents directory" ``` --- ### Task 1.3: troubleshoot-agent도 agents로 이동 **Files:** - Move: `src/troubleshoot-agent.ts` → `src/agents/troubleshoot-agent.ts` - Modify: `src/openai-service.ts` (import 경로 수정) - Modify: `src/tools/troubleshoot-tool.ts` (import 경로 수정) **Step 1: 파일 이동** ```bash mv src/troubleshoot-agent.ts src/agents/troubleshoot-agent.ts ``` **Step 2: openai-service.ts import 수정** 기존: ```typescript import { getTroubleshootSession, processTroubleshoot } from './troubleshoot-agent'; ``` 변경: ```typescript import { getTroubleshootSession, processTroubleshoot } from './agents/troubleshoot-agent'; ``` **Step 3: tools/troubleshoot-tool.ts import 수정** 필요 시 import 경로 수정 **Step 4: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npm run dev` Expected: 에러 없이 실행 **Step 5: 커밋** ```bash git add src/agents/troubleshoot-agent.ts src/openai-service.ts src/tools/troubleshoot-tool.ts git commit -m "refactor: move troubleshoot-agent to agents directory" ``` --- ### Task 1.4: 타입 정의 추가 (types.ts) **Files:** - Modify: `src/types.ts` **Step 1: DomainSession 타입 추가** ```typescript // Domain Agent Session export interface DomainSession { telegramUserId: string; status: 'gathering' | 'suggesting' | 'confirming' | 'setting_ns' | 'completed'; collectedInfo: { keywords?: string; purpose?: string; // 블로그, 쇼핑몰, 브랜드 등 suggestions?: Array<{ domain: string; price: number; available: boolean; }>; }; targetDomain?: string; // 등록/NS변경 대상 targetNameservers?: string[]; // NS변경 시 새 네임서버 messages: Array<{ role: 'user' | 'assistant'; content: string }>; createdAt: number; updatedAt: number; } // Deposit Agent Session export interface DepositSession { telegramUserId: string; status: 'collecting_amount' | 'collecting_name' | 'confirming' | 'completed'; collectedInfo: { amount?: number; depositorName?: string; }; messages: Array<{ role: 'user' | 'assistant'; content: string }>; createdAt: number; updatedAt: number; } ``` **Step 2: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 3: 커밋** ```bash git add src/types.ts git commit -m "feat: add DomainSession and DepositSession types" ``` --- ## Phase 2: 도메인 에이전트 ### Task 2.1: domain-agent.ts 기본 구조 생성 **Files:** - Create: `src/agents/domain-agent.ts` **Step 1: 세션 CRUD 함수 작성** ```typescript /** * Domain Agent - 도메인 관리 AI 에이전트 * * 기능: * - 대화형 도메인 추천 상담 * - 도메인 등록 확인 흐름 * - 네임서버 변경 확인 흐름 * - 단순 조회는 즉시 응답 (세션 없음) */ import type { Env, DomainSession } from '../types'; import { createLogger } from '../utils/logger'; const logger = createLogger('domain-agent'); const SESSION_TTL_MS = 3600 * 1000; // 1 hour // Session CRUD export async function getDomainSession( db: D1Database, userId: string ): Promise { try { const now = Date.now(); const result = await db.prepare( 'SELECT * FROM domain_sessions WHERE user_id = ? AND expires_at > ?' ).bind(userId, now).first<{ user_id: string; status: string; collected_info: string | null; target_domain: string | null; messages: string | null; created_at: number; updated_at: number; }>(); if (!result) { return null; } return { telegramUserId: result.user_id, status: result.status as DomainSession['status'], collectedInfo: result.collected_info ? JSON.parse(result.collected_info) : {}, targetDomain: result.target_domain || undefined, messages: result.messages ? JSON.parse(result.messages) : [], createdAt: result.created_at, updatedAt: result.updated_at, }; } catch (error) { logger.error('세션 조회 실패', error as Error, { userId }); return null; } } export async function saveDomainSession( db: D1Database, userId: string, session: DomainSession ): Promise { try { const now = Date.now(); const expiresAt = now + SESSION_TTL_MS; await db.prepare(` INSERT INTO domain_sessions (user_id, status, collected_info, target_domain, messages, created_at, updated_at, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET status = excluded.status, collected_info = excluded.collected_info, target_domain = excluded.target_domain, messages = excluded.messages, updated_at = excluded.updated_at, expires_at = excluded.expires_at `).bind( userId, session.status, JSON.stringify(session.collectedInfo || {}), session.targetDomain || null, JSON.stringify(session.messages || []), session.createdAt || now, now, expiresAt ).run(); logger.info('세션 저장 성공', { userId, status: session.status }); } catch (error) { logger.error('세션 저장 실패', error as Error, { userId }); throw error; } } export async function deleteDomainSession( db: D1Database, userId: string ): Promise { try { await db.prepare('DELETE FROM domain_sessions WHERE user_id = ?') .bind(userId) .run(); logger.info('세션 삭제 성공', { userId }); } catch (error) { logger.error('세션 삭제 실패', error as Error, { userId }); throw error; } } export async function cleanupExpiredDomainSessions(db: D1Database): Promise { try { const result = await db.prepare( 'DELETE FROM domain_sessions WHERE expires_at < ?' ).bind(Date.now()).run(); const deleted = result.meta.changes || 0; if (deleted > 0) { logger.info('만료 세션 정리', { deleted }); } return deleted; } catch (error) { logger.error('만료 세션 정리 실패', error as Error); return 0; } } ``` **Step 2: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 3: 커밋** ```bash git add src/agents/domain-agent.ts git commit -m "feat: add domain-agent session CRUD functions" ``` --- ### Task 2.2: 도메인 에이전트 AI 호출 함수 작성 **Files:** - Modify: `src/agents/domain-agent.ts` **Step 1: AI 도구 정의 및 호출 함수 추가** ```typescript // Domain Expert AI Tools const domainAgentTools = [ { type: 'function' as const, function: { name: 'check_domain', description: '도메인 등록 가능 여부와 가격을 확인합니다.', parameters: { type: 'object', properties: { domain: { type: 'string', description: '확인할 도메인 (예: example.com)', }, }, required: ['domain'], }, }, }, { type: 'function' as const, function: { name: 'search_suggestions', description: '키워드 기반으로 도메인을 추천합니다.', parameters: { type: 'object', properties: { keywords: { type: 'string', description: '도메인 추천을 위한 키워드', }, }, required: ['keywords'], }, }, }, { type: 'function' as const, function: { name: 'get_whois', description: '도메인 WHOIS 정보를 조회합니다.', parameters: { type: 'object', properties: { domain: { type: 'string', description: '조회할 도메인', }, }, required: ['domain'], }, }, }, { type: 'function' as const, function: { name: 'get_tld_price', description: 'TLD 가격을 조회합니다.', parameters: { type: 'object', properties: { tld: { type: 'string', description: 'TLD (예: com, io, net)', }, }, required: ['tld'], }, }, }, { type: 'function' as const, function: { name: 'list_my_domains', description: '사용자의 도메인 목록을 조회합니다.', parameters: { type: 'object', properties: {}, }, }, }, { type: 'function' as const, function: { name: 'get_nameservers', description: '도메인의 네임서버를 조회합니다.', parameters: { type: 'object', properties: { domain: { type: 'string', description: '조회할 도메인', }, }, required: ['domain'], }, }, }, ]; // AI 호출 함수 async function callDomainExpertAI( env: Env, session: DomainSession, userMessage: string ): Promise<{ action: 'question' | 'suggest' | 'register' | 'set_ns' | 'immediate'; message: string; collectedInfo?: DomainSession['collectedInfo']; targetDomain?: string; targetNameservers?: string[]; toolCalls?: Array<{ name: string; args: Record }>; }> { if (!env.OPENAI_API_KEY) { throw new Error('OPENAI_API_KEY not configured'); } const { getOpenAIUrl } = await import('../utils/api-urls'); const conversationHistory = session.messages.map(m => ({ role: m.role === 'user' ? 'user' as const : 'assistant' as const, content: m.content, })); const systemPrompt = `당신은 10년 경력의 도메인 컨설턴트입니다. ## 전문성 - 브랜딩, SEO, 가격 대비 가치를 고려한 조언 제공 - 불필요한 프리미엄 도메인 추천 자제 - 실용적이고 기억하기 쉬운 도메인 추천 ## 성격 - 따뜻하고 친근하지만 전문적인 어조 - 비기술자도 이해하기 쉽게 설명 ## 작업 분류 ### 세션 필요 작업 (action으로 반환) - "도메인 추천" → action="suggest", 키워드/용도 수집 - "도메인 등록" → action="register", 도메인명 확인 - "네임서버 변경" → action="set_ns", 변경할 NS 확인 ### 즉시 응답 작업 (도구 호출 후 action="immediate") - 가격 조회 → get_tld_price 도구 호출 - WHOIS 조회 → get_whois 도구 호출 - 내 도메인 목록 → list_my_domains 도구 호출 - 네임서버 조회 → get_nameservers 도구 호출 - 도메인 가용성 확인 → check_domain 도구 호출 ## 현재 수집된 정보 ${JSON.stringify(session.collectedInfo, null, 2)} ## 응답 형식 (반드시 JSON만 반환) { "action": "question" | "suggest" | "register" | "set_ns" | "immediate", "message": "사용자에게 보여줄 메시지", "collectedInfo": { ... }, // 수집된 정보 업데이트 "targetDomain": "example.com", // register, set_ns 시 "targetNameservers": ["ns1.example.com", "ns2.example.com"] // set_ns 시 }`; try { const response = await fetch(getOpenAIUrl(env), { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${env.OPENAI_API_KEY}`, }, body: JSON.stringify({ model: 'gpt-4o-mini', messages: [ { role: 'system', content: systemPrompt }, ...conversationHistory, { role: 'user', content: userMessage }, ], tools: domainAgentTools, tool_choice: 'auto', response_format: { type: 'json_object' }, max_tokens: 800, temperature: 0.7, }), }); if (!response.ok) { const error = await response.text(); throw new Error(`OpenAI API error: ${response.status} - ${error}`); } const data = await response.json() as { choices: Array<{ message: { content: string | null; tool_calls?: Array<{ id: string; function: { name: string; arguments: string }; }>; }; }>; }; const assistantMessage = data.choices[0].message; // Tool calls가 있으면 처리 if (assistantMessage.tool_calls && assistantMessage.tool_calls.length > 0) { const toolCalls = assistantMessage.tool_calls.map(tc => ({ name: tc.function.name, args: JSON.parse(tc.function.arguments), })); return { action: 'immediate', message: '', toolCalls, }; } // JSON 응답 파싱 const aiResponse = assistantMessage.content || ''; const parsed = JSON.parse(aiResponse); return { action: parsed.action, message: parsed.message, collectedInfo: parsed.collectedInfo, targetDomain: parsed.targetDomain, targetNameservers: parsed.targetNameservers, }; } catch (error) { logger.error('Domain Expert AI 호출 실패', error as Error); throw error; } } ``` **Step 2: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 3: 커밋** ```bash git add src/agents/domain-agent.ts git commit -m "feat: add domain-agent AI tools and callDomainExpertAI function" ``` --- ### Task 2.3: 도메인 에이전트 도구 실행 함수 이관 **Files:** - Modify: `src/agents/domain-agent.ts` - Reference: `src/tools/domain-tool.ts` (로직 복사) **Step 1: domain-tool.ts에서 핵심 로직 복사** 기존 `domain-tool.ts`의 `callNamecheapApi`, `executeDomainAction` 함수들을 `domain-agent.ts`로 이관. 단, 함수명 변경: - `executeDomainAction` → `executeDomainTool` ```typescript // domain-agent.ts에 추가 // Namecheap API 호출 함수 (domain-tool.ts에서 복사) async function callNamecheapApi( funcName: string, funcArgs: Record, allowedDomains: string[], env?: Env, telegramUserId?: string, db?: D1Database, userId?: number ): Promise { // ... domain-tool.ts의 callNamecheapApi 함수 전체 복사 } // 도구 실행 함수 async function executeDomainTool( toolName: string, args: Record, env: Env, telegramUserId: string, db: D1Database ): Promise { // 사용자 도메인 목록 조회 const user = await db.prepare( 'SELECT id FROM users WHERE telegram_id = ?' ).bind(telegramUserId).first<{ id: number }>(); if (!user) { return '🚫 사용자 정보를 찾을 수 없습니다.'; } const domains = await db.prepare( 'SELECT domain FROM user_domains WHERE user_id = ? AND verified = 1' ).bind(user.id).all<{ domain: string }>(); const userDomains = domains.results?.map(d => d.domain) || []; switch (toolName) { case 'check_domain': { // domain-tool.ts의 'check' action 로직 const domain = args.domain as string; // ... 구현 } case 'search_suggestions': { // domain-tool.ts의 executeSuggestDomains 로직 // ... 구현 } case 'get_whois': { // domain-tool.ts의 'whois' action 로직 // ... 구현 } case 'get_tld_price': { // domain-tool.ts의 'price' action 로직 // ... 구현 } case 'list_my_domains': { // domain-tool.ts의 'list' action 로직 // ... 구현 } case 'get_nameservers': { // domain-tool.ts의 'get_ns' action 로직 // ... 구현 } default: return `알 수 없는 도구: ${toolName}`; } } ``` **Step 2: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 3: 커밋** ```bash git add src/agents/domain-agent.ts git commit -m "feat: add domain tool execution functions to domain-agent" ``` --- ### Task 2.4: 도메인 에이전트 메인 처리 함수 작성 **Files:** - Modify: `src/agents/domain-agent.ts` **Step 1: processDomainConsultation 함수 작성** ```typescript export async function processDomainConsultation( userMessage: string, session: DomainSession, env: Env ): Promise { try { logger.info('도메인 상담 처리 시작', { userId: session.telegramUserId, message: userMessage.slice(0, 50), status: session.status }); // 취소 키워드 처리 if (/^(취소|다시|처음|리셋)/.test(userMessage.trim())) { await deleteDomainSession(env.DB, session.telegramUserId); return '도메인 상담이 취소되었습니다. 다시 시작하려면 "도메인 추천"이라고 말씀해주세요.'; } // 무관한 메시지 감지 const unrelatedPatterns = /날씨|시간|계산|서버|입금|충전|잔액/; if (unrelatedPatterns.test(userMessage)) { return '__PASSTHROUGH__'; } // 세션에 메시지 추가 session.messages.push({ role: 'user', content: userMessage }); // AI 호출 const aiResult = await callDomainExpertAI(env, session, userMessage); // 도구 호출이 있으면 실행 if (aiResult.action === 'immediate' && aiResult.toolCalls) { const results: string[] = []; for (const toolCall of aiResult.toolCalls) { const result = await executeDomainTool( toolCall.name, toolCall.args, env, session.telegramUserId, env.DB ); results.push(result); } // 세션 삭제 (즉시 응답은 세션 불필요) await deleteDomainSession(env.DB, session.telegramUserId); return results.join('\n\n'); } // 수집된 정보 업데이트 if (aiResult.collectedInfo) { session.collectedInfo = { ...session.collectedInfo, ...aiResult.collectedInfo }; } if (aiResult.targetDomain) { session.targetDomain = aiResult.targetDomain; } if (aiResult.targetNameservers) { session.targetNameservers = aiResult.targetNameservers; } // 세션에 AI 응답 추가 session.messages.push({ role: 'assistant', content: aiResult.message }); // 상태 전환 switch (aiResult.action) { case 'question': session.status = 'gathering'; break; case 'suggest': session.status = 'suggesting'; // TODO: 도메인 추천 실행 break; case 'register': session.status = 'confirming'; // TODO: 등록 확인 흐름 break; case 'set_ns': session.status = 'setting_ns'; // TODO: NS 변경 확인 흐름 break; } await saveDomainSession(env.DB, session.telegramUserId, session); return aiResult.message; } catch (error) { logger.error('도메인 상담 처리 실패', error as Error); await deleteDomainSession(env.DB, session.telegramUserId); return '죄송합니다. 도메인 상담 중 오류가 발생했습니다.\n다시 시도하려면 "도메인 추천"이라고 말씀해주세요.'; } } ``` **Step 2: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 3: 커밋** ```bash git add src/agents/domain-agent.ts git commit -m "feat: add processDomainConsultation main handler" ``` --- ### Task 2.5: openai-service.ts에 도메인 세션 체크 추가 **Files:** - Modify: `src/openai-service.ts` **Step 1: import 추가** ```typescript import { getDomainSession, processDomainConsultation } from './agents/domain-agent'; ``` **Step 2: generateOpenAIResponse에 도메인 세션 체크 추가** 서버 세션 체크 전에 도메인 세션 체크 추가: ```typescript export async function generateOpenAIResponse( env: Env, userMessage: string, systemPrompt: string, recentContext: { role: 'user' | 'assistant'; content: string }[], telegramUserId?: string, db?: D1Database, chatIdStr?: string ): Promise { // Check if domain session is active if (telegramUserId && env.DB) { try { const domainSession = await getDomainSession(env.DB, telegramUserId); if (domainSession && domainSession.status !== 'completed') { logger.info('Active domain session detected', { userId: telegramUserId, status: domainSession.status }); const result = await processDomainConsultation(userMessage, domainSession, env); if (result !== '__PASSTHROUGH__') { return result; } } } catch (error) { logger.error('Domain session check failed', error as Error); } } // ... 기존 서버 세션 체크 코드 ... ``` **Step 3: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 4: 커밋** ```bash git add src/openai-service.ts git commit -m "feat: add domain session check to openai-service" ``` --- ### Task 2.6: domain-tool.ts를 에이전트 시작 트리거로 변경 **Files:** - Modify: `src/tools/domain-tool.ts` **Step 1: manage_domain 도구를 세션 시작 트리거로 변경** ```typescript // domain-tool.ts 전체 리팩토링 import type { Env } from '../types'; import { createLogger } from '../utils/logger'; import { getDomainSession, saveDomainSession } from '../agents/domain-agent'; const logger = createLogger('domain-tool'); export const manageDomainTool = { type: 'function', function: { name: 'manage_domain', description: '도메인 관련 모든 작업을 처리합니다. 도메인 추천, 등록, 가격 조회, WHOIS, 네임서버 관리 등.', parameters: { type: 'object', properties: { request: { type: 'string', description: '사용자의 도메인 관련 요청 (자연어)', }, }, required: ['request'], }, }, }; // suggest_domains 도구는 manage_domain으로 통합, 제거 예정 export const suggestDomainsTool = manageDomainTool; export async function executeManageDomain( args: { request: string }, env?: Env, telegramUserId?: string, db?: D1Database ): Promise { if (!env || !telegramUserId || !db) { return '🚫 도메인 관리 권한이 없습니다.'; } // 세션 시작 const existingSession = await getDomainSession(db, telegramUserId); if (!existingSession) { // 새 세션 생성 const newSession = { telegramUserId, status: 'gathering' as const, collectedInfo: {}, messages: [], createdAt: Date.now(), updatedAt: Date.now(), }; await saveDomainSession(db, telegramUserId, newSession); logger.info('도메인 세션 시작', { userId: telegramUserId }); return '__START_DOMAIN_SESSION__'; // 특수 마커로 세션 시작 알림 } return '__DOMAIN_SESSION_ACTIVE__'; // 이미 세션 활성화 중 } export async function executeSuggestDomains( args: { keywords: string }, env?: Env, telegramUserId?: string, db?: D1Database ): Promise { // manage_domain으로 리다이렉트 return executeManageDomain({ request: `도메인 추천: ${args.keywords}` }, env, telegramUserId, db); } ``` **Step 2: tools/index.ts 수정** 도구 목록에서 suggest_domains 제거 또는 manage_domain과 통합 **Step 3: 빌드 테스트** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npx tsc --noEmit` Expected: 타입 에러 없음 **Step 4: 커밋** ```bash git add src/tools/domain-tool.ts src/tools/index.ts git commit -m "refactor: convert domain-tool to session start trigger" ``` --- ## Phase 3: 예치금 에이전트 (Task 3.1 - 3.5) Phase 2와 동일한 패턴으로: ### Task 3.1: deposit-agent.ts를 agents로 이동 및 세션 로직 추가 ### Task 3.2: 예치금 에이전트 AI 호출 함수 작성 ### Task 3.3: 예치금 에이전트 도구 실행 함수 정리 ### Task 3.4: processDepositConsultation 함수 작성 ### Task 3.5: openai-service.ts에 예치금 세션 체크 추가 (상세 내용은 Phase 2 패턴과 동일하게 진행) --- ## Phase 4: 정리 및 테스트 ### Task 4.1: 사용하지 않는 코드 정리 **Files:** - Delete: `src/tools/deposit-tool.ts` (deposit-agent로 통합됨) - Modify: `src/tools/index.ts` (import 정리) ### Task 4.2: 로컬 테스트 **Step 1: 로컬 서버 실행** Run: `cd /Users/kaffa/Projects/bots/telegram-bot-workers && npm run dev` **Step 2: 도메인 에이전트 테스트** ```bash # 도메인 추천 테스트 curl -X POST http://localhost:8787/webhook \ -H "Content-Type: application/json" \ -H "X-Telegram-Bot-Api-Secret-Token: test" \ -d '{"message":{"chat":{"id":123},"from":{"id":123},"text":"도메인 추천해줘"}}' # .com 가격 조회 테스트 (즉시 응답) curl -X POST http://localhost:8787/webhook \ -H "Content-Type: application/json" \ -H "X-Telegram-Bot-Api-Secret-Token: test" \ -d '{"message":{"chat":{"id":123},"from":{"id":123},"text":".com 가격"}}' ``` **Step 3: 예치금 에이전트 테스트** ```bash # 충전 테스트 (세션 시작) curl -X POST http://localhost:8787/webhook \ -H "Content-Type: application/json" \ -H "X-Telegram-Bot-Api-Secret-Token: test" \ -d '{"message":{"chat":{"id":123},"from":{"id":123},"text":"충전할게"}}' # 잔액 조회 테스트 (즉시 응답) curl -X POST http://localhost:8787/webhook \ -H "Content-Type: application/json" \ -H "X-Telegram-Bot-Api-Secret-Token: test" \ -d '{"message":{"chat":{"id":123},"from":{"id":123},"text":"잔액"}}' ``` ### Task 4.3: 문서 업데이트 **Files:** - Modify: `CLAUDE.md` - Modify: `README.md` 파일 구조, 에이전트 설명 업데이트 ### Task 4.4: 최종 커밋 및 배포 ```bash git add . git commit -m "feat: complete domain and deposit agent refactoring" # 프로덕션 마이그레이션 (주의!) wrangler d1 execute telegram-conversations --file=migrations/006_add_agent_sessions.sql # 배포 npm run deploy ``` --- ## 위험 요소 체크리스트 - [ ] 마이그레이션 전 D1 백업 - [ ] 기존 domain-tool.ts 테스트가 통과하는지 확인 - [ ] 기존 deposit-agent.ts 테스트가 통과하는지 확인 - [ ] 세션 TTL 만료 후 정상 동작 확인 - [ ] `__PASSTHROUGH__` 동작 확인 - [ ] Circuit Breaker 동작 확인