Community라이팅 & 에디팅github.com

lukethecat/aperture

AI 에이전트가 더 나은 일일 보고서를 선별하고, 모든 결정을 기록하며, 사용자 피드백을 통해 학습할 수 있도록 도와주는 스킬입니다.

aperture란 무엇인가요?

Aperture는 AI 에이전트가 뉴스 큐레이션을 자동화하고 개선할 수 있도록 설계된 스킬입니다. 이 스킬은 소스의 프론트 페이지를 스캔하고, 어제의 스냅샷과 비교하여 새로운 아이템을 식별합니다. 그런 다음 가중치 프로필을 기반으로 사전 선별하고, LLM을 통해 검토하며, 중복을 제거하고 보고서를 게시합니다. 모든 결정과 데이터는 추가 전용 로그(테이프)에 기록되어 감사와 재현이 가능합니다. 사용자 피드백은 프로필을 업데이트하는 데 사용되어 에이전트가 시간이 지남에 따라 사용자의 취향을 학습하고 적응할 수 있도록 합니다. Aperture는 구현에 구애받지 않으며, 참조 Python 구현이나 패턴을 다른 런타임에 이식하여 사용할 수 있습니다.

지원 대상~Claude Code✓Codex CLI~Cursor
npx skills add lukethecat/aperture

Installed? Explore more 라이팅 & 에디팅 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

스킬: Aperture

요약 — 사용자가 실제로 원하는 것을 더 많이 발견하고 더 나은 일일 보고서를 생성하고자 하는 AI 에이전트를 위한 스킬입니다. Aperture는 소스 프론트 페이지를 스캔하고, 어제와 비교(diff)하며, 가중치 프로필로 사전 선별하고, LLM으로 검토하고, 중복을 제거하고, 게시하며, 사용자 피드백을 통해 프로필을 발전시킵니다. 모든 결정은 추가 전용 테이프(tape)에 기록됩니다.

이 스킬은 구현에 구애받지 않습니다(implementation-agnostic). 에이전트는 이 파일을 읽거나, 포함된 Python 참조 구현을 호출하거나, 패턴을 자체 런타임에 이식하여 실행할 수 있습니다.


1. 목적

에이전트에게 정적 RSS 리더와 일회성 LLM 요약기를 대체하는 결정론적이고 감사 가능한 뉴스 큐레이션 스킬을 제공합니다:

  1. 프론트 페이지 스캔 — '프론트 페이지 읽기'를 피드 폴링이 아닌 일급 작업으로 취급합니다.
  2. 모든 결정 기록 — 모든 소스 스냅샷, 아이템, 거부 사유, 프로필 버전 및 보고서를 감사 및 재현을 위해 추가 전용 로그에 기록합니다.
  3. 사용자 피드백을 통한 학습 — 명시적인 피드백을 통해 에이전트의 취향을 업데이트하고 모든 변경 사항을 버전 관리하고 되돌릴 수 있도록 유지합니다.

에이전트가 이 스킬을 실행하면 테이프를 통해 "왜 이것이 선택되었는가?"와 "어제는 무엇이 달랐을까?"에 답할 수 있습니다.


2. 핵심 개념

개념의미
버티컬(Vertical)설정된 뉴스 주제 (예: 기술, 자동차, AI 정책). 각 버티컬은 자체 소스, 프로필 및 테이프를 가집니다.
테이프(Tape)추가 전용 JSONL 로그. 버티컬당 하나의 파일. 모든 소스 스냅샷, 아이템, 프로필 버전, 피드백 및 보고서는 레코드입니다.
프로필(Profile)버티컬의 '취향': 가중치가 적용된 키워드, 보너스가 있는 카테고리 및 부정어. 버전 관리되며, 모든 변경 사항이 기록됩니다.
스캔(Scan)각 소스의 목록 페이지를 가져오고, 제목/링크를 추출하며, URL을 정규화하고, 이전 스냅샷과 비교(diff)합니다.
반영 루프(Reflection loop)사용자 피드백 → 구문 분석된 프로필 작업 → 버전 증가 → 진화 기록 → 최근 풀링된 아이템 재확인.

3. 4단계 파이프라인

┌─────────┐    ┌──────────┐    ┌─────────┐    ┌──────────┐
│ 수집    │ -> │   편집   │ -> │ 검토    │ -> │ 게시    │
│ (스캔)  │    │(사전 선별│    │  (LLM)  │    │ (중복 제거│
│         │    │  규칙)   │    │         │    │  + 보고서)│
└─────────┘    └──────────┘    └─────────┘    └──────────┘
     │               │               │               │
     └───────────────┴───────────────┴───────────────┘
                         │
                    추가 전용 테이프(TAPE)

3.1 수집 — 프론트 페이지 스캔

목표: 설정된 각 소스로부터 오늘의 후보 아이템을 생성합니다.

단계:

  1. 각 소스에 대해 list_url을 가져옵니다.
  2. 소스의 extract_profile을 사용하여 {title, url} 쌍을 추출합니다:
    • rss — <item> 블록을 파싱합니다.
    • generic_links — <a href> 태그를 파싱합니다.
    • regex — 제목/url/날짜를 위한 명명된 그룹이 있는 정규식을 적용합니다.
    • json_api — JSON 경로를 탐색하고 제목/url 키를 읽습니다.
  3. 모든 URL을 정규화합니다:
    • 조각(fragments) 및 추적 매개변수(utm_*, fbclid 등)를 제거합니다.
    • 후행 슬래시와 선행 www.를 제거합니다.
    • https 체계를 강제하고, 호스트를 소문자로 만듭니다.
  4. 이 소스에 대한 이전 프론트페이지 스냅샷(테이프에서 읽음)과 비교(diff)합니다.
    • 새로운 url_norm 값이 후보가 됩니다.
    • 현재 전체 스냅샷을 새로운 frontpage 테이프 레코드로 저장합니다.
  5. 소스 상태 업데이트:
    • 성공 → fail_count 리셋.
    • 실패 → fail_count 증가; 연속 3회 실패 시 알림.

게시 날짜를 파싱하는 대신 프론트페이지를 비교하는 이유는 무엇입니까? 많은 소스가 신뢰할 수 없는 타임스탬프나 스크래핑 방지 레이아웃을 가지고 있기 때문입니다. "프론트 페이지의 새로운 내용"은 "오늘 윈도우 내의 뉴스"에 대한 강력한 프록시입니다.

시간 윈도우 가드 (결정론적, LLM 아님): pubDate를 노출하는 풀(pull) 소스(RSS/Atom)의 경우 스캐너는 비교 전에 엄격한 컷오프를 적용합니다. 기본 윈도우는 36시간입니다. extract_profile.window_hours로 재정의할 수 있습니다. 윈도우보다 오래된 항목은 삭제됩니다. 날짜가 없거나 구문 분석할 수 없는 항목은 기본적으로 제외됩니다(missing_date_policy: exclude). 엔진이 확인할 수 없는 날짜는 신뢰할 수 없는 날짜이기 때문입니다. 날짜를 신뢰할 수 없고 프론트페이지 비교가 주요 신호인 소스에 대해서만 missing_date_policy: include로 설정하십시오.

이 규칙은 날짜-윈도우 적용이 판단이 아니라 기록 관리이기 때문에 존재합니다. 결정론적이고 감사 가능해야 하며, 날짜를 잘못 읽고 오래된 이야기를 오늘 보고서에 섞는 경향이 있는 LLM에 위임해서는 안 됩니다.

3.2 편집 — 프로필로 사전 선별

목표: 저렴한 규칙으로 후보를 점수화하고, 넓은 깔때기를 유지합니다.

공식:

점수 = 합계(일치하는 키워드 가중치)
      + 합계(카테고리 일치 보너스)
      - 합계(일치하는 부정어 가중치)

단계:

  1. 버티컬의 최신 프로필을 로드합니다.
  2. 각 후보 제목에 대해:
    • 키워드 및 카테고리(ASCII 용어의 경우 단어 경계)를 일치시킵니다.
    • 부정어를 뺍니다.
    • 일치하는 용어에 대해 last_hit을 기록합니다.
  3. 임계값: 기본값 점수 >= 2는 통과입니다. 낮은 점수는 reject_reason: low_score로 거부됩니다.
  4. 모든 후보를 item 레코드로 테이프에 기록합니다:
    • 통과 → stage: prescreened.
    • 거부됨 → stage: rejected, reject_reason 포함.

거부된 아이템은 보정을 위한 연료입니다. 절대 삭제하지 마십시오.

3.3 검토 — 구조화된 두 번째 패스

목표: 사전 선별된 아이템이 실제 뉴스이고, 주제에 적합하며, 광고/채용 공고가 아닌지 확인합니다.

단계:

  1. 사전 선별된 아이템을 점수(내림차순)로 정렬합니다.
  2. 독점을 피하기 위해 소스당 최대 표현 수를 제한합니다(예: 소스당 최대 4개).
  3. 각 아이템에 대해 기사 텍스트(처음 ~1000자)를 가져오고 대체 체인을 통해 게시 날짜를 추출합니다:
    • JSON-LD datePublished
    • <meta article:published_time>
  4. 설정된 LLM 제공자에게 배치 프롬프트를 보냅니다. 각 항목에 대해 요청합니다:
    • is_news — 광고/채용 공고/공지 사항이 아님
    • is_ad, is_job — 실격 조건
    • vertical_fit — 0에서 1까지의 관련성 점수
    • summary — 2-3문장 요약
  5. 거부 사유 적용:
    • not_news, is_ad, is_job, low_fit.
  6. 결과를 stage: verified 또는 stage: rejected인 item 레코드로 테이프에 기록합니다.

LLM 제공자가 설정되지 않은 경우: 파이프라인은 --dry 규칙 전용 모드로 실행되며 사전 선별된 아이템을 직접 승격시킵니다.

3.4 게시 — 중복 제거 및 보고

목표: 중복을 제거하고 읽을 수 있는 일일 보고서를 생성합니다.

단계:

  1. 최근 풀링된 아이템(최근 14일, 오늘 제외)을 로드합니다.
  2. 확인된 아이템에 대한 2단계 중복 제거:
    • 정확한 url_norm 일치 → 건너뜁니다.
    • 해밍 거리 ≤ 3인 제목 simhash → 동일 이벤트; 클러스터로 그룹화.
  3. 각 클러스터에 대해 가장 높은 점수를 받은 아이템을 기본 아이템으로 선택하고, 나머지는 관련 소스가 됩니다.
  4. 클러스터를 소스 수와 점수로 정렬합니다.
  5. 보고서 형식을 지정합니다. 상태 바닥글을 추가합니다:
    • 스캔된 소스, 사전 선별된 아이템, 거부됨, 풀링됨, 보고서 아이템 수
    • 소스 상태 알림
    • 단계별 소요 시간
  6. report 레코드를 테이프에 기록합니다.

4. 반영 루프 (자체 진화)

트리거: 사용자가 보고서를 읽고 피드백을 제공합니다. 예:

"AI 안전 관련 기사를 더 많이, 스폰서 게시물은 더 적게, EU 규제도 계속 주시해 주세요."

단계:

  1. 피드백을 프로필 작업으로 구문 분석합니다:
    • add_keyword: {term, weight}
    • adjust_weight: {term, delta}
    • remove_keyword: {term}
    • add_negative: {term, weight}
    • adjust_negative_weight: {term, delta}
    • remove_negative: {term}
  2. 프로필에 작업을 적용하고 버전을 증가시킵니다.
  3. 작업과 원본 피드백 텍스트가 포함된 evolution 테이프 레코드를 기록합니다.
  4. 새로운 부정어/낮아진 가중치에 대해 최근 풀링된 아이템(최근 7일)을 다시 확인합니다.
    • 이제 필터링될 항목이 있으면 사용자에게 개수를 알립니다.
  5. 확인 요약을 반환합니다.

감쇠 규칙 (매주):

  • origin: learned인 용어는 30일 동안 적중(hit)되지 않으면 가중치가 1 감소합니다.
  • 가중치 0 → 보류 삭제 목록으로 이동; 삭제 전 사용자에게 문의합니다.
  • origin: manual 용어는 자동 삭제되지 않고 경고만 합니다.

5. 소스 획득 분류

소스는 다섯 가지 방법으로 획득할 수 있습니다. 엔진은 테이프에 도달하면 획득 방법과 관계없이 균일하게 처리합니다. 보고서의 소스 레지스트리는 각 소스를 status [name](url) · method로 렌더링하여 독자가 소스가 어떻게 획득되었는지 한눈에 알 수 있도록 합니다.

유형메커니즘사용 시기레지스트리 표시
pull크론의 RSS/API/curl안정적인 피드 및 API🟢 [Hacker News](https://news.ycombinator.com/rss) · pull
scan프론트페이지 비교 + 사전 선별SPA, 비정규 사이트🟢 [TechCrunch](https://techcrunch.com) · scan
push웹훅/SSE/브리지내부 알림 또는 폐쇄형 생태계🟢 [Internal Alerts](...) · push
search임시 검색새 소스 발견 또는 주요 이벤트 따라잡기🟢 [Search](...) · search
human-feed사용자가 링크를 드롭독점 팁 및 직접 신호🟢 Weixin Tip · human-feed

버티컬 구성에 {id, name, list_url, extract_profile}을 등록하여 소스를 추가합니다. human-feed 소스는 extract_profile = { method = "human_feed" }로 설정하고 list_url은 생략할 수 있습니다.

에이전트 지원 human-feed

유용한 변형은 에이전트 지원 human-feed입니다: 에이전트 자체가 플랫폼 검색(예: 최근 24-36시간 내 AI 해시태그 아래 게시물에 대한 X 검색)을 수행하고, ≥1개의 후보를 선별하여 human-feed 채널을 통해 주입합니다. 주입된 아이템은 pull/scan 아이템과 동일한 사전 선별, 검토 및 중복 제거 단계를 거칩니다. human-feed는 후보 풀에 대한 진입을 보장하지만 보고서에 대한 무료 패스는 아닙니다.

"메시지에서 멈추지 마십시오"

소셜 미디어 게시물, 헤드라인 또는 팁은 신호일 뿐 인용이 아닙니다. 주입된 모든 아이템은 테이프에 도달하기 전에 원본 기사 URL로 확인되어야 합니다. 에이전트가 게시물 URL만 찾을 수 있는 경우 scripts/x_hunt.py는 원본 기사에 대한 최선의 검색을 시도합니다. 실패하면 후보는 삭제되고 그 이유가 테이프에 기록됩니다. 게시된 보고서는 독자를 최종 소스로 게시물이나 헤드라인으로 안내하지 않습니다.

참조 구현 (scripts/x_hunt.py)

scripts/x_hunt.py는 결정론적 기록 관리입니다: 이미 선별된 아이템을 테이프에 기록합니다. 검색, 읽기 및 선택은 에이전트의 판단 작업이며, 에이전트 자체의 WebSearch/도구를 사용하여 수행됩니다.

# 권장 경로: 에이전트가 이미 헤드라인을 원본 기사 URL로 확인했습니다.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI announces GPT-5" \
  --url "https://openai.com/blog/introducing-gpt-5" \
  --inject

# X 게시물 URL만 사용 가능한 경우 스크립트가 원본 URL을 확인하려고 시도합니다.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI announces GPT-5" \
  --url "https://x.com/OpenAI/status/1234567890" \
  --inject

# 최선의 자동화된 검색 대체 (종종 차단됨; 드물게 사용).
python scripts/x_hunt.py --vertical ai-frontier \
  --query "AI artificial intelligence site:x.com" --inject

# 주입하지 않고 검색 대체 후보 검토
python scripts/x_hunt.py --vertical ai-frontier \
  --query "AI artificial intelligence site:x.com"

주입된 레코드는 다음 필드와 함께 item으로 테이프에 기록됩니다:

  • source_id: 등록된 소스 ID(기본값 owner_tips).
  • stage: scanned — 사전 선별 단계에서 파이프라인에 진입합니다.
  • facilitated_by: agent — 감사 가능한 출처.

삭제된 레코드(확인 불가능한 게시물 URL, 주제에서 벗어난 후보 등)도 stage: dropped 및 drop_reason과 함께 테이프에 추가되므로 감사 추적에는 포함되지 않은 내용이 포함됩니다.

관심사 분리: 판단(검색/읽기/선택/출처 확인)은 에이전트의 몫입니다. 기록 관리(URL 정규화, ID 할당, 테이프에 추가)는 스크립트의 몫입니다. 이는 스킬을 결정론적으로 유지하고 플랫폼별 검색 전략을 에이전트 자체 기능에 맡깁니다.

이 모드는 플랫폼을 수동 콘텐츠 소스에서 능동적인 소스 발견 사냥으로 전환합니다: 에이전트가 같은 매체에서 좋은 스토리를 반복해서 발견하면 ECHO는 "'<매체>'를 추적 소스로 추가할까요?"라고 물을 수 있고, '예'라는 답변에 따라 해당 매체를 공식 pull/scan 소스로 승격시킵니다. 이 폐쇄 루프가 스킬의 자체 진화가 실제로 작동하는 방식입니다.


6. 테이프 레코드 유형

각 테이프 라인은 type 필드가 있는 JSON 객체입니다.

유형필드
sourceid, name, vertical, list_url, extract_profile, health
frontpagesource_id, date, items[], count
profilevertical, version, keywords[], negatives[], categories[], reason
itemid, vertical, source_id, title, url, url_norm, stage, scores, reject_reason, simhash, cluster_id
reportdate, vertical, item_ids[], generated_at, stats
feedbackdate, vertical, text, applied_ops[], status
evolutionvertical, from_version, to_version, ops[], reason, date

7. 참조 구현 실행

# 규칙 전용 드라이 런 (LLM 불필요)
python -m engine.pipeline --dry --vertical tech --config config/example_vertical.toml

# LLM 검토 포함
export APERTURE_LLM_API_KEY="sk-..."
python -m engine.pipeline --vertical tech --config config/example_vertical.toml

# 피드백 적용
python -c "from engine.feedback import apply_feedback; apply_feedback('AI 안전 관련 기사를 더 많이, 광고는 더 적게', 'tech')"

게시 후 선택적으로 ECHO를 실행하여 사용자에게 최대 두 개의 명확화 질문을 할 수 있습니다:

from engine.echo import ask

questions = ask("tech")
for q in questions:
    print(q["question"])

# 나중에 한 단어 답변 적용: yes / no / skip / silence
from engine.echo import apply_answer
apply_answer(question_id, "tech", "yes")

8. ECHO — 사전 예방적 명확화

ECHO는 선택적인 사전 예방적 계층으로, 각 보고서 후 사용자에게 최대 두 개의 한 단어 명확화 질문을 합니다. 수동적인 독자를 능동적인 프로필 트레이너로 전환합니다.

ECHO는 의도적으로 …

[번역을 위해 잘림]

관련 스킬