Skip to main content
← AI Agent 아키텍처 개요
Agent가 맥락을 유지하고 과거 경험으로부터 학습하려면 메모리(Memory) 가 필요합니다. 인간의 기억 체계와 유사하게, Agent의 메모리도 여러 유형으로 나뉩니다.

왜 Agent Memory가 핵심인가

메모리 없는 Agent의 한계

메모리가 없는 Agent는 매 대화가 “첫 만남”처럼 시작 됩니다. 사용자가 누구인지, 이전에 어떤 대화를 나눴는지, 어떤 선호를 가지고 있는지 전혀 기억하지 못합니다.
주의 비유: 메모리 없는 Agent = 매일 기억을 잃는 직원. 어제 했던 일을 오늘 또 처음부터 설명해야 합니다. 고객이 “지난주에 말씀드린 환불 건이요”라고 하면, “어떤 환불 건이요?”라고 되물어야 합니다. 이런 Agent에게 복잡한 업무를 맡길 수 있을까요?
구체적으로 메모리가 없을 때 발생하는 문제는 다음과 같습니다:
문제설명영향
반복 질문매 세션마다 동일한 정보를 다시 요청사용자 피로도 급증
맥락 단절이전 대화와 연결된 후속 질문에 대응 불가복잡한 업무 처리 불가
개인화 불가사용자 선호/습관을 반영한 응답 생성 불가일률적이고 기계적인 경험
학습 불가과거 실수를 반복하고 성공 패턴을 축적하지 못함Agent 품질이 정체
멀티턴 실패긴 대화에서 초반 맥락을 잊어버림복잡한 작업 완료율 저하

메모리가 Agent 성능에 미치는 영향

업계 벤치마크와 실제 운영 데이터에 따르면, 적절한 메모리 시스템을 도입한 Agent는 다음과 같은 성능 개선을 보입니다:
  • 작업 완료율 +30%: 이전 맥락을 기억하므로 멀티스텝 작업에서 중간에 실패하는 빈도가 대폭 감소
  • 사용자 만족도 +40%: 반복 질문이 줄어들고 개인화된 응답을 제공하여 체감 품질 향상
  • 평균 대화 턴 수 -25%: 이미 알고 있는 정보를 다시 물어볼 필요가 없어 업무 처리 속도 향상
  • 재방문율 +50%: “기억해주는 Agent”에 대한 사용자 신뢰도 상승
참고 핵심: Memory는 Agent를 단순 도구에서 신뢰할 수 있는 동료 로 격상시키는 핵심 요소입니다. 단순 Q&A 챗봇이라면 메모리 없이도 가능하지만, 업무 자동화 Agent 를 목표로 한다면 메모리 설계가 필수입니다.

메모리 유형

Short-term Memory (단기 기억)

현재 대화의 히스토리(메시지 목록)를 의미합니다. LLM의 Context Window 크기에 의해 제한되며, 모든 Agent가 기본적으로 가지는 가장 기본적인 메모리입니다.

Context Window 관리 전략

LLM의 Context Window는 무한하지 않습니다. 대화가 길어질수록 토큰 예산이 소진되며, 이를 효과적으로 관리하는 전략이 필요합니다.
전략설명장점단점
Sliding Window최근 N개 메시지만 유지구현 간단오래된 중요 맥락 손실
Summarization오래된 대화를 요약하여 압축핵심 정보 보존요약 품질에 의존, 추가 LLM 호출 비용
Token Budget토큰 수 기준으로 관리정밀한 비용 제어메시지 중간에 잘릴 수 있음
Selective Retention중요도 점수 기반 선별 유지최적의 정보 밀도구현 복잡도 높음

LangGraph에서 대화 히스토리 관리

from langgraph.checkpoint.memory import MemorySaver
from langgraph.prebuilt import create_react_agent
from langchain_databricks import ChatDatabricks

# 모델 설정
model = ChatDatabricks(endpoint="databricks-claude-sonnet-4")

# 체크포인트로 대화 상태 자동 저장
checkpointer = MemorySaver()
agent = create_react_agent(model, tools=[], checkpointer=checkpointer)

# thread_id로 대화 세션 구분 — 같은 thread_id면 이전 대화를 기억
config = {"configurable": {"thread_id": "user-123-session-1"}}

# 첫 번째 대화
result1 = agent.invoke(
    {"messages": [("user", "저는 데이터 엔지니어 김철수입니다.")]},
    config
)

# 두 번째 대화 — 같은 thread_id이므로 이전 대화를 기억
result2 = agent.invoke(
    {"messages": [("user", "제 이름이 뭐였죠?")]},
    config
)
# → "김철수님이시죠" (이전 대화를 기억)

Context Window가 가득 찼을 때 전략

대화가 매우 길어져 Context Window 한계에 도달하면 세 가지 접근법을 선택할 수 있습니다: 1. 요약 후 교체 (추천)
from langchain_core.messages import SystemMessage

def summarize_and_compress(messages, model, max_tokens=50000):
    """오래된 메시지를 요약하여 Context Window 공간 확보"""
    if estimate_tokens(messages) < max_tokens:
        return messages

    # 오래된 메시지들을 요약
    old_messages = messages[:-10]  # 최근 10개는 유지
    recent_messages = messages[-10:]

    summary = model.invoke([
        SystemMessage(content="아래 대화를 핵심 정보만 간결하게 요약하세요."),
        *old_messages
    ])

    # 요약본 + 최근 메시지로 교체
    return [
        SystemMessage(content=f"[이전 대화 요약]\n{summary.content}"),
        *recent_messages
    ]
2. Sliding Window (단순하지만 효과적)
def sliding_window(messages, window_size=20):
    """최근 N개 메시지만 유지"""
    system_msgs = [m for m in messages if isinstance(m, SystemMessage)]
    conversation = [m for m in messages if not isinstance(m, SystemMessage)]
    return system_msgs + conversation[-window_size:]
3. 중요도 기반 선별 유지 (고급)
def selective_retention(messages, model, max_tokens=50000):
    """각 메시지의 중요도를 평가하여 선별 유지"""
    scored = []
    for msg in messages:
        # 숫자, 이름, 날짜 등 핵심 정보 포함 여부로 점수 부여
        score = calculate_importance(msg)
        scored.append((score, msg))

    scored.sort(key=lambda x: x[0], reverse=True)

    retained = []
    total_tokens = 0
    for score, msg in scored:
        tokens = estimate_tokens([msg])
        if total_tokens + tokens <= max_tokens:
            retained.append(msg)
            total_tokens += tokens

    # 시간순 재정렬
    return sort_by_timestamp(retained)

Long-term Memory (장기 기억)

세션을 넘어 영속적으로 저장되는 정보입니다. 일반적으로 Vector DB나 외부 데이터베이스로 구현하며, Agent가 관련성이 있을 때 과거 상호작용을 검색하여 활용합니다.

구현 아키텍처 3가지

아키텍처저장 방식검색 방식장점단점
Vector DB 기반대화/문서를 임베딩하여 벡터 저장의미적 유사도 검색자연어로 검색 가능, 유연함정확한 필터링 어려움
구조화 DB 기반(user_id, key, value) 테이블 저장SQL 쿼리로 정확 조회정확하고 빠른 조회의미적 검색 불가
하이브리드Vector DB + 구조화 DB 결합유사도 검색 + SQL 필터링두 장점 결합, 가장 실용적구현 복잡도 높음

Databricks Vector Search로 Long-term Memory 구현

from databricks.vector_search.client import VectorSearchClient
from datetime import datetime

vsc = VectorSearchClient()
index = vsc.get_index(
    endpoint_name="memory_vs_endpoint",
    index_name="catalog.schema.memory_index"
)

def store_memory(user_id: str, content: str, metadata: dict = None):
    """대화 내용을 임베딩하여 Vector Search에 저장"""
    timestamp = datetime.now().isoformat()
    record = {
        "id": f"{user_id}_{timestamp}",
        "text": content,
        "user_id": user_id,
        "timestamp": timestamp,
        "memory_type": "conversation",
    }
    if metadata:
        record.update(metadata)
    index.upsert([record])

def recall_memory(user_id: str, query: str, top_k: int = 5):
    """관련 기억을 의미 검색으로 가져오기"""
    results = index.similarity_search(
        query_text=query,
        columns=["text", "timestamp", "memory_type"],
        filters={"user_id": user_id},
        num_results=top_k
    )
    return results.get("result", {}).get("data_array", [])

# 사용 예시
store_memory("user-456", "고객이 환불 정책에 대해 문의함. 30일 이내 전액 환불 안내 완료.")
memories = recall_memory("user-456", "환불 관련 이전 문의")

Lakebase(PostgreSQL)로 구조화 메모리 구현

import psycopg2
from datetime import datetime

# Lakebase 연결 (Databricks 서버리스 PostgreSQL)
conn = psycopg2.connect(
    host="<lakebase-endpoint>.cloud.databricks.com",
    port=5432,
    dbname="agent_memory_db",
    user="token",
    password="<databricks-token>"
)
cursor = conn.cursor()

def store_user_preference(user_id: str, key: str, value: str):
    """사용자 프로필 메모리 저장 (UPSERT)"""
    cursor.execute("""
        INSERT INTO agent_memory (user_id, key, value, updated_at)
        VALUES (%s, %s, %s, NOW())
        ON CONFLICT (user_id, key)
        DO UPDATE SET value = %s, updated_at = NOW()
    """, (user_id, key, value, value))
    conn.commit()

def get_user_preferences(user_id: str) -> list:
    """사용자의 모든 저장된 선호/프로필 조회"""
    cursor.execute(
        "SELECT key, value FROM agent_memory WHERE user_id = %s ORDER BY updated_at DESC",
        (user_id,)
    )
    return cursor.fetchall()

# 사용 예시
store_user_preference("user-456", "preferred_language", "한국어")
store_user_preference("user-456", "department", "데이터 엔지니어링팀")
store_user_preference("user-456", "timezone", "Asia/Seoul")

prefs = get_user_preferences("user-456")
# → [("timezone", "Asia/Seoul"), ("department", "데이터 엔지니어링팀"), ...]

하이브리드 메모리 아키텍처 (권장)

실무에서 가장 효과적인 방식은 Vector Search + Lakebase를 함께 사용 하는 것입니다:
┌─────────────────────────────────────────────────────┐
│                 Agent System Prompt                  │
│                                                     │
│  [Profile Memory]    ← Lakebase (구조화 데이터)       │
│  이름: 김철수, 부서: DE팀, 언어: 한국어                │
│                                                     │
│  [Recent Context]    ← Short-term (현재 대화)         │
│  최근 3개 메시지                                      │
│                                                     │
│  [Relevant History]  ← Vector Search (의미 검색)      │
│  과거 유사한 대화 3건                                  │
└─────────────────────────────────────────────────────┘

Episodic Memory (에피소드 기억)

과거 문제 해결 과정의 기록입니다. “지난번에 비슷한 오류가 발생했을 때, 이렇게 해결했다…”와 같은 경험 기반 학습을 가능하게 합니다.

구현 패턴: MLflow Tracing 기반 경험 학습

Agent의 모든 실행은 MLflow Tracing을 통해 기록됩니다. 성공한 실행 경로와 실패한 경로를 분류하고, 유사한 상황에서 성공 패턴을 우선 추천하는 방식으로 Episodic Memory를 구현할 수 있습니다.
import mlflow
from mlflow.client import MlflowClient

client = MlflowClient()

def search_successful_episodes(query: str, top_k: int = 3) -> list:
    """과거 성공한 에피소드를 검색하여 참고"""
    # MLflow에서 성공한 실행 이력 조회
    runs = client.search_runs(
        experiment_ids=["<experiment_id>"],
        filter_string='attributes.status = "FINISHED" AND tags.outcome = "success"',
        order_by=["metrics.user_satisfaction DESC"],
        max_results=top_k
    )

    episodes = []
    for run in runs:
        episodes.append({
            "task": run.data.tags.get("task_description", ""),
            "approach": run.data.tags.get("approach", ""),
            "tools_used": run.data.tags.get("tools_used", ""),
            "outcome": run.data.tags.get("outcome", ""),
            "duration_sec": run.data.metrics.get("duration_sec", 0),
        })
    return episodes

def record_episode(task: str, approach: str, tools: list, outcome: str):
    """현재 에피소드를 기록하여 미래 참조용으로 저장"""
    with mlflow.start_run(tags={
        "task_description": task,
        "approach": approach,
        "tools_used": ",".join(tools),
        "outcome": outcome,
    }) as run:
        mlflow.log_metric("user_satisfaction", 1.0 if outcome == "success" else 0.0)

# 사용 예시: 유사 에피소드 검색 후 프롬프트에 주입
past_episodes = search_successful_episodes("ETL 파이프라인 오류 해결")
episode_context = "\n".join([
    f"- 과거 사례: {ep['task']}{ep['approach']} (결과: {ep['outcome']})"
    for ep in past_episodes
])
# → System Prompt에 [Past Experiences] 섹션으로 삽입

경험 기반 학습 아키텍처

[사용자 요청] → [유사 과거 에피소드 검색]

               성공 에피소드 발견?
              ┌── Yes ──┐── No ──┐
              ↓                   ↓
      성공 경로 우선 시도     기본 추론으로 진행
              ↓                   ↓
         결과 기록 ←──────── 결과 기록

      [새 에피소드로 저장]

Working Memory (작업 기억)

현재 진행 중인 추론을 위한 임시 메모장입니다. 계획(Plan), 중간 결과, 현재 상태 등을 저장하며, Agent State로 구현되는 경우가 많습니다.

LangGraph State를 활용한 Working Memory

from typing import TypedDict, Annotated
from langgraph.graph import StateGraph, END
import operator

class AgentState(TypedDict):
    """Agent의 Working Memory를 State로 관리"""
    messages: Annotated[list, operator.add]  # 대화 히스토리
    plan: list[str]                           # 현재 실행 계획
    current_step: int                         # 진행 중인 단계
    intermediate_results: dict                # 중간 결과 저장
    scratchpad: str                           # Agent의 메모장

def planning_node(state: AgentState) -> dict:
    """작업 계획을 수립하고 Working Memory에 저장"""
    plan = generate_plan(state["messages"][-1])
    return {
        "plan": plan,
        "current_step": 0,
        "scratchpad": f"총 {len(plan)}단계 계획 수립 완료"
    }

def execution_node(state: AgentState) -> dict:
    """현재 단계를 실행하고 중간 결과를 Working Memory에 기록"""
    step = state["plan"][state["current_step"]]
    result = execute_step(step, state["intermediate_results"])

    new_results = {** state["intermediate_results"], f"step_{state['current_step']}": result}
    return {
        "current_step": state["current_step"] + 1,
        "intermediate_results": new_results,
        "scratchpad": state["scratchpad"] + f"\n단계 {state['current_step']} 완료: {result}"
    }

# 그래프 구성
graph = StateGraph(AgentState)
graph.add_node("plan", planning_node)
graph.add_node("execute", execution_node)

Agent Scratchpad 패턴

Scratchpad 는 Agent가 추론 과정에서 중간 메모를 남기는 공간입니다. 복잡한 멀티스텝 작업에서 특히 유용합니다.
def build_scratchpad_prompt(state: AgentState) -> str:
    """Working Memory의 scratchpad를 프롬프트에 반영"""
    scratchpad = state.get("scratchpad", "")
    intermediate = state.get("intermediate_results", {})

    prompt_section = "[Working Memory - 현재 진행 상황]\n"
    if scratchpad:
        prompt_section += f"메모: {scratchpad}\n"
    if intermediate:
        prompt_section += "중간 결과:\n"
        for key, val in intermediate.items():
            prompt_section += f"  - {key}: {val}\n"

    return prompt_section

메모리 유형별 비교

메모리 유형저장 메커니즘Databricks 도구예시
Short-termLLM Context Window (메시지 리스트)ChatAgent messages 파라미터현재 대화의 이전 질문/답변 참조
Long-termVector DB / 외부 DBVector Search, Lakebase (PostgreSQL)3개월 전 고객 문의 내역 검색
Episodic구조화된 로그 저장소MLflow Tracking, Delta Table”지난번 ETL 실패는 스키마 변경 때문이었음”
WorkingAgent State (인메모리)LangGraph State, ChatAgent context현재 5단계 중 3단계 진행 중, 중간 계산 결과
성공 예시: 고객 지원 Agent가 “지난번에 문의하신 환불 건은 어떻게 되셨나요?”라고 물을 수 있으려면 Long-term Memory 가 필요합니다. 현재 세션의 대화만으로는 이전 세션의 맥락을 알 수 없기 때문입니다.
주의 현재 대부분의 Agent는 Short-term Memory만 가집니다. Long-term Memory는 Vector Search나 Lakebase(PostgreSQL)를 활용하여 별도 구현해야 합니다. Agent 프레임워크가 자동으로 제공하는 것이 아닙니다.

현업에서의 Agent Memory 구현 패턴

실제 기업들이 운영 환경에서 채택하는 메모리 패턴은 다음과 같이 분류됩니다:
패턴설명적합한 사용 사례복잡도
Session Memory대화 세션 동안만 유지, 세션 종료 시 소멸FAQ 챗봇, 단순 Q&A낮음
User Profile Memory사용자별 선호/이력을 영속적으로 저장개인화 서비스, CRM 연동 Agent중간
Knowledge MemoryAgent가 학습한 도메인 지식을 축적도메인 전문 Agent, 사내 정책 Agent높음
Team Memory여러 Agent가 공유하는 공동 기억Multi-Agent 시스템, Supervisor 패턴매우 높음

Session Memory

가장 기본적인 패턴으로, ChatAgent의 messages 파라미터가 이에 해당합니다. 별도 인프라 없이 구현 가능하며, 대부분의 Agent 프레임워크에서 기본 제공합니다.

User Profile Memory

사용자가 처음 접속했을 때 프로필 정보를 로드하여 System Prompt에 주입합니다. 대화 중 새롭게 파악된 정보(직책 변경, 새로운 선호 등)를 자동 갱신합니다.

Knowledge Memory

Agent가 업무를 수행하면서 새로 학습한 지식을 축적합니다. 예를 들어, “이 회사의 환불 정책은 구매 후 30일 이내”라는 정보를 한 번 확인하면 메모리에 저장하여, 이후 같은 질문에 즉시 답변합니다.

Team Memory

Multi-Agent 시스템에서 Supervisor Agent와 Worker Agent들이 공유하는 메모리입니다. 한 Agent가 발견한 정보를 다른 Agent가 활용할 수 있어, 중복 작업을 방지합니다.

Memory 솔루션 비교

솔루션유형특징Databricks 연동
Databricks Vector Search벡터 기반Unity Catalog 통합, 권한 자동 적용, 서버리스네이티브
Databricks Lakebase구조화 DBPostgreSQL 호환, 서버리스, ACID 트랜잭션네이티브
Redis인메모리 KV초고속 읽기/쓰기, TTL 자동 만료 지원External 연결 필요
Mem0AI 메모리 전용자동 메모리 추출/정리/검색, LLM 기반External 연결 필요
LangGraph Checkpointer상태 관리대화 상태 자동 저장/복구, thread 기반MLflow 통합 가능
Amazon Bedrock AgentCore MemoryAWS 관리형Bedrock Agent 전용 메모리 서비스External 연결 필요
Azure Cosmos DB Agent MemoryAzure 관리형글로벌 분산 DB 기반, 낮은 레이턴시External 연결 필요
참고 Databricks 환경에서의 권장 조합: Vector Search(의미 검색용 Long-term Memory) + Lakebase(구조화 프로필 데이터) + LangGraph Checkpointer(세션 관리). 이 조합은 별도 외부 서비스 없이 Databricks 플랫폼 내에서 완결됩니다.

메모리 설계 베스트 프랙티스

1. TTL(Time-To-Live) 설정

오래된 메모리는 가치가 떨어지거나 오히려 해로울 수 있습니다. 3년 전 선호했던 제품을 지금도 추천하면 오히려 역효과입니다.
메모리 유형권장 TTL근거
대화 히스토리 (세부)30일세부 대화는 빠르게 가치 감소
사용자 선호90일선호는 비교적 안정적이나 변할 수 있음
학습된 도메인 지식1년정책/규정 변경 주기에 맞춤
에피소드 (성공 패턴)6개월도구/API 변경으로 오래된 패턴이 무효화될 수 있음

2. 메모리 요약(Consolidation)

주기적으로 세부 기억을 요약하여 저장 공간을 절약하고 검색 품질을 높입니다.
def consolidate_memories(user_id: str, days_old: int = 30):
    """30일 이상 된 세부 기억을 요약하여 압축"""
    old_memories = get_memories_older_than(user_id, days_old)

    if not old_memories:
        return

    # LLM으로 오래된 기억들을 요약
    summary = model.invoke(f"""
    다음은 사용자 {user_id}의 과거 대화 기록입니다.
    핵심 정보만 추출하여 간결하게 요약하세요:
    {format_memories(old_memories)}
    """)

    # 요약본 저장 + 원본 삭제
    store_memory(user_id, summary.content, {"memory_type": "consolidated"})
    delete_memories(old_memories)

3. 선택적 망각(Selective Forgetting)

모든 것을 기억하는 것이 능사가 아닙니다. 관련 없는 기억은 삭제하고, 특히 개인정보 삭제 요청(GDPR Right to Erasure) 에 대응할 수 있어야 합니다.
def forget_user_data(user_id: str):
    """GDPR 삭제 요청 대응: 특정 사용자의 모든 메모리 삭제"""
    # Vector Search에서 삭제
    index.delete(filter={"user_id": user_id})

    # Lakebase에서 삭제
    cursor.execute("DELETE FROM agent_memory WHERE user_id = %s", (user_id,))
    conn.commit()

    # MLflow 에피소드에서 태그 익명화
    anonymize_episodes(user_id)

4. 메모리 범위(Scoping)

메모리의 접근 범위를 명확히 구분해야 합니다:
범위접근 주체예시
Session-level현재 대화 세션만현재 대화의 맥락
User-level특정 사용자의 모든 세션사용자 선호, 과거 이력
Agent-level특정 Agent의 모든 사용자Agent가 학습한 도메인 지식
Global-level모든 Agent, 모든 사용자공통 정책, 회사 규정

5. 검색 전략: 복합 랭킹

메모리 검색 시 Vector similarity만으로는 부족합니다. 여러 신호를 결합하여 최적의 기억을 찾아야 합니다.
def recall_with_ranking(user_id: str, query: str, top_k: int = 5):
    """복합 랭킹으로 최적의 메모리 검색"""
    # 1단계: Vector similarity로 후보 검색 (넉넉하게)
    candidates = recall_memory(user_id, query, top_k=top_k * 3)

    # 2단계: 복합 점수 계산
    scored = []
    for mem in candidates:
        similarity_score = mem["score"]  # 의미적 유사도
        recency_score = calculate_recency_weight(mem["timestamp"])  # 최신성
        importance_score = mem.get("importance", 0.5)  # 메타데이터 중요도

        # 가중 합산
        final_score = (
            0.5 * similarity_score +
            0.3 * recency_score +
            0.2 * importance_score
        )
        scored.append((final_score, mem))

    # 3단계: 최종 순위로 반환
    scored.sort(key=lambda x: x[0], reverse=True)
    return [mem for _, mem in scored[:top_k]]

메모리 안티패턴

실무에서 흔히 발견되는 메모리 설계 실수와 해결 방법입니다:
안티패턴문제해결
무한 축적메모리가 무한히 커져 검색 성능 저하, 비용 증가TTL 설정 + 주기적 Consolidation
Privacy 위반개인정보(PII)가 메모리에 영구 저장PII 마스킹, 삭제 정책, GDPR 대응 로직
Context Pollution관련 없는 기억이 프롬프트에 포함되어 답변 품질 저하관련도 임계값 설정 (예: similarity > 0.7만 포함)
메모리 충돌서로 모순되는 기억이 공존 (예: “부서: 마케팅” vs “부서: 영업”)최신 기억 우선(timestamp), 충돌 감지 및 해결 로직
과도한 의존메모리 시스템 장애 시 Agent 전체가 멈춤메모리를 graceful degradation으로 설계 (없어도 기본 동작)
비용 폭발모든 대화를 벡터 임베딩하여 저장하면 스토리지/컴퓨팅 비용 급증중요한 정보만 선별 저장, 요약 후 저장
주의 Context Pollution은 가장 흔한 안티패턴 입니다. 관련 없는 과거 기억이 프롬프트에 포함되면 LLM이 혼란을 일으켜 오히려 메모리가 없는 것보다 나쁜 결과를 만들 수 있습니다. 반드시 관련도 임계값 을 설정하세요.

Databricks에서 Agent Memory 구축 가이드

실전에서 Databricks 플랫폼 위에 Agent Memory를 단계별로 구축하는 방법입니다.

Step 1. Session Memory — 기본 제공

ChatAgent의 messages 파라미터를 활용하면 별도 구현 없이 세션 내 메모리를 사용할 수 있습니다.
from databricks.agents import ChatAgent

class MyAgent(ChatAgent):
    def predict(self, messages, context=None):
        # messages에 이전 대화 히스토리가 자동 포함됨
        # → 별도 메모리 구현 불필요 (세션 내 한정)
        response = self.model.invoke(messages)
        return response

Step 2. User Profile Memory — Lakebase 활용

-- Lakebase에 사용자 프로필 테이블 생성
CREATE TABLE agent_memory (
    user_id     VARCHAR(255) NOT NULL,
    key         VARCHAR(255) NOT NULL,
    value       TEXT,
    created_at  TIMESTAMP DEFAULT NOW(),
    updated_at  TIMESTAMP DEFAULT NOW(),
    expires_at  TIMESTAMP,  -- TTL 지원
    PRIMARY KEY (user_id, key)
);

-- 인덱스 추가
CREATE INDEX idx_memory_user ON agent_memory(user_id);
CREATE INDEX idx_memory_expires ON agent_memory(expires_at);

Step 3. Semantic Memory — Vector Search 활용

-- Delta Table로 메모리 저장소 생성
CREATE TABLE catalog.schema.conversation_memory (
    id          STRING,
    user_id     STRING,
    content     STRING,
    timestamp   TIMESTAMP,
    memory_type STRING,
    importance  DOUBLE DEFAULT 0.5
);

-- Vector Search Index 생성
CREATE VECTOR SEARCH INDEX memory_index
ON catalog.schema.conversation_memory (content)
USING MODEL `databricks-gte-large-en`
WITH (endpoint_name = "memory_vs_endpoint");

Step 4. 하이브리드 통합 — System Prompt에 메모리 주입

모든 메모리를 통합하여 Agent의 System Prompt에 주입하는 최종 단계입니다.
def build_memory_enhanced_prompt(user_id: str, current_query: str) -> str:
    """모든 메모리 소스를 결합하여 풍부한 프롬프트 생성"""

    # 1. User Profile Memory (Lakebase)
    profile = get_user_preferences(user_id)
    profile_section = "[사용자 프로필]\n"
    for key, value in profile:
        profile_section += f"- {key}: {value}\n"

    # 2. Relevant History (Vector Search)
    relevant_memories = recall_with_ranking(user_id, current_query, top_k=3)
    history_section = "[관련 과거 대화]\n"
    for mem in relevant_memories:
        history_section += f"- ({mem['timestamp']}): {mem['text']}\n"

    # 3. Episodic Memory (MLflow)
    episodes = search_successful_episodes(current_query, top_k=2)
    episode_section = "[참고할 과거 경험]\n"
    for ep in episodes:
        episode_section += f"- {ep['task']}: {ep['approach']}{ep['outcome']}\n"

    # 통합 System Prompt
    system_prompt = f"""당신은 전문 업무 지원 Agent입니다.

{profile_section}
{history_section}
{episode_section}

위 메모리를 참고하여 사용자에게 개인화되고 맥락에 맞는 답변을 제공하세요.
새롭게 파악된 중요 정보는 메모리에 저장하세요."""

    return system_prompt

고객 FAQ

”메모리 없이 Agent를 운영할 수 있나요?”

단순 Q&A는 가능합니다.”오늘 날씨 알려줘”, “이 함수 사용법 알려줘” 같은 단발성 질문에는 메모리가 필요 없습니다. 하지만 다음 중 하나라도 해당되면 메모리가 필수입니다:
  • 사용자별 개인화 가 필요한 경우
  • 이전 대화의 맥락을 이어가야 하는 경우
  • Agent가 과거 경험으로부터 학습 해야 하는 경우
  • 멀티스텝 작업 에서 중간 결과를 추적해야 하는 경우

”메모리 구현에 어떤 DB를 써야 하나요?”

Databricks 환경이라면 Vector Search + Lakebase 조합을 권장 합니다.
  • Vector Search: 자연어 기반 의미 검색. “지난번 환불 관련 대화” 같은 퍼지 검색에 적합. Unity Catalog 통합으로 권한 관리 자동.
  • Lakebase (PostgreSQL): 구조화된 데이터 저장. 사용자 프로필, 설정값 등 정확한 조회에 적합. 서버리스로 관리 부담 최소.
  • 두 가지를 결합 하면 “이름이 뭐였지?” (구조화 조회)와 “비슷한 문제를 겪은 적 있나?” (의미 검색)를 모두 처리할 수 있습니다.

”메모리 데이터는 어떻게 보호하나요?”

세 가지 계층으로 보호합니다:
  1. 접근 제어: Unity Catalog ACL을 통해 메모리 테이블/인덱스에 대한 읽기/쓰기 권한을 세밀하게 관리합니다. Agent별, 팀별로 접근 가능한 메모리 범위를 제한할 수 있습니다.
  2. PII 마스킹: 메모리 저장 전에 개인정보(이름, 전화번호, 주민번호 등)를 자동 탐지하여 마스킹합니다. Databricks의 AI Functions(ai_mask())를 활용하면 편리합니다.
  3. TTL 기반 삭제: 보존 기한이 지난 메모리는 자동 삭제하여 불필요한 데이터 보유를 방지합니다. GDPR/PIPA 등 개인정보 보호법 준수에 필수적입니다.

”메모리가 많아지면 성능이 느려지지 않나요?”

적절히 설계하면 문제가 되지 않습니다. 핵심 전략은 다음과 같습니다:
  • TTL + Consolidation: 오래된 세부 기억은 요약하고, 만료된 기억은 삭제
  • 관련도 임계값: Vector Search에서 유사도가 낮은 결과는 필터링
  • 인덱싱 최적화: Lakebase에 적절한 인덱스 설정, Vector Search에 메타데이터 필터 활용
  • 비동기 저장: 메모리 저장은 응답 반환 후 비동기로 처리하여 사용자 체감 지연 최소화

다음: Multi-Agent 패턴 →