feat: complete agent refactoring integration
- Add deposit session routing to openai-service.ts - Convert deposit-tool to agent trigger (delegate to deposit-agent) - Update CLAUDE.md with new agent architecture Changes: - openai-service.ts: Import and check hasDepositSession, route to processDepositConsultation - deposit-tool.ts: Convert action → natural language → delegate to Deposit Agent - CLAUDE.md: Update Core Services, Function Calling Tools, Data Layer, Deposit System sections All tests passing (192/195), dev server starts successfully. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
66
CLAUDE.md
66
CLAUDE.md
@@ -193,15 +193,21 @@ Telegram Webhook → Security Validation → Command/Message Router
|
|||||||
| 파일 | 역할 | 주요 함수 |
|
| 파일 | 역할 | 주요 함수 |
|
||||||
|------|------|----------|
|
|------|------|----------|
|
||||||
| `index.ts` | Worker 진입점, Email Handler | `fetch()`, `email()` |
|
| `index.ts` | Worker 진입점, Email Handler | `fetch()`, `email()` |
|
||||||
| `openai-service.ts` | AI 응답 + Function Calling | `generateResponse()`, `executeFunctionCall()` |
|
| `openai-service.ts` | AI 응답 + Function Calling + 세션 라우팅 | `generateResponse()`, `executeFunctionCall()` |
|
||||||
| `summary-service.ts` | 프로필 시스템 | `updateSummary()`, `getConversationContext()` |
|
| `summary-service.ts` | 프로필 시스템 | `updateSummary()`, `getConversationContext()` |
|
||||||
| `deposit-agent.ts` | 예치금 함수 (코드 직접 처리) | `executeDepositFunction()` |
|
|
||||||
| `server-agent.ts` | 서버 전문가 AI 상담 | `processServerConsultation()`, `callServerExpertAI()` |
|
|
||||||
| `security.ts` | Webhook 보안, Rate Limiting (KV) | `validateWebhook()`, `checkRateLimit()` |
|
| `security.ts` | Webhook 보안, Rate Limiting (KV) | `validateWebhook()`, `checkRateLimit()` |
|
||||||
| `services/notification.ts` | 관리자 알림 | `notifyAdmin()` |
|
| `services/notification.ts` | 관리자 알림 | `notifyAdmin()` |
|
||||||
| `commands.ts` | 봇 명령어 | `handleCommand()` |
|
| `commands.ts` | 봇 명령어 | `handleCommand()` |
|
||||||
| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` |
|
| `telegram.ts` | Telegram API | `sendMessage()`, `sendTypingAction()` |
|
||||||
|
|
||||||
|
**Agent System (세션 기반 전문가 AI):**
|
||||||
|
| 파일 | 역할 | 상태 관리 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `agents/server-agent.ts` | 서버 추천 상담 (30년 경력 아키텍트) | KV (1시간) |
|
||||||
|
| `agents/troubleshoot-agent.ts` | 트러블슈팅 상담 | KV (1시간) |
|
||||||
|
| `agents/domain-agent.ts` | 도메인 추천 상담 (10년 경력 컨설턴트) | D1 (1시간) |
|
||||||
|
| `agents/deposit-agent.ts` | 예치금 입금 신고 상담 (금융 상담사) | D1 (30분) |
|
||||||
|
|
||||||
**Logging & Monitoring:**
|
**Logging & Monitoring:**
|
||||||
| 파일 | 역할 |
|
| 파일 | 역할 |
|
||||||
|------|------|
|
|------|------|
|
||||||
@@ -212,17 +218,17 @@ Telegram Webhook → Security Validation → Command/Message Router
|
|||||||
|
|
||||||
### Function Calling Tools (9개)
|
### Function Calling Tools (9개)
|
||||||
|
|
||||||
| 도구 | 함수명 | 외부 API | 트리거 키워드 |
|
| 도구 | 함수명 | 처리 방식 | 트리거 키워드 |
|
||||||
|------|--------|----------|---------------|
|
|------|--------|----------|---------------|
|
||||||
| 날씨 | `get_weather` | wttr.in | 날씨 |
|
| 날씨 | `get_weather` | wttr.in API | 날씨 |
|
||||||
| 검색 | `search_web` | Brave Search | ~란, ~뭐야 (한글→영문 자동 번역) |
|
| 검색 | `search_web` | Brave Search API (한글→영문 자동 번역) | ~란, ~뭐야 |
|
||||||
| 시간 | `get_current_time` | 내장 | 몇 시, 시간 |
|
| 시간 | `get_current_time` | 내장 | 몇 시, 시간 |
|
||||||
| 계산 | `calculate` | 내장 | 계산, +, -, *, / |
|
| 계산 | `calculate` | 내장 | 계산, +, -, *, / |
|
||||||
| 문서 | `lookup_docs` | Context7 | 문서, 사용법, API |
|
| 문서 | `lookup_docs` | Context7 API | 문서, 사용법, API |
|
||||||
| 도메인 | `manage_domain` | 코드 직접 처리 → Namecheap | 도메인, 네임서버, WHOIS |
|
| 도메인 | `manage_domain` | **Domain Agent 위임** (세션 기반) | 도메인, 네임서버, WHOIS |
|
||||||
| 도메인 추천 | `suggest_domains` | GPT + Namecheap | 도메인 추천, 도메인 제안 |
|
| 도메인 추천 | `suggest_domains` | GPT + Namecheap API | 도메인 추천, 도메인 제안 |
|
||||||
| 예치금 | `manage_deposit` | 코드 직접 처리 | 입금, 충전, 잔액, 계좌 |
|
| 예치금 | `manage_deposit` | **Deposit Agent 위임** (세션 기반) | 입금, 충전, 잔액, 계좌 |
|
||||||
| 서버 | `manage_server` | Server Expert AI (세션 기반) | 서버, VPS, 클라우드, 호스팅 |
|
| 서버 | `manage_server` | **Server Agent 위임** (세션 기반) | 서버, VPS, 클라우드, 호스팅 |
|
||||||
|
|
||||||
### Data Layer (D1 SQLite)
|
### Data Layer (D1 SQLite)
|
||||||
|
|
||||||
@@ -237,6 +243,8 @@ Telegram Webhook → Security Validation → Command/Message Router
|
|||||||
| `user_domains` | 도메인 소유권 | user_id, domain, verified |
|
| `user_domains` | 도메인 소유권 | user_id, domain, verified |
|
||||||
| `server_orders` | 서버 주문 | user_id, spec_id, status |
|
| `server_orders` | 서버 주문 | user_id, spec_id, status |
|
||||||
| `server_specs` | 서버 스펙 | name, cpu, ram, disk, price |
|
| `server_specs` | 서버 스펙 | name, cpu, ram, disk, price |
|
||||||
|
| `domain_sessions` | 도메인 상담 세션 | user_id, status, collected_info, messages |
|
||||||
|
| `deposit_sessions` | 예치금 상담 세션 | user_id, status, collected_info, messages |
|
||||||
|
|
||||||
**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환
|
**AI Fallback:** OpenAI 미설정 시 Workers AI (Llama 3.1 8B) 자동 전환
|
||||||
|
|
||||||
@@ -266,29 +274,43 @@ Telegram Webhook → Security Validation → Command/Message Router
|
|||||||
|
|
||||||
### 4.1 Deposit System
|
### 4.1 Deposit System
|
||||||
|
|
||||||
**manage_deposit 도구 파라미터:**
|
**아키텍처 변경 (2026-02-05):**
|
||||||
```typescript
|
- **이전**: manage_deposit 도구 → 직접 코드 실행
|
||||||
{
|
- **현재**: manage_deposit 도구 → **Deposit Agent 위임** (세션 기반 상담)
|
||||||
action: 'balance' | 'account' | 'request' | 'history' | 'cancel' | 'pending' | 'confirm' | 'reject',
|
|
||||||
depositor_name?: string, // request용
|
**흐름:**
|
||||||
amount?: number, // request용
|
```
|
||||||
transaction_id?: number, // cancel, confirm, reject용
|
사용자: "잔액 확인해줘"
|
||||||
limit?: number // history용 (기본 10)
|
↓
|
||||||
}
|
메인 AI → manage_deposit(action="balance") 호출
|
||||||
|
↓
|
||||||
|
deposit-tool.ts: action → 자연어 변환 ("잔액 확인해줘")
|
||||||
|
↓
|
||||||
|
Deposit Agent (금융 상담사 페르소나)
|
||||||
|
- 세션 생성/조회 (D1, TTL 30분)
|
||||||
|
- OpenAI Function Calling (get_balance, get_account_info, request_deposit)
|
||||||
|
- 도구 실행 → executeDepositFunction()
|
||||||
|
↓
|
||||||
|
응답 반환 + 세션 저장
|
||||||
```
|
```
|
||||||
|
|
||||||
**action별 처리:**
|
**Deposit Agent Function Calling:**
|
||||||
| 함수 | 설명 | 권한 |
|
| 함수 | 설명 | 권한 |
|
||||||
|------|------|------|
|
|------|------|------|
|
||||||
| `get_balance` | 잔액 조회 | 모든 사용자 |
|
| `get_balance` | 잔액 조회 | 모든 사용자 |
|
||||||
| `get_account_info` | 입금 계좌 안내 | 모든 사용자 |
|
| `get_account_info` | 입금 계좌 안내 | 모든 사용자 |
|
||||||
| `request_deposit` | 입금 신고 | 모든 사용자 |
|
| `request_deposit` | 입금 신고 (금액+입금자명 필수) | 모든 사용자 |
|
||||||
| `get_transactions` | 거래 내역 | 모든 사용자 |
|
| `get_transactions` | 거래 내역 | 모든 사용자 |
|
||||||
| `cancel_transaction` | 입금 취소 | 모든 사용자 |
|
| `cancel_transaction` | 입금 취소 | 모든 사용자 |
|
||||||
| `get_pending_list` | 대기 목록 | 관리자 |
|
| `get_pending_list` | 대기 목록 | 관리자 |
|
||||||
| `confirm_deposit` | 입금 확인 | 관리자 |
|
| `confirm_deposit` | 입금 확인 | 관리자 |
|
||||||
| `reject_deposit` | 입금 거절 | 관리자 |
|
| `reject_deposit` | 입금 거절 | 관리자 |
|
||||||
|
|
||||||
|
**스마트 파싱:**
|
||||||
|
- "홍길동 5만원 입금" → 금액(50000) + 입금자명(홍길동) 즉시 추출 → confirming 상태
|
||||||
|
- "3만원 입금" → 금액만 추출 → collecting_name 상태 (입금자명 요청)
|
||||||
|
- Agent가 세션 컨텍스트 유지, 대화형 정보 수집
|
||||||
|
|
||||||
**자동 매칭 흐름:**
|
**자동 매칭 흐름:**
|
||||||
```
|
```
|
||||||
[시나리오 1: 사용자 먼저]
|
[시나리오 1: 사용자 먼저]
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { ERROR_MESSAGES } from './constants/messages';
|
|||||||
import { getServerSession, processServerConsultation } from './agents/server-agent';
|
import { getServerSession, processServerConsultation } from './agents/server-agent';
|
||||||
import { getTroubleshootSession, processTroubleshoot } from './agents/troubleshoot-agent';
|
import { getTroubleshootSession, processTroubleshoot } from './agents/troubleshoot-agent';
|
||||||
import { processDomainConsultation, hasDomainSession } from './agents/domain-agent';
|
import { processDomainConsultation, hasDomainSession } from './agents/domain-agent';
|
||||||
|
import { processDepositConsultation, hasDepositSession } from './agents/deposit-agent';
|
||||||
import { sendMessage } from './telegram';
|
import { sendMessage } from './telegram';
|
||||||
|
|
||||||
const logger = createLogger('openai');
|
const logger = createLogger('openai');
|
||||||
@@ -287,6 +288,29 @@ export async function generateOpenAIResponse(
|
|||||||
});
|
});
|
||||||
// Continue with normal flow if session check fails
|
// Continue with normal flow if session check fails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for active deposit session
|
||||||
|
try {
|
||||||
|
const hasDepositSess = await hasDepositSession(env.DB, telegramUserId);
|
||||||
|
|
||||||
|
if (hasDepositSess) {
|
||||||
|
logger.info('예치금 세션 감지, 예치금 에이전트로 라우팅', {
|
||||||
|
userId: telegramUserId
|
||||||
|
});
|
||||||
|
const depositResponse = await processDepositConsultation(env.DB, telegramUserId, userMessage, env);
|
||||||
|
|
||||||
|
// PASSTHROUGH: 무관한 메시지는 일반 처리로 전환
|
||||||
|
if (depositResponse !== '__PASSTHROUGH__') {
|
||||||
|
return depositResponse;
|
||||||
|
}
|
||||||
|
// Continue to normal flow below
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Deposit session check failed, continuing with normal flow', error as Error, {
|
||||||
|
telegramUserId
|
||||||
|
});
|
||||||
|
// Continue with normal flow if session check fails
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!env.OPENAI_API_KEY) {
|
if (!env.OPENAI_API_KEY) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { executeDepositFunction, type DepositContext } from '../agents/deposit-agent';
|
import { processDepositConsultation } from '../agents/deposit-agent';
|
||||||
import type {
|
import type {
|
||||||
Env,
|
Env,
|
||||||
DepositFunctionResult,
|
DepositFunctionResult,
|
||||||
@@ -159,61 +159,58 @@ export async function executeManageDeposit(
|
|||||||
const { action, depositor_name, amount, transaction_id, limit } = args;
|
const { action, depositor_name, amount, transaction_id, limit } = args;
|
||||||
logger.info('시작', { action, depositor_name, amount, userId: maskUserId(telegramUserId) });
|
logger.info('시작', { action, depositor_name, amount, userId: maskUserId(telegramUserId) });
|
||||||
|
|
||||||
if (!telegramUserId || !db) {
|
if (!telegramUserId || !db || !env) {
|
||||||
return '🚫 예치금 기능을 사용할 수 없습니다.';
|
return '🚫 예치금 기능을 사용할 수 없습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 사용자 조회
|
// Convert action to natural message and delegate to deposit agent
|
||||||
const user = await db.prepare(
|
const userMessage = buildDepositMessage(args);
|
||||||
'SELECT id FROM users WHERE telegram_id = ?'
|
|
||||||
).bind(telegramUserId).first<{ id: number }>();
|
|
||||||
|
|
||||||
if (!user) {
|
logger.info('예치금 에이전트로 위임', { userMessage, userId: maskUserId(telegramUserId) });
|
||||||
return '🚫 사용자 정보를 찾을 수 없습니다.';
|
|
||||||
|
// Delegate to deposit agent
|
||||||
|
const response = await processDepositConsultation(db, telegramUserId, userMessage, env);
|
||||||
|
|
||||||
|
if (response === '__PASSTHROUGH__') {
|
||||||
|
return '예치금 관련 요청을 이해하지 못했습니다.';
|
||||||
}
|
}
|
||||||
|
|
||||||
const isAdmin = telegramUserId === env?.DEPOSIT_ADMIN_ID;
|
return response;
|
||||||
const context: DepositContext = {
|
}
|
||||||
userId: user.id,
|
|
||||||
telegramUserId,
|
|
||||||
isAdmin,
|
|
||||||
db,
|
|
||||||
};
|
|
||||||
|
|
||||||
// action → executeDepositFunction 매핑
|
/**
|
||||||
const actionMap: Record<string, string> = {
|
* Convert manage_deposit tool arguments to natural language message for deposit agent
|
||||||
balance: 'get_balance',
|
*/
|
||||||
account: 'get_account_info',
|
function buildDepositMessage(args: {
|
||||||
request: 'request_deposit',
|
action: string;
|
||||||
history: 'get_transactions',
|
depositor_name?: string;
|
||||||
cancel: 'cancel_transaction',
|
amount?: number;
|
||||||
pending: 'get_pending_list',
|
transaction_id?: number;
|
||||||
confirm: 'confirm_deposit',
|
limit?: number
|
||||||
reject: 'reject_deposit',
|
}): string {
|
||||||
};
|
switch (args.action) {
|
||||||
|
case 'balance':
|
||||||
const funcName = actionMap[action];
|
return '잔액 확인해줘';
|
||||||
if (!funcName) {
|
case 'account':
|
||||||
return `🚫 알 수 없는 작업: ${action}`;
|
return '입금 계좌 알려줘';
|
||||||
|
case 'request':
|
||||||
|
if (args.amount && args.depositor_name) {
|
||||||
|
return `${args.depositor_name} ${args.amount}원 입금했어`;
|
||||||
|
} else if (args.amount) {
|
||||||
|
return `${args.amount}원 입금할게`;
|
||||||
}
|
}
|
||||||
|
return '입금 신고할게';
|
||||||
try {
|
case 'history':
|
||||||
const funcArgs: ManageDepositArgs = {
|
return args.limit ? `거래 내역 ${args.limit}개 보여줘` : '거래 내역 보여줘';
|
||||||
action: action as ManageDepositArgs['action']
|
case 'cancel':
|
||||||
};
|
return args.transaction_id ? `거래 ${args.transaction_id} 취소해줘` : '입금 취소할게';
|
||||||
if (depositor_name) funcArgs.depositor_name = depositor_name;
|
case 'pending':
|
||||||
if (amount) funcArgs.amount = Number(amount);
|
return '대기 중인 입금 목록 보여줘';
|
||||||
if (transaction_id) funcArgs.transaction_id = Number(transaction_id);
|
case 'confirm':
|
||||||
if (limit) funcArgs.limit = Number(limit);
|
return args.transaction_id ? `거래 ${args.transaction_id} 승인해줘` : '입금 승인할게';
|
||||||
|
case 'reject':
|
||||||
logger.info('executeDepositFunction 호출', { funcName, funcArgs });
|
return args.transaction_id ? `거래 ${args.transaction_id} 거절해줘` : '입금 거절할게';
|
||||||
const result = await executeDepositFunction(funcName, funcArgs, context);
|
default:
|
||||||
logger.info('결과', { result: JSON.stringify(result).slice(0, LOG_RESULT_MAX_LENGTH) });
|
return `예치금 ${args.action}`;
|
||||||
|
|
||||||
// 결과 포맷팅 (고정 형식)
|
|
||||||
return formatDepositResult(action, result);
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('예치금 처리 오류', error as Error);
|
|
||||||
return '🚫 예치금 처리 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user