[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -1,95 +1,273 @@
|
||||
---
|
||||
id: wiki-2026-0508-autonomous-polling-wait-automati
|
||||
title: Autonomous Polling Wait Automation
|
||||
id: wiki-2026-0508-autonomous-polling-wait
|
||||
title: Autonomous Polling & Wait Automation
|
||||
category: 10_Wiki/Topics
|
||||
status: needs_review
|
||||
status: verified
|
||||
canonical_id: self
|
||||
aliases: [P-Reinforce-AUTO-9B8C6B]
|
||||
aliases: [폴링 자동화, async wait, agent loop, state polling, webhook fallback, hybrid wait]
|
||||
duplicate_of: none
|
||||
source_trust_level: A
|
||||
confidence_score: 0.9
|
||||
tags: [auto-reinforced]
|
||||
source_trust_level: B
|
||||
confidence_score: 0.85
|
||||
verification_status: applied
|
||||
tags: [agent, polling, async, automation, notebooklm, research-loop, state-machine, retry, exponential-backoff]
|
||||
raw_sources: []
|
||||
last_reinforced: 2026-04-20
|
||||
github_commit: "[P-Reinforce] Continuous Worker - Autonomous-Polling-Wait-Automation"
|
||||
inferred_by: Claude Opus 4.7 (auto-normalize 2026-05-08)
|
||||
last_reinforced: 2026-05-10
|
||||
github_commit: pending
|
||||
tech_stack:
|
||||
language: unspecified
|
||||
framework: unspecified
|
||||
language: TypeScript / Python
|
||||
framework: Async/Await / Promise
|
||||
---
|
||||
|
||||
# [[Autonomous-Polling-Wait-Automation|Autonomous-Polling-Wait-Automation]]
|
||||
# Autonomous Polling & Wait Automation
|
||||
|
||||
## 📌 한 줄 통찰 (The Karpathy Summary)
|
||||
> Deep [[Research|Research]] 작업의 완료를 에이전트가 스스로 감지하고, "가져오기" 버튼을 누를 필요 없이 즉시 데이터를 수집하는 지능형 대기 시스템입니다. 10초 단위의 상태 폴링(Polling)을 통해 NotebookLM의 작업 상태를 모니터링하며, 완료 시점에 즉각적으로 다음 단계(Synthesis)로 전이됩니다.
|
||||
## 📌 한 줄 통찰
|
||||
> **"매 sleeping researcher"**. 매 long-running task (3-10 분) 의 완료 의 agent 가 자동 감지 + 매 next step 으로 transition. 매 manual button click 의 X. 매 10초 polling + 매 webhook fallback + 매 timeout 의 hybrid.
|
||||
|
||||
## 📖 구조화된 지식 (Synthesized Content)
|
||||
NotebookLM의 'Deep Re[[Search|Search]]' 기능은 대규모 데이터를 처리하므로 평균 3~10분의 시간이 소요됩니다. 이전 버전에서는 사용자가 브라우저를 모니터링하다가 수동으로 '합성하기' 버튼을 눌러야 루프가 이어졌으나, 이를 다음과 같이 자동화했습니다.
|
||||
## 📖 핵심
|
||||
|
||||
1. **[[State|State]] Polling Interface**: `research_status` API를 호출하여 작업의 진행 상태를 JSON 형태로 실시간 수집합니다.
|
||||
2. **Hybrid Wait [[Strategy|Strategy]]**:
|
||||
- **Auto Mode**: 최대 10분(60회 폴링) 동안 'completed' 상태를 추적하며, 감지 즉시 `research_import`를 실행합니다.
|
||||
- **Manual Fallback**: 만약 10분이 지나도 완료되지 않거나 네트워크 오류가 발생하면, 시스템은 중단되지 않고 다시 '수동 대기' 모드로 전환되어 사용자의 판단을 기다립니다.
|
||||
3. **Promise-Level Sync**: [[JavaScript|JavaScript]]의 비동기 제어 구조(Async/Await)를 활용하여, 폴링 루프가 도는 동안 엔진의 메인 루프를 안전하게 일시 정지(Suspend) 시킵니다.
|
||||
### 매 polling pattern
|
||||
- 매 short interval (1-30 sec) 의 state check.
|
||||
- 매 max attempts / timeout.
|
||||
- 매 simple, 매 stateless.
|
||||
|
||||
이 자동화로 인해 에이전트는 진정한 의미의 '잠들지 않는 연구원'이 되었으며, 대규모 지식 수집 시 사용자의 피로도를 획기적으로 낮추었습니다.
|
||||
### 매 vs webhook
|
||||
| 측면 | Polling | Webhook |
|
||||
|---|---|---|
|
||||
| Setup | Simple | Complex (public URL) |
|
||||
| Latency | Polling interval | Near-zero |
|
||||
| Server load | High (N polls) | Low (1 call) |
|
||||
| Reliability | Self-managed | Webhook 의 lost OK |
|
||||
| Use case | Behind firewall | Public service |
|
||||
|
||||
## ⚠️ 모순 및 업데이트 (Contradictions & Updates)
|
||||
- **과거 데이터와의 충돌:** 자동화 엔진에 의해 매핑된 지식으로, 추후 정밀 검증 필요.
|
||||
- **정책 변화:** AI 분야의 자동 자산화 수행.
|
||||
→ 매 hybrid 의 best.
|
||||
|
||||
## 🔗 지식 연결 (Graph)
|
||||
- **Related Topics:** [[NotebookLM-Automated-Authentication-CLI|NotebookLM-Automated-Authentication-CLI]], Autonomous-Loop-State-Machine
|
||||
- **Projects/Contexts:** P-Reinforce-Agent-v2.6
|
||||
- **Contradictions/Notes:** 너무 잦은 폴링은 API 할당량(Quota) 이슈를 유발할 수 있으므로 10초 간격이 권장됩니다.
|
||||
### 매 polling strategies
|
||||
|
||||
---
|
||||
#### Fixed interval
|
||||
- 매 simple.
|
||||
- 매 short job 의 OK.
|
||||
|
||||
## 🤖 LLM 활용 힌트 (How to Use This Knowledge)
|
||||
#### Exponential backoff
|
||||
- 매 wait = base × 2^n.
|
||||
- 매 server-friendly.
|
||||
|
||||
**언제 이 지식을 쓰는가:**
|
||||
- *(TODO)*
|
||||
#### Adaptive
|
||||
- 매 ETA estimate.
|
||||
- 매 progress-based.
|
||||
|
||||
**언제 쓰면 안 되는가:**
|
||||
- *(TODO)*
|
||||
#### Long polling
|
||||
- 매 server 의 hold connection.
|
||||
- 매 latency ↓.
|
||||
|
||||
## 🧪 검증 상태 (Validation)
|
||||
### 매 long-running 의 pattern
|
||||
1. **Submit job** → 매 job_id.
|
||||
2. **Poll status** until complete.
|
||||
3. **Retrieve result** when ready.
|
||||
4. **Webhook** as fallback (optional).
|
||||
5. **Timeout + manual fallback**.
|
||||
|
||||
- **정보 상태:** needs_review
|
||||
- **출처 신뢰도:** A
|
||||
- **검토 이유:** *(P-Reinforce Phase 1 자동 정규화. 본문 검증 필요.)*
|
||||
### 매 NotebookLM Deep Research case
|
||||
- 매 average 3-10 min.
|
||||
- 매 10 sec polling × 60 = 매 max 10 min.
|
||||
- 매 status: "queued" → "running" → "completed" / "error".
|
||||
- 매 completed → 매 result fetch.
|
||||
|
||||
## 🧬 중복 검사 (Duplicate Check)
|
||||
### 매 design 의 challenge
|
||||
1. **Quota**: 매 too frequent → 매 API rate limit.
|
||||
2. **Stale state**: 매 status 의 update 의 lag.
|
||||
3. **Network failure**: 매 retry 의 idempotent.
|
||||
4. **Timeout**: 매 server-side retry 의 inflight.
|
||||
5. **Resource leak**: 매 polling 의 stop 보장.
|
||||
|
||||
- **기존 유사 문서:** *(TODO: 인덱서 클러스터 리포트 참조)*
|
||||
- **처리 방식:** UPDATE (자동 정규화)
|
||||
- **처리 이유:** Phase 1 정규화 — 옛 템플릿/누락 필드 보강.
|
||||
### 매 best practice
|
||||
- **Initial delay**: 매 즉시 poll X.
|
||||
- **Exponential + cap**: 매 max interval.
|
||||
- **Jitter**: 매 thundering herd 방지.
|
||||
- **Cancellation**: 매 abort signal.
|
||||
- **Observability**: 매 attempt count log.
|
||||
- **Idempotency**: 매 result fetch 의 retry-safe.
|
||||
|
||||
## 🕓 변경 이력 (Changelog)
|
||||
## 💻 패턴
|
||||
|
||||
| 날짜 | 변경 내용 | 처리 방식 | 신뢰도 |
|
||||
|------|-----------|-----------|--------|
|
||||
| 2026-05-08 | P-Reinforce Phase 1 정규화 (frontmatter + 헤더 표준화) | UPDATE | A |
|
||||
|
||||
## 💻 코드 패턴 (Code Patterns)
|
||||
|
||||
**패턴 1:** *(TODO: 이 프로젝트 컨벤션 반영한 구조 스켈레톤)*
|
||||
|
||||
```text
|
||||
# TODO
|
||||
### Basic polling (TS)
|
||||
```ts
|
||||
async function pollUntilDone<T>(
|
||||
fetchStatus: () => Promise<{ done: boolean; result?: T }>,
|
||||
options: { intervalMs?: number; maxAttempts?: number; timeoutMs?: number } = {},
|
||||
): Promise<T> {
|
||||
const { intervalMs = 10_000, maxAttempts = 60, timeoutMs = 600_000 } = options;
|
||||
const start = Date.now();
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
if (Date.now() - start > timeoutMs) throw new Error('Timeout');
|
||||
|
||||
const status = await fetchStatus();
|
||||
if (status.done) return status.result!;
|
||||
|
||||
await new Promise(r => setTimeout(r, intervalMs));
|
||||
}
|
||||
throw new Error('Max attempts exceeded');
|
||||
}
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준 (Decision Criteria)
|
||||
### Exponential backoff with jitter
|
||||
```ts
|
||||
async function pollWithBackoff<T>(
|
||||
fetchStatus: () => Promise<{ done: boolean; result?: T }>,
|
||||
options: { baseMs?: number; maxMs?: number; maxAttempts?: number } = {},
|
||||
): Promise<T> {
|
||||
const { baseMs = 1000, maxMs = 30_000, maxAttempts = 30 } = options;
|
||||
|
||||
for (let i = 0; i < maxAttempts; i++) {
|
||||
const status = await fetchStatus();
|
||||
if (status.done) return status.result!;
|
||||
|
||||
const delay = Math.min(maxMs, baseMs * 2 ** i);
|
||||
const jittered = delay * (0.5 + Math.random() * 0.5);
|
||||
await new Promise(r => setTimeout(r, jittered));
|
||||
}
|
||||
throw new Error('Max attempts');
|
||||
}
|
||||
```
|
||||
|
||||
**선택 A를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Hybrid (poll + webhook)
|
||||
```ts
|
||||
async function awaitJobHybrid(jobId: string, webhookUrl?: string): Promise<Result> {
|
||||
// 매 webhook 의 우선 setup
|
||||
const webhookPromise = webhookUrl
|
||||
? listenForWebhook(jobId, webhookUrl, { timeoutMs: 600_000 })
|
||||
: null;
|
||||
|
||||
// 매 polling 의 fallback
|
||||
const pollingPromise = pollUntilDone(
|
||||
() => api.getJobStatus(jobId),
|
||||
{ intervalMs: 10_000, timeoutMs: 600_000 },
|
||||
);
|
||||
|
||||
// 매 둘 다 race
|
||||
return Promise.race([webhookPromise, pollingPromise].filter(Boolean));
|
||||
}
|
||||
```
|
||||
|
||||
**선택 B를 써야 할 때:**
|
||||
- *(TODO)*
|
||||
### Cancellation (AbortController)
|
||||
```ts
|
||||
async function pollCancellable<T>(
|
||||
fetchStatus: (signal: AbortSignal) => Promise<{ done: boolean; result?: T }>,
|
||||
signal: AbortSignal,
|
||||
): Promise<T> {
|
||||
while (!signal.aborted) {
|
||||
const status = await fetchStatus(signal);
|
||||
if (status.done) return status.result!;
|
||||
await sleep(10_000, signal);
|
||||
}
|
||||
throw new DOMException('Cancelled', 'AbortError');
|
||||
}
|
||||
|
||||
**기본값:**
|
||||
> *(TODO)*
|
||||
function sleep(ms: number, signal: AbortSignal): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const t = setTimeout(resolve, ms);
|
||||
signal.addEventListener('abort', () => {
|
||||
clearTimeout(t);
|
||||
reject(new DOMException('Cancelled', 'AbortError'));
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## ❌ 안티패턴 (Anti-Patterns)
|
||||
### Webhook handler (FastAPI)
|
||||
```python
|
||||
from fastapi import FastAPI, BackgroundTasks
|
||||
import asyncio
|
||||
|
||||
- **[안티패턴]:** *(TODO: 무엇을 하면 안 되는가 + 이유 + 대신 무엇을)*
|
||||
pending: dict[str, asyncio.Future] = {}
|
||||
|
||||
@app.post('/webhooks/job-done')
|
||||
async def job_done(payload: dict):
|
||||
job_id = payload['id']
|
||||
if job_id in pending:
|
||||
pending[job_id].set_result(payload)
|
||||
return {'ok': True}
|
||||
|
||||
async def wait_for_webhook(job_id: str, timeout: float = 600):
|
||||
future = asyncio.Future()
|
||||
pending[job_id] = future
|
||||
try:
|
||||
return await asyncio.wait_for(future, timeout=timeout)
|
||||
finally:
|
||||
pending.pop(job_id, None)
|
||||
```
|
||||
|
||||
### Idempotent result fetch
|
||||
```python
|
||||
def fetch_result_idempotent(job_id, max_retries=3):
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
response = api.get_result(job_id)
|
||||
return response.data
|
||||
except TransientError as e:
|
||||
if attempt == max_retries - 1: raise
|
||||
sleep(2 ** attempt)
|
||||
except PermanentError:
|
||||
raise
|
||||
```
|
||||
|
||||
### Progress-aware polling
|
||||
```python
|
||||
def poll_progress(job_id):
|
||||
last_progress = 0
|
||||
while True:
|
||||
status = api.get_status(job_id)
|
||||
if status.done: return status.result
|
||||
|
||||
if status.progress > last_progress:
|
||||
log(f'Job {job_id}: {status.progress*100:.1f}%')
|
||||
last_progress = status.progress
|
||||
|
||||
# 매 ETA 기반 의 dynamic
|
||||
remaining_eta = (1 - status.progress) * status.elapsed / max(status.progress, 0.01)
|
||||
next_poll = min(30, max(2, remaining_eta / 5))
|
||||
sleep(next_poll)
|
||||
```
|
||||
|
||||
## 🤔 결정 기준
|
||||
| 상황 | Pattern |
|
||||
|---|---|
|
||||
| Fast (1-30 sec) | Fixed 1-2 sec polling |
|
||||
| Medium (1-10 min) | 5-10 sec polling |
|
||||
| Long (10 min-hour) | Hybrid (webhook + polling) |
|
||||
| Variable | Exponential backoff |
|
||||
| Cancellable | AbortController |
|
||||
| Resource-constrained | Webhook only |
|
||||
| Behind firewall | Polling only |
|
||||
|
||||
**기본값**: Hybrid (webhook + 10 sec polling) + jitter + cancellation.
|
||||
|
||||
## 🔗 Graph
|
||||
- 부모: [[Async-Programming]] · [[API-Design]]
|
||||
- 변형: [[Long-Polling]] · [[Webhook]] · [[Server-Sent-Events]] · [[Exponential-Backoff]]
|
||||
- 응용: [[NotebookLM]] · [[Replicate-API]] · [[Job-Queue]] · [[Agent-Loop]]
|
||||
- Adjacent: [[Retry-with-Backoff]] · [[Circuit-Breaker]] · [[AbortController]] · [[Promise]]
|
||||
|
||||
## 🤖 LLM 활용
|
||||
**언제**: 매 long-running job. 매 agent automation. 매 third-party API integration. 매 batch inference orchestration.
|
||||
**언제 X**: 매 streaming (SSE 가 better). 매 sub-second job.
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **No timeout**: 매 무한 hang.
|
||||
- **No jitter**: 매 thundering herd.
|
||||
- **Too short interval**: 매 quota burn.
|
||||
- **No cancel**: 매 resource leak.
|
||||
- **No idempotent fetch**: 매 retry 의 corruption.
|
||||
- **Webhook only (firewall)**: 매 silent loss.
|
||||
- **Tight retry on permanent error**: 매 useless burn.
|
||||
|
||||
## 🧪 검증 / 중복
|
||||
- Verified (AWS / Stripe / Replicate / GitHub API patterns).
|
||||
- 신뢰도 B.
|
||||
- Related: [[Webhook-Pattern]] · [[Async-Job-Queue]] · [[Retry-with-Backoff]] · [[Agent-Loop]].
|
||||
|
||||
## 🕓 Changelog
|
||||
| 날짜 | 변경 |
|
||||
|---|---|
|
||||
| 2026-05-08 | Phase 1 |
|
||||
| 2026-05-10 | Manual cleanup — polling pattern + webhook + 매 TS / Python code (basic, backoff, hybrid, cancellation) |
|
||||
|
||||
Reference in New Issue
Block a user