用 Copilot SDK 构建 GitHub Issue 智能分类工具

教程GitHub2026年3月24日8 分钟阅读
用 Copilot SDK 构建 GitHub Issue 智能分类工具
维护开源项目时,面对几十个未处理的 GitHub Issue,分类整理耗时耗力。作者用 Copilot SDK 和 React Native 开发了 IssueCrush,通过 AI 自动生成 Issue 摘要,让分类决策快了好几倍。

GitHub Copilot SDK 能让你在自己的应用里集成 Copilot Chat 背后的 AI 能力。为了看看实际效果,我开发了一个叫 IssueCrush 的 Issue 分类应用。下面是我的实践经验和入门指南。

如果你维护过开源项目,或者在活跃的团队仓库工作过,一定熟悉这种感觉:打开 GitHub,通知徽标显示着 47 个未处理 Issue。有些是 bug,有些是功能请求,有些本该是讨论的问题,还有些是三年前 Issue 的重复。

分类 Issue 的心理负担是真实存在的。每个 Issue 都需要切换上下文:读标题、扫描述、查标签、想优先级、做决定。乘以几十个跨仓库的 Issue,你的大脑很快就会一团浆糊。

我想让这个过程更快。借助 GitHub Copilot SDK,我找到了方法。

IssueCrush:滑动分类,AI 摘要

IssueCrush 把你的 GitHub Issue 显示成可滑动的卡片。左滑关闭,右滑保留。点击“获取 AI 摘要”,Copilot 会读取 Issue 内容,告诉你它是什么以及该怎么处理。维护者无需阅读冗长的描述,就能获得即时、可操作的上下文,从而更快地做出分类决策。下面是我集成 GitHub Copilot SDK 的具体方法。

Screenshot of a developer workspace showing a VS Code editor open to the IssueCrush project, with a package.json file visible and a terminal running a server that logs AI-generated issue summaries using the GitHub Copilot SDK. On the right, an iPhone simulator displays the IssueCrush mobile app, listing a GitHub issue with labels, a progress indicator, and a ‘Get AI Summary’ button, along with swipe-style approve and reject buttons at the bottom.

架构挑战

第一个技术决策是确定在哪里运行 Copilot SDK。React Native 应用不能直接使用 Node.js 包,而 Copilot SDK 需要 Node.js 运行时。SDK 内部管理一个本地的 Copilot CLI 进程,并通过 JSON-RPC 与之通信。由于这种对 CLI 二进制文件和 Node 环境的依赖,集成必须在服务端运行,而不是直接在 React Native 应用中。这意味着服务器必须安装 Copilot CLI 并确保其在系统 PATH 中可用。

我最终确定了服务端集成模式:

Architecture diagram showing a React Native and web client communicating over HTTPS with a Node.js server. The server runs the GitHub Copilot SDK, which manages a local Copilot CLI process via JSON-RPC. The CLI connects to the GitHub Copilot service, while the client separately interacts with GitHub OAuth and the GitHub REST API for issue data.

这种设置的优势:

  • 所有客户端共享单个 SDK 实例,无需为每个移动客户端启动新连接。服务器为每个请求管理一个实例。开销更小,认证握手更少,清理更简单。
  • Copilot 认证密钥保存在服务端,确保凭证安全。你的 API token 永远不会触及客户端。它们待在服务器上,而不是在可能被反编译的 React Native 包里。
  • AI 不可用时的优雅降级,即使 Copilot 服务宕机或超时,你仍然可以分类 Issue。应用会回退到基础摘要。AI 让分类更快,但它不应该成为单点故障。
  • 请求日志记录用于调试和监控,因为每个提示和响应都经过你的服务器。你可以跟踪延迟、捕获失败、调试提示问题,而无需在移动客户端上添加检测工具。

在构建类似应用之前,你需要:

  1. 在服务器上安装 Copilot CLI
  2. 拥有 GitHub Copilot 订阅,或使用 BYOK 配置 并提供自己的 API 密钥。
  3. 对 Copilot CLI 进行认证。在服务器上运行 copilot auth,或设置 COPILOT_GITHUB_TOKEN 环境变量。

如何实现 Copilot SDK 集成

Copilot SDK 使用基于会话的模型。你启动一个客户端(这会生成 CLI 进程),创建会话,发送消息,然后清理。

code
const { CopilotClient, approveAll } = await import('@github/copilot-sdk');
 
let client = null; 
let session = null; 
 
try { 
   // 1. Initialize the client (spawns Copilot CLI in server mode) 
   client = new CopilotClient(); 
   await client.start(); 
 
  // 2. Create a session with your preferred model 
  session = await client.createSession({
  model: 'gpt-4.1',
  onPermissionRequest: approveAll,
});
 
  // 3. Send your prompt and wait for response 
   const response = await session.sendAndWait({ prompt }); 
 
  // 4. Extract the content 
   if (response && response.data && response.data.content) { 
     const summary = response.data.content; 
     // Use the summary... 
   } 
 
} finally { 
   // 5. Always clean up 
   if (session) await session.disconnect().catch(() => {}); 
   if (client) await client.stop().catch(() => {}); 
} 

关键 SDK 模式

1. 生命周期管理

SDK 遵循严格的生命周期:start() → createSession() → sendAndWait() → disconnect() → stop()

我花了两个小时调试内存问题才意识到自己漏掉了 disconnect() 调用。用 try/finally 包裹每个会话交互。清理调用上的 .catch(() => {}) 可以防止清理错误掩盖原始错误。

2. 分类任务的提示工程

提示结构为模型提供了足够的上下文来完成工作。我提供关于 Issue 的结构化信息,而不是直接转储原始文本:

code
const prompt = `You are analyzing a GitHub issue to help a developer quickly understand it and decide how to handle it. 
 
Issue Details: 
- Title: ${issue.title} 
- Number: #${issue.number} 
- Repository: ${issue.repository?.full_name || 'Unknown'} 
- State: ${issue.state} 
- Labels: ${issue.labels?.length ? issue.labels.map(l => l.name).join(', ') : 'None'} 
- Created: ${issue.created_at} 
- Author: ${issue.user?.login || 'Unknown'} 
 
Issue Body: 
${issue.body || 'No description provided.'} 
 
Provide a concise 2-3 sentence summary that: 
1. Explains what the issue is about 
2. Identifies the key problem or request 
3. Suggests a recommended action (e.g., "needs investigation", "ready to implement", "assign to backend team", "close as duplicate") 
 
Keep it clear, actionable, and helpful for quick triage. No markdown formatting.`; 

标签和作者上下文比你想象的更重要。来自首次贡献者的 Issue 与来自核心维护者的 Issue 需要不同的处理方式,AI 会利用这些信息来调整其摘要。

3. 响应处理

sendAndWait() 方法在会话空闲时返回助手的响应。在访问嵌套属性之前,务必验证响应链是否存在:

code
const response = await session.sendAndWait({ prompt }, 30000); // 30 second timeout 
 
let summary; 
if (response && response.data && response.data.content) { 
   summary = response.data.content; 
} else { 
   throw new Error('No content received from Copilot'); 
} 

sendAndWait() 的第二个参数是以毫秒为单位的超时时间。对于复杂 Issue 要设置得足够高,但也要足够低,以免用户盯着旋转加载器。我见过太多“undefined is not an object”错误,所以永远不要跳过响应链的空值检查。

客户端服务层

在 React Native 端,我将 API 调用包装在一个处理初始化和错误状态的服务类中:

code
// src/lib/copilotService.ts 
import type { GitHubIssue } from '../api/github'; 
import { getToken } from './tokenStorage'; 
export interface SummaryResult { 
   summary: string; 
   fallback?: boolean; 
   requiresCopilot?: boolean; 
} 
 
export class CopilotService { 
   private backendUrl = process.env.EXPO_PUBLIC_API_URL || 'http://localhost:3000'; 
 
  async initialize(): Promise<{ copilotMode: string }> { 
     try { 
       const response = await fetch(`${this.backendUrl}/health`); 
       const data = await response.json(); 
 
      console.log('Backend health check:', data); 
       return { copilotMode: data.copilotMode || 'unknown' }; 
     } catch (error) { 
       console.error('Failed to connect to backend:', error); 
       throw new Error('Backend server not available'); 
     } 
   } 
 
  async summarizeIssue(issue: GitHubIssue): Promise<SummaryResult> { 
     try { 
       const token = await getToken(); 
 
      if (!token) { 
         throw new Error('No GitHub token available'); 
       } 
 
      const response = await fetch(`${this.backendUrl}/api/ai-summary`, { 
         method: 'POST', 
         headers: { 
           'Content-Type': 'application/json', 
         }, 
         body: JSON.stringify({ issue, token }), 
       }); 
 
      const data = await response.json(); 
 
      if (!response.ok) { 
         if (response.status === 403 && data.requiresCopilot) { 
           return { 
             summary: data.message || 'AI summaries require a GitHub Copilot subscription.', 
             requiresCopilot: true, 
           }; 
         } 
         throw new Error(data.error || 'Failed to generate summary'); 
       } 
 
      return { 
         summary: data.summary || 'Unable to generate summary', 
         fallback: data.fallback || false, 
       }; 
     } catch (error) { 
       console.error('Copilot summarization error:', error); 
       throw error; 
     } 
   } 
} 
 
export const copilotService = new CopilotService(); 

React Native 集成

UI 是简单的 React 状态管理。点击按钮,调用服务,缓存结果:

code
const [loadingAiSummary, setLoadingAiSummary] = useState(false); 
 
const handleGetAiSummary = async () => { 
   const issue = issues[currentIndex]; 
   if (!issue || issue.aiSummary) return; 
 
  setLoadingAiSummary(true); 
   try { 
     const result = await copilotService.summarizeIssue(issue); 
     setIssues(prevIssues => 
       prevIssues.map((item, index) => 
         index === currentIndex ? { ...item, aiSummary: result.summary } : item 
       ) 
     ); 
   } catch (error) { 
     console.error('AI Summary error:', error); 
   } finally { 
     setLoadingAiSummary(false); 
   } 
}; 

一旦 Issue 对象上有了摘要,卡片就会将按钮替换为摘要文本。如果用户滑走再回来,缓存的版本会立即渲染。无需第二次 API 调用。

优雅降级

AI 服务可能会失败。网络问题、速率限制和服务中断时有发生。服务器处理两种故障模式:订阅错误返回 403 状态码,以便客户端显示明确消息;其他所有情况则回退到基于 Issue 元数据构建的摘要。

code
} catch (error) { 
   // Clean up on error 
   try { 
     if (session) await session.disconnect().catch(() => {}); 
     if (client) await client.stop().catch(() => {}); 
   } catch (cleanupError) { 
     // Ignore cleanup errors 
   } 
 
  const errorMessage = error.message.toLowerCase(); 
 
  // Copilot subscription errors get a clear 403 
   if (errorMessage.includes('unauthorized') || 
       errorMessage.includes('forbidden') || 
       errorMessage.includes('copilot') || 
       errorMessage.includes('subscription')) { 
     return res.status(403).json({ 
       error: 'Copilot access required', 
       message: 'AI summaries require a GitHub Copilot subscription.', 
       requiresCopilot: true 
     }); 
   } 
 
  // Everything else falls back to a metadata-based summary 
   const fallbackSummary = generateFallbackSummary(issue); 
   res.json({ summary: fallbackSummary, fallback: true }); 
} 

回退摘要基于现有信息构建:

code
function generateFallbackSummary(issue) { 
   const parts = [issue.title]; 
 
  if (issue.labels?.length) { 
     parts.push(`\nLabels: ${issue.labels.map(l => l.name).join(', ')}`); 
   } 
 
  if (issue.body) { 
     const firstSentence = issue.body.split(/[.!?]\s/)[0]; 
     if (firstSentence && firstSentence.length < 200) { 
       parts.push(`\n\n${firstSentence}.`); 
     } 
   } 
 
  parts.push('\n\nReview the full issue details to determine next steps.'); 
   return parts.join(''); 
} 

其他值得注意的模式

服务器暴露一个 /health 端点,用于指示 AI 可用性。客户端在启动时检查它,如果后端不支持,则完全隐藏摘要按钮。没有损坏的按钮。

摘要是按需生成的,而不是预先生成的。这可以降低 API 成本,并避免用户在未阅读 Issue 就滑过时浪费调用。

SDK 使用 await import('@github/copilot-sdk') 加载,而不是顶层的 require。这让服务器即使 SDK 有问题也能启动,从而使部署和调试更顺畅。

依赖项

code
{ 
   "dependencies": { 
     "@github/copilot-sdk": "^0.1.14", 
     "express": "^5.2.1" 
   } 
} 

SDK 通过 JSON-RPC 与 Copilot CLI 进程通信。你需要安装 Copilot CLI 并确保其在你的 PATH 中可用。查看 SDK 的包要求 以了解最低 Node.js 版本。

构建过程中的收获

服务端是正确的选择。SDK 需要 Copilot CLI 二进制文件,而你不可能把它安装在手机上。在服务器上运行它可以将 AI 逻辑集中在一处,简化移动客户端,并确保凭证永远不会离开后端。

提示结构比提示长度更重要。为模型提供标题、标签和作者等有组织的元数据,比将整个 Issue 正文作为原始文本转储能产生更好的摘要。给模型一些可用的信息,它会给你有用的反馈。

永远要有备用方案。AI 服务会宕机。速率限制会发生。从第一天起就为优雅降级而设计。即使 AI 部分离线,你的用户也应该能够分类 Issue。

清理你的会话。SDK 需要显式清理:先 disconnect()stop()。我有一次跳过了 disconnect() 调用,花了两个小时追踪内存泄漏。每次都使用 try/finally

缓存结果。一旦有了摘要,就把它存储在 Issue 对象上。如果用户滑走再回来,缓存的版本会立即渲染。无需第二次 API 调用,不浪费钱,没有额外延迟。

AI 可以让维护工作可持续。分类是那种消耗精力却又看不见的任务。没人为此感谢你,而且它会迅速堆积。如果你能把处理 50 个 Issue 的时间减半,那省下的时间就可以用于代码审查、指导,或者只是不再害怕通知徽标。Copilot SDK 是一个工具,但更大的想法更重要:看看维护工作中哪些部分让你精疲力尽,问问 AI 是否能先帮你处理一遍。

亲自尝试

@github/copilot-sdk 为构建智能开发者工具开辟了真正的可能性。结合 React Native 的跨平台覆盖范围,你可以将 AI 驱动的工作流带到移动端,感觉原生且快速。

如果你正在构建类似的东西,可以从我概述的服务端模式开始。这是实现工作集成的最简单路径,并且可以随着你的应用扩展。源代码可在 GitHub 上获取:AndreaGriffiths11/IssueCrush

开始使用 Copilot SDK 看看你还能构建什么。入门指南 会引导你完成第一个集成,大约只需五行代码。有反馈或想法吗?加入 SDK 讨论区 的对话。


标签

作者

Andrea Griffiths

Andrea 是 GitHub 的高级开发者布道师,在开发者工具领域拥有超过十年的经验。她将技术深度与使命相结合,旨在让先进技术更易于使用。在从军队服役和建筑管理转型到软件开发之后,她为连接复杂的工程概念与实际实施带来了独特的视角。她与她的威尔士伴侣、两个儿子和两只狗住在佛罗里达州,在那里她继续通过 GitHub 的全球计划推动创新并支持开源。在线关注她 @acolombiadev。

本文编译自 Building AI-powered GitHub issue triage with the Copilot SDK,版权归原作者所有。

觉得有用?分享给更多人

获取每周 AI 工具精选

工具推荐、实战教程和生态洞察,每周更新。

相关文章

Starlette 1.0 发布,最大的变化是引入了基于异步上下文管理器的 lifespan 机制。作者利用 Claude 的 Skill 功能,让 AI 克隆代码库并学习新版本特性,然后成功生成了一个包含项目、任务、评论和标签的完整任务管理应用。

教程Simon Willison·3月22日·5 分钟

本文介绍了如何利用 NVIDIA 开源工具链,通过合成数据生成、困难负样本挖掘和多跳问题训练,快速微调向量嵌入模型以适配特定领域。该方法显著提升了 RAG 系统的检索性能,并提供了完整的代码和数据集。

教程Hugging Face·3月20日·8 分钟

评论