- Add full conversation flow with session management - Handle tool call execution - Support __PASSTHROUGH__ and __SESSION_END__ markers - Add hasDomainSession helper for routing - Export executeDomainAction from domain-tool.ts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
29 KiB
도메인/예치금 에이전트 리팩토링 구현 계획
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: 마이그레이션 파일 작성
-- 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: 커밋
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: 디렉토리 생성 및 파일 이동
mkdir -p src/agents
mv src/server-agent.ts src/agents/server-agent.ts
Step 2: openai-service.ts import 수정
기존:
import { getServerSession, processServerConsultation } from './server-agent';
변경:
import { getServerSession, processServerConsultation } from './agents/server-agent';
Step 3: tools/server-tool.ts import 수정
기존:
import { getServerSession, saveServerSession, deleteServerSession } from '../server-agent';
변경:
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: 커밋
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: 파일 이동
mv src/troubleshoot-agent.ts src/agents/troubleshoot-agent.ts
Step 2: openai-service.ts import 수정
기존:
import { getTroubleshootSession, processTroubleshoot } from './troubleshoot-agent';
변경:
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: 커밋
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 타입 추가
// 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: 커밋
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 함수 작성
/**
* 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<DomainSession | null> {
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<void> {
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<void> {
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<number> {
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: 커밋
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 도구 정의 및 호출 함수 추가
// 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<string, unknown> }>;
}> {
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: 커밋
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
// domain-agent.ts에 추가
// Namecheap API 호출 함수 (domain-tool.ts에서 복사)
async function callNamecheapApi(
funcName: string,
funcArgs: Record<string, unknown>,
allowedDomains: string[],
env?: Env,
telegramUserId?: string,
db?: D1Database,
userId?: number
): Promise<unknown> {
// ... domain-tool.ts의 callNamecheapApi 함수 전체 복사
}
// 도구 실행 함수
async function executeDomainTool(
toolName: string,
args: Record<string, unknown>,
env: Env,
telegramUserId: string,
db: D1Database
): Promise<string> {
// 사용자 도메인 목록 조회
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: 커밋
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 함수 작성
export async function processDomainConsultation(
userMessage: string,
session: DomainSession,
env: Env
): Promise<string> {
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: 커밋
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 추가
import { getDomainSession, processDomainConsultation } from './agents/domain-agent';
Step 2: generateOpenAIResponse에 도메인 세션 체크 추가
서버 세션 체크 전에 도메인 세션 체크 추가:
export async function generateOpenAIResponse(
env: Env,
userMessage: string,
systemPrompt: string,
recentContext: { role: 'user' | 'assistant'; content: string }[],
telegramUserId?: string,
db?: D1Database,
chatIdStr?: string
): Promise<string> {
// 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: 커밋
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 도구를 세션 시작 트리거로 변경
// 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<string> {
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<string> {
// 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: 커밋
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: 도메인 에이전트 테스트
# 도메인 추천 테스트
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: 예치금 에이전트 테스트
# 충전 테스트 (세션 시작)
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: 최종 커밋 및 배포
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 동작 확인