ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用AI编程助手6周打造开源实时全球情报终端GodEye

用AI编程助手6周打造开源实时全球情报终端GodEye 一个人围着 AI 编程助手连滚带爬干了 6 周最后真的把一个“上帝之眼”给搓出来了。项目在 GitHub 上开源代号 GodEye——一个面向公开互联网的实时全球情报终端。简单说它能把全球新闻、开发者社区、技术动态、行业公告、指数行情这类公开信息源全部接进来用 AI 拆成结构化事件再通过一个实时刷新的信息面板推送给你。适合需要盯公开信息变化的人比如做投资的、做竞品分析的、搞开源社区运营的或者纯粹像我一样不想错过重要信息的普通技术人。之所以敢叫“情报终端”是因为它真的不是那种半小时更新一次的 RSS 阅读器而是从采集、清洗、语义理解到实时推送的完整链路。更让我意外的是整个项目不是大团队做的从架构到代码绝大多数时间只有我和 AI 结对。这篇文章就把整个项目的来龙去脉、技术决策、踩坑过程都摊开讲清楚。1. 起因我不想每天开 30 个网页去猜“今天到底发生了什么”1.1 一个做信息的反而被信息淹没了我在数据行业干了挺久平时工作就是和日志、指标、数据管道打交道。按理说应该很擅长处理信息但真正让我难受的恰恰是我盯着几十个网站和公众号却仍然会漏掉和自己相关的重要变化。举个例子某个开源项目早上发了新版本支持了某种关键能力我正好在做一个技术选型如果能在发布当天知道就可以立刻评估但等我周末无意刷到时别人已经讨论到第三轮了。还有一次我们依赖的一个海外服务半夜改了接口协议早上团队才从报错里发现问题。如果有东西能实时盯住公告页哪怕只是提前半小时发一条告警后边的损失都能少很多。我想做的不是又一个“新闻聚合器”。新闻聚合器解决的是“读什么”而我要解决的是“发生了什么变化、这件事跟我有什么关系、严重程度如何”。这需要程序能判断文本里的主体、行为和时间线那自然就会想到用 AI 做语义抽取而不是只靠关键词匹配。1.2 为什么不去买商业舆情系统我也不是没试过商业产品。团队里采购过舆情监控系统功能确实强能监测很多平台但那是按年付费的价格对个人来说完全不可接受。另外这类系统通常跑在服务商的云端数据留在别人那里对于很多内部信息终端的使用场景来说这本身就是个问题。也有一些开源的替代品比如各种自建 RSS 服务、Google Alerts 的替代方案能解决一部分“盯源”需求。但我想要的语义分析、实时推送、AI 摘要、事件重要度排序要么是付费墙要么是系统太重要么是定制成本高。后来我换了思路与其花时间说服自己凑合用不如借着 AI 辅助开发的能力提升自己写一套最小可用的原型看看能做到什么程度。于是 GodEye 的第一个 commit 就这么来了。项目名是英文 god-eye中文叫“上帝之眼”多少带点中二劲但确实是我心里的目标把分散在世界各地的公开信息统一放到一个终端里。1.3 动手前先给自己划了三个红线做这种项目的诱惑非常大因为“能拿到的数据”和“能合法拿到的数据”中间有很宽的灰色地带。但在第一行代码前我先给自己定了三条规矩防止跑偏。第一只采集公开渠道的数据。包括公开新闻网站、博客、官方公告、原子订阅源、开发者社区公开 API。不碰任何需要登录后的私人页面不碰需要绕过权限校验的接口。第二不处理个人隐私数据。GodEye 要观察的是“事件”不是“人”。即使从公开来源里抓到了个人信息也只保留与事件相关的主体名称不做个人画像。第三保留数据来源和时效。每条事件必须能追溯原始链接AI 生成摘要时不允许“脑补”来源里没有的事实。我不要一个会一本正经编新闻的终端。这三条红线后来在开源讨论区里被反复提到。很多人担心 AI 情报工具会被用来做坏事。我在 README 第一段就写清楚GodEye 只处理公开信息不提供针对任何个体的监控能力部署者需要自行确保数据源合规。2. 一人团队的系统骨架先把数据管道理顺再谈智能2.1 总体架构是条“信息装配线”刚开始我很容易陷入一种误区一上来就研究模型想用大模型直接对全文做意图分析。但在实际写代码时发现如果不先把数据管道理顺AI 就是个“没有东西可消化”的空壳。GodEye 的架构其实非常朴素很像一条信息处理的装配线。这条装配线可以分为四个环节采集层多种连接器分别去抓取不同类型的源比如 RSS、Atom、网页、JSON API、WebSocket 流统一转成内部原始消息格式。清洗层去广告、去重复、提取正文、识别发布时间输出干净的文本然后进入消息队列。AI 分析层多路 worker 消费消息调用嵌入模型和大语言模型做事件抽取、去重、分类、摘要、重要度打分。推送与展示层分析结果写入 PostgreSQL同时通过 Redis Pub/Sub 广播给后端服务后端通过 SSE 推送到浏览器端和通知渠道钉钉、Slack、Telegram 等。这个架构不算创新但好在所有模块都能独立伸缩。一个人开发最大的问题是很容易把代码写成一坨互相咬合的齿轮。我特意给每个环节定了只认数据格式、不认内部实现的接口。比如采集层不管后面的 worker 是不是吃大模型只管输出 raw entryAI 分析层也不关心上游是爬虫还是 API只管消费队列。2.2 为什么没有选微服务也没有选全家桶框架网上很多开源项目一上来就拆成五六个服务。我觉得一个人维护微服务完全是自找麻烦。GodEye 的定位是“单机可部署的实时终端”不是“企业级分布式平台”所以我选择了模块化单体进程内用 asyncio 队列跨进程需要的时候再用 Redis 解耦。技术栈选了 Python 3.11 FastAPI。FastAPI 的异步性能和自动 OpenAPI 文档都比较适合这种需要对外提供接口、并经常被前端调用的项目。加上 AI 生态基本都在 Python 这边如果将来想接新的模型推理库直接 pip install 就行。存储层我用了 PostgreSQL主要保存事件主表、数据源配置、去重指纹、运行指标。连接池用 asyncpgORM 用 SQLAlchemy 2.0。为什么不用 MongoDB因为事件本身有强结构时间、来源、主体、分类、链接、摘要、状态用关系模型做过滤和排序非常直接。去重指纹和高维向量只是一个字段PG 加 pgvector 扩展就可以不需要再引入向量数据库。队列层最初我直接用 PostgreSQL 的表轮询。后来并发一上来发现大量连接浪费在 wait 上。改为 Redis Stream 后消费效率明显提升。项目部署时用 Docker Compose 起三个容器app、postgres、redis已经足够跑通全流程。如果你想更低配Postgres 和 Redis 也可以外挂到已有的实例上。2.3 配置化数据源是扩展性的关键开源出来之后我收到最多的提问是“我该怎么加一个自己的数据源”。我在项目里把数据源设计成了 YAML 配置而不是硬编码。这是我从 HACS 这类开源项目里学到的扩展点要做得足够简单。例如一个 RSS 源只需要这样一段配置sources: - id: hacker_news type: rss url: https://news.ycombinator.com/rss category: tech interval: 60 - id: github_trending type: html url: https://github.com/trending selectors: article: article.Box-row title: h2 a link: h2 a对于普通 RSS 源配置完就能跑对于网页源需要多写几个 CSS selector。为了降低门槛我后来加了一个“网页抓取测试页”在后台输入 URL 和 selector立刻能看到抓取结果不用改代码反复重启。这个功能在项目早期帮我省了大量的调试时间。真正核心的采集逻辑则由适配器类完成。如果你要接入一个完全不同的协议只需要继承基类实现fetch和parse两个方法然后在配置文件的 type 字段里引用自己的插件路径即可。因为模块化单体的界限清晰理论上你也可以以后把某个 connector 拆出来单独部署成一个 worker。3. AI 不是玩具而是“事件理解流水线”的三道工序3.1 任务一把杂乱文本抽取为结构化事件AI 在我这个项目里并不是花架子。开头一版我试过纯正则加关键词规则去抽取事件效果很差。同样一条新闻标题“NVIDIA 发布新一代架构”可以有不同的写法但“发布”这个动作、“NVIDIA”这个主体可以被大量词汇模糊覆盖。规则写到最后又长又脆。后来我把信息抽取拆成了三个大模型子任务。第一道工序是“事件六要素抽取”给定原始标题和正文让模型输出一个 JSON包含事件时间、地点、主体、动作、对象、关联链接外加一个事件类型。用提示词约束模型“没有信息就填 null不要猜测”。举个例子输入“某开源数据库在凌晨发布了 3.0 版本并宣布将支持向量索引功能”输出会被整理成{ event_type: software_release, subject: 某开源数据库, action: release, object: 3.0, key_points: [support_vector_index], published_at: 2024-06-01T00:00:00Z, url: https://..., importance: 7 }这种结构化结果比纯文本摘要强很多因为后续做过滤、排序、关联都变得非常方便。我可以在 Postgres 里直接按事件类型查询“今天有哪些软件 release”也可以按主体做时间线。3.2 任务二跨源去重避免一个事件重复轰炸事件终端的核心体验就是“不要重复”。一段新闻可能同时出现在很多网站、社区、电报频道。如果不做跨源去重你的终端会像复读机一样。传统去重靠标题相似度但互联网标题经过人工改写、缩写、追加后缀普通的字符串哈希根本不可靠。我的方案是三层去重URL 指纹同一链接只抓一次。文本 SimHash对归一化后的标题和正文首段计算 64 位 SimHash两两汉明距离小于 3 就认为是同一事件放到待合并池。AI 语义确认对前两层判断为“疑似重复”的候选对调用一次大模型判断“它们是否在描述同一事件”并返回保留哪条正文。用上 AI 语义确认后误杀率大幅下降。比如两个网页都在讲同一场发布会但一个重点在 GPU一个重点在软件栈SimHash 会比较高但它们其实是同一个事件的不同报道角度。模型会回答“是同一事件建议合并补充两个来源链接”。这一步能显著提高信息质量。最终推给用户的事件都是“唯一事件对象”底下管理着多个证据链接而不是一个孤零零的标题。用户点开看到的报告里会列出“哪些网站都在说这件事”信任度会高很多。3.3 任务三自动生成摘要和风险等级抽取 JSON 只是“看懂了”但用户真正需要的是“读得快、判断快”。第三道工序负责生成两个东西一段不超过 150 字的导读摘要一个“重要度/紧急度”打分。摘要要求只基于输入文本改写不能添加原文不存在的信息。风险等级我定义为三个维度。影响范围是影响特定几个人还是某个行业速度是事件刚开始发酵还是已经结束可信度原始来源的权威程度如何模型对这三个维度各输出 0 到 10 分加权后映射成 P0、P1、P2、P3 四个优先级。通过这套打分我就能在实时面板上把 P0 事件置顶、变红并让推送渠道只发 P0/P1 的事件。否则半夜三点一个无关紧要的版本更新也会把手机震醒。3.4 AI 辅助开发的隐性收益单人也能搞出全栈项目标题里说“用 AI 搓出来的”这里除了指产品里的 AI 能力也指开发过程本身。老实说如果没有 AI 编程助手我一个人在 6 周内搞完从爬虫、后端、前端到部署运维的全栈项目几乎不可能。具体来说AI 在开发中帮了三种忙。第一生成 boilerplate所有连接器的骨架、FastAPI 路由、SQLAlchemy 模型先让 AI 批量生成我再把接口规范对齐省掉大量重复劳动。第二正则和选择器调试给 AI 一段 HTML让它写出对应的 XPath 或 CSS selector比人肉看 DOM 快得多。第三修 bug 时的“第二大脑”比如 Redis Stream 的 pending entries 一直不消失我扫代码容易漏但把相关代码和报错贴给 AI它会建议检查消费者组的 ack 逻辑果然一查就是那里。但我也要提醒AI 并不是万能也不是把问题丢给它就好。它经常给出看起来合理但过时的代码尤其是依赖版本变动比较快的库必须自己跑测试验证。另外如果没有能力读懂 AI 写的代码后期维护会非常痛苦。所以我把 AI 定位成会说话的结对编程对象而不是甩手掌柜。核心架构始终是我自己定的AI 负责当高级外援。4. 实时并不意味着疯狂加机器我把延迟压到 2 秒的优化记录4.1 数据源实时度分级不是所有源都值得秒级抓取所谓“实时终端”最忌讳的就是把所有数据源都设置为 5 秒抓一次。很多网站没有这么高的更新频率频繁抓取纯属增加对方服务器压力还可能被封 IP。我对源做了分级。S 级明确支持 Webhook 或 WebSocket 的源一旦有事件立刻推送例如 GitHub 的 webhook、交易所的公开行情流。 A 级RSS/Atom 源支持条件请求可以接受 60 秒左右的延迟通过 ETag 和 Last-Modified 减少无效请求。 B 级普通网页通过合理的轮询间隔抓取默认 300 秒一次根据站点更新频率动态调整。这套分级的好处是真正的“实时”只保留在 S 级源其他源用较低的频率即可。既不烧资源也不会因为过度抓取导致被封。实现时我还给每个源加了“忙碌度”自适应如果连续多次抓取发现内容没有变化就自动拉长下一次间隔到 5 分钟如果抓取到新内容就缩短间隔到配置的最小值。这个更像是爬虫要懂礼貌对长期稳定性很有帮助。4.2 关键链路优化消息队列和请求并发之间的平衡当采集层把消息喷进队列以后AI 分析层很容易成为瓶颈。最开始我是所有事件逐条调云端大模型后来发现 RSS 源经常一次性抓回 50 条新闻如果逐条串行调用延迟直接爆炸。优化手段分了三层。第一采集层不再逐条入队而是批量聚合。把同一批次抓到的内容打包成一个“源批次消息”分析 worker 收到后再做批量预处理。能走本地小模型分类的先过滤只有触发 AI 深度分析的事件才调用大模型。这一步直接让大模型调用量下降了 70%。第二对可能阻塞的请求全部设置超时和重试上限。一开始有些源挂了爬虫一直等套接字超时一个 worker 卡好几分钟。加上asyncio.timeout后每个请求最长等待 15 秒失败就立刻降级并进入重试队列。刚开始还不理解为什么要定制超时后来看到日志里一次抓取耗时 5 分钟才发现这个坑有多深。第三合理控制并发数。我用信号量限制了同时打开的 HTTP 连接数和模型调用数避免大流量时把数据库连接池打爆。实时效果不是无脑并发堆出来的很多时候队列里积压 2000 条消息你控制好消费者粒度保证每条链路不阻塞才是持续的稳定。4.3 前端实时刷新SSE 比 WebSocket 更适合“读多写少”最初我原计划用 WebSocket 实现浏览器面板的数据推送做了两天发现没必要。GodEye 的推送方向非常固定后端到浏览器且浏览器几乎不需要给后端发消息。对于这种读多写少的场景SSEServer-Sent Events明显更轻量也更稳定。SSE 走普通 HTTP对 Nginx 等代理非常友好不需要额外处理 WebSocket 升级。断线重连也更加自动浏览器 EventSource API 自带重连机制不用像 WebSocket 那样自己实现心跳和恢复。唯一不足是单向但我的前端只是实时刷新事件列表不需要往服务器推消息所以完全够用。后端推送链路我用了 Redis Pub/Sub。AI worker 分析完一条事件并写入 Postgres 后会发布一条带事件 ID 的消息到 Redis。后端进程订阅这个 channel然后通过 SSE 输出给所有前端连接。为了让用户心跳不乱跳每条消息都带顺序号前端收到后增量更新列表而不是刷新整页。实测下来从数据源发布内容到 GodEye 面板出现事件S 级源延迟一般在 1 到 2 秒A 级源由于轮询间隔会有 30 到 60 秒的延迟但相比之前每天手动刷网页已经完全是两个体验。在优化记录里我还给每条事件打上了fetch_time、parse_time、ai_analyze_time、push_time四个时间戳方便做链路瓶颈分析。这是我从以前做数据管道养成的习惯没有观测就没有优化。5. 单兵作战最容易翻车的五个细节我全踩过5.1 反爬和网页改版你的爬虫今天可能已经死了一个人做聚合类项目最脆弱的环节永远是外部页面改版。某个源如果不提供 RSS只给 HTML那我的 HTML 解析器就得依赖页面结构。CSS selector 写得再健壮也架不住前端随便改个 class 名。上一秒还好好的采集器下一秒可能全部抓成空值。我的应对思路是做“解析健康度监控”。每个源的采集结果都有一个“解析成功率”字段如果连续几次成功率为 0系统会在管理后台亮红灯同时通过 webhook 通知我。这样我就不会等到用户问“怎么三天没更新了”才发现。同时给解析器增加多组 selector 配置做成资源列表而不是只依赖单一路径。例如要抓标题可以配置h2 a和.title-link等多个候选按顺序尝试。这样即使前端改版只要没改得面目全非还能自动兜底。5.2 事件去重只靠 MD5 会掉进转载地狱去重是这类终端最影响体验的模块。最开始我天真地认为给 URL 做 MD5 就够了但同一个事件经常有不同的 URL。我也试过纯标题相似度比如用 Python 的 difflib但标题一旦加了站点名后缀或中间加表情、空格相似度算法经常误判。后来我去翻了搜索系统常用的 SimHash 方案才发现去重是一套“粗排精排”的逻辑。先计算两个标题的 SimHash 汉明距离距离小于阈值就进入候选池再用大模型做最终判断。为了避免每条都请求大模型我加了一个布隆过滤器缓存已有的事件标题配合 PG 里的全文索引几乎只有真正的新事件才会触发大模型。这套方案在误杀方面明显比纯规则好。尤其对于跨语言的同一事件比如中文媒体和英文媒体同时在报道某个海外项目发布文本完全不重合但 SimHash 和关键词匹配都找不到相似性。这里我用嵌入向量算余弦相似度再结合发布时间窗口和实体名称重合度去衡量效果才真正可接受。5.3 日志和告警必须第一周就做别等出现事故再补个人项目最容易偷懒的就是可观测性。刚开始我觉得自己盯着终端跑就行结果某个深夜队列积压页面空白我却毫无感觉。第二天起床才看到日志里全是超时。那类消息没用因为没有告警你根本不会实时去看日志。后来我引入结构化日志每条事件输出 JSON 格式的日志包含源 ID、处理阶段、耗时、是否成功。我又写了一个轻量健康检查接口每隔 30 秒检查一次 Redis 队列长度、AI worker 心跳、数据库连接数超过阈值就通过 Server 酱或企业微信机器人推送告警。从此再也不会半夜睡不着点开了。5.4 缓存和数据库膨胀无人值守系统的“慢刀子”实时系统跑得越久状态越多。一个月后我发现 PostgreSQL 已经占了十几 GBRedis 里积压了无数消费确认状态。最可怕的是 Redis Stream 一直增长没有任何过期策略最后把磁盘塞满了。解决办法是每类数据定生命周期。原始消息只保留 7 天标准化事件保留 90 天重要事件可以长期保留。清理任务用 APScheduler 定时扫表删除过期数据并 VACUUM避免表膨胀造成查询越来越慢。如果以后数据量真的大到需要分库结构上已经预留了事件表按日期分区的空间。5.5 不要拿生产机器当试验田我这个项目一开始是直接在自己的服务器上开发的结果 Python 依赖更新把系统搞乱连带服务挂了。后来学乖了所有镜像打包进 Docker Compose宿主机只需要有 docker 和 docker compose。连数据库数据都挂载到卷里重装系统也能一键恢复。对于任何一个人维护的开源项目发布环境越简单越好。不是说你的机器性能要强而是要让部署过程可重复。现在用户拉下仓库后改几行环境变量docker compose up -d就能跑起来。这也是这个项目能拿到一些 star 的重要原因。6. 开源之后项目的形态完全变了6.1 从“自己能跑”到“别人能跑”代码写出来自己用很爽但开源是完全不同的工程问题。早期版本有很多我自己的隐式约定比如某个环境变量不写就在代码里给了默认值某个数据源挂了就静默跳过也不告诉用户。开源后这些都会被骂死。所以发布前我做了一份很详细的部署文档从 Python 版本、系统依赖到每个环境变量的含义全部写清楚。配置示例也尽量简单用户只要把env.example复制成.env改两个密码就能启动。文档写“保姆级”没什么丢人的能减少大量 issue。项目还加了“自我检查”启动逻辑如果环境变量配置不对会在启动日志里直接提示哪里错了而不是等请求进来才报错。这是开源项目的基本礼仪。6.2 用户需求把我推向了“插件化”原本的源都是内置适配器后来不断有用户提交新源请求。我不想针对某个网站单独维护逻辑也不希望每个用户都 fork 一份代码改。因此我把 connector 做成了插件化数据源定义、解析逻辑、AI 抽取模板都支持外部覆盖。第三方可以直接写一个独立的 Python 包放到plugins目录系统启动时自动发现加载。另一个来自社区的建议是告警渠道。一开始只写了一种通知方式马上有人问能不能接入钉钉、Slack、Telegram。我意识到核心发布应该抽象成“目标”用户只需配置一个 Webhook URL不同的渠道通过一个轻量转换层支持。现在项目已经可以一键推送到钉钉、飞书、Slack、Telegram还会按 P0/P1 级别走不同的通知策略。6.3 我现在对个人 AI 项目的新体会GodEye 开发到开源这段时间我最大的体会是AI 不是用来代替你做决定的它更像给了你一个可以随时对话、随时帮忙写模板代码的 junior 工程师。真正的架构判断、边界控制仍然靠人来完成。如果你也想从零开始做类似的东西我会建议第一版尽量窄。先盯住一个领域的几个源把采集、AI 分析、推送跑通再横向扩展数据源数量。功能膨胀是这个项目最大的敌人我中途也不断想把所有功能都塞进去最后被迫做了很多减法。关于开源我也调整了心态。早期总怕代码写得不够好、被人笑话但真的放出去以后收获的反馈远远超出预期。有人帮你找 bug有人帮你补文档这种正循环是一个人开发时永远体会不到的。GodEye 现在依然在迭代我已经把下一阶段的路标写在 README 里更多插件化数据源、更好的跨语言实体对齐、更便宜的轻量模型优先策略。如果你也盯着几十个网页却总觉得会漏掉什么不妨试试自己动手或者直接基于这个项目改一版把它变成真正符合自己信息需求的雷达。这个外号有点大但至少对个人公开信息的实时掌握它已经把能做到的事情做到了。
返回列表