forked from Gitlink/gitlink-cli
11 KiB
11 KiB
gitlink-issue-triage — 分析算法详解
本文档面向 AI Agent 开发者 和 想理解决策细节的工程师。 普通使用者只需阅读 SKILL.md 即可。
1. 输入数据
1.1 Issue 字段(来自 issue +view --format json)
{
"number": 142, // project_issues_index,网页 URL 中的序号
"subject": "登录页面点击登录无反应",
"description": "线上环境用户反馈...",
"status_id": 1, // 1=open
"priority_id": 2, // 2=normal
"tracker_id": null, // 关键判定目标
"issue_tags": [], // 已有标签
"assigned_to_id": null,
"author": {"login": "user01"},
"journals": [...] // 评论历史
}
1.2 仓库元数据
| API | 用途 |
|---|---|
GET /v1/:owner/:repo/issue_tags.json |
仓库可用标签 name→id 映射 |
GET /v1/:owner/:repo/issue_assigners.json |
可指派用户列表 |
GET /v1/:owner/:repo/issues.json?state=all&limit=100 |
历史 Issue 标题(用于关联推荐) |
2. 决策流水线
Issue JSON
│
▼
┌─────────────────────────────┐
│ Stage A: 文本预处理 │
│ - 拼接 subject + description │
│ - 全角转半角 │
│ - 大小写归一化 │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage B: tracker 决策 │
│ - 标题规则集(高优先级) │
│ - 正文规则集(低优先级) │
│ - 多命中按优先级排序 │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage C: priority 决策 │
│ - 严重度信号扫描 │
│ - 默认 normal │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage D: 标签建议 │
│ - 仓库标签语义匹配 │
│ - 缺失则跳过 │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage E: assignee 建议 │
│ - @mention 解析 │
│ - 否则 null │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage F: related_issues 推荐 │
│ - 关键词 Jaccard 相似度 │
│ - Top-3 + duplicate 检测 │
└────────────┬────────────────┘
▼
┌─────────────────────────────┐
│ Stage G: confidence 计算 │
│ - 规则命中数 / 总信号数 │
│ - < 0.5 标记需人工复核 │
└─────────────────────────────┘
3. 完整关键词规则表
3.1 Tracker 规则(按优先级降序)
# bug(tracker_id: 1)
bug:
title_patterns:
- "bug"
- "错误"
- "失败"
- "崩溃"
- "异常"
- "报错"
- "不能"
- "无法"
- "crash"
- "error"
- "exception"
- "broken"
- "不工作"
- "无反应"
body_patterns:
- "复现步骤"
- "重现"
- "stack trace"
- "回归"
# duplicate(tracker_id: 6,优先级仅次于 bug)
duplicate:
title_patterns:
- "重复"
- "duplicate"
- "same as"
- "已经提过"
body_patterns:
- "和 #\\d+ 一样"
- "同 #\\d+"
# feature(tracker_id: 2)
feature:
title_patterns:
- "feature"
- "希望"
- "建议"
- "新增"
- "支持.*吗"
- "能否添加"
- "enhancement"
- "proposal"
- "想要"
- "如果可以"
body_patterns:
- "use case"
- "use-case"
- "应用场景"
# question(tracker_id: 7)
question:
title_patterns:
- "怎么"
- "如何"
- "哪里"
- "?"
- "?"
- "请问"
- "question"
- "help"
body_patterns:
- "我刚开始用"
- "新手"
- "文档没写"
# doc(tracker_id: 4)
doc:
title_patterns:
- "文档"
- "README"
- "教程"
- "doc"
- "typo"
- "拼写"
- "错别字"
body_patterns:
- "文档不全"
- "示例无法运行"
# support(tracker_id: 3)
support:
title_patterns:
- "支持"
- "求助"
- "support"
- "咨询"
- "如何配置"
3.2 Priority 规则
urgent:
patterns:
- "紧急"
- "urgent"
- "ASAP"
- "线上"
- "production"
- "数据丢失"
- "数据泄露"
- "安全"
- "security"
- "CVE"
- "RCE"
- "越权"
high:
patterns:
- "重要"
- "阻塞"
- "block"
- "无法工作"
- "完全不能用"
- "high"
- "所有用户"
- "全员受影响"
low:
patterns:
- "minor"
- "小问题"
- "nice to have"
- "低优"
- "不急"
- "建议"
- "锦上添花"
# 默认 normal(无任何上述信号)
4. 置信度计算
confidence = 0.0
signals = 0
# tracker 决策信号
if title_match:
confidence += 0.4
signals += 1
if body_match:
confidence += 0.2
signals += 1
if multiple_match_conflict:
confidence -= 0.15
# priority 决策信号
if urgent_or_high_signal:
confidence += 0.2
signals += 1
# 关联 Issue 强信号
if duplicate_score >= 0.7:
confidence += 0.15
signals += 1
# 描述长度(信息量)
if len(description) < 20:
confidence -= 0.2 # 信息不足
# AI 语义判断的额外加权
if ai_semantic_decision:
confidence += 0.1
# 归一化到 [0, 1]
confidence = max(0, min(1, confidence))
阈值:
confidence >= 0.7→ 直接应用0.5 <= confidence < 0.7→ 应用但标记"建议复核"confidence < 0.5→ 不应用,仅放入"待人工"队列
5. 关联 Issue 算法
5.1 文本预处理
def tokenize(text):
# 中文:2-gram 字符切片
# 英文:小写化 + 词形还原
# 去停用词("的", "了", "the", "a", "an", ...)
tokens = set()
# ... implementation
return tokens
5.2 Jaccard 相似度
def jaccard(a: set, b: set) -> float:
if not a or not b:
return 0.0
return len(a & b) / len(a | b)
5.3 关联决策
| 相似度 | 决策 |
|---|---|
| ≥ 0.7 且一方已关闭 | 推荐 mark as duplicate |
| ≥ 0.7 双方都开 | 评论"可能与 #X 相关" |
| 0.4 - 0.7 | 列入"可能相关",由人工判断 |
| < 0.4 | 不关联 |
6. 输出 Schema
完整分析报告遵循以下 JSON Schema(简化版):
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"required": ["repository", "analyzed_at", "total", "items"],
"properties": {
"repository": {"type": "string", "pattern": "^[^/]+/[^/]+$"},
"analyzed_at": {"type": "string", "format": "date-time"},
"total": {"type": "integer", "minimum": 0},
"by_tracker": {
"type": "object",
"additionalProperties": {"type": "integer"}
},
"by_priority": {
"type": "object",
"additionalProperties": {"type": "integer"}
},
"items": {
"type": "array",
"items": {
"type": "object",
"required": ["number", "title", "decisions", "confidence"],
"properties": {
"number": {"type": "integer"},
"title": {"type": "string"},
"current_tracker": {"type": ["string", "null"]},
"current_labels": {"type": "array", "items": {"type": "string"}},
"decisions": {
"type": "object",
"required": ["tracker", "priority"],
"properties": {
"tracker": {"type": "string", "enum": ["bug", "feature", "support", "doc", "test", "duplicate", "question"]},
"priority": {"type": "string", "enum": ["low", "normal", "high", "urgent"]},
"labels": {"type": "array", "items": {"type": "string"}},
"assignee": {"type": ["string", "null"]},
"related_issues": {"type": "array", "items": {"type": "integer"}},
"mark_duplicate": {"type": ["integer", "null"]}
}
},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
"reasoning": {"type": "string"},
"matched_rules": {"type": "array", "items": {"type": "string"}},
"needs_review": {"type": "boolean"}
}
}
}
}
}
7. 边界情况处理
| 情况 | 处理 |
|---|---|
| Issue 无正文 | confidence 上限 0.5;强制 needs_review=true |
| 标题过长(> 100 字) | 截取前 50 字做匹配 |
| 标题全英文 | 跳过中文规则,仅用英文规则 |
| 仓库无任何标签 | 跳过 Stage D,在报告中提示 |
| @mention 用户不在 assigners 列表 | 不指派,提示"权限不足" |
| 历史 Issue < 5 个 | 跳过关联推荐 |
| 已有 tracker 的 Issue | 默认不覆盖,除非用户加 --force |
8. 性能建议
| 规模 | 建议 |
|---|---|
| ≤ 20 个 Issue | 单次分析,内存缓存元数据 |
| 20-100 个 | 分批 20/批,每批后用户确认 |
| > 100 个 | 强制分批,每批 20,建议夜间运行 |
API 调用次数估算:N * 1 (view) + 3 (元数据) + N * 0.3 (平均关联) ≈ 1.3N + 3。
9. 参考实现
伪代码(Python-like):
def triage_issue(issue, repo_meta, history):
text = normalize(issue.subject + " " + issue.description)
# Stage B: tracker
tracker, tracker_rules = decide_tracker(text)
# Stage C: priority
priority, priority_rules = decide_priority(text)
# Stage D: labels
labels = match_labels(tracker, priority, repo_meta.tags)
# Stage E: assignee
assignee = parse_mention(issue.description, repo_meta.assigners)
# Stage F: related
related, duplicate = find_related(issue, history)
# Stage G: confidence
confidence = compute_confidence(
tracker_rules, priority_rules, duplicate, len(issue.description)
)
return {
"number": issue.number,
"decisions": {
"tracker": tracker,
"priority": priority,
"labels": labels,
"assignee": assignee,
"related_issues": related,
"mark_duplicate": duplicate,
},
"confidence": confidence,
"matched_rules": tracker_rules + priority_rules,
"needs_review": confidence < 0.7,
}
完整可运行实现请参考 examples/triage-batch-workflow.md 中的 AI Agent 提示词。