AI 코딩 에이전트를 위한 memory: 저장하는 쪽이 쉬운 절반입니다

지난주의 Harness 글은 실제 세션 브리핑을 보여줬고, 그 안의 한 줄이 나머지 전부보다 더 많은 일을 하고 있었습니다:

- **MCP Server** [PITFALL] (claude-code/vincent, 1 day ago): every new ID argument
  name must be added to idArgumentResolvers in authz.go, or the cross-org check
  silently skips it.

그것을 읽은 에이전트는 앞선 에이전트가 날린 오후를 통째로 아낍니다. 좋습니다. 이제 그 줄을 여섯 달 묵혀보세요. 똑같은 문장이, 똑같이 자신 있는 어조로, 그사이 두 사람이 다시 쓴 파일을 다루는 에이전트에게 배달됩니다. 그 줄에서 달라 보이는 건 아무것도 없습니다. 에이전트에게는 그것을 알아챌 방법이 없고, 당신에게도 없습니다.

그게 코딩 에이전트 memory의 진짜 문제이고, 대부분의 도구가 만드는 부분은 그쪽이 아닙니다. 적어두는 일은 쉬운 절반입니다.

왜 메모 더미로는 그 질문에 답할 수 없는가

오늘날 에이전트 memory의 흔한 형태는 에이전트가 덧붙여 쓰는 마크다운 파일, 그리고 에이전트가 써 넣는 벡터 스토어입니다. 둘 다 저장은 잘합니다. 둘 다 그럭저럭 잘 꺼내옵니다. 하지만 둘 중 어느 쪽도 어떤 메모의 발밑이 움직였다고 말해주지 못합니다. 둘 다 그 메모가 구조적인 의미에서 무엇에 관한 것인지 모르기 때문입니다. 벡터 스토어는 그 메모가 "gateway"와 "headers"라는 단어 근처에 있다는 것은 압니다. 하지만 ApiGateway가 당신 시스템의 container라는 것, 그것에 소스 경로가 있다는 것, 그리고 그 경로의 코드가 3주 전부터 문서화된 모델과 더 이상 맞지 않는다는 것은 모릅니다.

아키텍처 모델은 그 셋을 전부 압니다. 그것이 memory를 모델 옆에 두어야 하는 이유의 전부이고, 이 글에서 다른 제품이 한 스프린트 만에 베낄 수 없는 유일한 부분입니다.

memory는 이제 Archyl에, 모든 플랜에 들어 있습니다. 무엇을 하는지 보여드리겠습니다.

memory는 대화가 아니라 요소에 붙습니다

memory는 note, convention, pitfall 중 하나로 타입이 지정된 사실이며, C4 요소 하나 또는 프로젝트 전체에 붙습니다. 에이전트는 MCP를 통해 쓰고, 사람은 Agent Hub나 다이어그램 위 아무 요소의 상세 패널에서 씁니다.

▶ remember(projectId: …, element: "ApiGateway", kind: "pitfall",
    content: "The gateway strips custom headers longer than 8KB — payloads must go in the body.")

네 번째 종류로 session_outcome이 있고, 작업 세션이 끝날 때 자동으로 쓰입니다. 리스 여덟 개를 들고 있던 세션이 만드는 것은 여덟 요소에 붙은 하나의 memory이지, 같은 문단의 사본 여덟 개가 아닙니다. 이 형태는 꺼내오는 쪽에서 중요합니다. 그 요소들 중 어느 하나에 대해 묻는 에이전트는 그 결과를 한 번만 받습니다. 세션이 우연히 여덟 가지를 건드렸다는 이유로 같은 요약을 여덟 번 되읽지 않습니다.

memory의 본문과 제목은 저장 시 암호화됩니다. 제품 안의 다른 모든 민감한 콘텐츠 컬럼과 마찬가지입니다.

recall은 공통 부분 문자열이 아니라 의미로 순위를 매깁니다

recall은 의미적 유사도와 용어 중복을 0.55 대 0.45의 가중치로 섞습니다. "rate limiting"에 대해 묻는 에이전트는 다른 누군가가 "throttling"에 대해 써둔 메모를 받습니다. 단어 매칭 검색은 놓치고 동료라면 결코 놓치지 않을 바로 그 경우입니다.

벡터는 설계상 베스트 에포트입니다. AI 프로바이더(OpenAI 호환 또는 Ollama)가 설정되어 있지 않으면 벡터도 없고, 점수 계산은 예전에 동작하던 그대로 순수하게 어휘적으로만 남습니다. 깨지는 게 아니라 성능이 낮아지는 쪽이고, 프로바이더 없이 셀프 호스팅한다면 이 점이 중요합니다. 그리고 프로바이더가 없던 시절에 쓰인 memory가 영원히 이류로 남지도 않습니다. 프로바이더를 하나 설정하는 순간 백그라운드 워커가 그것들의 벡터를 채워 넣습니다.

같은 사실을 두 번 쓰면 그건 확인이 됩니다

프로젝트가 이미 아는 것을 다시 말한다고 두 번째 사본이 생기지는 않습니다. 코사인 유사도 0.94를 넘으면, 그 쓰기는 대신 기존 memory를 확인하고, 응답은 deduplicated: true라고 말합니다. 에이전트가 자기가 배운 것을 다시 주장하는 건 잡음이 아니라 증거입니다.

0.82와 0.94 사이에 흥미로운 대역이 있습니다. 가깝지만 같은 사실은 아닌 구간입니다. 이것들은 저장되고, 근접한 것들은 similarTo로 되돌아옵니다. 그래서 쓰는 사람은 아직 살아 있고 아직 배달되고 있는 memory와 조용히 모순되는 대신, 의도적으로 remember(supersedes: "Old title")을 호출할 수 있습니다.

memory는 쓰임새에서 배웁니다

모든 recall은 어떤 memory를 어떤 세션에 배달했는지 기록합니다. 세션이 끝날 때 usedMemories가 실제로 의지한 것들을 지목합니다.

이 두 신호에는 일부러 같은 가중치를 주지 않았습니다. memory를 배달받은 것은 정황입니다. 그것을 썼다고 말하는 것은 증언입니다. 그래서 memory의 순위를 올리는 것은 인용뿐이고, 로그 스케일에 1.8x 상한이 걸려 있습니다. 인기 있는 memory가 그것을 바로잡는 더 새로운 memory를 묻어버리지 못하게 하기 위해서입니다. 다섯 세션에 배달되고도 인용이 한 번도 없는 memory는 0.75 배수를 받고 잡음으로 취급됩니다.

취급되는 것이지, 삭제되는 것이 아닙니다. memory 안의 어떤 것도 휴리스틱에 의해 제거되는 일은 없습니다. 무시된 memory는 노출 횟수와 함께 리뷰 큐로 들어가고, 사람이 판단합니다. 같은 원칙이 이 기능 전체를 관통합니다: 고치되, 지우지 않습니다.

memory에는 수명 주기가 있습니다

신선도는 그 memory가 마지막으로 참이라고 알려진 시점, 즉 생성 시점이나 가장 최근의 확인 시점부터 45일 반감기로 감쇠합니다. confirm_memory는 그 시계를 초기화하고 확인 횟수를 하나 올립니다. remember(supersedes: …)는 바뀐 사실을 대체합니다. 옛 버전은 검색 대상에서 빠지지만 히스토리와 그래프에는 남기 때문에, 작년에 프로젝트가 무엇을 믿었는지는 여전히 볼 수 있습니다.

그 위에 종류별 가중치가 얹히고, 이건 분명한 의견을 담고 있습니다: pitfall은 3.0, convention은 2.0, 평범한 note는 1.5, session outcome은 1.0. 이제 곧 코드를 바꿀 에이전트에게는 "이건 너를 물 거야"가 "여기서 이런 일이 있었어"를 이깁니다.

memory를 실제로 무효화하는 건 드리프트입니다

위의 모든 것은 그럭저럭 괜찮은 장부 정리입니다. 이 절이 바로 memory가 아키텍처 도구에 속하는 이유입니다.

시간은 진실의 대리 지표로는 약합니다. 서비스 경계가 어떻게 동작하는지에 대해 2년 전에 쓴 convention은 아마 아직 맞습니다. 지난달에 썼더라도 그 뒤로 다시 쓰인 파일에 대한 메모라면 아마 틀렸습니다. 감쇠는 이 둘을 똑같이 다룹니다. 가진 게 시계뿐이니까요.

memory를 실제로 의심스럽게 만드는 것은 그 요소 뒤의 코드가 바뀌는 일입니다. Archyl은 이미 그것을 결정론적으로 계산합니다. 드리프트 점수가 문서화된 모델과 저장소를 비교해 더 이상 맞지 않는 요소를 지목합니다. UI에서, API에서, 또는 drift-score GitHub Action으로 푸시마다 돌릴 수 있습니다. memory는 이제 거기에 연결되어 있습니다.

드리프트가 어긋난 요소를 찾아내면, 그 요소에 붙은 모든 memory에 그 일이 일어난 시점이 찍힙니다. 그 시점보다 앞서 마지막으로 확인된 memory는, 그 뒤로 발밑이 움직여버린 무언가를 설명하고 있는 것입니다. 여기서 세 가지가 따라옵니다:

  • 순위에서 0.6 배수만큼 강등됩니다. 강등이지 숨김이 아닙니다. 그것이 그 요소에 대해 누군가 쓴 유일한 글일 수도 있고, 숨기는 것은 경고와 함께 배달하는 것보다 나쁘기 때문입니다.
  • 사람이 볼 리뷰 큐에 나타납니다.
  • 에이전트는 브리핑에서, 메타데이터가 아니라 말로 된 경고를 읽습니다:
- **ApiGateway** [PITFALL] [VERIFY — the element drifted since this was written]
  (claude-code/sarah, 96 days ago): the gateway strips custom headers longer than 8KB.

memory를 다시 확인하면 그 표시는 지워집니다. 확인은 드리프트가 던진 질문에 직접 답하는 행위이기 때문입니다: 누군가 들여다봤고, 여전히 성립한다고.

그 메커니즘의 두 절반이 같은 제품 안에 있습니다. 지식이 여기 있고, 그것을 무너뜨릴 수 있는 모델 대 코드 비교도 여기 있습니다. 채팅 클라이언트에 볼트로 붙인 memory 레이어는 첫 절반만 갖고 있고, 두 번째를 구할 방법이 없습니다.

memory끼리 서로 링크됩니다

memory에 제목을 붙이면 주소를 가진 것이 됩니다. 그러면 다른 어떤 memory든 자기 내용 안에서 [[Title]]로 그것을 참조할 수 있습니다. Obsidian 방식입니다. 같은 문법이 C4 요소를 이름으로 해석하고([[ApiGateway]]), 결정도 해석합니다([[ADR-17]]). 아직 존재하지 않는 제목으로 향한 링크는 보류 상태로 남아 있다가, 누군가 그 memory를 쓰는 순간 스스로 연결됩니다.

링크는 읽기 전용이 아닙니다. recall이 링크를 따라갑니다. 상위 매치들이 링크된 이웃을 함께 끌어오고, via로 표시되어 무엇이 그것들을 데려왔는지 볼 수 있습니다. 그 경계가 왜 존재하는지 설명하는 ADR로 링크된 gateway 관련 pitfall은 근거가 붙은 채로 도착합니다.

knowledge map, 그리고 우리가 버린 그래프

memory 패널의 첫 버전은 노드-링크 그래프였습니다. 렌더링도 됐고, 클러스터도 만들었고, 딱 스크린샷 찍고 싶은 종류의 물건처럼 보였습니다. 그것이 답하던 질문은 "어떤 memory가 어떤 memory로 링크되는가"였고, 그건 아무도 하지 않던 질문이었습니다.

사람들이 알아야 하는 것은 자기 아키텍처의 어느 부분을 프로젝트가 이해하고 있고, 어느 부분에 대해 아무도 한 글자도 쓰지 않았는가입니다. 그래서 교체했습니다. 패널은 이제 C4 요소마다 셀 하나를 보여줍니다: 그것에 대해 무엇이 알려져 있는지, 그 지식이 얼마나 신선한지, 거기에 pitfall이 몇 개나 있는지, 그리고 아무것도 없는 요소에 대해서는 눈에 보이는 공백을. 여기서 어떤 대시보드도 전에 주지 못한 한 줄이 나옵니다:

3 of 19 elements documented

그 문장은 쓸모 있는 방식으로 불편합니다. 그래프는 그렇지 않았습니다.

이것이 하지 않는 일

의미적 recall에는 AI 프로바이더가 필요합니다. OpenAI 호환 엔드포인트도 없고 Ollama도 없다면 벡터도 없고, 순위는 용어 중복으로 되돌아갑니다. 이 페이지의 나머지 전부는 그래도 동작합니다.

요소 매칭은 여전히 어휘적입니다. memory는 이제 의미로 순위를 매깁니다. 그 앞 단계, 당신의 작업이 어떤 요소들에 관한 것인지 고르는 find_relevant_context는 여전히 이름, 설명, 태그, 경로의 단어 중복으로 점수를 냅니다. "checkout"에 관한 작업은 여전히 OrderProcessor라는 component를 끌어올리지 못합니다. 이건 여러 에이전트, 하나의 아키텍처에서 한계로 짚었고, 지금도 그대로입니다.

유용성 신호는 에이전트가 자기가 쓴 것을 인용해야만 존재합니다. archyl-harness 스킬은 에이전트에게 sessionIdrecall에 넘기고 끝낼 때 usedMemories를 지목하도록 가르칩니다. 강제하는 것은 없습니다. 스킬 없이 연결된 에이전트는 노출만 만들고 인용은 만들지 않으며, 그건 아무도 유용하다고 여기지 않은 memory와 똑같이 읽힙니다.

memory의 범위는 프로젝트 단위입니다. 조직 전체에 걸친 convention은 그것을 필요로 하는 프로젝트마다 따로 써 넣어야 합니다. 그게 다음에 고칠 것입니다.

그리고 전체에 대한 정직한 단서: memory는 이제 막 나왔습니다. 도입 수치도, 벤치마크도, 이것이 뭔가를 아껴줬다고 말해줄 고객도 없습니다. 위에 쓴 것은 코드가 실제로 하는 일이고, 그 하나하나를 당신 프로젝트에서 직접 확인해볼 수 있습니다.

어디서 시작할까

이미 Harness를 돌리고 있다면 memory는 벌써 켜져 있습니다. remember, recall, confirm_memory는 coding 프로파일의 열여섯 개 tool 중 셋입니다. Claude Code 플러그인 버전 0.8.0이 바로 순위 계산이 의존하는 두 가지 습관을 에이전트에게 가르치는 조각입니다: sessionIdrecall에 넘길 것, 그리고 끝낼 때 무엇을 썼는지 지목할 것.

처음 할 만한 일은 memory를 쓰는 게 아닙니다. knowledge map을 열고 커버리지 줄을 읽는 것입니다. 거기 표시된 비율이 무엇이든, 그것이 그 아키텍처를 이해하는 사람이 휴가를 떠나도 살아남는 당신 아키텍처의 비율입니다. 보기 전에 숫자를 맞혀보고, 그다음에 보세요.

그다음에는 트래픽이 가장 많고 쓰인 것은 가장 적은 요소 하나를 골라, 신입에게 첫날 말해줄 함정을 쓰세요. 그게 다음 에이전트에게 필요한 memory이고, 누군가 그걸 타이핑하기 전까지는 아무리 검색을 잘해도 찾아내지 못합니다.


memory는 Archyl Harness의 일부입니다: 작업 세션, 프리플라이트 게이트, Guard 훅, 그리고 Fleet console. 플러그인, 스킬, Guard 훅GitHub Actions는 오픈 소스이고, 전체 레퍼런스는 Harness 가이드에 있습니다. 함께 읽기: 작업 세션, 여러 에이전트, 하나의 아키텍처, 그리고 왜 당신의 에이전트에게는 룰 파일이 있고 모델은 없는가.