AI 어시스턴트의 폴링 에이전트: 11가지 구현 패턴
AI 에이전트의 신뢰할 수 있는 폴링 패턴
폴링 에이전트(Polling Agent)는 AI 어시스턴트 아키텍처에서 가장 화려하지는 않은 부분 중 하나이지만, 동시에 가장 유용한 부분 중 하나이기도 합니다.
일반적인 채팅 어시스턴트는 사용자가 무언가를 물어볼 때까지 기다립니다. 반면 폴링 에이전트는 지속적으로 모니터링합니다. 소스를 확인하고, 변경 사항을 감지하며, 해당 변경 사항이 중요한지 판단한 후 행동을 취합니다. 이 행동은 알림, 요약, 초안 작성, 도구 호출 또는 전체 워크플로우일 수 있습니다.
이것이 어시스턴트가 “내 질문에 답해 줘"라는 요청에서 “이것을 대신 지켜봐 줘"라는 역할로 진화하는 방식입니다. 반응형(Reactive)이 아니라 사용자를 대신하여 조건이 충족될 때 자동으로 작동하는 백그라운드 프로세스로 변하게 됩니다.

여기서 중요한 설계 원칙은 간단합니다. 언어 모델(Language Model)에게 시간 관리, 상태 유지, 재시도 또는 잠금(Locking) 책임을 맡기지 마십시오. 이러한 부분은 일반적인 백엔드 인프라스트럭처를 사용해야 합니다. 모델은 그 가치가 빛을 발하는 영역, 즉 복잡한 컨텍스트 해석, 의미론적 판단, 유용한 언어 생성에 사용되어야 합니다.
폴링 에이전트란 무엇인가?
폴링 에이전트는 소스를 반복적으로 확인하고 특정 조건이 충족되면 어시스턴트 작업을 트리거하는 백그라운드 프로세스입니다. 광범위한 AI 시스템 스택 — 어시스턴트가 LLM, 메모리, 도구, 라우팅, 관찰성(Observability)을 결합하는 곳 — 에서 폴링 레이어는 어시스턴트를 단순한 반응형에서 능동형(Proactive)으로 만드는 핵심 요소입니다. 5개 레이어의 전체적인 그림에 대해서는 AI 어시스턴트 아키텍처: LLM, 메모리, 도구, 라우팅, 관찰성을 참조하십시오.
예시:
- 매일 아침 받은 편지함을 확인하여 중요한 메시지를 요약합니다.
- 노션(Notion) 작업 목록을 감시하여 다음 할 일(Todo) 항목을 실행합니다.
- GitHub 이슈의 상태가 변경될 때까지 모니터링합니다.
- AI 작업이 완료될 때까지 결과를 폴링합니다.
- 예약 슬롯이 이용 가능해질 때까지 확인합니다.
- 공급자 포털에서 문서가 나타날 때까지 감시합니다.
- 매주 새로운 연구 논문을 스캔하여 관련 있는 내용을 요약합니다.
실용적인 폴링 에이전트는 다섯 가지 책임이 있습니다:
- 적절한 시간에 깨어납니다.
- 소스에서 데이터를 읽습니다.
- 이미 본 내용을 기억합니다.
- 새로운 상태가 중요한지 결정합니다.
- 안전하게 한 번만 실행하여 중복을 방지합니다.
일반적인 프로덕션 흐름은 다음과 같습니다:
scheduler
-> polling worker
-> source system
-> state store
-> deterministic filters
-> optional LLM evaluation
-> assistant action
이 구조는 가장 좋은 방식으로 지루합니다. 지루한 시스템은 새벽 2시에 디버깅하기 더 쉽습니다.
모든 폴링 에이전트가 필요로 하는 상태
폴링 에이전트는 내구성 있는 상태(Durable State)가 필요합니다. 대화 기록만으로는 충분하지 않습니다. 어시스턴트는 대화를 기억할 수 있지만, 시스템에는 신뢰할 수 있는 운영 기록이 필요합니다.
좋은 폴링 상태 기록에는 일반적으로 다음 내용이 포함됩니다:
{
"poll_id": "poll_123",
"user_id": "user_456",
"source_type": "notion",
"source_ref": "database_tasks",
"condition": "take one task in Todo state and execute it",
"interval_seconds": 600,
"last_run_at": "2026-06-19T01:00:00Z",
"next_run_at": "2026-06-19T01:10:00Z",
"last_seen_cursor": "cursor_or_timestamp",
"last_result_hash": "b64e8a...",
"failure_count": 0,
"status": "active"
}
정확한 스키마는 소스에 따라 다르지만, 대부분의 시스템은 이러한 개념이 필요합니다.
폴링 정의
이것은 에이전트가 무엇을 모니터링하고 있는지, 그리고 그 이유를 설명합니다.
poll_id
user_id
workspace_id
source_type
source_ref
condition_text
priority
status
예를 들어:
source_type: notion
source_ref: Tasks database
condition_text: Find one Todo task, claim it, execute it, mark it Complete.
스케줄
이것은 에이전트가 언제 실행되어야 하는지를 설명합니다.
interval_seconds
cron_expression
timezone
last_run_at
next_run_at
jitter
10분마다 노션을 확인하는 Hermes 에이전트의 경우:
interval_seconds: 600
timezone: Australia/Melbourne
커서 또는 스냅샷
이것은 에이전트가 동일한 데이터를 다시 처리하지 않도록 도와줍니다.
소스에 따라 다음 중 하나일 수 있습니다:
last_seen_id
last_seen_timestamp
api_cursor
etag
version
content_hash
노션 작업 큐의 경우, 커서보다 작업 상태와 클레임(claim) 필드가 더 중요할 수 있습니다. 그러나 Gmail, GitHub 또는 동기화 API의 경우 커서는 일반적으로 중요합니다.
클레임 또는 리스(Lease)
이것은 두 개의 워커가 같은 작업을 가져가는 것을 방지합니다.
claimed_by
claimed_at
claim_expires_at
run_id
예를 들어, 노션 작업은 다음과 같이 변경될 수 있습니다:
Status: Todo
다음과 같이:
Status: InProgress
ClaimedBy: hermes
ClaimedAt: 2026-06-19T01:00:00Z
ClaimExpiresAt: 2026-06-19T01:30:00Z
RunId: run_789
이는 “단 하나의 워커만 가져가기를 바랐다"는 희망과 “시스템에 클레임 프로토콜이 있다"는 확신 사이의 차이입니다.
실행 기록
이것은 실행 동안 무슨 일이 있었는지 기록합니다.
run_id
poll_id
source_object_id
started_at
finished_at
status
items_checked
items_changed
decision_summary
error
실행 기록은 노션이나 다른 외부 도구가 아닌 어시스턴트 백엔드에 보관되어야 합니다. 노션은 인간의 가시성에는 좋지만, 유일한 실행 로그로 사용하기에는 이상적이지 않습니다.
중복 제거 기록
이것은 중복 알림이나 반복적인 작업을 방지합니다.
dedupe_key
poll_id
source_object_id
condition_version
action_type
delivered_at
예를 들어:
user_456:poll_123:notion_page_999:execute:v1
동일한 작업이 다시 시도되면 시스템은 이를 억제할 수 있습니다.
방법 1: 예약된 폴링 워커
가장 간단한 신뢰할 수 있는 패턴입니다.
스케줄러는 고정된 간격마다 깨어나서 워커를 호출합니다. 워커는 소스를 읽고, 상태를 업데이트하며, 필요시 어시스턴트 작업을 트리거합니다.
scheduler
-> worker
-> source API
-> database
-> assistant action
실행 방식
스케줄러는 시간을 담당합니다. 이는 cron, 클라우드 스케줄러, Kubernetes CronJob 또는 작은 내부 스케줄러일 수 있습니다.
모든 간격마다 스케줄러는 워커 실행을 시작합니다. 워커는 구성을 로드하고, 대상 소스를 쿼리하며, 저장된 상태와 결과를 비교한 후 필요시 행동을 취합니다.
간단한 어시스턴트의 경우, 이 정도면 종종 충분합니다. 단일 스케줄러와 가벼운 워커 프로세스는 큐, 리스 또는 분산 조정 없이도 하루 수십 번의 확인을 처리할 수 있습니다.
상태 모델
스케줄러는 매우 적은 정보를 저장합니다. 일반적으로 작업을 트리거할 때만 알고 있습니다.
애플리케이션 데이터베이스가 중요한 상태를 저장합니다:
poll definition
schedule
cursor or snapshot
last run time
failure count
status
워커는 상태가 없어야(Stateless) 합니다. 실행 중 임시 데이터를 보유할 수 있지만, 내구성 있는 진실은 데이터베이스에 속해야 합니다.
예시 흐름
Every 10 minutes:
trigger Hermes polling worker
Worker:
load active poll configuration
query source
compare with previous state
run deterministic checks
call LLM only if needed
update state
emit assistant event
가장 적합한 경우
다음에 예약된 폴링 워커를 사용하십시오:
- 일일 요약.
- 시간별 확인.
- 소규모 내부 자동화.
- 간단한 “이것을 지켜봐” 작업.
- 저량에서 중량 수준의 어시스턴트 작업.
단점
예약된 폴링은 이해하기 쉽지만, 규모가 커질 때 취약해질 수 있습니다. 많은 폴링이 동시에 실행되면 워커를 과부하 상태로 만들거나 제공자의 속도 제한(Rate Limit)을 초과할 수 있습니다. 스케줄러가 직접 작업을 시작하면 재시도도 복잡해질 수 있습니다.
방법 2: 큐 기반 폴링 워커
큐 기반 폴링은 일반적으로 프로덕션 AI 어시스턴트에 대한 가장 좋은 기본 옵션입니다.
스케줄러는 폴링을 직접 실행하지 않습니다. 큐에 작업을 넣습니다. 워커 프로세스는 큐에서 작업을 소비합니다.
scheduler
-> queue
-> worker pool
-> source API
-> state store
-> assistant action
실행 방식
스케줄러는 실행 예정인 폴링을 스캔하고 작업을 큐에 삽입합니다. 워커는 여유가 있을 때 작업을 가져옵니다.
이것은 역압(Backpressure)을 제공합니다. 시스템이 바쁠 경우, 작업은 소스 API나 LLM 제공자를 압도하는 대신 큐에서 대기합니다.
상태 모델
데이터베이스가 폴링 상태를 저장합니다:
poll_id
user_id
source_ref
condition_text
next_run_at
cursor
status
failure_count
큐 메시지는 작게 유지해야 합니다:
{
"poll_id": "poll_123",
"scheduled_for": "2026-06-19T01:10:00Z",
"attempt": 1
}
워커는 시작 시 데이터베이스에서 전체 상태를 로드합니다.
예시 흐름
Every minute:
scheduler finds polls where next_run_at <= now
scheduler enqueues jobs
Workers:
pull jobs from queue
lock or lease the poll
query the source
update state
emit assistant action if needed
set next_run_at
가장 적합한 경우
다음에 큐 기반 폴링을 사용하십시오:
- 다중 사용자 AI 어시스턴트.
- 많은 동시 폴링.
- 속도 제한이 있는 통합.
- 재시도 가능한 백그라운드 작업.
- 시간이 다르게 소요되는 작업.
- 신뢰성이 중요한 SaaS 제품.
단점
큐는 인프라스트럭처를 추가합니다. 데드 레터 처리, 멱등성, 가시성 타임아웃 및 재시도 정책이 필요합니다. 프로덕션 시스템에는 가치가 있지만, 소규모 프로토타입에는 과도할 수 있습니다.
방법 3: 외부 도구를 작업 큐로 사용
이것은 노션과 Hermes 예시에서 사용되는 패턴입니다.
외부 도구는 단순한 데이터 소스가 아닙니다. 인간의 인터페이스가 되는 작업 큐가 됩니다. 에이전트는 주기적으로 도구를 확인하고, 하나의 작업을 클레임하며, 실행한 후 작업 상태를 업데이트합니다.
scheduler
-> Hermes worker
-> Notion database
-> claim one task
-> execute task
-> update Notion status
실행 방식
Hermes는 10분마다 노션 데이터베이스를 쿼리하여 Todo 상태의 작업을 하나 찾습니다. 보통 우선순위와 생성 시간을 기준으로 다음 작업을 선택합니다. 그런 다음 InProgress로 설정하여 작업을 클레임합니다.
그 후 Hermes는 작업을 실행합니다. 실행이 성공하면 작업을 Complete로 표시합니다. 실패하면 Failed로 표시하거나 재시도 횟수를 늘려 Todo로 되돌립니다.
상태 모델
노션은 인간 인터페이스용 작업 상태를 저장합니다:
Title
Description
Status: Todo | InProgress | Complete | Failed
Priority
CreatedAt
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
RetryCount
LastError
CompletedAt
Hermes 백엔드는 운영 실행 상태를 저장합니다:
run_id
notion_page_id
started_at
finished_at
execution_status
tool_calls
LLM trace
error details
idempotency_key
이 분리는 중요합니다. 노션은 가시성과 수동 편집에 뛰어납니다. Hermes 백엔드는 로그, 재시도, 중복 제거 및 감사 기록에 더 적합합니다.
예시 흐름
Every 10 minutes:
Hermes wakes up
Hermes:
query Notion for one task where Status = Todo
sort by Priority, CreatedAt
update selected task to InProgress
set ClaimedBy, ClaimedAt, ClaimExpiresAt, RunId
execute the task
write execution log
set task to Complete or Failed
가장 적합한 경우
다음과 같은 경우 이 패턴을 사용하십시오:
- 인간이 이미 노션, Jira, Linear, Trello 또는 다른 도구에서 작업을 관리할 때.
- 어시스턴트가 보이는 작업을 처리하기를 원할 때.
- 작업 보드가 사용자 인터페이스일 때.
- 간단한 인간-인-더-루프(Human-in-the-loop) 자동화 모델이 필요할 때.
단점
외부 도구는 완벽한 큐가 아닙니다. 원자적 클레임이 제한될 수 있습니다. 쿼리 일관성이 지연될 수 있습니다. 속도 제한이 적용될 수 있습니다. 에이전트가 여러 인스턴스에서 실행될 수 있다면 신중한 클레임 또는 리스 전략이 필요합니다.
실용적인 권장 사항은 노션을 인간 인터페이스용 작업 받은 편지함으로 사용하되, 모든 실행 로그, 재시도 기록, 추적 및 멱등성 키는 Hermes에 보관하는 것입니다. 노션은 사용자에게 가시성을 제공하고, Hermes는 시스템을 신뢰할 수 있게 유지합니다. Hermes에서 이 패턴 뒤에 있는 디스패처 및 동시성 메커니즘에 대해서는 Hermes 에이전트의 셀프 호스팅 LLM 워크플로우를 위한 칸반을 참조하십시오.
방법 4: 장시간 실행되는 워커 루프
장시간 실행되는 루프는 가장 간단한 구현입니다.
while True:
due_polls = db.find_due_polls()
for poll in due_polls:
run_poll(poll)
sleep(30)
이 패턴은 스케줄링과 실행을 하나의 서비스에서 결합하여 백그라운드 에이전트 작업의 가장 간단한 시작점을 제공합니다.
실행 방식
워커 프로세스가 지속적으로 실행됩니다. 몇 초 또는 몇 분마다 데이터베이스에서 실행 예정인 폴링을 확인하고 실행합니다. 구축하기 쉽고, 추론하기 쉬우며, 개발 중에 빠르게 반복할 수 있습니다.
상태 모델
데이터베이스는 여전히 내구성 있는 상태를 저장합니다:
poll configuration
next_run_at
cursor
last result
failure count
status
프로세스 메모리에는 임시 상태만 포함되어야 합니다:
current batch
short-lived cache
in-flight run
중요한 진행 상황을 메모리에만 저장하지 마십시오. 프로세스가 충돌하면 내구성 있는 저장소에 쓰이지 않은 모든 상태가 사라지며, 다음 실행은 중단된 지점을 알 수 없습니다.
가장 적합한 경우
다음에 장시간 실행되는 루프를 사용하십시오:
- 프로토타입.
- 로컬 개발.
- 내부 도구.
- 단일 테넌트 시스템.
- 저량 에이전트.
단점
이 패턴은 여러 레플리카(replicas)로 사용할 때 위험해집니다. 리스가 없으면 두 워커가 같은 폴링을 실행할 수 있습니다. 또한 실제 큐나 워크플로우 엔진의 운영 기능이 부족합니다.
장시간 실행되는 루프는 시작점으로 잘못된 것은 아니지만, 분산 스케줄러가 아니며 그렇게 취급되어서는 안 됩니다. 여러 레플리카나 더 강력한 신뢰성 보장이 필요해지면 위의 더 구조화된 패턴 중 하나로 이동해야 합니다.
방법 5: 웹훅 우선 및 폴링 폴백
소스가 웹훅을 지원한다면 사용하십시오. 폴링은 기본 메커니즘이 아니라 백업이 되어야 합니다.
external system
-> webhook endpoint
-> event store
-> assistant action
reconciliation poll
-> source API
-> compare with event store
-> repair missed events
실행 방식
외부 시스템은 변경 사항이 발생하면 웹훅 엔드포인트로 이벤트를 보냅니다. 시스템은 이벤트를 저장하고 비동기식으로 처리합니다.
더 느린 조정(Reconciliation) 폴링이 몇 시간마다 또는 하루에 한 번 실행됩니다. 누락된 이벤트가 있는지 확인합니다.
상태 모델
이벤트 저장소는 들어오는 웹훅을 기록합니다:
event_id
source_type
source_object_id
event_type
received_at
payload_hash
processed_at
signature_valid
조정 폴링은 다음을 저장합니다:
last_reconciliation_at
last_seen_cursor
last_seen_version
소스 객체 테이블은 최신 알려진 상태를 저장합니다:
external_id
current_status
external_updated_at
last_processed_event_id
가장 적합한 경우
다음에 웹훅 우선 아키텍처를 사용하십시오:
- GitHub 이벤트.
- Stripe 이벤트.
- Slack 이벤트.
- CRM 업데이트.
- 배포 알림.
- 티켓팅 시스템.
단점
웹훅은 공개 엔드포인트, 서명 검증, 재생 보호 및 이벤트 중복 제거가 필요합니다. 일부 제공자는 불완전한 이벤트를 보내기도 하므로 전체 객체를 가져와야 할 수도 있습니다.
그렇더라도 좋은 웹훅이 존재한다면 매분마다 폴링하는 것은 일반적으로 낭비입니다.
방법 6: 제공자 측 백그라운드 작업 폴링
때때로 폴링 대상은 AI 작업 자체일 수 있습니다.
애플리케이션은 장시간 실행되는 제공자 작업을 시작하고, 작업 ID를 저장한 후 나중에 완료되었는지 확인합니다.
app
-> start AI background job
-> store provider job id
-> poll status
-> fetch result
-> notify user
실행 방식
어시스턴트는 제공자와 함께 작업을 시작합니다. 제공자는 ID를 반환합니다. 백엔드는 해당 ID를 저장하고 작업이 성공, 실패, 만료 또는 시간 초과될 때까지 상태를 확인합니다.
상태 모델
백엔드는 다음을 저장합니다:
assistant_task_id
provider_job_id
user_id
status
created_at
last_checked_at
expires_at
result_ref
제공자는 임시 작업 상태와 출력을 저장합니다.
출력이 중요하면 작업이 완료되자마자 자체 내구성 있는 저장소로 복사하십시오. 제공자 측 결과 저장소는 유지 기간이 짧으며 자체 시스템의 적절한 아카이브를 대체할 수 없습니다.
가장 적합한 경우
다음에 제공자 측 백그라운드 작업 폴링을 사용하십시오:
- 긴 AI 연구 작업.
- 대용량 문서 처리.
- 코드베이스 분석.
- 보고서 생성.
- 데이터 추출 작업.
- 일반 HTTP 요청 시간 초과를 초과하는 작업.
단점
이 패턴은 하나의 문제를 해결합니다: 장시간 제공자 작업을 기다리는 것. 그러나 워크플로우 엔진, 스케줄러, 큐 또는 비즈니스 상태 저장소를 대체하지는 않습니다.
방법 7: 내구성 있는 워크플로우 엔진
내구성 있는 워크플로우 엔진은 장시간 실행, 타이머, 재시도 및 복구를 관리합니다. Go 및 Python 기반 어시스턴트 백엔드에 있어 Temporal이 가장 흔한 선택이며, 전체 구현 가이드는 Go에서 Temporal을 사용한 워크플로우 애플리케이션 구현을 참조하십시오.
매번 대기 및 재시도를 수동으로 연결하는 대신, 프로세스를 워크플로우로 모델링합니다.
workflow engine
-> activity: check source
-> timer: wait
-> activity: evaluate result
-> activity: notify user
실행 방식
워크플로우가 한 번 시작되면 자신의 대기를 제어합니다. 몇 분, 몇 일 또는 몇 주 동안 수면 상태가 될 수 있습니다. 워커 프로세스가 충돌하더라도 워크플로우 엔진은 기록된 상태에서 다시 시작할 수 있습니다.
상태 모델
워크플로우 엔진은 다음을 저장합니다:
workflow_id
execution history
timer state
activity attempts
retry policy
current workflow state
애플리케이션 데이터베이스는 다음을 저장합니다:
user-facing poll definition
authorization references
business records
notification records
워크플로우 엔진은 프로세스 상태 — 실행 기록, 타이머, 재시도 및 활동 시도 — 를 소유합니다. 데이터베이스는 비즈니스 상태 — 사용자 구성, 인증 기록, 알림 및 감사 로그 — 를 소유합니다. 이 둘을 분리하면 각 레이어가 혼란스러운 하이브리드가 되는 것을 방지할 수 있습니다.
가장 적합한 경우
다음에 내구성 있는 워크플로우를 사용하십시오:
- 다단계 비즈니스 프로세스.
- 장시간 자동화.
- 인간 승인 흐름.
- 신뢰할 수 있는 재시도.
- 감사 가능한 백그라운드 작업.
- 실패 후 다시 시작해야 하는 프로세스.
단점
워크플로우 엔진은 개념과 인프라스트럭처를 추가합니다. 프로세스가 중요할 때 훌륭하지만, 간단한 시간별 확인에는 무거울 수 있습니다.
방법 8: 영구 에이전트 런타임
일부 에이전트 프레임워크는 에이전트 상태를 지속하고, 실행을 체크포인트하며, 나중에 다시 시작할 수 있습니다.
에이전트 자체에 다단계 추론 프로세스가 있을 때 유용합니다.
scheduler or workflow
-> agent runtime
-> load checkpoint
-> call tools
-> save checkpoint
-> resume later
실행 방식
외부 스케줄러 또는 워크플로우가 에이전트를 시작합니다. 에이전트 런타임은 이전 상태를 로드하고, 다음 단계를 실행하며, 필요시 도구를 호출한 후 체크포인트를 작성합니다.
에이전트 런타임은 유일한 스케줄러가 되어서는 안 됩니다. 더 큰 백엔드 아키텍처 내의 추론 레이어로 취급하는 것이 좋습니다.
상태 모델
에이전트 체크포인트 저장소에는 다음이 포함됩니다:
current node
messages
tool outputs
intermediate reasoning state
pending action
장기 메모리에는 다음이 포함됩니다:
stable user preferences
facts
project context
source references
운영 상태는 여전히 다른 곳에 속해야 합니다:
poll schedule
cursor
status
retry count
dedupe records
유용한 규칙: 메모리는 커서가 아니며, 체크포인트는 큐가 아닙니다. 에이전트 메모리는 모델이 아는 것을 저장하고, 운영 상태는 프로세스가 어디에 있고 무엇을 했는지 추적합니다. 이 둘을 혼동하면 동시성 또는 재시작 후에만 나타나는 미묘한 버그로 이어집니다. 작업 메모리, 내구성 있는 상태 및 검색 레이어에 대한 전체 설계 공간은 AI 어시스턴트의 메모리 시스템에서 다룹니다.
가장 적합한 경우
다음에 영구 에이전트 런타임을 사용하십시오:
- 다단계 연구.
- 일시 중지 및 재개하는 에이전트.
- 인간-인-더-루프 작업.
- 도구 중심 추론.
- 시간이 지남에 따라 컨텍스트가 누적되는 작업.
단점
에이전트 지속성은 운영 신뢰성과 동일하지 않습니다. 여전히 스케줄링, 잠금, 재시도, 속도 제한 및 감사 로그가 필요합니다.
방법 9: 데이터베이스 동기화 및 변경 평가
이 패턴에서 폴링은 외부 데이터를 자체 데이터베이스로 동기화하는 데 사용됩니다. 어시스턴트는 각 평가 주기마다 외부 API를 직접 쿼리하는 대신 로컬 데이터베이스 변경 사항에 반응합니다.
sync poller
-> external API
-> local database
-> change evaluator
-> assistant action
이것은 데이터 동기화를 어시스턴트 지능과 분리합니다. 동기화 워커는 로컬 레코드를 최신 상태로 유지하는 것을 담당하고, 평가자는 변경 사항에 대해 무엇을 할지 결정하는 것을 담당합니다. 각 레이어는 독립적으로 테스트, 모니터링 및 확장할 수 있습니다.
실행 방식
동기화 워커는 주기적으로 외부 변경 사항을 가져와 정규화된 레코드를 데이터베이스에 작성합니다. 두 번째 워커 또는 변경 스트림이 업데이트된 행을 감지하고 어시스턴트가 행동해야 하는지 결정합니다.
상태 모델
동기화 테이블은 다음을 저장합니다:
external_id
source_type
raw_payload
normalized_fields
external_updated_at
synced_at
version
content_hash
동기화 상태는 다음을 저장합니다:
source_cursor
last_sync_at
rate_limit_status
failure_count
어시스턴트 평가 테이블은 다음을 저장합니다:
object_id
evaluation_status
last_evaluated_hash
decision
notification_id
가장 적합한 경우
다음에 이 패턴을 사용하십시오:
- CRM 동기화.
- 티켓팅 시스템.
- 회계 문서.
- 제품 재고.
- 규정 준수 검토.
- 검색 인덱싱.
- 내부 대시보드.
단점
모든 것을 동기화하는 것은 비용이 많이 들고 불필요할 수 있습니다. 또한 프라이버시 및 유지 의무를 생성할 수 있습니다. 단일 어시스턴트 작업 이상으로 로컬 데이터에 가치가 있을 때 이 패턴을 사용하십시오.
방법 10: 적응형 폴링
적응형 폴링은 상태, 긴급성 또는 최근 활동에 따라 빈도를 변경합니다.
active object: poll every 1 minute
waiting object: poll every 1 hour
stale object: poll once per day
completed object: stop polling
실행 방식
각 실행 후, 워커는 다음 실행이 언제 이루어져야 하는지 결정합니다.
객체가 최근 변경되었다면 더 빨리 폴링합니다. 오랫동안 변경 사항이 없다면 속도를 늦춥니다. 작업이 완료되었다면 중지합니다.
상태 모델
폴링 상태에는 다음이 포함됩니다:
current_interval
minimum_interval
maximum_interval
backoff_policy
last_activity_at
priority
stop_condition
소스 스냅샷에는 다음이 포함됩니다:
status
updated_at
activity_level
expected_next_change
가장 적합한 경우
다음에 적응형 폴링을 사용하십시오:
- 배포 상태.
- 배송 추적.
- 캘린더 슬롯 이용 가능성.
- 가격 모니터링.
- 빌드 작업.
- 장시간 제공자 작업.
- 폭발적인 업데이트가 있는 모든 소스.
단점
적응형 폴링은 추론하기 어려울 수 있습니다. 작업이 엄격한 시간에 실행되어야 한다면 엄격하게 유지하십시오. 규정 준수 작업을 스마트하게 만들지 마십시오.
방법 11: LLM 평가자가 있는 시맨틱 폴링
시맨틱 폴링은 조건이 모호할 때 사용됩니다.
코드는 다음에 답할 수 있습니다:
Is status equal to Complete?
Is price below 100?
Is there a new message?
LLM은 다음에 답하는 데 도움을 줄 수 있습니다:
Does this email sound urgent?
Is this customer likely unhappy?
Is this research paper relevant?
Does this change require my attention?
실행 방식
워커는 먼저 저렴한 결정론적 필터를 적용합니다. 후보 항목만 LLM으로 전달됩니다.
new item?
matches source filters?
not already processed?
not obviously irrelevant?
그런 다음 LLM이 더 작은 후보 집합을 평가하고 구조화된 출력을 반환합니다.
{
"should_notify": true,
"urgency": "high",
"reason": "The customer reports a production outage."
}
상태 모델
폴링 정의는 다음을 저장합니다:
semantic_condition
examples
negative_examples
user_preference_summary
model_config
평가 로그는 다음을 저장합니다:
input_reference
model
prompt_version
structured_output
confidence
cost
latency
폴링 상태는 다음을 저장합니다:
last_seen_ids
last_evaluated_hashes
last_decision
last_decision_reason
가장 적합한 경우
다음에 시맨틱 폴링을 사용하십시오:
- 중요한 이메일 감지.
- 고객 감정 모니터링.
- 연구 알림.
- 영업 기회 감지.
- 보안 분류(Triage).
- 임원 브리핑.
단점
LLM 호출은 비용이 들고 지연 시간을 추가합니다. 프롬프트와 스키마가 느슨하면 일관성이 없을 수도 있습니다. 먼저 결정론적 필터를 사용하십시오. 판단이 실제로 필요할 때만 모델에게 질문하십시오.
결정 테이블: 폴링 에이전트 방법 선택
| Method | Best Application | Pros | Cons |
|---|---|---|---|
| Scheduled polling worker | Simple recurring assistant tasks | Easy to build, easy to debug, minimal infrastructure | Limited scaling, basic retries, can overload workers if many polls fire together |
| Queue-based polling workers | Production SaaS assistants with many users | Scalable, resilient, supports retries and backpressure | Requires queue infrastructure, idempotency, dead letter handling |
| External tool as task queue | Notion, Jira, Linear, Trello based task execution | Human-friendly, easy to inspect, works with existing workflows | External tools are not perfect queues, atomic claim may be difficult |
| Long-running worker loop | Prototypes and internal tools | Very simple, fast to implement, few moving parts | Weak reliability, poor multi-replica behavior, limited operational control |
| Webhook-first with polling fallback | Event-driven integrations | Fast reaction, fewer API calls, reconciliation catches missed events | Needs public endpoint, event validation, dedupe, provider webhook support |
| Provider-side background job polling | Long-running AI provider jobs | Handles slow AI tasks, simple status model, good for async UX | Only manages provider job status, not full business workflow |
| Durable workflow engine | Long-running multi-step processes | Strong retries, timers, audit history, recovery after crashes | More infrastructure and concepts, heavy for simple polling |
| Persistent agent runtime | Multi-step reasoning agents | Preserves agent context, supports pause and resume, good for tool-heavy tasks | Not a scheduler or queue replacement, still needs operational backend |
| Database sync plus change evaluation | Systems where external data has local value | Clean separation, local reporting, fewer repeated external calls | More storage, more sync complexity, possible privacy and retention concerns |
| Adaptive polling | Bursty sources or variable urgency tasks | Reduces cost, respects rate limits, reacts faster when activity is high | Harder to reason about, not ideal for strict schedules |
| Semantic polling with LLM evaluator | Fuzzy conditions requiring judgment | Handles natural language intent, useful summaries, flexible decisions | Cost, latency, prompt quality risk, should not replace simple code checks |
권장 기본 아키텍처
대부분의 프로덕션 AI 어시스턴트에 대해 다음으로 시작하십시오:
polls table
-> scheduler
-> queue
-> stateless workers
-> deterministic filters
-> optional LLM evaluator
-> notification or assistant action
최소한의 스키마:
CREATE TABLE polls (
id TEXT PRIMARY KEY,
user_id TEXT NOT NULL,
source_type TEXT NOT NULL,
source_ref TEXT NOT NULL,
condition_text TEXT NOT NULL,
schedule_type TEXT NOT NULL,
interval_seconds INTEGER,
timezone TEXT,
next_run_at TIMESTAMP NOT NULL,
last_run_at TIMESTAMP,
cursor_value TEXT,
last_hash TEXT,
status TEXT NOT NULL,
failure_count INTEGER NOT NULL DEFAULT 0,
last_error TEXT,
created_at TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE TABLE poll_runs (
id TEXT PRIMARY KEY,
poll_id TEXT NOT NULL,
started_at TIMESTAMP NOT NULL,
finished_at TIMESTAMP,
status TEXT NOT NULL,
items_checked INTEGER,
items_matched INTEGER,
decision_summary TEXT,
error TEXT
);
CREATE TABLE notifications (
id TEXT PRIMARY KEY,
poll_id TEXT NOT NULL,
user_id TEXT NOT NULL,
dedupe_key TEXT NOT NULL,
title TEXT NOT NULL,
body TEXT NOT NULL,
delivered_at TIMESTAMP,
UNIQUE (dedupe_key)
);
이것은 깔끔한 분리를 제공합니다:
scheduler owns time
queue owns buffering
worker owns execution
database owns state
LLM owns semantic judgment
assistant owns user interaction
이 분리가 신뢰할 수 있는 폴링 에이전트의 핵심입니다.
예시: 노션 작업을 처리하는 Hermes 에이전트
이제 아키텍처를 구체적인 사례에 적용해 보겠습니다.
노션 데이터베이스에 작업이 있다고 가정합니다. Hermes는 10분마다 실행되어야 하며, Todo 상태의 작업을 하나 가져와서 InProgress로 설정하고, 실행한 후 Complete로 표시해야 합니다.
이는 가장 잘 다음과 같이 설명됩니다:
external tool as task queue
+
scheduled polling worker
+
claim or lease based execution
프로덕션 버전에서는 다음과 같습니다:
queue-based polling with Notion as the human-facing task inbox
노션 작업 속성
노션 데이터베이스에는 다음과 같은 필드가 포함되어야 합니다:
Name
Status: Todo | InProgress | Complete | Failed
Priority
CreatedAt
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
RetryCount
LastError
CompletedAt
중요한 필드는 ClaimedAt, ClaimExpiresAt 및 RunId입니다. 이들은 작업 클레임을 가시적이고 복구 가능하게 만듭니다.
Hermes 실행 상태
Hermes는 자체 실행 기록도 유지해야 합니다:
run_id
notion_page_id
started_at
finished_at
status
input_snapshot
tool_calls
result_summary
error
idempotency_key
이것은 노션이 수동으로 편집되거나, API 호출이 실패하거나, Hermes가 실제로 무엇을 했는지 감사해야 할 때를 대비합니다.
실행 흐름
Every 10 minutes:
Hermes scheduler creates a run
Hermes worker:
finds one Notion task where Status = Todo
sorts by Priority and CreatedAt
claims the task by setting Status = InProgress
writes ClaimedBy, ClaimedAt, ClaimExpiresAt, and RunId
executes the task
writes execution logs to Hermes backend
sets Notion Status = Complete on success
sets Notion Status = Failed on failure
Hermes가 작업을 클레임한 후 충돌하면 리스가 만료될 수 있습니다:
Status = InProgress
ClaimExpiresAt < now
이후 실행은 작업을 복구하거나 실패로 표시할 수 있습니다.
실패 처리
성공 시:
Status = Complete
CompletedAt = now
LastError = empty
복구 가능한 실패 시:
Status = Todo
RetryCount = RetryCount + 1
LastError = short error message
복구 불가능한 실패 시:
Status = Failed
LastError = clear explanation
안전성을 위해 Hermes는 멱등성 키도 사용해야 합니다:
notion_page_id + task_version + action_type
이것은 재시도가 잘못된 시점에 발생하더라도 동일한 작업이 두 번 실행되는 것을 방지합니다.
이것이 단순한 폴링이 아닌 이유
폴링 부분은 단지 깨어나는 메커니즘일 뿐입니다. 실제 아키텍처는 작업 클레임과 신뢰할 수 있는 실행입니다.
간단한 구현은 다음과 같습니다:
Every 10 minutes, find a Todo task and do it.
신뢰할 수 있는 구현은 다음과 같습니다:
Every 10 minutes, claim exactly one eligible task, record the run, execute idempotently, and move the task to a terminal state.
이것이 데모와 신뢰할 수 있는 에이전트 사이의 차이입니다.
일반적인 폴링 에이전트 실수
실수 1: 클레임 프로토콜 부재
두 워커가 같은 작업을 볼 수 있다면 두 워커 모두 실행할 수 있습니다.
다음 사용:
ClaimedBy
ClaimedAt
ClaimExpiresAt
RunId
현재 하나의 워커만 실행 중이라도, 나중에 두 번째 워커가 나타날 수 있다고 가정하고 설계하십시오.
실수 2: 중복 제거 키 부재
모든 외부 작업에는 중복 제거 키가 있어야 합니다.
user_id + poll_id + source_object_id + action_type + condition_version
이것은 반복적인 알림, 이메일, 작업 실행 및 도구 호출을 방지합니다. 이러한 키의 범위 지정, 저장 및 테스트에 대한 더 넓은 원칙도 동일하게 적용됩니다 — 분산 시스템에서 실제로 작동하는 멱등성을 참조하십시오.
실수 3: LLM을 너무 일찍 호출
모델에게 데이터베이스 필터링을 시키지 마십시오.
나쁨:
Send all tasks to the LLM and ask which one is Todo.
더 좋음:
Use the Notion API filter to fetch Todo tasks.
Then use the LLM only if task interpretation is needed.
실수 4: 노션을 유일한 백엔드로 취급
노션은 좋은 인간 인터페이스입니다. 그러나 완전한 실행 백엔드는 아닙니다.
실행 로그, 재시도, 추적 및 멱등성 기록을 Hermes에 보관하십시오.
실수 5: 무한 폴링
모든 폴링에는 중지 조건이 있어야 합니다.
예시:
stop after success
stop after date
stop after max retries
stop when user disables it
stop after repeated authorization failure
중지 조건이 없는 폴링 에이전트는 조용한 비용 누수입니다.
실수 6: 관찰성 부재
다음 질문에 답할 수 있어야 합니다:
What did the agent run?
Why did it run?
What did it read?
What did it change?
Why did it fail?
Did it notify the user?
Did it run twice?
이 질문에 답할 수 없다면 시스템은 중요한 작업을 수행할 준비가 되지 않은 것입니다.
관찰성 체크리스트
다음과 같은 메트릭을 추적하십시오:
polls_due
polls_started
polls_succeeded
polls_failed
tasks_claimed
tasks_completed
tasks_failed
claim_expired_count
duplicate_suppressed_count
llm_calls
llm_cost
rate_limit_count
average_run_duration
다음과 같은 로그 필드를 기록하십시오:
poll_id
run_id
source_type
source_object_id
claim_id
cursor_before
cursor_after
decision
dedupe_key
error
다음에 대한 관리자 뷰를 구축하십시오:
active polls
stuck InProgress tasks
recent failures
high retry tasks
dead letter jobs
expensive LLM evaluations
disabled integrations
폴링 에이전트는 실패가 조용하고 문제가 누군가 알아차리기 전에 누적될 수 있는 백그라운드에서 실행됩니다. 백그라운드 시스템은 문제가 생겼을 때 사후에 추가되는 것이 아니라 처음부터 가시성이 구축되어 있어야 합니다. AI 및 LLM 기반 시스템에 대한 전체 관찰성 스택 — 메트릭, 추적, 구조화된 로그 및 SLO — 에 대해서는 LLM 시스템의 관찰성: 프로덕션에서의 메트릭, 추적, 로그 및 테스트을 참조하십시오.
최종 권장 사항
진지한 AI 어시스턴트를 위해 큐 기반 폴링 워커와 내구성 있는 상태 저장소로 시작하십시오. 제공자가 지원하는 곳에서 웹훅을 추가하십시오. 속도 제한이 중요한 경우 적응형 폴링을 사용하십시오. 프로세스가 장시간이고 다단계인 경우 내구성 있는 워크플로우 엔진을 사용하십시오. 에이전트가 시간에 걸쳐 추론해야 하는 경우 영구 에이전트 런타임을 사용하십시오.
Hermes와 노션 예시의 경우, 올바른 아키텍처는 다음과 같습니다:
Notion as the human-facing task inbox
Hermes scheduler every 10 minutes
Hermes worker with claim or lease logic
Hermes backend for execution logs and idempotency
Notion status updates for visibility
폴링 간격은 어려운 부분이 아닙니다. 어려운 부분은 에이전트가 하나의 작업을 클레임하고, 한 번 실행하고, 무슨 일이 있었는지 기록하며, 시스템이 인간이 이해할 수 있는 상태로 남도록 하는 것입니다.
폴링 스크립트를 신뢰할 수 있는 AI 어시스턴트로 만드는 것은 간격이나 모델이 아니라, 작업을 클레임하고, 기록하며, 시스템이 인간과 미래 실행이 모두 이해할 수 있는 상태로 남도록 하는 дисциплина(규율)입니다.