☰
Codex 实战 Skills:用 Skill 自动抓取 20 个 RSS 订阅,并用 AI 自动输出中文摘要(TaoToken 统一 Key 接入版)
2026/9/25 13:05:33 网站建设 项目流程

1. 为什么我要把 20 个 RSS 源塞进 Codex Skill

每天打开阅读器,20 多个订阅源堆着上百条未读,标题党一堆,真正值得看的没几条。我试过用传统 RSS 阅读器加规则过滤,但规则写起来比读文章还累,而且英文源还得自己翻译。后来我把这件事拆成两步:抓取交给脚本,理解交给 AI,中间用 Codex 的 Skill 机制串起来,一次配置就能跑通全流程。

Codex 的 Skill 本质上是一个可被 Agent 调用的能力单元,你可以把它理解成给 AI 装了一只“手”:这只手负责去网上把 RSS 拉回来、把正文洗干净,再交给模型生成中文摘要。本文要交付的就是这样一个名为rss_aggregator的 Skill,包含config.toml与settings.json的关键字段、20 个 RSS 源的抓取逻辑,以及摘要输出的验证动作。适合已经会用命令行、想把手动读 RSS 变成自动流水线的开发者。

整条链路里,AI 调用是最容易卡住的一环:不同模型的 Key 格式不一样,切换模型要改代码。我用 TaoToken 的统一 Key 通道来解决这个问题,一个 Key 走 OpenAI 兼容接口,Skill 里只配一个base_url和api_key,换模型不用动业务代码。下面从环境准备开始,一步步把这条流水线搭起来。

2. TaoToken 前置:统一 Key 与接口地址

在写 Skill 之前,先把 AI 通道准备好。TaoToken 提供的是 OpenAI 兼容的 API 通道,也就是说你原来用openaiSDK 写的代码,只需要改base_url和api_key两个地方就能接上。对 Skill 这种要长期跑的自动化任务来说,统一 Key 的好处是:抓取脚本、摘要脚本、后续可能加的翻译脚本,全都共用一套凭证,不用为每个模型单独维护配置。

你需要先拿到一个 API Key。登录官网后进入控制台,在 API Keys 页面创建一个新 Key,复制出来保存好,后面写进config.toml。接口地址固定为https://taotoken.net/api,注意这个地址不带任何查询参数,直接作为base_url使用。

注意:Key 只显示一次,创建后立刻复制。不要把它硬编码进提交到 Git 的脚本里,用环境变量或本地配置文件承载。

如果你还没决定用哪个模型,可以先去模型对话页面手动试几条 RSS 正文,看看摘要质量和速度是否符合预期,再决定写进配置的模型名。对于长期跑的编码类或 Agent 类任务,Coding Plan 的额度模型通常比按次调用更划算,具体可以在控制台里对比。

3. 可复制配置:config.toml 与 settings.json

Skill 的配置分两层:config.toml管运行参数和 AI 通道,settings.json管 RSS 源清单和输出行为。这样拆的好处是,换模型只动 toml,加订阅源只动 json,互不干扰。

先看config.toml:

[llm] base_url = "https://taotoken.net/api" api_key = "sk-你的TaoToken密钥" model = "gpt-4o-mini" temperature = 0.2 max_tokens = 600 timeout = 30 [fetch] concurrency = 5 request_timeout = 12 retry_times = 2 user_agent = "Mozilla/5.0 (compatible; RSSAggregatorSkill/1.0)" [output] result_file = "aggregated_results.json" max_articles_per_run = 20 summary_language = "zh-CN"

base_url指向 TaoToken 的 API 地址,api_key填你刚创建的 Key。temperature设 0.2 是为了让摘要稳定,不要每次跑出来风格差异太大。concurrency控制并发抓取数,5 是个比较稳的值,太高容易被源站限流。

再看settings.json,这里放 20 个 RSS 源:

{ "sources": [ { "name": "Hacker News", "url": "https://hnrss.org/frontpage", "category": "Tech" }, { "name": "Ars Technica", "url": "https://feeds.arstechnica.com/arstechnica/index", "category": "Tech" }, { "name": "The Verge", "url": "https://www.theverge.com/rss/index.xml", "category": "Tech" }, { "name": "TechCrunch", "url": "https://techcrunch.com/feed/", "category": "Business" }, { "name": "InfoQ 中文", "url": "https://www.infoq.cn/feed", "category": "Backend" }, { "name": "阮一峰的网络日志", "url": "https://www.ruanyifeng.com/blog/atom.xml", "category": "Frontend" }, { "name": "酷 壳", "url": "https://coolshell.cn/feed", "category": "Backend" }, { "name": "少数派", "url": "https://sspai.com/feed", "category": "Product" }, { "name": "机器之心", "url": "https://www.jiqizhixin.com/rss", "category": "AI" }, { "name": "量子位", "url": "https://www.qbitai.com/feed", "category": "AI" }, { "name": "GitHub Blog", "url": "https://github.blog/feed/", "category": "DevOps" }, { "name": "Cloudflare Blog", "url": "https://blog.cloudflare.com/rss/", "category": "Security" }, { "name": "AWS News", "url": "https://aws.amazon.com/blogs/aws/feed/", "category": "Cloud" }, { "name": "Kubernetes Blog", "url": "https://kubernetes.io/feed.xml", "category": "DevOps" }, { "name": "React Blog", "url": "https://react.dev/rss.xml", "category": "Frontend" }, { "name": "Vue Blog", "url": "https://blog.vuejs.org/feed.rss", "category": "Frontend" }, { "name": "Rust Blog", "url": "https://blog.rust-lang.org/feed.xml", "category": "Backend" }, { "name": "Python Insider", "url": "https://pythoninsider.blogspot.com/feeds/posts/default", "category": "Backend" }, { "name": "OpenAI Blog", "url": "https://openai.com/blog/rss.xml", "category": "AI" }, { "name": "Hugging Face Blog", "url": "https://huggingface.co/blog/feed.xml", "category": "AI" } ], "dedup": { "by_url": true, "by_title_similarity": 0.9 } }

这 20 个源覆盖了 AI、前后端、DevOps、安全、产品几个方向,中英文都有。dedup段控制去重策略,by_url是基础去重,by_title_similarity用标题相似度挡掉不同源转载的同一篇文章。

4. 抓取与摘要:Skill 主流程代码

配置就位后,写 Skill 的执行逻辑。核心分三步:并发拉取 RSS、用 Readability 提取正文、调用 TaoToken 生成中文摘要。下面这段代码可以直接放进rss_aggregator_skill.py:

import json import tomllib import feedparser import requests from concurrent.futures import ThreadPoolExecutor, as_completed from readability import Document from bs4 import BeautifulSoup from openai import OpenAI with open("config.toml", "rb") as f: cfg = tomllib.load(f) with open("settings.json", "r", encoding="utf-8") as f: settings = json.load(f) client = OpenAI( base_url=cfg["llm"]["base_url"], api_key=cfg["llm"]["api_key"], ) HEADERS = {"User-Agent": cfg["fetch"]["user_agent"]} def fetch_rss(source): try: resp = requests.get(source["url"], headers=HEADERS, timeout=cfg["fetch"]["request_timeout"]) resp.raise_for_status() feed = feedparser.parse(resp.content) return source, feed.entries[:5] except Exception as e: print(f"[RSS失败] {source['name']}: {e}") return source, [] def extract_content(url): try: resp = requests.get(url, headers=HEADERS, timeout=cfg["fetch"]["request_timeout"]) resp.raise_for_status() doc = Document(resp.text) soup = BeautifulSoup(doc.summary(html_partial=True), "html.parser") text = soup.get_text(separator="\n", strip=True) return text[:4000] except Exception as e: print(f"[正文失败] {url}: {e}") return "" def summarize(title, content, source_name): prompt = f"""你是技术内容编辑。请对下面文章输出严格 JSON,不要 Markdown 代码块。 字段:category(从 AI/前端/后端/DevOps/安全/产品/其他 中选一个)、 keywords(3-5 个)、summary(150 字左右简体中文,直击技术要点)。 标题:{title} 来源:{source_name} 正文:{content}""" try: resp = client.chat.completions.create( model=cfg["llm"]["model"], messages=[{"role": "user", "content": prompt}], temperature=cfg["llm"]["temperature"], max_tokens=cfg["llm"]["max_tokens"], ) raw = resp.choices[0].message.content.strip() if raw.startswith("```"): raw = raw.strip("`").lstrip("json").strip() return json.loads(raw) except Exception as e: print(f"[摘要失败] {title}: {e}") return {"category": "其他", "keywords": [], "summary": "摘要生成失败"}

主流程把抓取和摘要串起来,控制单次运行的文章数:

def run(): results = [] with ThreadPoolExecutor(max_workers=cfg["fetch"]["concurrency"]) as pool: futures = [pool.submit(fetch_rss, s) for s in settings["sources"]] for fut in as_completed(futures): source, entries = fut.result() for entry in entries: if len(results) >= cfg["output"]["max_articles_per_run"]: break content = extract_content(entry.link) if not content: continue meta = summarize(entry.title, content, source["name"]) results.append({ "title": entry.title, "source": source["name"], "url": entry.link, "category": meta.get("category", "其他"), "keywords": meta.get("keywords", []), "summary": meta.get("summary", ""), }) with open(cfg["output"]["result_file"], "w", encoding="utf-8") as f: json.dump(results, f, ensure_ascii=False, indent=2) print(f"完成,共 {len(results)} 篇") return results if __name__ == "__main__": run()

这段代码里,base_url和api_key全部从config.toml读取,业务逻辑里没有任何硬编码的模型信息。想换模型,改 toml 里一行model就行,抓取和摘要代码不用动。

5. 验证请求:跑通并检查输出

配置和代码都写好后,先做一次小规模验证,别一上来就跑满 20 个源。把settings.json里的sources临时只留 2 个,max_articles_per_run改成 3,然后执行:

python rss_aggregator_skill.py

正常的话你会看到类似输出:

完成,共 3 篇

打开aggregated_results.json,检查每条记录是否包含title、source、url、category、keywords、summary六个字段,摘要是不是中文、有没有明显跑题。如果摘要字段是“摘要生成失败”,说明 AI 通道没通,先单独验证 Key:

curl https://taotoken.net/api/v1/chat/completions \ -H "Authorization: Bearer sk-你的密钥" \ -H "Content-Type: application/json" \ -d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"回复:ok"}]}'

返回里能看到choices字段就说明 Key 和地址都没问题。确认单源跑通后,再把 20 个源恢复,max_articles_per_run设回 20,完整跑一次。第一次全量跑建议观察日志里有没有大量[RSS失败]或[正文失败],有的话按下一节的排查表处理。

6. 本篇常见错排查

跑这条流水线时,报错基本集中在三类:网络抓取、正文提取、AI 调用。下面这张表是我实际踩过的坑和对应处理方式。

现象可能原因处理方式
[RSS失败]大量出现源站限流或 URL 失效降低concurrency,逐个 curl 验证 URL
[正文失败]但 RSS 正常目标站反爬或正文在 JS 里换用该源的 RSS 全文,或跳过该源
摘要返回“生成失败”Key 错误或base_url写错用上面的 curl 单独验证通道
摘要非中文Prompt 被截断检查max_tokens是否过小
同一篇文章重复出现去重未生效确认dedup.by_url为 true,检查 URL 是否带追踪参数
运行很慢串行抓取或源站响应慢提高concurrency,但别超过 8

其中最常见的是base_url多写了/v1或少了/api。TaoToken 的接口地址是https://taotoken.net/api,SDK 会自动补全路径,你不需要手动加/v1/chat/completions。如果摘要一直失败,先去 API Keys 页面确认 Key 状态是否正常,再检查config.toml里有没有多余空格。

另一个容易忽略的点是readability对某些中文站点提取效果一般,正文可能只剩标题。遇到这种情况,可以在extract_content里加一个兜底:如果提取出的文本长度小于 200 字,就直接用 RSS 条目自带的summary字段作为正文,虽然信息量少一点,但至少能生成摘要。

7. 下一步:把 Skill 接进你的工作流

跑通之后,这个 Skill 可以按你的习惯接进不同环节。想每天定时跑,用系统计划任务调用python rss_aggregator_skill.py即可;想把结果推到手机,读aggregated_results.json再调一个推送接口就行。如果你还想让 Agent 在编码时自动调用这个 Skill,可以把脚本注册成 Codex 可发现的工具,具体接入方式参考接入文档里的 Skill 注册章节。

需要长期跑、文章量大的话,建议在控制台里看一下 Coding Plan 的额度模型,比单次调用更适合这种每天固定消耗的场景。模型选择上,摘要任务用轻量模型就够,把省下的额度留给更复杂的推理任务。配置骨架已经给全,剩下的就是按你的订阅源清单替换settings.json,然后让它自己跑起来。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询