여러 에이전트, 하나의 아키텍처: 그중 둘이 같은 시스템을 바꾸면 무슨 일이 생기는가

에이전트 셋, pull request 셋, 재시도 로직이 어디에 있어야 하는지에 대한 합리적인 의견 셋.

하나는 재시도를 HTTP 클라이언트에 넣습니다. 하나는 핸들러를 감쌉니다. 하나는 큐를 추가하고 그것을 비웁니다. 셋 중 하나만 따로 읽으면 당신은 approve할 겁니다. 같은 날 오후에 셋을 함께 읽으면, 시스템이 이제 세 군데에서 재시도하고, backoff 정책이 세 개이며, 그걸 결정한 사람은 아무도 없다는 걸 알게 됩니다.

한 코드베이스 위에서 둘 이상의 에이전트가 동시에 일하기 시작하면 문제는 이런 모양이 됩니다. 각 에이전트는 국소적으로 옳습니다. 비일관성은 전역적이고, 마지막으로 리뷰하는 사람에게만 보입니다.

왜 룰 파일은 이걸 중재하지 못하는가

표준적인 답은 룰 파일입니다: CLAUDE.md, AGENTS.md, .cursor/rules. 우리는 그 파일들이 왜 낡아가는지에 대해 쓴 적이 있고, 낡는 것은 여기서 더 작은 문제입니다. 더 큰 문제는 룰 파일이 중재를 할 수 없다는 것입니다.

그건 산문입니다. 같은 문단을 받은 두 에이전트는 그 문단에 대한 두 가지 다른 독해를 만들어 냅니다. 둘 다 방어 가능하고, 그 독해들이 만나는 지점은 어디에도 없습니다. 룰 파일은 저장소 단위로 존재하므로, 서비스 경계에 관한 규칙은 한 저장소에 있고 그 경계 반대편의 서비스는 다른 저장소에 있습니다. 그리고 상태가 없습니다. 다른 에이전트가 40분 전에 무언가를 제안했다는 사실을 알 수 없습니다. 그건 파일이고, 파일은 아무것도 알지 못하기 때문입니다.

중재를 위해 필요한 건 더 나은 산문이 아닙니다. 두 에이전트가 함께 읽고 쓰며, 결정을 담을 수 있고, 불일치를 알아차릴 수 있는 공유된 무언가입니다.

모델이 주고 문서가 주지 않는 것

아키텍처 모델은 질의할 수 있는 요소와 관계입니다. 시스템, container, component, 그 사이의 엣지, 그리고 그 엣지에 붙어 있는, 설계를 설계로 만들어 주는 것들: 경계를 의도적인 것으로 만든 결정, 알려야 할 오너, consumer가 의존하는 contract.

여기서 세 가지가 따라 나오고, 각각은 의도가 아니라 메커니즘입니다.

모든 에이전트가 같은 바이트를 읽을 수 있다. generate-context 액션은 모델로부터 archyl.txt를 씁니다. 기본은 markdown이고, 선택적으로 저장소에 자동 commit할 수 있습니다. 아홉 개의 에이전트가 생성된 파일 하나를 읽는 것은, 아홉 개의 에이전트가 각자 산문 문서를 바꿔 말하는 것과는 다른 상황입니다. 영리한 방법은 아닙니다. 그냥 공유될 뿐입니다.

불일치는 들어오는 길에 잡을 수 있다. conformance-check 액션은 pull request가 변경한 파일에 대해 아키텍처 규칙을 실행하고, 위반을 인라인으로 주석 달고, 당신이 고른 심각도에서 검사를 실패시킵니다. fail-onerror, warning, none을 받습니다. "재시도는 클라이언트에 둔다"가 문장이 아니라 규칙이라면, 그것을 다른 곳에 둔 두 에이전트는 리뷰가 아니라 CI에서 그 사실을 알게 됩니다.

결정이 머물 곳이 있다. ADR은 그것이 제약하는 C4 요소에 붙습니다. 그 큐가 존재하는 이유는 큐 위에 있지, 어떤 에이전트도 본 적 없는 3월의 Slack 스레드 안에 있지 않습니다.

우리가 틀렸던 부분

여기서부터 이 글은 좋은 아이디어에 관한 블로그 글이 아니게 됩니다.

에이전트는 모델을 직접 바꾸지 않습니다. 변경 요청을 엽니다. 사람이 검토하고 병합하는 제안입니다. 변경 요청이 생성되면 archyl은 그것이 어떤 모델 버전을 기준으로 만들어졌는지 기록합니다. 병합되면 버전이 올라갑니다. 바로 이 문제를 위해 갖고 싶은 기계 장치입니다.

우리는 그 둘을 한 번도 연결하지 않았습니다.

기준 버전은 생성 시점에 기록되었고 어디에서도 다시 읽히지 않았습니다. 즉, 다음 순서가 조용히, 그리고 완전히 통과했습니다.

  1. 에이전트 A와 에이전트 B가 둘 다 모델을 읽는다. 둘 다 버전 7을 본다.
  2. A가 변경 요청을 연다. B가 변경 요청을 연다. 둘 다 버전 7을 기준으로 한다.
  3. A의 변경 요청이 병합된다. 모델은 이제 버전 8이다.
  4. B의 변경 요청이 병합된다. 그것은 더 이상 존재하지 않는 모델을 기준으로 쓰였다.

경고도 없고, 충돌도 없고, 히스토리에 아무 기록도 없습니다. 두 번째 변경 묶음이 첫 번째 위에 내려앉고, 둘이 서로 모순된다면 그 모순이 이제 문서화된 아키텍처가 됩니다. 이건 충돌 감지를 떼어낸 병합이고, "여러 에이전트" 이야기 전체가 막아야 했던 바로 그 실패입니다.

그래서 고쳤습니다. 기준 버전이 더 이상 프로젝트와 맞지 않는 변경 요청을 병합하면 이제 409 Conflict와 두 버전을 모두 짚어 주는 메시지로 실패합니다:

architecture request is based on version 7 but the model is now at version 9;
rebase the request and merge again

다만 이것을 안전하게 만드는 건 비교가 아닙니다. 같은 순간에 도착한 두 병합은 둘 다 비교를 통과하고 둘 다 진행될 것입니다. 그 틈을 닫는 것은 버전 증가 자체를 조건부로 만드는 것입니다. 병합은 모델이 여전히 그 변경 요청이 만들어진 기준 버전에 있을 때만 모델을 앞으로 나아가게 합니다. 이미 움직였다면 병합은 나아가게 할 것을 찾지 못하고, 전체가 롤백되며, 변경은 단 하나도 적용되지 않습니다. 앞선 비교는 오직 오류 메시지가 당신이 얼마나 뒤처져 있는지 알려 줄 수 있도록 존재합니다.

400이 아니라 409라는 점은 보이는 것보다 더 중요합니다. 400에서 재시도하는 에이전트는 영원히 루프를 돕니다. 잘못된 형식의 요청은 계속 잘못된 형식이기 때문입니다. 409는 그 반대를 말합니다. 당신이 보낸 것은 괜찮았고, 다만 적용 가능하지 않게 되었다는 뜻입니다. 현재 모델을 가져와서 다시 시도하십시오.

이것이 여전히 하지 못하는 것

네 가지 한계이고, 모두 직접 확인할 수 있습니다.

어떤 에이전트도 무언가를 병합하지 않는다. 변경 요청을 병합하는 MCP tool은 없습니다. 에이전트는 제안하고, 사람이 검토하고 병합합니다. 이건 의도적인 경계이고 없앨 계획도 없습니다. 다만 그 말은 루프가 완전히 자동은 아니라는 뜻이고, 자동인 것처럼 설계하면 안 된다는 뜻입니다.

충돌 감지가 거칠다. 버전은 프로젝트 단위이지 요소 단위가 아닙니다. 같은 프로젝트의 정말로 무관한 구석을 건드리는 두 에이전트도 버전에서는 충돌합니다. 그건 틀리더라도 안전한 방향이지만, 틀린 건 틀린 겁니다.

컨텍스트 검색이 어휘적이다. find_relevant_context는 이름, 설명, 태그, 경로의 단어 겹침으로 요소에 점수를 매깁니다. 임베딩도 없고 동의어 확장도 없어서, "checkout"에 관한 작업이 OrderProcessor라는 component를 떠오르게 하지는 않습니다. 장점은 진짜입니다(결정적이고, 토큰 비용이 없고, 코드가 어디로도 전송되지 않습니다). 하지만 그건 매칭이지 이해가 아닙니다.

규칙은 스스로 쓰이지 않는다. 위의 모든 것은 누군가가 "재시도는 클라이언트에 둔다"를 적합성 규칙으로 표현했다는 것을 전제합니다. 비어 있는 규칙 집합은 아무것도 잡지 못합니다. 에이전트를 몇 개 돌리든 마찬가지입니다.

이번 주에 무엇을 할 것인가

당신이 어디에 서 있는지 알아내는 데 무언가를 살 필요는 없습니다.

당신 팀이 에이전트가 작성한 pull request를 두 개 이상 병합한 가장 최근 한 주를 고르십시오. 순서대로가 아니라 함께 읽으십시오. 그중 두 개가 같은 결정을 다르게 내렸는지 물어보고, 그다음 지금의 구성에서 무엇이 그것을 알려 주었을지 물어보십시오.

답이 "리뷰어가 알아챘다"라면, 그건 리뷰어가 아홉 개를 읽는 날까지만 통합니다.


변경 요청, 적합성 규칙, C4 모델은 archyl의 일부입니다. GitHub Actionsagent skills는 오픈 소스입니다. 함께 읽기: 왜 당신의 에이전트에게는 룰 파일이 있고 모델은 없는가, 변경 요청은 어떻게 동작하는가, 그리고 모델을 정직하게 유지하는 방법.