TrendHackergeeknews intel
live
← 블로그 목록
TECH NOTE · 2026-07-02

Hermes 에이전트 아키텍처 해부: 에이전트 루프·컨텍스트·게이트웨이·메모리·크론

영상: Hermes Architecture EXPLAINED — Memory, Context & Gateways
AI AgentAgent LoopMemoryContext WindowGateway
▶ YouTube에서 보기

들어가며: "항상 켜져 있는 AI 비서"는 안을 열면 의외로 단순하다

요즘 개인용 AI 에이전트(personal AI agent)가 빠르게 늘고 있습니다. 텔레그램으로 말을 걸면 답하고, 매일 아침 뉴스 요약을 보내주고, 대화를 거듭할수록 나에 대해 더 잘 알게 되는 비서 말입니다. 이런 에이전트가 겉으로는 마법처럼 보이지만, 내부 아키텍처를 열어 보면 놀랄 만큼 단순한 몇 개의 부품으로 이뤄져 있습니다.

이 글은 Hermes 에이전트의 아키텍처를 고수준(high-level)에서 해부한 설명을 정리한 것입니다. Hermes를 어떻게 쓰는지, 그리고 이런 에이전트를 직접 만들고 싶을 때 어떤 구조를 참고하면 되는지를 이해하는 데 초점을 맞춥니다. 다룰 주제는 다음과 같습니다. 전체 아키텍처의 조감도, 에이전트 루프(agent loop), 컨텍스트가 만들어지는 방식, 컨텍스트 압축, 여러 메시징 서비스를 잇는 게이트웨이(gateway), 메모리(memory), 그리고 크론 잡(cron jobs)입니다.

핵심 메시지를 먼저 말하면 이렇습니다. Hermes는 복잡한 프레임워크가 아니라, "메시지가 올 때마다 컨텍스트를 조립해 LLM에 넘기고, 툴을 돌리고, 배운 것을 파일에 적어 두는" 아주 미니멀한 루프 하나를 중심으로 돌아갑니다.

조감도: 코어 하나에 연결 통로와 부속 서비스가 붙는다

가장 먼저 전체 구조를 새의 눈으로 내려다보겠습니다. 컴포넌트는 몇 개 되지 않습니다.

중심에는 AI 에이전트 코어(AI agent core)가 있습니다. 이 코어의 실체가 바로 뒤에서 다룰 에이전트 루프입니다. 이 코어에 접속하는 방법은 세 가지입니다.

  • CLI: 커맨드라인에서 hermes라고 입력하면 바로 에이전트에 연결됩니다.
  • Gateway: 항상 실행되는 시스템으로, 텔레그램·이메일·Slack 등 메시징 서비스를 통해 에이전트에 연결해 줍니다.
  • API: 프로그래밍 방식으로 에이전트를 호출하는 통로입니다.

이 세 가지가 모두 하나의 AI 에이전트 코어로 모입니다. 그리고 이 코어에는 여러 부속 서비스가 딸려 있습니다. Hermes를 설치하면 바로 쓸 수 있도록(out of the box) 다음이 미리 준비되어 있습니다.

  • Tools: 웹 검색, 파일 읽기/쓰기처럼 에이전트가 호출할 수 있는 도구 모음.
  • Skills: 에이전트가 활용할 수 있는 사전 정의된 능력 모음.
  • Memory: 뒤에서 자세히 다룰 메모리. 여기서 미리 알아둘 점은 메모리가 두 갈래로 나뉜다는 것입니다. 하나는 외부 메모리(external memory)로 mem0, supermemory 같은 외부 제공자를 붙이는 방식이고, 다른 하나는 내부 메모리(internal memory)로 세션 트랜스크립트(session transcript), 즉 모든 대화 기록 자체입니다.

여기에 더해 에이전트가 직접 수정할 수 있는 파일들이 있습니다. soul.md는 에이전트의 성격을, user.md는 지금 이 에이전트를 쓰는 사용자에 대한 정보를 담습니다. 이 파일들이 에이전트에게 "너는 누구이고, 지금 대화하는 사람은 누구인가"를 알려 줍니다.

이것이 전체 시스템의 뼈대입니다. 보다시피 구조 자체는 매우 단순하고, 지금부터 각 부품을 하나씩 뜯어봅니다.

에이전트 루프: 미니멀리즘의 정수

에이전트 루프는 Hermes의 심장입니다. 그런데 이 루프는 정말로 단순합니다. PyAgent나 OpenCode 같은 미니멀리스트 에이전트에서 볼 수 있는 것과 비슷한, 사용자가 메시지를 보낼 때마다 도는 반복 구조일 뿐입니다.

순서대로 따라가 보겠습니다.

  1. 사용자가 메시지를 보낸다. 루프의 시작점입니다.
  2. 컨텍스트를 빌드한다(build context). 에이전트는 자신이 가진 내부 메모리와, 미리 준비된 프롬프트들을 모아 컨텍스트를 조립합니다.
  3. 컨텍스트와 메시지 히스토리를 LLM에 보낸다. 이때 보내는 컨텍스트에는 시스템 프롬프트, soul.md 파일들, user.md 파일들, 그리고 메시지 히스토리가 들어갑니다.
  4. LLM이 툴 호출을 결정한다. LLM이 도구를 부르기로 하면, 그 툴을 실제로 실행하고 결과를 다시 LLM에게 돌려줍니다. 이 과정은 LLM이 "더 이상 도구가 필요 없다"고 판단할 때까지 계속 반복됩니다. 예를 들어 웹 검색을 쓰거나, 파일을 쓰거나 읽거나 수정하는 식입니다.
  5. 최종 응답을 준다. 필요한 툴 호출을 모두 마치면 사용자에게 최종 답변을 돌려줍니다.
  6. 메모리를 업데이트한다(memory update). 여기가 루프의 마지막이자 핵심입니다. 응답을 준 뒤, 에이전트는 방금의 대화를 분석해 "기억할 만한 것이 있는가"를 판단하고, 있다면 메모리에 적어 둡니다.

바로 이 마지막 메모리 업데이트 단계가 Hermes를 "쓸수록 배우고 나아지는(continuously learning)" 에이전트로 만드는 장치입니다. 다음에 비슷한 질문을 받으면 지난번에 배운 것을 이미 알고 있게 되는 것입니다.

루프 자체는 이게 전부입니다. 사용자가 메시지를 보낼 때마다 이 과정이 처음부터 끝까지 한 바퀴 도는, 아주 직관적인 구조입니다.

컨텍스트: 몇 개의 마크다운 파일이 성격과 기억을 만든다

에이전트를 만들 때 컨텍스트를 어떻게 조립하느냐는 매우 중요한 문제라서, 별도 섹션으로 자세히 봅니다. Hermes의 컨텍스트도 역시 매우 미니멀합니다. 핵심은 몇 개의 마크다운 파일입니다.

첫 번째는 soul.md입니다. 이 파일은 에이전트의 성격(personality)입니다. 어떤 톤(tone)을 쓸지, 무엇에서 영감을 받는지, 목표가 무엇인지, 사용자에게 어떤 방식으로 다가갈지를 여기에 적습니다. Claude의 잘 쓰인 거대한 시스템 프롬프트를 본 적이 있다면, 바로 그런 것을 자신에게 맞게 써 넣는 자리라고 보면 됩니다. 중요한 점은, Hermes를 갓 설치했을 때 이 파일은 보통 비어 있다는 것입니다. 직접 채우거나 Hermes에게 써 달라고 하지 않으면, 기본 시스템 프롬프트가 대신 들어갑니다. 기본값은 그저 "너는 항상 켜져 있는 가상 비서 Hermes다" 정도만 알려 주므로, 나에게 맞춘 에이전트를 원한다면 soul.md를 직접 갖추는 편이 좋습니다.

그다음은 메모리 디렉터리 안의 파일들입니다.

user.mdsoul.md와 달리 Hermes가 사용자에 대해 무언가를 배울 때마다 자동으로 갱신합니다. 대화 중에 "나는 소프트웨어 엔지니어이고 이걸 하고 있어" 또는 "나는 시장 분석가야"라고 말하면, 에이전트는 그것이 사용자에 대한 정보임을 알아채고 이 파일에 저장합니다.

memory.md는 좀 더 임의적인 사실(arbitrary facts)을 담는 메모리입니다. 에이전트가 스스로 갱신하며(직접 수정하도록 시킬 수도 있습니다), 사용자에 대한 정보라기보다는 도구 사용법, 여러 워크플로, 대화에서 배운 흥미롭고 유용한 것들을 적어 둡니다. soul.md에 설정된 목표에 따라, 앞으로의 대화에 도움이 될 만한 것을 골라 저장합니다.

이 마크다운 파일들 외에도 컨텍스트에는 보통 다른 것들이 더 붙습니다.

  • 과거 세션 요약: 지난 대화들의 요약이 붙을 수 있습니다. 단, 이것은 외부 메모리를 설정했을 때만 나타납니다. 외부 메모리를 켜지 않았다면 이 부분은 없습니다.
  • Skill 설명과 Tool 설명: 에이전트가 쓸 수 있는 능력과 도구에 대한 설명.
  • 메시지(최신 대화): 가장 최근에 주고받은 메시지들. 대화 전체일 수도 있고, 대화가 일정 임계값을 넘으면 요약된 형태일 수도 있습니다.

정리하면, 메시지를 보낼 때마다 이 컨텍스트가 통째로 조립되어 Hermes에게 전달됩니다. 이것이 루프의 "build context" 단계에서 벌어지는 일입니다.

컨텍스트 압축: 문자 수를 4로 나눠 어림잡고, 넘치면 요약한다

컨텍스트를 이야기했으니 자연스럽게 컨텍스트 압축(context compression)으로 넘어갑니다. LLM에는 컨텍스트 윈도(context window)라는 한계가 있습니다. 보통 25만 토큰에서 많게는 100만 토큰 정도입니다.

Hermes를 설정할 때, 메시지 히스토리에 대해 언제 압축을 트리거할지 묻습니다. 기본값은 컨텍스트의 50%입니다. 즉 컨텍스트의 절반을 쓰면 Hermes가 압축 함수를 발동해 모든 메시지를 요약합니다. 이 임계값을 넘으면 이전 메시지들을 요약하고, 그 요약을 컨텍스트에 붙인 뒤, 원래의 이전 메시지들을 이 요약으로 대체합니다. 이 임계값은 커스터마이즈할 수 있어서, 컨텍스트 윈도가 작은 모델을 쓴다면 70%나 80%로 올리는 편이 나을 수도 있습니다.

압축은 두 순간에 확인됩니다. 첫째는 각 턴이 시작되기 전, 즉 LLM을 호출하기 직전입니다. 둘째는 에러가 났을 때입니다. LLM이 컨텍스트 윈도 초과 에러를 돌려주면 컨텍스트를 확인해 요약합니다. 최대 컨텍스트에 도달하지 않았다면, 매 턴 시작 전에 확인이 이뤄집니다.

그렇다면 지금 컨텍스트가 얼마나 큰지는 어떻게 잴까요? 여기서 재미있는 두 가지 방식이 나옵니다.

첫 메시지의 경우를 봅시다. 아직 LLM에 아무것도 보내지 않은 상태에서는 토큰 수 데이터가 없습니다. 정확히 재려면 토크나이저(tokenizer)를 돌려야 하지만, 그건 비쌉니다. 그래서 Hermes는 훨씬 단순하게 처리합니다. 전체 문자 수를 4로 나눈 값을 컨텍스트의 근사치로 삼는 것입니다. 이 근삿값이 50%(또는 설정한 임계값)를 넘으면 요약 압축을 트리거합니다.

추정 토큰전체 문자 수4\text{추정 토큰} \approx \frac{\text{전체 문자 수}}{4}

첫 메시지 이후에는 더 나은 방법이 있습니다. LLM에 메시지를 보내 응답을 받으면, 응답 안에 usage 파라미터가 함께 옵니다. 여기에는 입력 토큰, 출력 토큰, 또는 총 사용량 정보가 들어 있는데, 제공자(provider)마다 조금씩 다릅니다. 이 값은 실제로 쓰는 모델과 같은 토크나이저로 계산된 값이므로 훨씬 정확합니다. 물론 이 단계에서도 같은 토크나이저를 직접 돌릴 수 있지만, 그건 너무 비싸고, 앞의 문자÷4 방식으로도 충분히 쓸 만하며 저렴합니다.

압축에 쓰이는 프롬프트는 context_compressor.py 파일 안에 있습니다(리팩터링에 따라 위치가 바뀌지만 대략 1,400번째 줄 부근입니다). 이 프롬프트는 LLM에게 전체를 요약하되 여러 섹션으로 나눠 정리하도록 지시합니다. 예를 들면 전체 목표(full goal), 제약(constraints), 완료된 행동(completed actions), 활성 상태(active state), 진행 이력(historical progress), 현재 막힌 지점(blocked), 내린 핵심 결정(key decisions), 이미 해결된 질문(resolved questions), 관련 파일(relevant files), 중요한 컨텍스트(critical context), 이전 요약(previous summaries), 다음 턴에 반영할 것, 요약할 턴 등입니다.

여기서 흥미로운 비교가 하나 나옵니다. 이 압축 프롬프트는 PyAgent 같은 더 미니멀한 에이전트에 비해 훨씬 덜 미니멀합니다. Pi의 컨텍스트 요약 프롬프트는 훨씬 작고 간결한데, Hermes의 것은 더 풍부하고 그만큼 에이전트에게 "지금 무슨 일이 벌어지고 있는지"에 대한 맥락을 더 많이 넘겨 줍니다.

게이트웨이: 텔레그램·Slack·이메일을 하나의 코어로 잇는 관문

게이트웨이는 텔레그램, WhatsApp, 이메일, 문자, Slack 같은 여러 메시징 플랫폼을 통해 에이전트와 대화할 수 있게 해 주는 부분입니다. 사실 Hermes에서 가장 어려운 부분은 아니지만, Hermes를 이만큼 대중적으로 만든 부분이기도 합니다.

게이트웨이가 하는 일은 크게 두 가지입니다. 첫째, 게이트웨이가 설정되면 들어오는 메시지를 수신(listen)합니다. 둘째, 그 메시지를 올바른 형식으로 바꾸고 전체 컨텍스트와 대화를 조립해 AI 에이전트로 전달합니다.

동작을 조금 더 들여다보면, 게이트웨이는 asyncio 루프를 띄워 계속 돌면서 여러 게이트웨이(텔레그램, Discord, 이메일, SMS, WhatsApp 등)를 폴링(poll)하거나 대기합니다. 그런데 각 서비스마다 폴링 방식이 다릅니다.

  • 웹훅(webhook) 으로 동작하는 것.
  • 작은 폴링 루프로 동작하는 것. 예를 들어 텔레그램은 매초 도는 작은 루프로 텔레그램 API에 "새 메시지 있니?"를 물어볼 수 있습니다.
  • 웹소켓(websocket) 으로 동작하는 것.

이처럼 서드파티 게이트웨이마다 연동을 독립적으로 설정해야 하므로, 게이트웨이는 하나의 만능 시스템이 아니라 각각 따로 구성해야 하는 시스템입니다. 이 설정은 hermes setup gateway로 이뤄지고, 예를 들어 텔레그램이라면 봇 ID를 만들고, 에이전트와 통신을 허용할 사용자 ID들을 등록하는 식입니다.

게이트웨이가 단순히 메시지를 받기만 하는 게 아니라는 점이 중요합니다. CLI처럼 대화 전체를 계속 들고 있는 직접 인터페이스가 아니기 때문에, 게이트웨이는 컨텍스트를 매번 처음부터 조립(construct context)해야 합니다. 루프의 "build context" 단계가 사실은 게이트웨이 맥락에서 더 잘 이해됩니다. 매 메시지마다 컨텍스트를 밑바닥부터 다시 짓기 때문입니다.

예를 들어 게이트웨이가 텔레그램에서 메시지를 받으면, 받는 것은 딱 그 한 개의 메시지일 뿐 대화 전체가 아닙니다. 그래서 게이트웨이가 메시지 히스토리를 직접 조립해야 합니다. 이 히스토리는 세션 ID로 식별합니다. 식별자의 첫 부분은 게이트웨이 이름(예: telegram)이고, 여기에 텔레그램이 돌려준 세션 ID, 그리고 몇 개의 추가 ID를 이어 붙입니다. 이 모든 것은 로컬 SQLite 데이터베이스에 저장됩니다.

동작 순서를 정리하면 이렇습니다. 새 텔레그램 메시지가 오면, 게이트웨이는 telegram 접두사에 텔레그램이 돌려준 세션 ID를 붙여 SQLite에서 그 대화의 모든 메시지 히스토리를 조회하고, 이를 컨텍스트에 붙여 AI 에이전트로 보냅니다. 만약 게이트웨이가 메시지를 받기만 했다면 메시지 히스토리라는 것 자체가 없었을 텐데, 이렇게 히스토리가 만들어지는 것입니다.

게이트웨이에는 일종의 세션 매니저(session manager)도 있습니다. 이것은 어떤 메시지가, 그리고 언제 보낸 메시지가 실제로 LLM에 도달할지를 관리합니다. 예를 들어 에이전트가 아직 이전 메시지를 처리하는 중에 새 메시지를 보내면, 세션 매니저가 그것을 어떻게 다룰지 결정합니다. 이는 메시지를 보내는 방식에 따라 갈립니다.

  • 텔레그램에서 /interrupt를 쓰면 에이전트를 중단(interrupt) 시킵니다.
  • 그냥 메시지를 보내면 큐에 쌓아(queue up) 둡니다.
  • /steer를 보내면 진행 중인 작업의 방향을 틉니다(steer).

이처럼 게이트웨이는 단순 수신기가 아니라, 메시지 히스토리를 조립하고 동시성까지 조율하는 꽤 묵직한 부품입니다. 다른 종류의 에이전트를 위해 직접 코딩해 볼 만한, 재미있고 유용한 부분이기도 합니다.

메모리: 마크다운, SQLite, 외부 제공자의 3층 구조

메모리는 앞의 컨텍스트 압축과 게이트웨이, 에이전트 루프에서 이미 여러 번 스쳤지만, 별도 섹션이 필요할 만큼 중요합니다. Hermes의 메모리는 세 가지 형태로 옵니다.

첫째, 마크다운 파일입니다. 앞서 본 soul.md, 그리고 메모리 디렉터리 안의 memory.mduser.md입니다. 이들은 항상 시스템 프롬프트 바로 뒤에 컨텍스트 윈도에 붙습니다.

둘째, SQLite 데이터베이스입니다. Hermes는 지난 대화를 회상(recall)할 수 있는데, 과거 대화가 마크다운 파일로 저장되지는 않습니다. 대신 SQLite에 Hermes와 주고받은 모든 메시지, 모든 상호작용이 저장됩니다. 테이블도 여러 개, 행도 데이터 모델도 여러 개지만 본질은 같습니다. 모든 세션의 전체 트랜스크립트(full transcripts)입니다. 각 세션은 서로 다른 ID를 갖고 저장되며, 게이트웨이에서 이어지는 세션이 바로 여기서 조회됩니다. 예를 들어 텔레그램 대화라면, SQLite에 세션 ID와 "텔레그램에서 왔다"는 식별자가 함께 있고, 그것을 끌어와 대화를 이어 갑니다.

여기에 아주 유용한 장치가 하나 더 있습니다. 대화의 텍스트만 담은 별도 테이블(bare text table)입니다. 오로지 텍스트만 담아 두어 유사도 검색(similarity search)을 쉽게 수행할 수 있게 합니다.

셋째, 외부 메모리(external memory)입니다. 이것은 기본값으로는 설정되어 있지 않습니다. 외부 메모리는 여러 외부 제공자에서 옵니다. mem0, SuperMemory, Honcho 등 Hermes가 지원하는 제공자가 여럿 있고, 모두 에이전트를 위한 지능·기억 제공에 특화되어 있습니다. 다만 제공자마다 작동 방식이 다릅니다.

  • mem0: 유사도 검색 방식을 씁니다.
  • Honcho: 또 다른 방식으로 동작합니다.
  • SuperMemory: 매 턴마다 대화 전체 히스토리를 보내 저장하게 한 뒤, 언어 모델을 이용해 그중에서 알맞은 기억을 추출합니다.

외부 메모리가 실제로 무엇을 하는지 그 조회 타이밍이 흥미롭습니다. 외부 메모리를 설정하면, 첫 메시지 이후에 외부 메모리를 조회합니다. 예를 들어 새 기능이나 워크플로에 대한 대화를 시작하면, 첫 메시지 이전에는 조회하지 않습니다. 그러나 첫 메시지를 보내고 에이전트가 이미 응답한 뒤, 즉 에이전트가 지금 무슨 이야기를 하는지 파악한 다음에는, 당신의 다음 질문이 무엇일지 추측하며 외부 메모리를 조회합니다. 이는 사람이 질문에 답한 직후 "예전에 이 주제로 나눴던 대화"를 동시에 떠올리는 것과 비슷합니다. 그렇게 떠올린 기억은 이어지는 메시지에서 드러납니다.

이 타이밍에는 실용적인 팁이 숨어 있습니다. 외부 메모리를 켜 둔 상태에서 Hermes가 첫 시도에 무언가를 기억하지 못하면, 첫 메시지에서 무엇을 떠올리려는지 설명하고 후속 메시지로 더 물어보십시오. 그때는 이미 외부 메모리 시스템을 조회한 뒤이기 때문입니다.

대부분의 사람은 외부 메모리를 켜지 않지만, 무료로 지원되는 제공자도 여럿 있고 Hermes가 사용자로부터 배우는 방식을 크게 개선해 주므로 켜 두기를 권할 만합니다. 다만 각 제공자가 풍부한 LLM 질의를 쓰는지, 시맨틱 검색을 쓰는지 등 방식이 제각각이라, 이들을 제대로 비교하려면 별도의 다룸이 필요할 만큼 방대한 주제입니다.

크론 잡: 매분 도는 tick과 jobs.json

마지막은 이런 종류의 에이전트에서 매우 특징적인 크론 잡(cron jobs)입니다. 서버를 다뤄 봤다면 크론이 익숙할 텐데, 보통은 매분 도는 시스템 프로세스가 그 시각에 예약된 작업을 꺼내 실행하는 구조입니다. Hermes도 거의 같습니다.

다만 Hermes의 크론은 서버의 크론 프로세스에 묶여 있지 않습니다. Hermes에서 크론은 자체 루프로, 매분 tick이라는 함수를 실행합니다. 매분 틱이 돌면서, 예약해 둔 크론 잡 목록을 꺼내 그 자리에서 실행합니다.

Hermes에서 크론 잡은 에이전트에게 이런 일을 시키는 장치입니다. "매일 아침 최신 AI 뉴스를 이메일로 보내줘", "매일 내 커뮤니티 Slack에 AI 발전 소식을 보내줘", "매주 금요일 상사에게 이 내용을 이메일로 보내줘" 같은 식입니다. 특정 시점에 할 일을 자동화하고, 이를 매주·매일·매월 계속 반복하게 합니다.

여기서 문서와 실제가 어긋나는 흥미로운 지점이 있습니다. 문서는 크론 잡이 SQLite에 저장된다고 말하지만, 실제 분석에서는 SQLite에서 크론 잡을 찾을 수 없었고 코드도 SQLite에서 끌어오지 않는 것으로 보입니다. 실제로 크론 잡은 평범한 JSON 파일에 저장됩니다. 위치는 .hermes/cron/jobs.json입니다. 모든 크론 잡이 프롬프트와 해야 할 일 등과 함께 이 JSON에 나열되고, Hermes는 틱을 돌 때마다 이 파일을 확인해 실행할 잡이 있는지 판단합니다. 잡을 갱신하면 이 파일이 갱신됩니다.

또한 크론 디렉터리 안에는 output 디렉터리가 있고, 그 아래 잡 ID별 디렉터리가 있습니다. 각 잡 ID 안에는 그 잡의 각 실행(run)에 대한 마크다운 파일이 쌓입니다. 정리하면, 매분 도는 단순한 함수가 jobs.json을 확인해 실행할 잡을 찾고, 있으면 실행한 뒤 결과를 해당 실행의 마크다운 파일에 저장하는 구조입니다.

마지막으로 사소하지만 중요한 트리비아가 하나 있습니다. 크론은 결과를 텔레그램 같은 특정 채널로 자동 전송하지 않습니다. 대신 홈(home) 메시징 플랫폼으로 보냅니다. 게이트웨이를 설정할 때(예: 텔레그램·Discord·Slack), 특정 사용자 ID를 그 게이트웨이의 홈으로 삼을지 묻습니다. 크론 잡이 실행되면 알림이 이 홈 통합(home integration)으로 전달됩니다. 즉 크론은 에이전트가 send message 도구를 호출해서 보내는 것이 아니라, 시스템이 직접 홈으로 알림 메시지를 전송하는 방식입니다.

Codex와 함께 쓰기: 런타임 백엔드냐, 위임 코딩 에이전트냐

앞의 조감도가 "Hermes 코어에 어떤 통로가 붙는가"였다면, 실제로 Hermes를 굴릴 때 자주 나오는 질문은 "그 코어를 무슨 모델로 돌리고, 코딩은 누구에게 시키나"입니다. 여기서 흔한 조합이 OpenAI의 Codex CLI인데, Hermes 공식 문서를 보면 두 결이 있습니다. 방향을 먼저 못 박으면, Hermes가 Codex를 감싸는 래퍼가 아니라 Hermes가 본체이고 Codex를 자기 백엔드나 코딩 툴로 붙여 쓰는 쪽입니다.

첫째, Codex를 모델 백엔드/런타임으로 씁니다. 가장 흔한 이유는 별도 API 키 없이 ChatGPT 구독으로 Hermes를 돌리기 위해서입니다. hermes auth add openai-codex로 Codex의 OAuth를 붙이면(openai-codex provider), Hermes는 자기 루프를 그대로 유지한 채 OpenAI Responses API를 당신 구독으로 호출합니다. 한 발 더 나간 옵션이 Codex App-Server Runtime(/codex-runtime codex_app_server, 옵트인)입니다. 이걸 켜면 Hermes가 openai/* 턴을 Codex CLI의 app-server에 통째로 넘겨 툴 루프 자체를 Codex가 돌립니다. 터미널·파일 편집·플랜·샌드박스는 Codex 내장 툴(shell·apply_patch·update_plan)이 담당하고, Codex에 깔아 둔 네이티브 플러그인(Linear·GitHub·Gmail 등)이 자동 이관되며, Hermes의 고유 툴(웹 검색·브라우저·비전 등)은 Hermes를 MCP 서버로 역등록해 콜백으로 부릅니다. 이때 Hermes는 세션 DB·게이트웨이·메모리·스킬 리뷰라는 "바깥 껍데기" 역할을 맡습니다. 다만 이 런타임에서는 delegate_task·memory·session_search·todo처럼 에이전트 루프 상태가 필요한 툴은 쓸 수 없어, 필요하면 /codex-runtime auto로 되돌립니다.

둘째, Codex를 위임 코딩 서브에이전트로 씁니다. Hermes가 자기 루프를 돌면서, 기본 번들 스킬 autonomous-ai-agents/codex를 통해 terminal 툴로 codex를 셸아웃해 코딩을 시키는 방식입니다.

terminal(command="codex exec 'Add dark mode toggle to settings'", workdir="~/project", pty=true)

긴 작업은 --full-auto(샌드박스 자동승인)나 --yolo(무샌드박스)로 background 실행한 뒤 process 툴로 폴링·입력하고, 이슈별 git worktree에 Codex를 여러 개 띄워 병렬로 PR을 만들거나 codex review로 PR 리뷰를 시키기도 합니다. 전제는 codex CLI 설치, git 저장소 안(Codex는 git 밖에서 실행을 거부), 그리고 pty=true(대화형 터미널 앱이라 PTY가 없으면 멈춤)입니다.

정리하면, 첫째 방식은 "모델 엔진을 Codex 구독으로", 둘째 방식은 "코딩 일감을 Codex에게" 맡기는 것이고, 둘 다 Hermes가 본체로 남는다는 점이 핵심입니다.

마치며: 단순한 루프 하나가 "항상 배우는 비서"를 만든다

지금까지 Hermes 에이전트의 아키텍처를 고수준에서 훑었습니다. 핵심을 다시 정리하면 다음과 같습니다.

  • 조감도: AI 에이전트 코어에 CLI·게이트웨이·API 세 통로가 붙고, tools·skills·memory와 soul.md·user.md 같은 부속이 딸린 단순한 구조입니다.
  • 에이전트 루프: 메시지 → 컨텍스트 빌드 → LLM 호출 → 툴 호출 반복 → 최종 응답 → 메모리 업데이트. 이 마지막 메모리 업데이트가 "쓸수록 배우는" 성질을 만듭니다.
  • 컨텍스트: soul.md(성격), user.md(사용자 정보, 자동 갱신), memory.md(임의 사실)를 중심으로, 필요 시 과거 세션 요약·skill/tool 설명·최신 메시지가 붙습니다.
  • 컨텍스트 압축: 기본 50% 임계값. 첫 메시지는 문자 수÷4로 어림잡고, 이후에는 응답의 usage 값으로 정확히 잽니다. 넘치면 여러 섹션으로 구조화해 요약합니다.
  • 게이트웨이: 여러 메시징 서비스를 하나의 코어로 잇는 관문. 매 메시지마다 SQLite에서 세션 히스토리를 조회해 컨텍스트를 재조립하고, 세션 매니저로 interrupt·queue·steer를 조율합니다.
  • 메모리: 마크다운(항상 붙음), SQLite(전체 트랜스크립트 + 유사도 검색용 텍스트 테이블), 외부 메모리(mem0·SuperMemory·Honcho 등, 기본 비활성·첫 메시지 이후 조회)의 3층.
  • 크론 잡: 매분 도는 tick.hermes/cron/jobs.json을 확인해 예약 작업을 실행하고, 결과는 홈 메시징 플랫폼으로 전달합니다.
  • Codex와 함께 쓰기: Codex를 모델 백엔드로(ChatGPT 구독 인증의 openai-codex provider, 또는 툴 루프까지 넘기는 Codex App-Server Runtime) 쓰거나, 번들 스킬로 codex exec를 셸아웃해 코딩을 위임합니다. 어느 쪽이든 Hermes가 본체로 남습니다.

Hermes가 복잡한 프레임워크가 아니라는 점이 이 해부의 결론입니다. 겉보기에는 여러 채널에서 항상 응답하고 스스로 배우는 정교한 비서지만, 그 안에는 "메시지가 올 때마다 컨텍스트를 조립해 LLM에 넘기고, 툴을 돌리고, 배운 것을 파일과 DB에 적어 두는" 미니멀한 루프가 있을 뿐입니다. Hermes나 OpenClaw 같은 에이전트를 더 잘 쓰거나 직접 만들어 보려 할 때, 이 조감도가 좋은 출발점이 됩니다.