用 OpenCode Agent 实现 GitHub 重复 PR 自动检测:duplicate-pr Agent 实战指南
【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork
导读
在协作仓库中,多个贡献者常常针对同一个 issue 或 feature 提交相互重叠的 Pull Request,人工排查既耗时又容易遗漏。本篇文章以 .opencode/agents/duplicate-pr.md 为核心骨架,讲解 openwork 仓库中如何用 OpenCode 的自定义 Agent 实现"PR 打开即自动查重"的流水线:包括 Agent 的 YAML frontmatter 声明、检索策略与输出契约、以及配套的 GitHub Actions 工作流如何把检测结果自动回帖到 PR 评论区。读完本文,你将掌握在任意 OpenCode 项目中声明一个"仅暴露单一工具、专职做一件事"的 primary Agent,并用opencode run --agent把它接入 CI 事件流的完整方法。
一、这个 Agent 要解决的问题:PR 去重
重复 PR 是开源协作中的典型噪音:两个贡献者可能基于同一个 issue 各自实现了同一功能,或者在相近时间段提交了修复同一缺陷的 PR。如果不加识别,维护者会把时间浪费在重复审查、重复跑 CI、重复合并甚至冲突回滚上。
仓库为此在 .opencode/agents/ 目录下维护了一组面向 GitHub 工作流的 primary Agent,与duplicate-pr同级的还有 triage.md(issue 分类打标签)、changelog.md(变更日志)、executor.md(执行编码任务)、orchestrator.md(编排规划)等,形成了一个"CI 事件 → OpenCode Agent → GitHub 交互"的自动化闭环。duplicate-pr专职负责其中一环:在 PR 刚被打开时,基于其标题与描述检索是否已有目标相同或高度相关的开放 PR。
二、Agent 声明:YAML frontmatter 详解
duplicate-pr.md 的文件主体是给 LLM 的角色指令,而文件顶部的 YAML frontmatter 则定义了 Agent 的运行模式、可见工具与归属模型:
--- mode: primary hidden: true model: openai/gpt-5.6-luna color: "#E67E22" tools: "*": false "github-pr-search": true ---各字段的作用与注意点:
| 字段 | 值 | 含义与影响 |
|---|---|---|
mode | primary | Agent 可以作为独立执行入口被直接调用(配合opencode run --agent),这与仅能由其他 Agent 通过 Task 工具调用的 subagent 不同。同目录下 orchestrator.md 中提到的executor属于被编排方,而duplicate-pr是独立执行方 |
hidden | true | 该 Agent 不会出现在交互式会话的 Agent 列表中,属于"仅供脚本/CI 调用"的隐藏入口,避免污染用户的日常选择 |
model | openai/gpt-5.6-luna | 固定使用该模型执行任务,保证输出质量与成本可控 |
color | "#E67E22" | 在 OpenCode UI 中展示的 Agent 标识色(橙色),纯展示用途 |
tools."*" | false | 默认禁用全部工具,这是最小权限原则的体现:Agent 只能调用下方显式开启的工具,无法读写文件、执行任意命令 |
tools."github-pr-search" | true | 唯一放行的工具:在 GitHub 上按关键字检索 PR |
这种"默认全关、显式放行一个工具"的写法是该 Agent 安全性的核心设计:重复检测任务只需要搜索能力,不需要代码编辑或 Shell 执行权限,因此把攻击面和误操作面都收敛到最小。
提示:
github-pr-search这类工具由 OpenCode 的插件机制注册(仓库的 .opencode/opencode.json 中声明了插件与 MCP 服务配置),如果你的环境未安装对应插件,工具将不可用,Agent 会无法完成任务。
三、角色指令与检索策略
frontmatter 之下的正文是 Agent 的核心行为规范,原文的关键指令可以拆解为四条:
1. 明确职责边界。原文声明:"You are a duplicate PR detection agent. When a PR is opened, your job is to search for potentially duplicate or related open PRs."——Agent 只做"检测",不做审查、不评价代码质量、不修改任何东西。
2. 使用专用工具而非通用搜索。原文要求使用github-pr-search工具检索"可能针对同一 issue 或 feature 的 PR"。之所以指定专用工具,是因为它面向 GitHub PR 数据做了语义化封装(按标题/描述相关性召回、过滤掉已关闭 PR 等),比让 LLM 直接调用ghCLI 更稳定、更可控。
3. 自排除当前 PR。原文有一条强约束:"The input will contain a lineCURRENT_PR_NUMBER: NNNN. This is the current PR number, you should not mark that the current PR as a duplicate of itself."——输入中会携带当前 PR 编号,Agent绝不允许把自己标记为自己的重复项。这条看似简单的防呆规则,实际是防止 Agent 在检索结果中看到自己、产生"自我重复"误报的关键。
4. 多轮关键词检索。原文要求:"Search using keywords from the PR title and description. Try multiple searches with different relevant terms."——不能只搜标题里的一个词,而要拆出标题与描述中的功能关键词、模块名、技术名词,尝试多组不同组合,以提高召回率。例如一个标题为 "Fix: add retry to den-db transient read" 的 PR,应分别用den-db、transient read、retry等词多次检索。
四、输出契约:让机器可消费的结果格式
该 Agent 对输出有严格的格式约定,这是它能够被 CI 脚本直接捕获、回帖的关键:
发现潜在重复时:列出候选 PR 的标题与 URL,并简要说明它们为什么可能相关。格式类似:
Potential duplicates found: - #4521 "Fix: retry on den-db transient read failures" (https://github.com/.../pull/4521) — likely related: same den-db transient read path - #4498 "feat: add read retry for den-db" — likely related: same feature scope未发现重复时:只输出
No duplicate PRs found,一个多余的字都不要。原文特别强调:"BUT ONLY SAY 'No duplicate PRs found' (don't say anything else if no dups)"。整体要求:保持回复简洁、可执行("Keep your response concise and actionable")。
这种"有结果给清单、无结果给固定句"的约定,让下游脚本可以无歧义地判断检测结果,无需解析自由文本。类似的"确定性输出"风格也体现在仓库其他 Agent 中,例如 triage.md 对打标签条件做了逐条布尔化限定("Only add if…"、"Do not add for…"),都是为了把 LLM 输出变成可判定的机器行为。
五、CI 集成:从 Agent 到 PR 评论的完整链路
Agent 定义本身只是"大脑",真正让它跑起来的是 .github/workflows/opencode-agents.yml 中的duplicate-prsjob。这条工作流把 GitHub 事件、OpenCode CLI 与 PR 评论串成一条自动化链路,核心步骤如下:
1. 准备环境。检出仓库(fetch-depth: 1),安装 Node 24,然后从 constants.json 中读取仓库锁定的 opencode 版本并安装:
- name: Install opencode run: | version="$(node -e "const fs=require('fs'); const parsed=JSON.parse(fs.readFileSync('constants.json','utf8')); process.stdout.write(String(parsed.opencodeVersion||'').trim().replace(/^v/,''));")" curl -fsSL https://opencode.ai/install | bash -s -- --version "$version" --no-modify-path从constants.json读版本号而非写死,保证 CI 中运行的 opencode 与仓库开发环境锁定的版本一致。
2. 组装 Prompt。用gh pr view拉取 PR 的标题与描述,连同CURRENT_PR_NUMBER: $PR_NUMBER一起写入pr_info.txt。这一步精确对应了 Agent 指令中"输入包含CURRENT_PR_NUMBER: NNNN一行"的约定:
{ echo "Check for duplicate PRs related to this new PR:" echo "" echo "CURRENT_PR_NUMBER: $PR_NUMBER" echo "" echo "Title: $(gh pr view \"$PR_NUMBER\" --json title --jq .title)" echo "" echo "Description:" gh pr view "$PR_NUMBER" --json body --jq .body } > pr_info.txt3. 调用 Agent 并回帖。使用opencode run --agent duplicate-pr以 headless 方式执行 Agent,把输出包装成评论正文(并显式声明"以下评论由 LLM 生成,可能不准确"),再用gh pr comment回帖到 PR 上:
opencode run --agent duplicate-pr "$(cat pr_info.txt)" > /tmp/comment_output.txt { echo "_The following comment was made by an LLM, it may be inaccurate:_" echo "" cat /tmp/comment_output.txt } > /tmp/comment_body.txt gh pr comment "$PR_NUMBER" --body-file /tmp/comment_body.txt工作流权限声明为pull-requests: write(以及 issue 分支的issues: write),这是回帖所需的最小权限。两个分支共用同一个工作流,通过workflow_dispatch的target输入(issue/pull_request)和number输入选择执行triage-issue还是duplicate-prsjob。
六、为什么这样设计:从仓库实践看可靠性保障
结合仓库中的其他 Agent 与工作流,可以归纳出这套重复 PR 检测方案背后的几条工程原则,它们正是值得在自建 Agent 时复用的经验:
- 单工具最小权限:
tools."*": false+ 只开github-pr-search,让一个 Agent 只拥有一把钥匙。对比 triage.md 只开github-triage,可见该仓库对所有 GitHub 交互类 Agent 都采用了同一安全范式。 - 确定性输出协议:固定输出格式(候选清单或
No duplicate PRs found)保证脚本可解析、结果可断言,这也是 headless/CI 场景下使用 LLM Agent 的前提。 - 输入自排除:通过
CURRENT_PR_NUMBER显式阻止自我误报,属于"提示词层面的防呆"。 - 人类兜底:CI 在 LLM 输出前加上 "The following comment was made by an LLM, it may be inaccurate:" 的免责声明,提醒维护者把检测结果当线索而非结论。这与仓库对 fork PR 的人工审查清单 review-a-contributor-pr/SKILL.md(逐项核查 DCO、
ee/CLA、Warden 检查等)形成互补:自动化负责"先筛一遍",人类负责"最终把关"。
七、本地复现与扩展思路
如果你想在本地亲自验证这条链路(不需要触发 CI),可以按以下步骤复现:
# 1. 确保 opencode 已安装且配置了 OPENAI_API_KEY 与 GITHUB_TOKEN # 2. 手工构造与 CI 相同格式的输入 cat > /tmp/pr_info.txt <<'EOF' Check for duplicate PRs related to this new PR: CURRENT_PR_NUMBER: 9999 Title: Fix den-db transient read retry Description: Adds retry logic for transient read failures in den-db. EOF # 3. 以 headless 方式运行 duplicate-pr Agent opencode run --agent duplicate-pr "$(cat /tmp/pr_info.txt)"在此基础上可以按需扩展:
- 把
workflow_dispatch换成pull_request/pull_request_target触发器,实现"PR 打开即自动查重"(注意 fork PR 的 token 权限与密钥暴露问题,仓库在 review-a-contributor-pr/SKILL.md 中对此有专门讨论); - 在 Agent 指令中增加"优先报告 open(未合并/未关闭)PR"的过滤规则,进一步提高信噪比;
- 让 Agent 在发现重复时额外建议"可以关联到哪个 issue",帮助维护者一键关闭重复 PR。
结语
duplicate-pr是一个教科书级的最小化 Agent 示例:用 10 余行 frontmatter + 一段行为指令,加上一条 30 余行的 CI job,就为仓库建立了一道自动化的 PR 去重防线。它的设计思路——单工具权限、确定性输出、输入自排除、人类兜底——可以直接迁移到任何基于 OpenCode 的协作工作流中。如果你想深入了解该仓库 Agent 体系的完整面貌,可以继续阅读 .opencode/agents/ 下的其他 Agent 定义、.opencode/skills/ 下的技能清单,以及 .github/workflows/opencode-agents.yml 的完整工作流实现。
【免费下载链接】openworkThe open-source alternative to Claude Cowork (powered by opencode)项目地址: https://gitcode.com/GitHub_Trending/ope/openwork
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考