forums.fast.ai爬虫
递增爬行 forums.fast.ai 使用话语公共JSON API,并将所有主题和帖子持久化到磁盘。
用法
- 浏览论坛。第一次运行从Hugging Face下载预抓取的历史记录,然后获取自该快照以来的任何新帖子。后续运行是递增的(~分钟)。
- 构建搜索索引(爬行后运行一次,约几分钟)
- 构建语义向量索引(爬行后运行一次,约20分钟)
git clone https://github.com/adamhajari/fastai-forum-mcp
cd fastai-forum-mcp
uv sync
# fetch history and crawl forum for new posts
uv run python forum_crawler.py
uv run python forum_crawler.py --stats # show stats without crawling
# build BM25 search index
uv run python build_index.py
# build FAISS semantic vector index (recommended model)
uv run python build_embeddings.py --model nomic-ai/nomic-embed-text-v1 --max-seq-length 512嵌入模型
nomic-ai/nomic-embed-text-v1 给出了最佳的评估结果(MRR=0.434,200个保留查询的R@1=0.290)。它要求 einops 包(已在 pyproject.toml)以及 --max-seq-length 512 以避免Apple Silicon MPS上的内存不足错误。
如果你在使用nomi时遇到问题, multi-qa-MiniLM-L6-cos-v1 是一种可靠的回退,结果几乎一样强(MRR=0.410,R@1=0.290),没有特殊要求:
uv run python build_embeddings.py --model multi-qa-MiniLM-L6-cos-v1使用Claude Code注册MCP服务器
回购包括 .mcp.json 如果您从内部运行Claude Code,则可以正常工作 fastai-forum-mcp 目录。如果从其他目录(更常见)运行Claude Code,请将服务器添加到全局Claude配置中:
claude mcp add fastai-forum -- uv --directory /path/to/fastai-forum-mcp run python mcp_server.py替换 /path/to/fastai-forum-mcp 使用克隆此仓库的绝对路径。然后重新启动Claude Code。
第一次运行从下载预抓取的数据 拥抱脸,然后获取比该快照更新的任何帖子。后续运行仅获取自上次运行以来有新活动的主题。
更新拥抱脸数据集
重新爬行后,将更新的数据推送到 huggingface.co/datasets/adamhajari/fastai-forum 因此,其他人可以从最新的帖子中受益:
# 1. Crawl new posts
uv run python forum_crawler.py
# 2. Rebuild indexes
uv run python build_index.py
uv run python build_embeddings.py --model nomic-ai/nomic-embed-text-v1 --max-seq-length 512
# 3. Upload to Hugging Face (requires write access to adamhajari/fastai-forum)
uv run python upload_to_hub.py压缩存档约为52MB(未压缩为237MB),使上传和下载速度更快。
文件结构
fastai-forum-mcp/
├── .mcp.json # tells Claude Code how to start the MCP server
├── forum_crawler.py # the crawler
├── build_index.py # builds the BM25 search index from crawled posts
├── build_embeddings.py # builds the FAISS semantic vector index from crawled posts
├── mcp_server.py # MCP server — exposes search_forum tool to Claude
├── upload_to_hub.py # compresses posts/ and uploads to Hugging Face
├── pyproject.toml
├── uv.lock
└── data/ # created after running the crawler (gitignored)
├── metadata.json # index of all known topics
├── search_index.pkl # BM25 index (built by build_index.py)
└── posts/
├── 12345.json # all posts for topic 12345
├── 12346.json
└── ...data/metadata.json
跟踪爬虫状态和每个主题的轻量级摘要。在跑步过程中每25个主题检查一次。
{
"last_run": "2024-01-15T10:30:00+00:00",
"topics": {
"12345": {
"title": "How do I train a model?",
"created_at": "2022-01-15T10:30:00.000Z",
"last_posted_at": "2022-03-01T14:22:00.000Z",
"bumped_at": "2022-03-01T14:22:00.000Z",
"posts_count": 8,
"category_id": 10,
"tags": ["fastai", "training"],
"slug": "how-do-i-train-a-model"
}
}
}data/posts/{topic_id}.json
每个主题一个文件,其中包含主题元数据及其所有帖子。
{
"topic": {
"id": 12345,
"title": "How do I train a model?",
"created_at": "2022-01-15T10:30:00.000Z",
"last_posted_at": "2022-03-01T14:22:00.000Z",
"posts_count": 8,
"views": 342,
"like_count": 12,
"category_id": 10,
"tags": ["fastai", "training"],
"slug": "how-do-i-train-a-model"
},
"posts": {
"98001": {
"id": 98001,
"post_number": 1,
"username": "jsmith",
"created_at": "2022-01-15T10:30:00.000Z",
"updated_at": "2022-01-15T10:30:00.000Z",
"reply_to_post_number": null,
"cooked": "
How do I train a model?
",
"raw": "How do I train a model?",
"like_count": 2,
"reads": 45
}
}
}帖子以帖子ID为键的扁平字典存储。回复通过以下方式链接到其父级 reply_to_post_number。要查找父帖子,请执行以下操作:
搜索索引
看 搜索索引设计.md 用于设计说明、当前方法和使用SQLite FTS5的有记录的替代方法。
帖子以帖子ID为键的扁平字典存储。回复通过以下方式链接到其父级 reply_to_post_number。要查找父帖子,请执行以下操作:
import json
with open("data/posts/12345.json") as f:
topic = json.load(f)
by_number = {p["post_number"]: p for p in topic["posts"].values()}
for post in topic["posts"].values():
if post["reply_to_post_number"]:
parent = by_number.get(post["reply_to_post_number"])