[{"data":1,"prerenderedAt":5138},["ShallowReactive",2],{"tech-ai-content-pipeline":3,"tech-summaries":4271},{"id":4,"title":5,"author":6,"body":7,"category":4255,"date":4256,"description":4257,"extension":4258,"image":4259,"meta":4260,"navigation":358,"path":4262,"readingTime":4263,"seo":4264,"stem":4265,"tags":4266,"__hash__":4270},"tech\u002Ftech\u002Fai-content-pipeline.md","AI Content Pipeline: Dari Scraping Ide sampai Publishing Bot","Zainul Fanani",{"type":8,"value":9,"toc":4216},"minimark",[10,19,22,28,31,34,40,46,49,52,57,60,91,94,97,118,121,124,150,153,157,160,210,213,303,306,309,546,549,560,563,567,570,573,720,723,761,772,775,861,864,868,871,874,879,882,899,902,964,968,971,991,994,1090,1094,1097,1114,1117,1278,1282,1285,1288,1302,1309,1313,1319,1322,1342,1344,1448,1451,1465,1468,1501,1504,1528,1535,1622,1625,1629,1632,1635,1677,1680,1768,1771,1923,1926,1930,1933,1936,1944,1947,1951,1954,1956,2042,2045,2116,2120,2123,2180,2183,2187,2190,2228,2231,2235,2238,2241,2258,2340,2344,2347,2410,2414,2417,2455,2458,2516,2519,2523,2526,2529,2554,2557,2563,2566,2700,2706,2774,2777,2861,2865,2871,2874,2891,2894,2926,2929,3065,3068,3072,3075,3078,3095,3098,3158,3161,3232,3235,3354,3357,3361,3364,3367,3384,3387,3431,3434,3437,3475,3478,3542,3545,3549,3566,3569,3653,3656,3660,3663,3666,3692,3695,3712,3715,3812,3815,3819,3822,3825,3851,3854,3877,3880,3929,3932,3936,3939,3942,3959,3962,3965,3985,3988,3991,4001,4004,4008,4011,4015,4018,4022,4025,4029,4032,4036,4039,4043,4046,4050,4053,4060,4067,4074,4081,4088,4092,4095,4178,4181,4185,4188,4194,4197,4200,4203,4206,4209,4212],[11,12,13,14,18],"p",{},"Ada satu masalah yang kelihatannya sepele, tapi makin lama makin brutal: ",[15,16,17],"strong",{},"ide konten itu berlimpah, tapi attention kita terbatas",".",[11,20,21],{},"Setiap hari ada thread bagus di X, update menarik di GitHub, artikel teknis di blog engineering, video pendek yang menjelaskan konsep dengan cara sederhana, diskusi LinkedIn yang memancing sudut pandang baru, sampai obrolan random yang sebenarnya bisa jadi bahan tulisan. Kalau semuanya dicatat manual, kita jadi sekretaris internet. Kalau dibiarkan, banyak ide bagus hilang begitu saja.",[11,23,24,25,18],{},"Di sisi lain, content production modern sudah bukan sekadar “nulis artikel”. Untuk satu ide saja, output-nya bisa pecah jadi banyak bentuk: blog post, GitHub repo, newsletter, LinkedIn post, WhatsApp broadcast, short video, internal memo, atau draft proposal. Itu artinya bottleneck sebenarnya bukan cuma produksi. Bottleneck pertama adalah ",[15,26,27],{},"discovery dan curation",[11,29,30],{},"Saya lebih suka melihat ini sebagai engineering problem, bukan sekadar creative problem.",[11,32,33],{},"Pertanyaannya bukan “bagaimana mencari ide?” Pertanyaannya adalah:",[35,36,37],"blockquote",{},[11,38,39],{},"Bagaimana membangun sistem yang setiap pagi menyodorkan 10 ide terbaik, fresh, relevan, tidak repetitif, dan siap didelegasikan ke agent produksi konten?",[11,41,42,43,18],{},"Jawaban saya: build an ",[15,44,45],{},"AI-powered content discovery and curation pipeline",[11,47,48],{},"Bukan full autopilot yang langsung scrape lalu publish ngawur. Itu resep cepat untuk bikin blog terasa seperti spam factory. Yang benar adalah semi-autonomous pipeline: mesin mengumpulkan, membersihkan, menilai, dan menyusun prioritas; manusia tetap memilih mana yang feasible, mana yang cocok dengan voice brand, dan mana yang worth shipping.",[11,50,51],{},"Artikel ini membahas desain lengkapnya: dari scraping media sosial dan website, database ide, scoring algorithm, morning top 10, human curation, content production delegation, sampai score decay supaya ide lama tidak muncul terus seperti sales follow-up yang nggak tahu malu.",[53,54,56],"h2",{"id":55},"problem-kita-tidak-kekurangan-ide-kita-kekurangan-sistem","Problem: Kita Tidak Kekurangan Ide, Kita Kekurangan Sistem",[11,58,59],{},"Kalau Anda developer, tech lead, atau engineering manager, kemungkinan besar Anda punya banyak sekali input harian:",[61,62,63,67,70,73,76,79,82,85,88],"ul",{},[64,65,66],"li",{},"Release notes dari tool yang dipakai tim",[64,68,69],{},"GitHub trending repositories",[64,71,72],{},"Hacker News atau Reddit discussion",[64,74,75],{},"LinkedIn posts dari founders dan engineers",[64,77,78],{},"Internal Slack\u002FTelegram conversations",[64,80,81],{},"Customer questions yang berulang",[64,83,84],{},"Error, incident, atau lesson learned dari project",[64,86,87],{},"Paper, benchmark, framework, atau library baru",[64,89,90],{},"Competitor content dan product updates",[11,92,93],{},"Semua itu bisa jadi konten.",[11,95,96],{},"Tapi tanpa sistem, yang terjadi biasanya salah satu dari tiga pola ini:",[98,99,100,106,112],"ol",{},[64,101,102,105],{},[15,103,104],{},"Ide bagus lewat begitu saja"," karena tidak dicatat.",[64,107,108,111],{},[15,109,110],{},"Ide dicatat tapi tidak diprioritaskan",", akhirnya jadi graveyard di Notion\u002FTrello\u002FGoogle Sheet.",[64,113,114,117],{},[15,115,116],{},"Ide yang sama muncul berulang",", sementara ide baru tenggelam.",[11,119,120],{},"Manual curation juga punya masalah lain: mood. Hari ini semangat, besok sibuk, lusa lupa. Konten tidak boleh bergantung pada mood. Kalau sistem produksi konten adalah bagian dari growth engine, dia perlu rhythm, metrics, dan feedback loop.",[11,122,123],{},"Jadi pipeline yang ingin kita bangun punya tujuan jelas:",[61,125,126,129,132,135,138,141,144,147],{},[64,127,128],{},"Mengumpulkan ide dari banyak sumber secara otomatis",[64,130,131],{},"Menyimpan semua kandidat di database",[64,133,134],{},"Memberi score berdasarkan relevansi, novelty, engagement potential, dan feasibility",[64,136,137],{},"Menampilkan top 10 setiap pagi",[64,139,140],{},"Membiarkan manusia memilih yang paling cocok",[64,142,143],{},"Mendelegasikan produksi ke bot\u002Fblog manager yang sesuai",[64,145,146],{},"Menurunkan score item yang sudah pernah tampil",[64,148,149],{},"Menjaga suggestion tetap fresh dan tidak repetitive",[11,151,152],{},"Ini bukan magic. Ini cuma good systems engineering applied to content.",[53,154,156],{"id":155},"high-level-architecture","High-Level Architecture",[11,158,159],{},"Secara konsep, pipeline ini terdiri dari delapan komponen:",[98,161,162,168,174,180,186,192,198,204],{},[64,163,164,167],{},[15,165,166],{},"Source connectors"," — scraper untuk social media, RSS, website, GitHub, newsletter, dan internal notes.",[64,169,170,173],{},[15,171,172],{},"Normalizer"," — mengubah berbagai format input menjadi satu schema yang konsisten.",[64,175,176,179],{},[15,177,178],{},"Idea database"," — tempat semua content candidates disimpan.",[64,181,182,185],{},[15,183,184],{},"Enrichment agent"," — menambahkan summary, tags, entity, dan context.",[64,187,188,191],{},[15,189,190],{},"Scoring engine"," — menghitung prioritas tiap ide.",[64,193,194,197],{},[15,195,196],{},"Morning briefing"," — menyusun top 10 setiap pagi.",[64,199,200,203],{},[15,201,202],{},"Human curation UI"," — owner memilih ide yang applicable.",[64,205,206,209],{},[15,207,208],{},"Production delegation"," — mengirim job ke bot yang mengelola blog\u002Fchannel tertentu.",[11,211,212],{},"Diagram sederhananya begini:",[214,215,220],"pre",{"className":216,"code":217,"language":218,"meta":219,"style":219},"language-mermaid shiki shiki-themes github-light github-dark","flowchart LR\n    A[Social Media Scrapers] --> D[Normalizer]\n    B[Website and RSS Scrapers] --> D\n    C[GitHub and Internal Notes] --> D\n    D --> E[(Idea Database)]\n    E --> F[AI Enrichment Agent]\n    F --> G[Scoring Engine]\n    G --> H[Morning Top 10 Briefing]\n    H --> I[Human Curation]\n    I --> J[Content Production Bots]\n    J --> K[Blog, GitHub, Newsletter, Social]\n    H --> L[Score Decay]\n    L --> E\n","mermaid","",[221,222,223,231,237,243,249,255,261,267,273,279,285,291,297],"code",{"__ignoreMap":219},[224,225,228],"span",{"class":226,"line":227},"line",1,[224,229,230],{},"flowchart LR\n",[224,232,234],{"class":226,"line":233},2,[224,235,236],{},"    A[Social Media Scrapers] --> D[Normalizer]\n",[224,238,240],{"class":226,"line":239},3,[224,241,242],{},"    B[Website and RSS Scrapers] --> D\n",[224,244,246],{"class":226,"line":245},4,[224,247,248],{},"    C[GitHub and Internal Notes] --> D\n",[224,250,252],{"class":226,"line":251},5,[224,253,254],{},"    D --> E[(Idea Database)]\n",[224,256,258],{"class":226,"line":257},6,[224,259,260],{},"    E --> F[AI Enrichment Agent]\n",[224,262,264],{"class":226,"line":263},7,[224,265,266],{},"    F --> G[Scoring Engine]\n",[224,268,270],{"class":226,"line":269},8,[224,271,272],{},"    G --> H[Morning Top 10 Briefing]\n",[224,274,276],{"class":226,"line":275},9,[224,277,278],{},"    H --> I[Human Curation]\n",[224,280,282],{"class":226,"line":281},10,[224,283,284],{},"    I --> J[Content Production Bots]\n",[224,286,288],{"class":226,"line":287},11,[224,289,290],{},"    J --> K[Blog, GitHub, Newsletter, Social]\n",[224,292,294],{"class":226,"line":293},12,[224,295,296],{},"    H --> L[Score Decay]\n",[224,298,300],{"class":226,"line":299},13,[224,301,302],{},"    L --> E\n",[11,304,305],{},"Kalau mau dibuat lebih production-ready, pisahkan antara ingestion, scoring, dan presentation. Jangan satu script raksasa yang melakukan semuanya. Itu cepat di awal, tapi susah dirawat.",[11,307,308],{},"Saya biasanya membagi sistem seperti ini:",[214,310,312],{"className":216,"code":311,"language":218,"meta":219,"style":219},"flowchart TB\n    subgraph Ingestion\n        S1[X Scraper]\n        S2[LinkedIn Scraper]\n        S3[RSS Reader]\n        S4[GitHub Scanner]\n        S5[Internal Notes]\n    end\n\n    subgraph Processing\n        N[Normalize]\n        E[Deduplicate]\n        A[AI Enrich]\n        Q[Quality Filter]\n    end\n\n    subgraph Storage\n        DB[(Postgres Ideas DB)]\n        V[(Vector Index)]\n    end\n\n    subgraph Decision\n        SC[Scoring Engine]\n        DEC[Score Decay]\n        BR[Morning Briefing]\n    end\n\n    subgraph Execution\n        HC[Human Picks]\n        BOT[Managing Bots]\n        PUB[Publish Channels]\n    end\n\n    S1 --> N\n    S2 --> N\n    S3 --> N\n    S4 --> N\n    S5 --> N\n    N --> E --> A --> Q --> DB\n    A --> V\n    DB --> SC --> BR --> HC --> BOT --> PUB\n    BR --> DEC --> DB\n",[221,313,314,319,324,329,334,339,344,349,354,360,365,370,375,380,386,391,396,402,408,414,419,424,430,436,442,448,453,458,464,470,476,482,487,492,498,504,510,516,522,528,534,540],{"__ignoreMap":219},[224,315,316],{"class":226,"line":227},[224,317,318],{},"flowchart TB\n",[224,320,321],{"class":226,"line":233},[224,322,323],{},"    subgraph Ingestion\n",[224,325,326],{"class":226,"line":239},[224,327,328],{},"        S1[X Scraper]\n",[224,330,331],{"class":226,"line":245},[224,332,333],{},"        S2[LinkedIn Scraper]\n",[224,335,336],{"class":226,"line":251},[224,337,338],{},"        S3[RSS Reader]\n",[224,340,341],{"class":226,"line":257},[224,342,343],{},"        S4[GitHub Scanner]\n",[224,345,346],{"class":226,"line":263},[224,347,348],{},"        S5[Internal Notes]\n",[224,350,351],{"class":226,"line":269},[224,352,353],{},"    end\n",[224,355,356],{"class":226,"line":275},[224,357,359],{"emptyLinePlaceholder":358},true,"\n",[224,361,362],{"class":226,"line":281},[224,363,364],{},"    subgraph Processing\n",[224,366,367],{"class":226,"line":287},[224,368,369],{},"        N[Normalize]\n",[224,371,372],{"class":226,"line":293},[224,373,374],{},"        E[Deduplicate]\n",[224,376,377],{"class":226,"line":299},[224,378,379],{},"        A[AI Enrich]\n",[224,381,383],{"class":226,"line":382},14,[224,384,385],{},"        Q[Quality Filter]\n",[224,387,389],{"class":226,"line":388},15,[224,390,353],{},[224,392,394],{"class":226,"line":393},16,[224,395,359],{"emptyLinePlaceholder":358},[224,397,399],{"class":226,"line":398},17,[224,400,401],{},"    subgraph Storage\n",[224,403,405],{"class":226,"line":404},18,[224,406,407],{},"        DB[(Postgres Ideas DB)]\n",[224,409,411],{"class":226,"line":410},19,[224,412,413],{},"        V[(Vector Index)]\n",[224,415,417],{"class":226,"line":416},20,[224,418,353],{},[224,420,422],{"class":226,"line":421},21,[224,423,359],{"emptyLinePlaceholder":358},[224,425,427],{"class":226,"line":426},22,[224,428,429],{},"    subgraph Decision\n",[224,431,433],{"class":226,"line":432},23,[224,434,435],{},"        SC[Scoring Engine]\n",[224,437,439],{"class":226,"line":438},24,[224,440,441],{},"        DEC[Score Decay]\n",[224,443,445],{"class":226,"line":444},25,[224,446,447],{},"        BR[Morning Briefing]\n",[224,449,451],{"class":226,"line":450},26,[224,452,353],{},[224,454,456],{"class":226,"line":455},27,[224,457,359],{"emptyLinePlaceholder":358},[224,459,461],{"class":226,"line":460},28,[224,462,463],{},"    subgraph Execution\n",[224,465,467],{"class":226,"line":466},29,[224,468,469],{},"        HC[Human Picks]\n",[224,471,473],{"class":226,"line":472},30,[224,474,475],{},"        BOT[Managing Bots]\n",[224,477,479],{"class":226,"line":478},31,[224,480,481],{},"        PUB[Publish Channels]\n",[224,483,485],{"class":226,"line":484},32,[224,486,353],{},[224,488,490],{"class":226,"line":489},33,[224,491,359],{"emptyLinePlaceholder":358},[224,493,495],{"class":226,"line":494},34,[224,496,497],{},"    S1 --> N\n",[224,499,501],{"class":226,"line":500},35,[224,502,503],{},"    S2 --> N\n",[224,505,507],{"class":226,"line":506},36,[224,508,509],{},"    S3 --> N\n",[224,511,513],{"class":226,"line":512},37,[224,514,515],{},"    S4 --> N\n",[224,517,519],{"class":226,"line":518},38,[224,520,521],{},"    S5 --> N\n",[224,523,525],{"class":226,"line":524},39,[224,526,527],{},"    N --> E --> A --> Q --> DB\n",[224,529,531],{"class":226,"line":530},40,[224,532,533],{},"    A --> V\n",[224,535,537],{"class":226,"line":536},41,[224,538,539],{},"    DB --> SC --> BR --> HC --> BOT --> PUB\n",[224,541,543],{"class":226,"line":542},42,[224,544,545],{},"    BR --> DEC --> DB\n",[11,547,548],{},"Kenapa pakai vector index? Karena similarity search penting untuk deduplication dan novelty scoring. Banyak ide yang beda wording tapi sama substansi. Misalnya:",[61,550,551,554,557],{},[64,552,553],{},"“How AI agents automate newsletters”",[64,555,556],{},"“Building autonomous content curation agents”",[64,558,559],{},"“AI workflow for daily content ideas”",[11,561,562],{},"Secara literal berbeda. Secara semantic, mungkin satu cluster. Kalau tidak ditangani, morning briefing akan terasa repetitive.",[53,564,566],{"id":565},"data-model-jangan-mulai-dari-prompt-mulai-dari-schema","Data Model: Jangan Mulai dari Prompt, Mulai dari Schema",[11,568,569],{},"Kesalahan umum dalam AI automation: orang langsung mulai dari prompt. Padahal sistem yang stabil mulai dari data model.",[11,571,572],{},"Untuk content pipeline, minimal kita butuh schema seperti ini:",[214,574,578],{"className":575,"code":576,"language":577,"meta":219,"style":219},"language-sql shiki shiki-themes github-light github-dark","CREATE TABLE content_ideas (\n    id UUID PRIMARY KEY,\n    source_type TEXT NOT NULL,\n    source_url TEXT,\n    source_author TEXT,\n    raw_text TEXT NOT NULL,\n    normalized_title TEXT,\n    summary TEXT,\n    tags TEXT[],\n    entities TEXT[],\n    language TEXT,\n    detected_topic TEXT,\n    relevance_score NUMERIC DEFAULT 0,\n    engagement_score NUMERIC DEFAULT 0,\n    novelty_score NUMERIC DEFAULT 0,\n    feasibility_score NUMERIC DEFAULT 0,\n    freshness_score NUMERIC DEFAULT 0,\n    final_score NUMERIC DEFAULT 0,\n    presented_count INTEGER DEFAULT 0,\n    selected_count INTEGER DEFAULT 0,\n    rejected_count INTEGER DEFAULT 0,\n    status TEXT DEFAULT 'candidate',\n    created_at TIMESTAMPTZ DEFAULT now(),\n    last_seen_at TIMESTAMPTZ DEFAULT now(),\n    last_presented_at TIMESTAMPTZ,\n    selected_at TIMESTAMPTZ,\n    decay_factor NUMERIC DEFAULT 1.0\n);\n","sql",[221,579,580,585,590,595,600,605,610,615,620,625,630,635,640,645,650,655,660,665,670,675,680,685,690,695,700,705,710,715],{"__ignoreMap":219},[224,581,582],{"class":226,"line":227},[224,583,584],{},"CREATE TABLE content_ideas (\n",[224,586,587],{"class":226,"line":233},[224,588,589],{},"    id UUID PRIMARY KEY,\n",[224,591,592],{"class":226,"line":239},[224,593,594],{},"    source_type TEXT NOT NULL,\n",[224,596,597],{"class":226,"line":245},[224,598,599],{},"    source_url TEXT,\n",[224,601,602],{"class":226,"line":251},[224,603,604],{},"    source_author TEXT,\n",[224,606,607],{"class":226,"line":257},[224,608,609],{},"    raw_text TEXT NOT NULL,\n",[224,611,612],{"class":226,"line":263},[224,613,614],{},"    normalized_title TEXT,\n",[224,616,617],{"class":226,"line":269},[224,618,619],{},"    summary TEXT,\n",[224,621,622],{"class":226,"line":275},[224,623,624],{},"    tags TEXT[],\n",[224,626,627],{"class":226,"line":281},[224,628,629],{},"    entities TEXT[],\n",[224,631,632],{"class":226,"line":287},[224,633,634],{},"    language TEXT,\n",[224,636,637],{"class":226,"line":293},[224,638,639],{},"    detected_topic TEXT,\n",[224,641,642],{"class":226,"line":299},[224,643,644],{},"    relevance_score NUMERIC DEFAULT 0,\n",[224,646,647],{"class":226,"line":382},[224,648,649],{},"    engagement_score NUMERIC DEFAULT 0,\n",[224,651,652],{"class":226,"line":388},[224,653,654],{},"    novelty_score NUMERIC DEFAULT 0,\n",[224,656,657],{"class":226,"line":393},[224,658,659],{},"    feasibility_score NUMERIC DEFAULT 0,\n",[224,661,662],{"class":226,"line":398},[224,663,664],{},"    freshness_score NUMERIC DEFAULT 0,\n",[224,666,667],{"class":226,"line":404},[224,668,669],{},"    final_score NUMERIC DEFAULT 0,\n",[224,671,672],{"class":226,"line":410},[224,673,674],{},"    presented_count INTEGER DEFAULT 0,\n",[224,676,677],{"class":226,"line":416},[224,678,679],{},"    selected_count INTEGER DEFAULT 0,\n",[224,681,682],{"class":226,"line":421},[224,683,684],{},"    rejected_count INTEGER DEFAULT 0,\n",[224,686,687],{"class":226,"line":426},[224,688,689],{},"    status TEXT DEFAULT 'candidate',\n",[224,691,692],{"class":226,"line":432},[224,693,694],{},"    created_at TIMESTAMPTZ DEFAULT now(),\n",[224,696,697],{"class":226,"line":438},[224,698,699],{},"    last_seen_at TIMESTAMPTZ DEFAULT now(),\n",[224,701,702],{"class":226,"line":444},[224,703,704],{},"    last_presented_at TIMESTAMPTZ,\n",[224,706,707],{"class":226,"line":450},[224,708,709],{},"    selected_at TIMESTAMPTZ,\n",[224,711,712],{"class":226,"line":455},[224,713,714],{},"    decay_factor NUMERIC DEFAULT 1.0\n",[224,716,717],{"class":226,"line":460},[224,718,719],{},");\n",[11,721,722],{},"Field pentingnya bukan cuma title dan URL. Yang bikin pipeline pintar adalah metadata:",[61,724,725,731,737,743,749,755],{},[64,726,727,730],{},[15,728,729],{},"presented_count"," untuk tahu item sudah berapa kali muncul",[64,732,733,736],{},[15,734,735],{},"selected_count"," untuk learning dari preferensi manusia",[64,738,739,742],{},[15,740,741],{},"rejected_count"," untuk menurunkan item yang sering ditolak",[64,744,745,748],{},[15,746,747],{},"decay_factor"," untuk mengurangi score setelah tampil",[64,750,751,754],{},[15,752,753],{},"novelty_score"," untuk menghindari topik yang sudah terlalu sering muncul",[64,756,757,760],{},[15,758,759],{},"feasibility_score"," untuk membedakan ide menarik vs ide yang bisa dieksekusi",[11,762,763,764,767,768,771],{},"Kalau Anda membangun ini untuk banyak blog atau brand, tambahkan table ",[221,765,766],{},"channels"," dan ",[221,769,770],{},"channel_fit_score",". Satu ide bisa cocok untuk blog engineering, tapi tidak cocok untuk LinkedIn executive update. Satu ide bisa bagus untuk GitHub tutorial, tapi terlalu teknis untuk newsletter umum.",[11,773,774],{},"Contoh tambahan:",[214,776,778],{"className":575,"code":777,"language":577,"meta":219,"style":219},"CREATE TABLE content_channels (\n    id UUID PRIMARY KEY,\n    name TEXT NOT NULL,\n    type TEXT NOT NULL,\n    audience TEXT,\n    tone TEXT,\n    owner_bot TEXT,\n    publishing_rules JSONB\n);\n\nCREATE TABLE idea_channel_scores (\n    idea_id UUID REFERENCES content_ideas(id),\n    channel_id UUID REFERENCES content_channels(id),\n    fit_score NUMERIC NOT NULL,\n    reason TEXT,\n    PRIMARY KEY (idea_id, channel_id)\n);\n",[221,779,780,785,789,794,799,804,809,814,819,823,827,832,837,842,847,852,857],{"__ignoreMap":219},[224,781,782],{"class":226,"line":227},[224,783,784],{},"CREATE TABLE content_channels (\n",[224,786,787],{"class":226,"line":233},[224,788,589],{},[224,790,791],{"class":226,"line":239},[224,792,793],{},"    name TEXT NOT NULL,\n",[224,795,796],{"class":226,"line":245},[224,797,798],{},"    type TEXT NOT NULL,\n",[224,800,801],{"class":226,"line":251},[224,802,803],{},"    audience TEXT,\n",[224,805,806],{"class":226,"line":257},[224,807,808],{},"    tone TEXT,\n",[224,810,811],{"class":226,"line":263},[224,812,813],{},"    owner_bot TEXT,\n",[224,815,816],{"class":226,"line":269},[224,817,818],{},"    publishing_rules JSONB\n",[224,820,821],{"class":226,"line":275},[224,822,719],{},[224,824,825],{"class":226,"line":281},[224,826,359],{"emptyLinePlaceholder":358},[224,828,829],{"class":226,"line":287},[224,830,831],{},"CREATE TABLE idea_channel_scores (\n",[224,833,834],{"class":226,"line":293},[224,835,836],{},"    idea_id UUID REFERENCES content_ideas(id),\n",[224,838,839],{"class":226,"line":299},[224,840,841],{},"    channel_id UUID REFERENCES content_channels(id),\n",[224,843,844],{"class":226,"line":382},[224,845,846],{},"    fit_score NUMERIC NOT NULL,\n",[224,848,849],{"class":226,"line":388},[224,850,851],{},"    reason TEXT,\n",[224,853,854],{"class":226,"line":393},[224,855,856],{},"    PRIMARY KEY (idea_id, channel_id)\n",[224,858,859],{"class":226,"line":398},[224,860,719],{},[11,862,863],{},"Dengan ini, morning briefing bisa bilang bukan cuma “ini ide bagus”, tapi juga “ini cocok untuk blog A, GitHub repo B, dan LinkedIn thread C”.",[53,865,867],{"id":866},"phase-1-scraping-multi-source","Phase 1: Scraping Multi-Source",[11,869,870],{},"Scraping jangan dipahami sebagai “ambil semua yang bisa diambil”. Itu barbar. Better approach: buat connectors yang purpose-driven.",[11,872,873],{},"Sumber bisa dibagi menjadi beberapa kategori:",[875,876,878],"h3",{"id":877},"_1-social-signals","1. Social Signals",[11,880,881],{},"Social media memberi sinyal engagement dan trend velocity. Dari sini kita bisa mengambil:",[61,883,884,887,890,893,896],{},[64,885,886],{},"Post dengan likes\u002Fcomments tinggi",[64,888,889],{},"Thread yang viral di niche tertentu",[64,891,892],{},"Pertanyaan yang sering muncul",[64,894,895],{},"Opinion yang memicu diskusi",[64,897,898],{},"Pain point yang disebut berulang",[11,900,901],{},"Yang perlu disimpan bukan cuma text. Simpan juga engagement metadata.",[214,903,907],{"className":904,"code":905,"language":906,"meta":219,"style":219},"language-python shiki shiki-themes github-light github-dark","@dataclass\nclass SocialItem:\n    source: str\n    url: str\n    author: str\n    text: str\n    likes: int\n    comments: int\n    shares: int\n    published_at: datetime\n    scraped_at: datetime\n","python",[221,908,909,914,919,924,929,934,939,944,949,954,959],{"__ignoreMap":219},[224,910,911],{"class":226,"line":227},[224,912,913],{},"@dataclass\n",[224,915,916],{"class":226,"line":233},[224,917,918],{},"class SocialItem:\n",[224,920,921],{"class":226,"line":239},[224,922,923],{},"    source: str\n",[224,925,926],{"class":226,"line":245},[224,927,928],{},"    url: str\n",[224,930,931],{"class":226,"line":251},[224,932,933],{},"    author: str\n",[224,935,936],{"class":226,"line":257},[224,937,938],{},"    text: str\n",[224,940,941],{"class":226,"line":263},[224,942,943],{},"    likes: int\n",[224,945,946],{"class":226,"line":269},[224,947,948],{},"    comments: int\n",[224,950,951],{"class":226,"line":275},[224,952,953],{},"    shares: int\n",[224,955,956],{"class":226,"line":281},[224,957,958],{},"    published_at: datetime\n",[224,960,961],{"class":226,"line":287},[224,962,963],{},"    scraped_at: datetime\n",[875,965,967],{"id":966},"_2-website-and-rss","2. Website and RSS",[11,969,970],{},"RSS masih underrated. Untuk blog engineering, RSS adalah sumber yang relatif bersih karena sudah berupa artikel. Kita bisa ambil:",[61,972,973,976,979,982,985,988],{},[64,974,975],{},"Title",[64,977,978],{},"Summary",[64,980,981],{},"URL",[64,983,984],{},"Published date",[64,986,987],{},"Author",[64,989,990],{},"Tags",[11,992,993],{},"Pseudocode:",[214,995,997],{"className":904,"code":996,"language":906,"meta":219,"style":219},"import feedparser\nfrom datetime import datetime\n\n\ndef scrape_rss(feed_url: str) -> list[dict]:\n    feed = feedparser.parse(feed_url)\n    items = []\n\n    for entry in feed.entries:\n        items.append({\n            \"source_type\": \"rss\",\n            \"source_url\": entry.link,\n            \"source_author\": getattr(entry, \"author\", None),\n            \"raw_text\": f\"{entry.title}\\n\\n{getattr(entry, 'summary', '')}\",\n            \"published_at\": getattr(entry, \"published\", None),\n            \"scraped_at\": datetime.utcnow().isoformat(),\n        })\n\n    return items\n",[221,998,999,1004,1009,1013,1017,1022,1027,1032,1036,1041,1046,1051,1056,1061,1066,1071,1076,1081,1085],{"__ignoreMap":219},[224,1000,1001],{"class":226,"line":227},[224,1002,1003],{},"import feedparser\n",[224,1005,1006],{"class":226,"line":233},[224,1007,1008],{},"from datetime import datetime\n",[224,1010,1011],{"class":226,"line":239},[224,1012,359],{"emptyLinePlaceholder":358},[224,1014,1015],{"class":226,"line":245},[224,1016,359],{"emptyLinePlaceholder":358},[224,1018,1019],{"class":226,"line":251},[224,1020,1021],{},"def scrape_rss(feed_url: str) -> list[dict]:\n",[224,1023,1024],{"class":226,"line":257},[224,1025,1026],{},"    feed = feedparser.parse(feed_url)\n",[224,1028,1029],{"class":226,"line":263},[224,1030,1031],{},"    items = []\n",[224,1033,1034],{"class":226,"line":269},[224,1035,359],{"emptyLinePlaceholder":358},[224,1037,1038],{"class":226,"line":275},[224,1039,1040],{},"    for entry in feed.entries:\n",[224,1042,1043],{"class":226,"line":281},[224,1044,1045],{},"        items.append({\n",[224,1047,1048],{"class":226,"line":287},[224,1049,1050],{},"            \"source_type\": \"rss\",\n",[224,1052,1053],{"class":226,"line":293},[224,1054,1055],{},"            \"source_url\": entry.link,\n",[224,1057,1058],{"class":226,"line":299},[224,1059,1060],{},"            \"source_author\": getattr(entry, \"author\", None),\n",[224,1062,1063],{"class":226,"line":382},[224,1064,1065],{},"            \"raw_text\": f\"{entry.title}\\n\\n{getattr(entry, 'summary', '')}\",\n",[224,1067,1068],{"class":226,"line":388},[224,1069,1070],{},"            \"published_at\": getattr(entry, \"published\", None),\n",[224,1072,1073],{"class":226,"line":393},[224,1074,1075],{},"            \"scraped_at\": datetime.utcnow().isoformat(),\n",[224,1077,1078],{"class":226,"line":398},[224,1079,1080],{},"        })\n",[224,1082,1083],{"class":226,"line":404},[224,1084,359],{"emptyLinePlaceholder":358},[224,1086,1087],{"class":226,"line":410},[224,1088,1089],{},"    return items\n",[875,1091,1093],{"id":1092},"_3-github-signals","3. GitHub Signals",[11,1095,1096],{},"Untuk audience developer, GitHub adalah content goldmine. Tapi jangan cuma ambil trending repo. Ambil juga:",[61,1098,1099,1102,1105,1108,1111],{},[64,1100,1101],{},"New releases dari projects yang relevan",[64,1103,1104],{},"Issues yang banyak comment",[64,1106,1107],{},"PR yang memperkenalkan pattern baru",[64,1109,1110],{},"README yang menjelaskan architecture menarik",[64,1112,1113],{},"Stars velocity",[11,1115,1116],{},"Contoh connector:",[214,1118,1120],{"className":904,"code":1119,"language":906,"meta":219,"style":219},"import requests\n\n\ndef search_github_repos(query: str, token: str) -> list[dict]:\n    response = requests.get(\n        \"https:\u002F\u002Fapi.github.com\u002Fsearch\u002Frepositories\",\n        headers={\"Authorization\": f\"Bearer {token}\"},\n        params={\n            \"q\": query,\n            \"sort\": \"stars\",\n            \"order\": \"desc\",\n            \"per_page\": 20,\n        },\n        timeout=20,\n    )\n    response.raise_for_status()\n\n    ideas = []\n    for repo in response.json()[\"items\"]:\n        ideas.append({\n            \"source_type\": \"github\",\n            \"source_url\": repo[\"html_url\"],\n            \"source_author\": repo[\"owner\"][\"login\"],\n            \"raw_text\": f\"{repo['full_name']}\\n{repo.get('description') or ''}\",\n            \"metadata\": {\n                \"stars\": repo[\"stargazers_count\"],\n                \"forks\": repo[\"forks_count\"],\n                \"language\": repo[\"language\"],\n                \"updated_at\": repo[\"updated_at\"],\n            },\n        })\n    return ideas\n",[221,1121,1122,1127,1131,1135,1140,1145,1150,1155,1160,1165,1170,1175,1180,1185,1190,1195,1200,1204,1209,1214,1219,1224,1229,1234,1239,1244,1249,1254,1259,1264,1269,1273],{"__ignoreMap":219},[224,1123,1124],{"class":226,"line":227},[224,1125,1126],{},"import requests\n",[224,1128,1129],{"class":226,"line":233},[224,1130,359],{"emptyLinePlaceholder":358},[224,1132,1133],{"class":226,"line":239},[224,1134,359],{"emptyLinePlaceholder":358},[224,1136,1137],{"class":226,"line":245},[224,1138,1139],{},"def search_github_repos(query: str, token: str) -> list[dict]:\n",[224,1141,1142],{"class":226,"line":251},[224,1143,1144],{},"    response = requests.get(\n",[224,1146,1147],{"class":226,"line":257},[224,1148,1149],{},"        \"https:\u002F\u002Fapi.github.com\u002Fsearch\u002Frepositories\",\n",[224,1151,1152],{"class":226,"line":263},[224,1153,1154],{},"        headers={\"Authorization\": f\"Bearer {token}\"},\n",[224,1156,1157],{"class":226,"line":269},[224,1158,1159],{},"        params={\n",[224,1161,1162],{"class":226,"line":275},[224,1163,1164],{},"            \"q\": query,\n",[224,1166,1167],{"class":226,"line":281},[224,1168,1169],{},"            \"sort\": \"stars\",\n",[224,1171,1172],{"class":226,"line":287},[224,1173,1174],{},"            \"order\": \"desc\",\n",[224,1176,1177],{"class":226,"line":293},[224,1178,1179],{},"            \"per_page\": 20,\n",[224,1181,1182],{"class":226,"line":299},[224,1183,1184],{},"        },\n",[224,1186,1187],{"class":226,"line":382},[224,1188,1189],{},"        timeout=20,\n",[224,1191,1192],{"class":226,"line":388},[224,1193,1194],{},"    )\n",[224,1196,1197],{"class":226,"line":393},[224,1198,1199],{},"    response.raise_for_status()\n",[224,1201,1202],{"class":226,"line":398},[224,1203,359],{"emptyLinePlaceholder":358},[224,1205,1206],{"class":226,"line":404},[224,1207,1208],{},"    ideas = []\n",[224,1210,1211],{"class":226,"line":410},[224,1212,1213],{},"    for repo in response.json()[\"items\"]:\n",[224,1215,1216],{"class":226,"line":416},[224,1217,1218],{},"        ideas.append({\n",[224,1220,1221],{"class":226,"line":421},[224,1222,1223],{},"            \"source_type\": \"github\",\n",[224,1225,1226],{"class":226,"line":426},[224,1227,1228],{},"            \"source_url\": repo[\"html_url\"],\n",[224,1230,1231],{"class":226,"line":432},[224,1232,1233],{},"            \"source_author\": repo[\"owner\"][\"login\"],\n",[224,1235,1236],{"class":226,"line":438},[224,1237,1238],{},"            \"raw_text\": f\"{repo['full_name']}\\n{repo.get('description') or ''}\",\n",[224,1240,1241],{"class":226,"line":444},[224,1242,1243],{},"            \"metadata\": {\n",[224,1245,1246],{"class":226,"line":450},[224,1247,1248],{},"                \"stars\": repo[\"stargazers_count\"],\n",[224,1250,1251],{"class":226,"line":455},[224,1252,1253],{},"                \"forks\": repo[\"forks_count\"],\n",[224,1255,1256],{"class":226,"line":460},[224,1257,1258],{},"                \"language\": repo[\"language\"],\n",[224,1260,1261],{"class":226,"line":466},[224,1262,1263],{},"                \"updated_at\": repo[\"updated_at\"],\n",[224,1265,1266],{"class":226,"line":472},[224,1267,1268],{},"            },\n",[224,1270,1271],{"class":226,"line":478},[224,1272,1080],{},[224,1274,1275],{"class":226,"line":484},[224,1276,1277],{},"    return ideas\n",[875,1279,1281],{"id":1280},"_4-internal-notes-and-conversations","4. Internal Notes and Conversations",[11,1283,1284],{},"Ini sering paling valuable. Banyak content bagus lahir dari pertanyaan real customer atau problem internal. Kalau ada WhatsApp\u002FTelegram\u002FSlack summary, meeting notes, atau incident report, semuanya bisa masuk pipeline.",[11,1286,1287],{},"Tapi hati-hati: privacy dan confidentiality. Jangan asal masukkan data sensitif ke pipeline publik. Saya biasanya pakai rule:",[61,1289,1290,1293,1296,1299],{},[64,1291,1292],{},"Strip nama orang\u002Fperusahaan jika tidak perlu",[64,1294,1295],{},"Mask phone\u002Femail\u002FAPI keys",[64,1297,1298],{},"Flag internal-only ideas",[64,1300,1301],{},"Human approval wajib sebelum publish",[11,1303,1304,1305,1308],{},"Architecture-wise, internal notes bisa masuk sebagai source type ",[221,1306,1307],{},"internal_note",", lalu diberi constraint lebih ketat.",[53,1310,1312],{"id":1311},"phase-2-normalization-and-deduplication","Phase 2: Normalization and Deduplication",[11,1314,1315,1316,18],{},"Setelah scraping, semua source punya format berbeda. Normalizer bertugas membuat semuanya menjadi bentuk ",[221,1317,1318],{},"content_idea",[11,1320,1321],{},"Normalization melakukan beberapa hal:",[61,1323,1324,1327,1330,1333,1336,1339],{},[64,1325,1326],{},"Clean whitespace dan HTML",[64,1328,1329],{},"Remove tracking parameters dari URL",[64,1331,1332],{},"Extract title jika memungkinkan",[64,1334,1335],{},"Detect language",[64,1337,1338],{},"Generate short summary",[64,1340,1341],{},"Assign preliminary tags",[11,1343,993],{},[214,1345,1347],{"className":904,"code":1346,"language":906,"meta":219,"style":219},"from urllib.parse import urlparse, urlunparse, parse_qsl, urlencode\n\nTRACKING_PARAMS = {\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"}\n\n\ndef clean_url(url: str) -> str:\n    parsed = urlparse(url)\n    query = [(k, v) for k, v in parse_qsl(parsed.query) if k not in TRACKING_PARAMS]\n    return urlunparse(parsed._replace(query=urlencode(query)))\n\n\ndef normalize_item(item: dict) -> dict:\n    raw_text = \" \".join(item[\"raw_text\"].split())\n\n    return {\n        \"source_type\": item[\"source_type\"],\n        \"source_url\": clean_url(item.get(\"source_url\", \"\")),\n        \"source_author\": item.get(\"source_author\"),\n        \"raw_text\": raw_text,\n        \"metadata\": item.get(\"metadata\", {}),\n    }\n",[221,1348,1349,1354,1358,1363,1367,1371,1376,1381,1386,1391,1395,1399,1404,1409,1413,1418,1423,1428,1433,1438,1443],{"__ignoreMap":219},[224,1350,1351],{"class":226,"line":227},[224,1352,1353],{},"from urllib.parse import urlparse, urlunparse, parse_qsl, urlencode\n",[224,1355,1356],{"class":226,"line":233},[224,1357,359],{"emptyLinePlaceholder":358},[224,1359,1360],{"class":226,"line":239},[224,1361,1362],{},"TRACKING_PARAMS = {\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_term\", \"utm_content\"}\n",[224,1364,1365],{"class":226,"line":245},[224,1366,359],{"emptyLinePlaceholder":358},[224,1368,1369],{"class":226,"line":251},[224,1370,359],{"emptyLinePlaceholder":358},[224,1372,1373],{"class":226,"line":257},[224,1374,1375],{},"def clean_url(url: str) -> str:\n",[224,1377,1378],{"class":226,"line":263},[224,1379,1380],{},"    parsed = urlparse(url)\n",[224,1382,1383],{"class":226,"line":269},[224,1384,1385],{},"    query = [(k, v) for k, v in parse_qsl(parsed.query) if k not in TRACKING_PARAMS]\n",[224,1387,1388],{"class":226,"line":275},[224,1389,1390],{},"    return urlunparse(parsed._replace(query=urlencode(query)))\n",[224,1392,1393],{"class":226,"line":281},[224,1394,359],{"emptyLinePlaceholder":358},[224,1396,1397],{"class":226,"line":287},[224,1398,359],{"emptyLinePlaceholder":358},[224,1400,1401],{"class":226,"line":293},[224,1402,1403],{},"def normalize_item(item: dict) -> dict:\n",[224,1405,1406],{"class":226,"line":299},[224,1407,1408],{},"    raw_text = \" \".join(item[\"raw_text\"].split())\n",[224,1410,1411],{"class":226,"line":382},[224,1412,359],{"emptyLinePlaceholder":358},[224,1414,1415],{"class":226,"line":388},[224,1416,1417],{},"    return {\n",[224,1419,1420],{"class":226,"line":393},[224,1421,1422],{},"        \"source_type\": item[\"source_type\"],\n",[224,1424,1425],{"class":226,"line":398},[224,1426,1427],{},"        \"source_url\": clean_url(item.get(\"source_url\", \"\")),\n",[224,1429,1430],{"class":226,"line":404},[224,1431,1432],{},"        \"source_author\": item.get(\"source_author\"),\n",[224,1434,1435],{"class":226,"line":410},[224,1436,1437],{},"        \"raw_text\": raw_text,\n",[224,1439,1440],{"class":226,"line":416},[224,1441,1442],{},"        \"metadata\": item.get(\"metadata\", {}),\n",[224,1444,1445],{"class":226,"line":421},[224,1446,1447],{},"    }\n",[11,1449,1450],{},"Deduplication sebaiknya dua lapis:",[98,1452,1453,1459],{},[64,1454,1455,1458],{},[15,1456,1457],{},"Exact dedupe"," berdasarkan canonical URL atau hash text.",[64,1460,1461,1464],{},[15,1462,1463],{},"Semantic dedupe"," berdasarkan embedding similarity.",[11,1466,1467],{},"Exact dedupe gampang:",[214,1469,1471],{"className":904,"code":1470,"language":906,"meta":219,"style":219},"import hashlib\n\n\ndef content_hash(text: str) -> str:\n    normalized = \" \".join(text.lower().split())\n    return hashlib.sha256(normalized.encode()).hexdigest()\n",[221,1472,1473,1478,1482,1486,1491,1496],{"__ignoreMap":219},[224,1474,1475],{"class":226,"line":227},[224,1476,1477],{},"import hashlib\n",[224,1479,1480],{"class":226,"line":233},[224,1481,359],{"emptyLinePlaceholder":358},[224,1483,1484],{"class":226,"line":239},[224,1485,359],{"emptyLinePlaceholder":358},[224,1487,1488],{"class":226,"line":245},[224,1489,1490],{},"def content_hash(text: str) -> str:\n",[224,1492,1493],{"class":226,"line":251},[224,1494,1495],{},"    normalized = \" \".join(text.lower().split())\n",[224,1497,1498],{"class":226,"line":257},[224,1499,1500],{},"    return hashlib.sha256(normalized.encode()).hexdigest()\n",[11,1502,1503],{},"Semantic dedupe lebih menarik. Kita generate embedding untuk summary\u002Fraw text, lalu cek apakah sudah ada item mirip di vector index.",[214,1505,1507],{"className":904,"code":1506,"language":906,"meta":219,"style":219},"\ndef is_semantic_duplicate(embedding, vector_index, threshold=0.88) -> bool:\n    nearest = vector_index.search(embedding, top_k=3)\n    return any(match.score >= threshold for match in nearest)\n",[221,1508,1509,1513,1518,1523],{"__ignoreMap":219},[224,1510,1511],{"class":226,"line":227},[224,1512,359],{"emptyLinePlaceholder":358},[224,1514,1515],{"class":226,"line":233},[224,1516,1517],{},"def is_semantic_duplicate(embedding, vector_index, threshold=0.88) -> bool:\n",[224,1519,1520],{"class":226,"line":239},[224,1521,1522],{},"    nearest = vector_index.search(embedding, top_k=3)\n",[224,1524,1525],{"class":226,"line":245},[224,1526,1527],{},"    return any(match.score >= threshold for match in nearest)\n",[11,1529,1530,1531,1534],{},"Kalau duplicate, jangan langsung buang. Update ",[221,1532,1533],{},"last_seen_at"," dan mungkin tambah source baru. Ide yang muncul dari banyak sumber bisa berarti sinyalnya kuat.",[214,1536,1538],{"className":904,"code":1537,"language":906,"meta":219,"style":219},"\ndef upsert_idea(candidate):\n    existing = find_by_url_or_hash(candidate)\n    if existing:\n        existing.last_seen_at = now()\n        existing.metadata[\"source_count\"] = existing.metadata.get(\"source_count\", 1) + 1\n        save(existing)\n        return existing\n\n    similar = find_semantic_match(candidate)\n    if similar:\n        similar.last_seen_at = now()\n        similar.metadata.setdefault(\"related_sources\", []).append(candidate[\"source_url\"])\n        save(similar)\n        return similar\n\n    return insert_new(candidate)\n",[221,1539,1540,1544,1549,1554,1559,1564,1569,1574,1579,1583,1588,1593,1598,1603,1608,1613,1617],{"__ignoreMap":219},[224,1541,1542],{"class":226,"line":227},[224,1543,359],{"emptyLinePlaceholder":358},[224,1545,1546],{"class":226,"line":233},[224,1547,1548],{},"def upsert_idea(candidate):\n",[224,1550,1551],{"class":226,"line":239},[224,1552,1553],{},"    existing = find_by_url_or_hash(candidate)\n",[224,1555,1556],{"class":226,"line":245},[224,1557,1558],{},"    if existing:\n",[224,1560,1561],{"class":226,"line":251},[224,1562,1563],{},"        existing.last_seen_at = now()\n",[224,1565,1566],{"class":226,"line":257},[224,1567,1568],{},"        existing.metadata[\"source_count\"] = existing.metadata.get(\"source_count\", 1) + 1\n",[224,1570,1571],{"class":226,"line":263},[224,1572,1573],{},"        save(existing)\n",[224,1575,1576],{"class":226,"line":269},[224,1577,1578],{},"        return existing\n",[224,1580,1581],{"class":226,"line":275},[224,1582,359],{"emptyLinePlaceholder":358},[224,1584,1585],{"class":226,"line":281},[224,1586,1587],{},"    similar = find_semantic_match(candidate)\n",[224,1589,1590],{"class":226,"line":287},[224,1591,1592],{},"    if similar:\n",[224,1594,1595],{"class":226,"line":293},[224,1596,1597],{},"        similar.last_seen_at = now()\n",[224,1599,1600],{"class":226,"line":299},[224,1601,1602],{},"        similar.metadata.setdefault(\"related_sources\", []).append(candidate[\"source_url\"])\n",[224,1604,1605],{"class":226,"line":382},[224,1606,1607],{},"        save(similar)\n",[224,1609,1610],{"class":226,"line":388},[224,1611,1612],{},"        return similar\n",[224,1614,1615],{"class":226,"line":393},[224,1616,359],{"emptyLinePlaceholder":358},[224,1618,1619],{"class":226,"line":398},[224,1620,1621],{},"    return insert_new(candidate)\n",[11,1623,1624],{},"Ini penting karena pipeline yang baik bukan cuma mengumpulkan, tapi juga mengerti bahwa internet sering mengulang hal yang sama dengan bungkus berbeda.",[53,1626,1628],{"id":1627},"phase-3-ai-enrichment","Phase 3: AI Enrichment",[11,1630,1631],{},"Setelah item masuk database, enrichment agent bertugas memahami isi. Ini tempat AI agent mulai berguna.",[11,1633,1634],{},"Untuk setiap ide, agent bisa menghasilkan:",[61,1636,1637,1642,1647,1652,1657,1662,1667,1672],{},[64,1638,1639],{},[221,1640,1641],{},"normalized_title",[64,1643,1644],{},[221,1645,1646],{},"summary",[64,1648,1649],{},[221,1650,1651],{},"detected_topic",[64,1653,1654],{},[221,1655,1656],{},"tags",[64,1658,1659],{},[221,1660,1661],{},"entities",[64,1663,1664],{},[221,1665,1666],{},"content_angles",[64,1668,1669],{},[221,1670,1671],{},"risk_notes",[64,1673,1674],{},[221,1675,1676],{},"possible_outputs",[11,1678,1679],{},"Contoh prompt enrichment:",[214,1681,1683],{"className":904,"code":1682,"language":906,"meta":219,"style":219},"ENRICHMENT_PROMPT = \"\"\"\nYou are a content strategy analyst for an engineering and AI blog.\nAnalyze the following scraped item.\n\nReturn JSON with:\n- normalized_title: concise title\n- summary: 2 sentence summary\n- tags: 3-7 tags\n- detected_topic: main topic\n- content_angles: 3 possible blog angles\n- possible_outputs: blog_post, github_repo, linkedin_post, newsletter, short_video\n- risk_notes: privacy, factual uncertainty, legal, or none\n\nItem:\n{raw_text}\nSource: {source_url}\n\"\"\"\n",[221,1684,1685,1690,1695,1700,1704,1709,1714,1719,1724,1729,1734,1739,1744,1748,1753,1758,1763],{"__ignoreMap":219},[224,1686,1687],{"class":226,"line":227},[224,1688,1689],{},"ENRICHMENT_PROMPT = \"\"\"\n",[224,1691,1692],{"class":226,"line":233},[224,1693,1694],{},"You are a content strategy analyst for an engineering and AI blog.\n",[224,1696,1697],{"class":226,"line":239},[224,1698,1699],{},"Analyze the following scraped item.\n",[224,1701,1702],{"class":226,"line":245},[224,1703,359],{"emptyLinePlaceholder":358},[224,1705,1706],{"class":226,"line":251},[224,1707,1708],{},"Return JSON with:\n",[224,1710,1711],{"class":226,"line":257},[224,1712,1713],{},"- normalized_title: concise title\n",[224,1715,1716],{"class":226,"line":263},[224,1717,1718],{},"- summary: 2 sentence summary\n",[224,1720,1721],{"class":226,"line":269},[224,1722,1723],{},"- tags: 3-7 tags\n",[224,1725,1726],{"class":226,"line":275},[224,1727,1728],{},"- detected_topic: main topic\n",[224,1730,1731],{"class":226,"line":281},[224,1732,1733],{},"- content_angles: 3 possible blog angles\n",[224,1735,1736],{"class":226,"line":287},[224,1737,1738],{},"- possible_outputs: blog_post, github_repo, linkedin_post, newsletter, short_video\n",[224,1740,1741],{"class":226,"line":293},[224,1742,1743],{},"- risk_notes: privacy, factual uncertainty, legal, or none\n",[224,1745,1746],{"class":226,"line":299},[224,1747,359],{"emptyLinePlaceholder":358},[224,1749,1750],{"class":226,"line":382},[224,1751,1752],{},"Item:\n",[224,1754,1755],{"class":226,"line":388},[224,1756,1757],{},"{raw_text}\n",[224,1759,1760],{"class":226,"line":393},[224,1761,1762],{},"Source: {source_url}\n",[224,1764,1765],{"class":226,"line":398},[224,1766,1767],{},"\"\"\"\n",[11,1769,1770],{},"Output-nya harus structured JSON. Jangan biarkan model jawab bebas. Kalau bebas, downstream system menderita.",[214,1772,1776],{"className":1773,"code":1774,"language":1775,"meta":219,"style":219},"language-json shiki shiki-themes github-light github-dark","{\n  \"normalized_title\": \"Building AI Agents for Content Curation\",\n  \"summary\": \"The item discusses using agents to collect and rank content ideas. It is relevant for teams trying to automate editorial workflows without removing human judgment.\",\n  \"tags\": [\"ai-agents\", \"automation\", \"content-strategy\", \"workflow\"],\n  \"detected_topic\": \"AI content automation\",\n  \"content_angles\": [\n    \"How to design a scoring engine for content ideas\",\n    \"Why human curation still matters in AI publishing\",\n    \"Building a morning briefing agent for creators\"\n  ],\n  \"possible_outputs\": [\"blog_post\", \"linkedin_post\", \"github_repo\"],\n  \"risk_notes\": \"none\"\n}\n","json",[221,1777,1778,1784,1800,1812,1842,1854,1862,1869,1876,1881,1886,1908,1918],{"__ignoreMap":219},[224,1779,1780],{"class":226,"line":227},[224,1781,1783],{"class":1782},"sVt8B","{\n",[224,1785,1786,1790,1793,1797],{"class":226,"line":233},[224,1787,1789],{"class":1788},"sj4cs","  \"normalized_title\"",[224,1791,1792],{"class":1782},": ",[224,1794,1796],{"class":1795},"sZZnC","\"Building AI Agents for Content Curation\"",[224,1798,1799],{"class":1782},",\n",[224,1801,1802,1805,1807,1810],{"class":226,"line":239},[224,1803,1804],{"class":1788},"  \"summary\"",[224,1806,1792],{"class":1782},[224,1808,1809],{"class":1795},"\"The item discusses using agents to collect and rank content ideas. It is relevant for teams trying to automate editorial workflows without removing human judgment.\"",[224,1811,1799],{"class":1782},[224,1813,1814,1817,1820,1823,1826,1829,1831,1834,1836,1839],{"class":226,"line":245},[224,1815,1816],{"class":1788},"  \"tags\"",[224,1818,1819],{"class":1782},": [",[224,1821,1822],{"class":1795},"\"ai-agents\"",[224,1824,1825],{"class":1782},", ",[224,1827,1828],{"class":1795},"\"automation\"",[224,1830,1825],{"class":1782},[224,1832,1833],{"class":1795},"\"content-strategy\"",[224,1835,1825],{"class":1782},[224,1837,1838],{"class":1795},"\"workflow\"",[224,1840,1841],{"class":1782},"],\n",[224,1843,1844,1847,1849,1852],{"class":226,"line":251},[224,1845,1846],{"class":1788},"  \"detected_topic\"",[224,1848,1792],{"class":1782},[224,1850,1851],{"class":1795},"\"AI content automation\"",[224,1853,1799],{"class":1782},[224,1855,1856,1859],{"class":226,"line":257},[224,1857,1858],{"class":1788},"  \"content_angles\"",[224,1860,1861],{"class":1782},": [\n",[224,1863,1864,1867],{"class":226,"line":263},[224,1865,1866],{"class":1795},"    \"How to design a scoring engine for content ideas\"",[224,1868,1799],{"class":1782},[224,1870,1871,1874],{"class":226,"line":269},[224,1872,1873],{"class":1795},"    \"Why human curation still matters in AI publishing\"",[224,1875,1799],{"class":1782},[224,1877,1878],{"class":226,"line":275},[224,1879,1880],{"class":1795},"    \"Building a morning briefing agent for creators\"\n",[224,1882,1883],{"class":226,"line":281},[224,1884,1885],{"class":1782},"  ],\n",[224,1887,1888,1891,1893,1896,1898,1901,1903,1906],{"class":226,"line":287},[224,1889,1890],{"class":1788},"  \"possible_outputs\"",[224,1892,1819],{"class":1782},[224,1894,1895],{"class":1795},"\"blog_post\"",[224,1897,1825],{"class":1782},[224,1899,1900],{"class":1795},"\"linkedin_post\"",[224,1902,1825],{"class":1782},[224,1904,1905],{"class":1795},"\"github_repo\"",[224,1907,1841],{"class":1782},[224,1909,1910,1913,1915],{"class":226,"line":293},[224,1911,1912],{"class":1788},"  \"risk_notes\"",[224,1914,1792],{"class":1782},[224,1916,1917],{"class":1795},"\"none\"\n",[224,1919,1920],{"class":226,"line":299},[224,1921,1922],{"class":1782},"}\n",[11,1924,1925],{},"Enrichment juga bisa menilai audience fit. Misalnya untuk blog engineering, item terlalu marketing-heavy bisa tetap disimpan, tapi score relevansinya rendah.",[53,1927,1929],{"id":1928},"phase-4-scoring-algorithm","Phase 4: Scoring Algorithm",[11,1931,1932],{},"Scoring adalah jantung pipeline. Kalau scoring jelek, morning briefing akan terasa random. Kalau scoring bagus, manusia merasa “wah, ini sistem ngerti gue”.",[11,1934,1935],{},"Saya suka memecah final score menjadi beberapa komponen:",[214,1937,1942],{"className":1938,"code":1940,"language":1941,"meta":219},[1939],"language-text","final_score =\n    0.30 * relevance_score +\n    0.20 * engagement_score +\n    0.20 * novelty_score +\n    0.15 * feasibility_score +\n    0.10 * freshness_score +\n    0.05 * strategic_fit_score\n","text",[221,1943,1940],{"__ignoreMap":219},[11,1945,1946],{},"Bobot ini bukan sakral. Untuk blog teknis, relevance dan feasibility mungkin lebih penting daripada engagement. Untuk social media growth, engagement bisa dinaikkan.",[875,1948,1950],{"id":1949},"relevance-score","Relevance Score",[11,1952,1953],{},"Relevance menilai apakah ide cocok dengan domain kita. Untuk blog engineering dan AI automation, topik seperti agent orchestration, Python automation, infrastructure, DevOps, data pipeline, dan applied AI akan tinggi.",[11,1955,993],{},[214,1957,1959],{"className":904,"code":1958,"language":906,"meta":219,"style":219},"CORE_TOPICS = {\n    \"ai agents\": 1.0,\n    \"automation\": 0.95,\n    \"python\": 0.85,\n    \"devops\": 0.8,\n    \"content strategy\": 0.75,\n    \"engineering management\": 0.7,\n}\n\n\ndef score_relevance(tags: list[str], detected_topic: str) -> float:\n    score = 0.0\n    for tag in tags:\n        score = max(score, CORE_TOPICS.get(tag.lower(), 0.0))\n\n    topic_score = CORE_TOPICS.get(detected_topic.lower(), 0.0)\n    return max(score, topic_score)\n",[221,1960,1961,1966,1971,1976,1981,1986,1991,1996,2000,2004,2008,2013,2018,2023,2028,2032,2037],{"__ignoreMap":219},[224,1962,1963],{"class":226,"line":227},[224,1964,1965],{},"CORE_TOPICS = {\n",[224,1967,1968],{"class":226,"line":233},[224,1969,1970],{},"    \"ai agents\": 1.0,\n",[224,1972,1973],{"class":226,"line":239},[224,1974,1975],{},"    \"automation\": 0.95,\n",[224,1977,1978],{"class":226,"line":245},[224,1979,1980],{},"    \"python\": 0.85,\n",[224,1982,1983],{"class":226,"line":251},[224,1984,1985],{},"    \"devops\": 0.8,\n",[224,1987,1988],{"class":226,"line":257},[224,1989,1990],{},"    \"content strategy\": 0.75,\n",[224,1992,1993],{"class":226,"line":263},[224,1994,1995],{},"    \"engineering management\": 0.7,\n",[224,1997,1998],{"class":226,"line":269},[224,1999,1922],{},[224,2001,2002],{"class":226,"line":275},[224,2003,359],{"emptyLinePlaceholder":358},[224,2005,2006],{"class":226,"line":281},[224,2007,359],{"emptyLinePlaceholder":358},[224,2009,2010],{"class":226,"line":287},[224,2011,2012],{},"def score_relevance(tags: list[str], detected_topic: str) -> float:\n",[224,2014,2015],{"class":226,"line":293},[224,2016,2017],{},"    score = 0.0\n",[224,2019,2020],{"class":226,"line":299},[224,2021,2022],{},"    for tag in tags:\n",[224,2024,2025],{"class":226,"line":382},[224,2026,2027],{},"        score = max(score, CORE_TOPICS.get(tag.lower(), 0.0))\n",[224,2029,2030],{"class":226,"line":388},[224,2031,359],{"emptyLinePlaceholder":358},[224,2033,2034],{"class":226,"line":393},[224,2035,2036],{},"    topic_score = CORE_TOPICS.get(detected_topic.lower(), 0.0)\n",[224,2038,2039],{"class":226,"line":398},[224,2040,2041],{},"    return max(score, topic_score)\n",[11,2043,2044],{},"Dalam production, saya lebih suka gabungkan rules dengan LLM judge. Rules memberi consistency, LLM memberi nuance.",[214,2046,2048],{"className":904,"code":2047,"language":906,"meta":219,"style":219},"\ndef llm_relevance_judge(idea, editorial_profile) -> float:\n    prompt = f\"\"\"\n    Rate relevance from 0 to 1 for this editorial profile.\n\n    Editorial profile:\n    {editorial_profile}\n\n    Idea:\n    {idea.summary}\n\n    Return only a number between 0 and 1.\n    \"\"\"\n    return float(call_llm(prompt))\n",[221,2049,2050,2054,2059,2064,2069,2073,2078,2083,2087,2092,2097,2101,2106,2111],{"__ignoreMap":219},[224,2051,2052],{"class":226,"line":227},[224,2053,359],{"emptyLinePlaceholder":358},[224,2055,2056],{"class":226,"line":233},[224,2057,2058],{},"def llm_relevance_judge(idea, editorial_profile) -> float:\n",[224,2060,2061],{"class":226,"line":239},[224,2062,2063],{},"    prompt = f\"\"\"\n",[224,2065,2066],{"class":226,"line":245},[224,2067,2068],{},"    Rate relevance from 0 to 1 for this editorial profile.\n",[224,2070,2071],{"class":226,"line":251},[224,2072,359],{"emptyLinePlaceholder":358},[224,2074,2075],{"class":226,"line":257},[224,2076,2077],{},"    Editorial profile:\n",[224,2079,2080],{"class":226,"line":263},[224,2081,2082],{},"    {editorial_profile}\n",[224,2084,2085],{"class":226,"line":269},[224,2086,359],{"emptyLinePlaceholder":358},[224,2088,2089],{"class":226,"line":275},[224,2090,2091],{},"    Idea:\n",[224,2093,2094],{"class":226,"line":281},[224,2095,2096],{},"    {idea.summary}\n",[224,2098,2099],{"class":226,"line":287},[224,2100,359],{"emptyLinePlaceholder":358},[224,2102,2103],{"class":226,"line":293},[224,2104,2105],{},"    Return only a number between 0 and 1.\n",[224,2107,2108],{"class":226,"line":299},[224,2109,2110],{},"    \"\"\"\n",[224,2112,2113],{"class":226,"line":382},[224,2114,2115],{},"    return float(call_llm(prompt))\n",[875,2117,2119],{"id":2118},"engagement-score","Engagement Score",[11,2121,2122],{},"Engagement score berasal dari social metrics dan trend velocity. Tapi hati-hati: viral bukan berarti valuable.",[214,2124,2126],{"className":904,"code":2125,"language":906,"meta":219,"style":219},"import math\n\n\ndef score_engagement(metadata: dict) -> float:\n    likes = metadata.get(\"likes\", 0)\n    comments = metadata.get(\"comments\", 0)\n    shares = metadata.get(\"shares\", 0)\n    stars = metadata.get(\"stars\", 0)\n\n    raw = likes + (2 * comments) + (3 * shares) + (0.5 * stars)\n    return min(1.0, math.log1p(raw) \u002F 10)\n",[221,2127,2128,2133,2137,2141,2146,2151,2156,2161,2166,2170,2175],{"__ignoreMap":219},[224,2129,2130],{"class":226,"line":227},[224,2131,2132],{},"import math\n",[224,2134,2135],{"class":226,"line":233},[224,2136,359],{"emptyLinePlaceholder":358},[224,2138,2139],{"class":226,"line":239},[224,2140,359],{"emptyLinePlaceholder":358},[224,2142,2143],{"class":226,"line":245},[224,2144,2145],{},"def score_engagement(metadata: dict) -> float:\n",[224,2147,2148],{"class":226,"line":251},[224,2149,2150],{},"    likes = metadata.get(\"likes\", 0)\n",[224,2152,2153],{"class":226,"line":257},[224,2154,2155],{},"    comments = metadata.get(\"comments\", 0)\n",[224,2157,2158],{"class":226,"line":263},[224,2159,2160],{},"    shares = metadata.get(\"shares\", 0)\n",[224,2162,2163],{"class":226,"line":269},[224,2164,2165],{},"    stars = metadata.get(\"stars\", 0)\n",[224,2167,2168],{"class":226,"line":275},[224,2169,359],{"emptyLinePlaceholder":358},[224,2171,2172],{"class":226,"line":281},[224,2173,2174],{},"    raw = likes + (2 * comments) + (3 * shares) + (0.5 * stars)\n",[224,2176,2177],{"class":226,"line":287},[224,2178,2179],{},"    return min(1.0, math.log1p(raw) \u002F 10)\n",[11,2181,2182],{},"Log scale penting supaya satu viral post tidak menghancurkan scoring lain.",[875,2184,2186],{"id":2185},"novelty-score","Novelty Score",[11,2188,2189],{},"Novelty mengecek apakah topik ini baru dibanding ide yang sudah pernah muncul atau sudah pernah dipublish.",[214,2191,2193],{"className":904,"code":2192,"language":906,"meta":219,"style":219},"\ndef score_novelty(idea_embedding, published_index, candidate_index) -> float:\n    published_similarity = published_index.max_similarity(idea_embedding)\n    recent_candidate_similarity = candidate_index.max_similarity(idea_embedding, days=14)\n\n    similarity = max(published_similarity, recent_candidate_similarity)\n    return max(0.0, 1.0 - similarity)\n",[221,2194,2195,2199,2204,2209,2214,2218,2223],{"__ignoreMap":219},[224,2196,2197],{"class":226,"line":227},[224,2198,359],{"emptyLinePlaceholder":358},[224,2200,2201],{"class":226,"line":233},[224,2202,2203],{},"def score_novelty(idea_embedding, published_index, candidate_index) -> float:\n",[224,2205,2206],{"class":226,"line":239},[224,2207,2208],{},"    published_similarity = published_index.max_similarity(idea_embedding)\n",[224,2210,2211],{"class":226,"line":245},[224,2212,2213],{},"    recent_candidate_similarity = candidate_index.max_similarity(idea_embedding, days=14)\n",[224,2215,2216],{"class":226,"line":251},[224,2217,359],{"emptyLinePlaceholder":358},[224,2219,2220],{"class":226,"line":257},[224,2221,2222],{},"    similarity = max(published_similarity, recent_candidate_similarity)\n",[224,2224,2225],{"class":226,"line":263},[224,2226,2227],{},"    return max(0.0, 1.0 - similarity)\n",[11,2229,2230],{},"Kalau similarity 0.9 dengan artikel yang baru publish minggu lalu, novelty score harus rendah. Bukan berarti idenya buruk. Artinya bukan prioritas pagi ini.",[875,2232,2234],{"id":2233},"feasibility-score","Feasibility Score",[11,2236,2237],{},"Ini bagian yang sering dilupakan. Ada ide yang kelihatan bagus tapi butuh data yang tidak kita punya, akses yang sulit, atau waktu riset panjang. Feasibility menilai apakah ide bisa dieksekusi dalam waktu reasonable.",[11,2239,2240],{},"Contoh heuristic:",[61,2242,2243,2246,2249,2252,2255],{},[64,2244,2245],{},"Ada source jelas: +0.2",[64,2247,2248],{},"Butuh eksperimen coding yang bisa dilakukan: +0.2",[64,2250,2251],{},"Butuh proprietary data: -0.3",[64,2253,2254],{},"Topik terlalu broad: -0.2",[64,2256,2257],{},"Bisa jadi tutorial praktis: +0.3",[214,2259,2261],{"className":904,"code":2260,"language":906,"meta":219,"style":219},"\ndef score_feasibility(idea) -> float:\n    score = 0.5\n\n    if idea.source_url:\n        score += 0.15\n    if \"tutorial\" in idea.tags or \"python\" in idea.tags:\n        score += 0.2\n    if \"requires private data\" in idea.risk_notes:\n        score -= 0.3\n    if len(idea.summary.split()) \u003C 20:\n        score -= 0.1\n    if \"possible_outputs\" in idea.metadata and \"github_repo\" in idea.metadata[\"possible_outputs\"]:\n        score += 0.1\n\n    return max(0.0, min(1.0, score))\n",[221,2262,2263,2267,2272,2277,2281,2286,2291,2296,2301,2306,2311,2316,2321,2326,2331,2335],{"__ignoreMap":219},[224,2264,2265],{"class":226,"line":227},[224,2266,359],{"emptyLinePlaceholder":358},[224,2268,2269],{"class":226,"line":233},[224,2270,2271],{},"def score_feasibility(idea) -> float:\n",[224,2273,2274],{"class":226,"line":239},[224,2275,2276],{},"    score = 0.5\n",[224,2278,2279],{"class":226,"line":245},[224,2280,359],{"emptyLinePlaceholder":358},[224,2282,2283],{"class":226,"line":251},[224,2284,2285],{},"    if idea.source_url:\n",[224,2287,2288],{"class":226,"line":257},[224,2289,2290],{},"        score += 0.15\n",[224,2292,2293],{"class":226,"line":263},[224,2294,2295],{},"    if \"tutorial\" in idea.tags or \"python\" in idea.tags:\n",[224,2297,2298],{"class":226,"line":269},[224,2299,2300],{},"        score += 0.2\n",[224,2302,2303],{"class":226,"line":275},[224,2304,2305],{},"    if \"requires private data\" in idea.risk_notes:\n",[224,2307,2308],{"class":226,"line":281},[224,2309,2310],{},"        score -= 0.3\n",[224,2312,2313],{"class":226,"line":287},[224,2314,2315],{},"    if len(idea.summary.split()) \u003C 20:\n",[224,2317,2318],{"class":226,"line":293},[224,2319,2320],{},"        score -= 0.1\n",[224,2322,2323],{"class":226,"line":299},[224,2324,2325],{},"    if \"possible_outputs\" in idea.metadata and \"github_repo\" in idea.metadata[\"possible_outputs\"]:\n",[224,2327,2328],{"class":226,"line":382},[224,2329,2330],{},"        score += 0.1\n",[224,2332,2333],{"class":226,"line":388},[224,2334,359],{"emptyLinePlaceholder":358},[224,2336,2337],{"class":226,"line":393},[224,2338,2339],{},"    return max(0.0, min(1.0, score))\n",[875,2341,2343],{"id":2342},"freshness-score","Freshness Score",[11,2345,2346],{},"Freshness adalah age-based score. Ide baru dapat boost. Ide lama turun perlahan.",[214,2348,2350],{"className":904,"code":2349,"language":906,"meta":219,"style":219},"from datetime import datetime, timezone\n\n\ndef score_freshness(created_at: datetime) -> float:\n    age_hours = (datetime.now(timezone.utc) - created_at).total_seconds() \u002F 3600\n    if age_hours \u003C 24:\n        return 1.0\n    if age_hours \u003C 72:\n        return 0.8\n    if age_hours \u003C 168:\n        return 0.5\n    return 0.2\n",[221,2351,2352,2357,2361,2365,2370,2375,2380,2385,2390,2395,2400,2405],{"__ignoreMap":219},[224,2353,2354],{"class":226,"line":227},[224,2355,2356],{},"from datetime import datetime, timezone\n",[224,2358,2359],{"class":226,"line":233},[224,2360,359],{"emptyLinePlaceholder":358},[224,2362,2363],{"class":226,"line":239},[224,2364,359],{"emptyLinePlaceholder":358},[224,2366,2367],{"class":226,"line":245},[224,2368,2369],{},"def score_freshness(created_at: datetime) -> float:\n",[224,2371,2372],{"class":226,"line":251},[224,2373,2374],{},"    age_hours = (datetime.now(timezone.utc) - created_at).total_seconds() \u002F 3600\n",[224,2376,2377],{"class":226,"line":257},[224,2378,2379],{},"    if age_hours \u003C 24:\n",[224,2381,2382],{"class":226,"line":263},[224,2383,2384],{},"        return 1.0\n",[224,2386,2387],{"class":226,"line":269},[224,2388,2389],{},"    if age_hours \u003C 72:\n",[224,2391,2392],{"class":226,"line":275},[224,2393,2394],{},"        return 0.8\n",[224,2396,2397],{"class":226,"line":281},[224,2398,2399],{},"    if age_hours \u003C 168:\n",[224,2401,2402],{"class":226,"line":287},[224,2403,2404],{},"        return 0.5\n",[224,2406,2407],{"class":226,"line":293},[224,2408,2409],{},"    return 0.2\n",[875,2411,2413],{"id":2412},"strategic-fit-score","Strategic Fit Score",[11,2415,2416],{},"Strategic fit mengikuti arah blog atau bisnis. Misalnya bulan ini fokus pada AI agents, OpenClaw, dan automation. Ide di area itu dapat boost.",[214,2418,2420],{"className":904,"code":2419,"language":906,"meta":219,"style":219},"CURRENT_FOCUS = [\"ai agents\", \"openclaw\", \"automation\", \"agent workflow\"]\n\n\ndef score_strategic_fit(idea) -> float:\n    text = f\"{idea.normalized_title} {idea.summary} {' '.join(idea.tags)}\".lower()\n    matches = sum(1 for focus in CURRENT_FOCUS if focus in text)\n    return min(1.0, matches \u002F 2)\n",[221,2421,2422,2427,2431,2435,2440,2445,2450],{"__ignoreMap":219},[224,2423,2424],{"class":226,"line":227},[224,2425,2426],{},"CURRENT_FOCUS = [\"ai agents\", \"openclaw\", \"automation\", \"agent workflow\"]\n",[224,2428,2429],{"class":226,"line":233},[224,2430,359],{"emptyLinePlaceholder":358},[224,2432,2433],{"class":226,"line":239},[224,2434,359],{"emptyLinePlaceholder":358},[224,2436,2437],{"class":226,"line":245},[224,2438,2439],{},"def score_strategic_fit(idea) -> float:\n",[224,2441,2442],{"class":226,"line":251},[224,2443,2444],{},"    text = f\"{idea.normalized_title} {idea.summary} {' '.join(idea.tags)}\".lower()\n",[224,2446,2447],{"class":226,"line":257},[224,2448,2449],{},"    matches = sum(1 for focus in CURRENT_FOCUS if focus in text)\n",[224,2451,2452],{"class":226,"line":263},[224,2453,2454],{},"    return min(1.0, matches \u002F 2)\n",[11,2456,2457],{},"Akhirnya:",[214,2459,2461],{"className":904,"code":2460,"language":906,"meta":219,"style":219},"\ndef compute_final_score(idea) -> float:\n    base = (\n        0.30 * idea.relevance_score +\n        0.20 * idea.engagement_score +\n        0.20 * idea.novelty_score +\n        0.15 * idea.feasibility_score +\n        0.10 * idea.freshness_score +\n        0.05 * idea.strategic_fit_score\n    )\n    return base * idea.decay_factor\n",[221,2462,2463,2467,2472,2477,2482,2487,2492,2497,2502,2507,2511],{"__ignoreMap":219},[224,2464,2465],{"class":226,"line":227},[224,2466,359],{"emptyLinePlaceholder":358},[224,2468,2469],{"class":226,"line":233},[224,2470,2471],{},"def compute_final_score(idea) -> float:\n",[224,2473,2474],{"class":226,"line":239},[224,2475,2476],{},"    base = (\n",[224,2478,2479],{"class":226,"line":245},[224,2480,2481],{},"        0.30 * idea.relevance_score +\n",[224,2483,2484],{"class":226,"line":251},[224,2485,2486],{},"        0.20 * idea.engagement_score +\n",[224,2488,2489],{"class":226,"line":257},[224,2490,2491],{},"        0.20 * idea.novelty_score +\n",[224,2493,2494],{"class":226,"line":263},[224,2495,2496],{},"        0.15 * idea.feasibility_score +\n",[224,2498,2499],{"class":226,"line":269},[224,2500,2501],{},"        0.10 * idea.freshness_score +\n",[224,2503,2504],{"class":226,"line":275},[224,2505,2506],{},"        0.05 * idea.strategic_fit_score\n",[224,2508,2509],{"class":226,"line":281},[224,2510,1194],{},[224,2512,2513],{"class":226,"line":287},[224,2514,2515],{},"    return base * idea.decay_factor\n",[11,2517,2518],{},"Simple, explainable, tunable. Itu lebih baik daripada black-box score yang tidak bisa didebug.",[53,2520,2522],{"id":2521},"phase-5-morning-briefing-system","Phase 5: Morning Briefing System",[11,2524,2525],{},"Morning briefing adalah interface utama antara machine curation dan human judgment. Kalau briefing-nya jelek, seluruh pipeline terasa tidak berguna.",[11,2527,2528],{},"Briefing harus singkat, actionable, dan punya context. Jangan cuma list title. Minimal tampilkan:",[61,2530,2531,2534,2536,2539,2542,2545,2548,2551],{},[64,2532,2533],{},"Rank",[64,2535,975],{},[64,2537,2538],{},"Score",[64,2540,2541],{},"Why it matters",[64,2543,2544],{},"Suggested output",[64,2546,2547],{},"Effort estimate",[64,2549,2550],{},"Risk note",[64,2552,2553],{},"Source link",[11,2555,2556],{},"Contoh format:",[214,2558,2561],{"className":2559,"code":2560,"language":1941,"meta":219},[1939],"Top 10 Content Ideas - 08:00 WITA\n\n1. AI agents for content curation\n   Score: 0.87\n   Why: Strong fit with current AI automation focus, good tutorial potential.\n   Output: Blog post + GitHub repo\n   Effort: Medium\n   Risk: None\n   Source: https:\u002F\u002Fexample.com\u002Fthread\n\n2. Postgres vector search for editorial workflows\n   Score: 0.82\n   Why: Practical engineering angle, can be turned into code-heavy tutorial.\n   Output: Technical blog\n   Effort: Medium\n   Risk: Needs benchmark validation\n",[221,2562,2560],{"__ignoreMap":219},[11,2564,2565],{},"Pseudocode scheduler:",[214,2567,2569],{"className":904,"code":2568,"language":906,"meta":219,"style":219},"from datetime import datetime\n\n\ndef generate_morning_briefing(db, limit=10):\n    ideas = db.query(\"\"\"\n        SELECT * FROM content_ideas\n        WHERE status = 'candidate'\n          AND final_score > 0.35\n        ORDER BY final_score DESC\n        LIMIT %s\n    \"\"\", [limit])\n\n    briefing = []\n    for rank, idea in enumerate(ideas, start=1):\n        briefing.append({\n            \"rank\": rank,\n            \"title\": idea.normalized_title,\n            \"score\": round(idea.final_score, 2),\n            \"why\": explain_score(idea),\n            \"suggested_outputs\": idea.metadata.get(\"possible_outputs\", []),\n            \"effort\": estimate_effort(idea),\n            \"risk\": idea.risk_notes,\n            \"source\": idea.source_url,\n        })\n\n    mark_as_presented(ideas)\n    return briefing\n",[221,2570,2571,2575,2579,2583,2588,2593,2598,2603,2608,2613,2618,2623,2627,2632,2637,2642,2647,2652,2657,2662,2667,2672,2677,2682,2686,2690,2695],{"__ignoreMap":219},[224,2572,2573],{"class":226,"line":227},[224,2574,1008],{},[224,2576,2577],{"class":226,"line":233},[224,2578,359],{"emptyLinePlaceholder":358},[224,2580,2581],{"class":226,"line":239},[224,2582,359],{"emptyLinePlaceholder":358},[224,2584,2585],{"class":226,"line":245},[224,2586,2587],{},"def generate_morning_briefing(db, limit=10):\n",[224,2589,2590],{"class":226,"line":251},[224,2591,2592],{},"    ideas = db.query(\"\"\"\n",[224,2594,2595],{"class":226,"line":257},[224,2596,2597],{},"        SELECT * FROM content_ideas\n",[224,2599,2600],{"class":226,"line":263},[224,2601,2602],{},"        WHERE status = 'candidate'\n",[224,2604,2605],{"class":226,"line":269},[224,2606,2607],{},"          AND final_score > 0.35\n",[224,2609,2610],{"class":226,"line":275},[224,2611,2612],{},"        ORDER BY final_score DESC\n",[224,2614,2615],{"class":226,"line":281},[224,2616,2617],{},"        LIMIT %s\n",[224,2619,2620],{"class":226,"line":287},[224,2621,2622],{},"    \"\"\", [limit])\n",[224,2624,2625],{"class":226,"line":293},[224,2626,359],{"emptyLinePlaceholder":358},[224,2628,2629],{"class":226,"line":299},[224,2630,2631],{},"    briefing = []\n",[224,2633,2634],{"class":226,"line":382},[224,2635,2636],{},"    for rank, idea in enumerate(ideas, start=1):\n",[224,2638,2639],{"class":226,"line":388},[224,2640,2641],{},"        briefing.append({\n",[224,2643,2644],{"class":226,"line":393},[224,2645,2646],{},"            \"rank\": rank,\n",[224,2648,2649],{"class":226,"line":398},[224,2650,2651],{},"            \"title\": idea.normalized_title,\n",[224,2653,2654],{"class":226,"line":404},[224,2655,2656],{},"            \"score\": round(idea.final_score, 2),\n",[224,2658,2659],{"class":226,"line":410},[224,2660,2661],{},"            \"why\": explain_score(idea),\n",[224,2663,2664],{"class":226,"line":416},[224,2665,2666],{},"            \"suggested_outputs\": idea.metadata.get(\"possible_outputs\", []),\n",[224,2668,2669],{"class":226,"line":421},[224,2670,2671],{},"            \"effort\": estimate_effort(idea),\n",[224,2673,2674],{"class":226,"line":426},[224,2675,2676],{},"            \"risk\": idea.risk_notes,\n",[224,2678,2679],{"class":226,"line":432},[224,2680,2681],{},"            \"source\": idea.source_url,\n",[224,2683,2684],{"class":226,"line":438},[224,2685,1080],{},[224,2687,2688],{"class":226,"line":444},[224,2689,359],{"emptyLinePlaceholder":358},[224,2691,2692],{"class":226,"line":450},[224,2693,2694],{},"    mark_as_presented(ideas)\n",[224,2696,2697],{"class":226,"line":455},[224,2698,2699],{},"    return briefing\n",[11,2701,2702,2705],{},[221,2703,2704],{},"explain_score"," penting. Humans trust systems better when systems explain themselves.",[214,2707,2709],{"className":904,"code":2708,"language":906,"meta":219,"style":219},"\ndef explain_score(idea) -> str:\n    reasons = []\n    if idea.relevance_score > 0.8:\n        reasons.append(\"high relevance to current editorial focus\")\n    if idea.engagement_score > 0.7:\n        reasons.append(\"strong engagement signal\")\n    if idea.novelty_score > 0.7:\n        reasons.append(\"fresh angle compared to recent posts\")\n    if idea.feasibility_score > 0.7:\n        reasons.append(\"practical to produce quickly\")\n\n    return \", \".join(reasons) or \"balanced score across multiple signals\"\n",[221,2710,2711,2715,2720,2725,2730,2735,2740,2745,2750,2755,2760,2765,2769],{"__ignoreMap":219},[224,2712,2713],{"class":226,"line":227},[224,2714,359],{"emptyLinePlaceholder":358},[224,2716,2717],{"class":226,"line":233},[224,2718,2719],{},"def explain_score(idea) -> str:\n",[224,2721,2722],{"class":226,"line":239},[224,2723,2724],{},"    reasons = []\n",[224,2726,2727],{"class":226,"line":245},[224,2728,2729],{},"    if idea.relevance_score > 0.8:\n",[224,2731,2732],{"class":226,"line":251},[224,2733,2734],{},"        reasons.append(\"high relevance to current editorial focus\")\n",[224,2736,2737],{"class":226,"line":257},[224,2738,2739],{},"    if idea.engagement_score > 0.7:\n",[224,2741,2742],{"class":226,"line":263},[224,2743,2744],{},"        reasons.append(\"strong engagement signal\")\n",[224,2746,2747],{"class":226,"line":269},[224,2748,2749],{},"    if idea.novelty_score > 0.7:\n",[224,2751,2752],{"class":226,"line":275},[224,2753,2754],{},"        reasons.append(\"fresh angle compared to recent posts\")\n",[224,2756,2757],{"class":226,"line":281},[224,2758,2759],{},"    if idea.feasibility_score > 0.7:\n",[224,2761,2762],{"class":226,"line":287},[224,2763,2764],{},"        reasons.append(\"practical to produce quickly\")\n",[224,2766,2767],{"class":226,"line":293},[224,2768,359],{"emptyLinePlaceholder":358},[224,2770,2771],{"class":226,"line":299},[224,2772,2773],{},"    return \", \".join(reasons) or \"balanced score across multiple signals\"\n",[11,2775,2776],{},"Delivery channel bisa Telegram, email, dashboard, atau internal app. Untuk saya, Telegram-style briefing paling enak karena bisa cepat dipilih dengan reply atau button.",[214,2778,2780],{"className":216,"code":2779,"language":218,"meta":219,"style":219},"sequenceDiagram\n    participant Cron\n    participant DB\n    participant Scorer\n    participant BriefingBot\n    participant Human\n    participant ProducerBot\n\n    Cron->>Scorer: Run daily scoring at 07:30\n    Scorer->>DB: Update final_score\n    Cron->>BriefingBot: Generate top 10 at 08:00\n    BriefingBot->>DB: Fetch ranked candidates\n    BriefingBot->>Human: Send morning briefing\n    Human->>BriefingBot: Pick item 1, 4, 7\n    BriefingBot->>ProducerBot: Create content jobs\n    ProducerBot->>DB: Update selected status\n",[221,2781,2782,2787,2792,2797,2802,2807,2812,2817,2821,2826,2831,2836,2841,2846,2851,2856],{"__ignoreMap":219},[224,2783,2784],{"class":226,"line":227},[224,2785,2786],{},"sequenceDiagram\n",[224,2788,2789],{"class":226,"line":233},[224,2790,2791],{},"    participant Cron\n",[224,2793,2794],{"class":226,"line":239},[224,2795,2796],{},"    participant DB\n",[224,2798,2799],{"class":226,"line":245},[224,2800,2801],{},"    participant Scorer\n",[224,2803,2804],{"class":226,"line":251},[224,2805,2806],{},"    participant BriefingBot\n",[224,2808,2809],{"class":226,"line":257},[224,2810,2811],{},"    participant Human\n",[224,2813,2814],{"class":226,"line":263},[224,2815,2816],{},"    participant ProducerBot\n",[224,2818,2819],{"class":226,"line":269},[224,2820,359],{"emptyLinePlaceholder":358},[224,2822,2823],{"class":226,"line":275},[224,2824,2825],{},"    Cron->>Scorer: Run daily scoring at 07:30\n",[224,2827,2828],{"class":226,"line":281},[224,2829,2830],{},"    Scorer->>DB: Update final_score\n",[224,2832,2833],{"class":226,"line":287},[224,2834,2835],{},"    Cron->>BriefingBot: Generate top 10 at 08:00\n",[224,2837,2838],{"class":226,"line":293},[224,2839,2840],{},"    BriefingBot->>DB: Fetch ranked candidates\n",[224,2842,2843],{"class":226,"line":299},[224,2844,2845],{},"    BriefingBot->>Human: Send morning briefing\n",[224,2847,2848],{"class":226,"line":382},[224,2849,2850],{},"    Human->>BriefingBot: Pick item 1, 4, 7\n",[224,2852,2853],{"class":226,"line":388},[224,2854,2855],{},"    BriefingBot->>ProducerBot: Create content jobs\n",[224,2857,2858],{"class":226,"line":393},[224,2859,2860],{},"    ProducerBot->>DB: Update selected status\n",[53,2862,2864],{"id":2863},"phase-6-human-curation-workflow","Phase 6: Human Curation Workflow",[11,2866,2867,2868,18],{},"Ini bagian yang menurut saya paling penting secara filosofi: ",[15,2869,2870],{},"human stays in the loop",[11,2872,2873],{},"AI bisa mengumpulkan dan mengurutkan, tapi manusia tetap menentukan:",[61,2875,2876,2879,2882,2885,2888],{},[64,2877,2878],{},"Apakah ide ini cocok dengan arah bisnis?",[64,2880,2881],{},"Apakah timing-nya pas?",[64,2883,2884],{},"Apakah ada data yang cukup?",[64,2886,2887],{},"Apakah topik ini sensitif?",[64,2889,2890],{},"Apakah output-nya harus blog, repo, atau social post?",[11,2892,2893],{},"Workflow idealnya tidak ribet. Human cukup memilih action:",[61,2895,2896,2901,2906,2911,2916,2921],{},[64,2897,2898],{},[221,2899,2900],{},"Approve as blog post",[64,2902,2903],{},[221,2904,2905],{},"Approve as GitHub tutorial",[64,2907,2908],{},[221,2909,2910],{},"Approve as LinkedIn post",[64,2912,2913],{},[221,2914,2915],{},"Save for later",[64,2917,2918],{},[221,2919,2920],{},"Reject",[64,2922,2923],{},[221,2924,2925],{},"Merge with another idea",[11,2927,2928],{},"Di backend, action ini menjadi feedback signal.",[214,2930,2932],{"className":904,"code":2931,"language":906,"meta":219,"style":219},"\ndef handle_human_action(idea_id: str, action: str, notes: str | None = None):\n    idea = db.get_idea(idea_id)\n\n    if action == \"approve_blog\":\n        idea.status = \"selected\"\n        idea.selected_count += 1\n        idea.selected_at = now()\n        create_content_job(idea, channel=\"blog\")\n\n    elif action == \"approve_github\":\n        idea.status = \"selected\"\n        idea.selected_count += 1\n        create_content_job(idea, channel=\"github\")\n\n    elif action == \"save_later\":\n        idea.status = \"backlog\"\n        idea.decay_factor *= 0.85\n\n    elif action == \"reject\":\n        idea.rejected_count += 1\n        idea.status = \"rejected\" if idea.rejected_count >= 2 else \"candidate\"\n        idea.decay_factor *= 0.5\n\n    elif action == \"merge\":\n        merge_idea_cluster(idea_id, notes)\n\n    db.save(idea)\n",[221,2933,2934,2938,2943,2948,2952,2957,2962,2967,2972,2977,2981,2986,2990,2994,2999,3003,3008,3013,3018,3022,3027,3032,3037,3042,3046,3051,3056,3060],{"__ignoreMap":219},[224,2935,2936],{"class":226,"line":227},[224,2937,359],{"emptyLinePlaceholder":358},[224,2939,2940],{"class":226,"line":233},[224,2941,2942],{},"def handle_human_action(idea_id: str, action: str, notes: str | None = None):\n",[224,2944,2945],{"class":226,"line":239},[224,2946,2947],{},"    idea = db.get_idea(idea_id)\n",[224,2949,2950],{"class":226,"line":245},[224,2951,359],{"emptyLinePlaceholder":358},[224,2953,2954],{"class":226,"line":251},[224,2955,2956],{},"    if action == \"approve_blog\":\n",[224,2958,2959],{"class":226,"line":257},[224,2960,2961],{},"        idea.status = \"selected\"\n",[224,2963,2964],{"class":226,"line":263},[224,2965,2966],{},"        idea.selected_count += 1\n",[224,2968,2969],{"class":226,"line":269},[224,2970,2971],{},"        idea.selected_at = now()\n",[224,2973,2974],{"class":226,"line":275},[224,2975,2976],{},"        create_content_job(idea, channel=\"blog\")\n",[224,2978,2979],{"class":226,"line":281},[224,2980,359],{"emptyLinePlaceholder":358},[224,2982,2983],{"class":226,"line":287},[224,2984,2985],{},"    elif action == \"approve_github\":\n",[224,2987,2988],{"class":226,"line":293},[224,2989,2961],{},[224,2991,2992],{"class":226,"line":299},[224,2993,2966],{},[224,2995,2996],{"class":226,"line":382},[224,2997,2998],{},"        create_content_job(idea, channel=\"github\")\n",[224,3000,3001],{"class":226,"line":388},[224,3002,359],{"emptyLinePlaceholder":358},[224,3004,3005],{"class":226,"line":393},[224,3006,3007],{},"    elif action == \"save_later\":\n",[224,3009,3010],{"class":226,"line":398},[224,3011,3012],{},"        idea.status = \"backlog\"\n",[224,3014,3015],{"class":226,"line":404},[224,3016,3017],{},"        idea.decay_factor *= 0.85\n",[224,3019,3020],{"class":226,"line":410},[224,3021,359],{"emptyLinePlaceholder":358},[224,3023,3024],{"class":226,"line":416},[224,3025,3026],{},"    elif action == \"reject\":\n",[224,3028,3029],{"class":226,"line":421},[224,3030,3031],{},"        idea.rejected_count += 1\n",[224,3033,3034],{"class":226,"line":426},[224,3035,3036],{},"        idea.status = \"rejected\" if idea.rejected_count >= 2 else \"candidate\"\n",[224,3038,3039],{"class":226,"line":432},[224,3040,3041],{},"        idea.decay_factor *= 0.5\n",[224,3043,3044],{"class":226,"line":438},[224,3045,359],{"emptyLinePlaceholder":358},[224,3047,3048],{"class":226,"line":444},[224,3049,3050],{},"    elif action == \"merge\":\n",[224,3052,3053],{"class":226,"line":450},[224,3054,3055],{},"        merge_idea_cluster(idea_id, notes)\n",[224,3057,3058],{"class":226,"line":455},[224,3059,359],{"emptyLinePlaceholder":358},[224,3061,3062],{"class":226,"line":460},[224,3063,3064],{},"    db.save(idea)\n",[11,3066,3067],{},"Human notes juga valuable. Kalau owner bilang “ini bagus tapi terlalu broad, fokus ke Python implementation”, note itu harus ikut ke production bot.",[53,3069,3071],{"id":3070},"phase-7-content-production-delegation","Phase 7: Content Production Delegation",[11,3073,3074],{},"Setelah ide dipilih, jangan langsung satu generic AI menulis semua. Better pattern: tiap blog\u002Fchannel punya managing bot dengan personality, rules, dan quality gate masing-masing.",[11,3076,3077],{},"Misalnya:",[61,3079,3080,3083,3086,3089,3092],{},[64,3081,3082],{},"Blog engineering bot: long-form technical writing, code snippets, diagrams",[64,3084,3085],{},"GitHub tutorial bot: repo scaffold, README, runnable examples",[64,3087,3088],{},"LinkedIn bot: short opinionated post, executive tone",[64,3090,3091],{},"Newsletter bot: curated summary, concise, link-heavy",[64,3093,3094],{},"Video bot: script, storyboard, caption plan",[11,3096,3097],{},"Content job bisa punya schema seperti ini:",[214,3099,3101],{"className":575,"code":3100,"language":577,"meta":219,"style":219},"CREATE TABLE content_jobs (\n    id UUID PRIMARY KEY,\n    idea_id UUID REFERENCES content_ideas(id),\n    channel_id UUID REFERENCES content_channels(id),\n    assigned_bot TEXT NOT NULL,\n    status TEXT DEFAULT 'queued',\n    human_notes TEXT,\n    draft_url TEXT,\n    quality_score NUMERIC,\n    created_at TIMESTAMPTZ DEFAULT now(),\n    completed_at TIMESTAMPTZ\n);\n",[221,3102,3103,3108,3112,3116,3120,3125,3130,3135,3140,3145,3149,3154],{"__ignoreMap":219},[224,3104,3105],{"class":226,"line":227},[224,3106,3107],{},"CREATE TABLE content_jobs (\n",[224,3109,3110],{"class":226,"line":233},[224,3111,589],{},[224,3113,3114],{"class":226,"line":239},[224,3115,836],{},[224,3117,3118],{"class":226,"line":245},[224,3119,841],{},[224,3121,3122],{"class":226,"line":251},[224,3123,3124],{},"    assigned_bot TEXT NOT NULL,\n",[224,3126,3127],{"class":226,"line":257},[224,3128,3129],{},"    status TEXT DEFAULT 'queued',\n",[224,3131,3132],{"class":226,"line":263},[224,3133,3134],{},"    human_notes TEXT,\n",[224,3136,3137],{"class":226,"line":269},[224,3138,3139],{},"    draft_url TEXT,\n",[224,3141,3142],{"class":226,"line":275},[224,3143,3144],{},"    quality_score NUMERIC,\n",[224,3146,3147],{"class":226,"line":281},[224,3148,694],{},[224,3150,3151],{"class":226,"line":287},[224,3152,3153],{},"    completed_at TIMESTAMPTZ\n",[224,3155,3156],{"class":226,"line":293},[224,3157,719],{},[11,3159,3160],{},"Ketika owner approve ide untuk blog, job dibuat:",[214,3162,3164],{"className":904,"code":3163,"language":906,"meta":219,"style":219},"\ndef create_content_job(idea, channel: str):\n    channel_config = db.get_channel(channel)\n\n    job = {\n        \"idea_id\": idea.id,\n        \"channel_id\": channel_config.id,\n        \"assigned_bot\": channel_config.owner_bot,\n        \"status\": \"queued\",\n        \"human_notes\": idea.metadata.get(\"human_notes\"),\n    }\n\n    db.insert(\"content_jobs\", job)\n    notify_bot(channel_config.owner_bot, build_assignment_prompt(idea, channel_config))\n",[221,3165,3166,3170,3175,3180,3184,3189,3194,3199,3204,3209,3214,3218,3222,3227],{"__ignoreMap":219},[224,3167,3168],{"class":226,"line":227},[224,3169,359],{"emptyLinePlaceholder":358},[224,3171,3172],{"class":226,"line":233},[224,3173,3174],{},"def create_content_job(idea, channel: str):\n",[224,3176,3177],{"class":226,"line":239},[224,3178,3179],{},"    channel_config = db.get_channel(channel)\n",[224,3181,3182],{"class":226,"line":245},[224,3183,359],{"emptyLinePlaceholder":358},[224,3185,3186],{"class":226,"line":251},[224,3187,3188],{},"    job = {\n",[224,3190,3191],{"class":226,"line":257},[224,3192,3193],{},"        \"idea_id\": idea.id,\n",[224,3195,3196],{"class":226,"line":263},[224,3197,3198],{},"        \"channel_id\": channel_config.id,\n",[224,3200,3201],{"class":226,"line":269},[224,3202,3203],{},"        \"assigned_bot\": channel_config.owner_bot,\n",[224,3205,3206],{"class":226,"line":275},[224,3207,3208],{},"        \"status\": \"queued\",\n",[224,3210,3211],{"class":226,"line":281},[224,3212,3213],{},"        \"human_notes\": idea.metadata.get(\"human_notes\"),\n",[224,3215,3216],{"class":226,"line":287},[224,3217,1447],{},[224,3219,3220],{"class":226,"line":293},[224,3221,359],{"emptyLinePlaceholder":358},[224,3223,3224],{"class":226,"line":299},[224,3225,3226],{},"    db.insert(\"content_jobs\", job)\n",[224,3228,3229],{"class":226,"line":382},[224,3230,3231],{},"    notify_bot(channel_config.owner_bot, build_assignment_prompt(idea, channel_config))\n",[11,3233,3234],{},"Assignment prompt harus membawa context lengkap:",[214,3236,3238],{"className":904,"code":3237,"language":906,"meta":219,"style":219},"\ndef build_assignment_prompt(idea, channel):\n    return f\"\"\"\n    You are the managing bot for {channel.name}.\n\n    Create content based on this selected idea.\n\n    Title: {idea.normalized_title}\n    Summary: {idea.summary}\n    Source: {idea.source_url}\n    Tags: {idea.tags}\n    Human notes: {idea.metadata.get('human_notes', 'none')}\n\n    Channel rules:\n    Audience: {channel.audience}\n    Tone: {channel.tone}\n    Publishing rules: {channel.publishing_rules}\n\n    Requirements:\n    - Produce a draft, not final publish\n    - Include factual source references\n    - Flag uncertainty\n    - Return status and draft location\n    \"\"\"\n",[221,3239,3240,3244,3249,3254,3259,3263,3268,3272,3277,3282,3287,3292,3297,3301,3306,3311,3316,3321,3325,3330,3335,3340,3345,3350],{"__ignoreMap":219},[224,3241,3242],{"class":226,"line":227},[224,3243,359],{"emptyLinePlaceholder":358},[224,3245,3246],{"class":226,"line":233},[224,3247,3248],{},"def build_assignment_prompt(idea, channel):\n",[224,3250,3251],{"class":226,"line":239},[224,3252,3253],{},"    return f\"\"\"\n",[224,3255,3256],{"class":226,"line":245},[224,3257,3258],{},"    You are the managing bot for {channel.name}.\n",[224,3260,3261],{"class":226,"line":251},[224,3262,359],{"emptyLinePlaceholder":358},[224,3264,3265],{"class":226,"line":257},[224,3266,3267],{},"    Create content based on this selected idea.\n",[224,3269,3270],{"class":226,"line":263},[224,3271,359],{"emptyLinePlaceholder":358},[224,3273,3274],{"class":226,"line":269},[224,3275,3276],{},"    Title: {idea.normalized_title}\n",[224,3278,3279],{"class":226,"line":275},[224,3280,3281],{},"    Summary: {idea.summary}\n",[224,3283,3284],{"class":226,"line":281},[224,3285,3286],{},"    Source: {idea.source_url}\n",[224,3288,3289],{"class":226,"line":287},[224,3290,3291],{},"    Tags: {idea.tags}\n",[224,3293,3294],{"class":226,"line":293},[224,3295,3296],{},"    Human notes: {idea.metadata.get('human_notes', 'none')}\n",[224,3298,3299],{"class":226,"line":299},[224,3300,359],{"emptyLinePlaceholder":358},[224,3302,3303],{"class":226,"line":382},[224,3304,3305],{},"    Channel rules:\n",[224,3307,3308],{"class":226,"line":388},[224,3309,3310],{},"    Audience: {channel.audience}\n",[224,3312,3313],{"class":226,"line":393},[224,3314,3315],{},"    Tone: {channel.tone}\n",[224,3317,3318],{"class":226,"line":398},[224,3319,3320],{},"    Publishing rules: {channel.publishing_rules}\n",[224,3322,3323],{"class":226,"line":404},[224,3324,359],{"emptyLinePlaceholder":358},[224,3326,3327],{"class":226,"line":410},[224,3328,3329],{},"    Requirements:\n",[224,3331,3332],{"class":226,"line":416},[224,3333,3334],{},"    - Produce a draft, not final publish\n",[224,3336,3337],{"class":226,"line":421},[224,3338,3339],{},"    - Include factual source references\n",[224,3341,3342],{"class":226,"line":426},[224,3343,3344],{},"    - Flag uncertainty\n",[224,3346,3347],{"class":226,"line":432},[224,3348,3349],{},"    - Return status and draft location\n",[224,3351,3352],{"class":226,"line":438},[224,3353,2110],{},[11,3355,3356],{},"Ini membuat pipeline modular. Discovery system tidak perlu tahu detail cara menulis blog. Blog bot yang tahu.",[53,3358,3360],{"id":3359},"phase-8-score-decay-mechanism","Phase 8: Score Decay Mechanism",[11,3362,3363],{},"Score decay adalah mekanisme kecil yang efeknya besar. Tanpa decay, ide yang score-nya tinggi akan muncul terus setiap pagi sampai manusia bosan.",[11,3365,3366],{},"Aturan dasarnya:",[61,3368,3369,3372,3375,3378,3381],{},[64,3370,3371],{},"Setiap kali item muncul di morning briefing, decay factor turun.",[64,3373,3374],{},"Kalau dipilih, status berubah jadi selected dan tidak muncul lagi sebagai candidate.",[64,3376,3377],{},"Kalau ditolak, score turun lebih agresif.",[64,3379,3380],{},"Kalau source baru muncul untuk ide yang sama, score bisa naik lagi.",[64,3382,3383],{},"Setelah periode tertentu, item bisa recover sedikit jika masih relevan.",[11,3385,3386],{},"Contoh:",[214,3388,3390],{"className":904,"code":3389,"language":906,"meta":219,"style":219},"\ndef mark_as_presented(ideas):\n    for idea in ideas:\n        idea.presented_count += 1\n        idea.last_presented_at = now()\n        idea.decay_factor *= 0.72\n        idea.final_score = compute_final_score(idea)\n        db.save(idea)\n",[221,3391,3392,3396,3401,3406,3411,3416,3421,3426],{"__ignoreMap":219},[224,3393,3394],{"class":226,"line":227},[224,3395,359],{"emptyLinePlaceholder":358},[224,3397,3398],{"class":226,"line":233},[224,3399,3400],{},"def mark_as_presented(ideas):\n",[224,3402,3403],{"class":226,"line":239},[224,3404,3405],{},"    for idea in ideas:\n",[224,3407,3408],{"class":226,"line":245},[224,3409,3410],{},"        idea.presented_count += 1\n",[224,3412,3413],{"class":226,"line":251},[224,3414,3415],{},"        idea.last_presented_at = now()\n",[224,3417,3418],{"class":226,"line":257},[224,3419,3420],{},"        idea.decay_factor *= 0.72\n",[224,3422,3423],{"class":226,"line":263},[224,3424,3425],{},"        idea.final_score = compute_final_score(idea)\n",[224,3427,3428],{"class":226,"line":269},[224,3429,3430],{},"        db.save(idea)\n",[11,3432,3433],{},"Kenapa 0.72? Tidak sakral. Ini berarti item yang muncul sekali akan turun 28%. Kalau masih sangat kuat, mungkin besok masih muncul. Kalau biasa saja, dia hilang dari top 10.",[11,3435,3436],{},"Untuk rejected item:",[214,3438,3440],{"className":904,"code":3439,"language":906,"meta":219,"style":219},"\ndef reject_idea(idea):\n    idea.rejected_count += 1\n    idea.decay_factor *= 0.45\n    if idea.rejected_count >= 2:\n        idea.status = \"rejected\"\n    db.save(idea)\n",[221,3441,3442,3446,3451,3456,3461,3466,3471],{"__ignoreMap":219},[224,3443,3444],{"class":226,"line":227},[224,3445,359],{"emptyLinePlaceholder":358},[224,3447,3448],{"class":226,"line":233},[224,3449,3450],{},"def reject_idea(idea):\n",[224,3452,3453],{"class":226,"line":239},[224,3454,3455],{},"    idea.rejected_count += 1\n",[224,3457,3458],{"class":226,"line":245},[224,3459,3460],{},"    idea.decay_factor *= 0.45\n",[224,3462,3463],{"class":226,"line":251},[224,3464,3465],{},"    if idea.rejected_count >= 2:\n",[224,3467,3468],{"class":226,"line":257},[224,3469,3470],{},"        idea.status = \"rejected\"\n",[224,3472,3473],{"class":226,"line":263},[224,3474,3064],{},[11,3476,3477],{},"Untuk recovery:",[214,3479,3481],{"className":904,"code":3480,"language":906,"meta":219,"style":219},"\ndef recover_old_candidates():\n    candidates = db.query(\"\"\"\n        SELECT * FROM content_ideas\n        WHERE status = 'candidate'\n          AND last_presented_at \u003C now() - interval '14 days'\n          AND decay_factor \u003C 1.0\n    \"\"\")\n\n    for idea in candidates:\n        idea.decay_factor = min(1.0, idea.decay_factor + 0.08)\n        idea.final_score = compute_final_score(idea)\n        db.save(idea)\n",[221,3482,3483,3487,3492,3497,3501,3505,3510,3515,3520,3524,3529,3534,3538],{"__ignoreMap":219},[224,3484,3485],{"class":226,"line":227},[224,3486,359],{"emptyLinePlaceholder":358},[224,3488,3489],{"class":226,"line":233},[224,3490,3491],{},"def recover_old_candidates():\n",[224,3493,3494],{"class":226,"line":239},[224,3495,3496],{},"    candidates = db.query(\"\"\"\n",[224,3498,3499],{"class":226,"line":245},[224,3500,2597],{},[224,3502,3503],{"class":226,"line":251},[224,3504,2602],{},[224,3506,3507],{"class":226,"line":257},[224,3508,3509],{},"          AND last_presented_at \u003C now() - interval '14 days'\n",[224,3511,3512],{"class":226,"line":263},[224,3513,3514],{},"          AND decay_factor \u003C 1.0\n",[224,3516,3517],{"class":226,"line":269},[224,3518,3519],{},"    \"\"\")\n",[224,3521,3522],{"class":226,"line":275},[224,3523,359],{"emptyLinePlaceholder":358},[224,3525,3526],{"class":226,"line":281},[224,3527,3528],{},"    for idea in candidates:\n",[224,3530,3531],{"class":226,"line":287},[224,3532,3533],{},"        idea.decay_factor = min(1.0, idea.decay_factor + 0.08)\n",[224,3535,3536],{"class":226,"line":293},[224,3537,3425],{},[224,3539,3540],{"class":226,"line":299},[224,3541,3430],{},[11,3543,3544],{},"Decay membuat suggestion tetap fresh tanpa membuang knowledge lama.",[53,3546,3548],{"id":3547},"feedback-loop-sistem-harus-belajar-dari-pilihan-manusia","Feedback Loop: Sistem Harus Belajar dari Pilihan Manusia",[11,3550,3551,3552,1825,3554,3557,3558,3561,3562,3565],{},"Kalau owner sering memilih ide dengan tags ",[221,3553,906],{},[221,3555,3556],{},"openclaw",", dan ",[221,3559,3560],{},"ai-agents",", scoring harus pelan-pelan menaikkan bobot area itu. Kalau ide dengan tags ",[221,3563,3564],{},"web3"," selalu ditolak, jangan terus muncul.",[11,3567,3568],{},"Simple learning bisa dibuat tanpa ML rumit:",[214,3570,3572],{"className":904,"code":3571,"language":906,"meta":219,"style":219},"\ndef update_topic_preferences():\n    selected = db.query(\"SELECT tags FROM content_ideas WHERE selected_count > 0\")\n    rejected = db.query(\"SELECT tags FROM content_ideas WHERE rejected_count > 0\")\n\n    weights = defaultdict(float)\n\n    for row in selected:\n        for tag in row.tags:\n            weights[tag] += 0.05\n\n    for row in rejected:\n        for tag in row.tags:\n            weights[tag] -= 0.03\n\n    for tag, delta in weights.items():\n        db.adjust_topic_weight(tag, delta, min_value=0.2, max_value=1.5)\n",[221,3573,3574,3578,3583,3588,3593,3597,3602,3606,3611,3616,3621,3625,3630,3634,3639,3643,3648],{"__ignoreMap":219},[224,3575,3576],{"class":226,"line":227},[224,3577,359],{"emptyLinePlaceholder":358},[224,3579,3580],{"class":226,"line":233},[224,3581,3582],{},"def update_topic_preferences():\n",[224,3584,3585],{"class":226,"line":239},[224,3586,3587],{},"    selected = db.query(\"SELECT tags FROM content_ideas WHERE selected_count > 0\")\n",[224,3589,3590],{"class":226,"line":245},[224,3591,3592],{},"    rejected = db.query(\"SELECT tags FROM content_ideas WHERE rejected_count > 0\")\n",[224,3594,3595],{"class":226,"line":251},[224,3596,359],{"emptyLinePlaceholder":358},[224,3598,3599],{"class":226,"line":257},[224,3600,3601],{},"    weights = defaultdict(float)\n",[224,3603,3604],{"class":226,"line":263},[224,3605,359],{"emptyLinePlaceholder":358},[224,3607,3608],{"class":226,"line":269},[224,3609,3610],{},"    for row in selected:\n",[224,3612,3613],{"class":226,"line":275},[224,3614,3615],{},"        for tag in row.tags:\n",[224,3617,3618],{"class":226,"line":281},[224,3619,3620],{},"            weights[tag] += 0.05\n",[224,3622,3623],{"class":226,"line":287},[224,3624,359],{"emptyLinePlaceholder":358},[224,3626,3627],{"class":226,"line":293},[224,3628,3629],{},"    for row in rejected:\n",[224,3631,3632],{"class":226,"line":299},[224,3633,3615],{},[224,3635,3636],{"class":226,"line":382},[224,3637,3638],{},"            weights[tag] -= 0.03\n",[224,3640,3641],{"class":226,"line":388},[224,3642,359],{"emptyLinePlaceholder":358},[224,3644,3645],{"class":226,"line":393},[224,3646,3647],{},"    for tag, delta in weights.items():\n",[224,3649,3650],{"class":226,"line":398},[224,3651,3652],{},"        db.adjust_topic_weight(tag, delta, min_value=0.2, max_value=1.5)\n",[11,3654,3655],{},"Dengan cara ini, sistem makin lama makin personal. Bukan karena prompt makin panjang, tapi karena feedback loop-nya rapi.",[53,3657,3659],{"id":3658},"quality-gates-before-publishing","Quality Gates Before Publishing",[11,3661,3662],{},"Saya anti full autopublish untuk konten yang membawa nama bisnis atau personal brand. AI boleh produce draft, tapi sebelum publish harus ada gate.",[11,3664,3665],{},"Minimal quality gate:",[61,3667,3668,3671,3674,3677,3680,3683,3686,3689],{},[64,3669,3670],{},"Fact check source utama",[64,3672,3673],{},"Link validation",[64,3675,3676],{},"No duplicate image",[64,3678,3679],{},"No hallucinated company\u002Fcustomer names",[64,3681,3682],{},"Code snippets reviewed atau marked pseudocode",[64,3684,3685],{},"Tone sesuai channel",[64,3687,3688],{},"SEO title dan description masuk akal",[64,3690,3691],{},"Human approval untuk final publish",[11,3693,3694],{},"Untuk technical blog, tambahkan:",[61,3696,3697,3700,3703,3706,3709],{},[64,3698,3699],{},"Code runnable jika diklaim runnable",[64,3701,3702],{},"Mermaid diagram valid",[64,3704,3705],{},"No exposed secrets",[64,3707,3708],{},"No private internal data",[64,3710,3711],{},"No claims without source atau disclaimer",[11,3713,3714],{},"Pseudocode gate:",[214,3716,3718],{"className":904,"code":3717,"language":906,"meta":219,"style":219},"\ndef run_quality_gate(draft):\n    checks = [\n        validate_links(draft),\n        detect_secrets(draft),\n        check_duplicate_images(draft),\n        verify_mermaid_blocks(draft),\n        check_required_frontmatter(draft),\n        llm_tone_review(draft),\n    ]\n\n    failed = [check for check in checks if not check.passed]\n    if failed:\n        return {\n            \"status\": \"needs_revision\",\n            \"failed_checks\": failed,\n        }\n\n    return {\"status\": \"ready_for_human_review\"}\n",[221,3719,3720,3724,3729,3734,3739,3744,3749,3754,3759,3764,3769,3773,3778,3783,3788,3793,3798,3803,3807],{"__ignoreMap":219},[224,3721,3722],{"class":226,"line":227},[224,3723,359],{"emptyLinePlaceholder":358},[224,3725,3726],{"class":226,"line":233},[224,3727,3728],{},"def run_quality_gate(draft):\n",[224,3730,3731],{"class":226,"line":239},[224,3732,3733],{},"    checks = [\n",[224,3735,3736],{"class":226,"line":245},[224,3737,3738],{},"        validate_links(draft),\n",[224,3740,3741],{"class":226,"line":251},[224,3742,3743],{},"        detect_secrets(draft),\n",[224,3745,3746],{"class":226,"line":257},[224,3747,3748],{},"        check_duplicate_images(draft),\n",[224,3750,3751],{"class":226,"line":263},[224,3752,3753],{},"        verify_mermaid_blocks(draft),\n",[224,3755,3756],{"class":226,"line":269},[224,3757,3758],{},"        check_required_frontmatter(draft),\n",[224,3760,3761],{"class":226,"line":275},[224,3762,3763],{},"        llm_tone_review(draft),\n",[224,3765,3766],{"class":226,"line":281},[224,3767,3768],{},"    ]\n",[224,3770,3771],{"class":226,"line":287},[224,3772,359],{"emptyLinePlaceholder":358},[224,3774,3775],{"class":226,"line":293},[224,3776,3777],{},"    failed = [check for check in checks if not check.passed]\n",[224,3779,3780],{"class":226,"line":299},[224,3781,3782],{},"    if failed:\n",[224,3784,3785],{"class":226,"line":382},[224,3786,3787],{},"        return {\n",[224,3789,3790],{"class":226,"line":388},[224,3791,3792],{},"            \"status\": \"needs_revision\",\n",[224,3794,3795],{"class":226,"line":393},[224,3796,3797],{},"            \"failed_checks\": failed,\n",[224,3799,3800],{"class":226,"line":398},[224,3801,3802],{},"        }\n",[224,3804,3805],{"class":226,"line":404},[224,3806,359],{"emptyLinePlaceholder":358},[224,3808,3809],{"class":226,"line":410},[224,3810,3811],{},"    return {\"status\": \"ready_for_human_review\"}\n",[11,3813,3814],{},"Ini bukan birokrasi. Ini safety belt. Konten yang salah bisa merusak trust lebih cepat daripada konten bagus membangunnya.",[53,3816,3818],{"id":3817},"deployment-pattern-start-small-then-scale","Deployment Pattern: Start Small, Then Scale",[11,3820,3821],{},"Kalau Anda ingin membangun pipeline ini, jangan langsung 20 sumber dan 8 bots. Start small.",[11,3823,3824],{},"Versi MVP cukup:",[98,3826,3827,3830,3833,3836,3839,3842,3845,3848],{},[64,3828,3829],{},"RSS scraper untuk 10 sumber",[64,3831,3832],{},"GitHub scanner untuk 5 query",[64,3834,3835],{},"Postgres database",[64,3837,3838],{},"Simple LLM enrichment",[64,3840,3841],{},"Rule-based scoring",[64,3843,3844],{},"Telegram\u002Femail morning briefing",[64,3846,3847],{},"Manual approve\u002Freject",[64,3849,3850],{},"Score decay",[11,3852,3853],{},"Setelah itu baru tambah:",[61,3855,3856,3859,3862,3865,3868,3871,3874],{},[64,3857,3858],{},"Social media connectors",[64,3860,3861],{},"Vector dedupe",[64,3863,3864],{},"Channel-specific scoring",[64,3866,3867],{},"Production bots",[64,3869,3870],{},"Dashboard",[64,3872,3873],{},"Learning from feedback",[64,3875,3876],{},"Automated quality gate",[11,3878,3879],{},"MVP architecture:",[214,3881,3883],{"className":216,"code":3882,"language":218,"meta":219,"style":219},"flowchart LR\n    RSS[RSS Feeds] --> PY[Python Ingestion Script]\n    GH[GitHub Search] --> PY\n    PY --> DB[(Postgres)]\n    DB --> AI[LLM Enrichment]\n    AI --> SCORE[Scoring]\n    SCORE --> TG[Telegram Morning Briefing]\n    TG --> HUMAN[Human Picks]\n    HUMAN --> BOT[Blog Bot Draft]\n",[221,3884,3885,3889,3894,3899,3904,3909,3914,3919,3924],{"__ignoreMap":219},[224,3886,3887],{"class":226,"line":227},[224,3888,230],{},[224,3890,3891],{"class":226,"line":233},[224,3892,3893],{},"    RSS[RSS Feeds] --> PY[Python Ingestion Script]\n",[224,3895,3896],{"class":226,"line":239},[224,3897,3898],{},"    GH[GitHub Search] --> PY\n",[224,3900,3901],{"class":226,"line":245},[224,3902,3903],{},"    PY --> DB[(Postgres)]\n",[224,3905,3906],{"class":226,"line":251},[224,3907,3908],{},"    DB --> AI[LLM Enrichment]\n",[224,3910,3911],{"class":226,"line":257},[224,3912,3913],{},"    AI --> SCORE[Scoring]\n",[224,3915,3916],{"class":226,"line":263},[224,3917,3918],{},"    SCORE --> TG[Telegram Morning Briefing]\n",[224,3920,3921],{"class":226,"line":269},[224,3922,3923],{},"    TG --> HUMAN[Human Picks]\n",[224,3925,3926],{"class":226,"line":275},[224,3927,3928],{},"    HUMAN --> BOT[Blog Bot Draft]\n",[11,3930,3931],{},"Ini bisa dibangun dalam beberapa hari. Yang penting schema dan workflow-nya benar.",[53,3933,3935],{"id":3934},"example-end-to-end-run","Example End-to-End Run",[11,3937,3938],{},"Bayangkan sistem berjalan jam 06:30.",[11,3940,3941],{},"Scraper menemukan beberapa items:",[61,3943,3944,3947,3950,3953,3956],{},[64,3945,3946],{},"GitHub repo baru tentang browser automation agents",[64,3948,3949],{},"Blog post tentang vector search untuk knowledge base",[64,3951,3952],{},"Thread tentang AI content workflows",[64,3954,3955],{},"Internal note tentang ide “morning briefing agent”",[64,3957,3958],{},"Release notes framework Python baru",[11,3960,3961],{},"Normalizer membersihkan URL dan text. Deduper melihat thread AI content workflows mirip dengan internal note, lalu menggabungkannya sebagai related sources. Enrichment agent memberi title: “Building a Morning Briefing Agent for Content Ideas”.",[11,3963,3964],{},"Scoring engine menghitung:",[61,3966,3967,3970,3973,3976,3979,3982],{},[64,3968,3969],{},"Relevance: 0.95 karena cocok dengan AI automation",[64,3971,3972],{},"Engagement: 0.72 karena thread ramai",[64,3974,3975],{},"Novelty: 0.81 karena belum pernah ditulis",[64,3977,3978],{},"Feasibility: 0.88 karena bisa dibuat tutorial",[64,3980,3981],{},"Freshness: 1.00 karena baru",[64,3983,3984],{},"Strategic fit: 0.90 karena sesuai focus bulan ini",[11,3986,3987],{},"Final score setelah decay factor 1.0: 0.87.",[11,3989,3990],{},"Jam 08:00, item masuk top 10. Owner memilih “approve as blog post” dan memberi note: “Bikin practical, include scoring formula dan score decay”. Blog bot menerima job, membuat outline, menulis draft, menambahkan code snippets dan Mermaid diagram. Quality gate mengecek link, image, frontmatter, dan secrets. Draft masuk human review.",[11,3992,3993,3994,3996,3997,4000],{},"Setelah item tampil di briefing, ",[221,3995,729],{}," naik dan decay factor turun. Karena item dipilih, status berubah ",[221,3998,3999],{},"selected",", jadi tidak muncul lagi sebagai candidate. Kalau source lain muncul besok tentang tema yang sama, deduper tahu bahwa cluster ini sudah selected dan hanya bisa muncul sebagai follow-up angle, bukan ide yang sama.",[11,4002,4003],{},"That is the difference between random AI automation and actual editorial operating system.",[53,4005,4007],{"id":4006},"results-you-should-expect","Results You Should Expect",[11,4009,4010],{},"Pipeline seperti ini biasanya memberi hasil di beberapa area:",[875,4012,4014],{"id":4013},"_1-ide-lebih-konsisten","1. Ide Lebih Konsisten",[11,4016,4017],{},"Tidak ada lagi hari blank. Setiap pagi ada top 10. Tidak semuanya akan bagus, tapi selalu ada bahan untuk dipilih.",[875,4019,4021],{"id":4020},"_2-curation-lebih-cepat","2. Curation Lebih Cepat",[11,4023,4024],{},"Human tidak mulai dari internet kosong. Human mulai dari shortlist yang sudah diberi context, score, dan suggested output.",[875,4026,4028],{"id":4027},"_3-produksi-lebih-terarah","3. Produksi Lebih Terarah",[11,4030,4031],{},"Bot produksi tidak menerima prompt vague seperti “tulis tentang AI”. Mereka menerima idea object dengan source, summary, reason, angle, dan channel rules.",[875,4033,4035],{"id":4034},"_4-repetition-turun","4. Repetition Turun",[11,4037,4038],{},"Score decay dan semantic dedupe mengurangi ide yang muter-muter. Ini bikin briefing terasa fresh.",[875,4040,4042],{"id":4041},"_5-knowledge-base-makin-kaya","5. Knowledge Base Makin Kaya",[11,4044,4045],{},"Setiap scraped item, selected item, rejected item, dan published output menjadi data. Lama-lama sistem bukan cuma pipeline konten, tapi editorial memory.",[53,4047,4049],{"id":4048},"lessons-learned","Lessons Learned",[11,4051,4052],{},"Ada beberapa lesson yang menurut saya penting.",[11,4054,4055,4056,4059],{},"Pertama, ",[15,4057,4058],{},"AI agent paling berguna ketika diberi workflow yang jelas",". Kalau agent cuma disuruh “carikan ide bagus”, hasilnya random. Kalau agent diberi schema, scoring dimensions, dan feedback loop, hasilnya jauh lebih stabil.",[11,4061,4062,4063,4066],{},"Kedua, ",[15,4064,4065],{},"human curation bukan kelemahan",". Banyak orang terlalu terobsesi dengan full automation. Untuk content yang punya brand voice, judgment manusia itu feature, bukan bottleneck. Yang perlu diotomasi adalah kerja repetitif sebelum keputusan.",[11,4068,4069,4070,4073],{},"Ketiga, ",[15,4071,4072],{},"scoring harus explainable",". Kalau sistem tidak bisa menjelaskan kenapa satu ide ranking #1, manusia akan sulit percaya. Simple weighted score yang bisa dijelaskan sering lebih baik daripada ML model opaque.",[11,4075,4076,4077,4080],{},"Keempat, ",[15,4078,4079],{},"decay is underrated",". Tanpa decay, sistem akan annoying. Dengan decay, sistem terasa hidup. Item lama mundur, item baru naik, tapi knowledge lama tidak hilang.",[11,4082,4083,4084,4087],{},"Kelima, ",[15,4085,4086],{},"quality gate wajib",". AI-generated draft tanpa gate itu seperti deploy tanpa test. Kadang lolos, kadang meledak. Untuk personal brand dan bisnis, jangan main-main.",[53,4089,4091],{"id":4090},"practical-implementation-checklist","Practical Implementation Checklist",[11,4093,4094],{},"Kalau mau build minggu ini, ini checklist yang paling realistis:",[61,4096,4099,4108,4118,4124,4130,4136,4142,4148,4154,4160,4166,4172],{"className":4097},[4098],"contains-task-list",[64,4100,4103,4107],{"className":4101},[4102],"task-list-item",[4104,4105],"input",{"disabled":358,"type":4106},"checkbox"," Tentukan 10-20 source awal",[64,4109,4111,4113,4114,4117],{"className":4110},[4102],[4104,4112],{"disabled":358,"type":4106}," Buat ",[221,4115,4116],{},"content_ideas"," table",[64,4119,4121,4123],{"className":4120},[4102],[4104,4122],{"disabled":358,"type":4106}," Tulis RSS\u002FGitHub scraper sederhana",[64,4125,4127,4129],{"className":4126},[4102],[4104,4128],{"disabled":358,"type":4106}," Tambahkan exact dedupe by URL\u002Fhash",[64,4131,4133,4135],{"className":4132},[4102],[4104,4134],{"disabled":358,"type":4106}," Tambahkan LLM enrichment JSON",[64,4137,4139,4141],{"className":4138},[4102],[4104,4140],{"disabled":358,"type":4106}," Implement scoring formula explainable",[64,4143,4145,4147],{"className":4144},[4102],[4104,4146],{"disabled":358,"type":4106}," Generate top 10 briefing harian",[64,4149,4151,4153],{"className":4150},[4102],[4104,4152],{"disabled":358,"type":4106}," Tambahkan approve\u002Freject\u002Fsave action",[64,4155,4157,4159],{"className":4156},[4102],[4104,4158],{"disabled":358,"type":4106}," Implement score decay setelah presented",[64,4161,4163,4165],{"className":4162},[4102],[4104,4164],{"disabled":358,"type":4106}," Buat production job queue untuk selected ideas",[64,4167,4169,4171],{"className":4168},[4102],[4104,4170],{"disabled":358,"type":4106}," Tambahkan quality gate sebelum publish",[64,4173,4175,4177],{"className":4174},[4102],[4104,4176],{"disabled":358,"type":4106}," Review feedback tiap minggu dan adjust bobot",[11,4179,4180],{},"Jangan tunggu sempurna. Pipeline yang sederhana tapi jalan setiap hari lebih valuable daripada arsitektur megah yang tidak pernah dipakai.",[53,4182,4184],{"id":4183},"closing-build-an-editorial-operating-system-not-a-content-gimmick","Closing: Build an Editorial Operating System, Not a Content Gimmick",[11,4186,4187],{},"AI-powered content pipeline bukan tentang mengganti penulis. Bukan juga tentang memproduksi artikel sebanyak mungkin sampai internet tambah penuh sampah.",[11,4189,4190,4191,18],{},"Buat saya, ini tentang membangun ",[15,4192,4193],{},"editorial operating system",[11,4195,4196],{},"Sistem yang menangkap sinyal dari internet, menyaring yang relevan, memberi prioritas, mengingat apa yang sudah pernah muncul, belajar dari pilihan manusia, lalu mengarahkan agent produksi dengan context yang cukup.",[11,4198,4199],{},"Mesin melakukan heavy lifting. Manusia menjaga taste.",[11,4201,4202],{},"Dan kombinasi itu jauh lebih kuat daripada keduanya berjalan sendiri-sendiri.",[11,4204,4205],{},"Kalau tim Anda punya banyak ide tapi sering kehilangan momentum, mulai dari pipeline kecil: scrape, store, score, brief, curate, delegate, decay. Itu saja dulu. Setelah rhythm-nya terbentuk, baru scale sources, bots, dan channels.",[11,4207,4208],{},"Karena content strategy yang bagus bukan cuma soal kreativitas. It is also about throughput, feedback loops, and system design.",[11,4210,4211],{},"Dan di era AI agents, tim yang menang bukan yang paling banyak prompt-nya. Tim yang menang adalah yang punya pipeline paling rapi.",[4213,4214,4215],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":219,"searchDepth":233,"depth":233,"links":4217},[4218,4219,4220,4221,4227,4228,4229,4237,4238,4239,4240,4241,4242,4243,4244,4245,4252,4253,4254],{"id":55,"depth":233,"text":56},{"id":155,"depth":233,"text":156},{"id":565,"depth":233,"text":566},{"id":866,"depth":233,"text":867,"children":4222},[4223,4224,4225,4226],{"id":877,"depth":239,"text":878},{"id":966,"depth":239,"text":967},{"id":1092,"depth":239,"text":1093},{"id":1280,"depth":239,"text":1281},{"id":1311,"depth":233,"text":1312},{"id":1627,"depth":233,"text":1628},{"id":1928,"depth":233,"text":1929,"children":4230},[4231,4232,4233,4234,4235,4236],{"id":1949,"depth":239,"text":1950},{"id":2118,"depth":239,"text":2119},{"id":2185,"depth":239,"text":2186},{"id":2233,"depth":239,"text":2234},{"id":2342,"depth":239,"text":2343},{"id":2412,"depth":239,"text":2413},{"id":2521,"depth":233,"text":2522},{"id":2863,"depth":233,"text":2864},{"id":3070,"depth":233,"text":3071},{"id":3359,"depth":233,"text":3360},{"id":3547,"depth":233,"text":3548},{"id":3658,"depth":233,"text":3659},{"id":3817,"depth":233,"text":3818},{"id":3934,"depth":233,"text":3935},{"id":4006,"depth":233,"text":4007,"children":4246},[4247,4248,4249,4250,4251],{"id":4013,"depth":239,"text":4014},{"id":4020,"depth":239,"text":4021},{"id":4027,"depth":239,"text":4028},{"id":4034,"depth":239,"text":4035},{"id":4041,"depth":239,"text":4042},{"id":4048,"depth":233,"text":4049},{"id":4090,"depth":233,"text":4091},{"id":4183,"depth":233,"text":4184},"tech","2026-06-08","Cara membangun pipeline discovery dan curation konten berbasis AI agents, mulai dari scraping multi-source, scoring, morning briefing, human curation, sampai delegasi produksi konten otomatis.","md","\u002Fimages\u002Fposts\u002Fai-content-pipeline-hero.jpg",{"owner":4259,"ogImage":4259,"readTime":4261},"18 min","\u002Ftech\u002Fai-content-pipeline",null,{"title":5,"description":4257},"tech\u002Fai-content-pipeline",[4267,4268,4269,906,3556],"ai","automation","content-strategy","79uHKVrydZKwu9xRahqxiK0mVR4lGHE5Kp-3ob2_2fU",[4272,4284,4286,4299,4309,4317,4326,4335,4347,4356,4369,4382,4393,4405,4418,4429,4440,4452,4462,4475,4487,4499,4512,4524,4536,4549,4559,4570,4580,4589,4600,4610,4619,4626,4638,4648,4658,4668,4677,4687,4699,4707,4716,4725,4737,4747,4755,4764,4776,4787,4795,4806,4815,4823,4833,4841,4849,4858,4866,4874,4882,4893,4902,4909,4917,4926,4934,4942,4950,4958,4966,4975,4984,4991,5000,5008,5015,5023,5031,5038,5047,5054,5061,5068,5077,5083,5092,5098,5105,5114,5121,5130],{"path":4273,"title":4274,"description":4275,"date":4276,"category":4255,"tags":4277,"image":4282,"readingTime":4283},"\u002Ftech\u002Fopenclaw-hermes-daily-work-harness","AI Agent Buat Daily Work Harus Punya Repair Harness","Email, meeting, dokumen, task, report — AI agent bukan cuma harus bisa kerja. Dia harus bisa dites, diperbaiki, dan diaudit.","2026-06-08T08:00:00+08:00",[3556,4278,3560,4279,4268,4280,4281],"hermes","business-workflow","daily-work","harness","\u002Fimages\u002Fposts\u002Fopenclaw-hermes-daily-work-harness-xiaohei.webp","6",{"path":4262,"title":5,"description":4257,"date":4256,"category":4255,"tags":4285,"image":4259,"readingTime":4263},[4267,4268,4269,906,3556],{"path":4287,"title":4288,"description":4289,"date":4290,"category":4255,"tags":4291,"image":4297,"readingTime":4298},"\u002Ftech\u002Fsubscription-hunter-ai","Personal AI Use Case: Subscription Hunter","Lo subscribe Netflix 3 platform, Spotify, iCloud, VPN, SaaS — berapa yang beneran lo pake? Agent audit + rekomendasi cancel.","2026-06-07T12:00:00+08:00",[3556,4292,4293,4268,4294,4295,4296],"personal-ai","subscription","finance","gmail","agent","\u002Fimages\u002Fposts\u002Fsubscription-hunter-ai-xiaohei.webp","3",{"path":4300,"title":4301,"description":4302,"date":4303,"category":4255,"tags":4304,"image":4307,"readingTime":4308},"\u002Ftech\u002Fidea-catcher-ai","Personal AI Use Case: Idea Catcher","Rekam voice note 30 detik, AI transcribe, auto-tag, jadi knowledge base. Nggak ada ide ilang.","2026-06-07T11:30:00+08:00",[3556,4278,4305,4292,4306],"voice-notes","idea-capture","\u002Fimages\u002Fposts\u002Fidea-catcher-ai-xiaohei.webp","4",{"path":4310,"title":4311,"description":4312,"date":4313,"category":4255,"tags":4314,"image":4316,"readingTime":4308},"\u002Ftech\u002Fexpense-tracker-ai","Personal AI Use Case: Expense Tracker via Chat","Nggak perlu app ribet. Kirim foto struk ke Telegram bot, AI auto-catet, categorize, laporan bulanan.","2026-06-07T11:00:00+08:00",[3556,4278,4315,4292,4268],"expense-tracker","\u002Fimages\u002Fposts\u002Fexpense-tracker-ai-xiaohei.webp",{"path":4318,"title":4319,"description":4320,"date":4321,"category":4255,"tags":4322,"image":4325,"readingTime":4298},"\u002Ftech\u002Fwa-chat-summarizer","Personal AI Use Case: WhatsApp Chat Summarizer","Group chat penuh drama dan stiker? Biarin AI aja yang baca.","2026-06-07T10:30:00+08:00",[3556,4323,4292,4268,4324],"whatsapp","chat-summary","\u002Fimages\u002Fposts\u002Fwa-chat-summarizer-xiaohei.webp",{"path":4327,"title":4328,"description":4329,"date":4330,"category":4255,"tags":4331,"image":4333,"readingTime":4334},"\u002Ftech\u002Fai-agent-self-maintaining","AI yang Bisa Mengurus Dirinya Sendiri","Use case OpenClaw yang agak aneh tapi powerful — agent yang bisa menjaga memory, healthcheck, skills, git sync, dan reminder-nya sendiri tanpa nunggu manusia panik duluan.","2026-06-07T07:30:00+08:00",[3556,4278,4292,4332,4268],"private-ai","\u002Fimages\u002Fposts\u002Fai-agent-self-maintaining-xiaohei.webp","7",{"path":4336,"title":4337,"description":4338,"date":4339,"category":4255,"tags":4340,"image":4345,"readingTime":4346},"\u002Ftech\u002Ffinance-tracker-mutasi-gmail-openclaw-hermes","Finance Tracker dari Mutasi Gmail — Workaround Banking API Pakai OpenClaw dan Hermes","Cara membangun finance tracker pribadi dari email mutasi bank seperti BCA, tanpa upload data ke aplikasi budgeting, memakai OpenClaw\u002FHermes sebagai agent lokal untuk recap, kategorisasi, dan insight pengeluaran.","2026-06-06 18:45:00+08:00",[3556,4278,4341,4342,4295,4268,4343,4344],"finance-tracker","personal-finance","ai-agent","privacy","\u002Fimages\u002Fposts\u002Ffinance-tracker-mutasi-gmail-openclaw-hermes-xiaohei.webp","11",{"path":4348,"title":4349,"description":4350,"date":4351,"category":4255,"tags":4352,"image":4355,"readingTime":4334},"\u002Ftech\u002Fai-pribadi-agent-bisa-diaudit","AI Pribadi Jangan Jadi Black Box — Agent Harus Bisa Diaudit","Kenapa workflow AI pribadi butuh agent yang punya jejak kerja, sumber jelas, dan bisa diperiksa — bukan cuma chatbot yang pintar menjawab.","2026-06-06 05:30:00+08:00",[4267,4292,4353,4354,4343,4268],"agentic-workflow","audit-trail","\u002Fimages\u002Fposts\u002Fai-pribadi-agent-bisa-diaudit-xiaohei.webp",{"path":4357,"title":4358,"description":4359,"date":4360,"category":4255,"tags":4361,"image":4367,"readingTime":4368},"\u002Ftech\u002Fbookmarks-operating-system-decisions","Bookmark Bukan Reading List — Tapi Operating System untuk Keputusan","Saya berhenti memperlakukan bookmark sebagai daftar bacaan. Sekarang bookmark jadi sinyal untuk morning brief, build radar, dan ide konten.","2026-06-03 22:10:00+08:00",[4362,4363,4364,3556,4268,4365,4366,4343],"bookmarks","decision-system","hermes-agent","sqlite","tailscale","\u002Fimages\u002Fposts\u002Fbookmarks-operating-system-decisions-xiaohei.webp","10",{"path":4370,"title":4371,"description":4372,"date":4373,"category":4255,"tags":4374,"image":4380,"readingTime":4381},"\u002Ftech\u002Fsetup-scraping-instagram-tiktok-threads-hermes-browser-tailscale","Tutorial: Scraping Instagram, TikTok, Threads dengan OpenClaw, Hermes + Browser + Tailscale","Panduan lengkap scraping sosial media dengan browser login persisten via CDP. Bisa pakai OpenClaw atau Hermes Agent sebagai operator browser. Akses aman lewat Tailscale.","2026-05-30",[3556,4364,4375,4366,4376,4377,4378,4379],"browser-automation","scraping","instagram","tiktok","threads","\u002Fimages\u002Fposts\u002Fsetup-scraping-instagram-tiktok-threads-hermes-browser-tailscale-xiaohei.webp","15",{"path":4383,"title":4384,"description":4385,"date":4386,"category":4255,"tags":4387,"image":4392,"readingTime":4308},"\u002Ftech\u002Fbalikpapan-openclaw-meetup-ai-automation","AI Automation Mulai Dilirik Bisnis Balikpapan","Pelaku usaha Balikpapan mulai melihat AI agent dan workflow automation sebagai cara mempercepat kerja, merapikan proses, dan menambah kapasitas operasional.","2026-05-22",[3556,4388,4343,4389,4390,4391],"ai-automation","balikpapan","growthcircle","radian-group","\u002Fimages\u002Fposts\u002Fbalikpapan-openclaw-meetup-ai-automation-image1.png",{"path":4394,"title":4395,"description":4396,"date":4397,"category":4255,"tags":4398,"image":4403,"readingTime":4404},"\u002Ftech\u002Fagentic-ai-strategic-thinking-owner","Agentic AI untuk Strategic Thinking Owner — Bukan Chatbot, Tapi Partner Mikir","Cara owner bisnis memakai agentic AI seperti OpenClaw sebagai strategic thinking assistant yang bisa diakses dari web app, WhatsApp, Telegram, dan dashboard.","2026-05-13 13:30:00+08:00",[3556,4399,4400,4401,4402,4268,4343],"agentic-ai","strategic-thinking","owner","whatsapp-bot","\u002Fimages\u002Fposts\u002Fagentic-ai-strategic-thinking-owner.webp","12",{"path":4406,"title":4407,"description":4408,"date":4409,"category":4255,"tags":4410,"image":4416,"readingTime":4417},"\u002Ftech\u002Fopenclaw-apartment-maintenance","OpenClaw untuk Apartment Maintenance: Dari Komplain WhatsApp Jadi Ticket yang Rapi","Tutorial campur Indonesia dan English buat bikin workflow apartment maintenance dengan OpenClaw: intake WhatsApp, ticket routing, SLA reminder, technician.","2026-05-11 17:35:00+08:00",[3556,4411,4323,4412,4413,4414,4415],"apartment-maintenance","property-management","facility-management","ticketing","sumopod","\u002Fimages\u002Fposts\u002Fopenclaw-apartment-maintenance-day.webp","13",{"path":4419,"title":4420,"description":4421,"date":4422,"category":4255,"tags":4423,"image":4428,"readingTime":4417},"\u002Ftech\u002Fopenclaw-water-tank-monitoring","OpenClaw untuk Monitoring Tandon Air dan Pompa: Alert WhatsApp yang Beneran Kepakai","Tutorial campur Indonesia dan English buat pakai OpenClaw sebagai operational layer untuk monitoring tandon air, pompa, level sensor, low-level alert,.","2026-05-11 17:30:00+08:00",[3556,4424,4425,4323,4426,4413,4427,4415],"water-tank","pump-monitoring","iot","apartment","\u002Fimages\u002Fposts\u002Fopenclaw-water-tank-day.webp",{"path":4430,"title":4431,"description":4432,"date":4433,"category":4255,"tags":4434,"image":4439,"readingTime":4417},"\u002Ftech\u002Fopenclaw-genset-ats-monitoring","OpenClaw untuk Monitoring Genset dan ATS? Ini Salah Satu Use Case Paling Masuk Akal","Tutorial campur Indonesia dan English buat pakai OpenClaw untuk monitoring genset dan ATS, alarm via WhatsApp, cloud logging, role access, dan.","2026-05-11 17:25:00+08:00",[3556,4435,4436,4323,4437,4438,4415],"genset","ats","industrial-monitoring","building-operations","\u002Fimages\u002Fposts\u002Fopenclaw-genset-ats-day.webp",{"path":4441,"title":4442,"description":4443,"date":4444,"category":4255,"tags":4445,"image":4451,"readingTime":4368},"\u002Ftech\u002Fopenclaw-kost-whatsapp-billing","OpenClaw Buat Sistem Kos-Kosan via WhatsApp? Dari Tanya Harga sampai Tagihan dan QRIS Bisa Jalan","Tutorial campur Indonesia dan English untuk bikin sistem kos-kosan dengan backend OpenClaw. Satu bot WhatsApp untuk tanya harga, fasilitas, reminder jatuh.","2026-05-11 15:25:00+08:00",[3556,4446,4323,4447,4448,4449,4450,4415],"kos-kosan","billing","qris","payment-gateway","property-automation","\u002Fimages\u002Fposts\u002Fopenclaw-kost-whatsapp-day.webp",{"path":4453,"title":4454,"description":4455,"date":4456,"category":4255,"tags":4457,"image":4461,"readingTime":4346},"\u002Ftech\u002Fopenclaw-iot-port-lighting-whatsapp","OpenClaw Buat Backbone IoT Lampu Pelabuhan? Bisa Banget. Monitor, Kontrol, dan Alert via WhatsApp","Tutorial campur Indonesia dan English buat pakai OpenClaw sebagai backbone IoT sederhana untuk monitor lampu pelabuhan, power usage, kontrol ON\u002FOFF via.","2026-05-11 13:20:00+08:00",[3556,4426,4323,4458,4459,4460,4415],"port-lighting","energy-monitoring","industrial-automation","\u002Fimages\u002Fposts\u002Fopenclaw-iot-port-lighting-day.webp",{"path":4463,"title":4464,"description":4465,"date":4466,"category":4255,"tags":4467,"image":4473,"readingTime":4474},"\u002Ftech\u002Fopenclaw-backup-reset-restore","OpenClaw VM Berantakan? Cara Backup ke GitHub, Reset VPS, lalu Restore Rapi Tanpa Kehilangan History","Guide campur Indonesia dan English buat backup file penting OpenClaw ke GitHub, reset VPS, dan restore lagi ke satu workspace yang rapi tanpa kehilangan.","2026-05-11 10:55:00+08:00",[3556,4468,4469,4470,4471,4472,4415],"vps","github-backup","restore","workspace","self-hosted-ai","\u002Fimages\u002Fposts\u002Fopenclaw-vm-backup-restore-day.webp","9",{"path":4476,"title":4477,"description":4478,"date":4479,"category":4255,"tags":4480,"image":4486,"readingTime":4283},"\u002Ftech\u002Fssh-terminal-dalam-dashboard","SSH Terminal di Browser dengan AI Chat? Radit Dashboard Punya.","Gak perlu PuTTY lagi. SSH langsung dari browser, dan kalau outputnya gak ngerti, AI-nya siap bantu jelasin. VPS recommendation via SUMOPOD.","2026-04-29",[4481,4482,4468,4483,4267,4484,4485],"ssh","terminal","browser","radit-dashboard","devops","\u002Fimages\u002Fposts\u002Fssh-terminal-dalam-dashboard.png",{"path":4488,"title":4489,"description":4490,"date":4491,"category":4255,"tags":4492,"image":4498,"readingTime":4346},"\u002Ftech\u002Fopenclaw-2026-4-24-broken-downgrade","OpenClaw v2026.4.24 Broken — Downgrade Sekarang","Rilis OpenClaw terbaru v2026.4.24 malah jadi bencana. Gateway crash loop, semua channel mati, dan yang bikin frustrasi: status bilang 'healthy' padahal bot.","2026-04-26 09:30:00+08:00",[3556,4493,4494,4495,4496,4497,4468,4268],"ai-assistant","troubleshooting","downgrade","gateway","telegram-bot","\u002Fimages\u002Fposts\u002Fopenclaw-2026-4-24-broken-hero-og.jpg",{"path":4500,"title":4501,"description":4502,"date":4503,"category":4255,"tags":4504,"image":4511,"readingTime":4474},"\u002Ftech\u002Fgoogle-skills-openclaw","google\u002Fskills buat OpenClaw, emang nyambung? Nyambung, kalau kamu manage-nya waras","Tutorial praktis pakai repo google\u002Fskills sebagai bahan baku skill library untuk OpenClaw. Bukan copy-paste buta, tapi review, adapt, manage, dan publish.","2026-04-25 13:55:00+08:00",[4505,3556,4506,4507,4508,4509,4510],"google-skills","google-cloud","gemini","cloud-run","bigquery","skill-management","\u002Fimages\u002Fposts\u002Fgoogle-skills-openclaw-cover-day.webp",{"path":4513,"title":4514,"description":4515,"date":4516,"category":4255,"tags":4517,"image":4523,"readingTime":4381},"\u002Ftech\u002Fgmail-ai-draft-real-voice","Cara Bikin AI Draft Email yang Nulis Pakai Gaya Kita, Bukan Gaya Robot","Tutorial practical untuk bikin workflow draft email AI yang bisa filter inbox, baca tone thread, pakai gaya email kita sendiri, bikin draft di Gmail, lalu.","2026-04-24 20:55:00+08:00",[4295,4518,4519,4520,3556,4521,4522],"ai-draft","email-automation","gog-cli","telegram","workflow","\u002Fimages\u002Fposts\u002Fgmail-ai-draft-real-voice-cover-gpt-image-2.webp",{"path":4525,"title":4526,"description":4527,"date":4528,"category":4255,"tags":4529,"image":4534,"readingTime":4535},"\u002Ftech\u002Fqwenpaw-sumopod-growth-circle","QwenPaw di Sumopod + Custom Provider Growth Circle, Cara Paling Cepat Punya AI Assistant yang Beneran Kepake","Tutorial lengkap pakai QwenPaw di Sumopod, masukin custom provider Growth Circle, tambah model gratis, sambungin Telegram, dan paham setup-nya tanpa ribet.","2026-04-24 08:45:00+08:00",[4530,4415,4531,4493,4521,4532,4533],"qwenpaw","growth-circle","custom-provider","openai-compatible","\u002Fimages\u002Fposts\u002Fqwenpaw-sumopod-growth-circle-cover-day.webp","20",{"path":4537,"title":4538,"description":4539,"date":4540,"category":4255,"tags":4541,"image":4548,"readingTime":4404},"\u002Ftech\u002Fthe-private-knowledge-os-second-brain-rag-hybrid-retrieval","I Built a Second Brain That Actually Remembers Everything","How to build a private RAG system with hybrid retrieval (vector + BM25), cross-encoder reranking, and production-grade security. Step-by-step guide.","2026-04-19",[4542,4543,4544,4545,4546,906,4547],"second-brain","rag","llm","knowledge-management","openai","retrieval","\u002Fimages\u002Fposts\u002Fbuild-blog.webp",{"path":4550,"title":4551,"description":4552,"date":4553,"category":4255,"tags":4554,"image":4558,"readingTime":4368},"\u002Ftech\u002Fwhatsapp-ai-agent-school-business","WhatsApp AI Agent untuk Sekolah dan Bisnis: Automasi yang Nggak Bikin Pusing","Begini caranya sekolah dan bisnis di Indonesia bisa automasi komunikasi lewat WhatsApp pakai AI agent. Tanpa app tambahan, tanpa login dashboard yang ribet.","2026-04-17",[4323,4343,4555,4556,4557,3556],"sekolah","bisnis","automasi","\u002Fimages\u002Fposts\u002Fwhatsapp-ai-agent-school-business.jpg",{"path":4560,"title":4561,"description":4562,"date":4563,"category":4255,"tags":4564,"image":4568,"readingTime":4569},"\u002Ftech\u002Ftoken-efficient-ai-agent","Hemat 70% Token AI Agent: Context Tiering & Lean Loading","Gimana cara cut token usage AI agent dari $2,130 jadi $732 per bulan tanpa lost functionality. Real benchmark data inside.","2026-04-15T16:35:00",[3556,4343,4565,4566,4567],"token-optimization","cost-saving","context-management","\u002Fimages\u002Fposts\u002Ftoken-efficient-ai-agent.webp","5",{"path":4571,"title":4572,"description":4573,"date":4574,"category":4255,"tags":4575,"image":4579,"readingTime":4334},"\u002Ftech\u002Fopenclaw-multi-account-routing","Tutorial: Multi-Account Routing dengan OpenClaw","Panduan lengkap OpenClaw Multi-Account Routing. Jalankan multiple AI persona dengan multiple akun subscription tanpa bayar lebih. Bahasa Indonesia dan English.","2026-04-15T10:30:00+08:00",[3556,4576,4577,4578,3560],"multi-account","routing","multi-agent","\u002Fimages\u002Fposts\u002Fopenclaw-multi-account-routing.webp",{"path":4581,"title":4582,"description":4583,"date":4584,"category":4255,"tags":4585,"image":4588,"readingTime":4404},"\u002Ftech\u002Fopenclaw-cron-job-automation","Tutorial: Cron Job Automation dengan OpenClaw","Panduan lengkap OpenClaw Cron Jobs. Schedule tasks, reports, dan alerts yang jalan otomatis. Campuran Bahasa Indonesia dan English.","2026-04-15T10:15:00+08:00",[3556,4586,4268,4587,4343],"cron","scheduled-tasks","\u002Fimages\u002Fposts\u002Fopenclaw-cron-job-automation.jpg",{"path":4590,"title":4591,"description":4592,"date":4593,"category":4255,"tags":4594,"image":4599,"readingTime":4368},"\u002Ftech\u002Fopenclaw-channel-integration","Tutorial: Konekin Telegram, WhatsApp, dan Discord ke AI Assistant Kamu","Panduan lengkap connect tiga chat platform ke OpenClaw Gateway sekaligus. Telegram, WhatsApp, dan Discord dalam satu panduan, Bahasa Indonesia campur English.","2026-04-15T10:00:00+08:00",[3556,4521,4323,4595,4596,4597,4598],"discord","channel","integration","chatbot","\u002Fimages\u002Fposts\u002Fopenclaw-channel-integration.webp",{"path":4601,"title":4602,"description":4603,"date":4604,"category":4255,"tags":4605,"image":4608,"readingTime":4609},"\u002Ftech\u002Fopenclaw-gateway-setup","Tutorial: Install OpenClaw Gateway dari Nol dalam 10 Menit","Panduan lengkap install dan setup OpenClaw Gateway dari awal. Dari instalasi sampai bisa chat via Telegram. Bahasa Indonesia campur English, easy to follow.","2026-04-15T09:00:00+08:00",[3556,4496,4606,4607,4521,4468,4268],"installation","setup","\u002Fimages\u002Fposts\u002Fopenclaw-gateway-setup.jpg","8",{"path":4611,"title":4612,"description":4613,"date":4614,"category":4255,"tags":4615,"image":4618,"readingTime":4417},"\u002Ftech\u002Fopenclaw-troubleshooting-guide","Tutorial: OpenClaw Troubleshooting Guide Lengkap","Panduan troubleshooting OpenClaw dari A sampai Z. Gateway crash, channel error, model rate limit, memory overflow, Docker issue, dan masih banyak lagi..","2026-04-15 11:00:00+08:00",[3556,4494,4496,4596,4616,4617],"error","fix","\u002Fimages\u002Fposts\u002Fopenclaw-troubleshooting-guide.webp",{"path":4620,"title":4621,"description":4622,"date":4623,"category":4255,"tags":4624,"image":4625,"readingTime":4346},"\u002Ftech\u002Fopenclaw-browser-automation","Tutorial: Browser Control Automation dengan OpenClaw","Panduan lengkap OpenClaw Browser Control. AI assistant bisa buka website, klik tombol, isi form, screenshot, dan extract data. Campuran Bahasa Indonesia dan.","2026-04-15 10:45:00+08:00",[3556,4483,4268,4376,4343],"\u002Fimages\u002Fposts\u002Fopenclaw-browser-automation.webp",{"path":4627,"title":4628,"description":4629,"date":4630,"category":4255,"tags":4631,"image":4637,"readingTime":4346},"\u002Ftech\u002Fopenclaw-mcp-server-setup","Tutorial: Konekin AI Assistant ke Google Workspace, Notion, GitHub, dan 100+ Tools Lainnya","Panduan lengkap setup MCP Server di OpenClaw. Connect Gmail, Google Calendar, Notion, GitHub, Slack dan tools lain ke AI assistant kamu. Bahasa Indonesia.","2026-04-15 09:30:00+08:00",[3556,4632,4597,4633,4634,4635,4636],"mcp","google-workspace","notion","github","tools","\u002Fimages\u002Fposts\u002Fopenclaw-mcp-server-setup.jpg",{"path":4639,"title":4640,"description":4641,"date":4642,"category":4255,"tags":4643,"image":4647,"readingTime":4334},"\u002Ftech\u002Fopenclaw-session-maintenance","Tutorial Lengkap: Cara Bersihkan Session OpenClaw yang Berantakan","Panduan lengkap maintenance session OpenClaw. Cleanup, compaction, pruning, orphan sessions, semuanya dibahas lengkap. Bahasa Indonesia campur English.","2026-04-14T08:00:00+08:00",[3556,4644,4645,4646,4268],"session","maintenance","cleanup","\u002Fimages\u002Fposts\u002Fopenclaw-session-maintenance.jpg",{"path":4649,"title":4650,"description":4651,"date":4652,"category":4255,"tags":4653,"image":4657,"readingTime":4474},"\u002Ftech\u002Fopenclaw-security-hardening","Tutorial Lengkap: Amankan OpenClaw Kamu dengan Security Hardening Checklist","Panduan lengkap security hardening untuk OpenClaw Gateway. Lindungi bot kamu dari akses tidak sah dengan checklist 15 langkah. Bahasa Indonesia campur English.","2026-04-14 09:00:00+08:00",[3556,4654,4655,4656,4468,4268],"security","hardening","checklist","\u002Fimages\u002Fposts\u002Fopenclaw-security-hardening.jpg",{"path":4659,"title":4660,"description":4661,"date":4662,"category":4255,"tags":4663,"image":4667,"readingTime":4569},"\u002Ftech\u002Fwhatsapp-customer-care-umkm","WhatsApp Jadi Customer Care 24\u002F7 — Solusi Cerdas untuk UMKM Indonesia","Tutorial lengkap bikin bot WhatsApp otomatis untuk usaha kecil menengah. Catalog, harga, order, semua otomatis. Bahasa Indonesiacampuran English.","2026-04-14",[4323,4664,4268,4665,4666],"customer-care","umkm","whatsapp-business","\u002Fimages\u002Fposts\u002Fwhatsapp-customer-care-umkm.jpg",{"path":4669,"title":4670,"description":4671,"date":4662,"category":4255,"tags":4672,"image":4676,"readingTime":4308},"\u002Ftech\u002Fwordpress-security-scanner","WordPress Security Scanner — Auto Deteksi & Bersihkan Malware","Tutorial bikin scanner otomatis untuk WordPress. Deteksi backdoor, redirect ke judol\u002Fslot, SEO spam injection, dan cleanup otomatis dengan satu command.",[4673,4654,4674,4675,4268,3556],"wordpress","malware","scanner","\u002Fimages\u002Fposts\u002Fwordpress-security-scanner.jpg",{"path":4678,"title":4679,"description":4680,"date":4681,"category":4255,"tags":4682,"image":4686,"readingTime":4308},"\u002Ftech\u002Ffile-search-knowledge-base-karpathy","Bangun Knowledge Base dari Dokumen Legal — Cara Karpathy","Tutorial lengkap bikin file search knowledge base dari PDF dokumen legal. Regex extraction + RAG scoring + LLM. Ala Andrej Karpathy.","2026-04-09",[3556,4683,4543,4684,4493,4685],"knowledge-base","karpathy","legal","\u002Fimages\u002Fposts\u002Ffile-search-kb-karpathy.webp",{"path":4688,"title":4689,"description":4690,"date":4691,"category":4255,"tags":4692,"image":4698,"readingTime":4569},"\u002Ftech\u002Fsimpen-bookmark-manager","Self-Hosted Bookmark Manager dengan Custom Branding via Nginx","Tutorial setup Karakeep bookmark manager self-hosted dengan Docker, nginx reverse proxy, dan custom branding tanpa edit source code","2026-04-07",[4693,4694,4695,4696,4697],"self-hosted","docker","nginx","bookmark","karakeep","\u002Fimages\u002Fposts\u002Fsimpen-bookmark-manager.webp",{"path":4700,"title":4701,"description":4702,"date":4703,"category":4255,"tags":4704,"image":4706,"readingTime":4334},"\u002Ftech\u002Ftailscale-vpn-exit-node","Tailscale VPN di VPS — Bypass Blokiran Internet dengan Mudah","Tutorial setup Tailscale sebagai exit node di VPS. Akses Reddit, situs yang diblokir, dan tingkatkan keamanan internet kamu.","2026-04-06",[4705,4366,4468,4654,4415],"vpn","\u002Fimages\u002Fposts\u002Ftailscale-vpn-exit-node.jpg",{"path":4708,"title":4709,"description":4710,"date":4711,"category":4255,"tags":4712,"image":4715,"readingTime":4334},"\u002Ftech\u002Fsistem-komunikasi-perumahan-bot-whatsapp","Sistem Komunikasi Cluster Perumahan dengan Bot WhatsApp — Otomatis, 24 Jam, Tanpa Drama","Bayangkan perumahan tanpa drama. Bot WhatsApp yang handle tamu, paket, komplain, iuran, dan pengumuman — 24\u002F7 tanpa manusia. Ini bukan mimpi, ini sudah bisa.","2026-04-05T16:00:00+08:00",[3556,4323,4713,4268,4714],"smart-home","perumahan","\u002Fimages\u002Fposts\u002Fhousing-header.webp",{"path":4717,"title":4718,"description":4719,"date":4720,"category":4255,"tags":4721,"image":4724,"readingTime":4368},"\u002Ftech\u002Fopenclaw-advance-marketing-system","OpenClaw Advance Marketing System: Bangun Mesin Iklan & Closing Otomatis 24 Jam","Review lengkap kelas OpenClaw Advance Marketing System oleh Ari Eko Prasethio & Rama Aditya. Pelajari cara membangun sistem marketing otomatis — dari.","2026-04-05T11:00:00+08:00",[3556,4267,4722,4268,4723],"marketing","kursus","\u002Fimages\u002Fposts\u002Fopenclaw-advance-marketing.jpg",{"path":4726,"title":4727,"description":4728,"date":4729,"category":4255,"tags":4730,"image":4735,"readingTime":4736},"\u002Ftech\u002Fcircutor-scout-iot-monitoring","Monitoring Energi Industri dengan IoT: Panduan Lengkap Circutor Scout + OpenClaw Dashboard","Panduan lengkap implementasi monitoring energi industri pakai Circutor Scout Platform + IoT system. Dari install CVM energy meter sampai custom dashboard.","2026-04-05 15:30:00",[4731,4426,4459,4732,4733,3556,4734,4268],"circutor","industrial","modbus","dashboard","\u002Fimages\u002Fposts\u002Fcircutor-header.jpg","22",{"path":4738,"title":4739,"description":4740,"date":4741,"category":4255,"tags":4742,"image":4746,"readingTime":4283},"\u002Ftech\u002Fattendance-story-driven-tracking","Story-Driven Attendance Tracking: How I Built a Smart System for My Engineering Team","Dari absensi manual ke sistem GPS otomatis — bagaimana saya mengubah cara tim engineering saya mencatat kehadiran dengan OpenClaw dan Android app.","2026-04-04 19:18:00",[3556,4743,4744,4268,4745],"hr-tech","engineering","android","\u002Fimages\u002Fposts\u002Fattendance-story-hero.jpg",{"path":4748,"title":4749,"description":4750,"date":4751,"category":4255,"tags":4752,"image":4754,"readingTime":4417},"\u002Ftech\u002Fopenclaw-cs-whatsapp-gateway","OpenClaw sebagai CS Otomatis — Arsitektur WhatsApp Gateway, Invoice & Database Strict","Bikin customer service bot WhatsApp dengan OpenClaw sebagai otak, gateway sebagai jembatan, dan database strict yang nggak bocor. Panduan lengkap dari.","2026-04-04 14:30:00",[3556,4323,4753,4268,4496,4543,4654],"customer-service","\u002Fimages\u002Fposts\u002Fcs-wa-architecture.jpg",{"path":4756,"title":4757,"description":4758,"date":4759,"category":4255,"tags":4760,"image":4763,"readingTime":4404},"\u002Ftech\u002Findustrial-energy-monitoring","Monitoring Listrik Industri: Cara Hemat Jutaan dari Motor, HVAC & PLC","Harga bahan bakar industri naik terus. Motor listrik menghabiskan 60-70% energi pabrik. Ini panduan lengkap monitoring sistem listrik industri — dari CT.","2026-04-04 14:14:00",[4732,4761,4762,4268,3556,4426],"monitoring","energy","\u002Fimages\u002Fposts\u002Findustrial-monitoring-header-og.jpg",{"path":4765,"title":4766,"description":4767,"date":4768,"category":4255,"tags":4769,"image":4774,"readingTime":4775},"\u002Ftech\u002Fopenclaw-smart-hotel","OpenClaw × Smart Hotel — Integrasi BAS, AI Concierge, dan Green Rewards","Tutorial lengkap implementasi OpenClaw sebagai smart hotel assistant: integrasi BAS (Building Automation System), AI concierge via WhatsApp, WiFi login.","2026-04-04 14:13:00",[3556,4770,4771,4426,4772,4773],"smart-hotel","bas","ai-concierge","building-automation","\u002Fimages\u002Fposts\u002Fsmart-hotel-header-og.jpg","21",{"path":4777,"title":4778,"description":4779,"date":4780,"category":4255,"tags":4781,"image":4785,"readingTime":4786},"\u002Ftech\u002Fopenclaw-llm-provider-guide","Panduan Lengkap Pilih LLM Provider untuk OpenClaw — Dari Personal Sampai 1 Juta User","Perbandingan lengkap LLM provider 2026: OpenAI, Anthropic, Google, DeepSeek, lokal. Rate limit, harga, Mac Mini vs VPS, multi-provider setup di OpenClaw,.","2026-04-04 14:12:00",[3556,4544,4267,4782,4468,4783,4268,4784],"provider","cost","local-llm","\u002Fimages\u002Fposts\u002Fopenclaw-llm-provider-guide.jpg","16",{"path":4788,"title":4789,"description":4790,"date":4791,"category":4255,"tags":4792,"image":4794,"readingTime":4736},"\u002Ftech\u002Fopenclaw-skill-ecosystem","Membangun Ekosistem AI Agent Skill dari 15+ GitHub Repos — 324 Skill dalam Satu Hari","Bagaimana gue menganalisis 15+ repository dengan total 500K+ stars, menyeleksi yang terbaik, dan mengintegrasikannya jadi satu ekosistem skill buat AI agent.","2026-04-04 14:11:00",[3556,4343,4793,4635,4268,4744,4578],"skills","\u002Fimages\u002Fposts\u002Fopenclaw-skill-ecosystem.webp",{"path":4796,"title":4797,"description":4798,"date":4799,"category":4255,"tags":4800,"image":4805,"readingTime":4346},"\u002Ftech\u002Fopenclaw-2026-4-2","OpenClaw 2026.4.2 — Task Flow Kembali, YOLO Mode Default, dan 2 Breaking Changes yang Wajib Lo Tahu","Review lengkap OpenClaw 2026.4.2: Task Flow restoration dengan managed\u002Fmirrored sync, YOLO mode jadi default, breaking changes xAI & Firecrawl, plus.","2026-04-04 14:10:00",[3556,4801,4802,4343,4654,4268,4803,4804],"update","task-flow","yolo-mode","breaking-changes","\u002Fimages\u002Fposts\u002Fopenclaw-2026-4-2.jpg",{"path":4807,"title":4808,"description":4809,"date":4810,"category":4255,"tags":4811,"image":4814,"readingTime":4569},"\u002Ftech\u002Fopenclaw-ops-self-healing","OpenClaw Ops — Bikin Gateway Kamu Self-Healing Setelah Update","OpenClaw tiap update suka break sesuatu. Auth ke-reset, exec approval nge-blok, cron job di-disable otomatis. Ini layer ops yang bikin gateway kamu fix diri.","2026-04-03 08:00:00+08:00",[3556,4812,4813,4654,4268],"ops","self-healing","\u002Fimages\u002Fposts\u002Fopenclaw-ops-self-healing-og.jpg",{"path":4816,"title":4817,"description":4818,"date":4819,"category":4255,"tags":4820,"image":4822,"readingTime":4609},"\u002Ftech\u002Fai-agent-dashboard-openclaw-sumopod-vps","Build AI Agent Dashboard dengan OpenClaw + Sumopod VPS","Tutorial lengkap bikin AI agent yang jalan 24\u002F7, terhubung ke Telegram, email, calendar, dan bikin dashboard real-time pakai Next.js.","2026-04-03",[4343,3556,4415,4497,4734,4821,4268],"n8n","\u002Fimages\u002Fposts\u002Fai-agent-dashboard-og.jpg",{"path":4824,"title":4825,"description":4826,"date":4819,"category":4255,"tags":4827,"image":4832,"readingTime":4298},"\u002Ftech\u002Fbuild-blog-nuxt-content-tailwind","Bikin Blog Statis dengan Nuxt Content — Dari Nol Sampai Live","Tired of Medium dan WordPress? Bikin blog sendiri dengan Nuxt Content + Tailwind CSS. Markdown-based, dark mode, search, RSS — semuanya gratis dan kamu yang.",[4828,4829,4830,4831],"nuxt","blog","tailwind","tutorial","\u002Fimages\u002Fposts\u002Fbuild-blog-og.jpg",{"path":4834,"title":4835,"description":4836,"date":4819,"category":4255,"tags":4837,"image":4840,"readingTime":4298},"\u002Ftech\u002Fdark-mode-search-nuxt-github-libraries","Dark Mode & Search di Nuxt 3? Nggak Perlu Ribet — Cukup Tambah 2 Script","Mau pasang dark mode dan search di blog Nuxt 3 kamu? Tanpa npm install, tanpa plugin ribet. Cukup tambah 2 library dari CDN — Darkmode.js dan Fuse.js.",[4838,4828,4839,4831],"dark-mode","javascript","\u002Fimages\u002Fposts\u002Fdark-mode-search-og.jpg",{"path":4842,"title":4843,"description":4844,"date":4819,"category":4255,"tags":4845,"image":4848,"readingTime":4298},"\u002Ftech\u002Femail-setup-namecheap-google-workspace","Punya Email Domain Sendiri? Cuma 15 Menit, Gratis!","Punya Email Domain Sendiri? Cuma 15 Menit, Gratis Bayangin punya email inquiry@yourdomain.com yang langsung masuk ke inbox kamu. Profesional, keren, dan —…",[4846,4633,4847,4831],"email","namecheap","\u002Fimages\u002Fposts\u002Femail-setup-og.jpg",{"path":4850,"title":4851,"description":4852,"date":4853,"category":4255,"tags":4854,"image":4857,"readingTime":4334},"\u002Ftech\u002Fopenclaw-exec-approvals-fix","Fix Exec Approvals OpenClaw 2026.4.1 — Command Gagal Approve?","Update ke 2026.4.1 tapi exec approvals error? Ini penyebabnya dan cara fix-nya. Dibahas praktis dengan konteks Radian Group, engineering, automation, dan pene","2026-04-02 09:00:00+08:00",[3556,4617,4855,4856],"exec","approvals","\u002Fimages\u002Fposts\u002Fopenclaw-exec-approvals-fix.jpg",{"path":4859,"title":4860,"description":4861,"date":4862,"category":4255,"tags":4863,"image":4864,"readingTime":4865},"\u002Ftech\u002Fopenclaw-dasar-pemula-guide","OpenClaw untuk Pemula: Panduan Setup dari Nol","Panduan lengkap setup OpenClaw dari nol — AI assistant yang bisa jalan 24\u002F7 di VPS kamu. Dibahas praktis dengan konteks Radian Group, engineering, automation,","2026-04-02 08:00:00+08:00",[3556,4831,4267],"\u002Fimages\u002Fposts\u002Fopenclaw-dasar-pemula-guide.jpg","2",{"path":4867,"title":4868,"description":4869,"date":4870,"category":4255,"tags":4871,"image":4873,"readingTime":4283},"\u002Ftech\u002Ftrae-agent-automation","Trae Agent: Satu AI untuk Semua Automasi — Setup & Tips","Trae agent bisa handle banyak tugas sekaligus. Ini cara setup-nya biar maksimal. Dibahas praktis dengan konteks Radian Group, engineering, automation, dan pen","2026-04-02",[4267,4872,4268,4296],"trae","\u002Fimages\u002Fposts\u002Ftrae-agent-automation.jpg",{"path":4875,"title":4876,"description":4877,"date":4878,"category":4255,"tags":4879,"image":4881,"readingTime":4346},"\u002Ftech\u002Fopenclaw-vs-hermes-agent","OpenClaw vs Hermes Agent (2026) — Komparasi Jujur, Tanpa Bacot","Dua AI agent platform terbaik 2026. Aku pakai keduanya — ini review jujur mana yang lebih cocok untuk apa. Dibahas praktis dengan konteks Radian Group, engine","2026-04-01 08:00:00+08:00",[3556,4278,4267,4880],"comparison","\u002Fimages\u002Fposts\u002Fopenclaw-vs-hermes-agent.jpg",{"path":4883,"title":4884,"description":4885,"date":4886,"category":4255,"tags":4887,"image":4892,"readingTime":4569},"\u002Ftech\u002Fabsensi-migrasi-supabase","Migrasi Absensi ke Supabase: Dari Spreadsheet ke Database Real-Time","Bosan dengan spreadsheet absensi yang error terus? Yuk migrasi ke Supabase — setup 30 menit, data real-time forever. Dibahas praktis dengan konteks Radian Gro","2026-04-01",[4888,4889,4890,4891],"supabase","database","absensi","migration","\u002Fimages\u002Fposts\u002Fabsensi-migrasi-supabase.jpg",{"path":4894,"title":4895,"description":4896,"date":4886,"category":4255,"tags":4897,"image":4901,"readingTime":4474},"\u002Ftech\u002Fai-coding-cli-openclaw-kiro-trae","AI Coding CLI Battle: OpenClaw vs Kiro vs Trae — Mana yang Paling Kencang?","Test head-to-head tiga AI coding CLI terbaik 2026. Hasilnya... nggak kayak yang kamu kira. Dibahas praktis dengan konteks Radian Group, engineering, automatio",[4267,4898,4899,4900,4872,3556],"coding","cli","kiro","\u002Fimages\u002Fposts\u002Fai-coding-cli-openclaw-kiro-trae.jpg",{"path":4903,"title":4904,"description":4905,"date":4886,"category":4255,"tags":4906,"image":4908,"readingTime":4569},"\u002Ftech\u002Fdashboard-vps-nextjs","Satu Dashboard untuk Semua VPS: Build dengan Next.js","Punya banyak VPS tapi monitor-nya masih cek satu-satu? Build dashboard gabungan yang ngumpulin semua metrics di satu tempat.",[4907,4734,4468,4761],"nextjs","\u002Fimages\u002Fposts\u002Fdashboard-vps-nextjs.jpg",{"path":4910,"title":4911,"description":4912,"date":4886,"category":4255,"tags":4913,"image":4916,"readingTime":4298},"\u002Ftech\u002Ferror-boundary-nextjs","Error Boundary di Next.js: Dashboard Nggak Lagi White Screen of Death","Satu error kecil bikin seluruh dashboard crash? Pasang error boundary — biar yang error cuma komponennya, bukan seluruh halaman.",[4907,4914,4734,4915],"error-handling","react","\u002Fimages\u002Fposts\u002Ferror-boundary-nextjs.jpg",{"path":4918,"title":4919,"description":4920,"date":4886,"category":4255,"tags":4921,"image":4925,"readingTime":4308},"\u002Ftech\u002Ffile-manager-search-nextjs","File Manager dengan Search & Highlight di Next.js","Cari file di dashboard tanpa reload page. Implementasi client-side search dengan highlight real-time. Dibahas praktis dengan konteks Radian Group, engineering",[4907,4922,4923,4924],"file-manager","search","ui","\u002Fimages\u002Fposts\u002Ffile-manager-search-nextjs.jpg",{"path":4927,"title":4928,"description":4929,"date":4886,"category":4255,"tags":4930,"image":4933,"readingTime":4865},"\u002Ftech\u002Fintegrasi-layanan-eksternal-openclaw","Integrasi Layanan Eksternal dengan OpenClaw — API, Webhook, dan N8N","Hubungkan OpenClaw ke dunia luar. Gmail, Slack, Notion, API apapun — semua bisa. Dibahas praktis dengan konteks Radian Group, engineering, automation, dan pen",[3556,4597,4931,4821,4932],"api","webhook","\u002Fimages\u002Fposts\u002Fintegrasi-layanan-eksternal-openclaw.webp",{"path":4935,"title":4936,"description":4937,"date":4886,"category":4255,"tags":4938,"image":4940,"readingTime":4941},"\u002Ftech\u002Fjasa-install-openclaw","Jasa Install OpenClaw Profesional 2026 — Panduan Lengkap","Mau install OpenClaw tapi bingung mulai dari mana? Panduan lengkap dari nol sampai production-ready. Dibahas praktis dengan konteks Radian Group, engineering,",[3556,4607,4606,4939],"server","\u002Fimages\u002Fposts\u002Fjasa-install-openclaw.jpg","25",{"path":4943,"title":4944,"description":4945,"date":4886,"category":4255,"tags":4946,"image":4949,"readingTime":4569},"\u002Ftech\u002Fnotifikasi-health-check-nextjs","Sistem Notifikasi Health Check di Next.js — Alert Sebelum User Komplen","Jangan tunggu user komplen. Setup health check dengan notifikasi real-time sebelum masalah jadi bencana. Dibahas praktis dengan konteks Radian Group, engineer",[4907,4761,4947,4948],"notification","health-check","\u002Fimages\u002Fposts\u002Fnotifikasi-health-check-nextjs.jpg",{"path":4951,"title":4952,"description":4953,"date":4886,"category":4255,"tags":4954,"image":4957,"readingTime":4298},"\u002Ftech\u002Fupgrade-openclaw-2026-3-31","Upgrade OpenClaw ke 2026.3.31 — Apa yang Baru dan Cara Upgrade","Update besar OpenClaw akhir Maret 2026. Fitur baru, breaking changes, dan cara upgrade tanpa drama. Dibahas praktis dengan konteks Radian Group, engineering, ",[3556,4955,4956],"upgrade","changelog","\u002Fimages\u002Fposts\u002Fupgrade-openclaw-2026-3-31.jpg",{"path":4959,"title":4960,"description":4961,"date":4962,"category":4255,"tags":4963,"image":4964,"readingTime":4965},"\u002Ftech\u002Fdashboard-briefing-monitor","AI Agent Dashboard Bagian 2: Dashboard, Briefing & System Monitor","Part 2 — Bikin halaman utama dashboard, morning briefing page, dan system monitor dengan real-time data. Dibahas praktis dengan konteks Radian Group, engineer","2026-03-28",[4907,4734,4761,4931],"\u002Fimages\u002Fposts\u002Fdashboard-briefing-monitor.jpg","18",{"path":4967,"title":4968,"description":4969,"date":4962,"category":4255,"tags":4970,"image":4973,"readingTime":4974},"\u002Ftech\u002Fmodels-settings-deploy","AI Agent Dashboard Bagian 4: Models, Settings & Deployment","Part 4 — Configuration models, settings page, animasi polish, API routes, dan deployment ke production. Dibahas praktis dengan konteks Radian Group, engineeri",[4907,4734,4971,4972],"deployment","production","\u002Fimages\u002Fposts\u002Fmodels-settings-deploy.jpg","30",{"path":4976,"title":4977,"description":4978,"date":4962,"category":4255,"tags":4979,"image":4982,"readingTime":4983},"\u002Ftech\u002Fsessions-skills-logs","AI Agent Dashboard Bagian 3: Sessions, Skills & Logs","Part 3 — Track sessions, manage skills hub, schedule cron jobs, dan monitoring logs. Dibahas praktis dengan konteks Radian Group, engineering, automation, dan",[4907,4734,4980,4981,4586],"sessions","logs","\u002Fimages\u002Fposts\u002Fsessions-skills-logs.jpg","31",{"path":4985,"title":4986,"description":4987,"date":4962,"category":4255,"tags":4988,"image":4990,"readingTime":4474},"\u002Ftech\u002Fsetup-layout","AI Agent Dashboard Bagian 1: Setup, Layout & Navigasi","Part 1 — Fondasi dashboard AI agent dari nol. Next.js 14, Tailwind, shadcn\u002Fui, dan arsitektur layout yang scalable. Dibahas praktis dengan konteks Radian Grou",[4907,4734,4267,4989,4607],"layout","\u002Fimages\u002Fposts\u002Fsetup-layout.jpg",{"path":4992,"title":4993,"description":4994,"date":4995,"category":4255,"tags":4996,"image":4999,"readingTime":4334},"\u002Ftech\u002Fai-video-generation-pipeline","Pipeline AI Video Generation: Dari Script jadi Video Otomatis","Bikin video tanpa buka video editor sekali pun. Setup pipeline AI yang jalan sendiri — dari naskah sampai final render. Dibahas praktis dengan konteks Radian ","2026-03-12",[4267,4997,4268,4998],"video","pipeline","\u002Fimages\u002Fposts\u002Fai-video-generation-pipeline.jpg",{"path":5001,"title":5002,"description":5003,"date":4995,"category":4255,"tags":5004,"image":5007,"readingTime":4283},"\u002Ftech\u002Frepliz-threads-auto-post","Auto-Post ke Threads dengan OpenClaw + Repliz — Content Marketing Otomatis","Threads tapi autopilot. Setup posting otomatis dengan AI-generated content yang engage. Dibahas praktis dengan konteks Radian Group, engineering, automation, ",[4379,5005,4268,5006],"repliz","social-media","\u002Fimages\u002Fposts\u002Frepliz-threads-auto-post.jpg",{"path":5009,"title":5010,"description":5011,"date":5012,"category":4255,"tags":5013,"image":5014,"readingTime":4334},"\u002Ftech\u002Fgmail-auto-label-triage","Gmail Auto-Label & Triage: Email Teratur Tanpa Manual Sorting","Inbox rapi otomatis. Setup AI-based email triage yang label, prioritas, dan sort email tanpa kamu sentuh. Dibahas praktis dengan konteks Radian Group, enginee","2026-03-11",[4295,4846,4268,4267],"\u002Fimages\u002Fposts\u002Fgmail-auto-label-triage.jpg",{"path":5016,"title":5017,"description":5018,"date":5012,"category":4255,"tags":5019,"image":5022,"readingTime":4569},"\u002Ftech\u002Fgog-cli-google-workspace","Gog CLI: Akses Google Workspace dari Terminal — Superpowers buat Dev","Gmail, Drive, Calendar, Sheets — semua dari terminal. Gog CLI bikin Google Workspace jadi tools developer, bukan apps bisnis.",[5020,4899,5021,4471,4482],"google","gog","\u002Fimages\u002Fposts\u002Fgog-cli-google-workspace.jpg",{"path":5024,"title":5025,"description":5026,"date":5012,"category":4255,"tags":5027,"image":5030,"readingTime":4308},"\u002Ftech\u002Fhindari-bug-kimi-openclaw","⚠️ BUG CRITICAL: Jangan Update OpenClaw ke Versi 2026.3.7!","Kimi 2.5 bikin OpenClaw error parah. Ini yang perlu kamu tahu sebelum update — dan cara fix-nya. Dibahas praktis dengan konteks Radian Group, engineering, aut",[3556,5028,5029,4494],"bug","kimi","\u002Fimages\u002Fposts\u002Fhindari-bug-kimi-openclaw.jpg",{"path":5032,"title":5033,"description":5034,"date":5012,"category":4255,"tags":5035,"image":5037,"readingTime":4283},"\u002Ftech\u002Fmulti-agent-shared-memory","Multi-Agent dengan Shared Memory — AI Team yang Bisa Ngobrol Sama","Satu agent nggak cukup? Bangun tim AI yang share memory dan koordinasi — kayak tim developer, tapi tanpa drama. Dibahas praktis dengan konteks Radian Group, e",[4267,4578,5036,3556],"memory","\u002Fimages\u002Fposts\u002Fmulti-agent-shared-memory.jpg",{"path":5039,"title":5040,"description":5041,"date":5012,"category":4255,"tags":5042,"image":5046,"readingTime":4334},"\u002Ftech\u002Fredis-caching-pattern","Redis Caching Pattern: Akselerasi API 10x dengan Cache yang Benar","API lambat? Cache dulu. Redis pattern yang bener bisa bikin response time turun drastis. Dibahas praktis dengan konteks Radian Group, engineering, automation,",[5043,5044,5045,4931],"redis","caching","performance","\u002Fimages\u002Fposts\u002Fredis-caching-pattern.jpg",{"path":5048,"title":5049,"description":5050,"date":5012,"category":4255,"tags":5051,"image":5053,"readingTime":4334},"\u002Ftech\u002Fservice-health-dashboard","Build Service Health Dashboard — Monitor Semua Service di Satu Tempat","Microservices? Monolith? Whatever. Dashboard ini nge-track health semua service kamu dalam real-time. Dibahas praktis dengan konteks Radian Group, engineering",[4734,4761,4485,5052],"health","\u002Fimages\u002Fposts\u002Fservice-health-dashboard.jpg",{"path":5055,"title":5056,"description":5057,"date":5012,"category":4255,"tags":5058,"image":5060,"readingTime":4368},"\u002Ftech\u002Fsmart-email-forward-pdf","Smart Email Forward dengan PDF Attachment — Otomatis & Pintar","Forward email berdasarkan konten, extract PDF, dan route ke orang yang tepat. Tanpa satu baris manual work. Dibahas praktis dengan konteks Radian Group, engin",[4846,4268,5059,4267],"pdf","\u002Fimages\u002Fposts\u002Fsmart-email-forward-pdf.jpg",{"path":5062,"title":5063,"description":5064,"date":5012,"category":4255,"tags":5065,"image":5067,"readingTime":4283},"\u002Ftech\u002Fsmart-email-triage-ai","Smart Email Triage dengan AI — Inbox Zero Tanpa Sakit Kepala","Filter email penting dari spam dalam sekejap. AI triage yang belajar preferensi kamu. Dibahas praktis dengan konteks Radian Group, engineering, automation, da",[4846,4267,4268,5066],"productivity","\u002Fimages\u002Fposts\u002Fsmart-email-triage-ai.jpg",{"path":5069,"title":5070,"description":5071,"date":5072,"category":4255,"tags":5073,"image":5076,"readingTime":4308},"\u002Ftech\u002Fauto-post-ke-website","Auto-Post ke Website dengan OpenClaw — Content Publishing Otomatis","Stop copy-paste manual. Setup auto-posting dari draft ke website dalam hitungan menit. Dibahas praktis dengan konteks Radian Group, engineering, automation, d","2026-03-08",[4268,5074,5075,3556],"website","content","\u002Fimages\u002Fposts\u002Fauto-post-ke-website.jpg",{"path":5078,"title":5079,"description":5080,"date":5072,"category":4255,"tags":5081,"image":5082,"readingTime":4308},"\u002Ftech\u002Fdeployment-butler-otomatis","Deployment Butler: Assistant Deployment yang Jaga Server 24\u002F7","Deploy tanpa was-was. Butler yang monitor deploys, rollback otomatis kalau error, dan kasih notifikasi real-time. Dibahas praktis dengan konteks Radian Group,",[4971,4268,4485,3556],"\u002Fimages\u002Fposts\u002Fdeployment-butler-otomatis.jpg",{"path":5084,"title":5085,"description":5086,"date":5072,"category":4255,"tags":5087,"image":5091,"readingTime":4308},"\u002Ftech\u002Fexcalidraw-diagram-ai","Generate Diagram Excalidraw dengan AI — Dari Prompt jadi Visual","Uraian teks panjang jadi diagram cantik dalam sekejap. Excalidraw + AI = combo mematikan untuk dokumentasi. Dibahas praktis dengan konteks Radian Group, engin",[5088,4267,5089,5090],"excalidraw","diagram","visual","\u002Fimages\u002Fposts\u002Fexcalidraw-diagram-ai.webp",{"path":5093,"title":5094,"description":5095,"date":5072,"category":4255,"tags":5096,"image":5097,"readingTime":4283},"\u002Ftech\u002Fn8n-integrasi-openclaw","Integrasi N8N dengan OpenClaw — Workflow Automation Powerhouse","N8N + OpenClaw = automation yang nggak terbatas. Dari email trigger sampai deployment pipeline. Dibahas praktis dengan konteks Radian Group, engineering, auto",[4821,3556,4268,4522],"\u002Fimages\u002Fposts\u002Fn8n-integrasi-openclaw.jpg",{"path":5099,"title":5100,"description":5101,"date":5072,"category":4255,"tags":5102,"image":5104,"readingTime":4334},"\u002Ftech\u002Fsmart-file-butler","Smart File Butler: Assistant File Management yang Pintar","File berserakan? Butler ini otomatis sort, tag, dan organize file kamu berdasarkan konten. Dibahas praktis dengan konteks Radian Group, engineering, automatio",[4268,5103,4267,3556],"file-management","\u002Fimages\u002Fposts\u002Fsmart-file-butler.jpg",{"path":5106,"title":5107,"description":5108,"date":5072,"category":4255,"tags":5109,"image":5113,"readingTime":4569},"\u002Ftech\u002Fvisual-data-alert","Visual Data Alert — Dari Data jadi Grafik Otomatis","Angka-angka mentah susah dibaca? Otomatis convert ke grafik dan kirim alert kalau ada anomali. Dibahas praktis dengan konteks Radian Group, engineering, autom",[5110,5111,5112,4268],"data","visualization","alert","\u002Fimages\u002Fposts\u002Fvisual-data-alert.jpg",{"path":5115,"title":5116,"description":5117,"date":5072,"category":4255,"tags":5118,"image":5120,"readingTime":4569},"\u002Ftech\u002Fvoice-memo-to-action","Voice Memo to Action — Bicara, AI yang Kerja","Record voice memo, AI convert jadi task, assign, dan track. Productivity level up tanpa ngetik. Dibahas praktis dengan konteks Radian Group, engineering, auto",[5119,4267,5066,4268],"voice","\u002Fimages\u002Fposts\u002Fvoice-memo-to-action.jpg",{"path":5122,"title":5123,"description":5124,"date":5125,"category":4255,"tags":5126,"image":5129,"readingTime":4283},"\u002Ftech\u002Fopenclaw-alibaba-coding","OpenClaw + Alibaba Cloud: 8 AI Model, 1 API Key — Hemat 90%","Bayar 8 provider AI berbeda? Stop. Pakai Alibaba Cloud, satu API key buat 8 model AI top — harga kaki lima. Dibahas praktis dengan konteks Radian Group, engin","2026-03-05 09:00:00+08:00",[3556,5127,4267,5128],"alibaba","cloud","\u002Fimages\u002Fposts\u002Fopenclaw-alibaba-coding.webp",{"path":5131,"title":5132,"description":5133,"date":5134,"category":4255,"tags":5135,"image":5137,"readingTime":4308},"\u002Ftech\u002Fopenclaw-multi-agent-system","Sistem Multi-Agent OpenClaw — Bangun Tim AI yang Koordinasi","4 AI agent, 1 koordinator, mission accomplished. Ini arsitektur multi-agent yang aku pakai sehari-hari. Dibahas praktis dengan konteks Radian Group, engineeri","2026-03-05 08:00:00+08:00",[3556,4578,4267,5136],"architecture","\u002Fimages\u002Fposts\u002Fopenclaw-multi-agent-system.webp",1781013020554]