news 2026/9/17 6:37:38

Pyrefly Issue Ranking Pipeline:用“双阶段 + 5 轮 LLM“管线自动为 GitHub Issue 排优先级

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Pyrefly Issue Ranking Pipeline:用“双阶段 + 5 轮 LLM“管线自动为 GitHub Issue 排优先级

Pyrefly Issue Ranking Pipeline:用"双阶段 + 5 轮 LLM"管线自动为 GitHub Issue 排优先级

【免费下载链接】pyreflyA fast type checker and language server for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyrefly

在类型检查器这类"误报即劝退用户、漏报即损害可信度"的项目中,GitHub Issue 的优先级排序是一项高人力成本、且容易被主观判断带偏的工程活动。Pyrefly 仓库在 scripts/issue_ranker/AGENTS.md 中描述了一套自动化管线:先从 GitHub 拉取 Issue,用 pyrefly/pyright/mypy 三个检查器实测 Issue 中的代码片段、结合真实开源项目(primer)上的错误数据做"影响面接地",再经过 5 轮分工明确的 LLM 调用(分类 → primer 影响 → 依赖关系 → 打分 → 终排)产出可复现的优先级榜单。读完本文,你能完整理解该管线的两阶段架构、各 Pass 的模型选择与成本估算、primer 分片并行机制,并能直接在本地或 CI 中运行 collect / rank / v1-analysis 各模式。

整体架构:collect 与 rank 两大阶段

该管线由 CLI 入口 驱动,支持--mode collect--mode rank--mode full(前两者合并)以及--mode v1-analysis(对既有 ranking.json 做事后 V1 差距分析,见 入口文件文档字符串)。

  • Collect 阶段--mode collect):从 GitHub API 抓取 Issue 并"富化"——提取代码、跑检查器、分类状态、解析 Issue 间关系,产出中间 JSON。
  • Rank 阶段--mode rank):在富化数据之上运行由 pipeline.py 编排的 5 轮 LLM 管线,输出 Markdown 报告与 JSON 结果。

两阶段解耦的好处是:昂贵的 collect(要跑真实检查器、安装依赖)只需要定期执行一次,而排名策略(prompt、权重、模型)可以在 rank 阶段快速迭代。

Collect 阶段的 7 个步骤

main.py 的collect_issue_data按顺序执行 7 个阶段:

  1. 拉取 Issue(github_issues.py):通过 GitHub API 获取 open issues,支持--labels标签过滤与--limit数量限制。
  2. 区分类型检查类 Issue_is_typechecking_issue):只有"值得提取代码并实际跑检查器"的 Issue 才进入后续环节。其判定逻辑是:
    • 标题含 "false positive" / "false negative" → 一定是类型检查问题;
    • 命中类型检查标签集(typecheckingnarrowingoverloadsconformancetypeshed等,见_TYPECHECKING_LABELS)→ 是;
    • 命中跳过标签集(performancelanguage-serverdocumentationconfigurationbuild-fails等,见_SKIP_CODE_LABELS)→ 否;
    • 无标签时退回标题关键词启发式(typetypingnarrowprotocoldataclass等),且刻意避免把 "error handling" 这类词误判为类型问题。
  3. 提取代码块(code_extractor.py):三级策略——先匹配显式python /py 围栏(_PYTHON_FENCE_RE),再匹配"看起来像 Python"的裸围栏(用行首def/class/import/from x import强信号避免误匹配错误输出文本),最后回退到LLM 提取:让 Haiku 模型基于 Issue 正文生成一个最小可复现代码片段(NO_CODE表示无法用代码演示)。
  4. LLM 修复残缺片段repair_snippet):Issue 里的粘贴代码常常缺 import、有语法错误或不完整。修复 prompt 明确要求"只修坏的部分、不重写逻辑",并携带 Issue 标题与正文作为上下文。
  5. 运行 pyrefly / pyright / mypy(issue_checker.py + dep_resolver.py):这是 collect 阶段技术含量最高的一环,下文单独展开。
  6. 状态分类(status_classifier.py):基于三方检查器结果把每个 Issue 归入五类之一——false_positive/false_negative/confirmed_bug/already_fixed/feature_request
  7. 解析关系(relationship_resolver.py):识别重复 Issue、阻塞关系(blocker)、父子 Issue,为 rank 阶段的依赖图 Pass 提供基础。

collect 结束后写出的 JSON 结构为{"issues": [...], "relationships": {...}, "metadata": {...}},metadata 中记录了标签过滤条件、Issue 总数、含代码的 Issue 数与类型检查 Issue 数(collect_issue_data末尾)。

检查器执行细节:先跑、再装依赖、重试一次、最后过滤

issue_checker.py 的设计文档(issue_checker.py 模块 docstring)概括了核心策略:每个代码片段写入临时目录,依次执行——

  • pyrefly:pyrefly check --output-format json <snippet>(JSON 输出便于机器解析);
  • pyright:pyright --outputjson <snippet>
  • mypy:mypy --no-error-summary <snippet>
  • 另外还会用python3直接执行片段,尽力捕获运行时输出——当第三方依赖让静态检查不可靠时,运行时行为是重要的旁证信号。

所有检查器调用均带 60 秒超时,超时返回"CHECKER_TIMEOUT"标记(这与 primer 管线中的超时标记一致)。

依赖处理是这个流程中最微妙的部分,dep_resolver.py 采用"让检查器先报缺失、再据错误信息提取模块名"的路线,而不是预先静态扫描 import——因为检查器本身已知道哪些模块是标准库、哪些是第三方,天然更准。其具体机制:

  • 用一组正则从三类检查器的错误信息中提取缺失模块名(pyrefly 的Could not resolve import of "foo"、pyright 的Import "foo" could not be resolved、mypy 的Cannot find implementation or library stub for module named "foo"等,见_MISSING_MODULE_PATTERNS);
  • 维护模块名 → pip 包名映射表(PIL → pillowcv2 → opencv-pythonsklearn → scikit-learn等,见_MODULE_TO_PACKAGE);
  • 先尝试批量pip install,失败则逐个安装(install_missing_modules,dep_resolver.py#L102-L146);
  • 安装成功后重跑全部检查器一次;重试后仍然残留的 import 类错误被过滤掉(片段本就不可能带全依赖),而安装失败的模块记入unresolved_deps字段。

unresolved_deps这个字段非常关键:下游的classify_status和打分 Pass 都会检查它——当依赖装不上且检查器"0 错误"时,系统明确知道这个 0 是不可信的(检查器根本没解析到第三方类型),会在 prompt 中附加DEPENDENCY WARNING,要求 LLM 转而依据 Issue 描述推理严重性,而不是轻信"0 错误 = 已修复"。

状态分类:启发式快路径 + LLM 兜底

status_classifier.py 先用廉价规则处理显而易见的情形:没有任何检查器结果(即没代码)→feature_request;三方检查器都没有"真实错误"(过滤掉reveal_type这类信息性输出后)→already_fixed。其余情形交给 Haiku 模型,prompt 中特别强调:跨检查器比较错误要"语义比较而非字面比较"reveal_type是信息性输出不计入错误数、import 错误应忽略。若 LLM 返回非法状态或调用失败,则回退到简单启发式(_heuristic_classify:只有 pyrefly 报错 → false positive;只有其他检查器报错 → false negative;都报错 → confirmed bug)。

Rank 阶段:5 轮 LLM 管线的模型分工与成本估算

run_pipeline 按顺序执行 5 个 Pass,每个 Pass 使用不同档位的小/大模型,并在日志中记录耗时与美元成本估算。成本估算表(_COST_ESTIMATES)大致为:Haiku 单次调用约 $0.002、Sonnet 约 $0.03、Opus 大批量调用约 $0.75。5 个 Pass 分别对应 passes/ 目录下的模块:

Pass模块模型与调用量职责
1. Categorizepasses/categorize.pyHaiku,每 Issue 一次分类 Issue 类型/领域(false positive、性能、IDE、规范符合性等)
2. Primer Impactpasses/primer_impact.py以确定性字符串匹配为主,少量 Haiku 模糊兜底把 Issue 匹配到 primer 真实错误上,量化影响面
3. Dependenciespasses/dependencies.pyOpus,仅 1 次构建依赖图:阻塞链、重复簇、依赖组
4. Scorepasses/score.pySonnet,每 Issue 一次0–100 加权打分,附分项 breakdown 与理由
5. Rankpasses/rank.pyOpus,按 50 个 Issue 一批结合全部前置结果做终排序与同分裁决

值得注意的健壮性设计(均可在 pipeline.py 中验证):

  • Pass 3 失败降级:依赖图构建若抛异常,则降级为空图(dependency_groups/blocking_chains/duplicate_clusters全空)继续后续 Pass;
  • Pass 5 完全失败降级:回退为"按 priority_score 排序 + 机械分层"的 fallback 排名,并在 summary 中标注原因;
  • Pass 结果缓存--pass-results <path>会落盘 Pass 1–4 的中间结果(categorizations/primer_impacts/dep_graph/scores),再次运行时若缓存完整则跳过 Pass 1–4、只重跑 Pass 5(run_pipeline#L65-L92)。CI 中这正是用来反复迭代终排 prompt 而不用重新支付前四轮成本的手段;
  • 打分重试score_all对单个 Issue 打分失败会等待 2 秒重试一次,再失败则记为priority_score: -1.0并附SCORING FAILED理由,避免整体管线因单点失败中断。

Pass 2:primer 匹配——确定性模板化 + 具体度评估

Pass 2 是"把排名扎根于真实影响面"的关键。primer_impact.py 的匹配流程:

  1. 建索引:遍历 primer 数据中每个项目的 pyrefly 错误,把错误消息中反引号包裹的标识符替换为`_`形成"消息模板"(_templatize,primer_impact.py#L27-L30),即Argument 'x' is not assignable to parameter 'y'归一为Argument '_is not assignable to parameter '_',按(error_kind, template)聚合出受影响项目集合与总错误数(_build_primer_index);
  2. Kind 匹配:先精确匹配 Issue 错误 kind 与 primer kind,再尝试归一化(去连字符/下划线、小写)匹配,再做子串包含匹配,最后才用一次 Haiku 调用做语义级模糊匹配(_fuzzy_match_kind);
  3. 模式具体度评估:匹配成功后再用一次 Haiku 调用评估"该模式对本 Issue 的具体度"——HIGH表示该 Issue 很可能就是这些 primer 错误的主要成因,LOW表示模式过于泛化、根因众多。这个pattern_specificity随后会被传入 Pass 4 的 prompt,让打分时按具体度加权 primer 计数,避免"匹配数很多但相关性很弱"的假信号。

Pass 4:打分权重——误报与性能最重,实现难度不计分

Pass 4 的系统 prompt(_SYSTEM_PROMPT)是整条管线价值观的浓缩,其权重表值得完整呈现:

权重信号说明
最高 (x3)误报影响pyrefly 报错而 pyright/mypy 不报错的 Issue——这类错误直接赶走用户;primer 频次高会放大该信号
最高 (x3)性能内存、检查速度、LSP 响应。性能问题直接阻碍规模化采用
高 (x2)团队指派优先级GitHub Projects 中的 P0/P1/P2(P0 → 80+ 分,P1 → 65+,P2 → 50+);但 prompt 明确允许 LLM 覆盖过期/错误的优先级
中 (x1.5)漏报 + 规范符合度pyrefly 漏报而 pyright/mypy 能抓到的真实类型错误;TypeVar/ParamSpec/overloads/narrowing 等规范差距。影响正确性,但因用户感知不到"没报错",紧急度低于误报
中 (x1.5)可处理性有清晰最小复现、根因已定位、范围明确 → 高分;描述含糊、无法复现 → 低分。特别强调:可处理性 ≠ 实现难度,修起来再难也不扣分
高 (x2)IDE 与易用性hover/补全/跳转/诊断等 LSP 功能;IDE bug 建议 60–80 分,IDE 功能 45–65 分
中 (x1.5)Primer 广度多少 primer 项目命中该错误模式,需结合具体度评估加权
高 (x2.5)战略采用打了 pytorch / google 标签的 Issue 是最高优先的采用目标,阻塞类应给 75+
中 (x1.5)生态采用pydantic、sqlalchemy 等框架标签,低于战略目标
低 (x0.5)边缘情形影响项目少、交互少、陈旧、小众场景

prompt 中有两条硬约束尤其体现设计意图:

  1. "修复难度或复杂度永远不能降低分数"——难修的 bug 和易修的 bug 一样重要,只按用户影响与采用风险打分(score.py#L47);
  2. "盲评"设计:LLM 不知道哪些 Issue 已被团队划入 V1 milestone,纯靠信号打分,从而可以用事后重叠率来盲验证管线质量(score.py 模块 docstring)。

打分输入还会组装丰富的上下文(score_issue):前序 Pass 的分类与 primer 命中数、反应数/评论数/子 Issue 数、重复簇规模、该 Issue 阻塞的其他 Issue 数量、unresolved_deps警告、Python 运行时输出、前 5 条评论内容,以及从 spec_fetcher.py 拉取的 typing 规范摘录(按 primer kind 或 pyrefly 错误 kind 检索)作为接地材料。输出为priority_score(0–100)+ 9 个分项 breakdown(false_positive_impact、performance、team_priority、correctness、actionability、ide_usability、primer_breadth、adoption_impact、community_demand)+ 一两句理由。

Primer 管线:用 137 个真实开源项目给排名"接地"

Rank 所需的 primer 数据由独立脚本 scripts/compare_typecheckers.py 生产:它克隆约 137 个开源 Python 项目,对每个项目分别运行 pyrefly、pyright、mypy,输出primer_errors.json供 ranker 的 primer_impact Pass 消费。其运行前提与本地用法(来自脚本 docstring,compare_typecheckers.py#L1-L31):

  • 需要一个安装了 pyright 与 mypy 的 Python 环境(pip install pyright mypy);
  • 支持两阶段模式:先--clone-only --cache-dir /tmp/primer_cache克隆项目,再在隔离环境内--reuse-cache跑检查器,方便在不能联网/克隆的受限环境(如容器内)分步执行;
  • 检查器输出有两种形态:默认的错误计数汇总表,以及--output-json的完整错误消息 JSON(后者才是 ranker 需要的输入);
  • 超时处理与 issue_checker 一致:子进程超时被 kill 后返回stderr="CHECKER_TIMEOUT"的占位结果(compare_typecheckers.py#L53-L69)——注意这里是刻意使用的专用标记"CHECKER_TIMEOUT"而非普通"timeout",下游解析时不会与代码里的超时字样混淆。

分片并行--shard-index/--num-shards两个参数实现(_apply_sharding):参数必须成对出现、--num-shards为正、--shard-index必须在[0, num_shards)区间内;项目列表按步长切片projects[shard_index :: num_shards]分片,各分片可完全并行。各分片的 JSON 结果由 scripts/merge_primer_shards.py 合并回单文件,再交给 rank 阶段的--primer-data

GitHub Actions 工作流:分片矩阵 + 三任务串联

.github/workflows/issue_ranking.yml 把上述脚本装配成可手动触发(workflow_dispatch)的 CI 工作流,输入参数包括:

  • labels:逗号分隔的标签过滤(留空 = 所有 open issues);
  • run_primer:是否运行 primer 对比(约增加 2 小时时长);
  • check_snippets:是否对 Issue 代码片段跑检查器(默认开);
  • v1_analysis_run_id:填了则只做 V1 分析,从指定 Run 下载 ranking.json,跳过 primer 与排名。

工作流包含 4 个 job,依赖关系为primer(×4) → primer-merge → rank,另有条件触发的v1-analysis-standalone

  1. primermatrix.shard: [0, 1, 2, 3]四分片并行跑compare_typecheckers.py,每分片timeout-minutes: 300;先cargo build --release构建 pyrefly 二进制,pip install pyright mypy,产物上传为 artifactprimer-shard-N(issue_ranking.yml#L29-L83)。
  2. primer-merge:用 sparse-checkout 只拉取 merge_primer_shards.py,下载 4 个分片 artifact 后合并为primer_errors.json,上传为primer-results
  3. rank:串联三个子步骤——
    • 收集python -m scripts.issue_ranker --mode collect,若check_snippets开启则pip install pyrefly pyright mypy并用which pyrefly定位二进制传入--pyrefly(issue_ranking.yml#L192-L232)。注意此处GITHUB_TOKEN优先取secrets.GH_PROJECT_TOKEN——该 token 需要read:project权限才能拉取 GitHub Projects 里的 P0/P1/P2 优先级,缺失时会回退到普通 token,此时优先级字段为空(工作流内注释有说明);
    • 排名--mode rank,若 primer 数据存在则附加--primer-data,并始终传--pass-results /tmp/pass_results_cache.json启用缓存;
    • V1 差距分析--mode v1-analysis --apply-labels,读 ranking.json,把分析结论以v1-verified/consider-adding/removing标签回写 GitHub Issue(入口代码见main.py#L380-L396,仅管理这三类标签,不碰其他标签),并追加到 Run Summary;
    • 最后打印分层统计(critical/high/medium/low 各多少)与 Top 10 Issue,并上传ranking.mdranking.json、缓存与全部日志作为 artifactissue-ranking
  4. v1-analysis-standalone:当只填了v1_analysis_run_id时,用gh run download <run_id> -n issue-ranking从历史 Run 拉取 ranking 产物,单独重跑 V1 分析——支持"排名只做一次,标签策略反复迭代"的低成本玩法。

权限方面值得注意:工作流整体permissions: {}显式清空,仅 rank 与 v1 job 按需申请issues: write(用于回写标签),LLM 调用密钥使用secrets.PRIMER_CLASSIFIER_API_KEY注入ANTHROPIC_API_KEY

本地运行与完整参数速查

AGENTS.md 给出的最小本地运行序列(需要:pyright/mypy 已安装、pyrefly 二进制、GITHUB_TOKENANTHROPIC_API_KEY):

# 1. Primer(需要 pyrefly 二进制;没有 cargo 时用 --pyrefly 指向现成二进制) python3 scripts/compare_typecheckers.py --output /tmp/primer.json \ --pyrefly /path/to/pyrefly # 2. 收集并富化 Issue(需要 GITHUB_TOKEN) python3 -m scripts.issue_ranker --mode collect \ --pyrefly /path/to/pyrefly --output /tmp/issues.json # 3. 排名(需要 ANTHROPIC_API_KEY) python3 -m scripts.issue_ranker --mode rank \ --primer-data /tmp/primer.json --issue-data /tmp/issues.json \ --output /tmp/ranking.md --output-json /tmp/ranking.json

完整 CLI 参数(来源:main.py 的main()):

参数说明
--mode {collect,rank,full,v1-analysis}必需。collect 抓取+富化;rank 跑 LLM 管线;full 两者合一;v1-analysis 对既有 ranking.json 做 V1 差距报告
--labels逗号分隔的 GitHub 标签过滤(默认所有 open issues)
--limit N最多抓取 N 个 Issue(调试用)
--pyrefly PATH用于检查代码片段的 pyrefly 二进制路径;collect 阶段不提供则跳过检查器执行
--primer-data PATHcompare_typecheckers.py 产出的 primer_errors.json
--issue-data PATHcollect 模式产出的 issue_data.json(rank 模式必需,除非用 full)
--output/-oMarkdown 报告输出路径;不传则打印到 stdout
--output-jsonJSON 结果输出路径
--pass-resultsPass 1–4 中间结果缓存路径;文件有效时跳过 Pass 1–4 只重跑 Pass 5
--ranking-jsonv1-analysis 模式的输入 ranking.json(缺省回退取--output-json值)
--apply-labelsv1-analysis 模式下把标签回写到 GitHub(仅管理 v1-verified / consider-adding / removing 三类)
--debug开启 DEBUG 级日志

测试方面,AGENTS.md 指出单测位于 scripts/issue_ranker/tests/(含对 ranker 与 compare_typecheckers 的测试),LLM 集成测试位于 scripts/issue_ranker/llm_tests/(覆盖代码提取、状态分类、各 Pass、GitHub 抓取与 LLM 传输层),并可用buck test pyrefly:issue_ranker_tests运行。

经验结论:primer 数据与团队优先级标签为何都重要

AGENTS.md 最后记录了 2026 年 3 月的一组管线调优经验(原文档中的关键结论,可视为该团队的实测发现):

  • Primer 数据显著改变排名:在 137 个真实项目上运行检查器让排名扎根于实际影响面;没有 primer 数据时,单个 Issue 的得分可能摆动 30–44 分,分层(tier)分布也会大幅漂移;
  • 团队优先级标签有独立价值:纳入 GitHub Projects 的 P0/P1/P2 后,管线排名与 V1 milestone 的重叠率提升约 16 个百分点;
  • 两者叠加效果最佳——真实影响面(客观、来自代码实测)与团队判断(主观、但反映业务约束)互为补充。

这一结论也解释了管线的整体设计哲学:确定性计算(检查器实测、字符串模板匹配、分片统计)负责提供可复现、可核实的"事实信号",LLM 只负责其中真正需要语义理解的环节(代码提取/修复、状态归类、依赖图、加权打分、终排裁决),并且每一处 LLM 输出都有启发式 fallback(状态分类的_heuristic_classify、Pass 3/5 的降级路径、Pass 2 的确定性匹配优先),使得整条管线在 API 不可用时仍能产出可用结果。

核心文件索引

路径角色
scripts/issue_ranker/AGENTS.md管线架构总览(本文主体文档)
scripts/issue_ranker/main.pyCLI 入口,collect 七阶段与四种 mode
scripts/issue_ranker/pipeline.py5 轮 LLM 管线编排、成本估算、缓存与降级
scripts/issue_ranker/code_extractor.py三级代码提取 + LLM 片段修复
scripts/issue_ranker/issue_checker.py三检查器执行、依赖重试、错误过滤
scripts/issue_ranker/dep_resolver.py从检查器错误提取缺失模块并安装
scripts/issue_ranker/status_classifier.py五态分类:启发式 + LLM 兜底
scripts/issue_ranker/relationship_resolver.py重复/阻塞/父子关系解析
scripts/issue_ranker/passes/5 个 Pass 实现(categorize / primer_impact / dependencies / score / rank)
scripts/issue_ranker/llm_transport.pyLLM API 封装(Anthropic + Llama),带重试
scripts/issue_ranker/spec_fetcher.py拉取 typing 规范摘录供打分接地
scripts/issue_ranker/report_formatter.pyMarkdown / JSON 报告生成
scripts/compare_typecheckers.pyprimer 对比脚本(克隆、分片、双检查器)
scripts/merge_primer_shards.py合并 primer 分片输出
.github/workflows/issue_ranking.ymlCI 工作流:分片矩阵、合并、排名、V1 分析
scripts/issue_ranker/tests/单元测试
scripts/issue_ranker/llm_tests/LLM 集成测试

【免费下载链接】pyreflyA fast type checker and language server for Python项目地址: https://gitcode.com/GitHub_Trending/py/pyrefly

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/17 6:36:51

SpringBoot+Vue智慧药店管理系统开发实践

1. 项目背景与核心价值智慧药店药品信息管理系统是传统药店数字化转型的关键基础设施。随着医药零售行业信息化程度提升&#xff0c;单纯依赖人工记录药品进销存的方式已无法满足现代药店管理需求。这个毕业设计项目通过SpringBoot框架实现了药品全生命周期管理&#xff0c;包含…

作者头像 李华
网站建设 2026/9/17 6:36:25

高分三号UFS数据预处理全流程:从L1A到地理编码影像

拿到高分三号超精细条带&#xff08;UFS&#xff09;数据的原始包时&#xff0c;很多人第一反应是“无从下手”&#xff1a;一坨L1A级复数产品&#xff0c;打开以后都是幅度相位&#xff0c;没有坐标&#xff0c;也没有能直接用的TIF。说实话&#xff0c;我当年第一次处理UFS数…

作者头像 李华
网站建设 2026/9/17 6:32:32

羽毛球场景目标检测实战:YOLO训练、小目标识别与标注格式转换全解析

做体育场景的目标检测项目时&#xff0c;我经常在通用数据集上碰一鼻子灰。尤其是羽毛球这种小目标、快速度、强遮挡的运动项目&#xff0c;直接用COCO预训练权重下场识别&#xff0c;效果只能用"惨烈"来形容——运动员漏检、裁判框错、羽毛球根本看不见。最近拿到了…

作者头像 李华
网站建设 2026/9/17 6:30:19

小爱音箱放本地音乐的完整指南:xiaomusic 从安装到开口点播

小爱音箱放本地音乐的完整指南&#xff1a;xiaomusic 从安装到开口点播 【免费下载链接】xiaomusic 使用小爱音箱播放音乐&#xff0c;音乐使用 yt-dlp 下载。 项目地址: https://gitcode.com/GitHub_Trending/xia/xiaomusic 周六早晨你随口一句"小爱同学&#xff…

作者头像 李华