这次我们来看一个很有意思的项目方向一个能自动发现“正在询问你想卖的东西”的人的工具。原始标题是 “Show HN: I built a tool that finds people asking for what you sell”核心思路很直接不用再被动等用户来找你而是通过监控 Hacker News、Reddit、X 等公开社区的讨论内容找到正在求推荐、找替代品、问报价的人第一时间把线索推给你。这类工具本质上是销售线索挖掘和社交媒体监听的小型自动化系统。它适合独立开发者、SaaS 创业者、自由职业者和做 B 端服务的人。比如你做了一款记账 SaaS就可以监控“recommend billing software”“billing tool for freelancers”这类帖子一旦有人提问你就能抢在竞争对手之前回复把需求变成真实客户。这篇文章会从架构设计、环境准备、最小可运行实现、功能测试、接口封装、批量任务、成本观察、问题排查这几个维度把这类工具拆开讲清楚。如果你正在考虑做类似的需求发现系统或者准备把这类工具接进自己的业务流可以直接照着下面的方案落地。1. 核心能力速览先给一张速览表快速判断这类工具是否值得你接着往下看。能力项说明项目类型销售线索挖掘 / 社媒监听 / 需求发现工具核心功能多平台公开讨论抓取、关键词规则过滤、语义意图判断、结果聚合与通知GPU 依赖无纯 CPU 即可运行显存完全不涉及主要成本目标平台 API 调用配额以及可选的 LLM Token 费用启动方式以作者仓库 README 为准本文提供通用命令行启动方案API 能力可自行封装 REST 接口正文给出 FastAPI 示例批量任务支持按规则文件批量监控多个关键词和平台适合场景独立开发者找早期用户、SaaS 找需求、自由职业者找客户、B2B 销售线索收集从标题看这个项目最早应该是面向 Hacker News 生态设计的但它的通用工作方式可以扩展到任意提供公开搜索接口的平台。具体功能边界和是否开源以仓库实际 README 为准下面直接进入技术拆解。2. 适用场景与使用边界这类工具解决的是“需求发现”问题而不是“客户管理”问题。它帮助你从公开讨论中发现潜在用户但不会替代 CRM、邮件营销或客服系统。适合的使用场景包括独立开发者刚上线产品想在 Hacker News、Reddit 等社区找到第一批种子用户。SaaS 产品想监控竞品相关讨论发现正在考虑替代方案的用户。自由职业者或外包团队想找“有没有人需要帮忙做某某项目”的帖子。内容团队想从社区高频问题中挖掘选题反向指导产品文档和 blog 内容。不适合什么场景也需要提前明确它不适合做大规模、高精度的销售线索打分系统。社区公开内容噪声多单靠关键词可能产生大量误报。它不适合抓取非公开数据也不应该用来收集用户的私密联系方式。它不适合做全量社交平台监听。每个平台 API 规则不同扩展成本比想象中高。使用边界必须说清楚公开社区不等于可以随意抓取。使用官方 API、控制请求频率、尊重平台条款是这一类工具长期运行的前提。3. 技术架构与关键设计这类工具的架构并不复杂核心是一条数据流水线抓取 - 归一化 - 过滤 - 意图判断 - 去重 - 输出和通知。抓取层负责从各平台读取公开内容。以 Hacker News 为例可以直接使用 Algolia 提供的公开搜索接口不需要 OAuth请求简单返回速度快。Reddit、X 等平台则需要申请 API Key并且有更严格的配额限制。归一化层解决多平台数据结构不一致的问题。不管是 HN 的 story、Reddit 的 post 还是 X 的 tweet最终都统一成同一个结构平台名、外部 ID、标题、正文、作者、链接、发布时间。这样后续的过滤、存储和通知只需要处理一套数据格式。过滤层是规则引擎通常由关键词白名单和排除词黑名单组成。比如你监控“billing software”时需要把包含“job”“hiring”的结果排除掉避免收到大量招聘信息。意图判断层是可选的也是这个工具进化成“智能”的关键一步。关键词过滤只能做到词汇匹配而 LLM 可以做语义判断识别发帖人是否真的有购买意图。代价是每次判断都需要消耗 Token所以更稳妥的做法是先让关键词规则粗筛一遍再做 LLM 精排。去重是很容易忽略但必须做的环节。同一批帖子可能在不同时间段被多次抓取或者同一话题被多个关键词命中。要用平台外部 ID 建唯一索引避免重复通知。输出和通知层可以设计成多种形式JSON 文件、SQLite 数据库、REST 接口回调、Webhook、IM 机器人消息。最小可行版本先写 JSON 文件就够了后期再接通知。整个系统的关键设计原则是分阶段处理不要把抓取和意图判断耦合在一起。先保证数据能稳定流进来再逐步加判断逻辑。4. 环境准备与本地部署4.1 环境准备这类工具属于轻量级服务对硬件没有特殊要求。推荐使用 Python 3.9 以上环境纯 CPU 机器完全可以跑内存占用通常在 200MB 以内。如果你要跑 LLM 意图打分需要额外准备一套 LLM API 的 Key模型本地部署则另说。需要安装的 Python 依赖如下包含抓取、接口和配置解析所需的最小集合pip install requests fastapi uvicorn pydantic如果你准备用 LLM 做意图判断还需要安装对应 SDK。以 OpenAI 风格接口为例pip install openai环境变量需要提前配置export OPENAI_API_KEY你的 key export LLM_MODEL你的模型名注意不同的平台 API 要求不同。Hacker News 的 Algolia 接口是免费的不需要 KeyReddit API 需要创建应用并获取 Client ID 和 SecretX 平台 API 需要开发者账号。以实际平台文档为准。4.2 最小可运行实现下面给出一套最小可运行的抓取器以 Hacker News 公开搜索接口为例实现“配置关键词 - 抓取最新内容 - 规则过滤 - 输出 JSON”的完整流程。先创建规则配置文件rules.json{ queries: [ { id: billing-tool, primary_query: billing, keywords: [recommend billing, billing software, billing tool], exclude: [job, hiring], platforms: [hackernews] } ] }核心抓取脚本main.pyimport sys import json import requests from datetime import datetime def fetch_hackernews(query: str, hits_per_page: int 20) - list[dict]: 抓取 Hacker News 搜索结果。 url https://hn.algolia.com/api/v1/search params { query: query, tags: story, hitsPerPage: hits_per_page, } resp requests.get(url, paramsparams, timeout30) resp.raise_for_status() data resp.json() results [] for hit in data.get(hits, []): results.append({ platform: hackernews, external_id: hit.get(objectID), title: hit.get(title), text: hit.get(story_text) or hit.get(title) or , author: hit.get(author), url: hit.get(url) or hit.get(story_url), created_at: hit.get(created_at), }) return results def filter_by_rules(items: list[dict], keywords: list[str], exclude: list[str]) - list[dict]: 根据关键词和排除词过滤结果。 matched [] for item in items: text f{item[title]} {item[text]}.lower() if any(k.lower() in text for k in keywords): if not any(e.lower() in text for e in exclude): matched.append(item) return matched def main() - None: config_path sys.argv[1] if len(sys.argv) 1 else rules.json config json.loads(open(config_path, encodingutf-8).read()) for rule in config[queries]: print(f[{datetime.now()}] 处理规则: {rule[id]}) for platform in rule.get(platforms, []): if platform ! hackernews: continue items fetch_hackernews(rule[primary_query], hits_per_page20) matched filter_by_rules(items, rule[keywords], rule.get(exclude, [])) for item in matched: print(json.dumps(item, ensure_asciiFalse, indent2)) if __name__ __main__: main()这段代码的逻辑很简单读取规则文件对每条规则执行搜索然后用关键词列表和排除词列表过滤最后把匹配结果打印出来。primary_query是抓取搜索用的主词keywords是精确匹配的词组两者结合可以明显降低噪声。4.3 启动与验证启动命令python main.py rules.json启动后可以看到类似下面的输出[2025-06-10 10:30:00] 处理规则: billing-tool { platform: hackernews, external_id: 44230001, title: Ask HN: Best billing software for freelancers in 2025?, text: I am looking for a billing tool that supports recurring invoices..., author: some_user, url: https://news.ycombinator.com/item?id44230001, created_at: 2025-06-10T09:12:00.000Z }如果输出为空先用最简单的关键词单独测试 API确认不是规则写得太严格。5. 功能测试与效果验证部署上线不是终点关键是验证这套监控系统是不是真的能发现有效线索。建议按下面的维度做一轮系统测试。5.1 召回测试召回测试的目的是确认“该抓到的帖子没有被漏掉”。准备 3 到 5 个已知的、包含目标关键词的帖子链接手工搜索确认它们存在然后运行抓取脚本看结果里是否包含这些帖子。判断标准至少能召回 80% 的已知相关帖子。如果召回率低先检查primary_query是否太窄或者hitsPerPage太小。5.2 准确率测试准确率测试的目的是确认“抓出来的结果里真正的需求帖占多少”。随机抽取 20 条抓取结果人工判断哪些是真实的购买/咨询需求哪些是无关内容。判断标准准确率低于 50% 时优先扩充排除词列表并在rules.json里增加更精确的关键词组合。例如只监控 “recommend billing software”而不是单个 “billing”。5.3 去重测试连续运行两次抓取脚本确认第二次运行不会重复输出相同的帖子。去重逻辑可以用简单的本地文件实现from pathlib import Path seen_path Path(seen_ids.txt) seen set(seen_path.read_text().splitlines()) if seen_path.exists() else set() for item in matched: if item[external_id] not in seen: seen.add(item[external_id]) print(json.dumps(item, ensure_asciiFalse)) seen_path.write_text(\n.join(seen))记住去重必须使用平台外部 ID而不是标题或 URL。标题可能被修改URL 可能为空。5.4 时间窗口测试社区搜索接口默认会返回历史结果如果不加时间限制第一次运行会输出大量旧帖子。建议每次只处理最近 24 小时的内容接口层用时间参数控制应用层再过滤一遍。5.5 稳定性测试让脚本持续运行两天观察是否出现 API 限流、请求超时或内存增长。如果上游接口偶尔超时需要给请求加上重试机制import time def fetch_with_retry(url: str, params: dict, retries: int 3) - dict: for attempt in range(retries): try: resp requests.get(url, paramsparams, timeout30) resp.raise_for_status() return resp.json() except Exception as e: if attempt retries - 1: raise e time.sleep(2 ** attempt) return {}测试完成后再进入接口封装和批量任务阶段。6. 接口 API、LLM 语义匹配与批量任务6.1 REST 接口封装抓取脚本本身只输出到命令行不太方便集成。用 FastAPI 包一层 REST 接口就能把监控能力暴露给其他系统比如自动化营销工具、低代码平台或自己的 Web 前端。新建app.pyfrom fastapi import FastAPI from pydantic import BaseModel from main import fetch_hackernews, filter_by_rules app FastAPI() class MonitorRequest(BaseModel): query: str keywords: list[str] [] exclude: list[str] [] app.post(/api/monitor) def monitor(req: MonitorRequest): items fetch_hackernews(req.query, hits_per_page20) keywords req.keywords or [req.query] matched filter_by_rules(items, keywords, req.exclude) return {count: len(matched), items: matched} app.get(/health) def health(): return {status: ok}启动接口服务uvicorn app:app --host 0.0.0.0 --port 8000本地测试调用curl -X POST http://127.0.0.1:8000/api/monitor \ -H Content-Type: application/json \ -d {query: billing, keywords: [recommend billing, billing software], exclude: [job]}预期返回结构{ count: 1, items: [ { platform: hackernews, external_id: 44230001, title: Ask HN: Best billing software for freelancers in 2025?, text: I am looking for a billing tool that supports recurring invoices..., author: some_user, url: https://news.ycombinator.com/item?id44230001, created_at: 2025-06-10T09:12:00.000Z } ] }接口能跑通之后就可以接到自己的工具里。比如企业微信机器人、飞书机器人、邮件订阅或者内部数据库。6.2 LLM 意图打分关键词规则的准确率有限而 LLM 特别适合判断一段文本是否为真实的购买需求。在过滤层之后再接一层 LLM 意图打分可以把大量“关键词命中但实际无关”的结果过滤掉。典型的 LLM 判断函数from openai import OpenAI client OpenAI() # 使用环境变量 OPENAI_API_KEY 初始化 def score_intent(text: str) - tuple[str, float]: prompt ( 你现在是一个销售线索筛选助手。\n 判断下面的帖子内容是否属于真实的购买/咨询需求。\n 如果发帖人正在寻找某个产品或服务的推荐、替代方案或报价输出 BUYER\n 否则输出 NOISE。\n 最后输出 0 到 1 的置信度格式为标签|置信度\n\n f帖子内容{text} ) resp client.chat.completions.create( modelyour-llm-model, # 替换成你实际可用的模型名 messages[{role: user, content: prompt}], temperature0, ) content resp.choices[0].message.content.strip() label, score content.split(|) return label, float(score)调用方式很简单先关键词过滤再拿过滤后的文本打意图分保留标签为 BUYER 且置信度大于阈值的记录。注意your-llm-model需要根据你实际使用的模型名替换并确认 API Key 和配额都可用。LLM 打分是有成本的所以顺序很重要先用免费的关键词规则粗筛把几百条结果压缩到十几条再让 LLM 精判这样 Token 消耗可以控制在很低的水平。6.3 批量任务与通知接口解决的是“按需查询”批量任务解决的是“持续监控”。最简单的批量任务可以是一个常驻循环每 5 分钟跑一次规则把新结果写入本地文件。import time import json from pathlib import Path from main import fetch_hackernews, filter_by_rules def save_to_file(rule_id: str, items: list[dict]) - None: out_dir Path(outputs) out_dir.mkdir(exist_okTrue) path out_dir / f{rule_id}.jsonl with open(path, a, encodingutf-8) as f: for item in items: f.write(json.dumps(item, ensure_asciiFalse) \n) def run_all(rules: dict) - None: for rule in rules[queries]: for platform in rule.get(platforms, []): if platform ! hackernews: continue items fetch_hackernews(rule[primary_query]) matched filter_by_rules(items, rule[keywords], rule.get(exclude, [])) save_to_file(rule[id], matched) if __name__ __main__: rules json.loads(Path(rules.json).read_text(encodingutf-8)) while True: run_all(rules) time.sleep(300) # 每 5 分钟执行一次按平台配额调整在实际生产环境里建议做三件事用 SQLite 存储抓取记录以platform external_id建唯一索引替代 JSONL 文件的去重逻辑。给每次批量任务加日志记录开始时间、处理条数、异常信息。通知模块做成可插拔接口先接 Webhook后面再扩展邮件或 IM 机器人。Webhook 通知的简化实现import requests def notify_webhook(url: str, item: dict) - None: requests.post(url, jsonitem, timeout10)批量任务遇到上游接口超时或限流时要加指数退避重试不要把全部请求集中在同一时间发出。7. 资源占用、性能与成本观察这个工具对资源的要求很低和跑大模型完全不是一个量级。本节重点说明几个需要关注的点。7.1 CPU 与内存整个抓取和过滤流程不涉及 GPU单核 CPU 就能胜任。内存占用取决于单次抓取返回的数据量Hacker News 单页返回 20 条结果时脚本内存占用通常在几十 MB 到一两百 MB压力很小。可以这样观察资源占用top -p $(pgrep -f python main.py)7.2 平台 API 配额真正的瓶颈是平台 API 配额。Hacker News 的 Algolia 公开接口相对宽松但也不建议高频率请求更不建议超过官方限速。Reddit 和 X 平台的 API 配额更严格批量任务启动前需要先确认自己在对应平台的配额并在代码里做好频率控制。通用做法是给请求加最小间隔import time MIN_INTERVAL_SECONDS 15 last_request_time 0.0 def rate_limited_request(): global last_request_time elapsed time.time() - last_request_time if elapsed MIN_INTERVAL_SECONDS: time.sleep(MIN_INTERVAL_SECONDS - elapsed) last_request_time time.time() # 然后执行实际请求7.3 LLM Token 成本LLM 意图打分是新增成本项。假设每天抓取 1000 条候选内容每条内容的 prompt 约 200 Token那么一天大约消耗 20 万 Token。如果使用付费模型这不是一笔小开销。降低成本的方法有三种先用关键词规则把候选压缩到每天 50 条以内再交给 LLM。设置意图阈值比如置信度低于 0.6 的记录直接丢弃。对同一用户的重复内容做聚合只打分一次。7.4 输出与存储膨胀如果脚本持续跑几个月输出文件会不断膨胀。建议每周清理一次旧数据或者建立归档策略。输出目录建议按日期分目录管理比如outputs/2025-06-10/hackernews_billing.jsonl既方便回溯也避免单文件过大。8. 常见问题与排查方法这类工具在部署和运行中遇到的问题比较集中整理成一张排查表问题现象可能原因排查方式解决方案抓不到结果关键词太严格或 query 不合适先用单关键词测试 API 返回放宽 keyword改用近义词组合API 返回 403缺少平台 API Key 或密钥过期查看请求日志和平台控制台重新生成 Key确认已开通 API 权限请求频率过高被限流请求间隔太短超过平台配额查看 HTTP 状态码和 Retry-After 头增加 sleep 间隔实现指数退避结果出现大量重复没有按 external_id 去重检查本地存储记录用 platform external_id 建唯一索引噪声结果太多只用了关键词过滤抽样查看匹配文本补充排除词加入 LLM 意图打分LLM 接口报错模型名或 API Key 配置不正确查看 LLM 返回的错误信息检查模型名、API Key、账户配额后台进程启动后消失终端关闭导致进程退出检查进程存活状态用 nohup 或 systemd 守护进程接口调用超时上游 API 慢或网络不稳定查看请求耗时日志调大 timeout增加重试机制结果输出时间乱序没有对时间字段排序检查抓取返回的 created_at在输出前按 created_at 排序从实际运行经验看90% 的问题出在 API 限流和噪声控制上。建议第一次部署时设置较长的请求间隔并把通知频率控制在每天 1 到 2 次避免被大量无意义结果刷屏。9. 最佳实践、合规与下一步9.1 工程化建议这类工具想稳定跑下去工程化细节比算法更重要。几个实用建议第一次运行先用手动模式输出到文件人工检查结果质量确认后再开启循环任务。保留一套最小规则配置只监控一个平台、一个关键词作为回归测试基线。模型文件、规则文件、输入素材、输出结果分目录管理避免所有东西堆在同一个目录下。批量任务必须加日志和失败重试至少要能回答“昨天有没有跑”“哪一次请求失败了”。接口服务如果部署在公网要限制访问范围加 API Key 或 IP 白名单避免被别人拿来当免费代理使用。9.2 合规与安全边界这是必须强调的部分。这类工具的商业模式建立在“监控公开讨论并联系当事人”的基础上如果不加约束很容易越界。以下几点需要明确只使用平台官方 API不绕过反爬机制不暴力抓取。只处理公开数据不收集非公开私信、邮箱、手机号等个人敏感信息。发帖人的联系方式属于个人数据处理和存储时需要遵守相关的个人信息保护法规。拿到线索后建议用有针对性的回复建立联系而不是高频发送模板营销信息。商用前务必阅读目标平台的服务条款确认此类监控行为在允许范围内。涉及商业回复时注明自己的身份和产品关系保持透明。9.3 后续扩展方向一个能跑通的最小版本已经解决了“需求发现”的问题后续可以往这些方向扩展多平台适配把抓取层抽象成适配器继续接 Reddit、X、V2EX、百度贴吧等平台。智能告警用 LLM 生成线索摘要按需求强度排序只推送高价值的线索。客户追踪线索处理状态从“待跟进”到“已回复”再到“已转化”形成简单 CRM。定时报表每天生成一份“昨日社区需求报告”汇总热点问题和潜在客户。接入 IM 机器人把新线索推送到企业微信、飞书或 Discord让团队实时跟进。这个方向最值得先做的是把单平台的关键词监控跑通拿到真实结果再谈扩展。最容易踩的坑是把架构设计得过于复杂忽略了 API 配额和噪声控制这两个实际问题。如果你正在做独立开发或者 SaaS 产品这类工具是一个值得投入的自动化方向建议收藏备用。