GitHub 集成实战:Issue、PR、Checks 与 Agent 任务流
GitHub 不只是代码托管平台,更是 Agent 工作流的调度中心。通过 GitHub App + Webhook + Check Run API,可以把 Issue 自动转成 Agent 任务,在 PR 中自动触发代码审查,把 CI 结果写回 Check Run,让 Agent 和开发者在同一个界面中协作。本文给出完整的集成架构和可运行的代码。
一、集成架构
┌─────────────────────────────────────────────────────────────────┐
│ GitHub 平台 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │
│ │ Issues │ │ PR / Diff│ │Check Runs│ │ Discussions │ │
│ └────┬─────┘ └────┬─────┘ └─────▲────┘ └──────▲───────┘ │
│ │ │ │ │ │
└───────┼──────────────┼───────────────┼────────────────┼─────────┘
│ │ │ │
Webhook Webhook API 回写 API 回写
│ │ │ │
┌───────▼──────────────▼───────────────────────────────┼─────────┐
│ Agent 工作台 │ │
│ ┌──────────────────────────────────────────────────┐│ │
│ │ Webhook 接收层 ││ │
│ │ · 签名校验 ││ │
│ │ · 事件路由 ││ │
│ │ · 幂等去重 ││ │
│ └──────────────────┬───────────────────────────────┘│ │
│ │ │ │
│ ┌──────────────────▼───────────────────────────────┐│ │
│ │ 事件处理器 ││ │
│ │ ┌─────────┐ ┌──────────┐ ┌──────────────────┐ ││ │
│ │ │Issue 处理│ │PR 处理 │ │Check Run 回写 │ ││ │
│ │ │→任务创建│ │→Agent审查│ │→状态/摘要/日志 │ ││ │
│ │ └─────────┘ └──────────┘ └──────────────────┘ ││ │
│ └──────────────────────────────────────────────────┘│ │
│ │ │
│ ┌──────────────────────────────────────────────────┐│ │
│ │ Agent 执行引擎 ││ │
│ │ 上下文包生成 → Agent 修复/审查 → 质量门禁 ││ │
│ └──────────────────────────────────────────────────┘│ │
└──────────────────────────────────────────────────────┘ │二、GitHub App 配置
2.1 权限范围(最小权限原则)
# github-app-manifest.yaml
# https://github.com/settings/apps → New GitHub App
github_app:
name: "agent-workbench"
description: "AI Agent 工作台 — 自动处理 Issue、审查 PR、回写检查结果"
# 权限范围(只给必要的最小权限)
permissions:
# 读取权限
issues: "read" # 读取 Issue 创建 Agent 任务
pull_requests: "read" # 读取 PR diff 做审查
contents: "read" # 读取仓库代码
metadata: "read" # 读取仓库元信息
# 写入权限
checks: "write" # 回写 Check Run 结果
issues_comments: "write" # 在 Issue 中评论进度
pull_request_reviews: "write" # 提交 PR Review
statuses: "write" # 回写 Commit Status
# 订阅的事件
events:
- issues # Issue 创建/编辑/关闭
- issue_comment # Issue 评论(Agent 命令)
- pull_request # PR 创建/更新/关闭
- push # 代码推送(触发检查)
# Webhook
webhook_url: "https://agent.example.com/webhook/github"
webhook_secret: "${WEBHOOK_SECRET}" # 用于签名校验
# OAuth(可选,用于以用户身份操作)
oauth:
callback_url: "https://agent.example.com/auth/github/callback"2.2 Webhook 签名校验
# app/webhook/verifier.py
import hmac
import hashlib
import time
from fastapi import Request, HTTPException
WEBHOOK_SECRET = os.environ["GITHUB_WEBHOOK_SECRET"]
MAX_SKEW_SECONDS = 300 # 时间戳偏差容忍
async def verify_github_webhook(request: Request) -> dict:
"""
校验 GitHub Webhook 签名。
防止伪造请求触发 Agent 任务。
"""
# 1. 获取签名头
signature = request.headers.get("X-Hub-Signature-256")
if not signature:
raise HTTPException(401, "Missing signature")
# 2. 读取请求体
body = await request.body()
# 3. 计算 HMAC
expected = hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
# 4. 比对(时间安全比对,防止时序攻击)
if not hmac.compare_digest(f"sha256={expected}", signature):
raise HTTPException(401, "Invalid signature")
# 5. 检查时间戳(防止重放攻击)
timestamp = request.headers.get("X-Hub-Signature-Timestamp")
if timestamp:
skew = abs(time.time() - int(timestamp))
if skew > MAX_SKEW_SECONDS:
raise HTTPException(401, "Timestamp too old")
# 6. 解析 payload
payload = json.loads(body)
# 7. 幂等检查(GitHub 可能重发 Webhook)
delivery_id = request.headers.get("X-GitHub-Delivery")
if is_duplicate(delivery_id):
return {"status": "duplicate", "data": payload}
return payload三、核心事件处理
3.1 Issue → Agent 任务
# app/handlers/issue_handler.py
"""
当 Issue 被打上 'agent-fix' 标签时,自动创建 Agent 任务。
"""
from typing import Optional
# Issue 标签 → Agent 任务类型映射
LABEL_TO_TASK = {
"agent-fix": "bugfix",
"agent-review": "code_review",
"agent-test": "test_generation",
"agent-docs": "documentation",
}
async def handle_issue_event(payload: dict):
action = payload["action"]
issue = payload["issue"]
repo = payload["repository"]["full_name"]
if action == "opened" or action == "labeled":
# 检查是否有 Agent 标签
labels = [l["name"] for l in issue["labels"]]
agent_labels = [l for l in labels if l in LABEL_TO_TASK]
if not agent_labels:
return # 不是 Agent 任务
task_type = LABEL_TO_TASK[agent_labels[0]]
# 创建 Agent 任务
task = await create_agent_task(
task_type=task_type,
source={
"platform": "github",
"repo": repo,
"issue_number": issue["number"],
"title": issue["title"],
"body": issue["body"],
"author": issue["user"]["login"],
"labels": labels,
},
priority=infer_priority(labels, issue["title"]),
)
# 在 Issue 中评论确认
await github_client.create_comment(
repo=repo,
issue_number=issue["number"],
body=f"🤖 Agent 任务已创建({task_type}),任务 ID: `{task.id}`\n\n"
f"预计处理时间: {task.estimated_time}\n"
f"进度将在此 Issue 中更新。"
)
def infer_priority(labels: list, title: str) -> str:
"""从标签和标题推断任务优先级"""
if "P0" in labels or "🔥 critical" in labels:
return "P0"
if "P1" in labels or "bug" in labels:
return "P1"
if any(kw in title.lower() for kw in ["紧急", "urgent", "hotfix"]):
return "P0"
return "P2"3.2 PR → Agent 审查
# app/handlers/pr_handler.py
"""
PR 创建或更新时,自动触发 Agent 代码审查。
"""
async def handle_pull_request_event(payload: dict):
action = payload["action"]
pr = payload["pull_request"]
repo = payload["repository"]["full_name"]
if action not in ["opened", "synchronize"]:
return # 只处理 PR 创建和更新
# 1. 创建 Check Run(状态:进行中)
check_run = await github_client.create_check_run(
repo=repo,
name="Agent Code Review",
head_sha=pr["head"]["sha"],
status="in_progress",
started_at=datetime.now().isoformat(),
)
try:
# 2. 获取 PR diff
diff = await github_client.get_pr_diff(
repo=repo,
pr_number=pr["number"],
)
# 3. 生成上下文包
context = await build_review_context(
repo=repo,
diff=diff,
pr_description=pr["body"],
changed_files=[f["filename"] for f in pr["files"]],
)
# 4. Agent 审查
review_result = await agent_review(
context=context,
review_prompt=REVIEW_PROM,
)
# 5. 提交 Review
if review_result.comments:
await github_client.create_review(
repo=repo,
pr_number=pr["number"],
commit_id=pr["head"]["sha"],
event="COMMENT" if review_result.approved else "REQUEST_CHANGES",
body=review_result.summary,
comments=[
{
"path": c["file"],
"line": c["line"],
"body": f"**[{c['severity']}]** {c['message']}",
}
for c in review_result.comments
],
)
# 6. 更新 Check Run 结果
await github_client.update_check_run(
repo=repo,
check_run_id=check_run["id"],
status="completed",
conclusion="success" if review_result.approved else "action_required",
output={
"title": f"Agent Review: {'✅ Approved' if review_result.approved else '⚠️ Changes Requested'}",
"summary": review_result.summary,
"text": review_result.detail_report,
},
)
except Exception as e:
# 失败也要更新 Check Run
await github_client.update_check_run(
repo=repo,
check_run_id=check_run["id"],
status="completed",
conclusion="failure",
output={
"title": "Agent Review Failed",
"summary": f"审查过程出错: {str(e)}",
},
)
raise3.3 完整 Webhook 路由
# app/webhook/router.py
from fastapi import FastAPI, Request
app = FastAPI()
EVENT_HANDLERS = {
"issues": handle_issue_event,
"pull_request": handle_pull_request_event,
"push": handle_push_event,
"check_run": handle_check_run_event,
}
@app.post("/webhook/github")
async def github_webhook(request: Request):
# 1. 签名校验
payload = await verify_github_webhook(request)
if payload.get("status") == "duplicate":
return {"status": "ok", "message": "duplicate, skipped"}
# 2. 事件路由
event_type = request.headers.get("X-GitHub-Event")
handler = EVENT_HANDLERS.get(event_type)
if handler:
# 异步处理,不阻塞 Webhook 响应
asyncio.create_task(handler(payload))
# 3. 快速返回(GitHub 要求 10 秒内响应)
return {"status": "ok"}四、GitHub Actions 集成
# .github/workflows/agent-trigger.yml
name: Agent 任务触发
on:
issues:
types: [opened, labeled]
issue_comment:
types: [created]
pull_request:
types: [opened, synchronize]
jobs:
trigger-agent:
runs-on: ubuntu-latest
# 只在特定标签或评论触发
if: |
(github.event_name == 'issues' && contains(join(github.event.issue.labels.*.name), 'agent-')) ||
(github.event_name == 'issue_comment' && startsWith(github.event.comment.body, '/agent')) ||
(github.event_name == 'pull_request')
steps:
- uses: actions/checkout@v4
- name: 解析 Agent 命令
id: parse
run: |
if [ "${{ github.event_name }}" == "issue_comment" ]; then
COMMENT="${{ github.event.comment.body }}"
TASK_TYPE=$(echo "$COMMENT" | head -1 | sed 's|/agent \([a-z]*\).*|\1|')
echo "task_type=$TASK_TYPE" >> $GITHUB_OUTPUT
echo "issue_number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
elif [ "${{ github.event_name }}" == "issues" ]; then
echo "task_type=bugfix" >> $GITHUB_OUTPUT
echo "issue_number=${{ github.event.issue.number }}" >> $GITHUB_OUTPUT
fi
- name: 触发 Agent 任务
env:
AGENT_API_KEY: ${{ secrets.AGENT_API_KEY }}
AGENT_ENDPOINT: ${{ vars.AGENT_ENDPOINT }}
run: |
curl -X POST "$AGENT_ENDPOINT/tasks" \
-H "Authorization: Bearer $AGENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": "github_actions",
"repo": "${{ github.repository }}",
"task_type": "${{ steps.parse.outputs.task_type }}",
"issue_number": ${{ steps.parse.outputs.issue_number }},
"check_run_id": "${{ github.run_id }}"
}'
- name: 更新 Issue 评论
if: github.event_name == 'issues' || github.event_name == 'issue_comment'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: ${{ steps.parse.outputs.issue_number }},
body: '🤖 Agent 任务已触发,处理中... 进度将通过 Check Run 更新。'
});五、真实经验与踩坑
5.1 Webhook 超时不能阻塞
场景:Webhook handler 直接调用 Agent 做代码审查,审查耗时 2 分钟。
问题:GitHub 要求 Webhook 10 秒内响应,超时后会重试,导致同一个事件被处理多次,Agent 重复审查同一个 PR。
解决方案:Webhook handler 只做"接收 + 入队",快速返回 200。实际处理通过消息队列异步执行。同时用 X-GitHub-Delivery 头做幂等去重,同一个 delivery ID 只处理一次。
5.2 Check Run 状态要及时更新
场景:Agent 审查 PR 过程中出错,但 Check Run 一直停在 "in_progress" 状态。
问题:PR 页面显示"等待检查",开发者不知道出了问题,也无法合并。
解决方案:在 try/except 中确保异常时也更新 Check Run 状态为 failure。同时设置超时机制——如果 Check Run 超过 15 分钟还没完成,自动标记为 timeout 并通知维护者。
5.3 安装权限不要过宽
场景:GitHub App 一开始申请了 contents: write 权限,让 Agent 可以直接推送代码。
问题:安全审计时被标记为"权限过宽"——Agent 不应该有直接推送 main 分支的权限。
解决方案:收窄权限——Agent 只通过 PR 提交代码修改,不需要 contents: write。对于需要自动推送的场景(如自动格式化),限制在特定分支(agent/*)并使用 Branch Protection Rules 控制合并。
六、参数说明表
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
webhook_secret |
string | 必填 | Webhook 签名密钥 |
app_id |
string | 必填 | GitHub App ID |
private_key |
string | 必填 | GitHub App 私钥(PEM 格式) |
permissions |
object | 见 2.1 | 权限范围 |
events |
list | 见 2.1 | 订阅的事件类型 |
check_run_timeout |
int | 900 |
Check Run 超时时间(秒) |
retry_max |
int | 3 |
Webhook 重试最大次数 |
rate_limit_buffer |
int | 100 |
预留的 API 调用配额 |
allowed_repos |
list | ["*"] |
允许触发 Agent 的仓库列表 |
auto_create_pr |
bool | false |
Agent 修复后是否自动创建 PR |
七、落地检查清单
- GitHub App 权限遵循最小权限原则
- Webhook 签名校验已启用(SHA-256)
- Webhook handler 10 秒内返回响应
- 事件处理有幂等去重(delivery ID)
- Check Run 状态在异常和超时时也能正确更新
- PR Review 评论内容有严重级别标注
- Agent 推送代码限制在
agent/*分支 - Webhook 失败的告警机制已配置
- API 调用有速率限制保护
- 审计日志记录了每次 Agent 操作
八、系列导航
上一篇:案例 004:用 Agent 做数据库迁移前的风险评估 下一篇:Jira / Linear 集成实战:从需求卡片到 Agent 执行计划