이벤트 리스너 (Event Listener)¶
CrewAI의 이벤트를 구독해서 커스텀 통합·모니터링을 만드는 방법을 설명해요.
Crew가 실행되는 동안 무언가 "지켜봐야" 할 때가 있죠. 에이전트가 작업을 끝냈는지, 어떤 툴을 호출했는지, LLM에 몇 토큰을 보냈는지 같은 정보를요. 이벤트 리스너는 바로 그 지점에 내 코드를 끼워 넣는 방법이에요.
개념¶
CrewAI는 내부적으로 이벤트 버스(event bus) 아키텍처를 사용해요. 실행 전 과정에서 일어나는 일들을 이벤트로 쏴주고, 그 이벤트를 구독한 핸들러가 반응하는 구조죠. 핵심 구성 요소는 세 가지예요.
- CrewAIEventsBus — 이벤트 등록과 발행을 관리하는 싱글턴(singleton) 이벤트 버스
- BaseEvent — 시스템 안의 모든 이벤트가 상속하는 베이스 클래스
- BaseEventListener — 커스텀 이벤트 리스너를 만들기 위한 추상 베이스 클래스
예를 들어 Crew가 실행을 시작하거나, 에이전트가 작업을 끝내거나, 툴이 호출되면 시스템은 그에 맞는 이벤트를 발행해요. 우리는 필요한 이벤트에 핸들러를 등록해서, 그 순간 커스텀 코드를 실행할 수 있는 거예요.
CrewAI AMP의 Prompt Tracing 기능도 이 이벤트 시스템을 기반으로 만들어졌어요. 프롬프트·완료·관련 메타데이터를 추적하고 저장하고 시각화해 주는데, 덕분에 이런 것들이 가능해져요.
- LLM에 보낸 모든 프롬프트의 전체 이력 조회
- 토큰 사용량·비용 추적
- 에이전트 추론 실패 디버깅
- 팀원과 프롬프트 시퀀스 공유
- 여러 프롬프트 전략 비교
- 규정 준수·감사를 위한 trace 내보내기
커스텀 이벤트 리스너 만들기¶
커스텀 리스너를 만드는 절차는 네 단계예요.
BaseEventListener를 상속하는 클래스를 만든다setup_listeners메서드를 구현한다- 관심 있는 이벤트에 핸들러를 등록한다
- 적절한 파일에서 리스너 인스턴스를 생성한다
간단한 예시를 볼게요.
from crewai.events import (
CrewKickoffStartedEvent,
CrewKickoffCompletedEvent,
AgentExecutionCompletedEvent,
)
from crewai.events import BaseEventListener
class MyCustomListener(BaseEventListener):
def __init__(self):
super().__init__()
def setup_listeners(self, crewai_event_bus):
@crewai_event_bus.on(CrewKickoffStartedEvent)
def on_crew_started(source, event):
print(f"Crew '{event.crew_name}' has started execution!")
@crewai_event_bus.on(CrewKickoffCompletedEvent)
def on_crew_completed(source, event):
print(f"Crew '{event.crew_name}' has completed execution!")
print(f"Output: {event.output}")
@crewai_event_bus.on(AgentExecutionCompletedEvent)
def on_agent_execution_completed(source, event):
print(f"Agent '{event.agent_role}' has completed its task!")
여기서 핵심은 setup_listeners 메서드 안에서 crewai_event_bus.on(이벤트타입) 데코레이터로 핸들러를 등록하는 거예요. 등록된 핸들러는 이벤트가 발행될 때마다 호출돼요. 로그만 찍는 예시지만, 같은 자리에 알림 발송이나 DB 저장 같은 실제 로직을 넣으면 커스텀 통합·모니터링으로 발전시킬 수 있어요.
이벤트 핸들러 구조¶
모든 이벤트 핸들러는 두 개의 파라미터를 받아요.
- source — 이벤트를 발행한 객체
- event — 이벤트 인스턴스. 이벤트 타입별로 담긴 데이터가 달라요.
모든 이벤트는 BaseEvent를 상속해서 공통 필드를 가지는데, 대표적으로 timestamp(발행 시각)와 type(이벤트 타입을 나타내는 문자열)이 포함돼요. 그 외 필드는 이벤트마다 달라요. 예를 들어 CrewKickoffCompletedEvent는 crew_name과 output 필드를 가져요.
이벤트 타입 목록¶
커스텀 리스너를 만들 때 가장 많이 쓰는 이벤트 유형만 정리했어요. 전체 목록은 더 길지만, 우선 자주 쓰는 것부터 익히면 돼요.
Crew 이벤트¶
- CrewKickoffStartedEvent — Crew 실행 시작
- CrewKickoffCompletedEvent — Crew 실행 완료
- CrewKickoffFailedEvent — Crew 실행 실패
- CrewTestStartedEvent / CrewTestCompletedEvent / CrewTestFailedEvent — Crew 테스트 시작·완료·실패
- CrewTrainStartedEvent / CrewTrainCompletedEvent / CrewTrainFailedEvent — Crew 학습 시작·완료·실패
- CrewTestResultEvent — 테스트 결과 제공(품질 점수, 실행 시간, 사용 모델 포함)
Task 이벤트¶
- TaskStartedEvent — Task 실행 시작
- TaskCompletedEvent — Task 실행 완료
- TaskFailedEvent — Task 실행 실패
- TaskEvaluationEvent — Task 평가 실행
Tool 사용 이벤트¶
- ToolUsageStartedEvent / ToolUsageFinishedEvent / ToolUsageErrorEvent — 툴 실행 시작·완료·오류
- ToolValidateInputErrorEvent — 툴 입력 검증 오류
- ToolExecutionErrorEvent — 툴 실행 오류
- ToolSelectionErrorEvent — 툴 선택 오류
Agent·LLM·Memory 이벤트¶
- AgentEvaluationCompletedEvent / AgentEvaluationFailedEvent — 에이전트 평가 완료·실패
- LLMCallStartedEvent / LLMCallCompletedEvent / LLMCallFailedEvent — LLM 호출 시작·완료·실패
- LLMStreamChunkEvent — 스트리밍 응답 청크 수신
- MemoryQueryStartedEvent / MemoryQueryCompletedEvent / MemoryQueryFailedEvent — 메모리 쿼리 시작·완료·실패
- MemorySaveStartedEvent / MemorySaveCompletedEvent / MemorySaveFailedEvent — 메모리 저장 시작·완료·실패
MCP·지식·가드레일·Flow 이벤트¶
- MCPConnectionStartedEvent / MCPConnectionCompletedEvent / MCPConnectionFailedEvent — MCP 서버 연결 시작·완료·실패
- MCPToolExecutionStartedEvent / MCPToolExecutionCompletedEvent / MCPToolExecutionFailedEvent — MCP 툴 실행 시작·완료·실패
- KnowledgeRetrievalStartedEvent / KnowledgeRetrievalCompletedEvent — 지식 검색 시작·완료
- LLMGuardrailStartedEvent / LLMGuardrailCompletedEvent / LLMGuardrailFailedEvent — 가드레일 검증 시작·완료·실패
- FlowStartedEvent / FlowFinishedEvent / FlowFailedEvent — Flow 실행 시작·완료·실패
Human In The Loop 이벤트¶
- FlowInputRequestedEvent —
Flow.ask()로 사용자 입력을 요청할 때 - HumanFeedbackRequestedEvent —
@human_feedback데코레이터가 붙은 메서드가 사람의 리뷰를 기다릴 때
이벤트별로 담긴 필드는 제각각이에요. 예를 들어 CrewTestResultEvent는 품질 점수·실행 시간·사용 모델을 담고, FlowPausedEvent는 flow 이름·ID·메서드 이름·현재 상태·피드백 요청 메시지를 담아요. 필요한 시점에 해당 이벤트의 필드를 확인하면 됩니다.
실무 관점¶
이벤트 리스너는 "Crew가 돌아가는 동안 무슨 일이 일어났는지"와 "그때마다 내 코드를 어떻게 실행할지"를 다루는 곳이에요. 실무에서 자주 쓰는 패턴은 몇 가지로 좁혀져요.
- 모니터링·알림 —
CrewKickoffFailedEvent나TaskFailedEvent를 구독해서 실패 시 슬랙·이메일 알림을 보내요. - 로깅·감사 —
ToolUsageStartedEvent같은 이벤트를 DB에 쌓아서 누가 어떤 툴을 어떻게 썼는지 추적해요. - 보조 데이터 수집 —
CrewTestResultEvent의 품질 점수를 수집해서 모델·데이터셋을 비교해요. - 운영 인프라 연동 —
FlowInputRequestedEvent를 구독해서 사람의 승인 흐름을 외부 시스템과 연결해요.
처음 만들 때는 관심 있는 이벤트를 하나 골라 로그부터 찍어 보는 걸 추천해요. 전체 목록을 한 번에 다 붙잡기보다, "지금 필요한 동작"에 해당하는 이벤트부터 익히는 게 좋아요. 나중에 A2A(에이전트 간 통신)·MCP·스트리밍 같은 더 넓은 이벤트가 필요해지면 그때 해당 카테고리를 살펴보면 돼요.
더 알아보기¶
- CrewAI 공식 문서의 이벤트 리스너 페이지 — https://docs.crewai.com/edge/en/concepts/event-listener
- 각 이벤트 타입에 담긴 필드 전체 목록은 위 페이지의 "Available Event Types" 섹션을 참고하세요.
- CrewAI AMP의 Prompt Tracing은 이 이벤트 시스템 위에서 동작하므로, 추적 기능을 만들 때 함께 보면 좋아요.