惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Schneier on Security
P
Proofpoint News Feed
Apple Machine Learning Research
Apple Machine Learning Research
WordPress大学
WordPress大学
博客园 - Franky
V
V2EX
爱范儿
爱范儿
J
Java Code Geeks
小众软件
小众软件
Last Week in AI
Last Week in AI
The Cloudflare Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Hugging Face - Blog
Hugging Face - Blog
T
The Blog of Author Tim Ferriss
酷 壳 – CoolShell
酷 壳 – CoolShell
The Register - Security
The Register - Security
GbyAI
GbyAI
Vercel News
Vercel News
Y
Y Combinator Blog
腾讯CDC
F
Fortinet All Blogs
I
InfoQ
N
Netflix TechBlog - Medium
B
Blog RSS Feed
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
量子位
博客园 - 司徒正美
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
V
Visual Studio Blog
博客园 - 三生石上(FineUI控件)
宝玉的分享
宝玉的分享
Blog — PlanetScale
Blog — PlanetScale
H
Help Net Security
云风的 BLOG
云风的 BLOG
A
About on SuperTechFans
Scott Helme
Scott Helme
T
Tor Project blog
U
Unit 42
Google Online Security Blog
Google Online Security Blog
PCI Perspectives
PCI Perspectives
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
NISL@THU
NISL@THU
D
Darknet – Hacking Tools, Hacker News & Cyber Security
aimingoo的专栏
aimingoo的专栏
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Stack Overflow Blog
Stack Overflow Blog
Security Archives - TechRepublic
Security Archives - TechRepublic

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
I shipped an AI pipeline in a month that reads Reddit, HN, and X for startup ideas. The hardest part wasn't the AI.
Benjian Dai · 2026-04-28 · via DEV Community

For the last month I've been building MonetScope — a pipeline that crawls Reddit, Hacker News, and X, reads what real people are complaining about, and surfaces the complaints as scored startup opportunities.

Going in, I assumed the hard part would be the AI layer. You know the story: prompt engineering, structured output, temperature tuning. That's where the demos happen and where most of the blog posts get written.

It wasn't. The LLM layer landed roughly on schedule. What took real engineering time were four other things — each one taught me something I'll carry into the next pipeline I build. Plus one deeply dumb cross-language serialization bug that nearly corrupted my data for a week without me noticing.

This is a write-up of those things.

The pipeline, very roughly

Before we dig in, the mental model. I'm keeping this deliberately abstract because the interesting part is the categories, not my specific libraries.

   [Reddit]    [Hacker News]    [X]
       \            |            /
        \           |           /
         +---> crawler layer <---+
                    |
             message queue
                    |
          deterministic filters
                    |
           multi-stage LLM layer
                    |
            grounding + storage
                    |
                 product UI

Enter fullscreen mode Exit fullscreen mode

Two runtimes: one for crawlers (good at "get data out of places"), one for orchestration plus API (good at "stay up under load"). A message queue between them. Boring, intentionally.

What's on that diagram today is three public platforms. What's on my whiteboard is more — additional communities, niche forums, and eventually a user-submitted channel where a founder can drop in their own support tickets, a competitor's review stream, or a CSV dump from a private Slack. Each new source is just another input box on the diagram, which is the whole point of having the diagram at this level of abstraction. The cost of adding source N+1 is not in the pipeline; it's in the per-platform quality heuristics, and that's a problem I enjoy having.

Now — the four things that took more time than the AI. Starting with the most embarrassing.

1. The scraping tool was wrong for two of three platforms

Every scraping tutorial you'll find online opens with the same heavyweight toolkit — headless browser, stealth plugins, proxy rotation, the works. I started there too.

This turned out to be wrong for one platform, unnecessary for another, and actively dumb to avoid on the third.

  • Platform A: I had a headless browser dutifully rendering pages for three weeks before I realized the same data was available through a much thinner path that didn't require rendering a single pixel. When I rewrote that spider it went from "needs a beefy worker" to "runs on a potato." I want those three weeks back.
  • Platform B: There was a developer-focused API available the entire time. Not just scrape-able, officially supported. I was reinventing its existence in the browser layer. Pure hubris — I had assumed "if the tutorial uses a browser for it, that must be the right tool."
  • Platform C: No shortcut. Actively hostile to scraping, continuous cat-and-mouse, and my time is worth more than the subscription. Paid for access. Never looked back.

The generalizable lesson: don't start with a tool and hunt for problems to solve with it. Start with how the source actually serves data to its own frontend. If it serves structured data, there's usually a path that isn't a browser. If it only renders via JS, that's your answer. If it's hostile, pay or skip.

The heavyweight-browser default isn't wrong — it's a good fallback when nothing else works. The failure mode is treating it as the starting point.

2. The cheapest filter is the one that runs before the LLM

Naive version of the pipeline: crawled post arrives → LLM processes it → structured output goes to the database.

Two problems at once.

It's expensive. Every post is tokens, and token cost on a content-scale pipeline dominates the bill within a week.

The output is worse. "Why is no one building X?" posts waste tokens producing confident-sounding opportunity cards that don't survive human review. A model asked to extract an opportunity from a substance-free rant will dutifully hallucinate one.

The fix is philosophically simple: put a deterministic filter in front of the LLM that rejects content the LLM would have rejected anyway, but for free.

What "deterministic" means varies per platform — what counts as a substantive post in one community is a throwaway in another. The thresholds are per-platform, and they drift as community norms change. I'm not going to publish the current values; they're part of the product. But the interesting thing about tuning them isn't the numbers. It's that I ended up building a small tuning harness that was more work than picking any individual threshold.

Ordering matters too. The deterministic filter runs before the stateful dedup layer, not after. Posts rejected today can be reconsidered tomorrow if they accumulate engagement — which they sometimes do, especially on HN.

Generalizable rule: before every LLM call, ask "can I cheaply reject this input first?" The answer is usually yes, and the win compounds: less cost per document, better signal on the documents that make it through, fewer false positives to clean up downstream.

3. Shipping an "AI-grounded" feature without lying to users

This is the section I most want a reader to take away, so I'll be careful about the level I pitch it at.

The product makes a specific promise: every claim it generates is backed by a quote from an identifiable, real user. You see "users complain that X breaks every Tuesday" and you can click through to the exact comment where someone said exactly that.

If that promise leaks — if even a small percentage of the quotes are paraphrased, massaged, or invented — the product has no reason to exist. "We summarize Reddit with AI" is a commodity. "We show you the literal thing the person said" is not.

LLMs in their default configuration will break that promise. Paraphrasing is what they're good at. Making up a plausible-sounding quote is easier for them than surfacing the specific boring one that matters. This isn't a flaw of any particular model; it's a property of optimized-for-fluency generation.

The approach I landed on, at the pattern level:

  1. Don't ask the LLM to find sources. Do extraction of candidate source material deterministically, before the generation step. The LLM sees a curated candidate pool, not the raw corpus.
  2. Constrain generation to operate on those candidates. The LLM's job is to synthesize and structure. It is not the layer that decides what's citable.
  3. Mechanically verify every output claim against a specific source record. If it doesn't match a real record, it doesn't ship. This is the step most pipelines skip, and it's the one that determines whether users still trust the output six months in.
  4. Fail closed. If verification can't find the source for a generated claim, drop the claim. If dropping claims leaves the output empty, drop the whole output. Empty is fine. Phantom is not.

I won't walk through the matching algorithm, what the candidate pool looks like in this codebase, or how I decide "drop claim vs drop whole output" — those are product-specific tuning and they're where the moat actually lives.

The pattern itself is freely available, and I wish I'd seen it articulated when I started: for any LLM feature where hallucination is a correctness bug rather than a style flaw, the pattern is pre-extract → constrain → verify → fail closed. Half-measures ship, but they don't keep trust.

4. One score is almost never enough

The hardest non-infrastructure problem turned out to be teaching the pipeline the difference between "people are angry" and "people will pay."

The naive move is one score per item — how good is this opportunity, 0-10. This doesn't work. It tries to answer two reader questions at once ("is there real pain here?" and "would anyone actually buy a solution?") and those are orthogonal.

A thread full of "someone should build X" is high on the first and near-zero on the second. A thread where one person has duct-taped three tools together and is actively shopping for a replacement is moderate on the first and very high on the second. A single composite score collapses those into noise.

The fix isn't clever math. It's the recognition that any time a single number is answering more than one question, it ends up answering none of them well. Split the signal into the questions you actually want answered, score those separately, and compose them deliberately — not average them. A lot of weak signal does not beat a little strong signal, even when the means come out similar.

One non-obvious finding I'll share because it's directional-only: the "obvious" communities (the big generalist ones) produce noisier signal than niche ones. Volume is a weak proxy for signal strength. Source diversity turned out to matter as much as source volume — an opportunity drawn from three different niche communities beats one drawn from thirty posts in one big general community, even though the raw evidence count is an order of magnitude lower.

Dev-applicable version: when a ranking algorithm isn't working, check whether you're averaging two signals that are answering different questions. You'll be surprised how often the answer is yes.

The dumb bug that almost invalidated everything

This one isn't a product moat — it's a general cross-language gotcha — so I'll share it in detail because the lesson is broadly useful to anyone building a polyglot system.

Two services in two languages, both writing to the same database column. The column stores vectors as text, like [0.123,0.456,...]. The database happily accepts whatever either language produces.

The trap: each language's default float-to-string produces slightly different output. Different widths. Different rounding at the edge case. To the human eye they look identical. To byte-wise comparison they aren't. To cosine similarity on the resulting vector, they're close but not the same vector.

Nothing broke. No exception. No type error. No failing test. What happened was that semantic similarity rankings drifted depending on which service had last written the record. Results were good, then mysteriously slightly bad, then good again. I chased this for most of a week before I realized what I was looking at.

The fix, in pseudocode:

// One canonical formatter, shared definition across both runtimes.
// Verified by golden-string test in each language.
format_vector(floats):
    return "[" + join(",", each f -> to_string_exact(f)) + "]"

Enter fullscreen mode Exit fullscreen mode

Where to_string_exact is explicitly pinned to the widest-precision, culture-invariant format available in each language — not whatever the default toString() happens to do. And the test is a literal string equality check against a hand-written golden output, run from both sides.

The broader lesson: "both sides are using the default" is a dangerous sentence in a polyglot system. Default serialization isn't a contract. If two runtimes are going to share a serialized format, write the format exactly once as a pure function, and verify its output byte-for-byte from both languages. Repeat for every format that crosses the runtime boundary — JSON casing was the other one that bit me, in passing.

What I'd do differently

Nothing kills a launch post faster than "everything went great." So:

  • I picked the wrong primary data store early, for the wrong reason — "flexibility." Future me didn't want flexibility. Future me wanted fewer moving parts. Moved to a boring relational DB with a JSON column type and things got better immediately. Lost about three weeks.
  • I wrote my own rate-limit layer before realizing a standard caching-server primitive plus ten lines of script would have done the same job in an afternoon. Lost a week on that one.
  • I underestimated observability. The liveness vs. readiness healthcheck split only happened after the third production incident. It should have been there on day one — you don't need it until you need it, and then you need it immediately.
  • The grounding / verification layer shipped too late. Weeks of early data had to be re-processed once I added it. It should have been part of the first LLM call, not the twelfth.

If there's a theme: my worst decisions were the ones where I picked the more flexible option so future-me would have more options. Future-me didn't want options. Future-me wanted something that worked.

Closing

That's four things that turned out harder than the AI, plus the serialization bug for flavor.

The product all this plumbing is in service of is at monetscope.com — free 14-day trial, no card. If you'd rather see what it produces before committing, this week's top 10 opportunities are at monetscope.com/this-week (just email, no card). The output is what the pipeline exists for, but to be honest the feedback I most want right now is on the engineering choices above, not the landing page.

If you've shipped an LLM-scored content pipeline yourself, I'd genuinely like to hear: how do you version and regression-test your prompts? That's the layer I feel weakest on, and I haven't found a tool I love. Current setup is git commit plus a hand-maintained regression set, and it's starting to creak as the prompt surface grows.

Also: if you maintain a community, newsletter, or data source you'd be interested in seeing indexed — that's on my roadmap, and I'm actively looking for source-expansion partnerships. DM me.

Thanks for reading this far.