Kilo 仓库重复 PR 检测 Agent 实战解析:从 Agent 定义、GitHub PR 搜索工具到自动化工作流
2026/9/12 11:45:39 网站建设 项目流程

Kilo 仓库重复 PR 检测 Agent 实战解析:从 Agent 定义、GitHub PR 搜索工具到自动化工作流

【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode

在开源仓库中,多个贡献者针对同一 issue 或功能提交多个 PR 是常见现象。重复 PR 会浪费维护者审阅精力、增加合并冲突风险,也让贡献者感到困惑。本文以 Kilo 仓库中真实存在的.opencode/agent/duplicate-pr.md为核心,完整拆解这个"重复 PR 检测 Agent"的设计:从 frontmatter 配置、系统提示词的行为契约,到其依赖的github-pr-search工具实现、script/duplicate-pr.ts调用脚本,再到曾经承载它的 GitHub Actions 工作流,让读者理解如何用声明式 Agent 定义 + 工具白名单 + 精确输出契约的方式,把一个重复 PR 检测任务做成可复用、可自动化的工程能力。

一、总体架构:一个 Agent 的四层构成

duplicate-pr检测 Agent 在 Kilo 仓库中由四个可独立查看的部件组成,构成一条完整的"定义 → 工具 → 调用 → 自动化"链路:

仓库路径作用
Agent 定义.opencode/agent/duplicate-pr.md声明模型、工具白名单与行为规范(核心主体)
工具实现.opencode/tool/github-pr-search.ts通过 GitHub Search API 检索开放 PR
调用脚本script/duplicate-pr.ts@kilocode/sdk启动 Kilo 会话并指定duplicate-prAgent
自动化工作流.github/workflows/disabled/pr-management.yml.disabled在 PR 打开事件上触发检测并回帖(当前处于 disabled 状态)

其中 Agent 定义是整个方案的"灵魂":它不写死任何检索逻辑,而是通过约束模型该做什么、能调用什么工具、以什么格式输出,把重复 PR 检测变成可被 LLM 稳定执行的确定性流程。

二、Agent 定义逐段拆解:.opencode/agent/duplicate-pr.md

2.1 frontmatter:声明式配置的关键字段

该文件开头的 YAML frontmatter 定义了 Agent 的运行元数据:

--- mode: primary hidden: true model: kilo/anthropic/claude-haiku-4.5 color: "#E67E22" tools: "*": false "github-pr-search": true ---

各字段含义与设计意图如下:

  • mode: primary:声明这是主 Agent(primary agent),与同目录下的 triage Agent(.opencode/agent/triage.md)一致,表示它可被会话直接调用执行主任务,而非仅作为辅助参考。mode取值为 primary/secondary/all 中的一种,从源码结构看(两个 agent 均声明 primary),primary 用于承载完整任务执行。
  • hidden: true:该 Agent 不在用户可见的 Agent 列表中展示,属于后台自动化用途,避免干扰用户手动选择 Agent 的界面。
  • model: kilo/anthropic/claude-haiku-4.5:指定运行模型为 Claude Haiku 4.5。选择轻量快速模型与任务性质匹配——重复 PR 检测是高频、低复杂度、对响应速度敏感的任务,不需要重型推理模型。
  • color: "#E67E22":为 Agent 分配的界面标识色(橙色),用于在 UI 中区分不同 Agent。
  • tools白名单:这是最关键的权限设计。"*": false表示默认禁用所有工具,然后显式启用"github-pr-search": true。这种"默认全禁、按需放行"的最小权限原则,能显著降低模型误用工具的风险,也是可审计、可预期的 Agent 设计范式。

2.2 系统提示词:把任务规范拆成可执行契约

frontmatter 之后是系统提示词正文,它把"检测重复 PR"这一目标分解为五条明确指令,逐条解读如下:

(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 的身份(重复 PR 检测员)与触发时机(PR 被打开时),职责边界被限定在"搜索可能与当前 PR 重复或相关的开放 PR"。

(2)工具使用指令

Use the github-pr-search tool to search for PRs that might be addressing the same issue or feature.

强制指定唯一的工具入口,避免模型自行编造检索方式或调用未授权的其他工具。

(3)自排除保护

IMPORTANT: 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 与自己匹配"的护栏。调用方必须在输入中携带CURRENT_PR_NUMBER: NNNN格式的行(NNNN 为当前 PR 编号),Agent 必须排除自身。该字段由工作流脚本通过 GitHub 事件负载注入,详见下文第四节。

(4)多轮检索策略

Search using keywords from the PR title and description. Try multiple searches with different relevant terms.

要求从标题与描述中抽取关键词,并用多个不同的相关词进行多次搜索。这背后的原因很实际:GitHub 的 issue/PR 搜索是全文关键词匹配,不同贡献者描述同一功能时用词可能完全不同(例如 "dark mode" 与 "theme toggle"),单次搜索极易漏检,多关键词轮询才能覆盖语义变体。

(5)输出契约

If you find potential duplicates: List them with their titles and URLs, briefly explain why they might be related. If no duplicates are found, say so clearly. BUT ONLY SAY "No duplicate PRs found" (don't say anything else if no dups). Keep your response concise and actionable.

输出格式被严格规定为两种:

  • 找到候选时:列出 PR 标题 + URL,并简述相关性理由;
  • 未找到时:只能输出恰好一句No duplicate PRs found,不得输出任何其他内容。

这个精确的输出契约(sentinel 值)是后续自动化工作流判断分支的关键——工作流正是通过比对返回值是否等于No duplicate PRs found来决定是否回帖(见第四节),这体现了"Agent 输出即程序输入"的工程化思想。

三、工具实现:github-pr-search 的底层原理

Agent 被授权使用的唯一工具定义在 .opencode/tool/github-pr-search.ts,它基于@kilocode/plugintool()API 声明。

3.1 参数 Schema

args: { query: tool.schema.string().describe("Search query for PR titles and descriptions"), limit: tool.schema.number().describe("Maximum number of results to return").default(10), offset: tool.schema.number().describe("Number of results to skip for pagination").default(0), },

三个参数:必填的query搜索关键词,可选的limit(默认 10)与offset(默认 0,用于翻页)。这为模型提供了分页能力,当候选较多时可继续翻页获取更多结果。

3.2 GitHub API 调用

工具内部通过 REST API 的 Search Issues 端点检索 PR:

const page = Math.floor(args.offset / args.limit) + 1 const searchQuery = encodeURIComponent(`${args.query} repo:${owner}/${repo} type:pr state:open`) const result = await githubFetch( `/search/issues?q=${searchQuery}&per_page=${args.limit}&page=${page}&sort=updated&order=desc`, )

关键细节:

  • 限定检索范围:查询串强制附加repo:anomalyco/opencode(当前仓库的上游 fork 命名,仓库被 fork 到 Kilo 后沿用)与type:pr state:open,即只搜索指定仓库中状态为 open 的 PR,从源头排除了已合并/已关闭的 PR 和 issue;
  • 排序:按updated倒序,让最近有活动的 PR 排在最前;
  • 鉴权:请求头携带Authorization: Bearer ${process.env.GITHUB_TOKEN}(详见 .opencode/tool/github-pr-search.ts 的githubFetch封装),环境变量GITHUB_TOKEN由调用方注入;
  • 错误处理:非 2xx 响应抛出GitHub API error: <status> <statusText>,便于上层脚本捕获。

3.3 输出格式化

const formatted = prs.map((pr) => `${pr.title}\n${pr.html_url}`).join("\n\n") return `Found ${result.total_count} PRs (showing ${prs.length}):\n\n${formatted}`

结果以 "标题 + URL" 成对呈现,且开头给出total_count(命中总数)与本次实际展示数量,方便模型判断是否需要翻页继续检索。无命中时分别返回No PRs found matching "..."No other PRs found matching "..."两种提示。工具描述中声明其返回 LLM-friendly 结果,包含 PR 编号、标题、作者、状态(open/closed/merged)、标签与描述片段。

值得一提的是,.opencode/opencode.jsonc全局配置中tools.github-pr-searchtools.github-triage均被置为false,说明该工具默认不向普通会话开放,仅在duplicate-prAgent 的白名单中被显式启用——与 frontmatter 的"*": false策略形成"全局关闭、局部放行"的双重保险。

四、调用脚本:script/duplicate-pr.ts

script/duplicate-pr.ts 是用 Bun 编写的 CLI 脚本,负责把 PR 信息交给duplicate-prAgent 并取回结论。

4.1 用法与参数

Usage: bun script/duplicate-pr.ts [options] <message> Options: -f, --file <path> File to attach to the prompt -h, --help Show this help message Examples: bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details"
  • <message>:直接作为文本消息发送给 Agent;
  • -f, --file:将指定文件(如pr_info.txt)作为附件附加到提示词中。脚本内部用Bun.file()校验文件存在性,并通过pathToFileURL把本地路径转为file://URL,以{ type: "file", url, filename, mime: "text/plain" }的形式拼入消息 parts——这正是工作流注入CURRENT_PR_NUMBER与 PR 标题/描述的载体。

4.2 会话执行流程

const opencode = await createKilo({ port: 0 }) const session = await opencode.client.session.create() const result = await opencode.client.session.prompt({ path: { id: session.data!.id }, body: { agent: "duplicate-pr", parts }, signal: AbortSignal.timeout(120_000), }).then((x) => x.data?.parts?.find((y) => y.type === "text")?.text ?? "") console.log(result.trim())

流程要点:

  • createKilo({ port: 0 })以随机端口启动本地 Kilo 服务器(port: 0表示由系统分配空闲端口),通过@kilocode/sdk的 client 与其通信;
  • 创建会话后在body中显式指定agent: "duplicate-pr",即把会话路由到本文讨论的 Agent 定义;
  • 请求设置AbortSignal.timeout(120_000)的 120 秒超时上限,防止模型无限挂起;
  • 结果从响应的 text parts 中提取并trim()后打印到 stdout——该 stdout 输出正是工作流断言分支的依据;
  • 执行完毕后opencode.server.close()关闭服务器,避免残留进程。

五、自动化落地:pr-management 工作流(disabled)

.github/workflows/disabled/pr-management.yml.disabled展示了该 Agent 曾经承载的端到端自动化(文件后缀.disabled表示当前在 CI 中停用,仅作历史参考)。

5.1 触发与前置过滤

工作流由pull_request_target事件在types: [opened]时触发,即仅在新 PR 打开的瞬间运行。第一步是团队成员过滤:若 PR 作者是opencode-agent[bot]或位于.github/TEAM_MEMBERS名单中,则跳过整个检测流程——内部成员与机器人提交的 PR 不再重复检查,节省算力并避免打扰。

5.2 构造 Agent 输入

run: | { 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.txt

这里通过gh pr view抓取当前 PR 的编号、标题与描述,写入pr_info.txt,其中CURRENT_PR_NUMBER: $PR_NUMBER这一行正是 Agent 提示词中"不得把自己判为重复"的护栏输入。

5.3 执行检测并回帖

COMMENT=$(bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details and search for duplicates") if [ "$COMMENT" != "No duplicate PRs found" ]; then gh pr comment "$PR_NUMBER" --body "_The following comment was made by an LLM, it may be inaccurate:_ $COMMENT" fi

这段逻辑完整印证了第二节的输出契约设计:

  1. pr_info.txt作为附件、检索指令作为消息,运行duplicate-pr.ts
  2. 以字符串精确比对No duplicate PRs found作为分支条件——只有未找到重复时才静默跳过;
  3. 找到候选时,通过gh pr comment回帖,帖子以斜体免责声明 "The following comment was made by an LLM, it may be inaccurate:" 开头,明确标注内容由 LLM 生成、可能存在不准确之处。这种"AI 建议 + 人类兜底"的姿态是机器人参与维护流程的稳妥实践。

5.4 同批次的关联维护

该工作流还包含一个add-contributor-labeljob:当作者关联为CONTRIBUTOR时自动为 PR 打上contributor标签,属于 PR 打开时刻配套的贡献者激励流程。

六、设计模式总结:与 triage Agent 的对照

仓库中与duplicate-pr同构的还有 issue 分流 Agent .opencode/agent/triage.md,两者对照可提炼出本仓库 Agent 化的通用模式:

维度duplicate-prtriage
模型kilo/anthropic/claude-haiku-4.5kilo/openai/gpt-5-nano
工具白名单github-pr-searchgithub-triage
输入契约CURRENT_PR_NUMBER行 + PR 标题/描述待分流 issue 内容 + 固定团队枚举
输出契约精确哨兵串No duplicate PRs found或候选列表按 7 个团队枚举赋值负责人,禁止添加标签

共同点非常明显:mode: primary+hidden: true+ 单一领域工具白名单 + 严格的输入/输出契约。这类 Agent 本质上是"以 LLM 为执行引擎、以文档为规范、以工具为唯一动作入口"的声明式自动化单元,既保留了 LLM 对自然语言的语义理解能力(多关键词检索、相关性判断),又通过工具白名单与输出契约把不确定性压缩到可编程消费的范围内。

七、部署与运行前提

若要在本地复现该 Agent 的检测能力,需要满足:

  1. 安装依赖:项目根目录执行bun install(Bun 是项目脚本的运行时);
  2. 准备环境变量GITHUB_TOKEN(GitHub API 鉴权,用于github-pr-search工具)与OPENCODE_API_KEY(Kilo API 密钥,用于启动 LLM 会话);
  3. 运行脚本:先构造包含CURRENT_PR_NUMBER与 PR 信息的pr_info.txt,再执行:
    bun script/duplicate-pr.ts -f pr_info.txt "Check the attached file for PR details and search for duplicates"
  4. 理解输出:stdout 若为No duplicate PRs found表示未发现重复;否则为候选 PR 的"标题 + URL"列表及相关性说明。

注意:仓库中该自动化工作流当前以.disabled后缀停用(可在 .github/workflows/disabled/pr-management.yml.disabled 查看其完整历史实现),工具实现中检索的repo:anomalyco/opencode为上游 fork 命名空间,若在其它仓库使用需相应调整。此外,仓库另有.github/workflows/kilo-auto-close.yml负责定期关闭长期无活动的 PR 与 issue,与重复 PR 检测形成"新 PR 打开时查重、长期无活动时回收"的互补治理闭环。

结语

.opencode/agent/duplicate-pr.md这一份不足 30 行的 Agent 定义出发,可以完整看到 Kilo 仓库如何把一个"PR 查重"的琐碎维护任务工程化:用 frontmatter 声明模型与工具权限,用系统提示词固化检索策略与输出契约,用github-pr-search工具隔离 GitHub API 细节,用script/duplicate-pr.ts统一调用入口,再用工作流脚本把"Agent 输出"变成"回帖决策"。这套"声明式 Agent + 最小工具白名单 + 哨兵输出契约"的模式,值得任何希望在 CI 流水线中引入 LLM 自动化判断的团队借鉴。

【免费下载链接】kilocodeKilo is the all-in-one agentic engineering platform. Build, ship, and iterate faster with the most popular open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/ki/kilocode

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

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

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

立即咨询