
ACP로 여러 AI 에이전트를 병렬 구동하는 멀티 에이전트 감독 앱을 만들었다
요약
ACP(Agent Client Protocol)를 활용하여 여러 AI 에이전트를 병렬로 구동하고 감독할 수 있는 Mac 네이티브 앱 개발 사례를 소개합니다. 여러 에이전트의 대화를 나란히 배치하고, 권한 요청을 하나의 Inbox로 통합하며, Apple Intelligence를 통해 온디바이스로 요청을 심사하는 구조를 제안합니다.
핵심 포인트
- ACP를 사용하여 서로 다른 AI 에이전트를 공통 실행 모델로 병렬 구동
- 분산된 에이전트의 Permission 요청을 하나의 Inbox로 집약하여 관리
- Apple Intelligence를 활용한 온디바이스 요청 요약 및 리스크 판정
- 에이전트의 실행 상태(작업 중, 대기 중 등)를 한눈에 파악하는 감독 중심 UX
ACP 대응 AI 에이전트를 여러 개 실행하여 각각에게 서로 다른 업무를 맡긴다. 진행 중인 대화는 옆으로 나란히 배치하고, 모든 에이전트로부터 전달되는 Permission(권한) 요청은 하나의 Inbox(수신함)로 모은다. 여기에 더해 Apple Intelligence가 요청을 로컬에서 심사하여, 조건을 충족하는 것만 단 한 번에 한해 대리 승인한다.
그런, 여러 AI 에이전트를 동시에 구동하는 것에 특화된 네이티브 Mac 앱을 만들고 있다.

여러 대화를 나란히 배치하고, 왼쪽의 Permission Inbox로 인간의 판단이 필요한 요청을 집약한 화면.
만든 것은 「AI 채팅」이 아니라, 에이전트의 감독 측면
이 앱의 핵심은 다음 세 가지로 이루어져 있다.
- ACP (Agent Client Protocol)를 사용하여, 서로 다른 AI 에이전트를 공통의 실행 모델로 병렬 구동한다
- 여러 대화를 동시에 표시하고, 모든 세션의 Permission 요청을 독립된 Inbox로 집약한다
- Apple의 Foundation Models framework를 사용하여, 요청 요약, 리스크 판정, 제한적인 대리 승인을 온디바이스 (On-device)로 수행한다
중요한 것은 여러 채팅을 하나의 앱에 몰아넣은 것이 아니다. 에이전트마다 분산되어 있던 실행 상태와 인간에게 돌아오는 판단을, 감독할 수 있는 형태로 재구성했다는 점이다.
본고에서는 이 앱을 케이스 스터디로서, ACP를 여러 에이전트의 런타임 경계로 하는 설계, 여러 패널과 Permission Inbox의 UX, Apple Intelligence에 안전하게 판단을 위임하기 위한 메커니즘을 소개한다.
본고의 앱은 개발 중이며, 화면이나 사양은 향후 변경될 가능성이 있다. 게재된 화면은 실제 UI를 기사용 고정 fixture로 구동한 것이며, 외부 에이전트나 실제 파일에는 접속하지 않는다. 또한, Foundation Models에 의한 판정은 안전성을 보장하는 것이 아니라, 후술할 결정론적인 제약 안에서 보조적으로 이용하고 있다.
Codex, Claude, Antigravity를 동시에 사용하고 싶었다
이 앱을 만들기 시작한 배경은 상당히 개인적인 것이다. 나는 평소 Codex, Claude, Antigravity를 동시에 사용하고 있다. 서로 다른 강점을 가진 에이전트가 있다면, 하나씩 차례대로 기다리는 것이 아니라 각각에게 별도의 업무를 맡기고 싶다.
처음에는 터미널을 여러 개 열어두었다. 병렬 실행 자체는 간단히 할 수 있다. 하지만 긴 지시를 작성하거나 대화의 흐름을 다시 읽기에는 터미널의 입력 경험이 고통스러웠다.
다음으로 Zed로 옮겼다. Zed는 ACP 대응 에이전트의 병렬 실행을 지원하지만, 표준 UI는 선택한 스레드를 하나의 Agent Panel에 표시하는 구성이라 내가 원했던 「여러 대화를 옆으로 나란히 두고 동시에 살펴보는」 형태와는 달랐다.
그 후에는 VS Code에 각각의 에이전트 확장 프로그램을 설치하여 채팅 패널을 나란히 놓고 사용하게 되었다. 이는 내가 원하던 운용에 상당히 가까웠다. 반면, VS Code 자체가 무겁고 확장 프로그램마다 UI나 조작 방법이 조금씩 다르다. 그 작은 차이가 매일 사용하다 보면 의외로 스트레스가 된다.
더 컸던 문제는 세션이 늘어났을 때의 상태 파악이었다.
- 아직 작업 중인가
- Permission을 기다리며 멈춰 있는가
- 인간의 입력을 기다리고 있는가
- 이미 완료되었는가
이것들을 확인하려면 각 패널과 대화를 순회해야 한다. Permission을 놓쳐 에이전트가 멈춘 채로 방치되는 경우도 있다. 세션이 너무 많이 열리면 애초에 무엇을 의뢰했던 대화였는지조차 떠올리기 어려워진다.
AI의 처리는 병렬화할 수 있어도, 인간의 판단이 세션마다 단절되어 있다면 운용 전체는 병렬화되지 않는다. 병목 현상이 AI의 생성 시간에서 인간의 발견과 승인으로 옮겨갔을 뿐이었다.
자신의 니즈에 맞는 것을 찾을 수 없다면 직접 만들어보자고 생각했다. 마침 ACP에 대한 이해를 깊게 하고 싶은 마음도 있었고, Mac에 탑재된 Apple Intelligence를 제대로 활용하지 못하고 있다는 느낌도 있었다. 외부 에이전트에게 업무를 맡기고, 로컬 모델에게 그 감독을 보조하게 한다면 각각의 강점을 자연스럽게 나눌 수 있지 않을까. 이것이 현재 구성의 출발점이 되었다.
AI 에이전트를 하나만 사용하고 있을 때는 채팅 중심의 UI로 큰 문제는 발생하기 어렵다. 에이전트의 발언, 도구 실행, 승인 요청이 하나의 시계열로 나열되기 때문이다.
하지만 세션이 늘어나면 대화 단위의 UI에는 한계가 보이기 시작한다. 예를 들어 3개의 세션에 기존 코드 조사, UI 수정, 테스트 실패 원인 규명을 의뢰했다고 가정하자. 잠시 다른 일을 하고 돌아오면, 3개 모두 서로 다른 상태가 되어 있다.
- 조사 세션은 추가 파일을 읽을 권한을 기다리고 있다
- 구현 세션은 파일 쓰기 권한을 기다리고 있다
- 테스트 세션은 명령 실행을 마치고 결과를 정리하고 있다
세션 목록에 읽지 않음 배지를 달면 무언가 일어났다는 것은 알 수 있다. "실행 중", "완료", "대기 중"으로 분류하면 살펴봐야 할 세션도 조금 더 알기 쉬워진다. 그럼에도 인간은 대기 중인 대화를 하나씩 열고, 직전의 문맥과 도구 호출 (Tool Call)을 읽고, 판단한 뒤, 다음 대화로 이동해야 한다.
여기서 필요했던 것은 세션을 정리하는 기능만이 아니었다.
인간이 지금 처리해야 할 판단을, 대화의 위치로부터 분리하여 모아주는 UI가 필요했다.
대화는 문맥을 이해하는 장소이며, 승인 요청은 의사결정을 처리하는 장소이다. 양자는 관련되어 있지만, 동일한 정보 구조로 다룰 필요는 없다.
"여러 개를 실행할 수 있다"만으로는 이 페인(Pain)을 해결할 수 없다
여러 AI 에이전트를 병행하여 구동하는 것뿐이라면, 전용 앱을 만들지 않아도 방법은 있다.
가장 단순한 방법은 터미널의 탭이나 팬(Pane)을 여러 개 열고, 각각에서 CLI형 에이전트를 실행하는 것이다. 유연하며 각 도구를 그대로 사용할 수 있다. 반면, 실행 상태나 권한(Permission)을 보여주는 방식은 에이전트마다 다르며, 어떤 팬이 입력을 기다리고 있는지도 인간이 직접 보고 판단해야 한다.
ACP 대응 에디터로는 Zed가 있다. 2026년 7월 시점의 Zed는 Parallel Agents를 지원하며, ACP External Agents를 포함한 여러 스레드를 동일한 창에서 병행 실행할 수 있다. 스레드마다 서로 다른 에이전트를 선택할 수도 있다. 따라서 "Zed에서는 여러 AI 에이전트를 병행 실행할 수 없다"라는 비교는 옳지 않다.
한편, 공식 문서에서 설명하는 표준 UI는 Threads Sidebar에서 스레드를 관리하고, 선택한 스레드를 하나의 Agent Panel에 표시하는 전환형 방식이다. 또한, Tool Permissions는 각 도구의 confirm / allow / deny를 다루지만, 공식 자료를 확인한 범위 내에서는 모든 스레드의 미처리 권한(Permission)을 독립된 인박스(Inbox)로 집약하는 기능은 찾을 수 없었다.
이는 우열의 문제라기보다, 중심에 두는 작업의 차이라고 생각한다. Zed는 코드 편집 환경 안에서 여러 작업 스레드를 띄우고, 정리하고, 전환하는 데 강점이 있다. 이번 앱에서 중심에 둔 것은, 여러 실행 주체를 동시에 관찰하고 인간에게 돌아온 판단만을 횡단적으로 처리하는 "감독면"이었다.
| 수단 | 해결할 수 있는 것 | 남는 페인 |
|---|---|---|
| 여러 개의 터미널 | 여러 CLI를 병행 실행할 수 있음 | 상태나 권한(Permission) 표현이 분산됨 |
| ... | ||
| "여러 실행", "여러 표시", "판단의 집약"은 별개의 문제다. 여러 세션을 구동할 수 있다는 것만으로는 감독의 인지 부하가 자동으로 낮아지지 않는다. |
ACP로 이종 에이전트를 동일한 실행 모델에 얹기
여러 에이전트를 공통 UI에 얹기 위한 핵심으로 삼은 것이 ACP였다.
실제 AI 에이전트는 일문일답식의 채팅이 아니다. 입력을 받은 후, 계획을 세우고, 문장을 반환하고, 도구를 호출하며, 중간 과정을 업데이트하고, 필요에 따라 권한(Permission)을 요청한다. 처리에는 긴 시간이 걸릴 수 있으며, 인간에 의한 취소도 발생한다.
앱 측에서는 다음 정보들을 문자열 로그로 뭉뚱그리지 않고, 세션의 타입이 지정된 이벤트(Typed Event)로 취급한다.
- 에이전트로부터의 메시지
- 작업 계획
- 도구 호출과 진행 상황
- 권한(Permission) 요청
- 사용 가능한 명령
- 모델이나 사고 강도 등의 설정
- 턴 완료와 취소
이렇게 파악하면, ACP는 "프롬프트를 보내고 텍스트를 받는 선"이 아니라, "에이전트라는 비동기 프로세스의 상태를 UI로 운반하는 경계"가 된다.
각 세션은 대화 이력뿐만 아니라 클라이언트, 이벤트 구독 Task, prompt Task, 스트리밍 중인 상태, 권한(Permission) 큐를 분리하여 가진다. Store 자체는 @MainActor 위에 있지만, 상태 업데이트 시에는 반드시 세션 ID를 전달한다.
struct SessionState {
let id: UUID
var messages: [Message]
...
이벤트 구독을 시작했을 때의 sessionID
를 수신 후 처리까지 전달한다.
let eventTask = Task {
let stream = await client.events()
for await event in stream {
...
여기서 이벤트 수신 시에 '현재 선택된 탭'을 참조해서는 안 된다. 비활성 상태인 에이전트로부터 메시지가 왔을 때, 전면의 입력 대상을 가로채거나 다른 대화에 이벤트를 섞어버릴 수 있기 때문이다.
또한, tool call (도구 호출)을 포함하는 긴 turn (턴)에 짧은 고정 타임아웃을 설정하면, 정상적으로 작업 중인 에이전트까지 연결을 끊어버리게 된다. 중간 과정은 session/update를 통해 받고, 정지가 필요한 경우에는 타임아웃의 부작용이 아니라 session/cancel에 의한 명시적인 조작으로 취급하고 있다.
대화를 나란히 배치하여 상태의 차이를 보이게 하기
나란히 배치하는 목적은 단순히 정보량을 늘리는 것이 아니다. 인간이 대화를 전환하지 않아도 '생성 중', '도구 실행 중', 'Permission (권한) 대기 중', '완료'와 같은 차이를 알아챌 수 있게 하는 데 있다.
인간이 여러 에이전트의 문장을 동시에 정독하는 것은 어렵다. 하지만 하나가 승인 대기 상태가 되었다거나, 두 개가 완료되었다거나, 하나는 아직 도구를 실행 중이라는 식의 상태 차이는 주변 시야로도 인식할 수 있다.
다중 세션 UI에서는 '동시에 읽을 수 있는 것'보다 '차이를 알아챌 수 있는 것'이 더 중요했다.
Permission을 모든 세션을 가로지르는 Inbox로 모으기
Permission 요청은 두 곳에 표시한다. 하나는 발생 원인이 되는 대화 내에 두는 인라인 Gate (inline Gate)이고, 다른 하나는 모든 세션의 미처리 요청을 모으는 Permission Inbox (권한 인박스)이다.
동일한 요청을 이중으로 보여주는 것처럼 보일 수 있지만, 역할은 다르다.
- 인라인 Gate는 '이 세션이 왜 여기서 멈췄는가'를 이해하는 장소
- Permission Inbox는 '인간이 지금 무엇을 판단해야 하는가'를 처리하는 장소
인라인 Gate는 국소적인 문맥 이해를 담당하고, Permission Inbox는 전체적인 감독을 담당한다.
Permission의 원본은 각 세션의 큐 (queue)에 남겨둔다. Inbox는 그것들을 이송하여 중앙 관리하는 것이 아니라, 요청을 가진 세션만을 추출한 유도된 뷰 (derived view)이다.
var inboxSessions: [SessionState] {
sessions.filter { !$0.permissionQueue.isEmpty }
}
...
이 구조는 '로컬에 소유하고, 글로벌하게 표시한다'라고 표현할 수 있다. 발생 원인의 문맥과 응답 경로를 유지한 채로, 인간의 판단만을 한곳으로 모을 수 있다.

각 요청은 발생 원인 세션에 속한 상태를 유지하며, 리스크 유형, 조작 대상, 리뷰 이유를 가로질러 비교할 수 있다.
표시는 집약하더라도, 응답 대상은 집약하지 않는다
가로지르는 Inbox에서 기술적으로 주의한 점은 Permission 응답의 배송 대상이다.
JSON-RPC의 request ID (요청 ID)는 앱 내의 모든 ACP 프로세스를 통해 유일하다고 보장할 수 없다. 서로 다른 프로세스에서 둘 다 id: 41인 요청이 발생할 수 있다.
Inbox가 request ID만 보고 응답 대상을 찾는다면, 다른 에이전트에게 승인을 보내버릴 위험이 있다.
따라서 UI는 request와 함께 소유 세션 ID를 전달한다. 우선순위가 지정된 세션이 실제로 해당 요청을 가지고 있는지 확인하고, 해당 세션의 클라이언트에게만 응답한다. 하위 호환성을 위해 모든 세션을 탐색하는 경우에도, 요청 전체가 단 한 건만 일치할 때로 한정한다. 모호하다면 응답하지 않는다.
func respond(to request: PermissionRequest, in sessionID: UUID) {
guard
sessions[sessionID]?.permissionQueue.contains(request) == true,
...
표시는 집약하더라도, 상태의 소유권과 응답 경로는 발생 원인에 남겨둔다.
다중 AI 패널은 단순히 겉모습만 위한 기능이 아니다. 분산된 응답 대상을 하나의 조작면에서 안전하게 다루기 위한 라우팅 (routing) 문제이기도 하다.
기술적인 필연은 아니지만, 귀여운 것은 두고 싶다
지금까지의 설계는 다중 세션의 상태 분리나 Permission의 응답 경로와 같은 기술적인 이유로 설명할 수 있다. 반면, 화면에 Buddy (버디)가 있다는 점이나, UI 전체가 로파이 (lo-fi)하고 약간 사이버펑크적인 CRT 스타일로 되어 있는 것에는 그리 고도의 필연성은 없다. 단순히 나의 취향이다.
Buddy를 배치한 가장 큰 이유는, 귀엽다고 생각했기 때문이다.
나는 Codex의 Pet처럼, 실용적인 도구 안에 약간의 유희(playfulness)가 들어있는 경험을 좋아한다. AI 에이전트 감독이라는 주제를 파고들다 보면, 화면은 대시보드, 경고, 리스크 분류, 승인 버튼뿐인 상태가 되기 쉽다. 그것은 기능적으로는 옳지만, 반드시 매일 열어보고 싶어지는 도구는 아니다. 업무를 지켜봐 주는 작은 파트너 정도는 있었으면 했다.
Buddy는 단순한 로고가 아니라, Permission Inbox의 상태에 반응한다. 판단 대기 상태가 없으면 조용히 화면을 지켜보다가, 요청이 도착하면 표정을 바꾸고, 몇 세션에서 몇 건이 멈춰 있는지 이야기한다. Permission 제안이나 한정 대리 승인을 수행하는 로컬 모델(Local Model)에도 무인격한 "자동 승인 엔진"이 아닌 Buddy라는 역할을 부여했다.
물론 귀엽다고 해서 권한 판단을 신뢰하는 것은 아니다. 안전 경계는 결정론적인 Safety Policy로 구축하며, Buddy는 그 안쪽에서만 작동한다. 캐릭터성과 안전성은 별개의 레이어로 취급하고 있다.
CRT풍의 외관도 취향에서 비롯되었다. AI 같은 하이테크한 것을 사용하다 보면, 그 반동으로 Old Mac 같은 오래된 컴퓨터를 만지고 싶어진다. 최신 AI 에이전트와 Apple Intelligence를 구동하는 화면을 픽셀 서체, 주사선, 시안(Cyan)과 마젠타(Magenta) 네온으로 만든다. 그 어울리지 않는 조합이 마음에 든다.
다만, 여러 세션의 정보량과 작은 픽셀 폰트의 궁합은 솔직히 그리 좋지 않다. 만들고 나서 "조금 보기 불편하네"라고 후회하는 부분도 있다 (웃음). 가독성은 앞으로도 개선하고 싶지만, Buddy와 이 세계관까지 무난하게 만들 생각은 없다. 매일 사용하는 도구이기에 효율뿐만 아니라, 스스로 만지고 싶어지는 여백도 남겨두고 싶다.
Apple Intelligence를 "로컬의 대리 승인자"로 사용하기
Permission을 한곳으로 모은다 해도, 인간이 내용을 하나씩 읽고 파악해야 하는 부담은 남는다. 그래서 Apple Intelligence의 Foundation Models framework를 사용하여, 요청 정리와 판단 보조를 온디바이스(On-device)로 수행하고 있다. 안전 조건을 충족하는 요청에 대해서는 정리나 제안뿐만 아니라, 일회성 허가까지 대리한다.
역할 분담은 명확히 했다.
- 외부 에이전트는 조사, 구현, 커맨드 실행 등의 업무를 진행한다
- ACP는 실행 상태를 전달한다
- Mac 앱은 여러 세션을 집약한다
- 로컬 모델은 인간이 이해하기 쉬운 형태로 정보를 압축한다
- 인간은 책임을 동반하는 최종 판단을 수행한다
Permission Reviewer의 출력은 자유 문장이 아니라 @Generable에 의한 좁은 구조로 제한했다.
@Generable
struct PermissionReview {
var decision: String // approve / needs_human
...
생성 후에는 문자열을 앱 측의 enum으로 변환하며, 알 수 없는 값이 반환된 경우에는 인간의 판단으로 넘긴다. Foundation Models를 이용할 수 없거나, 생성에 실패하거나, 결과를 검증할 수 없는 경우도 마찬가지이며, 외부 LLM으로 자동 전송하는 fallback은 두지 않았다.
다만, "대리 승인"이라는 이름에서 상상되는 것처럼 모델에 모든 것을 떠넘기는 방식은 아니다. 모델이 "안전"하다고 답한 것을 그대로 안전성의 근거로 삼지는 않는다.
대리 승인 전에 결정론적인 Safety Policy를 배치하기
모델에 전달하기 전에 결정론적인 보수적 게이트(Conservative Gate)를 통과시킨다. 예를 들어 다음 요청은 로컬 모델에 의한 대리 승인 대상에서 제외하고 인간에게 돌려보낸다.
- 네트워크 액세스
- 삭제 등의 파괴적인 조작
- 비밀 정보나 인증 정보와 관련된 조작
- workspace 외의 조작
- 내용을 분류할 수 없는 조작
allow_once가 제시되지 않은 요청- 안전하게 분해할 수 없는 shell command
shell command에 대해서도 안전한 shell parser를 만든 것은 아니다. 제어 연산자별로 분할하여, 제한된 read / build / test 계열의 allowlist와 대조하는 보수적인 판정 방식이다. redirect, 확장(expansion), glob, subshell, 환경 변수 대입 등을 완전히 해석할 수 없는 경우에는 인간에게 돌려보낸다.
switch SafetyPolicy.eligibility(for: input) {
case .humanOnly(let reason):
showNeedsHuman(reason)
...
대리 승인을 위해서는 다음 조건을 모두 만족해야 한다.
- 정적 정책(Static Policy)상 모델 심사 대상이어야 한다
- 모델의 제안이
approve여야 한다 - 리스크가
low여야 한다
이다 - 사용자가 이 조작을 허가한 확도가
high
이다 - Agent가
allow_once
를 제시하고 있다.
allow_always
는 대리 선택하지 않는다. 이는 일회성 조작을 허가하는 것과, 향후 동일한 종류의 조작에 대해 지속적인 권한을 부여하는 것을 동일한 강도로 취급하지 않기 때문이다.
안전성을 AI에게 맡기는 것이 아니라, 결정론적인 경계(Deterministic Boundary) 안에서 인간의 인지 부하를 낮추기 위해 AI를 사용한다.

수동, 제안만 하는 Shadow Mode, 제한적 대리 승인을 단계적으로 선택할 수 있다.
대리 승인은 Shadow Mode부터 단계적으로 도입한다
Permission 심사는 다음 3단계로 나누었다.
- Manual: 모든 것을 인간이 판단한다
- Suggest: 모델이 제안과 이유를 표시하지만, 응답은 인간이 수행한다
- Limited: 정적 정책(Static Policy)과 모델 판정 조건을 충족하는 경우에만, 일회성 허가를 대리한다
특히 중요한 것이 Suggest, 이른바 Shadow Mode였다.
모델의 분류 정확도뿐만 아니라, 인간이 "이 이유라면 판단 근거로 사용할 수 있다"라고 느끼는지를 관찰할 수 있다. 분류가 정확하더라도 이유가 너무 추상적이면 감독 보조로서 도움이 되지 않는다. 반대로 결론이 인간과 다르더라도, 놓치고 있던 논점을 제시할 수 있다면 가치가 있다.
자동화를 단순한 On/Off로 나누지 않고, 관측, 제안, 제한적 대리라는 단계로 나눈다. 책임을 동반하는 AI 기능에서는 유효한 도입 방법이라고 느끼고 있다.
비동기 AI에서는 "오래된 정답"을 버린다
Foundation Models에 의한 심사 중에도 앱의 상태는 변화한다. 모델이 생각하는 동안 인간이 먼저 응답할 수도 있다. 세션이 중단되거나, Agent가 요청을 철회하거나, 동일한 표시 위치에 다른 요청이 올 가능성도 있다.
판정 내용이 타당하더라도, 대상이 이미 존재하지 않는다면 적용해서는 안 된다. 따라서 일반적인 Permission review 경로에서는 결과를 반영하기 직전에 다음 사항을 재검증한다.
- 대상 세션이 동일한가
- request ID와 tool call ID가 동일한가
- 요청 전체가 바뀌지 않았는가
- Permission이 아직 큐(Queue)에 남아 있는가
- 심사 입력의 fingerprint가 일치하는가
일치하지 않으면 모델의 결과를 버린다.
guard
let queued = session.permissionQueue.first(
requestID: request.id,
...
비동기 AI 기능에서는 생성 시점에 맞았는지뿐만 아니라, 적용 시점에도 여전히 맞는지 검증할 필요가 있다.
이는 Permission에 국한된 이야기가 아니다. AI를 이용한 입력 보완, 태스크 추출, 분류, 요약에서도 추론 중에 원본 데이터가 바뀌면 동일한 문제가 발생한다. 프롬프트나 모델 선정뿐만 아니라, "늦게 돌아온 정답을 어떻게 다룰 것인가"도 앱 설계의 일부가 된다.
대화를 감독 가능한 정보로 변환한다
Foundation Models는 Permission 심사 외에도 사용하고 있다.
ACP 상의 대화로부터 인간이 편집할 수 있는 태스크 후보를 구조화하여 추출한다. 나아가 대화 내용과 보드(Board) 상의 태스크를 대조하여, 신규 생성, 기존 태스크와의 연관 설정, 진행 상태 업데이트를 보조한다.
여기에서도 생성 결과는 원본(Source of Truth)으로 삼지 않는다. 태스크 후보는 저장하기 전까지 비영구적이며 인간이 편집할 수 있다. Board Reconciler도 완료 상태로 자동 이동시키지 않는다. 모델을 사용할 수 없는 경우, 태스크 후보는 결정론적인 fallback으로 돌아가며, 보드 대조는 no-op(아무 작업도 하지 않음)이 된다.
클라우드의 강력한 에이전트에게는 일을 맡기고, Mac의 로컬 모델에는 그 일을 인간이 감독할 수 있는 형태로 정리하는 역할을 맡겼다. 외부 에이전트와 로컬 모델을 동일한 역할의 AI로서 경쟁시킬 필요는 없다.
멀티 에이전트 시대에 필요한 것은 "감독하는 UI"였다
여러 AI 에이전트를 다루는 UI를 만드는 과정에서 몇 가지 설계 원칙이 보였다.
- 에이전트의 문장뿐만 아니라 계획, 도구 실행, Permission 대기, 완료, 취소를 일급 객체(First-class state)로 취급한다.
- 세션 단위의 소유권을 유지하면서, 인간의 판단만을 횡단적으로 집약한다.
- AI의 능력에 따라 자동화 범위를 정하지 않고, 인간이 위임해도 좋은 책임 범위를 먼저 결정한다.
- 구조화된 출력(Structured Output), 정적 정책, 적용 조건, stale guard를 겹쳐서 각각 다른 실패를 방지한다.
- 온디바이스(On-device) 모델을 사용할 수 없는 상태를 일반적인 상황(Normal case)으로 설계한다.
- 일회성 허가와 지속적인 허가를 동일한 조작 강도로 취급하지 않는다.
AI 에이전트의 성능이 향상되고, 동시에 맡길 수 있는 업무가 늘어날수록 인간이 읽어야 할 문장은 줄어들지도 모른다. 하지만 책임을 동반하는 판단이 사라진다는 뜻은 아니다. 오히려 여러 에이전트가 병렬적으로 작동함으로써, 판단 요구의 발견, 비교, 우선순위 지정이 새로운 업무가 된다.
그때 필요한 것은 더 많은 채팅 창이 아니라, 여러 실행 상태를 조망하고 인간에게 돌아온 판단을 정리하기 위한 감독 인터페이스(Supervision interface)가 아닐까.
ACP는 서로 다른 에이전트를 동일한 채팅 UI에 연결하기 위한 메커니즘만이 아니다. 에이전트의 실행 상태를 클라이언트 측에서 재구성 가능한 이벤트(Event)로 받아들일 수 있다는 점에 가치가 있다.
Apple의 Foundation Models 또한 클라우드 에이전트의 대체재로만 파악할 필요는 없다. 단말기 상의 문맥(Context)을 인간이 판단하기 쉬운 형태로 정돈하는 감독 보조 도구로 사용할 수 있다.
AI의 업무를 병렬화한 끝에, 인간의 판단 대기 시간이 다음 병목 현상(Bottleneck)이 된다. 그 병목을 숨기는 것이 아니라, 보이는 형태로 집약하고 안전하게 조금씩 위임하는 것. 이번 구현은 이를 위한 하나의 접근 방식이다.
현재는 개인용으로 사용하고 있으며 공개 등은 하지 않은 상황이지만, 반응이 있다면 공개 및 공유 등을 고려하고 있습니다. 저와 같은 페인 포인트(Pain point)나 관심을 가진 분들에게 닿기를 바랍니다.
참고 자료
Discussion

AI 자동 생성 콘텐츠
본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기