Files
telegram-bot-workers/src/tools/memory-tool.ts
kappa 860e36a688 feat: add memory system and troubleshoot agent
Memory System:
- Add category-based memory storage (company, tech, role, location, server)
- Silent background saving via saveMemorySilently()
- Category-based overwrite (same category replaces old memory)
- Server-related pattern detection (AWS, GCP, k8s, traffic info)
- Memory management tool (list, delete)

Troubleshoot Agent:
- Session-based troubleshooting conversation (KV, 1h TTL)
- 20-year DevOps/SRE expert persona
- Support for server/infra, domain/DNS, code/deploy, network, database issues
- Internal tools: search_solution (Brave), lookup_docs (Context7)
- Auto-trigger on error-related keywords
- Session completion and cancellation support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 14:28:22 +09:00

141 lines
3.9 KiB
TypeScript

import type { Env, ManageMemoryArgs } from '../types';
import {
saveMemory,
getMemories,
deleteMemory,
deleteMemoryByContent,
} from '../services/memory-service';
import { createLogger, maskUserId } from '../utils/logger';
const logger = createLogger('memory-tool');
export const manageMemoryTool = {
type: 'function',
function: {
name: 'manage_memory',
description:
'사용자 기억 조회/삭제 전용. list: 사용자가 "내 기억", "뭘 기억해?" 요청 시. delete: 사용자가 "잊어줘" 요청 시. save는 시스템이 자동 처리하므로 호출하지 마세요.',
parameters: {
type: 'object',
properties: {
action: {
type: 'string',
enum: ['save', 'list', 'delete'],
description: 'save=저장, list=조회, delete=삭제',
},
content: {
type: 'string',
description: '저장할 내용 또는 삭제할 내용 (검색어)',
},
memory_id: {
type: 'number',
description: '삭제할 기억의 ID (선택)',
},
},
required: ['action'],
},
},
};
/**
* 기억 관리 도구 실행
*/
export async function executeManageMemory(
args: ManageMemoryArgs,
_env?: Env,
telegramUserId?: string,
db?: D1Database
): Promise<string> {
const { action, content, memory_id } = args;
logger.info('시작', {
action,
hasContent: !!content,
memoryId: memory_id,
userId: maskUserId(telegramUserId),
});
if (!telegramUserId || !db) {
return '🚫 기억 기능을 사용할 수 없습니다.';
}
// 사용자 조회
const user = await db
.prepare('SELECT id FROM users WHERE telegram_id = ?')
.bind(telegramUserId)
.first<{ id: number }>();
if (!user) {
return '🚫 사용자 정보를 찾을 수 없습니다.';
}
const userId = user.id;
try {
switch (action) {
case 'save': {
if (!content) {
return '🚫 저장할 내용을 입력해주세요.';
}
await saveMemory(db, userId, content);
// 자동 저장 시 AI가 이 결과를 사용자에게 전달하지 않도록 내부 마커 사용
return '[SAVED]';
}
case 'list': {
const memories = await getMemories(db, userId);
if (memories.length === 0) {
return '📋 저장된 기억이 없습니다.';
}
const memoryList = memories
.map((m, index) => `${index + 1}. ${m.content} (ID: ${m.id})`)
.join('\n');
return `📋 저장된 기억 (${memories.length}개)\n\n${memoryList}`;
}
case 'delete': {
// ID로 삭제
if (memory_id) {
const deletedCount = await deleteMemory(db, userId, memory_id);
if (deletedCount === 0) {
return `🚫 ID ${memory_id}에 해당하는 기억을 찾을 수 없습니다.`;
}
return `✅ 기억을 삭제했습니다 (ID: ${memory_id})`;
}
// 내용 검색으로 삭제
if (content) {
const deletedContents = await deleteMemoryByContent(
db,
userId,
content
);
if (deletedContents.length === 0) {
return `🚫 "${content}"와 일치하는 기억이 없습니다.`;
}
const deletedList = deletedContents
.map((c, i) => `${i + 1}. ${c}`)
.join('\n');
return `${deletedContents.length}개의 기억을 삭제했습니다:\n\n${deletedList}`;
}
return '🚫 삭제할 기억의 ID 또는 검색어를 입력해주세요.';
}
default:
return `🚫 알 수 없는 작업: ${action}`;
}
} catch (error) {
logger.error('기억 처리 오류', error as Error, { action });
return '🚫 기억 처리 중 오류가 발생했습니다. 잠시 후 다시 시도해주세요.';
}
}