CommunityEscrita e Ediçãogithub.com

lukethecat/aperture

Uma habilidade para agentes de IA que curam melhores relatórios diários — examina páginas iniciais, grava cada decisão e aprende com o feedback.

O que é aperture?

Aperture é uma habilidade projetada para agentes de IA que desejam produzir relatórios diários de curadoria de notícias de alta qualidade, com rastreabilidade e capacidade de aprendizado contínuo. Ela substitui leitores de RSS estáticos e sumarizadores únicos de LLM por um pipeline determinístico e auditável de quatro estágios: coleta (varredura de front pages de fontes configuradas), edição (pré-triagem com um perfil ponderado de palavras-chave e categorias), revisão (verificação por LLM para filtrar anúncios, empregos e conteúdo fora do tópico) e publicação (deduplicação e geração de um relatório legível). Todas as decisões são registradas em um 'tape' append-only (formato JSONL), um log imutável que permite auditoria e replay de qualquer estado anterior. O diferencial é o loop de reflexão: o feedback explícito do usuário é analisado para gerar operações de perfil (adicionar/ajustar/remover palavras-chave e termos negativos), que são versionadas e aplicadas automaticamente, refinando o 'gosto' do agente ao longo do tempo. A habilidade suporta múltiplas fontes (RSS, APIs, páginas web, feeds humanos facilitados pelo próprio agente via busca em plataformas como X) e implementações agnósticas (leitura do SKILL.md, implementação Python de referência ou portabilidade para qualquer runtime de agente).

Funciona com~Claude Code✓Codex CLI~Cursor
npx skills add lukethecat/aperture

Installed? Explore more Escrita e Edição skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Habilidade: Aperture

Resumo — Uma habilidade para agentes de IA que desejam produzir relatórios diários melhores e descobrir mais do que seus usuários realmente querem. Aperture examina as páginas iniciais das fontes, compara-as com o dia anterior, pré-seleciona com um perfil ponderado, revisa com um LLM, deduplica, publica e evolui o perfil a partir do feedback do usuário. Todas as decisões vão para um log append-only (tape).

Esta habilidade é agnóstica em relação à implementação. Um agente pode executá-la lendo este arquivo, chamando a implementação de referência em Python incluída, ou portando os padrões para seu próprio runtime.


1. Propósito

Dar aos agentes uma habilidade determinística e auditável de curadoria de notícias que substitui leitores de RSS estáticos e sumarizadores únicos de LLM:

  1. Examinar a página inicial — tratar "ler a página inicial" como uma operação de primeira classe, não apenas como polling de feeds.
  2. Gravar cada decisão — registrar cada snapshot de fonte, item, motivo de rejeição, versão de perfil e relatório em um log append-only para auditoria e replay.
  3. Aprender com o feedback do usuário — atualizar o gosto do agente através de feedback explícito e manter cada alteração versionada e reversível.

Quando um agente executa esta habilidade, ele pode responder "por que isso foi selecionado?" e "o que teria sido diferente ontem?" a partir do tape.


2. Conceitos centrais

ConceitoSignificado
VerticalUm segmento de notícias configurado (ex.: tecnologia, automóveis, política de IA). Cada vertical tem suas próprias fontes, perfil e tape.
TapeLog JSONL append-only. Um arquivo por vertical. Cada snapshot de fonte, item, versão de perfil, feedback e relatório é um registro.
PerfilO "gosto" da vertical: palavras-chave ponderadas, categorias com bônus e termos negativos. Versionado; cada alteração é registrada.
VarreduraBuscar a página de listagem de cada fonte, extrair títulos/links, normalizar URLs, comparar com o snapshot anterior.
Loop de reflexãoFeedback do usuário → operações de perfil analisadas → incremento de versão → registro de evolução → reavaliação de itens recentemente coletados.

3. Pipeline de quatro estágios

┌─────────┐    ┌──────────┐    ┌─────────┐    ┌──────────┐
│ coleta  │ -> │  edição  │ -> │ revisão │ -> │  publicar│
│(varredura│    │(pré-triagem│    │ (LLM)   │    │(dedup + │
│         │    │  regras) │    │         │    │ relatório│
└─────────┘    └──────────┘    └─────────┘    └──────────┘
     │               │               │               │
     └───────────────┴───────────────┴───────────────┘
                         │
                    TAPE append-only

3.1 Coleta — examinar a página inicial

Objetivo: produzir os itens candidatos de hoje de cada fonte configurada.

Passos:

  1. Para cada fonte, buscar list_url.
  2. Extrair pares {titulo, url} usando o extract_profile da fonte:
    • rss — analisar blocos <item>.
    • generic_links — analisar tags <a href>.
    • regex — aplicar uma regex com grupos nomeados para título/url/data.
    • json_api — navegar por um caminho JSON e ler as chaves de título/url.
  3. Normalizar cada URL:
    • Remover fragmentos e parâmetros de rastreamento (utm_*, fbclid, etc.).
    • Remover a barra no final e o www. inicial.
    • Forçar o esquema https, host em minúsculas.
  4. Comparar com o snapshot da página inicial anterior para esta fonte (lido do tape).
    • Novos valores de url_norm tornam-se candidatos.
    • Salvar o snapshot completo atual como um novo registro de frontpage no tape.
  5. Atualizar a saúde da fonte:
    • Sucesso → resetar fail_count.
    • Falha → incrementar fail_count; alertar se ≥ 3 falhas consecutivas.

Por que comparar com a página inicial em vez de analisar datas de publicação? Porque muitas fontes têm timestamps não confiáveis ou layouts anti-scraping. "Novo na página inicial" é um proxy robusto para "notícias na janela de hoje".

Guarda de janela de tempo (determinístico, não LLM): Para fontes pull que expõem pubDate (RSS/Atom), o scanner aplica um corte rígido antes da comparação. A janela padrão é de 36 horas; substituível com extract_profile.window_hours. Itens mais antigos que a janela são descartados. Itens com datas ausentes ou não analisáveis são excluídos por padrão (missing_date_policy: exclude) porque uma data que o mecanismo não consegue verificar é uma data em que ele não pode confiar. Defina missing_date_policy: include apenas para fontes onde as datas são sabidamente não confiáveis e a comparação de página inicial é o sinal primário.

Esta regra existe porque a aplicação da janela de data é contabilidade, não julgamento. Ela deve ser determinística e auditável — nunca delegada a um LLM, que é propenso a ler datas incorretamente e misturar histórias antigas no relatório de hoje.

3.2 Edição — pré-selecionar com o perfil

Objetivo: pontuar candidatos com regras baratas; manter o funil amplo.

Fórmula:

score = soma(pesos de palavras-chave correspondentes)
      + soma(bônus de correspondência de categoria)
      - soma(pesos negativos correspondentes)

Passos:

  1. Carregar o perfil mais recente da vertical.
  2. Para cada título candidato:
    • Corresponder palavras-chave e categorias (limite de palavra inteira para termos ASCII).
    • Subtrair termos negativos.
    • Registrar last_hit para qualquer termo correspondente.
  3. Limiar: pontuação padrão >= 2 passa; pontuações menores são rejeitadas com reject_reason: low_score.
  4. Escrever cada candidato no tape como um registro item:
    • Aprovado → stage: prescreened.
    • Rejeitado → stage: rejected, com reject_reason.

Itens rejeitados são o combustível para calibração. Nunca os delete.

3.3 Revisão — segunda passagem estruturada

Objetivo: verificar se os itens pré-selecionados são notícias reais, no tópico e não anúncios/empregos.

Passos:

  1. Ordenar itens pré-selecionados por pontuação (decrescente).
  2. Limitar a representação por fonte (ex.: máx. 4 por fonte) para evitar monopólio.
  3. Para cada item, buscar o texto do artigo (primeiros ~1000 caracteres) e extrair uma data de publicação através de uma cadeia de fallback:
    • JSON-LD datePublished
    • <meta article:published_time>
  4. Enviar um prompt em lote para o provedor de LLM configurado. Perguntar para cada item:
    • is_news — não é anúncio/emprego/comunicado
    • is_ad, is_job — desqualificadores
    • vertical_fit — pontuação de relevância de 0 a 1
    • summary — resumo de 2 a 3 frases
  5. Aplicar motivos de rejeição:
    • not_news, is_ad, is_job, low_fit.
  6. Escrever resultados no tape como registros item com stage: verified ou stage: rejected.

Se nenhum provedor de LLM estiver configurado: o pipeline executa no modo --dry apenas com regras e promove itens pré-selecionados diretamente.

3.4 Publicar — deduplicar e relatar

Objetivo: colapsar duplicatas e produzir um relatório diário legível.

Passos:

  1. Carregar itens recentemente agrupados (últimos 14 dias, excluindo hoje).
  2. Dedup de dois níveis para itens verificados:
    • Correspondência exata de url_norm → pular.
    • simhash de título com distância de Hamming ≤ 3 → mesmo evento; agrupar em um cluster.
  3. Para cada cluster, escolher o item com maior pontuação como item principal; os outros tornam-se fontes relacionadas.
  4. Ordenar clusters por contagem de fontes e pontuação.
  5. Formatir o relatório. Anexar um rodapé de status:
    • fontes examinadas, itens pré-selecionados, rejeitados, agrupados, contagem de itens do relatório
    • alertas de saúde da fonte
    • tempos dos estágios
  6. Escrever um registro report no tape.

4. Loop de reflexão (auto-evolução)

Gatilho: o usuário lê um relatório e dá feedback, ex.:

"Mais histórias de segurança de IA, menos posts patrocinados e fique de olho na regulação da UE."

Passos:

  1. Analisar o feedback em operações de perfil:
    • add_keyword: {termo, peso}
    • adjust_weight: {termo, delta}
    • remove_keyword: {termo}
    • add_negative: {termo, peso}
    • adjust_negative_weight: {termo, delta}
    • remove_negative: {termo}
  2. Aplicar operações ao perfil; incrementar versão.
  3. Escrever um registro evolution no tape contendo as operações e o texto de feedback original.
  4. Reavaliar itens recentemente agrupados (últimos 7 dias) em relação aos novos termos negativos/pesos reduzidos.
    • Se algum agora seria filtrado, relatar a contagem ao usuário.
  5. Retornar um resumo de confirmação.

Regra de decaimento (semanal):

  • Termos com origin: learned que não tiveram hit por 30 dias perdem 1 de peso.
  • Peso 0 → mover para uma lista de exclusão pendente; perguntar ao usuário antes de remover.
  • Termos com origin: manual nunca são auto-excluídos, apenas avisam.

5. Taxonomia de aquisição de fontes

Uma fonte pode ser adquirida de cinco maneiras. O mecanismo as trata uniformemente uma vez que chegam ao tape. O registro de fontes do relatório renderiza cada fonte como status [nome](url) · método para que um leitor possa ver rapidamente como a fonte é adquirida.

TipoMecanismoQuando usarExibição no registro
pullRSS/API/curl em cronFeeds e APIs estáveis🟢 [Hacker News](https://news.ycombinator.com/rss) · pull
scanComparação de página inicial + pré-seleçãoSPAs, sites irregulares🟢 [TechCrunch](https://techcrunch.com) · scan
pushWebhook/SSE/bridgeAlertas internos ou ecossistemas fechados🟢 [Alertas Internos](...) · push
searchBusca ad-hocDescoberta de novas fontes ou atualização de grandes eventos🟢 [Busca](...) · search
human-feedUsuário envia um linkDicas exclusivas e sinais de primeira mão🟢 Dica Weixin · human-feed

Adicione uma fonte registrando {id, nome, list_url, extract_profile} na configuração da vertical. Uma fonte human-feed define extract_profile = { method = "human_feed" } e pode omitir list_url.

Human-feed facilitado pelo agente

Uma variante útil é o human-feed facilitado pelo agente: o próprio agente realiza uma busca na plataforma (ex.: no X por posts sob uma hashtag de IA nas últimas 24–36 horas), seleciona ≥1 candidato e o injeta através do canal human-feed. O item injetado ainda passa pelos mesmos estágios de pré-seleção, revisão e dedup que os itens pull/scan — o human-feed garante entrada no pool de candidatos, não passagem livre para o relatório.

"Nunca pare na mensagem"

Um post de mídia social, manchete ou dica é um sinal, não uma citação. Cada item injetado deve resolver para a URL do artigo original antes de chegar ao tape. Se o agente só conseguir encontrar uma URL de post, scripts/x_hunt.py tenta uma busca de melhor esforço pelo artigo original; se falhar, o candidato é descartado e o motivo é registrado no tape. O relatório publicado nunca aponta os leitores para um post ou manchete como fonte final.

Implementação de referência (scripts/x_hunt.py)

scripts/x_hunt.py é contabilidade determinística: ele escreve um item já selecionado no tape. A busca, leitura e seleção são o trabalho de julgamento do agente, realizados com as próprias ferramentas de busca na web/ferramentas do agente.

# Caminho preferido: o agente já resolveu a manchete para a URL do artigo original.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI anuncia GPT-5" \
  --url "https://openai.com/blog/introducing-gpt-5" \
  --inject

# Se apenas uma URL de post do X estiver disponível, o script tenta resolver a URL original.
python scripts/x_hunt.py --vertical ai-frontier --source-id ainativef_zh \
  --title "OpenAI anuncia GPT-5" \
  --url "https://x.com/OpenAI/status/1234567890" \
  --inject

# Fallback de busca automatizada de melhor esforço (frequentemente bloqueada; use com moderação).
python scripts/x_hunt.py --vertical ai-frontier \
  --query "IA inteligência artificial site:x.com" --inject

# Revisar candidatos de fallback de busca sem injetar
python scripts/x_hunt.py --vertical ai-frontier \
  --query "IA inteligência artificial site:x.com"

O registro injetado é escrito no tape como um item com:

  • source_id: o id da fonte registrada (padrão owner_tips).
  • stage: scanned — ele entra no pipeline na pré-seleção.
  • facilitated_by: agent — proveniência auditável.

Registros descartados (URLs de post não resolvíveis, candidatos fora do tópico, etc.) também são anexados ao tape com stage: dropped e um drop_reason, para que a trilha de auditoria inclua o que não foi incluído.

Separação de responsabilidades: julgamento (busca/leitura/seleção/resolução de origem) pertence ao agente; contabilidade (normalizar URL, alocar id, anexar ao tape) pertence ao script. Isso mantém a habilidade determinística e deixa estratégias de recuperação específicas da plataforma para as próprias capacidades do agente.

Este modo transforma a plataforma de uma fonte de conteúdo passiva em uma caça ativa de descoberta de fontes: quando o agente repetidamente encontra boas histórias do mesmo veículo, o ECHO pode perguntar "Adicionar '<veículo>' como uma fonte rastreada?" e, em caso afirmativo, promover o veículo a uma fonte pull/scan formal. Esse loop fechado é a auto-evolução da habilidade em ação.


6. Tipos de registro do tape

Cada linha do tape é um objeto JSON com um campo type.

typecampos
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. Executando a implementação de referência

# Execução apenas com regras (sem LLM)
python -m engine.pipeline --dry --vertical tech --config config/example_vertical.toml

# Com revisão LLM
export APERTURE_LLM_API_KEY="sk-..."
python -m engine.pipeline --vertical tech --config config/example_vertical.toml

# Aplicar feedback
python -c "from engine.feedback import apply_feedback; apply_feedback('Mais segurança de IA, menos anúncios', 'tech')"

Após a publicação, opcionalmente execute o ECHO para fazer ao usuário até duas perguntas de esclarecimento:

from engine.echo import ask

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

# Depois, aplicar uma resposta de uma palavra: yes / no / skip / silence
from engine.echo import apply_answer
apply_answer(question_id, "tech", "yes")

8. ECHO — esclarecimento proativo

O ECHO é uma camada proativa opcional que faz ao usuário até duas perguntas de esclarecimento de uma palavra após cada relatório. Ele transforma leitores passivos em treinadores ativos de perfil.

O ECHO é intencionalmente …

[truncado para tradução]

Habilidades Relacionadas