发版与部署自动化
核心问题
"文件压缩节点"开发完成、构建通过、测试通过——现在要发版了。发版涉及编译、打包、版本号、安装包生成、发布日志等多个步骤,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这个命令做了三件事:
- 自动读取版本号 — 从编译后的 DLL 中读取 AssemblyVersion,不需要手动填版本号
powershell
$versionInfo = (Get-Item $DllPath).VersionInfo
$assemblyVersion = $versionInfo.FileVersion
$parts = $assemblyVersion.Split('.')
# → 生成 version.nsh(NSIS 版本定义文件)生成 NSIS 安装包 — 使用 version.nsh 中的版本号,编译出
growth-aios-x.x.x.x.exe内置运行时检测 — 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 的组合执行
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 | 本质 |
|---|---|---|
| 编译 Desktop | build-desktop | 执行 package.ps1 -Step desktop |
| 编译前端 | build-frontend | 执行 package.ps1 -Step frontend |
| 生成安装包 | build-installer | 执行 build-nsi-installer.ps1 |
| 上传 OSS | upload-oss | 执行 oss 上传命令 |
| 打 Git Tag | git-tag | 执行 git tag 命令 |
| 生成发布日志 | generate-changelog | 读取 git log 生成 Markdown |
在 release-publisher 的语境中,脚本就是 Tool。 build-nsi-installer.ps1 就是一个 Tool 的Script,package.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 SubAgent | 9 步发版自动完成 |
| Tool 定义 | ToolDefinition(Name + Description + Parameters) | 脚本包装为 LLM 可调用的函数 |
| Agent 定义 | CustomAgentDefinition(SystemPrompt + ToolIds) | Agent 按需选择 Tool 编排流程 |
| 发布日志 | Agent 从 git log 生成 | 不需要手动写 |
| 脚本+Agent | 各司其职 | Tool 执行 + Agent 判断 = 最佳组合 |
关联:第二部分 04(子代理分工)。