이벤트 발생 시 에이전트가 작동하여 승인을 받고 발주까지: Claude Managed Agent로 구현 가능성 분석
요약
본 기사는 Claude Managed Agents (CMA)를 활용하여 업무 자동화 에이전트를 구현하는 가능성을 분석했습니다. CMA는 다중 에이전트 구조, 메모리 관리, 스킬 호출 등 핵심 기능을 네이티브로 지원하며, 사람의 승인(permission policy `always_ask`)도 처리할 수 있습니다. 다만, 외부 이벤트 트리거 수신 및 사용자에게 보여줄 최종 승인 화면은 별도의 개발 계층이 필요합니다.
핵심 포인트
- CMA는 부모-자식 에이전트 구조와 메모리 관리를 네이티브로 지원합니다.
- 사람의 개입(승인)은 `always_ask` 정책과 `user.tool_confirmation`으로 구현 가능합니다.
- 외부 이벤트(Slack/이메일 등)를 트리거로 하려면 별도의 수신 계층 개발이 필요합니다.
- 전체적인 아키텍처는 CMA의 설계 매핑을 통해 구성할 수 있습니다.
손에 이런 구상도가 있었습니다. Slack이나 이메일, 상태를 트리거로 Claude Code Agent(부)가 가동되어 필요하다면 사람에게 모바일로 승인을 받고, 여러 서브 에이전트에게 작업을 할당하여, Slack 회신・이메일・리포트・발주까지 내보내는 업무 자동화의 그림입니다. 기반에는 업무 규칙・메모리・스킬・가이드라인을 '이런 작업이 오면 이 에이전트가 작동한다'로 정의해 두는 것입니다.
그림의 오른쪽 위에는 'Claude Managed Agent로 가능한가?'라고 적혀 있었습니다. 그래서 이 그림의 하나하나를 Claude Managed Agents (CMA) 기능에 대입하여, 어디까지 그대로 구성할 수 있는지 확인했습니다. 결론부터 말하자면, 부모-자식 에이전트・메모리・스킬・승인・출력은 CMA에 그대로 적용됩니다. 직접 만들어야 하는 부분은 트리거를 수신하는 부분과 승인 화면뿐이었습니다.
먼저 솔직하게 말씀드리자면, 이것은 엔드투엔드로 실제 구동까지 통한 기록은 아닙니다. CMA는 beta 버전이며, 각 기능은 공식 문서를 확인하며 '그림을 아키텍처로 번역한' 설계 매핑입니다. 코드는 구성 예시를 보여주기 위한 것이므로, 구현 시에는 최신 레퍼런스를 참고해 주십시오.
먼저 3줄로 요약하자면
- 부모 + 여러 서브 에이전트는 multiagent(coordinator+roster), 메모리는 Memory Stores, 스킬은 Skills, 업무 규칙은 agent의 system 프롬프트에 그대로 대응합니다.
- 사람의 승인은 permission policy
always_ask입니다. 발주 등의 부작용 툴로 인해 세션이 멈추고,user.tool_confirmation으로 allow/deny를 반환합니다. 대기(대기 상태 유지)는 스트림을 계속 연결하지 않아도 webhook으로 수집할 수 있습니다 - CMA가 직접 처리해주지 못하는 부분은 트리거 수신과 승인 버튼 화면입니다. 방치하여 돌릴 경우, 세션에 금액 상한선을 설정해 둘 수 있습니다.
| 용어 | 출처 | 대략적인 의미 |
|---|---|---|
| Claude Managed Agents (CMA) | Anthropic | Anthropic이 에이전트 루프와 샌드박스를 호스팅하는 매니지드형 에이전트 |
| ... | ||
| 먼저 전체적인 모습입니다. 그림의 상자를 CMA가 네이티브로 가진 것(녹색), 직접 만들 연결 부분(주황색), 사람의 개입(파란색)으로 색을 구분하여 재배열했습니다. |
대응 관계를 표로 나타내면 다음과 같습니다.
| 그림 요소 | CMA 기능 | 직접 제작 필요 여부 |
|---|---|---|
| 부모 에이전트 | Agent(multiagent: coordinator ) | — |
| 여러 서브 에이전트 | roster의 멤버 (각 subagent = thread, 병렬 위임) | — |
| 메모리 | Memory Stores (세션 교차로 영속성) | — |
| 스킬 | Skills (anthropic 제작 / 독자적) | — |
| 업무 규칙・가이드라인 | agent의 system, roster의 description으로 분배 | — |
| 사람의 승인 (모바일) | permission policy always_ask → user.tool_confirmation | |
| 알림・승인 화면 | ||
| Slack 회신・이메일・발주 | MCP 서버 + Vault(인증), 부작용은 승인 게이트 | — |
| 리포트 | 세션 출력(/mnt/session/outputs/ )→ Files API | — |
| 이벤트 (Slack/이메일/전화/주문) | — | 수신하여 sessions.create() 하는 계층 |
| 정기 트리거 | Deployment(cron) | — |
'이런 작업이 오면 이 에이전트가 작동한다'라는 그림의 하단 부분은, CMA에서는 coordinator의 system 프롬프트와 roster(각 서브 에이전트의 name과 description)으로 표현합니다. 부모는 각 멤버의 설명을 읽고 누구에게 위임할지 결정하므로, description은 부모가 읽게 하는 문장으로 작성해야 합니다.
먼저 기반이 되는 부모 에이전트입니다. 규칙(system), 스킬, 외부 연결(MCP), 그리고 위임 대상인 roster를 여기에 정의합니다. 에이전트는 한 번 만들어서 재사용하는 설정 객체이므로, 생성은 셋업 시점에 단 한 번만 합니다. 요청이 올 때마다 agents.create()
호출하면 사용되지 않는 에이전트들이 쌓여갑니다.
세션을 생성하려면 환경(컨테이너 설정 템플릿)의 ID도 필요하므로, 이것 역시 한 번만 만들어서 재사용합니다.
# 설정 (한 번만). 실제 필드는 공식 레퍼런스에 맞춥니다
import anthropic
client = anthropic.Anthropic()
...
mcp_servers와 tools의 mcp_toolset은 쌍으로 작성합니다. 서버를 선언만 한다고 연결할 곳이 정해질 뿐, 에이전트에게 도구는 나타나지 않습니다. 선언된 모든 서버는 반드시 어떤 toolset에서 이름으로 참조되어야 합니다.
{"type": "self"}을 roster에 넣어두면, 부모가 자신 자신의 복사본에게도 작업을 맡길 수 있습니다. 우선 조사 담당과 self 두 개부터 시작하여 전문 서브 에이전트를 늘려가는 것이 자연스럽습니다.
위임(委譲)에서 놓치기 쉬운 점은, 서브 에이전트가 부모의 대화 기록을 가지고 있지 않다는 것입니다. 스레드는 컨테이너와 파일 시스템을 공유하지만, 문맥은 공유하지 않습니다. '아까 건 진행해 줘'로는 통하지 않으므로, 전달할 작업에 경로(path)나 제약 조건(constraint)을 모두 적어줘야 합니다.
그림의 '메모리'는 Memory Stores에 해당합니다. 세션이 끝나도 남아있는 기억으로, 컨테이너에 /mnt/memory/<스토어명>/로 마운트되어 에이전트는 일반 파일 조작으로 읽기/쓰기를 합니다. 전용 메모리 도구를 외울 필요가 없습니다.
store = client.beta.memory_stores.create(
name="업무 메모",
description="고객별 문맥・과거 판단・정형 절차를 저장합니다. 작업 전에 반드시 확인하세요.",
...
description은 에이전트에게 전달되므로, 인간을 위한 설명이 아니라 모델에 읽히는 글로 작성해야 합니다. 세션에 붙일 때는 resources에 넣지만, 메모리 스토어는 세션 생성 시에만 붙일 수 있습니다. 나중에 resources.add()로 추가할 수는 없으니, 여기만 순서에 주의가 필요합니다.
업무 규칙과 가이드라인은 agent의 system 프롬프트에 작성하는 것이 기본입니다. '클레임은 즉시 에스컬레이션', '할인은 10%까지'와 같은 판단 기준을 여기에 모아둡니다.
참고로, 메모리 스토어에 인증 정보를 적게 해서는 안 됩니다. 한 번 기록된 키는 이후 그 스토어를 읽는 모든 세션에 재생됩니다. 키의 보관 장소는 후술할 Vault입니다.
가장 오해하기 쉬운 부분이 여기입니다. CMA는 Slack이나 이메일, 전화 등을 스스로 듣고 있는 것이 아닙니다. 외부 이벤트를 받아 sessions.create()하는 얇은 계층(layer)은 직접 준비해야 합니다.
# 당신의 Webhook 핸들러 안에서
session = client.beta.sessions.create(
agent=lead.id,
...
initial_events를 전달하면, 세션 생성과 첫 번째 지시 전송이 한 번의 호출로 끝납니다. 이때 세션은 idle을 거치지 않고 바로 running으로 만들어지므로, 'idle에서 running으로의 전환을 기다려 착지를 감지한다'는 방식으로 작성하면 영원히 기다리게 됩니다. 생성 응답의 status를 보는 것이 정답입니다.
그림의 '전화'는 CMA에 넣기 전에 음성에서 텍스트로 변환하는 과정이 별도로 필요합니다. 그 과정을 거치면, 나머지는 다른 이벤트와 같은 취급을 받습니다.
'매일 아침 9시에 보고서'와 같은 주기적 트리거는 Deployment에서 네이티브하게 구성할 수 있습니다. 여기는 수신 계층(receiving layer)을 작성할 필요가 없습니다.
client.beta.deployments.create(
name="일일 보고서",
agent=lead.id,
...
생성 응답의 schedule.upcoming_runs_at에 다음 실행 시간이 들어오므로, cron식 해석이 이 의도와 맞는지 그곳에서 확인할 수 있습니다. 실행은 부하 분산 때문에 지연될 수 있으며, 간격의 15%까지, 최대 9분 지연됩니다. 매시간 실행이라면 9분 지연될 수 있으니, 후속 처리에 엄격한 마감 시간을 두는 설계는 피하는 것이 좋습니다.
여름철 처리도 순탄치 않습니다. 벽시계 시간과 일치하는 것을 보기 때문에, 봄에 지나는 날에는 존재하지 않는 시간이 건너뛰어지고, 가을에 돌아오는 날에는 두 번 존재하는 시간이 두 번 발동합니다. 오전 1시부터 3시를 피하거나 UTC로 설정하는 것이 안전합니다.
그림의 '사람이 모바일에서 승인'에 해당하는 것이 permission policy의 always_ask입니다. 발주와 같이 부작용이 있는 도구에 이것을 붙이면, 에이전트가 그 도구를 호출하려는 순간 세션이 idle 상태가 되어 사람의 판단을 기다립니다. 읽기 전용 작업은 자동으로 통과시키고 위험한 작업만 멈추게 하는 식으로 설정할 수 있습니다.
tools=[{
"type": "agent_toolset_20260401",
"default_config": {"enabled": True,
...
발주처럼 외부 서비스를 호출하는 도구는 MCP를 통해 이루어집니다. mcp_toolset에도 같은 형태의 default_config와 configs가 통한다는 보고는 있지만, 공식 API 레퍼런스에는 {type, mcp_server_name}의 최소 형태만 나와 있습니다. 여기는 직접 확인한 후에 본 서비스에 적용하는 것이 좋을 부분입니다.
승인을 기다리는 쪽은 이벤트 스트림에서 agent.tool_use를 감시하고, evaluated_permission이 `
같은 경로를 거칩니다. 반면, 샌드박스에서 보이는 것은 더미 값이기 때문에, 키 형식을 로컬에서 검증하는 클라이언트는 시작 시 차단될 수 있습니다. 그 경우에는 이 메커니즘의 부작용을 의심해 보십시오.
리포트는 에이전트가 /mnt/session/outputs/에 작성한 파일을 Files API로 가져옵니다. Excel이나 슬라이드는 Skills에 맡기면 보기 좋게 만들 수 있습니다.
files = client.beta.files.list(scope_id=session.id,
betas=["managed-agents-2026-04-01"])
for f in files.data:
...
scope_id를 사용할 때는 베타 헤더가 2개 필요합니다. Files API의 헤더는 SDK가 자동으로 추가하지만, Managed Agents의 헤더는 직접 전달해야 합니다. 그리고 세션이 idle 상태가 된 후 파일 목록에 나타나기까지 1초에서 3초 정도 지연이 있으므로, 비어 있다면 몇 번 재시도해 주십시오.
여기는 당초 설계 매핑에서 빠져 있던 관점입니다. 이벤트 기반으로 저절로 작동하는 자동화를 프로덕션 환경에 배치하려면, 1세션당 얼마까지 사용할 수 있는지 제한하고 싶게 됩니다. CMA에는 세션 단위의 예산이 있습니다.
session = client.beta.sessions.create(
agent=lead.id,
environment_id=env_id,
...
금액은 최소 단위의 정수 문자열이며, `
| 관점 | CMA가 그대로 제공 | 직접 구현 |
|---|---|---|
| 부모-자식 에이전트 | multiagent | — |
| ... | 누락된 부분을 줍는 재고 조사 | |
| 외부 출력/발주 | MCP+Vault, 출력 파일 | MCP 서버 자체 준비 (기성품이 없을 경우) |
| 주기적 트리거 | Deployment(cron) | — |
| 사용액 상한선 | session budget | — |
| 합격 기준에 따른 반복 | Outcome (rubric 채점) | rubric 작성 |
| 이벤트 트리거 | — | Slack/메일/주문 수신 → sessions.create() |
| 전화 | — | 음성에서 텍스트로 변환 |
그림의 대부분은 CMA의 용어로 번역할 수 있었습니다. 부족한 부분은 '외부 세계의 이벤트를 CMA에 연결하는 입구'이며, 그곳은 얇은 Webhook 코드입니다. 역설적으로 에이전트 내부(부모-자식 분업/기억/승인/안전 발주)라는 어려운 부분은 거의 만들 필요가 없습니다.
한 가지 보충하자면, 이전 버전의 이 글에서 '조직 설정으로 관리자가 심은 deny/ask 규칙은 개인 자동화로는 덮어쓸 수 없다'고 적었습니다. 이것은 Claude Code의 관리 설정에 대한 이야기이며, CMA의 사양은 아닙니다. CMA의 permission policy는 에이전트와 세션에 연결되는 것이며, 조직 레벨에서 덮어쓸 수 없는 규칙을 심는 메커니즘은 참고 자료에서 찾을 수 없었습니다. 안전 측면의 보장으로 거기에 의존하는 것은 오류이므로 수정합니다.
이 그림을 그린 사람이 알고 싶었던 것은 아마도 '이거, 그림에 그린 떡밥(실현 불가능한 것) 아니지?'였을 겁니다. 대입해 본 답은 핵심은 거의 그대로 구축할 수 있지만, 트리거의 입구와 승인 화면이라는 끝 부분은 직접 만들어야 한다는 것입니다.
다시 쓰면서 깨달은 것은, 제가 무의식적으로 스트림이 계속 연결되어 있다는 전제 하에 설계하고 있었다는 점입니다. webhook과 예산 상한선을 알고 나니, 방치해 두는 자동화로서의 현실감이 한 단계 높아졌습니다. 매니지드형 에이전트는 드디어 '업무의 이벤트 기반 오케스트레이션'을 순수하게 작성할 수 있는 단계에 왔다는 것이 이번 체감입니다.
다음으로는 이 설계를 Vault・Memory・승인까지 포함하여 실제로 한 번 쭉 연결해 보고, 발주 승인이 정말로 내 기기로 알림이 오는 곳까지를 확인해 볼 생각입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기