메인 콘텐츠로 건너뛰기

하트비트 (Gateway)

하트비트와 Cron 중 무엇을 써야 하나요? 언제 무엇을 사용할지에 대한 안내는 Automation & Tasks를 참고하세요.
하트비트는 주기적인 에이전트 턴을 메인 세션에서 실행하여, 모델이 사용자에게 과도한 메시지를 보내지 않으면서 주의가 필요한 사항을 알려줄 수 있게 합니다. 하트비트는 예약된 메인 세션 턴이며, background task 레코드를 생성하지 않습니다. 작업 레코드는 분리된 작업(ACP 실행, 서브에이전트, 격리된 cron 작업)을 위한 것입니다. 문제 해결: Scheduled Tasks

빠른 시작 (초보자)

  1. 하트비트를 활성화된 상태로 둡니다(기본값은 30m, Anthropic OAuth/token 인증(Claude CLI 재사용 포함)일 때는 1h) 또는 원하는 주기를 설정합니다.
  2. 에이전트 워크스페이스에 작은 HEARTBEAT.md 체크리스트 또는 tasks: 블록을 만듭니다(선택 사항이지만 권장).
  3. 하트비트 메시지를 어디로 보낼지 결정합니다(target: "none"이 기본값이며, 마지막 연락처로 라우팅하려면 target: "last"로 설정).
  4. 선택 사항: 투명성을 위해 하트비트 reasoning 전달을 활성화합니다.
  5. 선택 사항: 하트비트 실행에 HEARTBEAT.md만 필요하다면 가벼운 bootstrap 컨텍스트를 사용합니다.
  6. 선택 사항: 매 하트비트마다 전체 대화 기록을 보내지 않도록 격리 세션을 활성화합니다.
  7. 선택 사항: 하트비트를 활성 시간대로 제한합니다(로컬 시간).
예시 구성:
{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "last", // 마지막 연락처로 명시적으로 전달 (기본값은 "none")
        directPolicy: "allow", // 기본값: direct/DM 대상 허용; 억제하려면 "block"으로 설정
        lightContext: true, // 선택 사항: bootstrap 파일에서 HEARTBEAT.md만 주입
        isolatedSession: true, // 선택 사항: 매 실행마다 새 세션 사용(대화 기록 없음)
        // activeHours: { start: "08:00", end: "24:00" },
        // includeReasoning: true, // 선택 사항: 별도의 `Reasoning:` 메시지도 전송
      },
    },
  },
}

기본값

  • 간격: 30m (또는 감지된 인증 모드가 Anthropic OAuth/token 인증인 경우 1h, Claude CLI 재사용 포함). agents.defaults.heartbeat.every 또는 에이전트별 agents.list[].heartbeat.every를 설정하세요. 비활성화하려면 0m을 사용합니다.
  • 프롬프트 본문(agents.defaults.heartbeat.prompt로 구성 가능): Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.
  • 하트비트 프롬프트는 사용자 메시지로 그대로 전송됩니다. 시스템 프롬프트에는 기본 에이전트에 대해 하트비트가 활성화되어 있고 실행이 내부적으로 하트비트로 표시된 경우에만 “Heartbeat” 섹션이 포함됩니다.
  • 0m으로 하트비트를 비활성화하면 일반 실행도 bootstrap 컨텍스트에서 HEARTBEAT.md를 제외하므로 모델이 하트비트 전용 지침을 보지 않습니다.
  • 활성 시간(heartbeat.activeHours)은 구성된 시간대에서 검사됩니다. 시간대 밖에서는 다음 유효 시간대의 틱까지 하트비트를 건너뜁니다.

하트비트 프롬프트의 목적

기본 프롬프트는 의도적으로 폭넓게 설계되어 있습니다:
  • 백그라운드 작업: “Consider outstanding tasks”는 에이전트가 후속 작업(받은편지함, 캘린더, 리마인더, 대기 중인 작업)을 검토하고 긴급한 사항을 알리도록 유도합니다.
  • 사람 확인: “Checkup sometimes on your human during day time”는 가벼운 “필요한 것 있나요?” 메시지를 가끔 보내도록 유도하지만, 설정된 로컬 시간대를 사용해 야간 스팸은 피합니다(/concepts/timezone 참고).
하트비트는 완료된 background tasks에 반응할 수 있지만, 하트비트 실행 자체는 작업 레코드를 생성하지 않습니다. 하트비트가 매우 구체적인 작업(예: “Gmail PubSub 통계 확인” 또는 “gateway 상태 검증”)을 하도록 하려면 agents.defaults.heartbeat.prompt(또는 agents.list[].heartbeat.prompt)를 사용자 정의 본문으로 설정하세요(그대로 전송됨).

응답 계약

  • 주의가 필요한 것이 없으면 **HEARTBEAT_OK**로 응답합니다.
  • 하트비트 실행 중 OpenClaw는 응답의 시작 또는 끝HEARTBEAT_OK가 있으면 이를 ack로 처리합니다. 이 토큰은 제거되며, 남은 내용이 ackMaxChars 이하(기본값: 300)면 응답은 폐기됩니다.
  • HEARTBEAT_OK가 응답의 중간에 있으면 특별하게 처리되지 않습니다.
  • 경고의 경우 HEARTBEAT_OK를 포함하지 말고 경고 텍스트만 반환하세요.
하트비트 외 실행에서는 메시지 시작/끝의 의도치 않은 HEARTBEAT_OK가 제거되고 로그에 기록됩니다. 메시지가 HEARTBEAT_OK만으로 이루어져 있으면 폐기됩니다.

구성

{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m", // 기본값: 30m (0m이면 비활성화)
        model: "anthropic/claude-opus-4-6",
        includeReasoning: false, // 기본값: false (가능할 때 별도의 Reasoning: 메시지 전달)
        lightContext: false, // 기본값: false; true이면 워크스페이스 bootstrap 파일에서 HEARTBEAT.md만 유지
        isolatedSession: false, // 기본값: false; true이면 매 하트비트를 새 세션에서 실행(대화 기록 없음)
        target: "last", // 기본값: none | 옵션: last | none | <channel id> (core 또는 plugin, 예: "bluebubbles")
        to: "+15551234567", // 선택적 채널별 override
        accountId: "ops-bot", // 선택적 멀티 계정 채널 id
        prompt: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.",
        ackMaxChars: 300, // HEARTBEAT_OK 뒤에 허용되는 최대 문자 수
      },
    },
  },
}

범위 및 우선순위

  • agents.defaults.heartbeat는 전역 하트비트 동작을 설정합니다.
  • agents.list[].heartbeat는 그 위에 병합되며, 어떤 에이전트든 heartbeat 블록이 있으면 그 에이전트들만 하트비트를 실행합니다.
  • channels.defaults.heartbeat는 모든 채널의 가시성 기본값을 설정합니다.
  • channels.<channel>.heartbeat는 채널 기본값을 override합니다.
  • channels.<channel>.accounts.<id>.heartbeat(멀티 계정 채널)는 채널별 설정을 override합니다.

에이전트별 하트비트

어떤 agents.list[] 항목이든 heartbeat 블록을 포함하면 그 에이전트들만 하트비트를 실행합니다. 에이전트별 블록은 agents.defaults.heartbeat 위에 병합되므로 공통 기본값을 한 번만 설정하고 에이전트별로 override할 수 있습니다. 예시: 에이전트가 두 개이고, 두 번째 에이전트만 하트비트를 실행합니다.
{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "last", // 마지막 연락처로 명시적으로 전달 (기본값은 "none")
      },
    },
    list: [
      { id: "main", default: true },
      {
        id: "ops",
        heartbeat: {
          every: "1h",
          target: "whatsapp",
          to: "+15551234567",
          timeoutSeconds: 45,
          prompt: "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.",
        },
      },
    ],
  },
}

활성 시간 예시

특정 시간대의 업무 시간으로 하트비트를 제한합니다:
{
  agents: {
    defaults: {
      heartbeat: {
        every: "30m",
        target: "last", // 마지막 연락처로 명시적으로 전달 (기본값은 "none")
        activeHours: {
          start: "09:00",
          end: "22:00",
          timezone: "America/New_York", // 선택 사항; userTimezone이 설정되어 있으면 그것을 사용하고, 아니면 호스트 시간대 사용
        },
      },
    },
  },
}
이 시간대(동부 시간 기준 오전 9시 이전 또는 오후 10시 이후) 밖에서는 하트비트를 건너뜁니다. 창 안의 다음 예약 틱은 정상적으로 실행됩니다.

24/7 설정

하트비트를 하루 종일 실행하려면 다음 패턴 중 하나를 사용하세요:
  • activeHours를 아예 생략합니다(시간대 제한 없음, 기본 동작).
  • 하루 전체 창을 설정합니다: activeHours: { start: "00:00", end: "24:00" }.
같은 startend 시간을 설정하지 마세요(예: 08:00에서 08:00). 이 경우 너비가 0인 창으로 처리되어 하트비트가 항상 건너뛰어집니다.

멀티 계정 예시

Telegram과 같은 멀티 계정 채널에서 특정 계정을 대상으로 하려면 accountId를 사용합니다:
{
  agents: {
    list: [
      {
        id: "ops",
        heartbeat: {
          every: "1h",
          target: "telegram",
          to: "12345678:topic:42", // 선택 사항: 특정 topic/thread로 라우팅
          accountId: "ops-bot",
        },
      },
    ],
  },
  channels: {
    telegram: {
      accounts: {
        "ops-bot": { botToken: "YOUR_TELEGRAM_BOT_TOKEN" },
      },
    },
  },
}

필드 참고

  • every: 하트비트 간격(duration 문자열, 기본 단위 = 분).
  • model: 하트비트 실행용 선택적 모델 override (provider/model).
  • includeReasoning: 활성화하면 가능할 때 별도의 Reasoning: 메시지도 전달합니다(/reasoning on과 동일한 형태).
  • lightContext: true이면 하트비트 실행은 가벼운 bootstrap 컨텍스트를 사용하고 워크스페이스 bootstrap 파일에서 HEARTBEAT.md만 유지합니다.
  • isolatedSession: true이면 각 하트비트는 이전 대화 기록이 없는 새 세션에서 실행됩니다. cron의 sessionTarget: "isolated"와 동일한 격리 패턴을 사용합니다. 하트비트당 토큰 비용을 크게 줄여줍니다. 최대 절감을 위해 lightContext: true와 함께 사용하세요. 전달 라우팅은 여전히 메인 세션 컨텍스트를 사용합니다.
  • session: 하트비트 실행용 선택적 세션 키.
    • main (기본값): 에이전트 메인 세션.
    • 명시적 세션 키(openclaw sessions --json 또는 sessions CLI에서 복사).
    • 세션 키 형식은 SessionsGroups를 참고하세요.
  • target:
    • last: 마지막으로 사용한 외부 채널로 전달.
    • 명시적 채널: 구성된 모든 채널 또는 plugin id(예: discord, matrix, telegram, whatsapp).
    • none (기본값): 하트비트를 실행하지만 외부로는 전달하지 않음.
  • directPolicy: direct/DM 전달 동작을 제어합니다:
    • allow (기본값): direct/DM 하트비트 전달 허용.
    • block: direct/DM 전달 억제(reason=dm-blocked).
  • to: 선택적 수신자 override(채널별 id, 예: WhatsApp의 E.164 또는 Telegram chat id). Telegram topic/thread에는 <chatId>:topic:<messageThreadId>를 사용하세요.
  • accountId: 멀티 계정 채널용 선택적 계정 id. target: "last"일 때, 마지막으로 확인된 채널이 계정을 지원하면 그 채널에 적용되고, 그렇지 않으면 무시됩니다. 계정 id가 확인된 채널에 구성된 계정과 일치하지 않으면 전달은 건너뜁니다.
  • prompt: 기본 프롬프트 본문을 override합니다(병합되지 않음).
  • ackMaxChars: HEARTBEAT_OK 뒤에 허용되는 최대 문자 수.
  • suppressToolErrorWarnings: true이면 하트비트 실행 중 도구 오류 경고 payload를 억제합니다.
  • activeHours: 하트비트 실행을 특정 시간 창으로 제한합니다. start(HH:MM, 포함, 하루 시작은 00:00 사용), end(HH:MM, 제외, 하루 끝은 24:00 허용), 선택적 timezone이 있는 객체입니다.
    • 생략 또는 "user": agents.defaults.userTimezone이 설정되어 있으면 그것을 사용하고, 아니면 호스트 시스템 시간대로 대체합니다.
    • "local": 항상 호스트 시스템 시간대를 사용합니다.
    • 모든 IANA 식별자(예: America/New_York): 직접 사용하며, 유효하지 않으면 위 "user" 동작으로 대체됩니다.
    • 활성 창에서는 startend가 같아서는 안 됩니다. 같으면 너비가 0인 창(항상 창 밖)으로 처리됩니다.
    • 활성 시간 창 밖에서는 창 안의 다음 틱까지 하트비트를 건너뜁니다.

전달 동작

  • 하트비트는 기본적으로 에이전트의 메인 세션(agent:<id>:<mainKey>)에서 실행되며, session.scope = "global"일 때는 global에서 실행됩니다. 특정 채널 세션(Discord/WhatsApp 등)으로 override하려면 session을 설정하세요.
  • session은 실행 컨텍스트에만 영향을 주며, 전달은 targetto로 제어됩니다.
  • 특정 채널/수신자에게 전달하려면 target + to를 설정하세요. target: "last"를 사용하면 전달은 해당 세션의 마지막 외부 채널을 사용합니다.
  • 하트비트 전달은 기본적으로 direct/DM 대상을 허용합니다. direct 대상 전송은 억제하면서 하트비트 턴은 계속 실행하려면 directPolicy: "block"을 설정하세요.
  • 메인 큐가 바쁘면 하트비트는 건너뛰고 나중에 다시 시도합니다.
  • target이 외부 대상이 없는 것으로 확인되면 실행은 계속되지만 발신 메시지는 전송되지 않습니다.
  • showOk, showAlerts, useIndicator가 모두 비활성화되면 실행은 사전에 reason=alerts-disabled로 건너뜁니다.
  • 경고 전달만 비활성화된 경우에도 OpenClaw는 하트비트를 실행하고, 기한이 된 작업 타임스탬프를 갱신하고, 세션 idle 타임스탬프를 복원하고, 외부 경고 payload는 억제할 수 있습니다.
  • 하트비트 전용 응답은 세션을 활성 상태로 유지하지 않습니다. 마지막 updatedAt 값이 복원되므로 idle 만료는 정상적으로 동작합니다.
  • 분리된 background tasks는 시스템 이벤트를 큐에 넣고 메인 세션이 무언가를 빨리 인지해야 할 때 하트비트를 깨울 수 있습니다. 이 깨우기 동작이 하트비트 실행을 background task로 만들지는 않습니다.

가시성 제어

기본적으로 HEARTBEAT_OK 확인 응답은 숨겨지고 경고 콘텐츠는 전달됩니다. 채널별 또는 계정별로 이를 조정할 수 있습니다:
channels:
  defaults:
    heartbeat:
      showOk: false # HEARTBEAT_OK 숨기기 (기본값)
      showAlerts: true # 경고 메시지 표시 (기본값)
      useIndicator: true # 인디케이터 이벤트 내보내기 (기본값)
  telegram:
    heartbeat:
      showOk: true # Telegram에서 OK 확인 응답 표시
  whatsapp:
    accounts:
      work:
        heartbeat:
          showAlerts: false # 이 계정에 대한 경고 전달 억제
우선순위: 계정별 → 채널별 → 채널 기본값 → 내장 기본값.

각 플래그의 역할

  • showOk: 모델이 OK 전용 응답을 반환할 때 HEARTBEAT_OK 확인 응답을 전송합니다.
  • showAlerts: 모델이 OK가 아닌 응답을 반환할 때 경고 콘텐츠를 전송합니다.
  • useIndicator: UI 상태 화면용 인디케이터 이벤트를 내보냅니다.
세 값이 모두 false이면 OpenClaw는 하트비트 실행 자체를 건너뜁니다(모델 호출 없음).

채널별 vs 계정별 예시

channels:
  defaults:
    heartbeat:
      showOk: false
      showAlerts: true
      useIndicator: true
  slack:
    heartbeat:
      showOk: true # 모든 Slack 계정
    accounts:
      ops:
        heartbeat:
          showAlerts: false # ops 계정에 대해서만 경고 억제
  telegram:
    heartbeat:
      showOk: true

일반적인 패턴

목표구성
기본 동작 (OK는 숨김, 경고는 표시)(구성 불필요)
완전히 무음 (메시지 없음, 인디케이터 없음)channels.defaults.heartbeat: { showOk: false, showAlerts: false, useIndicator: false }
인디케이터만 사용 (메시지 없음)channels.defaults.heartbeat: { showOk: false, showAlerts: false, useIndicator: true }
한 채널에서만 OK 표시channels.telegram.heartbeat: { showOk: true }

HEARTBEAT.md (선택 사항)

워크스페이스에 HEARTBEAT.md 파일이 있으면 기본 프롬프트는 에이전트가 이를 읽도록 지시합니다. 이를 “하트비트 체크리스트”라고 생각하면 됩니다: 작고, 안정적이며, 30분마다 포함해도 안전한 내용입니다. 일반 실행에서는 기본 에이전트에 대해 하트비트 지침이 활성화된 경우에만 HEARTBEAT.md가 주입됩니다. 0m으로 하트비트 주기를 비활성화하거나 includeSystemPromptSection: false를 설정하면 일반 bootstrap 컨텍스트에서 제외됩니다. HEARTBEAT.md가 존재하지만 사실상 비어 있는 경우(빈 줄과 # Heading 같은 markdown 헤더만 있는 경우), OpenClaw는 API 호출을 절약하기 위해 하트비트 실행을 건너뜁니다. 이 건너뜀은 reason=empty-heartbeat-file로 보고됩니다. 파일이 없으면 하트비트는 계속 실행되며 모델이 무엇을 할지 결정합니다. 프롬프트 팽창을 피하려면 작게 유지하세요(짧은 체크리스트 또는 리마인더). 예시 HEARTBEAT.md:
# Heartbeat checklist

- Quick scan: anything urgent in inboxes?
- If it’s daytime, do a lightweight check-in if nothing else is pending.
- If a task is blocked, write down _what is missing_ and ask Peter next time.

tasks: 블록

HEARTBEAT.md는 하트비트 내부에서 간격 기반 점검을 수행하기 위한 작은 구조화된 tasks: 블록도 지원합니다. 예시:
tasks:

- name: inbox-triage
  interval: 30m
  prompt: "Check for urgent unread emails and flag anything time sensitive."
- name: calendar-scan
  interval: 2h
  prompt: "Check for upcoming meetings that need prep or follow-up."

# Additional instructions

- Keep alerts short.
- If nothing needs attention after all due tasks, reply HEARTBEAT_OK.
동작:
  • OpenClaw는 tasks: 블록을 파싱하고 각 작업을 자체 interval에 따라 검사합니다.
  • 해당 틱에서 기한이 된 작업만 하트비트 프롬프트에 포함됩니다.
  • 기한이 된 작업이 없으면 하트비트는 낭비되는 모델 호출을 피하기 위해 완전히 건너뜁니다(reason=no-tasks-due).
  • HEARTBEAT.md의 작업 외 콘텐츠는 유지되며 기한이 된 작업 목록 뒤에 추가 컨텍스트로 덧붙여집니다.
  • 작업 마지막 실행 타임스탬프는 세션 상태(heartbeatTaskState)에 저장되므로 일반적인 재시작 후에도 간격이 유지됩니다.
  • 작업 타임스탬프는 하트비트 실행이 정상 응답 경로를 완료한 뒤에만 갱신됩니다. 건너뛴 empty-heartbeat-file / no-tasks-due 실행은 작업을 완료된 것으로 표시하지 않습니다.
작업 모드는 모든 틱에서 비용을 지불하지 않으면서 하나의 하트비트 파일에 여러 주기적 점검을 담고 싶을 때 유용합니다.

에이전트가 HEARTBEAT.md를 업데이트할 수 있나요?

네 — 그렇게 하라고 요청하면 가능합니다. HEARTBEAT.md는 에이전트 워크스페이스에 있는 일반 파일일 뿐이므로, 일반 채팅에서 에이전트에게 다음과 같이 말할 수 있습니다:
  • “매일 캘린더 확인을 추가하도록 HEARTBEAT.md를 업데이트해.”
  • “더 짧고 받은편지함 후속 작업에 집중되도록 HEARTBEAT.md를 다시 작성해.”
이를 더 능동적으로 수행하게 하려면 하트비트 프롬프트에 다음과 같은 명시적 문장을 포함할 수도 있습니다: “체크리스트가 오래되면 더 나은 것으로 HEARTBEAT.md를 업데이트해.” 안전 참고: HEARTBEAT.md에는 비밀 정보(API 키, 전화번호, 개인 토큰)를 넣지 마세요. 이 파일은 프롬프트 컨텍스트의 일부가 됩니다.

수동 깨우기 (온디맨드)

다음 명령으로 시스템 이벤트를 큐에 넣고 즉시 하트비트를 트리거할 수 있습니다:
openclaw system event --text "Check for urgent follow-ups" --mode now
여러 에이전트에 heartbeat가 구성되어 있으면 수동 깨우기는 그 에이전트들의 하트비트를 즉시 각각 실행합니다. 다음 예약 틱까지 기다리려면 --mode next-heartbeat를 사용하세요.

reasoning 전달 (선택 사항)

기본적으로 하트비트는 최종 “응답” payload만 전달합니다. 투명성을 원한다면 다음을 활성화하세요:
  • agents.defaults.heartbeat.includeReasoning: true
활성화하면 하트비트는 Reasoning: 접두사가 붙은 별도 메시지도 전달합니다 (reasoning on과 동일한 형태). 이는 에이전트가 여러 세션/codex를 관리하고 있을 때 왜 사용자에게 알림을 보내기로 결정했는지 보고 싶을 때 유용할 수 있습니다. 하지만 원하지 않는 더 많은 내부 세부 정보가 드러날 수도 있습니다. 그룹 채팅에서는 끄는 편이 좋습니다.

비용 고려

하트비트는 전체 에이전트 턴을 실행합니다. 간격이 짧을수록 토큰을 더 많이 사용합니다. 비용을 줄이려면:
  • 전체 대화 기록 전송을 피하기 위해 isolatedSession: true를 사용하세요(실행당 약 100K 토큰에서 약 2~5K로 감소).
  • bootstrap 파일을 HEARTBEAT.md로만 제한하려면 lightContext: true를 사용하세요.
  • 더 저렴한 model을 설정하세요(예: ollama/llama3.2:1b).
  • HEARTBEAT.md를 작게 유지하세요.
  • 내부 상태 업데이트만 원한다면 target: "none"을 사용하세요.

관련 항목