Ürün

Idea Codex

Dört açık kaynaktan pazar sinyali toplayan, bunları sorgulanabilir tablolara indirgeyen ve her satırı onu üreten çalışmaya bağlı tutan bir hat.

Neyi topluyor, neyi kabul etmiyor

Dört açık kaynak çalışıyor: Reddit, Hacker News, Steam ve Google Trends. Her birinin ortak bir portun arkasında kendi adaptörü var, gelen her yanıt da hiçbir şey onu okumadan önce sürümlenmiş bir zarfla ham havuza iniyor.

Gerisini şekillendiren kural şu: kaynağı olmayan rakam sıfır ediyor. Saklanmış bir yanıta kadar izlenemeyen iddialar [UNCITED] işaretini alıyor ve puanlamada hiç ağırlık taşımıyor. Bu bir iç düzen meselesi olarak durmuyor; üstüne kurulan şey denetlenebilmek zorunda olan bir analist ve kimsenin geri yürütemediği bir rakam da tam olarak onun önlemek için var olduğu hata oluyor.

5

Bounded context

Collection, refinement, analysis, candidates, content safety.

152

Test dosyası

221 kaynak dosyaya karşı, katı tipleme altında yazıldı.

9

Import çiti

Linter'ın denetlediği sözleşmeler; katman kayması CI'da düşüyor.

İndirgeme ile analiz ayrı işler

Sınırı çizmek bir karar kaydı gerektirdi. İndirgeme bir şeyin kendi niteliklerini üretiyor: temizleme, isim öbeği çıkarımı, duygu, hız. Analiz ise şeyler arasındaki yapıyı üretiyor: talep grafı, sıralamalar, kümeler.

Duygu analizini analize koymak daha kolay olurdu ve yanlış olurdu. Tek bir satırı tarif ediyor, yani tek kaynağın yeniden kurulması onu yeniden hesaplamak zorunda; dört kaynağa yayılan bir graf ise bunu yapmamalı.

Uzadıkça ucuzlayan örnekleme

Anlık görüntü sıklığını takvim değil puan belirliyor. Hızlı hareket eden şey sık yeniden örnekleniyor, kimsenin okumadığı şey rotasyondan düşüyor. Bir Steam yorumu, üzerinde durduğu oyunun popülerliğine ters orantılı puanlanıyor; böylece bir avuç dev oyun kuyruğu kaplayamıyor.

Ücretli kaynaklar çekim başına harcama defterine işleniyor ve ücretli bir kaynağa ancak ücretsiz olan denendikten sonra uzanılıyor.

GöstergeDoğrudan yol
  1. Toplama

    • Kaynak toplayıcıları

      Python

      Neden

      Reddit, Hacker News, Steam ve Google Trends. Her birine tek adaptör, hepsi tek portun arkasında duruyor; beşinci kaynak bu yüzden ikinci bir hat değil, bir adaptör oluyor.

    • Ham havuz

      Python

      Neden

      Her yanıt, hiçbir şey onu okumadan önce, sürümlenmiş bir zarf altında geldiği gibi saklanıyor. İndirgeyiciler yeniden yazılıp bunun üzerinde tekrar çalıştırılabiliyor; bir determinizm testi de yeniden kurulumun aynı yere düştüğünü kanıtlıyor.

    • Harcama defteri

      Python · DuckDB

      Neden

      Ücretli kaynaklar çekim başına ölçülüyor. Üstündeki kural şu: ücretli bir kaynağa ancak ücretsiz olan denenip neden yetmediği yazıldıktan sonra uzanılıyor.

  2. İndirgeme

    • Kaynak başına indirgeyici

      Python · spaCy

      Neden

      Kaynak başına bir tane, ortak bir omurga üzerinde. Temizleme, isim öbeği çıkarımı ve duygu analizi burada oluyor; bu katmanda hiçbir şey iki varlık arasında ilişki uydurmuyor.

    • Yeniden örnekleme kuyruğu

      Python · DuckDB

  3. Analiz

    • Sinyaller ve graf

      Python

    • İçerik güvenliği

      Python

  4. Substrat

    • İşlenmiş depo

      DuckDB · Python

      Neden

      Dar tablolar ve üzerlerinde ince bir sinyal katmanı duruyor. Her satır onu üreten çekim çalışmasını taşıyor, böylece herhangi bir rakam geldiği çekime kadar geri yürütülebiliyor.

  5. Yüzey

    • Ürün yüzeyi

      TypeScript · Next.js · NestJS · PostgreSQL

      Neden

      Ayrı bir repo; bu sitedeki SaaS altyapısından fork edildi, o yüzden kiracılık, iki katmanlı kimlik, denetim ve veri haklarıyla açılıyor. Üzerine koyduğu şey okuma yüzeyi oluyor: içeri brief giriyor, dışarı kaynaklı cevap çıkıyor.

Her satır geldiği çekimi hatırlıyorPythonİşlenmiş depo düğümünden
"""Migration refinement:0013 — per-pull-run provenance on entity tables (ADR-0029).Adds write-once `pull_run_id` (the pull run that produced the row, from the rawdoc) + `first_refined_at` (net-new marker; backfilled = refined_at for existingrows) to the 6 single-PK entity tables. Composite-PK/snapshot/trends tables areout of scope (Phase 1b-ii). steam_apps already has first_refined_at (m_0010)."""from __future__ import annotationsimport duckdbID = "0013_curated_provenance"# (table, needs_first_refined_at) — steam_apps already has it (m_0010);# steam_reviews does NOT have it (confirmed: m_0004_steam has no first_refined_at# on steam_reviews, and no subsequent migration adds it before m_0013)._TABLES = [    ("reddit_submissions", True),    ("reddit_comments", True),    ("hn_stories", True),    ("hn_comments", True),    ("steam_apps", False),    ("steam_reviews", True),]def up(con: duckdb.DuckDBPyConnection) -> None:    # Ordered in three passes within the migration transaction. DuckDB rejects    # CREATE INDEX while there are outstanding row UPDATEs in the same    # transaction, so ALL index creation must happen BEFORE the backfill UPDATEs    # (ADD COLUMN is DDL and does not count as an outstanding update). On a    # populated DB the naive add->update->index ordering raises    # "Cannot create index with outstanding updates".    # Pass 1 — add columns (DDL).    for table, needs_fra in _TABLES:        con.execute(f"ALTER TABLE {table} ADD COLUMN pull_run_id VARCHAR")        if needs_fra:            con.execute(f"ALTER TABLE {table} ADD COLUMN first_refined_at TIMESTAMPTZ")    # Pass 2 — create indexes (before any UPDATE; built on the fresh NULL columns).    for table, _ in _TABLES:        con.execute(            f"CREATE INDEX IF NOT EXISTS {table}_first_refined_idx ON {table}(first_refined_at)"        )        con.execute(f"CREATE INDEX IF NOT EXISTS {table}_pull_run_idx ON {table}(pull_run_id)")    # Pass 3 — backfill first_refined_at for existing rows (UPDATEs last).    for table, needs_fra in _TABLES:        if needs_fra:            con.execute(                f"UPDATE {table} SET first_refined_at = refined_at WHERE first_refined_at IS NULL"            )
Altı varlık tablosuna bir kez yazılan köken bilgisi ekleniyor; böylece ürünün gösterdiği herhangi bir rakam, onu üreten çekim çalışmasına kadar geri yürütülebiliyor. İlginç olan kısım sıralama. DuckDB, aynı işlem içinde bekleyen bir satır güncellemesi varken indeks oluşturmayı reddediyor, dolayısıyla ekle-doldur-indeksle şeklindeki bariz sıra dolu bir veritabanında patlıyor. Migration bu yüzden üç geçişte çalışıyor ve her indeks ilk UPDATE'ten önce kuruluyor. Yorum satırı çözümü değil hatayı kaydediyor, okuyanın ihtiyacı olan da o.

idea-codex430b9e9src/market_research/contexts/refinement/adapters/migrations/m_0013_curated_provenance.pySatır 1 – 5151 satır

"""Migration refinement:0013 — per-pull-run provenance on entity tables (ADR-0029).

Adds write-once `pull_run_id` (the pull run that produced the row, from the raw
doc) + `first_refined_at` (net-new marker; backfilled = refined_at for existing
rows) to the 6 single-PK entity tables. Composite-PK/snapshot/trends tables are
out of scope (Phase 1b-ii). steam_apps already has first_refined_at (m_0010).
"""

from __future__ import annotations

import duckdb

ID = "0013_curated_provenance"

# (table, needs_first_refined_at) — steam_apps already has it (m_0010);
# steam_reviews does NOT have it (confirmed: m_0004_steam has no first_refined_at
# on steam_reviews, and no subsequent migration adds it before m_0013).
_TABLES = [
    ("reddit_submissions", True),
    ("reddit_comments", True),
    ("hn_stories", True),
    ("hn_comments", True),
    ("steam_apps", False),
    ("steam_reviews", True),
]


def up(con: duckdb.DuckDBPyConnection) -> None:
    # Ordered in three passes within the migration transaction. DuckDB rejects
    # CREATE INDEX while there are outstanding row UPDATEs in the same
    # transaction, so ALL index creation must happen BEFORE the backfill UPDATEs
    # (ADD COLUMN is DDL and does not count as an outstanding update). On a
    # populated DB the naive add->update->index ordering raises
    # "Cannot create index with outstanding updates".
    # Pass 1 — add columns (DDL).
    for table, needs_fra in _TABLES:
        con.execute(f"ALTER TABLE {table} ADD COLUMN pull_run_id VARCHAR")
        if needs_fra:
            con.execute(f"ALTER TABLE {table} ADD COLUMN first_refined_at TIMESTAMPTZ")
    # Pass 2 — create indexes (before any UPDATE; built on the fresh NULL columns).
    for table, _ in _TABLES:
        con.execute(
            f"CREATE INDEX IF NOT EXISTS {table}_first_refined_idx ON {table}(first_refined_at)"
        )
        con.execute(f"CREATE INDEX IF NOT EXISTS {table}_pull_run_idx ON {table}(pull_run_id)")
    # Pass 3 — backfill first_refined_at for existing rows (UPDATEs last).
    for table, needs_fra in _TABLES:
        if needs_fra:
            con.execute(
                f"UPDATE {table} SET first_refined_at = refined_at WHERE first_refined_at IS NULL"
            )
  • Geriye doğru, ve bilerek. Neyin yeniden çekileceğine zaten işlenmiş olana bakarak karar veriliyor; kimsenin okumadığı şey rotasyondan düşüyor, hızlı hareket eden şey daha sık örnekleniyor. Sabit bir takvim ikisine de aynı parayı harcardı.

Ya bulunur, ya hesaplanır

Motorun beslediği ürün, üzerinde bir analistin oturduğu işlenmiş bir veritabanı oluyor. Bir sorunun cevabı substratta zaten varsa iş bir aramaya dönüşüyor; yoksa hat cevabı hesaplıyor ve substrat onu saklıyor. Veritabanını bayatlamaktan alıkoyan şey bu ikinci yol oluyor, toplamanın takvim yerine puanla sürülmesi de buradan geliyor.

Yüzey kendi reposunda duruyor; SaaS altyapısından fork edildiği için kiracılık, iki katmanlı kimlik, denetim ve veri haklarıyla açılıyor, üzerine de bu ürüne özgü olanı koyuyor: içeri brief giriyor, dışarı kaynaklı cevap çıkıyor. İki taraf birbirinin içine değil yazılı bir sözleşmeye bakıyor; motorun altta biçim değiştirmeye devam edebilmesini de bu sağlıyor.

Katkı

Yazılan commit
420
Kaydedilen karar
38

6 haftanın 4 tanesi aktif · 4 May 2026 – 14 Haz 2026

En uzun seri · 2 hafta · May – May

Yalnızca motor. Yüzey reposu geçmişini altyapıyla paylaşıyor, o iş de orada sayılıyor.

Ölçüm 2026-09-09