Skip to content

发版与部署自动化

核心问题

"文件压缩节点"开发完成、构建通过、测试通过——现在要发版了。发版涉及编译、打包、版本号、安装包生成、发布日志等多个步骤,Qoder 怎么帮我自动化?


使用篇:发版流程的复杂度

发版涉及多少步

Growth AIOS 的发版不是一个"点击发布"的动作,它涉及多个步骤:

1. 确认当前状态(版本号、分支、未提交变更)
2. 编译 Desktop 客户端(Release)
3. 编译 Web 前端(monorepo)
4. 编译后台组件
5. 全量打包到 publish/ 目录
6. 编译 NSIS 安装包
7. 基于 Git 提交生成发布日志
8. 上传 OSS
9. 打 Git Tag

发版九步流程

9 个步骤,任何一个出错都可能导致发布失败。手动执行既慢且容易遗漏。

脚本已经做了基础工作

在第六章中我们提到的构建脚本已经解决了「编译+打包」这一步:

powershell
.\build-nsi-installer.ps1 -All

这个命令做了三件事:

  1. 自动读取版本号 — 从编译后的 DLL 中读取 AssemblyVersion,不需要手动填版本号
powershell
$versionInfo = (Get-Item $DllPath).VersionInfo
$assemblyVersion = $versionInfo.FileVersion
$parts = $assemblyVersion.Split('.')
# → 生成 version.nsh(NSIS 版本定义文件)
  1. 生成 NSIS 安装包 — 使用 version.nsh 中的版本号,编译出 growth-aios-x.x.x.x.exe

  2. 内置运行时检测 — NSIS 安装包在安装时自动检测 .NET 8.0 Desktop Runtime 和 WebView2,缺失时静默安装

但脚本只能做「执行」,不能做「判断」。仍然需要人来确认:

  • 当前版本号是否正确?
  • 这次发版包含了哪些变更?
  • 发布日志要怎么写?

实践篇:release-publisher SubAgent

为什么需要 SubAgent

脚本可以做执行,但不能做判断。SubAgent 的作用就是在脚本之上加一层智能编排

在第一章配置的 AGENTS.md 中,我们已经声明了 release-publisher SubAgent:

markdown
---
name: release-publisher
description: 发布打包专家,负责编译多个子项目、生成安装包、上传OSS并编写发布日志
tools: Bash, Read, Write, Glob, Grep
mcpServers:
  - github
---

## 发布流程
1. 确认当前状态(版本号、分支、未提交变更)
2. 编译 Desktop 客户端
3. 编译 Web 前端
4. 编译后台组件
5. 全量打包
6. 编译安装包
7. 基于 Git 提交生成发布日志
8. 上传 OSS
9. Git Tag

## 约束
- 每完成一步输出进度标记
- 版本号从 DLL 的 AssemblyVersion 读取
- 发布日志基于实际提交记录生成

贯穿案例中的应用

"文件压缩节点"要随下一个版本发布了。你只需要说一句话:

发版 v3.8.9,这次新增了文件压缩节点

SubAgent 自动开始 9 步流程:

[1/9] 确认状态 → 当前版本 v3.8.9,在 main 分支,无未提交变更
[2/9] 编译 Desktop → 成功
[3/9] 编译 Web 前端 → 成功
[4/9] 编译后台组件 → 成功
[5/9] 全量打包 → 成功
[6/9] 编译安装包 → 成功,输出 growth-aios-3.8.9.0.exe
[7/9] 生成发布日志 → 基于 git log 生成
[8/9] 上传 OSS → 成功
[9/9] Git Tag → v3.8.9 已创建

脚本 vs Agent 的分工

能力脚本Agent
执行构建命令✅(调用脚本)
跨项目编排❌ 各脚本独立✅ 统一调度
错误诊断❌ 直接退出✅ 分析根因+修复建议
版本智能决策✅ 根据提交范围判断
发布日志❌ 手动写✅ AI 从 Git log 生成
OSS 上传

脚本负责执行,Agent 负责判断。 两者不是替代关系,是分工关系。


实践篇:版本号的"单源 truth"

版本管理的问题

大型项目发版时,版本号的管理经常成为痛点:

  • 哪个文件维护版本号?
  • 安装包的版本号是否和代码版本号一致?
  • 发版时要不要手动改版本号?

Growth 项目的方案是:版本号只有一个源头——csproj 文件中的 <Version> 标签。

xml
<!-- Desktop/Desktop.Client.csproj -->
<PropertyGroup>
    <Version>3.8.9.0</Version>
</PropertyGroup>

从这一点出发,所有环节自动获取版本号:

.csproj <Version>
  → 编译后写入 DLL 的 AssemblyVersion
    → build-nsi-installer.ps1 从 DLL 读取
      → 生成 version.nsh
        → NSIS 编译安装包
          → growth-aios-3.8.9.0.exe

发版时只需要改 csproj 中的一个数字,其他全部自动化。

贯穿案例中的应用

发布"文件压缩节点"的版本时,发版前在 csproj 中把版本号改为 3.9.0.0。然后:

发版 v3.9.0,新增文件压缩节点

release-publisher 自动从 DLL 读取版本号、生成安装包、打包——不再需要手动填任何版本号。


知识篇:Agent 与 Tool 的架构关系

前面一直在说"Agent 调用脚本",但脚本是怎么成为 Agent 的"工具"的?这里涉及一个核心概念——LLM Function Calling

什么是 Function Tool

在 LLM 语境下,Tool(工具)就是一个可以被 LLM 调用的函数。它有名字、有描述、有参数声明,LLM 根据这些信息自己决定什么时候调用它。

这是 Growth AIOS 中的 Tool 数据模型:

csharp
public class ToolDefinition
{
    public string Id { get; set; }            // 唯一标识(如 "build-desktop")
    public string Name { get; set; }          // 函数名(LLM Function Calling 使用)
    public string Description { get; set; }   // 描述(LLM 据此决定是否调用)
    public JsonElement Parameters { get; set; } // JSON Schema 参数声明
    public string Script { get; set; }        // Python 实现代码
}

一个 Tool 的本质就三样东西:告诉 LLM 它叫什么、能干什么、需要什么参数

什么是 Agent

Agent 是 Tool 的使用者。它比 Tool 多了一层"判断力"——有角色定义(System Prompt)、有推理参数、知道在什么场景下调用哪些 Tool。

这是 Agent 的模型:

csharp
public class CustomAgentDefinition
{
    public string Id { get; set; }               // 唯一标识
    public string SystemPrompt { get; set; }     // 角色定义 + 行为约束
    public List<string> ToolIds { get; set; }    // 它能用哪些 Tool
    public double Temperature { get; set; }       // 推理参数(严谨程度)
}

两者的关系可以用一句话概括:Tool 是能力,Agent 是决策者

从代码理解 Tool → Agent 的映射

Growth AIOS 中有两个实际的 Agent,恰好演示了这种关系。

AiToolAgent(custom-tool)——从自然语言生成 Tool:

csharp
// 用户说"帮我创建一个统计关键词的 Tool"
// AiToolAgent 输出一个 ToolDefinition JSON:
{
    "name": "count_keyword",
    "description": "统计文本中指定关键词的出现次数",
    "parameters": {
        "type": "object",
        "properties": {
            "text": { "type": "string", "description": "要分析的文本" },
            "keyword": { "type": "string", "description": "要统计的关键词" }
        },
        "required": ["text", "keyword"]
    },
    "script": "async def main(args: Args) -> dict:\n    ..."
}

这个 JSON 结构恰好对应 LLM Function Calling 的协议——Name 是函数名、Description 是说明、Parameters 告诉 LLM 参数格式

CustomAgentGeneratorAgent(custom-agent)——从自然语言生成 Agent:

csharp
// 用户说"帮我创建一个发布打包助手"
// CustomAgentGenerator 从可用 Tool 列表中挑选匹配的 Tool,
// 然后生成完整的 Agent 配置:
{
    "id": "release-assistant",
    "name": "发布打包助手",
    "systemPrompt": "你是专业的发布打包专家...",
    "toolIds": ["build-desktop", "build-installer", "upload-oss", "git-tag"],
    "callOptions": { "temperature": 0.1 }
}

关键点是:Tool 列表是从数据库动态查询出来的,不是人手工维护的。Agent 生成器会把你需求的 Tool 匹配上。

Tool → Agent 架构关系

Tool 的组合执行

Tool 定义中的 Script 字段是一段 Python 代码。当 LLM 决定调用某个 Tool 时,实际执行的是这段代码:

用户说"统计关键词"          LLM 输出: call count_keyword({"text":"...","keyword":"AI"})
     ↓                              ↓
PythonScriptTool.ToAITool()    PythonScriptAIFunction.InvokeCoreAsync()
     ↓                              ↓
注册到 EmbeddedAgentConfig     通过 CodeRunner 执行 script 中的 Python

整个链路的核心是 PythonScriptTool——它将用户定义的 Tool 包装为标准 AITool,注册到 LLM 的 Function Calling 框架中。对 LLM 来说,这些 Tool 和内置的计算器、搜索没有区别。

回到 release-publisher

理解了 Tool 和 Agent 的关系,再看 release-publisher SubAgent 就清晰了:

发版步骤对应 Tool本质
编译 Desktopbuild-desktop执行 package.ps1 -Step desktop
编译前端build-frontend执行 package.ps1 -Step frontend
生成安装包build-installer执行 build-nsi-installer.ps1
上传 OSSupload-oss执行 oss 上传命令
打 Git Taggit-tag执行 git tag 命令
生成发布日志generate-changelog读取 git log 生成 Markdown

在 release-publisher 的语境中,脚本就是 Tool。 build-nsi-installer.ps1 就是一个 Tool 的Scriptpackage.ps1 -Step desktop 就是另一个 Tool。Agent 不直接执行构建逻辑,它决定什么时候调用哪个脚本。

这就是为什么之前的"脚本 vs Agent"对比表中,Agent 能做的(编排、判断、分析)恰好是 LLM 的强项,而脚本能做的(编译、打包、上传)恰好是确定性操作。


知识篇:为什么"脚本 + Agent"是绝佳组合

脚本的弱点

脚本逻辑清晰、执行稳定、可重复——但也有天生的弱点:

  • 无法做模糊判断:脚本只能执行"如果 A 则 B"的确定性逻辑,无法处理"看起来不太对"的情况
  • 无法理解语义:脚本读不懂 Git 提交消息的含义,无法生成有意义的发布日志
  • 无法自我修复:脚本失败了就是失败了,不会分析失败原因

Agent 的弱点

Agent 智能、灵活、能理解语义——但也有弱点:

  • 执行不稳定:同样的指令在不同上下文中可能有不同结果
  • 成本高:AI 推理的成本远高于脚本执行
  • 不适合确定性操作:编译、打包这些操作不需要 AI 判断,只需要精确执行

组合方案

脚本负责:编译、打包、上传、版本读取  → 稳定、可重复
Agent 负责:编排、判断、分析、生成日志 → 智能、灵活

两者组合:Agent 调用脚本执行确定性操作,Agent 自己处理需要判断的业务

这就是 release-publisher 的设计哲学——脚本 + Agent 的组合,比任何单一方案都强。


本章小结

环节工具对贯穿案例的作用
版本号管理csproj → DLL → version.nsh改一个数字即可
安装包生成build-nsi-installer.ps1自动编译 NSIS 安装包
流程编排release-publisher SubAgent9 步发版自动完成
Tool 定义ToolDefinition(Name + Description + Parameters)脚本包装为 LLM 可调用的函数
Agent 定义CustomAgentDefinition(SystemPrompt + ToolIds)Agent 按需选择 Tool 编排流程
发布日志Agent 从 git log 生成不需要手动写
脚本+Agent各司其职Tool 执行 + Agent 判断 = 最佳组合

关联:第二部分 04(子代理分工)。