Skip to main content
Summary

이 장에서 배울 것

이번 장에서는 에이전트가 무엇을 기억하고, 무엇을 버리고, 어떤 작업 상태를 저장해야 하는지 배운다. 핵심은 단순하다. 운영 가능한 에이전트는 모든 대화를 프롬프트에 넣지 않는다. 필요한 정보만 목적에 맞게 저장하고, 다시 꺼내 쓰고, 오래된 정보는 갱신하거나 삭제한다.

이 장을 마치면 다음을 설명할 수 있어야 한다.

  • conversation memory, task state, long-term memory, vector store, artifact storage의 차이

  • Redis, SQL DB, 벡터 DB, 객체 저장소를 어디에 쓰는지

  • 최근성, 관련성, 중요도 기준으로 메모리를 읽고 쓰는 방법

  • 중복 메모리, 메모리 폭주, 오래된 정보 문제를 막는 정책

  • pending, running, waiting_approval, failed, completed 같은 작업 상태 설계

  • FAQ agent에 사용자 선호도와 최근 질문 상태를 저장하는 방법

  • 개인정보와 민감 정보를 저장하지 않거나 만료시키는 방법

Memory와 state 설계

왜 이 주제가 중요한가

초기 LLM 앱은 대화 기록을 통째로 프롬프트에 붙이는 방식으로도 동작한다. 사용자가 몇 번 질문하고 답변을 받는 정도라면 큰 문제가 없다. 하지만 에이전트가 도구를 호출하고, 여러 단계를 수행하고, 중간에 승인을 기다리고, 나중에 다시 작업을 이어가야 한다면 이야기가 달라진다.

예를 들어 문서 검토 에이전트가 있다고 하자. 사용자는 오전에 계약서를 올리고, 에이전트는 조항을 분석하다가 "외부 공유 전 승인 필요" 상태로 멈춘다. 오후에 사용자가 다시 접속했을 때 에이전트는 다음을 알아야 한다.

  • 어떤 문서를 처리 중이었는가

  • 분석은 어디까지 끝났는가

  • 어떤 도구를 호출했는가

  • 무엇 때문에 승인을 기다리는가

  • 사용자가 이전에 선호한다고 말한 답변 형식은 무엇인가

  • 저장하면 안 되는 개인정보는 무엇인가

이 정보를 모두 대화 기록에서 다시 추론하게 만들면 불안정하다. 모델이 놓칠 수 있고, 비용도 늘어나며, 같은 작업을 반복할 수 있다. 그래서 에이전트에는 memory와 state가 필요하다.

메모리는 "참고할 정보"에 가깝다. 상태는 "현재 작업이 어디까지 진행되었는지"를 나타낸다. 둘을 섞으면 에이전트는 쉽게 혼란스러워진다.

핵심 개념 설명

Memory는 모든 것을 기억하는 기능이 아니다

에이전트의 memory는 인간의 기억처럼 막연한 개념이 아니다. 시스템 설계 관점에서 memory는 다음 질문에 답하는 저장 구조다.

  • 왜 저장하는가

  • 어디에 저장하는가

  • 언제 다시 읽는가

  • 얼마나 오래 보관하는가

  • 누가 삭제할 수 있는가

  • 저장하면 안 되는 정보는 무엇인가

따라서 좋은 memory 설계는 "많이 기억하는 설계"가 아니다. 목적에 맞게 적게 기억하는 설계다.

conversation memory: 대화 흐름을 위한 기억

conversation memory는 최근 대화의 흐름을 유지하기 위한 정보다. 사용자가 "방금 말한 방식으로 다시 정리해줘"라고 말했을 때 필요한 맥락이 여기에 해당한다.

보통 저장 대상은 다음과 같다.

  • 최근 사용자 질문

  • 최근 에이전트 답변 요약

  • 현재 대화의 주제

  • 사용자가 방금 선택한 옵션

이 정보는 오래 보관할 필요가 없는 경우가 많다. 그래서 빠른 읽기와 쓰기가 가능한 Redis 같은 인메모리 저장소에 잘 맞는다. Redis는 데이터를 메모리에 저장해 빠르게 읽고 쓰는 저장소다. 세션 정보, 최근 대화 상태, 임시 캐시에 자주 사용된다.

conversation memory는 보통 만료 시간을 둔다. 예를 들어 FAQ agent라면 최근 질문 상태를 30분 또는 24시간 후 삭제할 수 있다.

task state: 작업 진행 상태

task state는 에이전트가 수행 중인 작업의 현재 위치다. 이것은 단순한 기억이 아니라 운영 상태다.

예를 들어 문서 요약 작업의 task state는 다음과 같을 수 있다.

{
  "task_id": "task_123",
  "user_id": "user_7",
  "status": "waiting_approval",
  "current_step": "send_summary_email",
  "input_artifact_id": "file_456",
  "last_tool_call": "draft_email",
  "approval_reason": "external_send_requires_approval",
  "updated_at": "2026-05-18T10:30:00Z"
}

이 정보는 재시작 후에도 복구되어야 한다. 서버가 꺼졌다 켜져도 작업이 사라지면 안 된다. 그래서 SQL DB 같은 영속 저장소에 저장하는 것이 일반적이다. SQL DB는 테이블 구조로 데이터를 저장하고, 검색과 갱신, 트랜잭션 처리에 강하다.

long-term memory: 장기 선호와 반복 정보

long-term memory는 여러 세션에 걸쳐 유지할 가치가 있는 정보다. 예를 들어 다음과 같은 정보가 있다.

  • 사용자는 답변을 표 형식보다 짧은 목록으로 선호한다.

  • 사용자는 기술 용어를 쉽게 풀어 설명해달라고 요청했다.

  • 사용자는 특정 제품군에 대한 FAQ를 자주 묻는다.

하지만 장기 기억은 위험하다. 사용자가 한 번 말한 내용을 영구적인 사실처럼 저장하면 안 된다. "오늘은 짧게 답해줘"는 일시적 선호일 수 있다. 반면 "앞으로 답변은 한국어로 해줘"는 장기 선호일 수 있다.

장기 기억에는 보존 기간과 갱신 규칙이 필요하다. 또한 사용자가 확인, 수정, 삭제할 수 있어야 한다.

vector store: 의미로 검색하는 지식 저장소

vector store는 문서나 문장을 숫자 배열인 벡터로 바꿔 저장한 뒤, 의미가 비슷한 내용을 찾아주는 저장소다. 키워드가 정확히 일치하지 않아도 관련 문서를 찾을 수 있다.

예를 들어 사용자가 "환불은 언제까지 돼?"라고 물었을 때 FAQ 문서에 "반품 및 환불 가능 기간"이라는 표현이 있어도 관련 내용을 찾을 수 있다.

벡터 DB는 다음에 적합하다.

  • FAQ 문서 검색

  • 사내 지식 베이스 검색

  • 제품 매뉴얼 검색

  • 이전 사례 검색

다만 vector store를 사용자 기억 저장소처럼 무분별하게 쓰면 안 된다. 검색 결과에는 오래된 내용이 섞일 수 있고, 비슷하지만 틀린 문서가 선택될 수 있다. 따라서 문서 버전, 출처, 갱신일을 함께 저장해야 한다.

artifact storage: 파일과 결과물 저장

artifact는 작업 중 생성되거나 업로드된 결과물이다. 문서 파일, 이미지, PDF, 변환된 텍스트, 요약본, 분석 리포트가 여기에 해당한다.

이런 파일은 DB 테이블에 직접 넣기보다 객체 저장소에 두는 경우가 많다. 객체 저장소는 파일을 객체 단위로 저장하는 공간이다. 예를 들어 S3 같은 방식의 저장소를 생각하면 된다.

DB에는 파일 자체가 아니라 다음 메타데이터를 저장한다.

  • artifact_id

  • 파일 위치

  • 파일 형식

  • 소유자

  • 생성 시간

  • 보존 기간

  • 접근 권한

이렇게 하면 대용량 파일과 작업 상태를 분리할 수 있다.

저장소 매핑 정리

구분목적예시적합한 저장소
conversation memory최근 대화 흐름 유지최근 질문, 현재 주제Redis
task state작업 진행 상태 복구pending, running, failedSQL DB
long-term memory장기 선호 저장답변 형식 선호, 언어 선호SQL DB 또는 별도 프로필 DB
vector store의미 기반 검색FAQ, 매뉴얼, 지식 문서벡터 DB
artifact storage파일과 결과물 보관PDF, 요약본, 리포트객체 저장소

쉬운 비유 또는 사례

에이전트를 사무실 직원에 비유해보자.

  • conversation memory는 책상 위 메모지다. 지금 통화 중인 내용이나 방금 받은 요청을 적어둔다. 오래 보관하지 않는다.

  • task state는 업무 관리 보드다. 어떤 일이 접수되었고, 진행 중인지, 승인을 기다리는지 표시한다.

  • long-term memory는 고객 프로필이다. 고객이 선호하는 보고서 형식이나 자주 요청하는 업무를 기록한다.

  • vector store는 회사 자료실이다. 필요한 매뉴얼과 규정을 찾아본다.

  • artifact storage는 문서 보관함이다. 실제 파일과 결과물을 보관한다.

문제는 모든 것을 책상 위 메모지에 쌓아둘 때 생긴다. 처음에는 편하지만 곧 필요한 정보를 찾기 어려워진다. 반대로 모든 통화 내용을 영구 고객 프로필에 저장하는 것도 위험하다. 사소한 말이 오래 남아 잘못된 판단의 근거가 될 수 있다.

좋은 에이전트는 정보를 "어디에 놓을지"를 안다.

메모리를 읽고 쓰는 기준

최근성

최근성은 얼마나 최근에 나온 정보인지 보는 기준이다. 현재 대화에서 방금 나온 말은 대개 중요하다. 예를 들어 사용자가 "이번 답변은 표로 정리해줘"라고 했다면 지금 답변에는 반영해야 한다.

하지만 최근 정보가 항상 장기 기억이 되는 것은 아니다. "이번만 표로 정리해줘"는 conversation memory에 남기고, 세션이 끝나면 삭제하는 편이 낫다.

관련성

관련성은 현재 작업과 얼마나 연결되어 있는지 보는 기준이다. 사용자가 환불 정책을 물었다면 배송 정책 문서보다 환불 문서가 더 관련 있다.

벡터 검색은 관련성 판단에 도움을 준다. 하지만 최종 판단은 검색 점수만으로 끝내면 안 된다. 문서의 최신성, 권한, 출처도 함께 확인해야 한다.

중요도

중요도는 나중에 다시 쓸 가치가 있는지 보는 기준이다. 예를 들어 "나는 한국어 답변을 선호한다"는 장기 기억 후보가 될 수 있다. 반면 "지금 카페에 있어" 같은 말은 대부분 저장할 필요가 없다.

장기 기억으로 저장하기 전에는 다음 질문을 통과해야 한다.

  • 사용자의 반복 경험을 개선하는가

  • 민감 정보가 아닌가

  • 사용자가 예상할 수 있는 저장인가

  • 나중에 바뀔 수 있는 정보인가

  • 만료 기간을 둘 수 있는가

메모리 문제와 처리 정책

중복 메모리

같은 선호가 여러 번 저장되면 에이전트가 충돌할 수 있다.

예를 들어 다음 기억이 함께 있다면 문제가 된다.

  • 사용자는 짧은 답변을 선호한다.

  • 사용자는 자세한 설명을 선호한다.

  • 사용자는 표 형식을 선호한다.

중복을 줄이려면 새 메모리를 저장하기 전에 기존 메모리와 비교해야 한다. 같은 종류의 선호라면 새 값을 추가하지 말고 갱신한다.

정책 예시는 다음과 같다.

같은 user_id와 preference_type이 이미 있으면 insert하지 않고 update한다.
이전 값은 history에 남기되 active=false로 바꾼다.

메모리 폭주

메모리 폭주는 너무 많은 정보가 저장되어 검색과 판단을 방해하는 현상이다. 모든 대화, 모든 도구 결과, 모든 중간 생각을 저장하면 빠르게 발생한다.

예방 정책은 간단하다.

  • 저장 가능한 정보 유형을 미리 정한다.

  • 기본값은 저장하지 않음으로 둔다.

  • 임시 정보에는 TTL, 즉 만료 시간을 둔다.

  • 긴 대화는 원문이 아니라 요약만 저장한다.

  • 도구 결과는 필요한 필드만 저장한다.

중요한 원칙은 "혹시 모르니 저장"을 피하는 것이다. 운영 시스템에서 이 말은 곧 비용, 보안, 품질 문제로 돌아온다.

오래된 정보

오래된 정보는 틀린 정보보다 더 위험할 수 있다. 예전에는 맞았지만 지금은 틀린 답을 만들기 때문이다.

처리 정책은 다음과 같다.

  • 메모리에 created_at, updated_at, expires_at을 둔다.

  • 지식 문서에는 버전과 갱신일을 둔다.

  • 오래된 장기 기억은 재확인 질문을 한다.

  • 정책 문서는 최신 버전만 검색 대상으로 둔다.

  • 사용자가 수정하거나 삭제할 수 있는 경로를 제공한다.

예를 들어 에이전트가 "이전에는 이메일 요약을 선호하셨습니다. 이번에도 이메일 형식으로 정리할까요?"라고 묻는 방식이 좋다. 오래된 기억을 확정 사실처럼 사용하지 않는 것이다.

state machine으로 작업 상태 설계하기

state machine은 작업이 가질 수 있는 상태와 이동 규칙을 정의하는 방식이다. 어렵게 생각할 필요 없다. "이 작업은 지금 어디에 있고, 다음에 어디로 갈 수 있는가"를 정하는 표다.

기본 상태는 다음처럼 시작할 수 있다.

상태의미
pending작업이 접수되었지만 아직 시작하지 않음
running에이전트가 작업 수행 중
waiting_approval사용자 또는 관리자의 승인을 기다림
failed오류로 중단됨
completed정상 완료됨

상태 이동은 제한되어야 한다.

pending -> running
running -> waiting_approval
running -> completed
running -> failed
waiting_approval -> running
waiting_approval -> failed
failed -> pending 또는 running

모든 상태에서 아무 상태로 이동할 수 있게 만들면 운영 중 원인을 추적하기 어렵다. 특히 승인 대기 상태를 건너뛰고 외부 발송이나 결제가 실행되면 심각한 사고가 된다.

재시작 후 복구 가능한 구조

에이전트는 언제든 중단될 수 있다. 서버 배포, 네트워크 오류, 도구 호출 실패, 시간 초과가 발생한다. 그래서 task state는 반드시 저장소에 남아야 한다.

최소한 다음 필드를 저장한다.

{
  "task_id": "task_123",
  "status": "running",
  "current_step": "search_faq",
  "input_summary": "사용자가 환불 기간을 질문함",
  "memory_refs": ["mem_10"],
  "artifact_refs": [],
  "retry_count": 1,
  "last_error": null,
  "created_at": "2026-05-18T10:00:00Z",
  "updated_at": "2026-05-18T10:01:00Z"
}

복구 로직은 다음 순서로 동작한다.

  1. running 상태였지만 오래 멈춘 작업을 찾는다.

  2. 마지막 current_step을 확인한다.

  3. 이미 완료된 도구 호출인지 확인한다.

  4. 재시도 가능한 작업이면 다시 실행한다.

  5. 위험 작업이면 waiting_approval로 전환한다.

  6. 재시도 횟수를 넘으면 failed로 전환하고 사용자에게 알린다.

여기서 중요한 것은 도구 호출의 중복 실행을 막는 것이다. 같은 이메일을 두 번 보내거나 같은 결제를 두 번 실행하면 안 된다. 따라서 외부 효과가 있는 작업에는 idempotency key를 둔다. idempotency key는 같은 요청이 반복되어도 한 번만 처리되게 하는 식별자다.

직접 해보는 실습: FAQ agent에 memory와 state 추가하기

이번 실습에서는 FAQ agent에 두 가지 기능을 추가한다.

첫째, 사용자의 답변 선호도를 저장한다. 둘째, 최근 질문 상태를 저장한다. 단, 개인정보와 민감 정보는 저장하지 않거나 만료시킨다.

1단계: 저장할 정보 정하기

저장 대상은 작게 시작한다.

정보저장 여부저장 위치보존 기간
최근 질문저장Redis24시간
현재 FAQ 주제저장Redis24시간
답변 길이 선호저장 가능SQL DB사용자가 삭제할 때까지 또는 180일
답변 언어 선호저장 가능SQL DB사용자가 삭제할 때까지 또는 180일
주민등록번호, 카드번호저장 금지없음없음
이메일, 전화번호기본 저장 금지필요 시 별도 동의짧은 기간

2단계: 간단한 스키마 만들기

사용자 선호도 테이블 예시는 다음과 같다.

CREATE TABLE user_preferences (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  preference_type TEXT NOT NULL,
  preference_value TEXT NOT NULL,
  source TEXT NOT NULL,
  active BOOLEAN NOT NULL,
  created_at TIMESTAMP NOT NULL,
  updated_at TIMESTAMP NOT NULL,
  expires_at TIMESTAMP
);

최근 질문 상태는 Redis에 다음처럼 저장할 수 있다.

{
  "user_id": "user_7",
  "last_question": "환불은 언제까지 가능한가요?",
  "current_topic": "refund_policy",
  "last_answer_id": "ans_91"
}

키는 예를 들어 faq_session:user_7로 만들고 TTL을 24시간으로 설정한다.

3단계: 저장 전 필터링하기

에이전트가 메모리를 저장하기 전에 다음 검사를 수행한다.

1. 민감 정보가 포함되어 있는가?
2. 일시적 요청인가, 장기 선호인가?
3. 기존 선호와 충돌하는가?
4. 저장 목적이 명확한가?
5. 만료 기간이 설정되어 있는가?

예를 들어 사용자가 "앞으로는 짧게 답해줘"라고 말하면 장기 선호 후보가 될 수 있다. 하지만 "이번 답변만 짧게 해줘"는 최근 대화 상태로만 처리한다.

4단계: 응답 생성 시 읽기 순서 정하기

FAQ agent는 답변 전에 다음 순서로 정보를 읽는다.

  1. Redis에서 최근 질문과 현재 주제를 읽는다.

  2. SQL DB에서 active 상태의 사용자 선호를 읽는다.

  3. 벡터 DB에서 관련 FAQ 문서를 검색한다.

  4. 만료된 선호는 사용하지 않는다.

  5. 충돌하는 선호가 있으면 사용자에게 확인한다.

이 순서가 중요한 이유는 메모리마다 역할이 다르기 때문이다. 최근 질문은 대화 흐름을 보완하고, 장기 선호는 답변 형식을 조정하며, 벡터 검색 결과는 사실 근거를 제공한다.

5단계: 테스트 질문

다음 입력으로 동작을 확인해보자.

사용자: 앞으로 답변은 짧게 핵심만 알려줘.
사용자: 환불은 언제까지 가능해?
사용자: 방금 답변을 조금 더 자세히 설명해줘.
사용자: 이번만 표로 정리해줘.
사용자: 내 카드번호는 1234-...야. 기억해줘.

기대 결과는 다음과 같다.

  • "짧게 핵심만"은 장기 선호로 저장 후보가 된다.

  • 환불 질문은 최근 질문 상태로 저장된다.

  • "방금 답변"은 Redis의 최근 상태를 활용한다.

  • "이번만 표로"는 장기 선호로 저장하지 않는다.

  • 카드번호는 저장하지 않고, 민감 정보는 보관할 수 없다고 안내한다.

흔한 실수와 오류 분석

실수 1: 대화 전체를 계속 프롬프트에 붙인다

가장 흔한 실수다. 구현은 쉽지만 비용이 늘고, 오래된 말이 현재 판단을 방해한다. 긴 대화는 요약하고, 현재 작업에 필요한 부분만 가져와야 한다.

실수 2: memory와 state를 같은 곳에 섞어 저장한다

사용자 선호와 작업 상태를 같은 JSON 덩어리에 넣으면 나중에 복구와 삭제가 어려워진다. "사용자가 선호하는 답변 형식"과 "문서 발송 승인을 기다리는 상태"는 성격이 다르다. 저장 위치와 보존 기간도 달라야 한다.

실수 3: 벡터 DB를 진실 저장소처럼 믿는다

벡터 검색은 관련 문서를 찾는 기술이지 정답을 보장하는 기술이 아니다. 검색된 문서가 오래되었거나 권한이 맞지 않거나 질문과 미묘하게 다를 수 있다. 답변에는 문서의 버전과 출처를 확인하는 단계가 필요하다.

실수 4: 삭제 정책이 없다

저장만 있고 삭제가 없으면 운영 리스크가 커진다. 만료 시간, 사용자 삭제 요청, 관리자 삭제, 법적 보존 기간을 미리 정해야 한다.

실수 5: 재시작 복구를 나중으로 미룬다

개발 환경에서는 모든 것이 한 번에 끝나는 것처럼 보인다. 실제 운영에서는 중간 실패가 자주 발생한다. task state를 저장하지 않으면 사용자는 같은 요청을 반복해야 하고, 에이전트는 같은 도구를 다시 호출할 수 있다.

요약

에이전트의 memory는 모든 것을 기억하는 기능이 아니다. 목적에 맞게 저장하고, 필요할 때 꺼내고, 더 이상 필요 없으면 삭제하는 구조다.

conversation memory는 최근 대화 흐름을 유지한다. task state는 작업이 어디까지 진행되었는지 기록한다. long-term memory는 반복적으로 유용한 사용자 선호를 저장한다. vector store는 의미 기반으로 지식 문서를 검색한다. artifact storage는 파일과 결과물을 보관한다.

운영 가능한 에이전트는 최근성, 관련성, 중요도를 기준으로 메모리를 읽고 쓴다. 중복 메모리, 메모리 폭주, 오래된 정보는 정책으로 막아야 한다. 또한 작업 상태를 state machine으로 정의해 재시작 후에도 복구할 수 있어야 한다.

퀴즈

  1. conversation memory와 long-term memory의 차이는 무엇인가?

  2. task state를 Redis에만 저장하면 어떤 문제가 생길 수 있는가?

  3. vector store가 정답을 보장하지 않는 이유는 무엇인가?

  4. "이번만 짧게 답해줘"와 "앞으로 짧게 답해줘"는 각각 어디에 저장하는 것이 적절한가?

  5. waiting_approval 상태가 필요한 이유는 무엇인가?

  6. 메모리 폭주를 막기 위한 정책을 세 가지 말해보라.

  7. 에이전트 재시작 후 작업을 복구하려면 task state에 어떤 필드가 필요할까?

체크리스트

  • 저장할 memory 유형을 구분했는가?

  • conversation memory에 만료 시간을 설정했는가?

  • task state를 영속 저장소에 저장하는가?

  • long-term memory 저장 기준을 정의했는가?

  • 민감 정보 저장 금지 정책이 있는가?

  • 사용자 선호의 수정과 삭제 방법이 있는가?

  • 벡터 DB 문서에 출처, 버전, 갱신일이 있는가?

  • 중복 메모리 갱신 정책이 있는가?

  • pending, running, waiting_approval, failed, completed 상태를 정의했는가?

  • 재시도와 중복 실행 방지 방식을 정했는가?

다음 장 연결

이번 장에서는 에이전트가 기억과 상태를 관리하는 방법을 다뤘다. 이제 에이전트는 최근 대화, 사용자 선호, 작업 상태, 지식 문서, 결과물을 구분해서 다룰 수 있다.

다음 장에서는 이 기반 위에서 더 복잡한 실행 구조를 다룬다. 하나의 에이전트가 모든 판단을 하는 대신, planner가 계획을 세우고, router가 적절한 경로를 고르며, 여러 subagent가 역할을 나누는 구조를 살펴본다. 이때도 memory와 state는 중심에 있다. 계획은 상태를 바꾸고, 라우팅은 필요한 기억을 읽으며, 여러 에이전트는 같은 작업 상태를 기준으로 협업해야 한다.

책 목록

AI 교육 6권 : 에이전트 설계·배포: 도구를 쓰는 LLM을 운영 가능한 시스템으로 만드는 법

전체 목차 보기

이 노트는 보도·비평·교육·연구 목적으로 작성되었습니다. 저작권 문의가 있으시면 에서 알려주세요.

틸노트는 사용자가 AI를 활용해 노트를 쉽게 작성하거나 편집할 수 있는 서비스입니다.

AI 스퀘어 단톡방에 함께해요

AI 활용과 틸노트 사용 팁을 나누는 오픈채팅방입니다. 입장 비밀번호는 tilnote입니다.

참여하기