AI 开源项目健康度分析与排名计划¶
背景与目标¶
我们的网站收录了数百个 AI 开源项目资源,目前仅手动维护基本信息。为了直观反映每个项目的活跃程度和健康状况,我们计划引入自动化的 GitHub 指标分析 和 评分系统。目标包括:
- 项目活跃度监测:定期检查项目是否仍在维护。重点关注最近的提交更新时间,及时标记出长期未更新的项目(例如超过一年未更新的项目)。
- 项目健康度评分:综合考虑 Star 数、人气、提交频率、Issue 活动等多维度指标,为每个项目计算一个综合得分,反映项目受欢迎程度和维护健康状况。
- 标签和视觉标识:根据指标给项目打标签,例如「新项目」(最近三个月内开源)、「人气项目」(Star 数超过 5000)等。在界面上,对不活跃的项目提供特殊样式(如封面灰度处理)和标签提示,方便用户一眼识别项目状态。
- 前端展示优化:在网站 AI 列表页 的项目卡片 (AI card) 上直接展示上述标签和综合评分;在 AI 详情页 中增设项目指标的详细展示(条形图或数值),避免用户二次跳转。
- 自动定期更新:通过后端任务每周自动刷新数据,保持指标最新。采用 Cloudflare Workers + D1 数据库实现,无需在前端通过 JS 实时获取,保证页面加载性能。
上述功能将使得页面直观显示每个开源项目的活跃程度和健康度,让访问者快速判断项目现状。
项目健康指标体系¶
单一指标(如 Star 数)不足以全面反映项目健康。我们参考 CHAOSS 开源健康度指标模型并结合实际,确定以下关键指标用于评估项目:
- ⭐ 人气(Popularity):项目的受关注程度。主要采用GitHub Stargazers(Star)数,可辅以Fork 数 作为参考。Star 数高表示受关注度高,但需结合其他因素判断项目是否真正成功或只是暂时流行。我们将对 Star 数进行适当的对数或分级处理,防止少数超高 Star 项目拉高平均值。另可考虑 Watchers 数(通常与 Star 类似)和 Fork 数作为人气的补充。
- 🛠 活跃度(Activity):项目的开发维护活跃情况。采用 最近提交时间 和 提交频率 来衡量。具体包括:GitHub 上的
pushed_at
时间(最后一次提交或合并的时间)以及过去一段时间(如最近 3 个月)的提交次数。近期有提交则表示项目仍在维护,否则可能停止。我们将定义分级规则,例如:最近 1 个月有提交=活跃度满分,3 个月内=高,6 个月=中,超过一年无更新=活跃度接近 0。GitHub 仓库的updated_at
字段和提交历史将用于计算这些值。对于提交频率,可调用 GitHub 提供的提交统计接口或利用最近提交记录计算。 - 👥 社区参与(Community):项目社区的活跃度和参与度。考虑Issue 和 PR 活动 以及贡献者数量 等。具体指标包括:开源以来累计的 Issue 数/PR 数及其关闭率,最近活跃的 Issue 数,项目的贡献者总数等。一个健康项目通常有一定的社区互动(有人提 Issue 且维护者响应)和多位贡献者参与(而非单人项目)。例如,我们会获取 GitHub 的
open_issues_count
(未关闭的问题/PR 数)以及通过贡献者 API 获取贡献者总数。贡献者数可以反映项目的“总线因子”(主要维护者人数),这一点也是 CHAOSS 强调的健康指标之一。我们将赋予贡献者数量一定权重:维护者越多项目越不易因单人离开而停滞。 - 📈 成熟度(Project Maturity):项目的发布和演进情况。这里可选考虑 项目创建时间(开源日期)和 Release 发布频率。新近开放源代码的项目(如 3 个月内)标记为“新”,代表项目处于起步阶段;而运行多年但长时间无重大更新的项目可能进入停滞。我们已通过
oss_date
字段记录了项目的开源日期(从仓库创建时间获取)以判断项目年龄。发布版本(Release)频率也是衡量项目成熟和活跃的依据之一,可通过 GitHub Releases API 获取最近发布日期或发布次数。但在首版实现中,Release 频率非必要,可作为扩展指标。
以上指标分别衡量不同方面,我们将对它们进行加权综合计算出一个百分制综合评分。比如,给予活跃度和人气各 40% 的权重,社区参与 20% 的权重(具体权重可调优),计算公式:
综合得分 = 0.4×人气得分 + 0.4×活跃度得分 + 0.2×社区参与得分(满分 100)
- 人气得分 可根据 Star 数在一定区间映射 0-100 分(例如参考对数刻度或分段:Star ≥ 10000 记为 100,5000≈90,1000≈75,100≈50,<50≈20 分等)。
- 活跃度得分 由最近无更新的时长和提交频率决定:1 个月内有更新=100,3 个月=80,6 个月=60,12 个月=40,>12 个月=0 分(线性或分段降低)。也可叠加最近 3 个月的提交次数(例如≥20 次=满分,5 次=中等,1 次=最低)作微调。
- 社区参与得分 综合贡献者人数和 Issue 活跃:例如≥10 位贡献者=100 分,5 位=80,1 位=50 分;以及最近打开的 Issue 数量、Issue 响应率等。由于获取 Issue 响应率较复杂,初期可用贡献者数和 open issues 数量近似衡量。
通过综合评分,能较客观地反映项目是否既受欢迎又维护良好。这样可避免仅凭 Star 高就误判项目健康——例如某项目 Star 很多但实际很少更新、Issue 寥寥,我们的评分会因为活跃度和社区分低而降低,反映出其潜在风险。
项目标签与状态标识¶
根据上述指标和阈值,我们将为项目动态生成若干状态标签,在 UI 上突出显示:
- 「新项目」标签:若项目开源时间在最近三个月内(根据 front matter 的
oss_date
或仓库创建日期),则认为项目很新,自动添加“New”标识。这有助于用户识别最新出现的项目。该标签在 AI 卡片上以醒目的样式标出,可能使用不同颜色的徽章表示。 - 「人气项目」标签:若项目 Star 数达到一定高度(初步定为 ≥5000 星),则标记为“人气项目”。这表示该项目在社区中非常受关注。UI 上可用“Hot”或🔥图标标注。这个阈值可根据整体项目 Star 水平调整(例如资源列表整体 Star 偏高则可提高门槛)。
- 「不活跃」标签(项目死亡):对于超过一年无任何更新的项目(没有代码提交,或者仓库已标记为 Archived),认为已基本停止维护。此类项目我们将在 AI 卡片上灰度处理其缩略图,并加上如“Archived”(已归档)或“Inactive”(不活跃)的标签提示用户。灰度效果通过在图片或卡片容器上添加 CSS 类实现(利用
filter: grayscale(100%)
等样式),直观暗示项目失去活力。若仓库在 GitHub 上被标记为 Archived,我们也视为不活跃并做相同处理。 - 其他可能的标签:
- 持续活跃: 可选,对最近一个月内有频繁更新的项目标记“Active”或闪电图标,以凸显高度活跃项目(此信息已反映在评分上,是否额外标签待定)。
- 单一维护者: 若贡献者数极少(如仅 1 人),可标注“单人维护”之类提示,但此可能不作为 UI 标签,仅在详情页注明。
上述标签主要在列表页的卡片上展示,和已有的 tags (例如“OSS”, “工具类别”等) 区分开。实现上,这些标签不直接写入 Markdown front matter,而是在渲染时根据数据库中的最新数据动态添加,确保及时性且无需手工更新 Markdown。
数据收集与分析实现方案¶
我们将开发一个 Cloudflare Workers 后端服务 来自动抓取 GitHub 数据、计算指标并存储结果。具体流程如下:
项目列表获取¶
首先需要获取所有需要分析的项目仓库列表。我们将针对内容目录 content/*/ai/
下的 Markdown 资源,筛选出 front matter 中含有 github
字段的项目。这可以通过以下方式实现:
- 离线预处理:在本地运行脚本遍历
content/zh/ai
和content/en/ai
目录,解析每个index.md
提取其中的github
URL 或仓库名称,并将列表上传至数据库。由于目前已有约 379 个资源,我们可以初始批量导入一次。之后新增的资源可以手动触发脚本同步,或采用定期扫描(频率低一些,如每周)以发现新项目。 - 在线解析:亦可在 Worker 中通过 GitHub API 获取仓库内容列表。例如使用 GitHub 的内容 API 列出
content/zh/ai
目录下文件并读取 front matter。这种方法实现复杂且消耗 API 配额,不是必要。鉴于资源清单变化不频繁,我们倾向于离线维护或通过网站构建流程导出列表。
注意:无论采用何种方式,我们都会去重仓库列表,因为一个项目在中英文两个版本中重复出现。可通过标准化仓库“owner/name”字符串作为唯一键来避免重复抓取。
调用 GitHub API 获取指标¶
利用 GitHub 提供的 API 批量获取每个项目的关键数据:
- 首选 GitHub REST API 的仓库接口,如
GET /repos/{owner}/{repo}
,直接返回 JSON 包含 stargazers_count(Star 数), forks_count(Fork 数), open_issues_count(未关闭 Issue+PR 数), watchers_count, created_at, updated_at, pushed_at(最后一次 push 时间), archived(是否已归档)等字段。这些字段涵盖了大部分我们需要的原始指标数据。 - 提交次数:REST 仓库对象不直接给出总提交数或近期提交数。我们可采用两种方法补充:
- 使用 GitHub 统计接口,如
GET /repos/{owner}/{repo}/stats/commit_activity
,获取过去一年每周提交次数,从中计算最近几个月的提交总数。但要注意该接口可能需要缓存,调用频率有限。 - 简单方法:调用
GET /repos/{owner}/{repo}/commits?per_page=1
并从响应的 Link header 中提取last
页页码,近似得到总提交数(这是常用技巧)。总提交数可以用于衡量项目迄今的开发投入量。如果一个项目 Star 很多但总提交寥寥,那么可能暗示项目功能简单或后续维护不足。 - 贡献者数:GitHub 也无直接字段给出贡献者总数。可通过
GET /repos/{owner}/{repo}/contributors?per_page=1&anon=1
,同样利用 Link header 得到贡献者人数。我们会获取此数据来评估维护者规模。 - Release 数:可选地,通过
GET /repos/{owner}/{repo}/releases?per_page=1
类似方法获取发布版本数量,以了解项目发布活跃度。 - 使用 GraphQL 优化(可选):为减少 API 调用次数,我们可考虑 GitHub GraphQL API,一次查询多个仓库的指标。但由于仓库较多(数百个),GraphQL 查询可能过长或复杂,需要分页。初始实现可以 REST 逐个请求,后续再优化批量获取。如果使用 GraphQL,可以在一个查询中请求多个 repo 的所需字段(Star 数、Fork 数、pushedAt 等)以及一定范围的 commit 历史计数。
为避免触发 GitHub 的速率限制,我们将:
- 使用 GitHub 个人访问令牌(PAT)进行认证,提高速率限制上限(未授权每小时 60 次,授权后每小时 5000 次)。
- 控制并发或添加少许延时,避免瞬间请求过多。可以每次更新时对数百项目顺序抓取,几分钟内完成,这在允许范围内。
- 对于长期不变的数据如
created_at
(开源日期)我们可以只获取一次保存,后续不变。但考虑实现简单,REST 接口都会返回,我们统一保存更新。 - 若某些 API 请求失败或超时,我们做好重试机制,最多重试 3 次。如仍失败则跳过该项目并记录警告,不影响其他项目更新。
数据处理与健康度计算¶
拿到每个项目的原始数据后,我们在 Worker 中进行计算和分类:
- 活跃状态判断:根据
pushed_at
与当前时间差计算未更新时长(月)。标记是否超过半年或一年。 - 新项目判断:根据
created_at
(或我们存储的oss_date
)与当前时间差,判断是否在 3 个月以内。 - 计算各项指标得分:按前述方法计算人气、活跃度、社区参与三个维度的评分(0~100)。例如:Star 数通过对数映射到 0-100,最后提交时间通过阈值映射分值,贡献者数/Issue 等映射社区分。
- 综合评分:按选定权重汇总三个子得分得到综合评分(如百分制)。我们可在计算后四舍五入保留整数,以便前端展示。
- 标签字段:根据阈值条件设置布尔标志或枚举值:
is_new
、is_popular
、is_inactive
等,用于前端快速判断添加相应标签。 - 灰度标识:如果项目 Inactive 或 Archived,设置例如
inactive=true
来提示前端对其缩略图应用灰度 CSS。
示例:某项目 Star 数 8000,最后提交于 2 个月前,贡献者 10 人,open issues 5 个。计算得知人气分 90(Star 接近热门阈值)、活跃度分 80(2 个月无更新稍降低)、社区分 95(贡献者多,issue 也不多)。综合得分=0.4×90+0.4×80+0.2×95=约 86 分。标记为
is_popular=true
(Star>5000),不是新项目(开源>3 月),不是 inactive(2 个月有更新)。
存储至数据库¶
将获取到的指标和计算结果写入 Cloudflare D1 数据库中。我们将在 D1 上建立一个表来保存项目及其指标,设计如下:
表:ai_projects (或类似名称) 字段示例:
repo_id
(文本主键):仓库标识符,采用owner/name
形式确保唯一性。name
:项目名称(可选,GitHub 返回的仓库名)。stars
:Star 数(整数)。forks
:Fork 数(整数)。open_issues
:未关闭 Issues 总数(整数)。watchers
:Watch 数(整数,可选)。contributors
:贡献者数量(整数)。created_at
:仓库创建时间(ISO 字符串)。pushed_at
:最后提交时间(ISO 字符串)。archived
:是否已归档(布尔)。commit_count
:总提交次数(整数)。score_popularity
:人气评分(0-100 整数)。score_activity
:活跃度评分(0-100 整数)。score_community
:社区评分(0-100 整数)。score_overall
:综合评分(0-100 整数)。is_new
:新项目标志(布尔)。is_popular
:人气项目标志(布尔)。is_inactive
:不活跃标志(布尔)。last_updated
:最后一次更新本记录的时间戳。
存储时采用 UPSERT 逻辑:如果该 repo 记录已存在则更新各字段,否则插入新记录。这保证后续每周运行时数据被刷新。通过保存计算后的结果,前端展示时无需每次计算,直接读取即可。
数据库的选择是Cloudflare D1 (SQLite),好处是可以在 Cloudflare Pages Functions 中方便地查询,且数据持久化由后端维护,不用修改原有 Markdown 内容。D1 数据量很小(几百行),查询性能极高。后续如果需要,也可以在数据库中保留历史快照用于趋势分析,但当前需求每次覆盖最新数据即可。
自动定期更新(实际实现说明)¶
- 调度频率:实际
tools/ai-oss-rank-worker
的调度采用每天运行(daily)策略,默认每天执行一次数据抓取与更新。可在运行环境(例如 wrangler.toml 的 cron 配置或 CI 的 schedule)中调整为更高/更低频率,但默认实现是每日触发以保证指标较及时。 - 分批策略:与设计一致,更新过程采用“分批处理”以降低并发与速率限制风险。每次执行会处理 manifest 中的若干仓库(默认每批最多 10 个),处理完一批后记录游标(cursor),下次从该游标继续,直到完成所有仓库或达到批次上限。
数据更新语义(关键点)¶
- UPSERT 而非盲目追加:每个仓库在数据库中的记录以唯一键(如 repo_slug + locale)标识,工具对单个 repo 执行 UPSERT(若存在则更新字段,否则插入新行)。因此正常日常更新不会导致同一仓库产生重复行,表内行数保持与仓库数量近似稳定。
- sync_state 游标:工具通过
sync_state
(或类似)的持久化记录保存当前处理进度(cursor)。定时任务会读该游标并继续处理下一批,回填脚本可选择 reset 游标以从头重跑。 - 历史记录策略:默认实现不保存每日快照;ai_projects 表仅保持最新计算结果。如需保留历史,请启用/创建
ai_projects_history
(或在回填脚本中额外写入历史表)来保存时间序列快照。默认不会无限增长行数,历史表为可选扩展。 - 重试与错误处理:若某次对单 Repo 的抓取失败,工具会记录错误并在下一次批处理中重试。严重失败可通过手动回填或重置游标重试全部或部分仓库。
手动更新与回填¶
- 手动触发(API):当前工具实现提供用于手动触发的接口(例如
POST /recalculate
或GET /recalculate?repo=owner/name
),可以只针对单个仓库或指定一组仓库触发更新(具体接口以 tools/ai-oss-rank-worker 源码为准)。在受限环境下,也可以通过 CLI/脚本直接运行回填命令。 - 回填脚本:仓库内提供的回填脚本(例如
npm run backfill
或 tools 内的 backfill 工具)会循环调用更新接口或直接执行抓取逻辑,直到完成所有未处理仓库。回填过程同样使用 UPSERT,因此在回填完成后表中行数只会包含每个仓库的一条最新记录(除非启用了历史表)。 - 在 CI/本地运行:可在本地或 CI 中运行回填以完成首次数据导入,例如:
- 本地: npm install && npm run typecheck && node ./tools/ai-oss-rank-worker/dist/backfill.js --limit=10
- CI:在工作流中调用回填脚本并将结果提交或上传到存储
- 重置游标:需要从头重新计算时,可通过提供 resetFlag(例如
resetState=true
)的接口或删除/重置sync_state
表记录来强制从头开始回填。
数据行增长说明(回答疑问)¶
- 日常运行是否会“每天插入新行”?不会。默认行为是按 repo 唯一键更新已有记录(UPSERT),因此不会因为每天运行而无限增加相同 repo 的重复行。
- 数据库里 row 总数是否会增长?在默认配置下,ai_projects 表的行数与被跟踪的仓库数量基本一致,不会随每天更新而爆炸性增长。只有在启用历史快照表(ai_projects_history)或额外日志表时,才会产生历史性增长。
- 如何查看当前进度与最后更新时间?工具会在 ai_projects 表中写入
last_updated
(或类似字段),并在 sync_state 中记录游标与上次运行时间,便于运维查看与调试。
项目标签与状态标识¶
根据上述指标和阈值,我们将为项目动态生成若干状态标签,在 UI 上突出显示:
- 「新项目」标签:若项目开源时间在最近三个月内(根据 front matter 的
oss_date
或仓库创建日期),则认为项目很新,自动添加“New”标识。这有助于用户识别最新出现的项目。该标签在 AI 卡片上以醒目的样式标出,可能使用不同颜色的徽章表示。 - 「人气项目」标签:若项目 Star 数达到一定高度(初步定为 ≥5000 星),则标记为“人气项目”。这表示该项目在社区中非常受关注。UI 上可用“Hot”或🔥图标标注。这个阈值可根据整体项目 Star 水平调整(例如资源列表整体 Star 偏高则可提高门槛)。
- 「不活跃」标签(项目死亡):对于超过一年无任何更新的项目(没有代码提交,或者仓库已标记为 Archived),认为已基本停止维护。此类项目我们将在 AI 卡片上灰度处理其缩略图,并加上如“Archived”(已归档)或“Inactive”(不活跃)的标签提示用户。灰度效果通过在图片或卡片容器上添加 CSS 类实现(利用
filter: grayscale(100%)
等样式),直观暗示项目失去活力。若仓库在 GitHub 上被标记为 Archived,我们也视为不活跃并做相同处理。 - 其他可能的标签:
- 持续活跃: 可选,对最近一个月内有频繁更新的项目标记“Active”或闪电图标,以凸显高度活跃项目(此信息已反映在评分上,是否额外标签待定)。
- 单一维护者: 若贡献者数极少(如仅 1 人),可标注“单人维护”之类提示,但此可能不作为 UI 标签,仅在详情页注明。
上述标签主要在列表页的卡片上展示,和已有的 tags (例如“OSS”, “工具类别”等) 区分开。实现上,这些标签不直接写入 Markdown front matter,而是在渲染时根据数据库中的最新数据动态添加,确保及时性且无需手工更新 Markdown。
数据收集与分析实现方案¶
我们将开发一个 Cloudflare Workers 后端服务 来自动抓取 GitHub 数据、计算指标并存储结果。具体流程如下:
项目列表获取¶
首先需要获取所有需要分析的项目仓库列表。我们将针对内容目录 content/*/ai/
下的 Markdown 资源,筛选出 front matter 中含有 github
字段的项目。这可以通过以下方式实现:
- 离线预处理:在本地运行脚本遍历
content/zh/ai
和content/en/ai
目录,解析每个index.md
提取其中的github
URL 或仓库名称,并将列表上传至数据库。由于目前已有约 379 个资源,我们可以初始批量导入一次。之后新增的资源可以手动触发脚本同步,或采用定期扫描(频率低一些,如每周)以发现新项目。 - 在线解析:亦可在 Worker 中通过 GitHub API 获取仓库内容列表。例如使用 GitHub 的内容 API 列出
content/zh/ai
目录下文件并读取 front matter。这种方法实现复杂且消耗 API 配额,不是必要。鉴于资源清单变化不频繁,我们倾向于离线维护或通过网站构建流程导出列表。
注意:无论采用何种方式,我们都会去重仓库列表,因为一个项目在中英文两个版本中重复出现。可通过标准化仓库“owner/name”字符串作为唯一键来避免重复抓取。
调用 GitHub API 获取指标¶
利用 GitHub 提供的 API 批量获取每个项目的关键数据:
- 首选 GitHub REST API 的仓库接口,如
GET /repos/{owner}/{repo}
,直接返回 JSON 包含 stargazers_count(Star 数), forks_count(Fork 数), open_issues_count(未关闭 Issue+PR 数), watchers_count, created_at, updated_at, pushed_at(最后一次 push 时间), archived(是否已归档)等字段。这些字段涵盖了大部分我们需要的原始指标数据。 - 提交次数:REST 仓库对象不直接给出总提交数或近期提交数。我们可采用两种方法补充:
- 使用 GitHub 统计接口,如
GET /repos/{owner}/{repo}/stats/commit_activity
,获取过去一年每周提交次数,从中计算最近几个月的提交总数。但要注意该接口可能需要缓存,调用频率有限。 - 简单方法:调用
GET /repos/{owner}/{repo}/commits?per_page=1
并从响应的 Link header 中提取last
页页码,近似得到总提交数(这是常用技巧)。总提交数可以用于衡量项目迄今的开发投入量。如果一个项目 Star 很多但总提交寥寥,那么可能暗示项目功能简单或后续维护不足。 - 贡献者数:GitHub 也无直接字段给出贡献者总数。可通过
GET /repos/{owner}/{repo}/contributors?per_page=1&anon=1
,同样利用 Link header 得到贡献者人数。我们会获取此数据来评估维护者规模。 - Release 数:可选地,通过
GET /repos/{owner}/{repo}/releases?per_page=1
类似方法获取发布版本数量,以了解项目发布活跃度。 - 使用 GraphQL 优化(可选):为减少 API 调用次数,我们可考虑 GitHub GraphQL API,一次查询多个仓库的指标。但由于仓库较多(数百个),GraphQL 查询可能过长或复杂,需要分页。初始实现可以 REST 逐个请求,后续再优化批量获取。如果使用 GraphQL,可以在一个查询中请求多个 repo 的所需字段(Star 数、Fork 数、pushedAt 等)以及一定范围的 commit 历史计数。
为避免触发 GitHub 的速率限制,我们将:
- 使用 GitHub 个人访问令牌(PAT)进行认证,提高速率限制上限(未授权每小时 60 次,授权后每小时 5000 次)。
- 控制并发或添加少许延时,避免瞬间请求过多。可以每次更新时对数百项目顺序抓取,几分钟内完成,这在允许范围内。
- 对于长期不变的数据如
created_at
(开源日期)我们可以只获取一次保存,后续不变。但考虑实现简单,REST 接口都会返回,我们统一保存更新。 - 若某些 API 请求失败或超时,我们做好重试机制,最多重试 3 次。如仍失败则跳过该项目并记录警告,不影响其他项目更新。
数据处理与健康度计算¶
拿到每个项目的原始数据后,我们在 Worker 中进行计算和分类:
- 活跃状态判断:根据
pushed_at
与当前时间差计算未更新时长(月)。标记是否超过半年或一年。 - 新项目判断:根据
created_at
(或我们存储的oss_date
)与当前时间差,判断是否在 3 个月以内。 - 计算各项指标得分:按前述方法计算人气、活跃度、社区参与三个维度的评分(0~100)。例如:Star 数通过对数映射到 0-100,最后提交时间通过阈值映射分值,贡献者数/Issue 等映射社区分。
- 综合评分:按选定权重汇总三个子得分得到综合评分(如百分制)。我们可在计算后四舍五入保留整数,以便前端展示。
- 标签字段:根据阈值条件设置布尔标志或枚举值:
is_new
、is_popular
、is_inactive
等,用于前端快速判断添加相应标签。 - 灰度标识:如果项目 Inactive 或 Archived,设置例如
inactive=true
来提示前端对其缩略图应用灰度 CSS。
示例:某项目 Star 数 8000,最后提交于 2 个月前,贡献者 10 人,open issues 5 个。计算得知人气分 90(Star 接近热门阈值)、活跃度分 80(2 个月无更新稍降低)、社区分 95(贡献者多,issue 也不多)。综合得分=0.4×90+0.4×80+0.2×95=约 86 分。标记为
is_popular=true
(Star>5000),不是新项目(开源>3 月),不是 inactive(2 个月有更新)。
存储至数据库¶
将获取到的指标和计算结果写入 Cloudflare D1 数据库中。我们将在 D1 上建立一个表来保存项目及其指标,设计如下:
表:ai_projects (或类似名称) 字段示例:
repo_id
(文本主键):仓库标识符,采用owner/name
形式确保唯一性。name
:项目名称(可选,GitHub 返回的仓库名)。stars
:Star 数(整数)。forks
:Fork 数(整数)。open_issues
:未关闭 Issues 总数(整数)。watchers
:Watch 数(整数,可选)。contributors
:贡献者数量(整数)。created_at
:仓库创建时间(ISO 字符串)。pushed_at
:最后提交时间(ISO 字符串)。archived
:是否已归档(布尔)。commit_count
:总提交次数(整数)。score_popularity
:人气评分(0-100 整数)。score_activity
:活跃度评分(0-100 整数)。score_community
:社区评分(0-100 整数)。score_overall
:综合评分(0-100 整数)。is_new
:新项目标志(布尔)。is_popular
:人气项目标志(布尔)。is_inactive
:不活跃标志(布尔)。last_updated
:最后一次更新本记录的时间戳。
存储时采用 UPSERT 逻辑:如果该 repo 记录已存在则更新各字段,否则插入新记录。这保证后续每周运行时数据被刷新。通过保存计算后的结果,前端展示时无需每次计算,直接读取即可。
数据库的选择是Cloudflare D1 (SQLite),好处是可以在 Cloudflare Pages Functions 中方便地查询,且数据持久化由后端维护,不用修改原有 Markdown 内容。D1 数据量很小(几百行),查询性能极高。后续如果需要,也可以在数据库中保留历史快照用于趋势分析,但当前需求每次覆盖最新数据即可。
自动定期更新(实际实现说明)¶
- 调度频率:实际
tools/ai-oss-rank-worker
的调度采用每天运行(daily)策略,默认每天执行一次数据抓取与更新。可在运行环境(例如 wrangler.toml 的 cron 配置或 CI 的 schedule)中调整为更高/更低频率,但默认实现是每日触发以保证指标较及时。 - 分批策略:与设计一致,更新过程采用“分批处理”以降低并发与速率限制风险。每次执行会处理 manifest 中的若干仓库(默认每批最多 10 个),处理完一批后记录游标(cursor),下次从该游标继续,直到完成所有仓库或达到批次上限。
数据更新语义(关键点)¶
- UPSERT 而非盲目追加:每个仓库在数据库中的记录以唯一键(如 repo_slug + locale)标识,工具对单个 repo 执行 UPSERT(若存在则更新字段,否则插入新行)。因此正常日常更新不会导致同一仓库产生重复行,表内行数保持与仓库数量近似稳定。
- sync_state 游标:工具通过
sync_state
(或类似)的持久化记录保存当前处理进度(cursor)。定时任务会读该游标并继续处理下一批,回填脚本可选择 reset 游标以从头重跑。 - 历史记录策略:默认实现不保存每日快照;ai_projects 表仅保持最新计算结果。如需保留历史,请启用/创建
ai_projects_history
(或在回填脚本中额外写入历史表)来保存时间序列快照。默认不会无限增长行数,历史表为可选扩展。 - 重试与错误处理:若某次对单 Repo 的抓取失败,工具会记录错误并在下一次批处理中重试。严重失败可通过手动回填或重置游标重试全部或部分仓库。
手动更新与回填¶
- 手动触发(API):当前工具实现提供用于手动触发的接口(例如
POST /recalculate
或GET /recalculate?repo=owner/name
),可以只针对单个仓库或指定一组仓库触发更新(具体接口以 tools/ai-oss-rank-worker 源码为准)。在受限环境下,也可以通过 CLI/脚本直接运行回填命令。 - 回填脚本:仓库内提供的回填脚本(例如
npm run backfill
或 tools 内的 backfill 工具)会循环调用更新接口或直接执行抓取逻辑,直到完成所有未处理仓库。回填过程同样使用 UPSERT,因此在回填完成后表中行数只会包含每个仓库的一条最新记录(除非启用了历史表)。 - 在 CI/本地运行:可在本地或 CI 中运行回填以完成首次数据导入,例如:
- 本地: npm install && npm run typecheck && node ./tools/ai-oss-rank-worker/dist/backfill.js --limit=10
- CI:在工作流中调用回填脚本并将结果提交或上传到存储
- 重置游标:需要从头重新计算时,可通过提供 resetFlag(例如
resetState=true
)的接口或删除/重置sync_state
表记录来强制从头开始回填。
数据行增长说明(回答疑问)¶
- 日常运行是否会“每天插入新行”?不会。默认行为是按 repo 唯一键更新已有记录(UPSERT),因此不会因为每天运行而无限增加相同 repo 的重复行。
- 数据库里 row 总数是否会增长?在默认配置下,ai_projects 表的行数与被跟踪的仓库数量基本一致,不会随每天更新而爆炸性增长。只有在启用历史快照表(ai_projects_history)或额外日志表时,才会产生历史性增长。
- 如何查看当前进度与最后更新时间?工具会在 ai_projects 表中写入
last_updated
(或类似字段),并在 sync_state 中记录游标与上次运行时间,便于运维查看与调试。
项目标签与状态标识¶
根据上述指标和阈值,我们将为项目动态生成若干状态标签,在 UI 上突出显示:
- 「新项目」标签:若项目开源时间在最近三个月内(根据 front matter 的
oss_date
或仓库创建日期),则认为项目很新,自动添加“New”标识。这有助于用户识别最新出现的项目。该标签在 AI 卡片上以醒目的样式标出,可能使用不同颜色的徽章表示。 - 「人气项目」标签:若项目 Star 数达到一定高度(初步定为 ≥5000 星),则标记为“人气项目”。这表示该项目在社区中非常受关注。UI 上可用“Hot”或🔥图标标注。这个阈值可根据整体项目 Star 水平调整(例如资源列表整体 Star 偏高则可提高门槛)。
- 「不活跃」标签(项目死亡):对于超过一年无任何更新的项目(没有代码提交,或者仓库已标记为 Archived),认为已基本停止维护。此类项目我们将在 AI 卡片上灰度处理其缩略图,并加上如“Archived”(已归档)或“Inactive”(不活跃)的标签提示用户。灰度效果通过在图片或卡片容器上添加 CSS 类实现(利用
filter: grayscale(100%)
等样式),直观暗示项目失去活力。若仓库在 GitHub 上被标记为 Archived,我们也视为不活跃并做相同处理。 - 其他可能的标签:
- 持续活跃: 可选,对最近一个月内有频繁更新的项目标记“Active”或闪电图标,以凸显高度活跃项目(此信息已反映在评分上,是否额外标签待定)。
- 单一维护者: 若贡献者数极少(如仅 1 人),可标注“单人维护”之类提示,但此可能不作为 UI 标签,仅在详情页注明。
上述标签主要在列表页的卡片上展示,和已有的 tags (例如“OSS”, “工具类别”等) 区分开。实现上,这些标签不直接写入 Markdown front matter,而是在渲染时根据数据库中的最新数据动态添加,确保及时性且无需手工更新 Markdown。
数据收集与分析实现方案¶
我们将开发一个 Cloudflare Workers 后端服务 来自动抓取 GitHub 数据、计算指标并存储结果。具体流程如下:
项目列表获取¶
首先需要获取所有需要分析的项目仓库列表。我们将针对内容目录 content/*/ai/
下的 Markdown 资源,筛选出 front matter 中含有 github
字段的项目。这可以通过以下方式实现:
- 离线预处理:在本地运行脚本遍历
content/zh/ai
和content/en/ai
目录,解析每个index.md
提取其中的github
URL 或仓库名称,并将列表上传至数据库。由于目前已有约 379 个资源,我们可以初始批量导入一次。之后新增的资源可以手动触发脚本同步,或采用定期扫描(频率低一些,如每周)以发现新项目。 - 在线解析:亦可在 Worker 中通过 GitHub API 获取仓库内容列表。例如使用 GitHub 的内容 API 列出
content/zh/ai
目录下文件并读取 front matter。这种方法实现复杂且消耗 API 配额,不是必要。鉴于资源清单变化不频繁,我们倾向于离线维护或通过网站构建流程导出列表。
注意:无论采用何种方式,我们都会去重仓库列表,因为一个项目在中英文两个版本中重复出现。可通过标准化仓库“owner/name”字符串作为唯一键来避免重复抓取。
调用 GitHub API 获取指标¶
利用 GitHub 提供的 API 批量获取每个项目的关键数据:
- 首选 GitHub REST API 的仓库接口,如
GET /repos/{owner}/{repo}
,直接返回 JSON 包含 stargazers_count(Star 数), forks_count(Fork 数), open_issues_count(未关闭 Issue+PR 数), watchers_count, created_at, updated_at, pushed_at(最后一次 push 时间), archived(是否已归档)等字段。这些字段涵盖了大部分我们需要的原始指标数据。 - 提交次数:REST 仓库对象不直接给出总提交数或近期提交数。我们可采用两种方法补充:
- 使用 GitHub 统计接口,如
GET /repos/{owner}/{repo}/stats/commit_activity
,获取过去一年每周提交次数,从中计算最近几个月的提交总数。但要注意该接口可能需要缓存,调用频率有限。 - 简单方法:调用
GET /repos/{owner}/{repo}/commits?per_page=1
并从响应的 Link header 中提取last
页页码,近似得到总提交数(这是常用技巧)。总提交数可以用于衡量项目迄今的开发投入量。如果一个项目 Star 很多但总提交寥寥,那么可能暗示项目功能简单或后续维护不足。 - 贡献者数:GitHub 也无直接字段给出贡献者总数。可通过
GET /repos/{owner}/{repo}/contributors?per_page=1&anon=1
,同样利用 Link header 得到贡献者人数。我们会获取此数据来评估维护者规模。 - Release 数:可选地,通过
GET /repos/{owner}/{repo}/releases?per_page=1
类似方法获取发布版本数量,以了解项目发布活跃度。 - 使用 GraphQL 优化(可选):为减少 API 调用次数,我们可考虑 GitHub GraphQL API,一次查询多个仓库的指标。但由于仓库较多(数百个),GraphQL 查询可能过长或复杂,需要分页。初始实现可以 REST 逐个请求,后续再优化批量获取。如果使用 GraphQL,可以在一个查询中请求多个 repo 的所需字段(Star 数、Fork 数、pushedAt 等)以及一定范围的 commit 历史计数。
为避免触发 GitHub 的速率限制,我们将:
- 使用 GitHub 个人访问令牌(PAT)进行认证,提高速率限制上限(未授权每小时 60 次,授权后每小时 5000 次)。
- 控制并发或添加少许延时,避免瞬间请求过多。可以每次更新时对数百项目顺序抓取,几分钟内完成,这在允许范围内。
- 对于长期不变的数据如
created_at
(开源日期)我们可以只获取一次保存,后续不变。但考虑实现简单,REST 接口都会返回,我们统一保存更新。 - 若某些 API 请求失败或超时,我们做好重试机制,最多重试 3 次。如仍失败则跳过该项目并记录警告,不影响其他项目更新。
数据处理与健康度计算¶
拿到每个项目的原始数据后,我们在 Worker 中进行计算和分类:
- 活跃状态判断:根据
pushed_at
与当前时间差计算未更新时长(月)。标记是否超过半年或一年。 - 新项目判断:根据
created_at
(或我们存储的oss_date
)与当前时间差,判断是否在 3 个月以内。 - 计算各项指标得分:按前述方法计算人气、活跃度、社区参与三个维度的评分(0~100)。例如:Star 数通过对数映射到 0-100,最后提交时间通过阈值映射分值,贡献者数/Issue 等映射社区分。
- 综合评分:按选定权重汇总三个子得分得到综合评分(如百分制)。我们可在计算后四舍五入保留整数,以便前端展示。
- 标签字段:根据阈值条件设置布尔标志或枚举值:
is_new
、is_popular
、is_inactive
等,用于前端快速判断添加相应标签。 - 灰度标识:如果项目 Inactive 或 Archived,设置例如
inactive=true
来提示前端对其缩略图应用灰度 CSS。
示例:某项目 Star 数 8000,最后提交于 2 个月前,贡献者 10 人,open issues 5 个。计算得知人气分 90(Star 接近热门阈值)、活跃度分 80(2 个月无更新稍降低)、社区分 95(贡献者多,issue 也不多)。综合得分=0.4×90+0.4×80+0.2×95=约 86 分。标记为
is_popular=true
(Star>5000),不是新项目(开源>3 月),不是 inactive(2 个月有更新)。
存储至数据库¶
将获取到的指标和计算结果写入 Cloudflare D1 数据库中。我们将在 D1 上建立一个表来保存项目及其指标,设计如下:
表:ai_projects (或类似名称) 字段示例:
repo_id
(文本主键):仓库标识符,采用owner/name
形式确保唯一性。name
:项目名称(可选,GitHub 返回的仓库名)。stars
:Star 数(整数)。forks
:Fork 数(整数)。open_issues
:未关闭 Issues 总数(整数)。watchers
:Watch 数(整数,可选)。contributors
:贡献者数量(整数)。created_at
:仓库创建时间(ISO 字符串)。pushed_at
:最后提交时间(ISO 字符串)。archived
:是否已归档(布尔)。commit_count
:总提交次数(整数)。score_popularity
:人气评分(0-100 整数)。score_activity
:活跃度评分(0-100 整数)。score_community
:社区评分(0-100 整数)。score_overall
:综合评分(0-100 整数)。is_new
:新项目标志(布尔)。is_popular
:人气项目标志(布尔)。is_inactive
:不活跃标志(布尔)。last_updated
:最后一次更新本记录的时间戳。
存储时采用 UPSERT 逻辑:如果该 repo 记录已存在则更新各字段,否则插入新记录。这保证后续每周运行时数据被刷新。通过保存计算后的结果,前端展示时无需每次计算,直接读取即可。
数据库的选择是Cloudflare D1 (SQLite),好处是可以在 Cloudflare Pages Functions 中方便地查询,且数据持久化由后端维护,不用修改原有 Markdown 内容。D1 数据量很小(几百行),查询性能极高。后续如果需要,也可以在数据库中保留历史快照用于趋势分析,但当前需求每次覆盖最新数据即可。
自动定期更新(实际实现说明)¶
- 调度频率:实际
tools/ai-oss-rank-worker
的调度采用每天运行(daily)策略,默认每天执行一次数据抓取与更新。可在运行环境(例如 wrangler.toml 的 cron 配置或 CI 的 schedule)中调整为更高/更低频率,但默认实现是每日触发以保证指标较及时。 - 分批策略:与设计一致,更新过程采用“分批处理”以降低并发与速率限制风险。每次执行会处理 manifest 中的若干仓库(默认每批最多 10 个),处理完一批后记录游标(cursor),下次从该游标继续,直到完成所有仓库或达到批次上限。
数据更新语义(关键点)¶
- UPSERT 而非盲目追加:每个仓库在数据库中的记录以唯一键(如 repo_slug + locale)标识,工具对单个 repo 执行 UPSERT(若存在则更新字段,否则插入新行)。因此正常日常更新不会导致同一仓库产生重复行,表内行数保持与仓库数量近似稳定。
- sync_state 游标:工具通过
sync_state
(或类似)的持久化记录保存当前处理进度(cursor)。定时任务会读该游标并继续处理下一批,回填脚本可选择 reset 游标以从头重跑。 - 历史记录策略:默认实现不保存每日快照;ai_projects 表仅保持最新计算结果。如需保留历史,请启用/创建
ai_projects_history
(或在回填脚本中额外写入历史表)来保存时间序列快照。默认不会无限增长行数,历史表为可选扩展。 - 重试与错误处理:若某次对单 Repo 的抓取失败,工具会记录错误并在下一次批处理中重试。严重失败可通过手动回填或重置游标重试全部或部分仓库。
手动更新与回填¶
- 手动触发(API):当前工具实现提供用于手动触发的接口(例如
POST /recalculate
或GET /recalculate?repo=owner/name
),可以只针对单个仓库或指定一组仓库触发更新(具体接口以 tools/ai-oss-rank-worker 源码为准)。在受限环境下,也可以通过 CLI/脚本直接运行回填命令。 - 回填脚本:仓库内提供的回填脚本(例如
npm run backfill
或 tools 内的 backfill 工具)会循环调用更新接口或直接执行抓取逻辑,直到完成所有未处理仓库。回填过程同样使用 UPSERT,因此在回填完成后表中行数只会包含每个仓库的一条最新记录(除非启用了历史表)。 - 在 CI/本地运行:可在本地或 CI 中运行回填以完成首次数据导入,例如:
- 本地: npm install && npm run typecheck && node ./tools/ai-oss-rank-worker/dist/backfill.js --limit=10
- CI:在工作流中调用回填脚本并将结果提交或上传到存储
- 重置游标:需要从头重新计算时,可通过提供 resetFlag(例如
resetState=true
)的接口或删除/重置sync_state
表记录来强制从头开始回填。
数据行增长说明(回答疑问)¶
- 日常运行是否会“每天插入新行”?不会。默认行为是按 repo 唯一键更新已有记录(UPSERT),因此不会因为每天运行而无限增加相同 repo 的重复行。
- 数据库里 row 总数是否会增长?在默认配置下,ai_projects 表的行数与被跟踪的仓库数量基本一致,不会随每天更新而爆炸性增长。只有在启用历史快照表(ai_projects_history)或额外日志表时,才会产生历史性增长。
- 如何查看当前进度与最后更新时间?工具会在 ai_projects 表中写入
last_updated
(或类似字段),并在 sync_state 中记录游标与上次运行时间,便于运维查看与调试。
项目标签与状态标识¶
根据上述指标和阈值,我们将为项目动态生成若干状态标签,在 UI 上突出显示:
- 「新项目」标签:若项目开源时间在最近三个月内(根据 front matter 的
oss_date
或仓库创建日期),则认为项目很新,自动添加“New”标识。这有助于用户识别最新出现的项目。该标签在 AI 卡片上以醒目的样式标出,可能使用不同颜色的徽章表示。 - 「人气项目」标签:若项目 Star 数达到一定高度(初步定为 ≥5000 星),则标记为“人气项目”。这表示该项目在社区中非常受关注。UI 上可用“Hot”或🔥图标标注。这个阈值可根据整体项目 Star 水平调整(例如资源列表整体 Star 偏高则可提高门槛)。
- 「不活跃」标签(项目死亡):对于超过一年无任何更新的项目(没有代码提交,或者仓库已标记为 Archived),认为已基本停止维护。此类项目我们将在 AI 卡片上灰度处理其缩略图,并加上如“Archived”(已归档)或“Inactive”(不活跃)的标签提示用户。灰度效果通过在图片或卡片容器上添加 CSS 类实现(利用
filter: grayscale(100%)
等样式),直观暗示项目失去活力。若仓库在 GitHub 上被标记为 Archived,我们也视为不活跃并做相同处理。 - 其他可能的标签:
- 持续活跃: 可选,对最近一个月内有频繁更新的项目标记“Active”或闪电图标,以凸显高度活跃项目(此信息已反映在评分上,是否额外标签待定)。
- 单一维护者: 若贡献者数极少(如仅 1 人),可标注“单人维护”之类提示,但此可能不作为 UI 标签,仅在详情页注明。
上述标签主要在列表页的卡片上展示,和已有的 tags (例如“OSS”, “工具类别”等) 区分开。实现上,这些标签不直接写入 Markdown front matter,而是在渲染时根据数据库中的最新数据动态添加,确保及时性且无需手工更新 Markdown。
数据收集与分析实现方案¶
我们将开发一个 Cloudflare Workers 后端服务 来自动抓取 GitHub 数据、计算指标并存储结果。具体流程如下:
项目列表获取¶
首先需要获取所有需要分析的项目仓库列表。我们将针对内容目录 content/*/ai/
下的 Markdown 资源,筛选出 front matter 中含有 github
字段的项目。这可以通过以下方式实现:
- 离线预处理:在本地运行脚本遍历
content/zh/ai
和content/en/ai
目录,解析每个index.md
提取其中的github
URL 或仓库名称,并将列表上传至数据库。由于目前已有约 379 个资源,我们可以初始批量导入一次。之后新增的资源可以手动触发脚本同步,或采用定期扫描(频率低一些,如每周)以发现新项目。 - 在线解析:亦可在 Worker 中通过 GitHub API 获取仓库内容列表。例如使用 GitHub 的内容 API 列出
content/zh/ai
目录下文件并读取 front matter。这种方法实现复杂且消耗 API 配额,不是必要。鉴于资源清单变化不频繁,我们倾向于离线维护或通过网站构建流程导出列表。
注意:无论采用何种方式,我们都会去重仓库列表,因为一个项目在中英文两个版本中重复出现。可通过标准化仓库“owner/name”字符串作为唯一键来避免重复抓取。
调用 GitHub API 获取指标¶
利用 GitHub 提供的 API 批量获取每个项目的关键数据:
- 首选 GitHub REST API 的仓库接口,如
GET /repos/{owner}/{repo}
,直接返回 JSON 包含 stargazers_count(Star 数), forks_count(Fork 数), open_issues_count(未关闭 Issue+PR 数), watchers_count, created_at, updated_at, pushed_at(最后一次 push 时间), archived(是否已归档)等字段。这些字段涵盖了大部分我们需要的原始指标数据。 - 提交次数:REST 仓库对象不直接给出总提交数或近期提交数。我们可采用两种方法补充:
- 使用 GitHub 统计接口,如
GET /repos/{owner}/{repo}/stats/commit_activity
,获取过去一年每周提交次数,从中计算最近几个月的提交总数。但要注意该接口可能需要缓存,调用频率有限。 - 简单方法:调用
GET /repos/{owner}/{repo}/commits?per_page=1
并从响应的 Link header 中提取last
页页码,近似得到总提交数(这是常用技巧)。总提交数可以用于衡量项目迄今的开发投入量。如果一个项目 Star 很多但总提交寥寥,那么可能暗示项目功能简单或后续维护不足。 - 贡献者数:GitHub 也无直接字段给出贡献者总数。可通过
GET /repos/{owner}/{repo}/contributors?per_page=1&anon=1
,同样利用 Link header 得到贡献者人数。我们会获取此数据来评估维护者规模。 - Release 数:可选地,通过
GET /repos/{owner}/{repo}/releases?per_page=1
类似方法获取发布版本数量,以了解项目发布活跃度。 - 使用 GraphQL 优化(可选):为减少 API 调用次数,我们可考虑 GitHub GraphQL API,一次查询多个仓库的指标。但由于仓库较多(数百个),GraphQL 查询可能过长或复杂,需要分页。初始实现可以 REST 逐个请求,后续再优化批量获取。如果使用 GraphQL,可以在一个查询中请求多个 repo 的所需字段(Star 数、Fork 数、pushedAt 等)以及一定范围的 commit 历史计数。
为避免触发 GitHub 的速率限制,我们将:
- 使用 GitHub 个人访问令牌(PAT)进行认证,提高速率限制上限(未授权每小时 60 次,授权后每小时 5000 次)。
- 控制并发或添加少许延时,避免瞬间请求过多。可以每次更新时对数百项目顺序抓取,几分钟内完成,这在允许范围内。
- 对于长期不变的数据如
created_at
(开源日期)我们可以只获取一次保存,后续不变。但考虑实现简单,REST 接口都会返回,我们统一保存更新。 - 若某些 API 请求失败或超时,我们做好重试机制,最多重试 3 次。如仍失败则跳过该项目并记录警告,不影响其他项目更新。
数据处理与健康度计算¶
拿到每个项目的原始数据后,我们在 Worker 中进行计算和分类:
- 活跃状态判断:根据
pushed_at
与当前时间差计算未更新时长(月)。标记是否超过半年或一年。 - 新项目判断:根据
created_at
(或我们存储的oss_date
)与当前时间差,判断是否在 3 个月以内。 - 计算各项指标得分:按前述方法计算人气、活跃度、社区参与三个维度的评分(0~100)。例如:Star 数通过对数映射到 0-100,最后提交时间通过阈值映射分值,贡献者数/Issue 等映射社区分。
- 综合评分:按选定权重汇总三个子得分得到综合评分(如百分制)。我们可在计算后四舍五入保留整数,以便前端展示。
- 标签字段:根据阈值条件设置布尔标志或枚举值:
is_new
、is_popular
、is_inactive
等,用于前端快速判断添加相应标签。 - 灰度标识:如果项目 Inactive 或 Archived,设置例如
inactive=true
来提示前端对其缩略图应用灰度 CSS。
示例:某项目 Star 数 8000,最后提交于 2 个月前,贡献者 10 人,open issues 5 个。计算得知人气分 90(Star 接近热门阈值)、活跃度分 80(2 个月无更新稍降低)、社区分 95(贡献者多,issue 也不多)。综合得分=0.4×90+0.4×80+0.2×95=约 86 分。标记为
is_popular=true
(Star>5000),不是新项目(开源>3 月),不是 inactive(2 个月有更新)。
存储至数据库¶
将获取到的指标和计算结果写入 Cloudflare D1 数据库中。我们将在 D1 上建立一个表来保存项目及其指标,设计如下:
表:ai_projects (或类似名称) 字段示例:
repo_id
(文本主键):仓库标识符,采用owner/name
形式确保唯一性。name
:项目名称(可选,GitHub 返回的仓库名)。stars
:Star 数(整数)。forks
:Fork 数(整数)。open_issues
:未关闭 Issues 总数(整数)。watchers
:Watch 数(整数,可选)。contributors
:贡献者数量(整数)。created_at
:仓库创建时间(ISO 字符串)。pushed_at
:最后提交时间(ISO 字符串)。archived
:是否已归档(布尔)。commit_count
:总提交次数(整数)。score_popularity
:人气评分(0-100 整数)。score_activity
:活跃度评分(0-100 整数)。score_community
:社区评分(0-100 整数)。score_overall
:综合评分(0-100 整数)。is_new
:新项目标志(布尔)。is_popular
:人气项目标志(布尔)。is_inactive
:不活跃标志(布尔)。last_updated
:最后一次更新本记录的时间戳。
存储时采用 UPSERT 逻辑:如果该 repo 记录已存在则更新各字段,否则插入新记录。这保证后续每周运行时数据被刷新。通过保存计算后的结果,前端展示时无需每次计算,直接读取即可。
数据库的选择是Cloudflare D1 (SQLite),好处是可以在 Cloudflare Pages Functions 中方便地查询,且数据持久化由后端维护,不用修改原有 Markdown 内容。D1 数据量很小(几百行),查询性能极高。后续如果需要,也可以在数据库中保留历史快照用于趋势分析,但当前需求每次覆盖最新数据即可。
自动定期更新(实际实现说明)¶
- 调度频率:实际
tools/ai-oss-rank-worker
的调度采用每天运行(daily)策略,默认每天执行一次数据抓取与更新。可在运行环境(例如 wrangler.toml 的 cron 配置或 CI 的 schedule)中调整为更高/更低频率,但默认实现是每日触发以保证指标较及时。 - 分批策略:与设计一致,更新过程采用“分批处理”以降低并发与速率限制风险。每次执行会处理 manifest 中的若干仓库(默认每批最多 10 个),处理完一批后记录游标(cursor),下次从该游标继续,直到完成所有仓库或达到批次上限。
数据更新语义(关键点)¶
- UPSERT 而非盲目追加:每个仓库在数据库中的记录以唯一键(如 repo_slug + locale)标识,工具对单个 repo 执行 UPSERT(若存在则更新字段,否则插入新行)。因此正常日常更新不会导致同一仓库产生重复行,表内行数保持与仓库数量近似稳定。
- sync_state 游标:工具通过
sync_state
(或类似)的持久化记录保存当前处理进度(cursor)。定时任务会读该游标并继续处理下一批,回填脚本可选择 reset 游标以从头重跑。 - 历史记录策略:默认实现不保存每日快照;ai_projects 表仅保持最新计算结果。如需保留历史,请启用/创建
ai_projects_history
(或在回填脚本中额外写入历史表)来保存时间序列快照。默认不会无限增长行数,历史表为可选扩展。 - 重试与错误处理:若某次对单 Repo 的抓取失败,工具会记录错误并在下一次批处理中重试。严重失败可通过手动回填或重置游标重试全部或部分仓库。
手动更新与回填¶
- 手动触发(API):当前工具实现提供用于手动触发的接口(例如
POST /recalculate
或GET /recalculate?repo=owner/name
),可以只针对单个仓库或指定一组仓库触发更新(具体接口以 tools/ai-oss-rank-worker 源码为准)。在受限环境下,也可以通过 CLI/脚本直接运行回填命令。 - 回填脚本:仓库内提供的回填脚本(例如
npm run backfill
或 tools 内的 backfill 工具)会循环调用更新接口或直接执行抓取逻辑,直到完成所有未处理仓库。回填过程同样使用 UPSERT,因此在回填完成后表中行数只会包含每个仓库的一条最新记录(除非启用了历史表)。 - 在 CI/本地运行:可在本地或 CI 中运行回填以完成首次数据导入,例如:
- 本地: npm install && npm run typecheck && node ./tools/ai-oss-rank-worker/dist/backfill.js --limit=10
- CI:在工作流中调用回填脚本并将结果提交或上传到存储
- 重置游标:需要从头重新计算时,可通过提供 resetFlag(例如
resetState=true
)的接口或删除/重置sync_state
表记录来强制从头开始回填。
数据行增长说明(回答疑问)¶
- 日常运行是否会“每天插入新行”?不会。默认行为是按 repo 唯一键更新已有记录(UPSERT),因此不会因为每天运行而无限增加相同 repo 的重复行。
- 数据库里 row 总数是否会增长?在默认配置下,ai_projects 表的行数与被跟踪的仓库数量基本一致,不会随每天更新而爆炸性增长。只有在启用历史快照表(ai_projects_history)或额外日志表时,才会产生历史性增长。
- 如何查看当前进度与最后更新时间?工具会在 ai_projects 表中写入
last_updated
(或类似字段),并在 sync_state 中记录游标与上次运行时间,便于运维查看与调试。
项目标签与状态标识¶
根据上述指标和阈值,我们将为项目动态生成若干状态标签,在 UI 上突出显示:
- 「新项目」标签:若项目开源时间在最近三个月内(根据 front matter 的
oss_date
或仓库创建日期),则认为项目很新,自动添加“New”标识。这有助于用户识别最新出现的项目。该标签在 AI 卡片上以醒目的样式标出,可能使用不同颜色的徽章表示。 - 「人气项目」标签:若项目 Star 数达到一定高度(初步定为 ≥5000 星),则标记为“人气项目”。这表示该项目在社区中非常受关注。UI 上可用“Hot”或🔥图标标注。这个阈值可根据整体项目 Star 水平调整(例如资源列表整体 Star 偏高则可提高门槛)。
- 「不活跃」标签(项目死亡):对于超过一年无任何更新的项目(没有代码提交,或者仓库已标记为 Archived),认为已基本停止维护。此类项目我们将在 AI 卡片上灰度处理其缩略图,并加上如“Archived”(已归档)或“Inactive”(不活跃)的标签提示用户。灰度效果通过在图片或卡片容器上添加 CSS 类实现(利用
filter: grayscale(100%)
等样式),直观暗示项目失去活力。若仓库在 GitHub 上被标记为 Archived,我们也视为不活跃并做相同处理。 - 其他可能的标签:
- 持续活跃: 可选,对最近一个月内有频繁更新的项目标记“Active”或闪电图标,以凸显高度活跃项目(此信息已反映在评分上,是否额外标签待定)。
- 单一维护者: 若贡献者数极少(如仅 1 人),可标注“单人维护”之类提示,但此可能不作为 UI 标签,仅在详情页注明。
上述标签主要在列表页的卡片上展示,和已有的 tags (例如“OSS”, “工具类别”等) 区分开。实现上,这些标签不直接写入 Markdown front matter,而是在渲染时根据数据库中的最新数据动态添加,确保及时性且无需手工更新 Markdown。
数据收集与分析实现方案¶
我们将开发一个 Cloudflare Workers 后端服务 来自动抓取 GitHub 数据、计算指标并存储结果。具体流程如下:
项目列表获取¶
首先需要获取所有需要分析的项目仓库列表。我们将针对内容目录 content/*/ai/
下的 Markdown 资源,筛选出 front matter 中含有 github
字段的项目。这可以通过以下方式实现:
- 离线预处理:在本地运行脚本遍历
content/zh/ai
和content/en/ai
目录,解析每个index.md
提取其中的github
URL 或仓库名称,并将列表上传至数据库。由于目前已有约 379 个资源,我们可以初始批量导入一次。之后新增的资源可以手动触发脚本同步,或采用定期扫描(频率低一些,如每周)以发现新项目。 - 在线解析:亦可在 Worker 中通过 GitHub API 获取仓库内容列表。例如使用 GitHub 的内容 API 列出
content/zh/ai
目录下文件并读取 front matter。这种方法实现复杂且消耗 API 配额,不是必要。鉴于资源清单变化不频繁,我们倾向于离线维护或通过网站构建流程导出列表。
注意:无论采用何种方式,我们都会去重仓库列表,因为一个项目在中英文两个版本中重复出现。可通过标准化仓库“owner/name”字符串作为唯一键来避免重复抓取。