awesome-python ADR 0001 详解:把列表从"目录"重构为"显而易见选择的短名单"
本文基于 awesome-python 仓库中的架构决策记录 docs/adr/0001-shortlist-not-catalog.md(状态:accepted),完整还原一次编辑策略重构的决策过程:为什么一个条目超过 576 条、分类无限膨胀的 awesome-* 列表要放弃"目录(catalog)"定位,转而成为回答"I want to do X in Python, which tool should I use?"这一唯一问题的"短名单(shortlist)"。读完后,你将掌握这套 curation 体系的完整规则(Use Case、Obvious Choice、Challenger、Displacement、Cap、Split)、其证据链来源(PyPI 下载量而非 GitHub stars)、以及规则如何在 CONTRIBUTING.md、CONTEXT.md、docs/audit-logs.md 和网站构建管线中落地执行。
1. 背景:旧的收录模型如何失效
ADR 记录了触发重构的三个事实(均以 2026 年中为时间基准):
- 列表当时持有 576 个条目,分布在 75 个 Section。当前 README.md 中的
###章节数为 77(含后续增减),与这一量级吻合; - 条目流入量同比增长 24 倍:过去 12 个月新增 96 条,而前一年只有 4 条,且增量集中在 AI and Agents 这类 Section(该 Section 曾达 41 个条目);
- 旧的收录模型是"三条泳道"——Industry Standard、Rising Star、Hidden Gem,且只有第一条泳道有上限。这个模型会接纳任何"孤立来看足够好"的项目,结果是分类无限增长,列表不再回答读者的实际问题:"X 这件事我用什么?"
重构的哲学依据被直接写进 ADR,即 Zen of Python 的一条:
There should be one — and preferably only one — obvious way to do it.
也就是说,列表的定位从"好的 Python 项目大全"收窄为"每个使用场景下显而易见的少数选择"。
2. 决策内容:新规则的完整定义
这一节逐条继承 ADR《The decision》部分,并结合仓库文件补充其执行细节。
2.1 收录边界测试:从"用 Python 写的"变为"服务 Python 开发者"
ADR 用一个新测试替换了旧要求"primarily written in Python (>50%)":
- 新测试(Serves Python Developers):只要 Python 开发者在自己的 Python 工作中使用它,实现语言和打包方式就无关紧要——
uv和ty是 Rust 写的,agent skill packs 是 markdown,都属于;反过来,一个没人会在 Python 工作中使用的纯 Python 项目不收录; - CONTEXT.md 中显式标注旧术语为应避免项:
_Avoid_: Python-first, written-in-Python (old requirement — removed),防止旧心智模型回流。
CONTRIBUTING.md 的"Quality Requirements"第一条与该测试完全对齐,并给出四个并行的硬性质量门槛:Active(12 个月内有提交)、Stable(非 alpha/beta/experimental)、Documented(README 有示例和用例说明)、Established(仓库至少 1 个月历史)。
2.2 Use Case 结构:投稿者不能创造自己需要的分类
ADR 规定 Use Case 由列表既有结构定义,"an entry PR can never create the subcategory it needs"。结合 CONTEXT.md 的结构词汇表,列表的层级是:
| 层级 | 定义 | 与规则的关系 |
|---|---|---|
| Thematic Group | 粗体分组行(如 "AI & ML"、"Web Development"),在 TOC 和正文中聚类 Section | 纯组织,不绑定规则 |
| Section | README.md 中的 ### 标题(如 "Testing"、"AI and Agents"),位于某个 Thematic Group 之下 |
无子分类时,整个 Section 就是单个 Use Case |
| Subcategory | Section 内带缩进条目的命名项目符号(如 "Testing" 下的 "Mock") | 每个 Subcategory 就是一个 Use Case |
| Entry | 单个条目,格式 - name - Description.,是"被收录/被替换/被修剪"的最小单位 |
有 PyPI 包名时以 PyPI 包名为显示名,否则用仓库名 |
一个值得注意的辨析:CONTEXT.md 特意警告不要用 "Category" 一词——TOC 里虽然叫 categories,但规则绑定的是 Use Case 而非 Section。投稿 PR 不能新建 Section 或 Subcategory 来"给项目安家",结构变更(新 Section、新 Subcategory、把过大的 Use Case 拆细)是维护者专属操作,CONTRIBUTING.md 的 "Automatic Rejection" 第一条就把"PR 创建新的 section 或 subcategory 并往里填内容"列为直接关闭项。
2.3 Cap:每 Use Case 至多 3 个 Obvious Choice + 2 个 Challenger,硬上限 5
ADR 给出的数字是 最多 3 个 Obvious Choices,外加最多 2 个标记为 Challenger 的条目,硬上限 5,并注明"numbers provisional, to be reviewed after the prune"——数字是临时性的,要在清剪完成后复审。
CONTRIBUTING.md 对此的表述进一步澄清了数字与质的关系:
Hard maximum: 5 entries per use case. This is a qualitative bar first and a numeric backstop second — most use cases should carry fewer.
即先定性门槛,后数量兜底,是天花板而非地板:一个新建的 Use Case 完全可以只放 1 个条目。CONTEXT.md 中 Cap 词条的措辞与之一致("a ceiling, not a floor")。
两层条目的区别与准入标准:
- Obvious Choice:资深 Python 开发者被问到"这件事用什么"时会不假思索说出的名字。注意 CONTEXT.md 标注旧名 "Industry Standard" 为应避免项;
- Challenger:还不是显而易见选择、但是某个在位者的可信继任者。其准入要求采用轨迹证据(adoption-trajectory evidence),而不是单纯的流行度。旧泳道名 "Rising Star" 和 "Hidden Gem" 均被标注废弃——ADR《Considered options》解释了原因:Rising Star 的势头在新模型下应作为 Challenger 名额或 Displacement 论据的证据,Hidden Gem 则与"obvious"在定义上互斥。
条目文本中没有任何 Challenger 标记——位置即标记:Use Case 内 Obvious Choices 排前、Challengers 排后,各自按 PyPI 月下载量降序;标准库模块一律排在使用场景最前;无下载量信号的项目(agent skill packs、非 PyPI 分发的项目)在同层内按字母序殿后。这意味着一个 Use Case 的末尾条目可能就是 Challenger 而非普通在位者。
2.4 证据信号:PyPI 下载量优先于 GitHub stars,且判断保留裁量
ADR 规定收录由维护者编辑判断决定,判断"primarily"参考的信号是 PyPI 下载量而非 GitHub stars,且判断最终生效(stated as final)。判断权同时覆盖该信号的已知失效模式:
- CI / 依赖拉高导致的虚高下载量;
- 模型类项目以权重文件形式被下载消费,而非 pip 安装(CONTEXT.md 将这一条扩展到一切非 pip 消费形式,如 renpy 的 SDK 下载、thumbor 这类部署型服务);
- 大而特定的受众被误读为"niche"。
这套"下载量优先"的证据链在仓库中有对应工具支撑:website/ 目录提供了三条抓取 PyPI 下载量的实现——website/fetch_pypi_downloads_via_bigquery.py(Google BigQuery 官方数据集)、website/fetch_pypi_downloads_via_pepy.py(PePy API)和 website/fetch_pypi_downloads_via_clickpy.py(ClickPy),维护者可交叉验证下载量数据;另有 website/fetch_github_stars.py 抓取 stars 作为对照参考。而 AGENTS.md 进一步规定:每个保留/移除理由都必须在决策时对照实时在线数据核验(下载量、仓库活跃度与归档状态、PyPI 元数据、项目文档),层级判定(obvious choice vs challenger)还需检索证据(采用轨迹、社区口碑),"训练数据的记忆不是证据,无法核验的必须标注为判断"。
2.5 Displacement:满员后的唯一入口
ADR 规定:一旦 Use Case 达到上限,唯一的进入方式是 Displacement——PR 必须指名它替换的条目,并论证新项目把那个条目的活儿干得更好。CONTRIBUTING.md 用 "One in, one out" 概括,且 CONTRIBUTING.md 的 "Automatic Rejection" 明确:Use Case 已满且 PR 没有 Displacement 论证的,直接关闭。CONTEXT.md 中还记录了一个相关概念 Second Tier:Challenger 名额可以由"被降级的在位者"占据(如 clickhouse-driver 排在官方客户端之后、django-haystack 排在 Search 场景的后面),它同样占用两个 Challenger 名额、同样以位置为标记;采用轨迹门槛只约束新准入,不约束降级。
2.6 标准库、资源类 Section 与追溯清剪
- 标准库:只有当 stdlib 模块本身就是该 Use Case 的显而易见选择时才占位——ADR 与 CONTRIBUTING.md 共用同一个判例:"tomllib yes, unittest no";
- Resources 类 Section(Newsletters、Podcasts、Websites)暂时不在改革范围内,且从源码结构看,网站构建管线根本不解析它们(CLAUDE.md:"Resources sections are not project entries: out of audit scope, and the website never parses them");
- 追溯清剪(retroactive prune):存量条目接受同一测试,执行方式是分阶段、最差先行的清剪(per-section sweep commits,每个 Section 一个提交,提交体逐一列出移除原因);被移除条目直接删除——git 历史就是档案。这一提交纪律在 AGENTS.md 中成文:"a prune sweep is one commit per section, its body listing each removal with its reason",与常规"每次提交一个条目"的规则互为例外。
3. 被否决的备选方案:ADR 的价值所在
ADR《Considered options》完整保留了三条被否决路线及其否决理由,这是理解"为什么是现在这套规则"的关键:
| 备选方案 | 否决理由 |
|---|---|
| 保留三条泳道、每条泳道都加上限 | 一旦准入变成比较性的,"够不够好才能进来"就是答错了的问题;Rising Star 的势头应当转化为 Challenger 名额或 Displacement 的证据,Hidden Gem 与"obvious"定义上互斥 |
| 只立新规则、不做追溯清剪 | 每个被拒 PR 都会遭遇"但 X 还在列表上"的先例论证,且读者看不到任何变化 |
| 把被移除条目存档到单独文件 | 等于在"一次点击可达"处重建目录,稀释这次变革要恢复的列表身份 |
这三条否决理由分别对应规则体系中的三处设计:Displacement 机制(否决项 1)、分阶段 prune(否决项 2)、"git history is the archive"(否决项 3)。读 ADR 时应把决策与备选对照着读,才能明白为什么"直接删除"不是粗暴而是刻意选择。
4. 后果:ADR 明确接受的代价
ADR《Consequences》部分逐条列出已接受的代价,每一条都值得工程/内容项目做类似重构时参照:
- 列表大幅缩水。ADR 给出的量化事实是:维护者对三个最大 Section 的预览保留了 80 条中的 45 条,并预判"most future PRs will be rejected for fullness, not badness"——未来多数 PR 会因"满员"而非"项目差"被拒;
- 主动放弃长尾搜索流量。awesome-python.com 将不再承载数百个小众工具名,因此失去对应的长尾检索流量。ADR 的表态是明确接受的:"reader trust over search surface"(读者信任优先于搜索覆盖面);
- 快速变化领域靠 Displacement 吸收流失。AI and Agents 这类领域按当前用量列出领跑者,人员/项目更替通过 Displacement 完成;过大的 Use Case 由维护者修剪或 Split(拆分为更细的 Use Case)。CONTEXT.md 对 Split 的定义是"在规模反映真正不同的活儿时,优先于任何修剪考虑的、维护者专属的结构重组";
- 外链 awesome-* 列表作为泄压阀。CONTRIBUTING.md 明确指向这类列表(例如 awesome-python-testing):"they exist precisely so this list doesn't have to be one"——想要穷举目录的读者走那些列表,本列表因此不必成为目录。
5. 规则如何落地:仓库中的执行证据
ADR 是"决定",而本仓库中至少有四处机制保证决定被执行,且彼此交叉印证:
5.1 CONTEXT.md:给规则和 LLM 的统一词汇表
CONTEXT.md 全文定义了本决策引入的领域语言(Entry、Sub-item、Use Case、Obvious Choice、Cap、Displacement、Challenger、Second Tier、Override、Split、Audit),并为每个词条附 "Avoid" 旧名对照。ADR 结尾的 "See CONTEXT.md for the vocabulary" 正是指向这里。值得一提的是,website/ 的构建管线会渲染出 website/templates/llms.txt 对应的 llms.txt 文件——这套结构化词汇同时服务于人类维护者和 LLM 消费者。
5.2 CONTRIBUTING.md:可执行的准入与拒绝清单
CONTRIBUTING.md 把 ADR 的抽象规则翻译成 PR 评审可直接套用的检查项:5 条 Quality Requirements、Admission 上限、Displacement、Dual-listing(同一工具在多个 Use Case 各占一席须独立挣得每席,且是维护者决策)、Override 条款(维护者可对特定条目/场景显式超限,但逐案生效、不可被投稿者引用),以及 5 步 Review Process(格式、分类、查重、活跃度、准入)和 9 条 Automatic Rejection 条款。
5.3 docs/audit-logs.md:超越单次提交的决策登记册
docs/audit-logs.md 是"单个 commit 无法展示的维护者决策"一览登记册,与 ADR 的"git history is the archive"原则互补:普通移除理由写在 prune sweep 的提交体里,而结构性例外记在此处。目前已登记两类:
- Naming Exceptions:显示名偏离 PyPI 包名规则的 10 个条目(如
pytorch对应包名torch、jinja对应Jinja2); - Mature-stable Keeps:5 个超出"12 个月活动"要求但按"成熟、稳定、无继任者"的编辑判断保留的条目(ftfy、itsdangerous、jieba、jinja、sortedcontainers)。
这正对应 CONTEXT.md 中 Override 词条的定义:维护者在 Audit 中做出、显式记录、逐案生效的超限决定,且"submitters cannot cite one"。
5.4 构建管线:结构词汇与解析器的对应关系
website/readme_parser.py 用 markdown-it-py 把 README.md 解析为结构化数据,其类型定义与 ADR/CONTEXT 词汇一一对应:ParsedGroup ↔ Thematic Group、ParsedSection ↔ Section、ParsedEntry.subcategory ↔ Subcategory、ParsedEntry.also_see ↔ Sub-item(缩进的 awesome-* 链接,不占名额、不计入 Cap、随父条目存亡)。模块 docstring 还记录了几个对 README 编辑者很重要的经验事实:## Projects 之前的内容被忽略;新增 Subcategory 不需要改解析器(无前置链接的项目符号 + 缩进条目即可被识别);构建输出的 "Total entries" 统计包含 Sub-item 而不仅是 Entry。这说明 ADR 的"结构由列表既有结构定义"在技术上是可自举的——结构词汇表与解析器、CONTRIBUTING 规则、审计流程形成了闭环。
6. 小结:一份可复用的"列表瘦身"决策模板
ADR 0001 的价值不止于 awesome-python 自身,它完整示范了内容型/索引型开源项目从"目录"转向"短名单"时的决策结构:
- 先用数据定义病态(576 条、24x 流入、AI 类 41 条),再给重构立一个可检验的单句目标(Zen of Python 的 one obvious way);
- 规则按"边界测试 → 单位(Use Case)→ 名额(Cap)→ 信号(PyPI 下载量)→ 满员入口(Displacement)→ 追溯清剪"顺序定义,并明确数字是临时值、留待清剪后复审;
- 备选方案与否决理由同文保留,防止规则在后续评审中被"还原";
- 代价显式入账(缩水幅度、搜索流量、快速领域的更替机制),避免重构后各方对结果各执一词;
- 词汇表(CONTEXT.md)、操作规范(CONTRIBUTING.md)、决策登记册(audit-logs.md)、构建管线(readme_parser.py / llms.txt)四件套保证决策不悬浮在文档层,而是可执行、可审计、可被机器消费的。
如果需要在 awesome-python 中提交或评审条目,请以 CONTRIBUTING.md 为操作准绳、以 CONTEXT.md 为术语基准,并留意 docs/audit-logs.md 中已登记的例外——那正是本 ADR 所说的"判断保留裁量,但裁量被记录"的具体体现。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00