Hermes 에이전트들은 대화가 아니라 칸반 상태로 일을 넘긴다
목차
칸반의 상태는 카드를 정리하는 라벨이 아니라 실행 스위치다. 상태 하나가 워커를 부를지, 사람을 기다릴지, 작업을 끝낼지를 결정한다.
한 명이던 AI 에이전트를 역할별로 나눈 뒤, 일이 어디에서 이어지는지가 문제가 됐다. 에이전트 하나를 쓸 때는 대화가 곧 조율이었다. 같은 대화 안에서 일을 시키고, 결과를 받고, 바로 다음 지시를 보탤 수 있었다.
역할별 프로파일은 각각 다른 프로세스로 실행된다. 자기 모델과 기억과 지침을 가지지만 다른 프로파일의 현재 대화는 자동으로 볼 수 없다. 한 프로파일이 일을 맡기겠다고 말해도 받을 프로파일은 그 말을 듣지 못한다. 둘 다 읽을 수 있는 곳에 작업 내용을 남겨야 한다.
Hermes에서는 그곳이 칸반 보드다. 그리고 보드에 기록된 상태가 일이 시작되는 시점과 사람이 끼어드는 시점을 정한다. 이 글에서는 여러 에이전트가 보드에서 일을 주고받는 과정과 상태를 정확히 써야 하는 이유를 설명한다.
프로세스 사이의 공동 보드
Hermes의 칸반 카드는 ~/.hermes/kanban.db에 저장된다. 카드에는 제목과 본문, 담당 프로파일, 상태가 들어간다. 일을 맡기는 쪽은 대화에서 빠진 배경과 완료 조건을 본문에 적고, 담당자를 지정한다. 일을 받는 쪽은 자기 대화 기록이 아니라 이 카드를 읽고 시작한다.
보드 옆에서는 디스패처가 기본 60초마다 실행된다. 디스패처는 실행할 수 있는 상태의 카드를 찾아 원자적으로 선점하고, 담당 프로파일을 별도 워커 프로세스로 띄운다. 부모 작업이 끝났는지, 이미 다른 워커가 선점했는지, 현재 상태가 실행 대상인지도 이때 확인한다. 알림을 받아 움직이는 구조가 아니라 주기마다 보드를 확인하러 가는 구조다.
디스패처는 카드를 만든 사람의 머릿속이나 그 사람의 대화를 읽지 않는다. 카드에 적힌 내용과 보드 상태를 읽는다. 사람이 아직 내용을 확인하지 않았더라도 카드가 실행 가능한 상태라면 워커를 띄운다. 그래서 상태는 진행 상황을 보여주는 표시를 넘어, 다음 동작을 정하는 계약이 된다.
8가지 상태의 동작 유형
Hermes 칸반에는 8가지 상태가 있다. 각 상태가 디스패처와 사람에게 요구하는 동작은 다음과 같다.
| 상태 | 계약 |
|---|---|
triage | 거친 아이디어를 두고 사람의 범위 확인을 기다리는 비실행 칼럼 |
todo | 모든 부모 카드가 done이 될 때까지의 의존성 대기 |
ready | 다음 디스패처 틱에 선점 가능한 실행 대기 |
running | 워커가 선점한 실행 중 작업 |
blocked | 해결되지 않은 조건에 따른 진행 중지 |
review | 구현 완료 후 검토 대기, 설정에 따른 리뷰 워커 선점 대상 |
done | 작업과 인수인계가 끝난 완료 상태 |
archived | 추가 실행 없이 남겨 두는 보관 상태 |
이 동작은 hermes_cli/kanban_db.py와 hermes_cli/config_defaults.py에서 확인할 수 있다. 상태 이름이 같아 보여도 모든 상태가 단순한 대기열은 아니다. todo는 부모 작업을 기다리고, ready는 워커 실행을 기다린다. done과 archived는 더 실행되지 않지만 하나는 완료이고 다른 하나는 보관이다.
triage, blocked, review에는 사람이 끼어들 이유가 들어 있다. 아직 범위를 다듬어야 하거나, 진행 조건이 풀리지 않았거나, 결과를 보고 판단해야 한다. 반대로 ready는 더 물을 것이 없다는 선언이다. 카드 본문이 충분해 보여서가 아니라 상태가 그렇게 계약한다.
triage와 ready의 실행 시점
부모가 없는 카드는 별도 상태를 지정하지 않으면 ready로 만들어진다. 디스패처는 다음 확인 주기, 최대 60초 안에 그 카드를 선점한다. 카드를 만들고 사람에게 내용을 알리는 사이에도 실행은 기다리지 않는다. 사람이 알림을 읽고 범위를 보태거나 지시를 고치려 할 때 워커는 이미 파일을 열어 작업 중일 수 있다.
triage에 세운 카드는 다르게 움직인다. 디스패처의 실행 후보에 들어가지 않으므로 시간이 아무리 지나도 워커가 뜨지 않는다. 사람이 내용을 확인하고 ready로 옮긴 뒤에야 실행이 시작된다. 기다리는 시간을 길게 잡는 방법이 아니다. 사람의 확인 전에는 실행 스위치를 꺼 두는 방법이다.
ready는 빠른 대기열이 아니다. 실행해도 된다는 승인이다. 범위나 완료 조건을 사람이 더 확인해야 한다면 먼저 triage에 두고, 확인이 끝났을 때 ready로 옮겨야 한다.
blocked 해제의 전제 조건
blocked는 잠깐 쉬는 버튼이 아니다. 현재 조건에서는 워커가 계속할 수 없다는 신고다. 자격증명, 승인, 외부 파일처럼 막은 조건이 그대로인데 카드를 풀면 워커는 다시 일어나 같은 이유로 멈춘다.
Hermes에는 이 반복을 끊는 장치가 있다. 같은 이유로 두 번 풀렸다가 두 번 막히면 BLOCK_RECURRENCE_LIMIT = 2에 걸린다. 카드는 다시 blocked에 머물지 않고 triage로 이동한다. 자동 해제와 재차단의 반복을 멈추고 사람의 결정을 받기 위한 경로다.
여기에 kanban.auto_decompose: true가 함께 켜져 있으면 다음 동작이 겹친다. 반복 감지기는 사람의 결정을 받기 위해 카드를 triage로 보내지만, 자동 분해기도 같은 상태의 카드를 처리한다. 분해기가 먼저 카드를 집으면 사람이 판단하기 전에 자식 카드가 만들어질 수 있다.
이 흐름에서 먼저 확인할 것은 설정을 끌지 여부가 아니다. blocked를 만든 조건이 실제로 해소됐는지다. 조건이 남아 있다면 상태도 그대로 두어야 한다. 조건 없이 해제하면 재실행, 재차단, 반복 감지, triage 이송이 차례로 일어난다.
review의 자동 실행 설정
review는 구현이 끝난 결과를 검토하는 칼럼이다. 그러나 기본 설정 kanban.review_dispatch: true에서는 이 상태도 디스패처의 실행 후보가 된다. 리뷰어 워커가 자동으로 떠서 결과를 승인하거나 수정 요청을 남긴다.
자동 리뷰가 필요한 운영에서는 이 동작이 맞다. 다만 사람이 직접 결과를 보려고 review에 세웠다면 기대와 실제 동작이 달라진다. 같은 프로파일이 담당한 카드라면 자신이 쓴 결과를 같은 프로파일이 다시 검토할 수도 있다. 검토의 품질과 별개로 사람이 개입할 대기 시간이 생기지 않는다.
kanban.review_dispatch: false로 두면 디스패처는 조회 단계부터 review 행을 건너뛴다. 워커가 카드를 잡은 뒤 사람에게 양보하는 방식이 아니라 처음부터 실행 후보에 올리지 않는 방식이다. 그래서 사람의 검토 칼럼을 운영하려면 상태 이름만 정할 것이 아니라 그 상태를 자동 실행이 처리하는지도 함께 정해야 한다.
triage와 review의 목적은 다르다. 하나는 실행 전 범위를 다듬는 상태이고 다른 하나는 실행 후 결과를 검토하는 상태다. 둘 다 사람의 개입을 기대한다면 해당 상태를 자동화가 처리하는지까지 확인해야 한다.
기본값과 설정이 나누는 사람의 위치
에이전트 하나를 쓸 때는 대화가 곧 작업 지시이자 조율이었다. 한 대화 안에서 요청을 고치면 다음 답변에 바로 반영됐다. 역할별 프로파일로 나누면 그 즉시성이 사라진다. 각 프로파일이 서로 다른 프로세스에서 실행되기 때문에, 일은 모두가 읽을 수 있는 보드를 거쳐야 한다.
그래서 남는 질문은 사람이 그 보드의 어디에 서느냐다. 앞에서 본 세 상태를 설정에 따라 나란히 놓으면 이렇다.
| 상태 | 기본값 | 설정을 바꾼 경우 |
|---|---|---|
triage | 자동 분해기가 카드를 받아 자식 카드로 분해 | 사람의 확인 전까지 비실행 |
blocked | 사람의 해제 대기 | 동일, 해제 전 조건 확인이 전제 |
review | 리뷰어 워커의 자동 선점 | 실행 후보에서 제외, 사람의 검토 대기 |
기본값에서 보드는 끝까지 스스로 굴러간다. 사람은 언제든 코멘트를 달거나 카드를 옮겨 끼어들 수 있지만, 끼어들지 않아도 일은 끝난다. 감시하는 자리이지 필수 관문은 아니다. human-on-the-loop이라고 부르는 배치다.
예외가 하나 있다. blocked는 기본값에서도 사람을 기다린다. 워커가 스스로 진행 불가를 신고한 상태이므로 자동으로 풀리지 않는다. 다만 이 정지도 영구적이지 않다. 조건 확인 없이 해제하기를 반복하면 반복 감지기가 카드를 triage로 보내고, 자동 분해기가 켜져 있으면 그 자리에서 다시 실행으로 돌아간다. 기본값에서 사람이 반드시 서야 하는 자리는 이 하나뿐이고, 그마저 잘못 쓰면 흡수된다.
triage를 입구로 쓰고 review를 자동 실행에서 빼면 배치가 달라진다. 카드가 시작되기 전에 한 번, 결과가 나온 뒤에 한 번, 사람의 확인이 없으면 다음 단계로 넘어가지 않는다. 사람이 감시자가 아니라 실행 경로 안의 단계가 된다. human-in-the-loop이다.
둘 중 하나가 옳은 것은 아니다. 되돌리기 쉬운 일을 여러 개 굴릴 때는 앞의 배치가 빠르고, 되돌리기 어려운 산출물에는 뒤의 배치가 맞는다. 선택의 단위도 보드 전체가 아니라 상태 하나다. review만 사람에게 맡기고 triage는 분해기에 넘기는 조합도 가능하다.
여러 에이전트를 쓴다는 것은 모델을 여러 개 고르는 데서 끝나지 않는다. 어느 상태를 사람의 자리로 남길지 정하는 일까지 포함한다. 설정을 건드리지 않은 상태도 그 선택의 결과다. 기본값은 사람 없이 끝까지 가는 쪽으로 맞춰져 있어서, 그것을 모르고 쓰면 사람이 볼 자리라고 생각한 칼럼에서 이미 다음 워커가 일하고 있다.
상태를 단순한 라벨로 읽으면 카드가 놓인 위치만 보인다. 실행 스위치로 읽으면 그 뒤에서 누가 움직이는지, 그리고 사람이 그 경로 안에 있는지 밖에 있는지가 보인다.