CI/CD中的AI如何用Agent自动生成Release Note

  发布时间:2026-06-23 10:45:26   作者:龙虾不加班   我要评论
这篇教程教你如何使用GitHub Actions和AI工具daily-report-agent自动生成ReleaseNote,省时,提高效率,本文介绍的非常详细,感兴趣的朋友一起看看吧

发 Release 最烦的不是写代码,是写 Release Note。

你在 GitHub 上看到一个 Release,写了 “Bug fixes and performance improvements”——作者的心态跟我一样:代码写完了,真不想再花 20 分钟组织语言总结改了什么。

但这个活 AI 特别擅长——读 Git 提交记录,分类整理,生成人类可读的 Release Note。

这正是我开源的 daily-report-agent 做的事。给它换个 Prompt,它就能从 Git 历史生成 Release Note 而不是日报。

这篇带你把这个能力嵌入 GitHub Actions,每次发 Release 自动生成变更说明。

整体流程

Git Push (触发 workflow)
  → GitHub Actions 拉取代码
    → 用 daily-report-agent 分析 git log
      → Agent 调用 LLM 分类 & 组织语言
        → 生成 Release Note Markdown
          → 自动发布到 GitHub Releases

第一步:把生成逻辑抽成一个独立命令

cmd/agentd/main.go 里除了 API 服务,再加一个 release-note 子命令:

// cmd/agentd/release.go
package main
import (
    "context"
    "fmt"
    "os"
    "os/exec"
    "strings"
    "time"
    "agent-project/internal/config"
    "agent-project/internal/llm"
)
// GenerateReleaseNote 从 Git 历史生成 Release Note
func GenerateReleaseNote(cfg *config.Config, fromTag, toTag string) (string, error) {
    // 1. 获取提交记录
    commits, err := getGitCommits(fromTag, toTag)
    if err != nil {
        return "", fmt.Errorf("获取提交记录失败: %w", err)
    }
    if len(commits) == 0 {
        return "无新提交", nil
    }
    // 2. 构造 Prompt
    prompt := buildReleaseNotePrompt(commits, fromTag, toTag)
    // 3. 调用 LLM
    client := llm.NewClient(cfg.LLM)
    resp, err := client.Chat(context.Background(), []llm.Message{
        {
            Role:    "system",
            Content: releaseNoteSystemPrompt,
        },
        {
            Role:    "user",
            Content: prompt,
        },
    })
    if err != nil {
        return "", fmt.Errorf("LLM 调用失败: %w", err)
    }
    return resp.Text, nil
}
// getGitCommits 获取两个 tag 之间的提交记录
func getGitCommits(fromTag, toTag string) ([]Commit, error) {
    rangeStr := "HEAD"
    if fromTag != "" && toTag != "" {
        rangeStr = fmt.Sprintf("%s..%s", fromTag, toTag)
    } else if fromTag != "" {
        rangeStr = fmt.Sprintf("%s..HEAD", fromTag)
    }
    cmd := exec.Command("git", "log", rangeStr,
        "--pretty=format:%H|||%an|||%ad|||%s|||%b",
        "--date=short",
    )
    output, err := cmd.Output()
    if err != nil {
        return nil, err
    }
    var commits []Commit
    for _, line := range strings.Split(strings.TrimSpace(string(output)), "\n") {
        if line == "" {
            continue
        }
        parts := strings.SplitN(line, "|||", 5)
        if len(parts) < 4 {
            continue
        }
        body := ""
        if len(parts) == 5 {
            body = parts[4]
        }
        commits = append(commits, Commit{
            Hash:    parts[0][:7],
            Author:  parts[1],
            Date:    parts[2],
            Subject: parts[3],
            Body:    body,
        })
    }
    return commits, nil
}
type Commit struct {
    Hash    string
    Author  string
    Date    string
    Subject string
    Body    string
}
// buildReleaseNotePrompt 构造分类 Prompt
func buildReleaseNotePrompt(commits []Commit, fromTag, toTag string) string {
    var sb strings.Builder
    sb.WriteString(fmt.Sprintf("从 %s 到 %s 的变更:\n\n", fromTag, toTag))
    for _, c := range commits {
        sb.WriteString(fmt.Sprintf("- [%s] %s (%s, %s)\n",
            c.Hash, c.Subject, c.Author, c.Date))
        if c.Body != "" {
            // 只取 body 的第一段
            bodyLines := strings.Split(c.Body, "\n\n")
            if len(bodyLines) > 0 {
                sb.WriteString(fmt.Sprintf("  详情: %s\n", bodyLines[0]))
            }
        }
    }
    sb.WriteString(fmt.Sprintf("\n共 %d 个提交。请生成 Release Note。", len(commits)))
    return sb.String()
}
const releaseNoteSystemPrompt = `你是一个开源项目的 Release Manager。
根据 Git 提交记录生成 Release Note。
分类规则:
- 🚀 新功能:feat 开头的提交
- 🐛 Bug 修复:fix 开头的提交
- 🔧 改进:refactor/perf/style 开头的提交
- 📝 文档:docs 开头的提交
- ⚠️ 破坏性变更:有 BREAKING CHANGE 标记或明显不向后兼容的提交
如果不能归类的,放在 "其他" 里。
格式要求:
- 使用 Markdown
- 每个条目一行,说明变更内容,不写过程
- 避免 "该同志"、"表现优异" 等套话
- 语言:中文
输出格式:
## 🚀 新功能
- xxx
## 🐛 Bug 修复
- xxx
## 🔧 改进
- xxx
## 📝 文档
- xxx
如有破坏性变更,在开头加:
> ⚠️ 本版本包含破坏性变更,请谨慎升级。`

第二步:写 GitHub Actions Workflow

# .github/workflows/release-note.yml
name: Generate Release Note
on:
  push:
    tags:
      - 'v*'         # 推送 v 开头 tag 时触发
  workflow_dispatch:   # 也支持手动触发
    inputs:
      from_tag:
        description: '从哪个 tag 开始'
        required: false
      to_tag:
        description: '到哪个 tag'
        required: false
jobs:
  generate-release-note:
    runs-on: ubuntu-latest
    permissions:
      contents: write  # 需要写权限来创建 Release
    steps:
      - name: 拉取代码
        uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 获取完整 git 历史
      - name: 安装 Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
      - name: 编译 agent
        run: go build -o agentd ./cmd/agentd
      - name: 确定 tag 范围
        id: tags
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            echo "FROM=${{ inputs.from_tag }}" >> $GITHUB_OUTPUT
            echo "TO=${{ inputs.to_tag }}" >> $GITHUB_OUTPUT
          else
            # 自动获取:上一次 tag → 当前 tag
            CURRENT_TAG="${{ github.ref_name }}"
            PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
            echo "FROM=${PREV_TAG}" >> $GITHUB_OUTPUT
            echo "TO=${CURRENT_TAG}" >> $GITHUB_OUTPUT
          fi
      - name: 生成 Release Note
        env:
          LLM_API_KEY: ${{ secrets.LLM_API_KEY }}
          LLM_MODEL: deepseek-v4-flash
        run: |
          ./agentd release-note \
            --from "${{ steps.tags.outputs.FROM }}" \
            --to "${{ steps.tags.outputs.TO }}" \
            > release-note.md
      - name: 展示生成的 Release Note
        run: cat release-note.md
      - name: 创建 GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.ref_name }}
          body_path: release-note.md
          draft: false
          prerelease: false
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

真实效果

以 daily-report-agent v0.2.0 为例,手动写 Release Note 大约花了 15 分钟。用 Agent 自动生成后:

Agent 生成的输出:

## 🚀 新功能
- 支持多仓库批量分析,自动汇总日报 (#15)
- 新增企业微信通知渠道,日报直接推送到群 (#18)
## 🐛 Bug 修复
- 修复 Git 提交信息中含特殊字符时解析崩溃的问题 (#12)
- 修复时区问题导致日报日期差一天 (#14)
## 🔧 改进
- LLM 调用增加指数退避重试,API 超时自动恢复 (#16)
- 日志改用 slog 结构化输出,方便接入日志平台 (#17)
## 📝 文档
- README 新增 Docker Compose 部署说明
- 补充 API 文档和调用示例

我审阅了一下,改了两处措辞,就发布了。全程 3 分钟。

Agent 不会漏提交——Git log 是完整的。它也不会把 feat 写成 fix。比人更快,比人更准确。

进阶:多仓库 Release Note 汇总

如果你有一个微服务项目,一次发版涉及 5 个仓库。Agent 可以逐个仓库分析,再汇总:

func GenerateMultiRepoReleaseNote(repos []string) (string, error) {
    var allNotes []string
    for _, repo := range repos {
        note, err := GenerateReleaseNoteForRepo(repo)
        if err != nil {
            log.Printf("[Warn] 仓库 %s 生成失败: %v", repo, err)
            continue
        }
        allNotes = append(allNotes, fmt.Sprintf("### %s\n\n%s", repo, note))
    }
    // 调一次 LLM 做最终汇总
    return summarizeReleaseNotes(allNotes)
}

成本

一次 Release Note 生成:

步骤Token 消耗费用
Git Log 提取0(本地执行)¥0
LLM 分类整理~3000 token¥0.009
GitHub API 发布0¥0
合计~3000 token¥0.009

不到 1 分钱。省了 15 分钟。

用 GitHub Actions 免费额度(每月 2000 分钟),完全在免费额度内。

不到1分钱省了15分钟。下一篇:Agent 安全审查清单——API Key 管理、用户数据隔离。10个检查项,每个配修复方案。

到此这篇关于CI/CD 中的 AI:用 Agent 自动生成 Release Note的文章就介绍到这了,更多相关CI/CD Agent 自动生成 Release Note内容请搜索脚本之家以前的文章或继续浏览下面的相关文章,希望大家以后多多支持脚本之家!

相关文章

最新评论