Skip to content

체크포인팅 (Checkpointing)

실행 상태를 자동 저장해서, 크루(crew)·플로우(flow)·에이전트(agent)가 실패 후에도 이어서 재개할 수 있게 해주는 기능이에요.

개요

체크포인팅(Checkpointing)은 실행 도중 실행 상태의 스냅샷(snapshot)을 저장해 두는 기능이에요. 덕분에 크루, 플로우, 에이전트가 실패 후 이어서 재개(resume)하거나, 다른 분기(branch)로 포크(fork)할 수 있어요.

체크포인트가 캡처하는 것

체크포인트 하나에는 CrewAI가 실행 도중 상태를 그대로 재현하는 데 필요한 모든 것이 담겨요.

  • 크루·플로우·에이전트의 전체 상태 — 설정(configuration), 에이전트 메모리와 지식 소스, 태스크 진행 상황, 중간 산출물, 내부 상태와 속성
  • 킥오프 입력값(kickoff inputs)
  • 해당 시점까지의 이벤트 히스토리
  • 그 체크포인트가 어느 실행에서 왔는지 연결하는 lineage ID(계보 ID)

복원(restore)하면 그 상태를 다시 만들어 이어서 진행해요. 완료된 태스크는 건너뛰고, 메모리와 지식은 다시 채워지며(rehydrate), 이후 작업은 원래 실행이 만들었던 것과 동일한 출력을 기준으로 돌아가요.

포킹(forking)은 새로운 lineage 아래에서 동일한 복원을 수행해요. 그래서 새 분기와 원래 실행이 서로 덮어쓰지 않고 체크포인트를 나란히 쓸 수 있어요.

체크포인트가 기록되는 시점

체크포인팅은 이벤트 기반(event-driven) 이에요. 런타임이 on_events로 고른 이벤트를 구독하고, 해당 이벤트가 발생할 때마다 체크포인트를 하나씩 써요.

기본값인 task_completed는 태스크 하나가 끝날 때마다 체크포인트 한 개를 만들어요. 세밀함(granularity)과 디스크 사용량 사이에서 꽤 합리적인 절충안이에요.

llm_call_completed처럼 더 자주 발생하는 이벤트는 더 세밀한 복구가 가능하지만, 파일을 훨씬 많이 써요.

저장소 (Storage)

CrewAI에 기본으로 포함된 제공자(provider)는 두 가지예요.

  • JsonProvider — 체크포인트당 파일 한 개를 써요. 사람이 읽고 검사하기 쉬워요.
  • SqliteProvider — 단일 SQLite 데이터베이스에 저장해요. 빈번한 체크포인팅에 더 적합해요.

둘 다 max_checkpoints가 설정되면 가장 오래된 체크포인트부터 정리(prune)해요.

참고 — 이벤트 기반 자동 체크포인트 쓰기는 best-effort(최선형)예요. 쓰기 실패는 로그로 남기고 실행은 계속돼요. 반면 수동 state.checkpoint()·state.acheckpoint() 호출은 실패 시 예외를 다시 던져요(re-raise).

상속 모델 (Inheritance model)

Crew, Flow, Agent 모두 checkpoint 인자를 받아요. 자식은 부모의 값을 상속받는데, 자식이 자기 값을 설정하거나 False를 넘겨 탈퇴(opt out)하지 않는 한 그대로 물려받아요. 크루 한 곳에서 체크포인팅을 켜면 모든 에이전트가 참여하고, 특정 에이전트만 골라서 제외할 수도 있어요.

튜토리얼: 실패한 크루 재개하기

약 5분 걸리는 워크스루예요. 태스크 두 개짜리 크루를 실행하다 중간에 끊고, 저장된 체크포인트에서 재개할 거예요.

1단계 — 체크포인팅을 켠 크루 만들기

from crewai import Agent, Crew, Task

researcher = Agent(role="Researcher", goal="Research", backstory="Expert")
writer = Agent(role="Writer", goal="Write", backstory="Expert")

crew = Crew(
    agents=[researcher, writer],
    tasks=[
        Task(description="Research AI trends", agent=researcher, expected_output="bullets"),
        Task(description="Write a summary", agent=writer, expected_output="paragraph"),
    ],
    checkpoint=True,
)

2단계 — 첫 태스크 후 중단하기

result = crew.kickoff()

첫 태스크가 끝난 뒤 Ctrl+C를 누르세요. ./.checkpoints/ 안에 <timestamp>_<uuid>.json 이름의 파일이 체크포인트예요.

3단계 — 체크포인트에서 재개하기

from crewai import CheckpointConfig

result = crew.kickoff(
    from_checkpoint=CheckpointConfig(
        restore_from="./.checkpoints/<timestamp>_<uuid>.json",
    ),
)

리서치 태스크는 건너뛰고, 라이터는 저장된 리서치 출력을 바탕으로 실행되며, 크루가 끝까지 완료돼요.

어떻게 하는지 (How-to)

기본값으로 체크포인팅 켜기

crew = Crew(agents=[...], tasks=[...], checkpoint=True)

task_completed마다 ./.checkpoints/에 저장돼요.

저장소와 빈도 커스터마이즈하기

from crewai import Crew, CheckpointConfig

crew = Crew(
    agents=[...],
    tasks=[...],
    checkpoint=CheckpointConfig(
        location="./my_checkpoints",
        on_events=["task_completed", "crew_kickoff_completed"],
        max_checkpoints=5,
    ),
)

저장소 제공자 고르기

# JsonProvider
from crewai import Crew, CheckpointConfig
from crewai.state import JsonProvider

crew = Crew(
    agents=[...],
    tasks=[...],
    checkpoint=CheckpointConfig(
        location="./my_checkpoints",
        provider=JsonProvider(),
        max_checkpoints=5,
    ),
)
# SqliteProvider
from crewai import Crew, CheckpointConfig
from crewai.state import SqliteProvider

crew = Crew(
    agents=[...],
    tasks=[...],
    checkpoint=CheckpointConfig(
        location="./.checkpoints.db",
        provider=SqliteProvider(),
        max_checkpoints=50,
    ),
)

— SQLite는 WAL 저널 모드를 켜서 동시 읽기를 지원해요. 고빈도 체크포인팅이면 SQLite를 권장해요.

에이전트 하나 제외하기

crew = Crew(
    agents=[
        Agent(role="Researcher", ...),
        Agent(role="Writer", ..., checkpoint=False),
    ],
    tasks=[...],
    checkpoint=True,
)

새 분기로 포크하기

fork()는 새 lineage 아래에서 체크포인트를 복원해서, 새 실행이 원래 실행과 충돌하지 않아요.

config = CheckpointConfig(restore_from="./my_checkpoints/<file>.json")
crew = Crew.fork(config, branch="experiment-a")
result = crew.kickoff(inputs={"strategy": "aggressive"})

branch 라벨은 선택사항이고, 생략하면 자동 생성돼요.

Crew, Flow, Agent 체크포인트

# Crew — 기본 트리거: task_completed
crew = Crew(
    agents=[researcher, writer],
    tasks=[research_task, write_task, review_task],
    checkpoint=CheckpointConfig(location="./crew_cp"),
)
# Flow
from crewai.flow.flow import Flow, start, listen
from crewai import CheckpointConfig

class MyFlow(Flow):
    @start()
    def step_one(self):
        return "data"

    @listen(step_one)
    def step_two(self, data):
        return process(data)

flow = MyFlow(
    checkpoint=CheckpointConfig(
        location="./flow_cp",
        on_events=["method_execution_finished"],
    ),
)
result = flow.kickoff()
# Agent
agent = Agent(
    role="Researcher",
    goal="Research topics",
    backstory="Expert researcher",
    checkpoint=CheckpointConfig(
        location="./agent_cp",
        on_events=["lite_agent_execution_completed"],
    ),
)
result = agent.kickoff(messages=[{"role": "user", "content": "Research AI trends"}])

수동으로 체크포인트 쓰기

어떤 이벤트에든 핸들러를 등록하고 state.checkpoint()를 호출하면 돼요.

# 동기(sync)
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent

if TYPE_CHECKING:
    from crewai.state.runtime import RuntimeState


@crewai_event_bus.on(LLMCallCompletedEvent)
def on_llm_done(source: Any, event: LLMCallCompletedEvent, state: RuntimeState) -> None:
    path = state.checkpoint("./my_checkpoints")
    print(f"Saved checkpoint: {path}")
# 비동기(async)
from __future__ import annotations

from typing import TYPE_CHECKING, Any

from crewai.events.event_bus import crewai_event_bus
from crewai.events.types.llm_events import LLMCallCompletedEvent

if TYPE_CHECKING:
    from crewai.state.runtime import RuntimeState


@crewai_event_bus.on(LLMCallCompletedEvent)
async def on_llm_done_async(source: Any, event: LLMCallCompletedEvent, state: RuntimeState) -> None:
    path = await state.acheckpoint("./my_checkpoints")
    print(f"Saved checkpoint: {path}")

핸들러가 세 개의 파라미터를 받으면 state 인자는 자동으로 공급돼요. 전체 이벤트 카탈로그는 Event Listeners를 참고하세요.

CLI로 살펴보기·재개·포크

crewai checkpoint
crewai checkpoint --location ./my_checkpoints
crewai checkpoint --location ./.checkpoints.db

왼쪽 패널이 체크포인트를 분기별로 묶어 보여주고, 포크는 부모 아래 중첩돼요. 체크포인트를 선택하면 메타데이터·엔티티 상태·태스크 진행 상황이 담긴 상세 패널이 열려요. Resume은 실행을 이어가고, Fork는 새 분기를 시작해요.

상세 패널에는 편집 가능한 영역이 두 곳 있어요.

  • Inputs — 원래 킥오프 입력값이 미리 채워지고, 편집 가능해요.
  • Task outputs — 완료된 태스크의 출력. 출력을 수정하고 Fork를 누르면 다운스트림 태스크가 무효화되어 수정된 컨텍스트를 기준으로 다시 실행돼요.

— "what if" 탐색에 유용해요. 포크하고, 조정하고, 관찰하기.

TUI 없이 체크포인트 검사하기

crewai checkpoint list ./my_checkpoints
crewai checkpoint info ./my_checkpoints/<file>.json
crewai checkpoint info ./.checkpoints.db

참고 (Reference)

CheckpointConfig

파라미터 타입 기본값 설명
location str "./.checkpoints" 저장 위치. JsonProvider면 디렉터리, SqliteProvider면 DB 파일 경로.
on_events list[CheckpointEventType \| Literal["*"]] ["task_completed"] 체크포인트를 트리거하는 이벤트 타입. CheckpointEventTypeLiteral이라 타입 체커가 자동완성하고 지원하지 않는 값은 거부해요.
provider BaseProvider JsonProvider() 저장 백엔드. JsonProvider 또는 SqliteProvider.
max_checkpoints int \| None None 유지할 최대 체크포인트 수. 쓰기마다 가장 오래된 것부터 정리돼요.
restore_from Path \| str \| None None from_checkpoint으로 넘길 때 복원할 체크포인트.

checkpoint 필드 값

Crew, Flow, Agent 모두 받는 값이에요.

동작
None 부모로부터 상속.
True 기본값으로 켜기.
False 명시적 탈퇴. 상속을 멈춤.
CheckpointConfig(...) 커스텀 설정.

이벤트 타입

on_events에는 CheckpointEventType 값을 조합해 넣을 수 있어요. 기본값 ["task_completed"]는 태스크 하나가 끝날 때마다 체크포인트 한 개를 쓰고, ["*"]는 모든 이벤트에 반응해요.

경고["*"]llm_call_completed 같은 고빈도 이벤트는 체크포인트를 매우 많이 써서 성능이 떨어질 수 있어요. 이런 경우 max_checkpoints와 함께 쓰세요.

지원되는 이벤트 전체 목록:

  • Tasktask_started, task_completed, task_failed, task_evaluation
  • Crewcrew_kickoff_started, crew_kickoff_completed, crew_kickoff_failed, crew_train_started, crew_train_completed, crew_train_failed, crew_test_started, crew_test_completed, crew_test_failed, crew_test_result
  • Agentagent_execution_started, agent_execution_completed, agent_execution_error, lite_agent_execution_started, lite_agent_execution_completed, lite_agent_execution_error, agent_evaluation_started, agent_evaluation_completed, agent_evaluation_failed
  • Flowflow_created, flow_started, flow_finished, flow_paused, method_execution_started, method_execution_finished, method_execution_failed, method_execution_paused, human_feedback_requested, human_feedback_received, flow_input_requested, flow_input_received
  • LLMllm_call_started, llm_call_completed, llm_call_failed, llm_stream_chunk, llm_thinking_chunk
  • LLM Guardrailllm_guardrail_started, llm_guardrail_completed, llm_guardrail_failed
  • Tooltool_usage_started, tool_usage_finished, tool_usage_error, tool_validate_input_error, tool_selection_error, tool_execution_error
  • Memorymemory_save_started, memory_save_completed, memory_save_failed, memory_query_started, memory_query_completed, memory_query_failed, memory_retrieval_started, memory_retrieval_completed, memory_retrieval_failed
  • Knowledgeknowledge_search_query_started, knowledge_search_query_completed, knowledge_query_started, knowledge_query_completed, knowledge_query_failed, knowledge_search_query_failed
  • Reasoningagent_reasoning_started, agent_reasoning_completed, agent_reasoning_failed
  • MCPmcp_connection_started, mcp_connection_completed, mcp_connection_failed, mcp_tool_execution_started, mcp_tool_execution_completed, mcp_tool_execution_failed, mcp_config_fetch_failed
  • Observationstep_observation_started, step_observation_completed, step_observation_failed, plan_refinement, plan_replan_triggered, goal_achieved_early
  • Skillskill_discovery_started, skill_discovery_completed, skill_loaded, skill_activated, skill_load_failed
  • Loggingagent_logs_started, agent_logs_execution
  • A2Aa2a_delegation_started, a2a_delegation_completed, a2a_conversation_started, a2a_conversation_completed, a2a_message_sent, a2a_response_received, a2a_polling_started, a2a_polling_status, a2a_push_notification_registered, a2a_push_notification_received, a2a_push_notification_sent, a2a_parallel_delegation_started, a2a_parallel_delegation_completed, a2a_transport_negotiated, a2a_content_type_negotiated, a2a_context_created, a2a_context_expired, a2a_context_idle, a2a_context_completed, a2a_context_pruned
  • System signalsSIGTERM, SIGINT, SIGHUP, SIGTSTP, SIGCONT
  • Wildcard"*" — 모든 이벤트에 반응.

저장소 제공자

제공자 설명
JsonProvider 체크포인트당 파일 한 개. location 안에 <timestamp>_<uuid>.json 이름으로 저장돼요.
SqliteProvider location에 있는 단일 DB 파일. WAL 저널링을 사용해요.

CLI

명령 용도
crewai checkpoint TUI 실행. 저장소 자동 감지.
crewai checkpoint --location <path> 특정 위치를 대상으로 TUI 실행.
crewai checkpoint list <path> 체크포인트 목록.
crewai checkpoint info <path> 체크포인트 파일 또는 SQLite DB의 최신 항목 검사.

실무 관점

  • 복구의 기본값은 task_completed — 태스크 단위로 복구 지점을 만들면 대부분의 실패 시나리오에서 손실이 작아요. 세밀함이 필요한 경우에만 llm_call_completed 같은 고빈도 이벤트로 올리세요.
  • 고빈도 체크포인팅은 SQLite + max_checkpoints — 파일 수가 폭발하지 않게 상한을 잡아두는 게 좋아요.
  • 포크로 "what if" 실험 — 같은 체크포인트에서 새 분기를 만들어 입력값이나 태스크 출력을 바꿔보면 원본 실행을 건드리지 않고 비교할 수 있어요.
  • 상속 모델 활용 — 크루 단위로 한 번 켜고, 제외할 에이전트만 checkpoint=False로 끄는 편이 명시적이고 관리하기 쉬워요.

더 알아보기