LobeHub 如何用 Claude Code `/dedupe` 命令自动化 GitHub Issue 去重:从五步 Agent 工作流到 3 天自动关闭的完整实现
开源仓库的 Issue 数量增长后,同一个 Bug 或需求往往会被不同用户以不同措辞重复提交,维护者需要耗费大量精力去识别与合并重复项。LobeHub 仓库(项目根目录即仓库根目录)将这一繁琐工作交给 Claude Code 以**可复用的 Slash Command(斜杠命令)**形式完成:仓库中的 .claude/commands/dedupe.md 定义了一条名为 /dedupe 的命令,由 GitHub Actions 在 Issue 创建时自动触发,通过多 Agent 并行检索、过滤与回帖,找出最可能的重复 Issue,并在给出反馈 3 天后自动关闭。读完本文,你将掌握这条命令的文件结构、五步执行流程、输出格式规范,以及它在 LobeHub 仓库中与 Actions Workflow、确定性预检脚本和自动关闭脚本协同工作的完整闭环。
一、命令文件解剖:Frontmatter 即权限边界
.claude/commands/dedupe.md 是一条标准的 Claude Code Slash Command 定义。所谓 Slash Command,是 Claude Code 中可通过 /命令名 直接触发的一段高度结构化的 Prompt 模板。文件开头的 YAML Frontmatter 定义了该命令的元数据:
---
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh api:*), Bash(gh issue comment:*)
description: Find duplicate GitHub issues
---
allowed-tools(允许的工具白名单):这是整条命令最关键的安全约束。它把 Agent 可执行的工具严格限定为gh这一 GitHub 官方 CLI 的五个子命令(issue view、search、issue list、api、issue comment),每条都带有*通配符放行参数。除此之外的任何工具——文件编辑、浏览器、其他 MCP 服务器——都无法被调用。后文会看到,这正是它敢被部署到面向不可信用户的公开 Issue 工作流中的底气。description(命令描述):Find duplicate GitHub issues,用于在 Agent 语境中描述命令用途,方便被正确调度。
正文第一句即点明命令目标:"Find up to 3 likely duplicate issues for a given GitHub issue."(为给定的 GitHub Issue 找出最多 3 个疑似重复项)。最多 3 个 是一个刻意设置的收敛上限,避免评论过长或引入噪音。
二、核心执行流程:一个编排者加 N 个并行检索者
命令正文以强指令口吻("follow these steps precisely",请精确按步骤执行)定义了五步流水线。它要求"先建立 todo list",让 Agent 在执行时对每步有显式的任务追踪:
第 1 步:前置资格检查(Preflight)
先由 Agent 确认目标 Issue 是否真的值得去重,命中以下任一情况则直接终止、不做任何处理:
- Issue 已关闭(closed);
- Issue 本就不需要去重,例如:
- 宽泛的产品反馈(没有具体解决方案);
- 正面反馈(positive feedback);
- 新的 MCP Marketplace 上架请求——这类请求由独立的 MCP Submission Handler 工作流负责,重复检测会与其产生冲突;
- 你此前已经在上面回复过重复评论(避免同一命令被重复触发时二次加评)。
这一步骤把"是否值得花钱/花时间跑大模型检索"的决策前置,属于典型的成本与噪音控制设计。
第 2 步:单 Agent 摘要(Issue Summarization)
派一个 Agent 读取目标 Issue 的完整内容,并返回一份摘要。这份摘要是第 3 步并行搜索的唯一输入源,它把原始 Issue 的冗长正文浓缩成可供多路检索的"语义锚点"。
第 3 步:5 路并行检索(Parallel Search)
基于第 2 步的摘要,同时启动 5 个并行 Agent 去 GitHub 上搜索重复 Issue,要求使用多样化的关键词与搜索思路。5 路并行的意义在于:
- 覆盖标题、正文、复现步骤、报错信息等不同角度的表述差异;
- 同一个问题常常被不同用户用完全不同的术语描述,单一检索词必然漏检;
- 各 Agent 独立搜索后汇聚结果,再由下一步统一做交叉过滤。
注意第 1、2 步产生的结果(摘要与候选)会在第 4 步被"喂给"另一个 Agent——文档原句为 "feed the results from #1 and #2 into another agent",即上一步的产出是下一步的输入,形成 Agent 之间的数据交接链。
第 4 步:过滤误报(False-Positive Filtering)
把第 2 步摘要与第 3 步搜索候选一起交给另一个 Agent,由它剔除"看起来相关、实际并非重复"的误报。过滤后若无剩余候选则终止,不发布任何评论。这一步是质量闸门:宁可漏判,也不要把不相关的 Issue 指认为重复,避免骚扰真正的 Issue 作者。
第 5 步:回帖(Comment)
最终在目标 Issue 下回复最多 3 个(或 0 个,若已无可疑项)重复 Issue 链接,并附带自动关闭的说明(格式详见下一节)。
三、评论格式:机器可读的契约
命令对回帖格式做了"严格按此格式"(follow the following format precisely)的规定,以"发现 3 个疑似重复"为例:
Found 3 possible duplicate issues:
1. <link to issue>
2. <link to issue>
3. <link to issue>
This issue will be automatically closed as a duplicate in 3 days.
- If your issue is a duplicate, please close it and 👍 the existing issue instead
- To prevent auto-closure, add a comment or 👎 this comment
> 🤖 Generated with Claude Code
这段看似简单的文案其实承担了三个契约角色:
- 对人类读者:清楚列出疑似重复项、给出明确的 3 天自动关闭预告;
- 对 Issue 作者:给出两条显式操作路径——"确认重复就自行关闭并 👍 既有 Issue""认为误判就追加评论或 👎 本条评论",把异议机制写进了提示文本;
- 对下游自动化脚本:
auto-close-duplicates.ts正是通过匹配评论中的Found与possible duplicate关键字来识别"去重评论",通过正则抽取#123或 GitHub Issue URL 中的编号来确定目标重复 Issue,再检查评论是否存在 Issue 作者的 👎(-1)反应来判定是否被否决。这条格式是人与机器共同的接口协议。
四、仓库落地全景:从 Action 触发到命令执行
只有命令文件并不能形成自动化。在 LobeHub 仓库中,.claude/commands/dedupe.md 只是整条流水线的"大脑",围绕它还有三层配套:
4.1 触发层:.github/workflows/claude-dedupe-issues.yml
该工作流在 Issue opened 事件和 手动 workflow_dispatch(可指定 issue_number)时触发,Job 名 claude-dedupe-issues,运行于 ubuntu-latest,超时 10 分钟,声明 issues: write 权限。其核心步骤链为:
actions/checkout@v6检出仓库;- 复用仓库自带的 .github/actions/setup-env 准备环境;
- 执行确定性预检(见 4.2);
- 仅当预检输出
should_dedupe == 'true'时,把 .claude/prompts/security-rules.md 复制到/tmp/claude-prompts/; - 调用
anthropics/claude-code-action@v1,通过--append-system-prompt注入安全规则,并以prompt: '/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}'触发命令。
工作流中 ${{ github.repository }}/issues/<编号> 形式的参数即 dedupe.md 第 1 步中"given GitHub issue"的来源。仓库对使用 Slash Command 而非直接 Prompt 的原因给出了显式注释:"Security: Using slash command which has built-in restrictions / The /dedupe command only performs read operations and label additions"——即 slash command 自带 Frontmatter 级的工具限制,天然比自由 Prompt 更安全。
4.2 守卫层:确定性预检脚本 should-run-dedupe.ts
在昂贵的 Claude 执行之前,先跑一段零 AI 成本、完全确定性的过滤逻辑:.github/scripts/should-run-dedupe.ts(Bun 脚本)。其导出的 shouldDedupeIssue() 按顺序判定:
- Issue 非
open状态 → 跳过(reason:Issue is not open); - 命中一组 MCP 相关标签 → 跳过(reason: 由 MCP submission workflow 处理)。标签集来自 .github/scripts/shared/mcp-labels.ts,包含
mcp-submission及一系列 legacy 标签; - 调用 .github/scripts/shared/mcp-submission-classifier.ts 的
classify(),对标题正文做内容级分类,识别出"即便没打标签也能被认出是 MCP 上架请求"的文本 → 跳过; - 否则 →
shouldDedupe: true(reason:Issue is eligible for duplicate detection)。
注意它的判定逻辑与 dedupe.md 第 1 步高度呼应——文档里的"MCP marketplace listing/submission request"与"已关闭"等豁免条件,正是由这道脚本在模型介入前就确定性拦截掉的。脚本会把 should_dedupe 与 reason 写入 GITHUB_OUTPUT,供工作流通过 steps.dedupe-preflight.outputs.should_dedupe 读取,从而用 if 条件决定是否继续。
4.3 收尾层:3 天后自动关闭 auto-close-duplicates.ts
.github/scripts/auto-close-duplicates.ts 是"3 days"承诺的执行者,由独立的定时工作流 .github/workflows/issue-auto-close-duplicates.yml 每天凌晨 2 点(cron 0 2 * * *)触发。脚本逐页拉取开放 Issue,对每条查找来自 Bot 用户、正文包含 Found 与 possible duplicate 的评论,仅当:
- 去重评论已存在超过 3 天;
- 该评论之后再无任何新评论(即没有作者或其他人追加留言抗议);
- 评论上没有 Issue 作者本人的 👎(
-1)反应;
……全部满足时,才通过 PATCH 接口关闭该 Issue(设置 state: closed、state_reason: duplicate、追加 duplicate 标签),并回帖说明"已自动关闭为 #编号 的重复项,若判断有误请重新打开"。这与去重评论中"add a comment or 👎 this comment to prevent auto-closure"的人机交互设计一一对应——作者一次 👎 就能中止自动关闭,形成了"AI 提议、作者可否决、静默期后执行"的保守闭环。
五、安全设计剖析:为什么可以信任公开 Issue 上的执行
将 LLM Agent 暴露给"任何陌生人都能提交内容"的公开 Issue,安全设计是核心考量。从 dedupe.md 及其周边配置可以提炼出四层防线:
- Frontmatter 工具白名单(命令层):
allowed-tools仅放行gh的 5 个只读/低危子命令,禁止文件编辑、网页抓取与其他 MCP 工具。命令正文的 Notes 也明确要求各子 Agent:"Do not use other tools, beyondgh"。 - 系统提示注入(运行层):工作流在触发前把 security-rules.md 以
--append-system-prompt注入,防止命令内容被 Issue 正文中的 Prompt Injection 劫持。 - 确定性前置闸门(流程层):
should-run-dedupe.ts用纯规则过滤,把明显无需去重(关闭、MCP 上架请求等)的 Issue 挡在 AI 执行之前,减少模型暴露面与 API 开销。 - 只写评论、不做破坏性操作(结果层):
/dedupe的输出仅是"回帖 + 可选的标签",真正的 Issue 关闭由独立脚本在满足静默期与作者未否决条件后才执行。
六、测试保障:用单测锁住工作流行为
该机制并非一次性脚本,仓库在 tests/github-scripts 下对其做了单测覆盖,保证后续演进不破坏契约:
- tests/github-scripts/shouldRunDedupe.test.ts 覆盖
shouldDedupeIssue()的判定矩阵:已关闭 Issue 跳过、带mcp-submission/mcp:remote标签的跳过、正文是本地或远程 MCP 上架请求的跳过、提到@lobehub/market-cli的"变体上架请求"也能被分类器识别跳过、普通开放 Issue 正常放行。 - tests/github-scripts/claudeDedupeWorkflow.test.ts 直接解析 YAML 工作流文件与 TS 源码断言架构约束:预检脚本必须只依赖
mcp-submission-classifier而不得依赖auto-handle-mcp-submission(保持预检与 MCP 处理执行器的解耦);且预检步骤id: dedupe-preflight的run必须包含bun run .github/scripts/should-run-dedupe.ts,Claude 步骤的if条件必须引用steps.dedupe-preflight.outputs.should_dedupe == 'true',从而锁定"先确定性预检、后 Claude 执行"的顺序。
七、使用与约束总结
/dedupe 命令在 LobeHub 中通常不被人手执行,而是由 claude-dedupe-issues.yml 在每次新 Issue 打开时自动以 /dedupe <仓库>/issues/<编号> 形式触发。需要了解的关键使用约束包括:
- 搜索一律走
ghCLI(issue list/search/api),不使用网页抓取; - 交互工具面最小化:不启用其他 MCP 服务器、不做文件编辑;
- 结果上限 3 个,宁缺毋滥,过滤后为空则不回复;
- 执行顺序不可乱:先资格检查,再单 Agent 摘要,再 5 路并行搜索,再独立过滤,最后回帖;
- 回帖格式是机器契约,任何改版都必须同步更新 auto-close-duplicates.ts 的匹配逻辑;
- 该命令只负责"找出并公告重复项",关闭动作由定时脚本在 3 天静默期后完成,且作者可通过评论或 👎 否决。
如果你正在维护自己的开源仓库并苦于 Issue 重复率过高,.claude/commands/dedupe.md 与其配套的 Workflow、预检脚本、自动关闭脚本共同构成了一套可直接借鉴的参考范式:用最少权限的 Slash Command 封装模型行为,用确定性脚本把守 AI 入口,用标准化的评论格式衔接下游自动化,再用带否决机制的静默关闭兜底——AI 参与维护流程的每个环节都被设计成可解释、可拦截、可撤回的。
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 StartedRust0627
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