1. 项目概述:一个能自我进化的AI开源项目导航站
如果你和我一样,每天在GitHub上寻找高质量的AI开源项目,那你肯定经历过这种痛苦:信息过载。每天都有成百上千个新项目冒出来,哪些是真正值得关注的?哪些已经过时了?哪些是“玩具”,哪些是能投入生产的“利器”?手动筛选和整理这些信息,几乎是个不可能完成的任务。
这就是我决定动手构建Hello-AI的初衷。它不是一个简单的静态链接收藏夹,而是一个由AI驱动、能够自我发现、自我评估、自我更新的动态知识库。你可以把它理解为一个24小时不间断工作的“AI猎头”,它的任务就是在GitHub的汪洋大海里,自动打捞那些真正有价值的AI项目,并为你分门别类地整理好。
这个项目的核心逻辑很简单:用AI来发现和筛选AI。听起来有点“套娃”,但实际效果非常惊人。目前,这个系统已经自动收集并评估了超过1.7万个项目,其中活跃(近6个月有更新)的高质量项目超过8400个,涵盖了从基础大模型、智能体框架、RAG、基础设施到多模态应用、开发者工具等十几个核心类别。所有这一切,都无需我手动干预,系统会像永动机一样,持续地探索、学习和进化。
2. 核心设计思路:从静态目录到动态智能体
传统的项目导航站,无论是人工维护的Awesome List,还是基于简单爬虫的聚合站,都存在几个致命问题:维护成本高、信息滞后、分类主观、难以规模化。一旦维护者停止更新,整个列表很快就会过时。
Hello-AI的设计目标,就是彻底解决这些问题。它的架构不是“建一个数据库然后往里填数据”,而是构建一个能够自主运行的“数据流水线”。这个流水线由四个核心层构成,形成了一个完整的闭环。
2.1 动态自进化发现层:让系统自己“找食吃”
这是整个系统的“触角”。它的起点是一个种子文件data/topics.json,里面预置了一些初始的探索主题,比如 “machine-learning”、“deep-learning”、“llm”。但关键点在于,这个主题列表不是固定的。
系统会优先探索那些“最近最少被探索”的主题,通过GitHub API搜索该主题下Star数超过500的新仓库。每当发现一个新项目,系统会解析这个项目的标签(Topics),如果遇到topics.json里没有的新标签,就会自动将其注册为“二级探索目标”。这意味着,系统的知识边界会随着探索不断扩张。今天它可能只知道“llm”,明天它可能就学会了“rag-pipeline”、“vector-database”这些更细分的领域。
所有新发现的项目,都会被放入一个待评估队列data/pending-projects.json。这里的设计考量是解耦发现与评估。发现过程可能很快,但评估(调用AI)相对较慢且可能有频率限制。将两者分开,可以让发现线程持续工作,而评估线程按自己的节奏消费队列。
实操心得:GitHub API的限速策略这是第一个大坑。GitHub对未认证的API调用有严格的速率限制(每小时60次)。即使使用个人访问令牌(GITHUB_TOKEN),对于搜索API也有每分钟30次的限制。我的策略是:
- 必须配置GITHUB_TOKEN:这是项目能持续运行的前提,否则几分钟就触达上限了。
- 实现指数退避重试:当遇到速率限制错误(HTTP 429)时,脚本会自动休眠一段时间(如2分钟),然后重试。休眠时间会随着连续失败次数递增,避免雪崩。
- 批量操作,减少请求:在更新项目状态(如Star数、最后更新时间)时,会使用GitHub的GraphQL API进行批量查询,一次请求获取多个仓库的信息,极大提升了效率。
2.2 AI批量评估引擎:核心的“大脑”
这是项目的灵魂所在。待评估队列里的项目,会被一批批地送入大型语言模型(LLM)进行“面试”。我并没有为每个项目单独调用一次AI,那样成本太高、速度太慢。而是采用了批量提示(Batch Prompting)的设计。
脚本会从队列中取出一定数量(通过EVALUATE_BATCH_SIZE配置,默认可能是10个)的项目,将它们的基本信息(仓库名、描述、README片段)组合成一个大的提示词,一次性发送给LLM。LLM的任务是:
- 判断价值:这个项目是否属于高质量的AI开源项目?是否值得收录?
- 智能分类:将它归入哪个主类别和子类别?(类别体系是动态从已收录项目中读取的,而非硬编码)。
- 生成摘要:用简洁的中文概括项目的核心功能和应用场景。
- 提取标签:提炼出3-5个关键的技术标签。
这种批量处理的方式,极大地减少了API调用次数,并充分利用了LLM的上下文窗口,性价比极高。AI评估的结果会写入核心数据库data/projects.json。如果AI认为某个项目质量不佳或无法归类,则会被移入“隔离区”data/rejected-projects/目录下,供后续人工审计,避免污染主库。
注意事项:LLM的“幻觉”与稳定性让AI做分类和摘要,最大的挑战是输出格式的稳定性和“幻觉”。你可能会遇到:
- 格式错乱:AI返回的不是标准的JSON,导致解析失败。
- 分类漂移:同一个类型的项目,这次被分到A类,下次被分到B类。
- 摘要空洞:生成的描述过于笼统,如“这是一个基于Python的AI工具”,毫无信息量。
我的解决方案是:
- 设计严格的提示词(Prompt):在提示词中明确要求以指定JSON格式输出,并给出清晰的分类定义和示例。
- 实现输出解析与重试:代码会尝试解析AI的返回。如果解析失败,会记录日志并将该项目退回队列,等待下次重试。有时需要尝试2-3次才能成功。
- 设置分类兜底逻辑:如果AI给出的分类不在现有体系中,系统会有一个映射逻辑,尝试将其归入最相近的已知类别,或者暂时放入“未分类”池。
2.3 自动化前端渲染层:数据到视图的无缝衔接
数据有了,如何呈现?我选择了VitePress来构建静态站点。但传统VitePress的导航和侧边栏是静态配置的,每次数据更新都需要手动改配置,这违背了自动化的初衷。
因此,我重写了VitePress的导航和侧边栏生成逻辑。现在,每当projects.json中的数据更新(比如新增了一个“量子机器学习”类别),VitePress在编译时会自动分析这个JSON文件,动态生成对应的导航菜单和侧边栏目录树。数据和视图完全解耦,真正做到了“数据变,视图即变”。
另一个关键脚本是generate-docs.js。它的工作是:
- 按子类别分组:遍历
projects.json,将同一个子类别下的项目聚合到一个Markdown页面中。 - 自动清理陈旧项目:根据
RECENCY_THRESHOLD_MONTHS(默认24个月)的设置,自动过滤掉长期未更新的项目,确保导航站里展示的都是活跃、有维护的项目。 - 生成项目卡片:为每个项目生成格式统一的Markdown块,包含星星数、最后更新、描述和直达链接。
2.4 自动化流水线:让闭环转起来
单个脚本执行一次很容易,但如何让这个“发现->评估->渲染”的闭环7x24小时持续运转?我编写了几个守护进程脚本:
scripts/loop-eval.js: 核心循环脚本。它在一个死循环中,依次执行“发现新项目”和“评估队列项目”,每次循环后休眠一段时间(通过LOOP_INTERVAL_SECONDS配置)。这是实现无人值守运行的关键。scripts/update-status.js: 增量状态更新脚本。项目收录后,其Star数和“最后更新时间”并不是一成不变的。这个脚本会定期扫描已收录的项目,批量更新这些动态信息,确保前端展示的数据是新鲜的。
通过组合这些脚本,整个系统就形成了一个自治的智能体,源源不断地为知识库注入新的、高质量的内容。
3. 本地部署与实操指南
看到这里,你可能已经想自己跑起来试试了。没问题,整个项目设计就是为了易于部署和二次开发。跟着下面的步骤,你可以在自己的机器上复现一个完全属于你的、自我成长的AI项目导航站。
3.1 环境准备与初始化
首先,你需要一个Node.js环境(v18或以上版本推荐)。
# 克隆仓库 git clone https://github.com/xxxily/hello-ai.git cd hello-ai # 安装依赖 npm install这一步通常很顺利。如果遇到网络问题,可以考虑配置npm镜像源。
3.2 关键环境变量配置
项目的所有核心行为都通过环境变量控制。复制模板文件并进行配置:
cp .env.example .env接下来,用文本编辑器打开.env文件,以下几个配置项至关重要:
GITHUB_TOKEN(强烈推荐): 这是项目的“氧气”。没有它,你几乎无法进行任何有效的发现操作,因为匿名API的速率限制太严格了。- 去GitHub -> Settings -> Developer settings -> Personal access tokens -> Tokens (classic) 生成一个。
- 权限勾选
public_repo(只读访问公共仓库信息) 就足够了。 - 将生成的令牌填入
.env文件:GITHUB_TOKEN=ghp_xxxxxx
LLM相关配置: 这是项目的“大脑”。你需要一个LLM API来执行评估任务。项目支持任何兼容OpenAI API格式的模型。
LLM_API_KEY: 你的LLM服务商API密钥。LLM_PROVIDER: 指定服务商,如openai,minimax,deepseek,ollama。如果不指定,系统会根据LLM_BASE_URL或其它API密钥环境变量自动推断。LLM_BASE_URL: API的基础地址。对于OpenAI是https://api.openai.com/v1;对于本地运行的Ollama,可能是http://127.0.0.1:11434/v1。LLM_MODEL: 指定使用的模型,如gpt-4o-mini,MiniMax-M2.5,llama3等。
低成本/零成本方案: 如果你使用本地模型(如通过Ollama部署),可以简单配置:
LLM_PROVIDER=ollama LLM_API_KEY=local-fallback # 一个占位符即可 LLM_BASE_URL=http://127.0.0.1:11434/v1 LLM_MODEL=llama3.2:3b # 或其他你本地有的模型这样,所有的评估请求都会发往你本地的Ollama服务,无需支付任何API费用。
核心行为参数:
DISCOVER_BATCH_SIZE: 每次从GitHub搜索时获取的项目数量(默认100)。调大可以提高单次发现效率,但可能增加单次请求的数据量。EVALUATE_BATCH_SIZE: 每次组合成批发送给LLM评估的项目数量(默认10)。这是平衡评估速度和AI上下文长度的关键。RECENCY_THRESHOLD_MONTHS: 项目陈旧性阈值(默认24)。超过此月数未更新的项目,在生成文档时会被自动过滤掉。你可以根据领域特性调整,对于变化极快的AI框架领域,设置为12(一年)可能更合适。
3.3 运行自动化流水线
配置好后,就可以启动系统的不同部分了。package.json里预置了多个脚本,对应不同的工作模式:
单次手动执行(测试用):
npm run ai:discover-eval这个命令会依次执行“发现新项目”和“评估队列项目”一次,然后退出。适合初次运行,检查配置是否正确。
持续后台守护进程(生产模式):
npm run ai:loop-eval这是主力命令。它会启动一个循环,持续执行发现和评估。每次循环后,会休眠
LOOP_INTERVAL_SECONDS秒(默认60秒),以规避API速率限制。这个进程可以放在后台长期运行(如使用pm2或screen)。交互式TUI守护进程(推荐):
npm run ai:loop-eval-tui这是我个人最常用的模式。它提供了一个简单的终端用户界面,让你可以实时看到发现了多少新项目、评估了多少、队列还剩多少,并且可以在运行时选择不同的参数(如按主题质量或时间排序)。
增量状态更新:
npm run ai:update-status这个脚本会安静地在后台运行,专门负责更新已收录项目的动态信息(Star数、最后提交时间)。你可以把它设置为一个独立的定时任务(例如每小时运行一次),与主发现循环解耦。
3.3.1 高级命令行参数
在执行npm run ai:discover-eval或其变体时,你可以附加一些参数来微调行为:
--sort-topic-by=quality|time:决定按什么顺序探索主题。quality:按主题的“质量分”降序探索。质量分是根据该主题下已收录的高星项目数量计算的。这会让系统优先深耕那些已经证明能产出好项目的领域。time:按主题的“最后探索时间”升序探索。这能保证所有主题都有被公平探索的机会,避免冷门主题被永远遗忘。
--consume-only:仅评估本地队列中的项目,不进行新的GitHub搜索。适合在API配额紧张或只想快速处理积压任务时使用。--resume:从上次中断的地方继续探索。脚本会记录上次探索到的主题和页码,使用此参数可以无缝续传。
3.4 生成网站与本地预览
当AI评估了一些项目,数据存入projects.json后,你就可以生成网站并预览了。
# 1. 根据 projects.json 生成分类好的Markdown文档 npm run ai:generate-docs # 2. 启动本地开发服务器(支持热重载) npm run docs:dev执行完第一步,你会发现在docs目录下生成了大量的.md文件,每个文件对应一个子类别,里面列出了所有属于该类的项目。执行第二步后,打开浏览器访问http://localhost:5173(VitePress默认端口),就能看到一个完整的、可交互的导航网站了。
如果你想部署到GitHub Pages或Vercel等静态托管服务,可以运行:
npm run docs:build这会在docs/.vitepress/dist目录下生成优化后的静态文件,直接上传即可。
4. 核心脚本原理解析与定制
要真正理解这个项目,或者想根据自己的需求进行定制,你需要深入几个核心脚本。下面我拆解其中最关键的几个部分。
4.1discover-and-evaluate.js:发现与评估的总控
这是项目的主入口脚本。它的逻辑流程图虽然复杂,但核心是一个状态机:
- 读取配置:加载环境变量,初始化GitHub和LLM客户端。
- 发现阶段:
- 从
topics.json中选取下一个要探索的主题(根据--sort-topic-by参数)。 - 调用GitHub搜索API,获取该主题下Star数超过阈值的新项目。
- 过滤掉已存在于
projects.json或pending-projects.json中的重复项。 - 将新项目追加到
pending-projects.json队列。 - 从新项目中提取未知标签,更新
topics.json。
- 从
- 评估阶段:
- 从
pending-projects.json队列中取出N个项目(EVALUATE_BATCH_SIZE)。 - 从
projects.json中读取当前的分类体系,动态构建给LLM的提示词。 - 调用LLM API,获取批量评估结果。
- 解析LLM返回的JSON,将合格的项目按分类写入
projects.json,将被拒的项目移入rejected-projects/目录。
- 从
- 状态保存与日志:记录本次探索的主题和页码,以便
--resume操作;输出详细的运行日志。
避坑技巧:处理GitHub API的搜索限制GitHub搜索API对结果有分页限制(最多1000条记录),并且对复杂查询有超时可能。我的策略是:
- 按时间范围分段搜索:在搜索查询中加入
created:>YYYY-MM-DD或pushed:>YYYY-MM-DD来限制范围,进行多次搜索来覆盖更广的时间段。- 多维度排序尝试:如果按
stars排序结果不理想,可以尝试按updated排序,以发现近期活跃的新星项目。- 优雅降级:如果搜索API完全不可用,脚本有一个后备方案:可以切换到仅消费本地队列的模式(
--consume-only),保证评估流程不中断。
4.2generate-docs.js:从数据到页面的转换器
这个脚本负责将结构化的projects.json数据,转换成VitePress可渲染的Markdown文件树。它的工作流程如下:
- 数据加载与清洗:读取
projects.json,并根据RECENCY_THRESHOLD_MONTHS过滤掉长期未更新的项目。 - 按类别分组:遍历所有项目,按照
category->subcategory的层级进行分组。 - 生成侧边栏配置:根据分组结果,动态生成VitePress所需的
sidebar配置对象,并写入.vitepress/config.mjs。这是实现动态导航的关键。 - 生成Markdown内容:为每个
subcategory创建一个.md文件。文件内容包含:- Frontmatter(标题、描述)。
- 一个项目表格,包含项目名、星星数、最后更新、简介和直达链接。
- 项目按星星数降序排列,方便用户一眼看到最受欢迎的项目。
// generate-docs.js 核心分组逻辑片段示意 const projectsByCategory = {}; allProjects.forEach(project => { const { category, subcategory } = project; if (!projectsByCategory[category]) { projectsByCategory[category] = {}; } if (!projectsByCategory[category][subcategory]) { projectsByCategory[category][subcategory] = []; } projectsByCategory[category][subcategory].push(project); }); // 然后遍历 projectsByCategory,为每个 subcategory 生成文件4.3scripts/loop-eval.js:永动机的引擎
这个脚本的实现非常简单,但却是自动化运行的基石。它就是一个while(true)循环:
// 简化的核心循环逻辑 const { execSync } = require('child_process'); const INTERVAL = process.env.LOOP_INTERVAL_SECONDS || 60; while (true) { try { console.log(`[${new Date().toISOString()}] Starting discovery and evaluation cycle...`); // 执行主发现评估脚本,可以附加参数 execSync('node discover-and-evaluate.js --sort-topic-by=quality', { stdio: 'inherit' }); console.log(`[${new Date().toISOString()}] Cycle completed. Sleeping for ${INTERVAL} seconds...`); } catch (error) { console.error(`[${new Date().toISOString()}] Cycle failed:`, error.message); // 出错时也休眠,避免疯狂重试 } // 休眠指定间隔 await new Promise(resolve => setTimeout(resolve, INTERVAL * 1000)); }你可以用pm2这样的进程管理器来守护这个脚本,确保它一直在运行。
npm install -g pm2 pm2 start npm --name "hello-ai" -- run ai:loop-eval pm2 save pm2 startup5. 常见问题与排查实录
在实际部署和运行过程中,你几乎一定会遇到下面这些问题。这里是我踩过坑后的解决方案。
5.1 GitHub API 速率限制(Rate Limiting)
问题现象:脚本运行一段时间后,控制台开始大量报错,提示API rate limit exceeded,随后发现过程停止。
根本原因:即使使用了GITHUB_TOKEN,GitHub对搜索API(/search/repositories)也有每分钟30次的限制。如果脚本搜索得太频繁,很容易触发。
解决方案:
- 调整发现节奏:增加
LOOP_INTERVAL_SECONDS的值,比如从60秒增加到300秒(5分钟),给API配额足够的恢复时间。 - 使用
--consume-only模式:当发现队列pending-projects.json中积压了大量项目时,可以暂时运行npm run ai:discover-eval -- --consume-only,专注于消化队列,暂停新的搜索,避免浪费宝贵的搜索配额。 - 实现更精细的退避机制:在脚本的GitHub客户端中,我已经集成了基础的重试逻辑。但如果问题持续,可以考虑在循环脚本
loop-eval.js中,在每次执行discover-and-evaluate.js后,根据本次消耗的搜索次数动态调整下一次循环的等待时间。
5.2 LLM API 调用失败或响应格式错误
问题现象:评估阶段卡住,日志显示LLM调用超时、返回非JSON内容,或者解析失败。
排查步骤:
- 检查网络和API密钥:首先确认
LLM_BASE_URL能访问,且LLM_API_KEY有效。对于本地Ollama,运行ollama list确认模型已拉取。 - 检查提示词和模型能力:如果使用的是小参数模型(如7B以下的),它可能无法严格遵守复杂的JSON输出格式。尝试:
- 简化提示词,减少对输出结构的复杂要求。
- 换用能力更强的模型(如
llama3.2:3b升级到llama3.2:1b并不总是更好,有时需要7B或以上的模型)。 - 在提示词中加入更具体的输出示例(Few-shot Learning)。
- 查看错误日志:脚本会将失败的响应内容记录到日志中。查看这些原始响应,能直观看出是网络超时、内容过滤,还是模型“胡说八道”。
- 启用
--consume-only并减少批次大小:将EVALUATE_BATCH_SIZE临时调小(比如从10调到3),降低单次提示的复杂度,提高成功率。
5.3 项目分类混乱或不准确
问题现象:一个明显的RAG框架被分到了“多媒体”类别,或者分类粒度不一致。
解决方案:
- 优化分类体系:分类不是一成不变的。你可以直接编辑
data/projects.json文件,调整已有的分类结构。系统下次评估时,会读取这个新的结构作为参考。比如,你发现“Agent”类别下的项目太多太杂,可以将其拆分为“Agent框架”、“Agent工具”、“Agent应用”等子类。 - 提供更明确的分类描述:在给LLM的提示词中,对每个类别和子类别的定义要清晰。例如,不要只写“RAG”,而要写成“RAG & 数据工程:包含向量数据库、检索增强生成框架、文档处理管道等与数据准备和检索相关的工具”。
- 人工干预与重新评估:对于已经错误分类的项目,你可以手动在
projects.json中修改其category和subcategory字段。或者,更彻底的方法是,将这个项目从projects.json中删除,然后运行npm run ai:re-evaluate-all命令(如果存在),或手动将其信息添加到pending-projects.json队列头部,让它被AI重新评估一次。
5.4 本地运行Ollama速度慢
问题现象:使用本地Ollama模型时,评估一个批次的项目需要好几分钟,效率极低。
优化建议:
- 升级硬件或使用更小模型:评估任务对推理速度要求高,但对复杂推理能力要求相对较低。可以尝试更小、更快的模型,如
llama3.2:3b、qwen2.5:3b或专门优化的phi3:mini。 - 调整Ollama参数:运行Ollama时,可以指定更高的并发数和优化参数。
在调用时,也可以在提示词中要求模型“快速响应”,并设置更低的OLLAMA_NUM_PARALLEL=4 ollama servetemperature(如0.1)以减少随机性,加快生成速度。 - 使用API服务:如果本地硬件实在有限,可以考虑使用性价比较高的云端API服务,如DeepSeek、MiniMax等,它们的速率限制通常比本地模型宽松,且响应更快。
5.5 网站生成后样式或导航错误
问题现象:运行npm run docs:dev后,网站能打开,但侧边栏导航是空的,或者页面布局错乱。
排查步骤:
- 确认
generate-docs已成功运行:检查docs/.vitepress/config.mjs文件是否被正确更新,里面的sidebar配置是否包含了你的项目分类。 - 检查VitePress版本:确保
package.json中VitePress的版本与项目兼容。有时升级VitePress大版本会导致主题组件不兼容。 - 清除缓存并重新安装:
rm -rf node_modules/.vite # 清除Vite缓存 npm install npm run docs:dev --force # 强制重新构建 - 检查自定义组件:如果你修改过
docs/.vitepress/theme下的自定义组件,可能存在语法错误。可以暂时注释掉自定义部分,使用默认主题测试。
这个项目本质上是一个高度可定制的框架。你完全可以基于它的核心流水线(发现->评估->呈现),去构建其他垂直领域的智能导航,比如“优秀的React组件库”、“DevOps工具大全”等等。只需要调整初始的探索主题和AI评估的提示词,一个新的智能知识库就能运转起来。