“集成入口与自动化触发”负责把来自不同系统的请求转换成 Open-Inspect 内部可以执行的后台任务:
这些入口来源不同,但最终都会落到同一类动作:
可以把这一层理解为“外部世界到后台编码任务的适配层”。
该模块本身通常不负责执行代码修改,也不直接操作 git 工作区。
它主要做几件事:
| 职责 | 说明 |
|---|---|
| 接收事件 | HTTP API、Webhook、Slack/GitHub/Linear 事件、Cron 调度 |
| 校验来源 | 签名校验、token 校验、登录态校验 |
| 归一化任务 | 把不同来源事件转换为统一的任务创建参数 |
| 创建 Session | 调用后台任务创建逻辑,为 Agent 分配仓库和环境 |
| 回写状态 | 将“已开始”“需要确认”“已完成”“失败”等消息写回来源系统 |
| 自动触发 | 定时任务、Sentry 告警、Webhook 规则自动创建 Agent 任务 |
textWeb / Slack / GitHub / Linear / Webhook / Cron / Sentry │ ▼ 入口 API / Webhook Handler │ ▼ 事件校验、权限检查、幂等判断 │ ▼ 构造统一任务参数 │ ▼ 创建 Background Agent Session │ ▼ Sandbox 启动,Agent 开始编码 │ ▼ 状态回写到 WebSocket / Slack / GitHub / Linear / Webhook
无论入口来自哪里,最终都需要拼出一份任务请求。通常包含这些字段:
tstype AgentTaskRequest = {
source: "web" | "slack" | "github" | "linear" | "webhook" | "cron" | "sentry";
/**
* 谁触发了任务。
* Web 登录用户、Slack 用户、GitHub 用户、Linear 用户,或系统任务。
*/
actor: {
id: string;
name?: string;
email?: string;
sourceUserId?: string;
};
/**
* 目标代码仓库。
*/
repository: {
owner: string;
name: string;
url?: string;
defaultBranch?: string;
};
/**
* Agent 要完成的事情。
*/
prompt: string;
/**
* 可选:指定起始分支、基础分支、工作目录等。
*/
branch?: string;
baseBranch?: string;
workingDirectory?: string;
/**
* 来源系统里的上下文,用于回写结果。
*/
external?: {
threadId?: string;
channelId?: string;
issueId?: string;
pullRequestId?: string;
commentId?: string;
alertId?: string;
webhookDeliveryId?: string;
};
/**
* 幂等键,避免同一个 webhook 重复创建任务。
*/
dedupeKey?: string;
};入口层最重要的工作,就是把外部事件转换成类似这样的结构。
Web UI 是最直接的入口。
常见场景:
典型请求形态:
httpPOST /api/sessions
Content-Type: application/json
{
"repository": "acme/api-server",
"prompt": "修复登录接口在 token 过期时返回 500 的问题,并补充测试",
"baseBranch": "main"
}Web 入口一般会带上当前登录用户信息。对于 GitHub 登录用户,创建 PR 时可以使用用户 OAuth token,从而让 PR 归属到真实用户。
Slack 集成通常适合下面的场景:
一个 Slack 事件大致会被转换成:
tsconst task: AgentTaskRequest = {
source: "slack",
actor: {
id: "slack:U123456",
name: "alice",
sourceUserId: "U123456"
},
repository: {
owner: "acme",
name: "web"
},
prompt: "根据这个线程的讨论,给结账页增加优惠码校验逻辑,并补充测试",
external: {
channelId: "C123456",
threadId: "1720000000.000100"
},
dedupeKey: "slack:C123456:1720000000.000100"
};Slack 入口的关键点是“线程上下文”。Agent 启动后,状态最好回写到同一个 thread,避免打断频道主线。
GitHub 入口通常用于:
常见触发方式:
text@open-inspect fix this @open-inspect add tests for this endpoint @open-inspect investigate why this CI check failed
GitHub webhook 事件会带有:
转换后的任务示例:
tsconst task: AgentTaskRequest = {
source: "github",
actor: {
id: "github:alice",
name: "alice",
sourceUserId: "alice"
},
repository: {
owner: "acme",
name: "api-server"
},
prompt: "PR 评论要求:修复这个测试失败,并保持现有接口兼容",
baseBranch: "main",
external: {
pullRequestId: "123",
commentId: "987654321"
},
dedupeKey: "github:delivery:8f2a..."
};GitHub 入口需要特别注意两类 token:
| token | 用途 |
|---|---|
| GitHub App installation token | clone、fetch、push 等 git 操作 |
| 用户 GitHub OAuth token | 创建 PR、读取用户信息、确保 PR 归属到真实用户 |
Open-Inspect 的安全模型是单租户模型。GitHub App 安装到哪些仓库,系统就能访问哪些仓库。
Linear 入口适合将 Issue 变成代码任务。
常见流程:
agent转换示例:
tsconst task: AgentTaskRequest = {
source: "linear",
actor: {
id: "linear:user_123",
name: "Alice"
},
repository: {
owner: "acme",
name: "frontend"
},
prompt: [
"Linear Issue: Checkout page should support promo codes",
"",
"Description:",
"用户需要在结账页输入优惠码。需要调用 /api/promotions/validate。",
"",
"Acceptance Criteria:",
"- 显示折扣金额",
"- 无效优惠码展示错误",
"- 增加单元测试"
].join("\n"),
external: {
issueId: "LIN-123"
},
dedupeKey: "linear:LIN-123:updated"
};Linear 入口的重点是把产品语义转成工程语义:仓库、目录、验收标准、测试要求都要尽量明确。
Webhook 是最通用的入口。适合接入:
推荐的 webhook payload 结构:
json{
"source": "internal-alerting",
"repository": {
"owner": "acme",
"name": "api-server"
},
"prompt": "最近 30 分钟 /v1/login 的 500 错误升高,请检查可能原因并提交修复 PR",
"baseBranch": "main",
"metadata": {
"severity": "high",
"service": "auth-api",
"runbook": "https://wiki.example.com/runbooks/auth-api"
}
}Webhook 入口一般需要做三件事:
Cron 适合周期性任务:
一个定时任务配置可以表达为:
tstype CronAgentJob = {
name: string;
schedule: string;
repository: string;
prompt: string;
enabled: boolean;
};示例:
json{
"name": "weekly-flaky-test-cleanup",
"schedule": "0 9 * * 1",
"repository": "acme/web",
"prompt": "检查最近一周 CI 中最常失败的测试,修复明显的 flaky test,并提交 PR。",
"enabled": true
}Cron 入口的 actor 通常不是某个用户,而是系统用户:
tsactor: {
id: "system:cron",
name: "Open-Inspect Cron"
}Sentry 入口适合把线上异常转成修复任务。
常见流程:
转换示例:
tsconst task: AgentTaskRequest = {
source: "sentry",
actor: {
id: "system:sentry",
name: "Sentry"
},
repository: {
owner: "acme",
name: "api-server"
},
prompt: [
"Sentry 线上错误告警,需要定位并修复。",
"",
"Error: TypeError: Cannot read properties of undefined (reading 'id')",
"Project: api-server",
"Environment: production",
"Release: api-server@1.42.0",
"URL: https://sentry.io/organizations/acme/issues/123456",
"",
"请:",
"1. 根据堆栈定位代码",
"2. 修复空值处理",
"3. 增加回归测试",
"4. 提交 PR"
].join("\n"),
external: {
alertId: "sentry:123456"
},
dedupeKey: "sentry:issue:123456"
};Sentry 入口不要只把错误标题交给 Agent。最好带上 stack trace、release、环境、最近部署版本和复现路径。
下面示例使用 curl 发送一个 webhook 请求。
先准备环境变量:
bashexport OPEN_INSPECT_URL="https://open-inspect.example.com"
export OPEN_INSPECT_WEBHOOK_TOKEN="replace-with-your-webhook-token"发送任务:
bashcurl -X POST "$OPEN_INSPECT_URL/api/webhooks/agent" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPEN_INSPECT_WEBHOOK_TOKEN" \
-d '{
"source": "manual-webhook",
"repository": {
"owner": "acme",
"name": "api-server"
},
"baseBranch": "main",
"prompt": "修复 /health 接口在数据库不可用时返回 500 的问题。期望返回 503,并补充测试。",
"metadata": {
"priority": "medium",
"requestedBy": "platform-team"
}
}'预期结果:
json{
"sessionId": "sess_...",
"status": "queued"
}如果你的部署中 webhook 路径不同,把
/api/webhooks/agent替换成实际路由即可。关键是 payload 需要包含仓库和 prompt。
创建文件 trigger-agent.mjs:
jsconst OPEN_INSPECT_URL = process.env.OPEN_INSPECT_URL;
const OPEN_INSPECT_WEBHOOK_TOKEN = process.env.OPEN_INSPECT_WEBHOOK_TOKEN;
if (!OPEN_INSPECT_URL || !OPEN_INSPECT_WEBHOOK_TOKEN) {
console.error("Missing OPEN_INSPECT_URL or OPEN_INSPECT_WEBHOOK_TOKEN");
process.exit(1);
}
const payload = {
source: "node-script",
repository: {
owner: "acme",
name: "frontend"
},
baseBranch: "main",
prompt: [
"给登录页增加邮箱格式校验。",
"",
"要求:",
"- 输入非法邮箱时展示错误信息",
"- 不调用后端登录接口",
"- 补充组件测试"
].join("\n"),
metadata: {
team: "growth"
}
};
const res = await fetch(`${OPEN_INSPECT_URL}/api/webhooks/agent`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${OPEN_INSPECT_WEBHOOK_TOKEN}`
},
body: JSON.stringify(payload)
});
const text = await res.text();
if (!res.ok) {
console.error(`Request failed: ${res.status}`);
console.error(text);
process.exit(1);
}
console.log(text);运行:
bashexport OPEN_INSPECT_URL="https://open-inspect.example.com"
export OPEN_INSPECT_WEBHOOK_TOKEN="replace-with-your-webhook-token"
node trigger-agent.mjs这个例子每天早上 9 点触发一次依赖检查任务。
创建 .github/workflows/open-inspect-cron.yml:
yamlname: Open Inspect Cron
on:
schedule:
- cron: "0 9 * * *"
workflow_dispatch:
jobs:
trigger-agent:
runs-on: ubuntu-latest
steps:
- name: Trigger Open-Inspect agent
run: |
curl -X POST "${OPEN_INSPECT_URL}/api/webhooks/agent" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${OPEN_INSPECT_WEBHOOK_TOKEN}" \
-d '{
"source": "github-actions-cron",
"repository": {
"owner": "acme",
"name": "api-server"
},
"baseBranch": "main",
"prompt": "检查依赖是否有可安全升级的 patch/minor 版本。若测试通过,请提交 PR。不要升级 major 版本。"
}'
env:
OPEN_INSPECT_URL: ${{ secrets.OPEN_INSPECT_URL }}
OPEN_INSPECT_WEBHOOK_TOKEN: ${{ secrets.OPEN_INSPECT_WEBHOOK_TOKEN }}这个方案适合不想在 Open-Inspect 内部配置 Cron,而是直接复用 GitHub Actions 调度能力的团队。
创建文件 sentry-alert.json:
json{
"source": "sentry",
"repository": {
"owner": "acme",
"name": "api-server"
},
"baseBranch": "main",
"prompt": "Sentry 发现生产环境错误:TypeError: Cannot read properties of undefined (reading 'id')。请检查用户对象为空时的处理逻辑,修复后补充回归测试。",
"metadata": {
"sentryIssue": "https://sentry.io/organizations/acme/issues/123456",
"environment": "production",
"release": "api-server@1.42.0",
"level": "error"
}
}发送:
bashcurl -X POST "$OPEN_INSPECT_URL/api/webhooks/agent" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $OPEN_INSPECT_WEBHOOK_TOKEN" \
--data @sentry-alert.jsonSlack 侧的配置依赖部署方式,但建议把用户消息整理成明确任务:
text@open-inspect repo=acme/frontend branch=main 请修复结账页优惠码校验问题: 现象: - 输入过期优惠码时页面没有错误提示 - Network 中 /api/promotions/validate 返回 400 期望: - 页面显示“优惠码已过期” - 不允许继续支付 - 补充组件测试
入口层解析后应尽量生成这样的 prompt:
text请在 acme/frontend 仓库 main 分支上修复结账页优惠码校验问题。 上下文: - 输入过期优惠码时页面没有错误提示 - /api/promotions/validate 返回 400 验收标准: - 页面显示“优惠码已过期” - 不允许继续支付 - 补充组件测试
好的 Slack 触发内容应包含:
在 Issue 或 PR 中评论:
text@open-inspect 请修复这个问题: - 当前登录 token 过期时接口返回 500 - 期望返回 401,并让前端跳转登录页 - 请补充后端单测和前端集成测试
GitHub 入口可以把 Issue/PR 标题、正文、当前评论、相关 diff 一起拼进 prompt。这样 Agent 不需要只依赖一条短评论判断上下文。
Open-Inspect 的 README 已明确说明:当前架构面向单租户部署。
关键点:
因此建议:
Webhook 是最容易被误用的入口。
不要暴露一个无需认证的“创建 Agent 任务”接口。至少需要:
否则任何人都可以触发 Agent 访问你的代码仓库。
Slack、GitHub、Linear、Sentry 的 webhook 都可能重试。
如果没有幂等处理,可能出现:
建议使用来源系统的 delivery id 或资源 id 构造幂等键:
textgithub:<delivery-id> slack:<channel-id>:<thread-ts> linear:<issue-id>:<event-id> sentry:<issue-id> cron:<job-name>:<scheduled-time>
Cron 和 Sentry 这类自动触发入口不应该无限制地创建任务。
建议加上:
例如,Sentry 只处理:
textenvironment = production level in [error, fatal] events_last_30m > 20 repository is mapped
外部系统里的原始文本通常不够直接。
入口层应该尽量补充:
不推荐:
text修一下这个 bug
推荐:
text修复 /v1/login 在 token 过期时返回 500 的问题。 当前行为: - token 过期时后端抛出 UnhandledTokenError - API 返回 500 期望行为: - 返回 401 - 响应体包含 code=TOKEN_EXPIRED - 前端收到后跳转登录页 要求: - 增加后端单测 - 增加前端集成测试 - 不修改公开 API 字段名
Agent 执行过程可能会产生大量日志。不要把所有日志都回写到 Slack/GitHub/Linear。
推荐回写这些状态:
不推荐把完整 token、环境变量、sandbox 日志直接回写到外部系统。
Open-Inspect 中:
因此排查“为什么 PR 不是我创建的”时,要看用户是否通过 GitHub 登录,以及是否携带可用 OAuth token。
Sentry 告警适合触发“调查和修复明显问题”,但不适合无条件自动合并。
建议流程:
特别是涉及支付、权限、数据迁移、认证逻辑时,不建议自动合并。
定时任务如果提示词长期不更新,可能逐渐失效。例如仓库结构变化后,旧 prompt 仍然要求修改不存在的路径。
建议:
如果 Slack、GitHub、Cron、Sentry 同时对同一仓库创建任务,可能出现:
建议按仓库限制并发数,例如:
textacme/api-server: max 3 active sessions acme/frontend: max 5 active sessions
对于 Sentry 和 Cron 这类自动入口,可以设置更低优先级。