본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 25. 00:43

Compact는 기억이 아니라 지도이다: AI 에이전트를 위한 Breadcrumb Memory 설계

요약

AI 에이전트의 장기 기억 구현 시 발생하는 정보 왜곡 문제를 해결하기 위한 'Breadcrumb Memory' 설계 패턴을 제안합니다. 요약(Summary)에 의존하는 대신 원문으로 돌아갈 수 있는 색인(Index) 역할을 하는 지도를 구축하여 정확한 값을 보존하는 방법을 다룹니다.

핵심 포인트

  • 요약은 정확한 값(정규식, SQL 등)을 재생성할 수 없어 정보 왜곡을 유발함
  • Compact를 기억 자체가 아닌 원문 복구를 위한 색인(Index)으로 설계해야 함
  • Breadcrumb Memory는 source_range, query_hints 등을 활용한 설계 패턴임
  • 장기 개발 에이전트의 신뢰성을 위해 Exact value 보존이 필수적임

Compact는 기억이 아니라 지도이다: AI 에이전트를 위한 Breadcrumb Memory 설계

AI 에이전트에게 장기 기억 (Long-term Memory)을 부여할 때, "과거의 대화를 요약하여 저장하면 된다"라고 생각하는 것은 자연스러운 일입니다. 실제로 많은 개발자가 그렇게 구현하고 있습니다.

하지만 이런 경험은 없으신가요? 에이전트에게 "전에 결정했던 정규 표현식, 다시 한번 보여줘"라고 부탁했더니, 그럴싸해 보이지만 미묘하게 다른 값이 돌아온 경우 말입니다. 이스케이프 (Escape) 문자가 하나 빠져 있거나, 도메인이 하나 부족합니다. 테스트는 통과하지 못합니다. 그런데 에이전트만은 자신만만하게 대답하고 있습니다.

이것은 버그라기보다, 요약 (Summary)으로부터 정규 표현식을 재생성한 결과입니다.

TL;DR

  • Compact는 기억 그 자체가 아니라, 원문으로 돌아가기 위한 색인 (Index)으로 취급한다
  • 정규 표현식, SQL, 파일 경로와 같은 정확한 값 (Exact value)은 요약 (Summary)에서 재생성하지 않는다
  • source_range, query_hints, status, Source Hit@k를 갖추면 장기 개발 에이전트의 기억이 망가지는 것을 방지할 수 있다

긴 대화를 그대로 프롬프트 (Prompt)에 계속 넣으면 컨텍스트 윈도우 (Context Window)가 가득 차게 됩니다. 그래서 오래된 대화를 Compact 하여 짧은 요약 (Summary)으로 변환하는 설계는 자연스러우며, 실제로 상당히 유효합니다. Claude Code나 Codex를 비롯해 많은 AI 에이전트에 이러한 기능이 구현되어 있습니다. 다만, 코드를 작성하는 에이전트를 며칠 이상 계속 사용하다 보면 이 설계의 위험성이 보이기 시작합니다.

예를 들어, 인증 관련 코드를 에이전트와 함께 며칠에 걸쳐 리팩터링 (Refactoring)하고 있다고 가정해 봅시다. 첫날에 다음과 같이 지시했습니다.

이메일 주소 유효성 검사는 학교용 계정만 허용하고 싶어.
단, teacher.example.ed.jp와 student.example.ed.jp는 둘 다 통과시켜줘.
오래된 alumni.example.ed.jp는 이제 통과시키지 마.

에이전트는 정규 표현식을 수정하고 테스트도 추가했습니다. 며칠 후, 이렇게 질문합니다.

저번에 결정했던 인증 이메일 주소 정규 표현식 조건을 다시 한번 알려줘.

단순히 Compact만 수행한 에이전트는 여기서 위험한 상태에 빠집니다. 기억에는 아마 다음과 같은 요약 (Summary)만이 남아 있을 것입니다.

사용자는 인증 로직을 수정했다.
학교용 이메일 주소만 허용하는 방침이 되었다.

이것만으로는 정확한 조건을 복원할 수 없습니다. teacher.example.ed.jpstudent.example.ed.jp는 통과시켜야 하는가? alumni.example.ed.jp는 어떻게 해야 하는가? 실제로 작성했던 정규 표현식은 무엇이었는가? 그 후의 테스트에서 무엇을 확인했는가? 이러한 정보들은 요약 (Summary)에서 사라집니다.

에이전트는 "그럴싸한 정규 표현식"을 만들어 반환해 버립니다. 하지만 그것은 이전에 결정했던 것이 아닙니다. 이것은 단순한 망각이 아니라, 요약 (Summary)에 의해 근거로 돌아갈 수 없게 된 상태입니다.

본 기사에서는 AI 에이전트의 Compact를 "기억 그 자체"가 아니라, 기억으로 돌아가기 위한 지도로 설계하는 방법을 고찰합니다. 이 설계를 Breadcrumb Memory라고 부르기로 하겠습니다.

단, 이것은 완전히 새로운 기억 메커니즘을 제안하는 글이 아닙니다. Breadcrumb Memory는 Observational Memory나 MemGPT 계열의 source recall 개념을 장기 개발·연구 지원 에이전트용으로 재정리한 설계 패턴입니다. 본 기사의 기여는 다음 세 가지로 압축됩니다.

  • Exact value를 요약 (Summary)에서 복원하지 않는다는 운용 규칙과 그 이유의 구체화
  • (요약 (Summary)과는 별개로, 향후 검색에 걸릴 수 있도록 어휘를 의도적으로 남기는) query_hints를 Recall surface로서 명시적으로 설계하는 것
  • ("답할 수 있었는가"가 아니라 "근거로 돌아갈 수 있었는가"를 측정하는) Source Hit@k를 평가 지표의 중심으로 두는 것

이것들을 스키마 (Schema), Recall policy, 상태 관리, 평가 지표까지 포함하여 일체적으로 설계하는 것이 본 기사의 주안점입니다.

본 기사에서 TiDB Cloud의 위치

Breadcrumb Memory의 설계는 특정 데이터베이스에 의존하지 않습니다. PostgreSQL + pgvector + FTS, TiDB Cloud, Cloudflare D1 + Vectorize, SQLite + local vector index 등 여러 기반에서 구현할 수 있습니다.

다만, 이 설계를 구현하다 보면 SQL filter, vector search, full-text search를 동일한 쿼리 내에서 다룰 수 있는 데이터베이스를 자연스럽게 원하게 됩니다. 본 기사에서는 이 세 가지를 동일한 기반에서 다루기 쉬운 예시로서 TiDB Cloud를 사용합니다. "TiDB이기 때문에 Breadcrumb Memory가 성립하는 것"이 아니라, "Breadcrumb Memory를 구현하면 SQL + vector를 통합하여 다룰 수 있는 기반이 필요해진다"는 순서입니다.

상정하고 있는 유스케이스

상정하고 있는 것은 수 일에서 수 주간 지속되는 개발·연구·실험 지원 에이전트입니다. 코드 리팩터링 (Refactoring), 연구 실험의 로그 관리, 논문이나 조사 메모의 지속적인 정리, 소규모 시스템 개발 지원과 같은 상황이 전형적인 케이스입니다.

특히 궁합이 좋은 것은 장기 지속되는 코딩 지원 에이전트입니다. 인증 로직 (Authentication logic), DB 스키마 (Schema), API 사양 (Specification), 테스트 조건, 배포 설정을 지속적으로 수정하다 보면, 과거의 대화에는 파일 경로, 함수명, DB 마이그레이션 (Migration), API 엔드포인트 (Endpoint), 정규 표현식 (Regular expression), 환경 변수, 포트 번호, 테스트 조건, 한 번 거절된 설계안, 나중에 덮어씌워진 방침과 같은 정보가 대량으로 등장합니다.

이러한 정보들은 요약 (Summary)으로부터 재생성하면 깨지기 쉬운 정보입니다. 평소에는 가벼운 Breadcrumb만을 사용하고, 필요할 때만 source_range를 통해 원문이나 도구 결과로 돌아간다. 그러한 설계가 효과를 발휘합니다.

반대로 짧은 FAQ 챗봇이나 단발성 RAG라면, Breadcrumb Memory는 다소 무거운 설계가 됩니다. 이는 범용 챗봇을 위한 메커니즘이라기보다, 장기 지속되는 개발·연구·실험 지원 에이전트를 위한 기억 설계입니다.

에이전트 메모리에서 정말로 필요한 것은 "떠올리는 방법"

RAG에서는 많은 경우 사용자의 질문과 관련된 문서를 검색하여 LLM에 전달합니다. 반면, AI 에이전트의 메모리는 조금 더 복잡합니다. 과거의 대화, 사용자의 선호도, 코드의 변경 이력, 도구 실행 결과, 실패한 시도, 도중에 결정된 방침 등 에이전트는 이들을 가로질러 움직입니다.

여기서 중요한 것은 "많이 저장하는 것"이 아닙니다. 정말로 필요한 것은 무엇을 기억할 것인가, 어떤 입도 (Granularity)로 기억할 것인가, 언제 떠올릴 것인가, 요약 (Summary)만으로 충분한가, 원문이나 도구 출력으로 돌아가야 하는가, 오래된 기억과 새로운 기억이 모순되면 어떻게 할 것인가와 같은 판단입니다.

에이전트 메모리는 저장 기능이라기보다 떠올리는 방법의 설계입니다.

이 사고방식은 구현 단계에서 빛을 발합니다. 메모리를 "무엇이든 저장하는 상자"로 만들면 금방 노이즈로 가득 차게 됩니다. 반면 "짧게 요약하는 기능"으로 한정하면, 이번에는 근거로 돌아갈 수 없습니다. 필요한 것은 그 중간에 있는 설계, 즉 짧게 유지하면서도 필요할 때는 올바른 원문으로 돌아갈 수 있는 구조입니다.

Compact는 기억이 아니라 지도이다

일반적인 compact는 과거의 대화를 짧게 요약합니다. 앞서 말한 인증 로직 이야기라면 아마 다음과 같을 것입니다.

"사용자는 인증 로직을 수정했다. 학교용 이메일 주소만 허용하는 방침이 되었다."

짧아지기는 했습니다. 하지만 지도(Map)로서는 약합니다. 어떤 대화에서 결정했는지, 어떤 코드 변경에 대응하는지, 어떤 테스트가 근거인지, 정확한 정규 표현식은 어디에 있는지와 같은 핵심적인 단서가 남아 있지 않습니다.

Breadcrumb Memory에서는 compact의 결과를 다음과 같이 가집니다.

{
"id": "mem_auth_email_policy_20260512",
"kind": "observation",
...

여기서 저장하고 있는 것은 단순한 summary가 아니라, summary는 "대략 어떤 내용인가"를 나타내고, source_range는 원문으로 돌아가는 위치를 나타냅니다. tags는 구조화된 필터링에 사용하며, query_hints는 향후 검색될 법한 어휘를 명시적으로 남깁니다. retrieve_when은 사용해야 할 상황을, status는 오래된 기억이나 교체된 기억을 다루기 위한 상태를 나타냅니다.

즉, 여기서의 compact는 기억의 압축이 아니라 인덱스 (Index)의 생성입니다.

Mastra의 문서에도 동일한 발상이 있습니다. 통상적으로 Observational Memory에서는 메시지를 observation으로 압축하는 반면, retrieval mode에서는 observation group을 원본 메시지에 링크하여, 정확한 문구·도구 출력·시계열이 필요할 때 recall tool로 source messages를 추적할 수 있도록 합니다. 요컨대 "summary로 모든 것을 떠안지 않는다"는 발상입니다.

Breadcrumb Memory의 위치 설정

Breadcrumb Memory는 기존의 메모리 메커니즘을 대체하는 것이 아니라, Mastra Observational Memory와 같은 "observation + source recall"의 사고방식을 자체 데이터 기반에 구현한다면 어떻게 될지를 고민하여 설계한 패턴입니다. schema, 검색 조건, 업데이트 로직, 평가 지표까지 포함하여 하나의 틀로 설계하기 위한 프레임워크로 정리했습니다.

기존 메커니즘과의 위치 설정을 정리하면 다음과 같습니다.

메커니즘위치 설정
Mastra Observational Memorybackground agent에서 이력을 observation으로 압축하고, 필요 시 source messages로 recall하는 메모리 메커니즘
...

managed API로 빠르게 시작하고 싶다면 mem9와 같은 메커니즘이 적합합니다. 반면, memory record의 schema, query_hints를 만드는 방법, source_range의 입도(granularity), active/superseded/conflicted의 처리, 평가 지표까지 직접 제어하고 싶다면 Breadcrumb Memory를 직접 구현할 가치가 있습니다.

지도를 펼치고, 목적지를 정하고, 현지로 날아가, 필요한 보물만 가져온다

Breadcrumb Memory의 검색 파이프라인은 Retrieve → Decide → Recall → Extract의 4단계로 나뉩니다. 지도의 메타포(metaphor)로 말하자면 다음과 같은 흐름입니다. Retrieve에서 지도를 펼치고, Decide에서 목적지를 정하며, Recall에서 현지로 날아가, Extract에서 필요한 보물만 가져옵니다.

이 4단계로 나눔으로써, "항상 원문 전체를 읽는 것"도 "summary만으로 억지로 대답하는 것"도 아닌, 그 중간 단계의 설계로 구현할 수 있습니다.

Retrieve: 지도를 펼치다

가장 먼저 가져오는 것은 원문이 아닙니다. 우선 summary, tags, query_hints를 대상으로 한 vector search와 SQL filter를 조합하여, 가벼운 Breadcrumb만을 검색합니다.

이 단계에서는 최대 10개 정도의 후보를 가져와 summary와 query_hints를 Decide로 넘깁니다. 가져오는 항목은 summary, tags, query_hints, retrieve_when, created_at, importance, status 등입니다. 정규 표현식 코드 전문이나 과거 대화 전문은 아직 가져오지 않습니다. 필요한 것은 지도뿐이며, 우선은 윤곽을 잡는 단계입니다.

사용자가 "전에 인증 관련해서 결정했던 게 뭐였지?"라고 물었을 경우, 우선 auth, email-validation, regex, 인증 로직과 같은 태그나 query_hints에 걸리는 Breadcrumb를 가져옵니다. 이 단계에서 필요한 것은 "어떤 기억이 관련이 있을 것인가"뿐입니다. 정규 표현식 그 자체를 복원할 필요는 아직 없습니다.

TiDB Cloud에서는 이 Retrieve를 다음과 같이 작성할 수 있습니다. status = 'active'project_id와 같은 SQL 필터와 embedding을 통한 의미 검색을 하나의 쿼리로 처리할 수 있습니다.

SELECT id, summary, source_thread_id, source_start_index, source_end_index, status
FROM agent_memories
WHERE user_id = ?
...

이는 단순한 vector search가 아니라, 오래된 기억을 superseded 상태로 남겨두면서도 일반적인 답변에서는 active한 기억만을 우선시할 수 있다는 점이 중요합니다.

Decide: 목적지를 정하다

다음으로, summary(요약)만으로 답할 수 있는지, 아니면 원문이나 도구 출력(tool output)으로 돌아가야 하는지를 판단합니다. "전에 인증 관련해서 무엇을 바꿨었지?"라는 질문이라면, 방침 수준의 summary만으로 충분할지도 모릅니다.

하지만 "전에 결정했던 이메일 주소 정규 표현식을 정확히 알려줘"라는 질문을 받는다면, summary로부터 정규 표현식을 재생성해서는 안 됩니다. 정규 표현식, SQL, 파일 경로, 포트 번호, API 사양, 테스트 조건과 같은 정보는 summary에서 재생성할 경우 망가지기 쉽습니다. 필요하다면 현장(원문)으로 날아가야 합니다.

단, exactValuePatterns와 같은 문자열 매칭에만 의존하는 것은 위험합니다. 사용자가 "어제 그거 어떻게 됐어?"와 같이 모호하게 질문할 경우, 표면적으로는 exact value(정확한 값)를 요구하지 않더라도 실제로는 원문이나 도구 출력으로 돌아가지 않으면 답할 수 없는 경우가 있습니다.

Decide 단계는 최소한 다음과 같은 단계적 판정을 수행할 것을 권장합니다. 먼저 질문을 거칠게 분류하여 exact value가 필요할지 추정하고, 판정이 모호하다면 보수적으로 source_range 쪽으로 기울입니다. summary만으로 충분한 경우라도 필요하다면 chunk index까지 올립니다.

분류는 처음부터 복잡하게 만들 필요 없이, 예를 들어 다음 3가지 분류만으로도 충분할 것입니다.

판정예시다음 동작
summary_ok전에 인증 관련해서 무엇을 바꿨어?Breadcrumb의 summary로 답변
exact_value_needed정규 표현식을 정확히 알려줘! SQL을 그대로 보여줘!source_range로 돌아감
ambiguous어제 그거 어떻게 됐어?chunk_index 이상으로 올림

경량 LLM(Large Language Model)에 던진다면, 프롬프트는 이 정도면 충분합니다.

사용자의 질문을 다음 3가지로 분류하세요.
summary_ok: 방침이나 개요만으로 답변 가능
exact_value_needed: 정규 표현식, SQL, 경로, 수치, 설정값 등 정확한 값이 필요
...

실운용에서는 summary_onlychunk_indexsource_rangellm_extract와 같이 단계적으로 올리는 편이 안정적입니다. 중요한 것은 Decide를 단판 승부로 하지 않는 것입니다. 첫 판정은 보수적으로 하고, 정보가 부족하면 후속 tool call(도구 호출)로 보완하는 것이 모호한 질문에 더 강해지는 방법입니다.

Recall: 현지로 날아가기

상세 정보가 필요한 경우에만, 에이전트는 다음과 같은 인터페이스를 통해 Tool을 호출합니다.

type RecallMemorySourceInput = {
sourceThreadId: string;
sourceStartIndex: number;
...

이 Tool이 source_range의 모든 원문을 그대로 반환하는 것이 아니라는 점이 중요합니다. source_range는 프롬프트에 직접 넣는 범위가 아니라, 재검색 및 추출할 대상 범위입니다.

필요한 exact value가 여러 source_range에 걸쳐 있는 경우, 한 번의 recall로는 부족합니다. 예를 들어, DB 스키마는 3일 전에 변경되었고, 대응하는 API 사양은 어제 결정되었다는 케이스에서는 양쪽 source를 모두 확인해야 정확한 답변을 구성할 수 있습니다. 이럴 때는 recallMemorySource를 여러 번, 필요하다면 병렬로 호출합니다.

검색은 단발성이 아니라 멀티홉(multi-hop)입니다. 하나의 source_range만 보고 답변을 끝내지 않고, 필요하다면 여러 source_range를 넘나들며 근거를 수집하는 설계로 만듭니다.

Extract: 필요한 보물만 챙겨오기

source_range가 길 경우, 그것을 전부 프롬프트에 넣어버리면 compact(압축)한 의미가 없습니다. raw events(원시 이벤트)로 돌아간 후에는, 이번 query(질의)에 필요한 부분만을 추출합니다.

source_range → raw_events → chunk 분할 → query 대조 → ranking → 필요 시 LLM 추출 → evidence snippets (증거 스니펫)

통상적인 상황에서는 가벼운 Breadcrumb만으로 해결하고, 정확성이 필요할 때만 원문으로 돌아갑니다. 돌아가더라도 원문을 전부 가져오는 것이 아니라, 필요한 부분만 가져옵니다. 이것이 Breadcrumb Memory의 핵심입니다.

exact value는 summary에서 재생성하지 않는다

Breadcrumb Memory에서 특히 중요한 것은 exact value(정확한 값)를 summary(요약)로부터 재생성하지 않는 것입니다. 여기서 말하는 exact value란 정규 표현식(Regular Expression), 포트 번호, 파일 경로, 환경 변수명, 함수명, SQL, API endpoint, 허용 도메인, 테스트 조건, 하이퍼파라미터(Hyperparameter)와 같은 정보를 가리킵니다.

이것들은 summary에 적혀 있는 것처럼 보이더라도 너무 신뢰해서는 안 됩니다. LLM은 summary를 만들 때 세부 사항을 자연스럽게 뭉뚱그립니다. 정규 표현식의 이스케이프(Escape)를 누락하거나, 포트 번호의 자릿수를 틀리거나, 파일 경로의 대소문자를 바꾸는 식입니다. 그러한 어긋남이 그대로 "그럴싸해 보이지만 이전에 결정했던 것과는 다른 값"으로 반환됩니다.

예를 들어, summary에 "학교용 이메일 주소만 허용하는 정규 표현식으로 변경했다"라고 남아 있다고 가정해 봅시다. 이 summary로부터 나중에 LLM이 정규 표현식을 재생성하게 해서는 안 됩니다. 중요한 것은 "학교용 이메일 주소만"이라는 분위기가 아니라, teacher.example.ed.jp는 허용하고, student.example.ed.jp는 허용하며, alumni.example.ed.jp는 허용하지 않는다는 정확한 조건이기 때문입니다.

따라서 Breadcrumb Memory에서는 다음과 같은 규칙을 둡니다. exact value는 summary에서 복원하지 않는다. exact value가 필요한 질문에서는 반드시 source_range로부터 recall(회상/검색)한다.

summary는 지도입니다. 정규 표현식이나 SQL, 파일 경로는 현지에 있는 실물입니다. 지도를 보고 실물을 상상으로 다시 그려서는 안 됩니다.

query_hints는 recall surface이다

Breadcrumb Memory에서 다음으로 중요한 것이 query_hints입니다.

사람이 모든 것을 손으로 쓸 필요는 없습니다. compact(압축) 시점에 observer agent(관찰자 에이전트)에게 다음과 같은 지시를 내립니다.

당신은 대화를 장기 기억으로 변환하는 observer입니다.
summary뿐만 아니라, 향후 검색될 가능성이 있는 어구를 query_hints로서 출력해 주세요.
query_hints에는 다음 종류의 어구를 포함해 주세요.
...

예를 들어, 인증 로직 변경에 관한 memory라면 다음과 같습니다.

{
"summary": "인증 로직에서는 teacher.example.ed.jp와 student.example.ed.jp를 허용하고, alumni.example.ed.jp는 제외하는 방침이 되었다.",
"query_hints": [
...

포인트는 summary와는 별도로 "향후 검색되기 위한 표면"을 만드는 것입니다. summary는 사람이 읽고 의미를 이해하기 위한 것이고, query_hints는 나중에 memory retrieval(메모리 검색)에 걸리게 하기 위한 것입니다. 따라서 embedding(임베딩) 대상도 summary로만 한정하지 않습니다. embedding_textsummary + retrieve_when + tags + query_hints와 같은 단순한 결합만으로도 충분합니다.

의미 검색(Semantic Search)에서는 summary가 효과적이고, 키워드 검색이나 재검색에서는 query_hints가 효과적입니다. query_hints는 사람을 위한 장식이 아니라, 향후 검색 품질을 높이기 위한 recall surface(회상 표면)입니다.

테이블 설계

최소 구성에서는 raw_events와 agent_memories라는 두 개의 테이블을 준비합니다.

raw_events는 대화나 도구 실행 결과의 원문을 저장하는 테이블이며, agent_memories는 compact 이후의 Breadcrumb를 저장하는 테이블입니다.

CREATE TABLE agent_memories (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
...

실제로는 여기에 embedding을 위한 vector column을 추가합니다. embedding의 대상은 summary로만 한정하지 않습니다. embedding_textsummary + retrieve_when + tags + query_hints로 충분합니다. summary만 사용하면 나중에 검색될 법한 어휘가 누락될 수 있기 때문입니다.

예를 들어, summary에는 「인증 로직을 변경했다」라고만 적혀 있더라도, 나중에 사용자는 「validateEmail의 조건이 뭐였지?」라거나 「AUTH_ALLOWED_DOMAINS는 어디서 결정했지?」라고 물어볼 수 있습니다. 이때 효과적인 것이 바로 query_hints입니다.

오래된 기억을 어떻게 다룰 것인가

장기 기억 (Long-term memory)에서 까다로운 점은 오래된 기억이 계속 남아 있다는 것입니다. 처음에는 「alumni.example.ed.jp도 허용한다」라고 결정했다고 가정해 봅시다. 하지만 나중에 「alumni.example.ed.jp는 허용하지 않는다」로 방침이 바뀌었습니다.

이때 오래된 기억을 완전히 삭제하면 왜 방침이 바뀌었는지 추적할 수 없게 됩니다. 반면 오래된 기억을 active 상태로 남겨두면, 검색 시 혼입되어 위험할 수 있습니다. 그래서 삭제하는 대신 superseded (대체됨) 상태로 만듭니다.

UPDATE agent_memories
SET
status = 'superseded',
...

새로운 기억은 오래된 기억을 대체한 것으로 저장하며, 검색 시에는 기본적으로 active한 기억을 우선합니다. 재순위화 (Rerank) 단계에서는 최신성도 점수에 포함합니다.

final_score =
vector_score
+ keyword_score
...

이를 통해 오래된 기억을 이력으로서 남기면서도, 현재의 답변에서는 새로운 기억을 우선할 수 있습니다. SQL로 상태 관리를 할 수 있는 기반 시스템의 강점이 드러나는 부분입니다.

source_range는 너무 길어도 너무 짧아도 좋지 않다

source_range는 너무 길어도, 너무 짧아도 문제가 됩니다. 100건 이상의 raw events를 하나의 memory에 연결하면, recallMemorySource 내에서 청크 (Chunk) 분할, 스코어링 (Scoring), 추출을 수행하는 비용이 증가합니다. 반대로 너무 짧으면 문맥 (Context)이 손실됩니다.

초기 구현에서는 「1 observation = 20~40 events 정도」를 기준으로 삼는 것이 좋습니다. 이론적인 최적값이 아니라 초기 구현을 위한 경험칙입니다. event의 입도 (Granularity), 도구 호출 (Tool call)의 크기, 대화 밀도에 따라 달라지므로, event 수뿐만 아니라 토큰 (Token) 수도 경계 조건에 포함하는 것이 안전할 것입니다. 「40 events 또는 6,000 tokens를 초과하면 자른다」라고 설정해 두면, 거대한 tool output이 하나의 event에 포함된 경우에도 대응하기 쉬울 것입니다.

source_range는 「고정 길이 + 토픽 경계」 하이브리드 방식으로 자른다

source_range를 자르는 방법에는 몇 가지 후보가 있습니다.

방법장점단점
일정 턴(Turn)마다 자르기저렴함, 구현이 간단함, 안정적임토픽 중간에 잘릴 수 있음
...

가장 현실적인 방법은 고정 길이를 기본으로 하되, 명확한 토픽 경계만을 보조적으로 사용하는 방법입니다. 초기 구현에서는 다음과 같이 합니다. raw_events를 최대 40 events로 임시 구분하고, tool call, file edit, test result, 사용자의 명시적인 화제 전환을 경계 후보로 삼습니다. 10 events 미만의 파편은 앞뒤와 결합하고, 60 events 또는 일정 토큰 수를 초과하는 경우에만 LLM을 사용하여 분할하며, 자주 호출되는 범위만 나중에 개선합니다.

source_range의 입도를 처음부터 완벽하게 만들려 하지 않는 것이 중요합니다. 기본은 저렴한 규칙으로 자르고, LLM을 사용하는 것은 경계가 모호하거나 범위가 너무 길어졌을 때뿐입니다.

recallMemorySource는 매번 전체를 처리하지 않는다

다음으로 문제가 되는 것은 recallMemorySource의 지연 시간 (Latency)입니다. 단순하게 구현하면 매번 raw events를 읽어 청크로 나누고, 쿼리 (Query)와 대조하여 순위를 매긴 뒤 LLM으로 근거 (Evidence)를 추출하는 과정이 실행됩니다. 이는 무거운 작업입니다. 특히 긴 스레드 (Thread)에서 매번 이를 수행하면 메모리 기능 자체가 병목 (Bottleneck)이 됩니다.

따라서 recall을 다음과 같이 단계화합니다.

  • Level 0: Breadcrumb summary만으로 답변
  • Level 1: 미리 생성된 chunk index에서 탐색
  • Level 2: source_range 내의 raw chunks만 재검색
  • Level 3: 정확한 값 (Exact value)이 필요할 때만 LLM으로 evidence 추출

매번 raw events로부터 전부 다시 만드는 것이 아니라, 저장 시점에 source_range별 chunk와 embedding을 어느 정도 만들어 둡니다. chunk 단위의 embedding을 준비하여 chunk index를 만듦으로써, recall(재검색) 시에는 raw events 전체가 아니라 사전에 만들어 둔 source_chunks를 검색할 수 있게 됩니다.

실제 운용에서는 일정 기간이 경과한 superseded(대체된) 기억이나 importance(중요도)가 극단적으로 낮은 기억은 archived(보관됨)로 옮기고, 본문은 Cold Storage(콜드 스토리지)로 퇴피시키는 것이 현실적일 것입니다.

active → superseded → archived → Cold Storage (S3 등)

이러한 계층을 넣어두면, 검색 대상이 되는 hot(활성) 데이터를 가볍게 유지하면서도 필요할 때는 오래된 원문도 복원할 수 있습니다.

schema와 query_hints는 「고정 코어 + 프로젝트 확장」으로 구성한다

Breadcrumb Memory의 schema(스키마)나 query_hints(쿼리 힌트)를 프로젝트마다 전부 튜닝하는 것은 매우 힘든 일입니다. 기본적으로는 고정 코어 + 프로젝트 확장 방식으로 구성하는 것을 권장합니다.

고정 코어는 summary, source_range, tags, query_hints, retrieve_when, importance, status, created_at, updated_at입니다. 이 부분은 변경하지 않습니다. 프로젝트 고유의 정보는 metadata에 담습니다.

코딩 지원의 경우 metadata에 repo, files, symbols, test_files를 포함시킵니다.

{
"repo": "school-equipment-reservation",
"files": ["src/auth.ts", "src/validate-email.ts"],
...
}

리서치 지원의 경우 paper_ids, concepts, citation_keys를 포함시킵니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0