본문 바로가기
WIKI 기술 지식 베이스

AI 메모리

원문 보기 위키 갱신

AI 메모리 (AI Memory)

AI 에이전트는 매 세션을 백지 상태에서 시작해요. 영구 메모리 시스템을 갖추면 에이전트가 배운 교훈을 저장하고, 벡터 검색으로 관련 맥락을 다시 꺼내 쓸 수 있어요. Turso의 임베디드 데이터베이스가 내장 벡터 함수까지 갖추고 있어서, 로컬 파일 하나로 이 모든 게 가능해요.

출처: 문서

본문

AI 에이전트는 매번 세션을 처음부터 다시 시작해요. 영구 메모리 시스템을 갖추면 에이전트가 배운 교훈을 저장하고, 벡터 검색으로 관련 맥락을 꺼내 쓸 수 있어요. Turso의 내장 벡터 함수가 있는 임베디드 데이터베이스 덕분에 로컬 파일 하나로 이 모든 게 가능해요.

이 가이드는 벡터 유사도 검색을 갖춘 메모리 시스템을 만들기 위한 스키마와 쿼리를 다뤄요.

스키마 (Schema)

가장 단순한 형태의 메모리 시스템은 테이블 두 개면 충분해요. 하나는 메모리용, 하나는 어떤 메모리가 어떤 태스크에 쓰였는지 추적용이에요.

CREATE TABLE IF NOT EXISTS memories (
    id              TEXT PRIMARY KEY,
    content         TEXT NOT NULL,
    embedding       F8_BLOB(384),
    category        TEXT NOT NULL,
    created_at      INTEGER NOT NULL,
    last_retrieved  INTEGER,
    retrieval_count INTEGER DEFAULT 0,
    source_task     TEXT
);

CREATE TABLE IF NOT EXISTS tasks (
    id               TEXT PRIMARY KEY,
    description      TEXT,
    embedding        F8_BLOB(384),
    started_at       INTEGER,
    finished_at      INTEGER
);
  • **memories**는 각 교훈을 벡터 임베딩과 카테고리(예: correction, insight, user, discovery)와 함께 저장해요. 임베딩은 384차원 int8 양자화 벡터(F8_BLOB(384))예요.
  • **tasks**는 에이전트가 작업한 각 태스크를 기록해서, 태스크 설명과 그 임베딩을 검색용으로 연결해요.

연결 (Connecting)

import { connect } from "@tursodatabase/database";

const db = await connect(".memory/agent.db");
await db.exec(SCHEMA);

메모리 저장하기 (Storing a memory)

에이전트가 뭔가를 배웠을 때 — 교정 사항, 사용자 선호, 통찰 — 임베딩과 함께 저장해요. vector8()을 쓰면 JSON float 배열을 int8 양자화 벡터로 바꿀 수 있어요:

INSERT INTO memories (id, content, embedding, category, created_at, source_task)
VALUES (?, ?, vector8(?), ?, ?, ?);

vector8()의 파라미터는 JSON 문자열화된 float 배열(예: all-MiniLM-L6-v2 같은 모델의 384차원 벡터)이에요. 양자화는 Turso가 내부적으로 처리해요:

db.prepare(`
  INSERT INTO memories (id, content, embedding, category, created_at)
  VALUES (?, ?, vector8(?), ?, ?)
`).run(id, content, JSON.stringify(Array.from(embedding)), "correction", Date.now());

관련 메모리 찾기 (Retrieving relevant memories)

새 태스크가 시작되면 Turso의 vector_distance_cos() 함수로 가장 관련성 높은 메모리를 찾아요:

SELECT id, content, category, created_at, retrieval_count,
       vector_distance_cos(embedding, vector8(?)) AS distance
FROM memories
WHERE embedding IS NOT NULL
ORDER BY distance ASC
LIMIT ?;

vector_distance_cos()는 코사인 거리를 반환해요(0 = 동일, 2 = 정반대). 값이 낮을수록 좋아요. 유사도로 바꾸려면 1.0 - distance를 쓰면 돼요.

검색 후에는 메모리의 메타데이터를 업데이트해요:

UPDATE memories SET last_retrieved = ?, retrieval_count = retrieval_count + 1
WHERE id = ?;

잘못된 메모리 교정하기 (Correcting wrong memories)

어떤 메모리가 틀린 것으로 밝혀지면 삭제하고 수정된 버전을 새로 저장해요:

DELETE FROM memories WHERE id = ?;

INSERT INTO memories (id, content, embedding, category, created_at)
VALUES (?, ?, vector8(?), 'correction', ?);

태스크 추적 (Task tracking)

에이전트가 무엇을 작업했는지 이해하려면 태스크를 기록해요:

-- Start a task
INSERT INTO tasks (id, description, embedding, started_at)
VALUES (?, ?, vector8(?), ?);

-- Complete a task
UPDATE tasks SET finished_at = ? WHERE id = ?;

오래된 메모리 정리하기 (Purging old memories)

오랫동안 있었지만 한 번도 검색되지 않은 메모리는 제거해요:

DELETE FROM memories
WHERE retrieval_count = 0 AND created_at < ?;

핵심 설계 포인트

  • 벡터 검색은 Turso의 vector8()과 vector_distance_cos()를 SQL 안에서 직접 사용해 코사인 유사도를 계산해요. int8 양자화는 float32 대비 저장 공간을 75% 줄이면서 검색 품질에는 거의 영향을 주지 않아요.
  • 프로젝트별 소규모 데이터셋이라면 ORDER BY distance LIMIT k 방식의 전체 테이블 스캔만으로도 벡터 인덱스 없이 충분히 빨라요.
  • 모든 것이 하나의 임베디드 데이터베이스 파일 안에서 동작하고 외부 서비스가 필요 없어요.

더 나아가기 (Going further)

위 스키마는 일부러 최소한으로 유지했어요. 실전에서는 메모리 저장소가 커져도 쓸모를 유지하도록 여러 전략을 실험하게 될 거예요. 몇 가지 아이디어:

  • 가중치 메모리(Weighted memories) — weight REAL 컬럼을 추가하고, 검색 후 에이전트가 실제로 유용했다고 판단했는지에 따라 가중치를 올리거나 내려요. 시간이 지나면서 가중치를 갱신할 때는 지수 이동 평균(exponential moving average)을 쓰면 좋아요.
  • 시간 감쇠(Time decay) — 검색 랭킹에 최근성(recency) 요소를 곱해서 오래된 메모리가 더 아래쪽에 랭크되게 해요. 예: (1.0 - distance) * POWER(0.95, days_since_last_retrieved).
  • 가비지 컬렉션(Garbage collection) — 가중치는 낮은데 검색 횟수만 높은 메모리(자주 검색되지만 쓸모가 없었던 것)나, 일정 기간이 지나도 한 번도 검색되지 않은 메모리를 주기적으로 제거해요.
  • 태스크 점수화(Task scoring) — 태스크별 결과 지표(사용 토큰, 에러, 교정)를 추적하고, 그 태스크에서 검색된 메모리에 대한 크레딧 신호(credit signal)를 계산하는 데 활용해요.
  • 검색 귀속(Retrieval attribution) — (memory_id, task_id, similarity, credit) 조인 테이블을 추가해서 어떤 메모리가 어떤 태스크에서 쓰였고 어떻게 평가되었는지 정확히 기록해요.

정답은 하나가 아니에요 — 최선의 전략은 에이전트의 워크로드, 메모리 생성 빈도, 얼마나 공격적으로 정리할지에 따라 달라져요. 단순하게 시작하고 반복 개선하세요.

예시 (Example)

memelord는 강화 학습과 가중치 감쇠(weight decay)를 AI 코딩 에이전트의 영구 메모리 레이어로 활용해 이 패턴을 구현한 MCP 서버이자 훅(hooks) 시스템이에요.

더 알아보기 (Learn more)