목차
맨 위로
아래로

작업 이력, 장기기억, 스킬을 연결하는 에이전트 도구 만들기

avatarbynn
작업 이력과 장기기억, 스킬이 다음 작업으로 이어지는 에이전트 시스템 커버

들어가며

지금 제가 사용하고 있는 에이전트 세팅을 공유합니다. 앞으로 계속 발전시켜 나가거나 다 나은 방법을 발견 할 수 도 있겠지만, 지금 저는 이렇게 쓰고 있습니다.

3가지 요소가 있어요.

  • 에이전트에게 기억력을 주는 것
  • 작업하면서도 성장하게 하는 것
  • 전체 이력을 남기는 것

핵심은 에이전트가 작업을 하면서 성장하게 하는 것입니다. 이를 위해 LLM이 관리하는 메모리 시스템, 자기 개선 스킬 시스템, 그리고 턴별 요약 메모를 도입했습니다.

에이전트는 성장하지 않는다

제가 제일 많이 하는 일은 에이전트에게 적절한 컨텍스트를 제공하는 것입니다. 그러다 보면 에이전트에게 매번 똑같은 설명을 반복하게 됩니다.

사람이라면 학습을 통해서 한두번 설명한 것을 다시 반복하지 않아도 되겠지만 에이전트는 성장하지 않습니다. 매번 처음부터 끝까지 알려줘야해요. 이 반복을 줄이고 싶었습니다.

사람처럼 일에 점점 익숙해지고, 작업을 반복할수록 더 잘하게 만들 수는 있는 방법을 고민했습니다. 적절한 도구를 쥐어준다면 학습이라는 것을 유사하게 모방할 수 있지 않을까요.

자가 개선 스킬

조사해보니 헤르메스 에이전트가 좋다고 하더라구요 제가 원하는 딱 그런 방식으로 동작하고 있었습니다. 메모리와 자가 개선을 통해 계속 가져가야할 지식과 스킬들을 관리해 나가는 것이죠. 턴마다 리뷰를 통해 지식을 축적하고 스킬을 업데이트하는 방식입니다.

몇 일 써보다가 그냥 직접만들어야겠다고 생각했습니다. 생각보다 제약이 많았거든요.

왜 직접 구현했나

왜 헤르메스 에이전트를 쓰면되는데 직접 구현했냐고 한다면, 가장 큰 이유는 헤르메스의 폐쇄성 때문이었습니다. 제대로 쓰려면 앞으로는 계속 헤르메스만 써야 해 처럼 느껴졌달까요? 제가 구성해둔 하네스나 스킬들 에이전트 관련 세팅들을 헤르메스로 가져오고, 다른 기술들이 나오면 헤르메스용으로 나올때까지 기다리거나 스킬만 가져오거나 도구호출들로 간접적으로 사용하거나 하는 방식을 선택해야 했습니다.

매일매일 새로운 도구들, 기능들이 쏟아지는데, 저는 헤르메스에 적용 가능한 것만 사용할 수 있고 나머지는 버려야 한다는 점이 자체 구현을 선택한 가장 큰 이유였습니다.

대신 핵심적인 아이디어와 스킬 생성/수정 여부를 판단하는 루브릭, 그리고 수백 개의 스킬을 주입해주는 툴과 훅은 거의 그대로 차용했습니다.

자동 리뷰 대신 수동 리뷰

제가 구현한 자가 개선 시스템에서 제일 아쉬운 부분은 자가개선스킬을 직접 호출해야한다는 것인데요. 현실적인 제약이 있었습니다.

헤르메스는 턴마다 전체 대화를 보면서 스킬을 관리합니다. 그리고 그 대화는 모두 캐시되어 비용을 절약하고 있습니다. 하지만 코덱스 cli는 아직까지 포크한 세션의 프롬프트 캐시를 유지하는 방법을 제공하지 않아서, 턴마다 전체 대화를 남기면 토큰 소비가 기하급수적으로 늘어납니다. 1턴에 1토큰씩 10턴의 대화를 했다고 하면, 턴마다 세션 포크로 백그라운드 리뷰를 할 때 1+2+...+10 = 55의 토큰을 소비해야 하는 것이죠.

이 때문에 하나의 대화가 끝나면 별도로 자가 개선 스킬을 직접 실행해서 전체 대화에서 필요한 스킬을 생성하는 식으로 변형해서 사용하고 있습니다. 코덱스에서 빠르게 해당 기능이 업데이트가 되면 좋겠네요.

자가 개선 스킬의 자가 개선

하나 더 고민되는 부분이 있다면. 내가 만든 시스템이 실제로 동작하는지는 어떻게 확인할 수 있을까? 그냥 돌아가는 시스템만 만들어둔 것은 아닐까? 였습니다.

대규모의 예제들을 구성해서 테스트 하는게 가장 좋겠지만, 저의 사용 패턴에 맞는 예제들을 모으는 것도 시간이 필요하니, 다른 방법을 찾아보았습니다.

제가 찾은 방법은 자가 개선 스킬 역시 자가 개선의 대상으로 만드는 것이었습니다. 더 좋은 방법을 발견하거나, 사용자가 실험을 통해 알아낸 사실을 적용할 수 있도록 말이죠. 이런 방식이란면 자가 개선 스킬도 계속 사용 할수록 개선될 겁니다.

다음 문제는 긴 대화였습니다. 대화가 길어진 후에 자가 개선 스킬을 실행하면 마지막에 했던 작업의 스킬만 만드는 문제가 있었습니다. 이를 해결하기 위해 메모 시스템을 도입했습니다.

메모

Stop hook을 활용해 턴마다 마지막 턴의 대화를 세션 메모로 남기게 했습니다. hook에서 세션 아이디를 받고, 마지막 턴의 트랜스크립트를 가져와 요약하는 방식입니다. 이 메모를 자가 개선 시 함께 참고해서 전체 기억을 다시 되살리고, 스킬을 생성/수정/병합하게 했습니다.

결국 2배의 토큰을 쓰게 되는 것은 맞습니다. 토큰 절약을 위해 미리 정한 예산만큼의 글자수만 에이전트에게 요약하게 했고, 툴 호출 output도 정해진 글자수로 잘라서 사용했습니다. 툴 호출 결과(검색 결과 등)가 주로 토큰을 많이 쓰는 데 비해, 에이전트의 의도는 크게 담고 있지 않다고 생각했습니다.

정확히는 메모를 두 겹으로 나눴습니다. 자가 개선 스킬이 참고하는 turn-history 메모에는 사용자의 의도, 결정, correction, 성공 패턴, 실패 패턴을 짧게 남깁니다. 그리고 장기기억 후보가 되는 세션 메모에는 조금 더 구조화된 정보를 남깁니다.

구분역할담는 내용
turn-history 메모자가 개선 스킬의 근거사용자 의도, 결정, correction, 성공 패턴, 실패 패턴
세션 메모장기기억 후보결정, 변경, 제약, 보류, 충돌, 관계 정보

장기기억 후보 스키마

장기기억 후보 스키마는 대략 다음과 같습니다.

{
  "memo_type": "decision | change | constraint | conflict | deferred | rejected_alternative",
  "status": "accepted | needs_review",
  "subject": "이번 턴에서 남길 만한 변화나 판단",
  "context": "나중에 이 메모만 봐도 이해할 수 있는 배경",
  "rationale": "왜 이 판단을 했는지",
  "changed_artifacts": ["수정된 파일 경로"],
  "referenced_artifacts": ["참고한 파일 경로"],
  "durable_value": "이 메모가 대화 밖에서도 살아남아야 하는 이유",
  "classification_clues": {
    "work_area": "memo-system",
    "concepts": ["session memo", "stop hook", "skill"],
    "candidate_forms": ["Decision", "Reference"],
    "affected_surfaces": ["agent", "memo_system"],
    "audience": ["agent", "developer"]
  },
  "relations": {
    "supersedes": [],
    "conflicts_with": [],
    "related_memos": []
  }
}

처음에는 단순히 요약만 남기면 되지 않을까 생각했습니다. 막상 다시 써보니 요약은 검색에는 도움이 되지만 행동을 바꾸지는 못했습니다. 이후에는 메모를 남길 때도 결정인지, 변경인지, 제약인지, 보류된 일인지 구분하게 했습니다. 또 subject, context, rationale, durable_value를 분리했습니다. 나중에 에이전트가 읽었을 때 "무슨 일이 있었는가"뿐 아니라 "왜 이걸 지켜야 하는가"까지 알게 하기 위해서입니다.

classification_clues는 나중에 장기기억으로 옮길 때를 위한 힌트입니다. 턴이 끝나는 순간에는 이 메모가 어떤 문서의 어느 위치에 들어가야 하는지까지 확정하기 어렵습니다. 따라서 stop hook은 최종 문서 위치를 결정하지 않고, memo-system, docs_search, tooling 같은 단서만 남깁니다. 최종 분류는 별도의 ingest 단계에서 하도록 역할을 나눴습니다.

그리고 이 메모는 자가 개선 스킬에서만 사용하는 게 아니라 장기 기억으로 남길 것들의 후보가 됩니다.

장기기억

모든 작업이 끝나고 memo-ingest 스킬을 사용하면 지금까지의 세션 메모와 전체 대화 이력에서 장기기억 후보를 선별합니다. 기억 관리에는 LLM 위키 방식을 채용했습니다.

memo-ingest는 자동 저장이 아니라 승격

제가 생각한 memo-ingest의 역할은 자동 저장이 아니라 승격입니다. 턴별 메모는 아직 임시 기록에 가깝습니다. 그 안에는 나중에도 중요한 결정이 있고, 그때는 중요해 보였지만 며칠 지나면 버려도 되는 내용도 섞여 있습니다. 이 때문에 memo-ingest는 세션 메모를 그대로 장기기억에 복사하지 않습니다.

대략 아래와 같이 동작합니다.

  1. 세션 메모와 전체 대화 이력을 같이 읽습니다.
  2. 반복될 만한 사용자 선호, 프로젝트 규칙, 작업 절차, 실패 패턴을 고릅니다.
  3. 이미 있는 장기기억 문서에 합칠지, 새 문서로 만들지 판단합니다.
  4. 기존 문서와 충돌하거나 더 오래된 내용이 있으면 supersedes, conflicts_with 관계를 확인합니다.
  5. 바로 확정하기 애매한 내용은 needs_review로 남기고, 확실한 내용만 장기기억에 반영합니다.

여기서 중요한 것은 "많이 기억하기"가 아니라 "다음 작업의 출발점을 높이는 것"입니다. 예를 들어 사용자가 어떤 문체를 선호한다는 사실은 장기기억이 될 수 있지만, 특정 명령어가 한 번 실패했다는 사실은 보통 장기기억이 아닙니다. 반대로 그 실패가 반복되는 환경 제약이나 검증 규칙을 알려준다면 장기기억 후보가 됩니다.

프로젝트마다 다른 위키

문서 체계는 프로젝트마다 다르게 구성합니다. 프로젝트마다 오래 남겨야 하는 지식의 형태가 다르기 때문입니다. 그래서 하나의 고정된 폴더 구조를 모든 프로젝트에 강제로 적용하기보다는, 프로젝트의 성격에 맞는 작은 위키를 만듭니다.

다만 위키를 구성하는 핵심 절차는 거의 비슷합니다.

  1. 세션 메모와 전체 대화 이력을 함께 읽습니다.
  2. 장기기억으로 승격할 만한 후보군을 고릅니다.
  3. 후보군을 보면서 해당 프로젝트에 필요한 분류 루브릭을 만듭니다.
  4. 각 후보가 새 문서가 되어야 하는지, 기존 문서에 합쳐져야 하는지, 별도 문서로 분할되어야 하는지 판단합니다.
  5. 인덱스 생성 스크립트를 돌려서 문서들의 프론트매터로 문서 인덱스를 구성합니다.

이때 문서가 너무 많은 역할을 맡으면 분할하고, 반대로 비슷한 판단이 흩어져 있으면 기존 문서에 합칩니다. 중요한 기준은 "나중에 에이전트가 이 문서를 읽고 행동을 다르게 할 수 있는가"입니다.

지식을 활용할 때는 에이전트가 전체 위키를 모두 읽지 않습니다. 먼저 인덱스를 보고 지금 작업에 필요한 문서를 고른 뒤, 그 문서만 읽어와 작업합니다. 프론트매터와 인덱스는 이 라우팅을 위한 장치입니다.

검색은 보조 수단

검색엔진을 도입해서 로컬 RAG 서치와 키워드 검색의 하이브리드 서치로도 작동시킬 수 있게 했습니다. 작은 규모에서는 인덱스 방식으로도 충분했습니다. 하나의 위키가 300 문서를 넘어가면 꼭 필요한 기능일 거라서 현재 계속 디벨롭 중입니다.

동작방식

간단한 사용 시나리오

간단한 사용 시나리오는 이렇습니다.

예를 들어 어떤 프로젝트에서 문서 작성 규칙을 정리하는 작업을 했다고 해보겠습니다.

작업 중에는 평소처럼 에이전트와 대화합니다. 파일을 읽고, 초안을 만들고, 사용자가 "이런 식으로 쓰지 말고, 원자료를 먼저 확인한 뒤 작성해달라"고 피드백합니다. 턴이 끝나면 stop hook이 마지막 턴을 보고 메모 후보를 남깁니다.

그 메모에는 이런 내용이 들어갈 수 있습니다.

  • 사용자는 최종 문장을 raw에서 바로 만들지 않고 SourcesRefined를 거치길 원한다.
  • 블로그 글에서는 AI를 목적처럼 말하지 않고 생산성 개선 수단으로 설명해야 한다.
  • 민감한 내부 정보는 공개 글에서 일반화해야 한다.

작업이 끝난 뒤 자가 개선 스킬을 실행하면, 세션 전체와 턴별 메모를 함께 읽습니다. 그리고 이 내용이 특정 프로젝트의 장기기억인지, 여러 작업에 재사용할 스킬인지 나눕니다. 프로젝트마다 지켜야 할 규칙이면 장기기억으로 남기고, "지원서 위키를 읽고 산출물을 만드는 순서"처럼 반복 가능한 절차라면 스킬로 승격합니다.

다음 작업에서는 에이전트가 처음부터 모든 설명을 다시 듣지 않아도 됩니다. 관련 인덱스나 스킬을 먼저 읽고, 지난번에 정한 방식대로 움직일 가능성이 높아집니다.

작업 메모가 장기기억과 스킬로 승격되어 다음 작업에 반영되는 순환 구조

전체 흐름

전체 흐름은 다음과 같습니다.

  1. 작업하면서 그 대화 턴들에 대한 요약을 훅으로 남깁니다.
  2. 작업하면서 알게 되는 프로젝트별 지식은 LLM 위키로 쌓아둡니다.
  3. 다음 작업에서 재사용할 만한 패턴은 자기 개선 스킬로 남깁니다.
  4. 스킬이 생성되면 변경/수정된 스킬만 유저프롬프트 훅으로 주입합니다.
  5. 작업 중에 자율적으로 에이전트가 인덱스를 보고 필요한 문서를 알아서 참고합니다.
  6. 작업 중에 에이전트가 생성스킬들을 보고 필요한 스킬을 알아서 사용합니다.
  7. 작업 후에 스킬과 지식을 생성합니다.

백로그

  • compact 전 스킬과 기억 생성하기
  • 더 빠르고 정확한 검색엔진을 활용해서 기억 시스템 고도화하기
  • 세션 메모와 장기기억 문서 사이의 승격 기준 더 엄격하게 만들기
  • 장기기억으로 남긴 내용이 실제 다음 작업에서 사용됐는지 추적하기
  • 오래된 기억이 새 규칙과 충돌할 때 폐기하거나 대체하는 흐름 만들기
  • 자가 개선 스킬이 너무 잘게 쪼개지지 않도록 병합/큐레이션 루틴 개선하기
  • 검색 결과가 애매할 때 by-topic 인덱스와 하이브리드 검색을 어떻게 같이 쓸지 정리하기
  • 메모에 남긴 needs_review 항목을 주기적으로 확인하는 점검 루틴 만들기

마치며

지식과 스킬과 태도

어떤 일을 더 잘하게 된다는 것은 지식을 쌓는다. 더 잘하는 법을 배운다. 일을 대하는 태도를 바로잡는다. 정도로 정리가 됩니다. 에이전트에서는 각각 메모리, 스킬, 하네스라는 말로 표현되고 있습니다.

LLM 모델에게 태도를 주입하는 것은 아직 잘 안 되고 있습니다. 지금은 모델의 특징에 따라 더 적합한 모델을 선택하거나, 정해진 틀 안에서 움직이게 하기 위해 행동을 제약하는 하네스로 해결하고 있는 것 같습니다.

스킬은 클로드나 헤르메스 에이전트들이 제안한 대로, 어떤 일을 수행한 방식을 스스로 평가해서 스킬을 추가하고 업데이트하면서 쌓아가는 방식이 주류가 된 것 같습니다.

메모리는 아직 정립된 방법은 없지만, 벡터나 Markdown 혹은 기타 DB에 데이터를 쌓고, RAG나 키워드 서치로 찾게 한다든지, 현재까지의 맥락을 계속 유지하고 오래된 것을 잊게 한다든지, 지식 그래프를 구성한다든지 하는 여러 가지 방식을 시도하고 있는 것 같아요.

매일 좋은 도구들이 나올 것이고, 에이전트는 점점 더 똑똑해질 겁니다. 하지만 지식과 스킬과 태도라는 것은 바뀌지 않을 것 같아요. 내 지식, 스킬, 태도를 가꾸는 것도 중요하지만 내가 사용하는 에이전트의 메모리, 스킬, 하네스도 함께 가꾸어 가야 할 것입니다.