Chapter 17 · Capstone 1: GitHub Auto-Triage Agent
Chapter goals: Build a complete bug auto-triage agent from scratch, using the official README's Triage example as a blueprint — connecting skills, tools, and sandbox into one end-to-end pipeline.
17.1 Goal and Blueprint
The agent we build autonomously completes end-to-end triage upon receiving a bug report:
bug report → reproduce → locate root cause → judge if expected → attempt fix → open issue reportThe official example skeleton (starting point for this chapter):
// agents/triage.ts — official README original example
'use agent';
import { useModel, useSandbox, useSkill, useTool } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import triage from '../skills/triage/SKILL.md';
import verify from '../skills/verify/SKILL.md';
import { openIssue, searchCode } from '../tools/github.ts';
export function Triage() {
useModel('anthropic/claude-sonnet-4-6');
useSandbox(local());
useSkill(triage);
useSkill(verify);
useTool(openIssue);
useTool(searchCode);
return `Triage a bug report end-to-end: reproduce the bug,
diagnose the root cause, verify whether the behavior is
intentional, and attempt a fix.`;
}17.2 Step One: The Tool Layer (tools/github.ts)
Tools are typed external actions. Start with two minimal tools:
// tools/github.ts
'use flue-tool';
import { defineTool } from '@flue/runtime';
// Search code in a repo (read-only, safe)
export const searchCode = defineTool({
name: 'search_code',
description: 'Search code in a GitHub repo; returns matching file paths',
inputSchema: {
type: 'object',
properties: {
repo: { type: 'string', description: 'Repo in owner/name form' },
query: { type: 'string', description: 'Search keyword' },
},
required: ['repo', 'query'],
},
async execute({ repo, query }) {
const url = `https://api.github.com/search/code?q=${encodeURIComponent(query)}+repo:${repo}`;
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
Accept: 'application/vnd.github+json',
},
});
const data = (await res.json()) as { items?: Array<{ path: string }> };
return (data.items ?? []).slice(0, 10).map((i) => i.path); // Paths only; save tokens
},
});// tools/github.ts (continued) — open issue (has side effects; idempotent)
let reported = new Set<string>(); // In production, swap for persistent dedup (Chapter 12)
export const openIssue = defineTool({
name: 'open_issue',
description: 'Create a triage-conclusion issue in the repo',
durable: true, // Declare durable: won't re-create on recovery
inputSchema: {
type: 'object',
properties: {
title: { type: 'string' },
body: { type: 'string', description: 'Includes reproduction steps, root cause, fix suggestion' },
labels: { type: 'array', items: { type: 'string' } },
},
required: ['title', 'body'],
},
async execute(args) {
const key = args.title;
if (reported.has(key)) return { duplicated: true }; // Idempotency guard
reported.add(key);
// ...POST to GitHub Issues API
},
});17.3 Step Two: Skills (skills/triage/SKILL.md, skills/verify/SKILL.md)
Two skills encapsulate the domain methodology:
<!-- skills/triage/SKILL.md -->
---
name: triage
description: End-to-end bug triage methodology. Use when a bug report arrives.
---
Triage流程:
1. 复现:确认 bug 可稳定复现
2. 定位:grep 相关代码,找到根因
3. 判断:是否为预期行为(查阅文档/历史 issue)
4. 修复:尝试最小修复 PR
5. 汇报:用 open_issue 工具创建分诊 issue<!-- skills/verify/SKILL.md -->
---
name: verify
description: Verify whether a behavior is expected. Use after triage定位.
---
Verify流程:
1. 查文档确认预期行为
2. 查历史 issue 看是否已知
3. 输出:is_expected: true/false + 依据17.4 Step Three: Assembly and Testing
// agents/triage.ts — final assembly
'use agent';
import { useModel, useSandbox, useSkill, useTool } from '@flue/runtime';
import { local } from '@flue/runtime/node';
import triage from '../skills/triage/SKILL.md';
import verify from '../skills/verify/SKILL.md';
import { openIssue, searchCode } from '../tools/github.ts';
export function Triage() {
useModel('anthropic/claude-sonnet-4-6');
useSandbox(local());
useSkill(triage);
useSkill(verify);
useTool(openIssue);
useTool(searchCode);
return `Triage a bug report end-to-end: reproduce the bug,
diagnose the root cause, verify whether the behavior is
intentional, and attempt a fix.`;
}Test with:
npx flue run src/agents/triage.ts --message "Bug: /search returns 500 on null query" --verbose17.5 Chapter Summary
- End-to-end triage: reproduce → locate → verify → fix → report;
- Tools: searchCode (read-only) + openIssue (durable, idempotent);
- Skills: triage methodology + verify methodology;
- Sandbox: local() for file/shell access;
- Assembly: one agent function, six hooks, one return value.
🧪 随堂测验
点击你认为正确的选项。答错时会展示正确答案与原因解析。
1. What does durable: true on openIssue mean?
2. Why use a Set for deduplication in the demo openIssue tool?
3. What is the role of the verify skill in this capstone?
4. Why is local() sandbox used for the triage agent?
🛠️ Hands-On Exercises
- Build the full triage agent: implement searchCode and openIssue tools, write triage and verify skills, assemble in one agent function.
- Test with a real GitHub issue; verify the issue is created and the dedup guard prevents duplicates on retry.
- Add a fourth tool (e.g., getIssue) and verify the agent can read existing issues before deciding to create a new one.
Capstone 1 complete. Next: Capstone 2: Slack On-Call Assistant.