第十六章:竞赛实战——从零搭建多 Agent 系统
本章目标:把前十五章学到的所有概念整合为一个完整的竞赛项目。从选题、架构设计、代码实现、调试、生产化加固到最终文档与展示,走完”从零到提交”的全流程。
16.1 腾讯犀牛鸟 2026 Agent 赛道概述
日常类比:一场有主题的烹饪比赛
想象你参加了一个烹饪比赛。比赛不只是”做一道菜”这么简单——它有特定的主题(比如”融合菜”)、评判维度(色香味+创新+实用)、时间限制(两小时)、以及展示环节(给评委讲解你的思路)。你不能只会做菜,还要会选菜、会规划时间、会讲故事。
Agent 赛道也一样:它不只是”写一个 Agent”,而是一个完整的工程竞赛,考验你的选题眼光、架构能力、编码实力和表达功力。
赛道背景
腾讯犀牛鸟开源人才培养计划是腾讯与高校合作的年度项目。2026 年的 Agent 赛道聚焦于:
- 多 Agent 协作系统:不是写一个简单的 chatbot,而是让多个 Agent 分工合作解决复杂问题
- 基于开源框架:使用 tRPC-Agent-Go(或其他指定框架)进行开发
- 实际应用场景:解决方案必须面向真实需求,而非纯学术玩具
- 工程质量:代码质量、测试覆盖、文档完备度都会被评审
评判维度
评判维度 权重 含义
─────────────────────────────────────────────────────
技术创新性 30% 是否有新颖的协作模式/工具设计
工程完备度 25% 代码质量/测试/错误处理/日志
实际应用价值 25% 方案能否真正解决问题
文档与展示 20% README/技术文档/Demo 的清晰度
时间线规划(典型 8 周赛程)
Week 1-2:选题调研 + 方案设计 + 原型验证
Week 3-4:核心功能实现 + 单 Agent 跑通
Week 5-6:多 Agent 集成 + StateGraph 编排
Week 7 :生产化加固 + 性能优化
Week 8 :文档撰写 + Demo 录制 + 提交
时间管理的核心原则:前紧后松。前两周确定方向、搭好骨架比什么都重要。第五周还在改选题的队伍基本没有好结果。
赛道与前面章节的对应关系
| 竞赛阶段 | 对应章节 | 核心概念 |
|---|---|---|
| 项目搭建 | 第 2 章(架构精读) | Runner/Flow/Agent 三层 |
| 单 Agent 开发 | 第 3 章(ReAct 循环) | Think-Act-Observe |
| 多 Agent 协作 | 第 4 章(Team) | Coordinator/Swarm |
| 复杂工作流 | 第 5 章(StateGraph) | 有向图/条件路由 |
| 生产化 | 第 11 章(Ralph Loop) | 可验证执行 |
| 内存管理 | 第 12 章(Memory) | Session/Long-term |
16.2 竞赛选题策略
日常类比:选餐厅位置
开餐厅选址有四个黄金原则——人流量大(需求明确)、竞争不太激烈(差异化空间)、租金能承受(技术复杂度可控)、未来有发展(有拓展性)。竞赛选题也是一样:你要找一个需求清晰、能展示多 Agent 协作价值、8 周内能完成、且有创新空间的方向。
四个选题原则
原则一:多 Agent 必要性(不能为了多 Agent 而多 Agent)
评委最反感的就是”伪多 Agent”——明明一个 Agent 就能搞定,硬拆成三个互相传话。好的选题应该满足:
- 任务天然包含多个专业领域(需要不同 system prompt 和 tool set)
- 单个 Agent 的 context window 装不下所有工具描述
- 子任务之间存在依赖关系或需要并行加速
原则二:复杂度可控(MVP 优先)
8 周时间 = 大约 320 小时有效工作时间(4人团队,每人每周约 10 小时)。你需要:
- 核心功能 2 周内能 demo
- 不依赖难以获取的外部资源(如大量标注数据、付费 API)
- 有明确的”完成”标准(不是开放性研究)
原则三:创新点可解释
“我用了 tRPC-Agent-Go”不是创新。创新必须是:
- 新的协作模式(比如”审稿-修改-验证”三角循环)
- 新的工具组合(比如”代码分析 + 测试生成 + 部署检查”联动)
- 新的验证机制(比如”Agent 输出必须通过另一个 Agent 的形式化验证”)
原则四:评委视角(30 秒内能理解价值)
评委一天看几十个项目。如果看完你的 README 前三段还不明白你在做什么、为什么重要、有什么创新——基本就没戏了。选题时就要想好”一句话卖点”。
六个候选方向详细分析
方向一:DevOps 智能流水线
一句话定位:让 AI 像高级 SRE 一样管理从代码提交到上线的全流程。
协作模式:
CodeReviewer Agent ──→ TestGenerator Agent ──→ Deployer Agent
│ │ │
├─ 读代码差异 ├─ 生成测试用例 ├─ 编排部署脚本
├─ 检查风格/安全/性能 ├─ 运行并验证覆盖率 ├─ 灰度/回滚决策
└─ 输出 review 意见 └─ 输出测试报告 └─ 输出部署状态
Coordinator 是 PipelineManager,它根据代码变更的范围和风险等级决定走哪条路径:小改动可能跳过部分环节,大改动则走完全流程。
复杂度:中高。需要对接 Git API、CI 系统、容器编排。但核心逻辑清晰。
创新点:
- 引入 Ralph Loop:每个 Agent 的输出都有可验证的断言(测试覆盖率 > 80%,无高危漏洞)
- 条件循环:Review 不通过 → 回到修改建议 → 重新 Review
- 风险分级:自动判断变更风险,调整流水线深度
适合团队:有后端开发经验,了解 CI/CD 概念。
评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★★★ / 实用价值 ★★★★★ / 文档展示 ★★★★☆
方向二:多语言翻译校对系统
一句话定位:让 AI 像专业翻译团队一样,初译、校对、润色三步分工,保证翻译质量。
协作模式:
Translator Agent ──→ Reviewer Agent ──→ Polisher Agent
│ │ │
├─ 源语言→目标语言 ├─ 对照原文检查 ├─ 优化表达流畅度
├─ 保持术语一致 ├─ 标注误译/漏译 ├─ 统一全文风格
└─ 输出初译稿 └─ 输出修改建议 └─ 输出终稿
使用 Swarm 模式 更合适——Reviewer 发现严重误译时,可以把控制权交还 Translator 重译该段落,而不需要通过中心调度。
复杂度:中。不需要复杂的外部系统集成,重点在 prompt 工程和质量验证。
创新点:
- 术语表一致性验证(工具化:查术语表数据库)
- 双向检验:Reviewer 独立回译,对比与原文的语义偏差
- 可量化质量分数:BLEU-like 自动评分 + 人工抽检结合
适合团队:对 NLP/翻译有兴趣,prompt engineering 能力强。
评分预估:技术创新 ★★★☆☆ / 工程完备度 ★★★★☆ / 实用价值 ★★★★☆ / 文档展示 ★★★★★
方向三:数据分析自动化平台
一句话定位:让 AI 像数据分析团队一样,从理解需求到出图出报告全流程自动化。
协作模式:
RequirementParser ──→ DataEngineer ──→ Analyst ──→ Reporter
│ │ │ │
├─ 理解分析需求 ├─ SQL查询 ├─ 统计分析 ├─ 生成图表
├─ 明确指标定义 ├─ 数据清洗 ├─ 趋势识别 ├─ 撰写结论
└─ 输出分析计划 └─ 输出数据集 └─ 输出洞察 └─ 输出报告
Coordinator 负责调度。但内部用 StateGraph 实现更复杂的流程:
// 伪代码:数据分析流程图
graph.AddNode("parse_requirement", parseRequirementNode)
graph.AddNode("query_data", queryDataNode)
graph.AddNode("validate_data", validateDataNode) // 数据质量检查
graph.AddNode("analyze", analyzeNode)
graph.AddNode("generate_report", reportNode)
// 条件路由:数据质量不达标 → 重新查询
graph.AddConditionalEdge("validate_data", func(state State) string {
if state.DataQuality < 0.8 {
return "query_data" // 循环回去重新查
}
return "analyze"
})
复杂度:中高。需要对接数据库、实现安全的 SQL 执行沙箱、图表生成。
创新点:
- 自我纠错循环:分析结果不合理时自动回溯
- 安全沙箱:SQL 执行有严格的权限控制和超时限制
- 渐进式分析:先给粗略结论,用户追问时深入
适合团队:有数据分析/BI 背景,熟悉 SQL。
评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★★☆ / 实用价值 ★★★★★ / 文档展示 ★★★★☆
方向四:智能客服系统
一句话定位:让 AI 像客服团队一样,分诊、解答、升级、回访全流程协作。
协作模式:
Triage Agent ──→ FAQ Agent / Technical Agent / Escalation Agent
│ │ │ │
├─ 理解用户意图 ├─ 查知识库 ├─ 排查技术问题 ├─ 转人工+摘要
├─ 分类问题类型 ├─ 回答常见问 ├─ 调用诊断工具 ├─ 记录工单
└─ 路由到对应Agent└─ 确认满意度 └─ 给出修复方案 └─ 通知相关人
使用 Swarm 模式:Triage 判断类型后直接 handoff 给对应 Agent,如果该 Agent 发现问题超出能力范围,再 handoff 给 Escalation。
复杂度:中。知识库检索是成熟技术(RAG),重点在路由准确性和用户体验。
创新点:
- 多轮上下文感知:跨 Agent handoff 时保持完整对话历史
- 情感分析:检测用户情绪,触发人工升级
- 解决率追踪:自动回访确认问题是否真正解决
适合团队:有产品思维,关注用户体验。
评分预估:技术创新 ★★★☆☆ / 工程完备度 ★★★★☆ / 实用价值 ★★★★★ / 文档展示 ★★★★★
方向五:智能测试生成系统
一句话定位:让 AI 像 QA 团队一样,分析需求、设计用例、生成代码、执行验证。
协作模式:
SpecAnalyzer ──→ TestDesigner ──→ CodeGenerator ──→ Executor
│ │ │ │
├─ 解析需求文档 ├─ 设计测试策略 ├─ 生成测试代码 ├─ 运行测试
├─ 识别边界条件 ├─ 覆盖率规划 ├─ Mock 数据生成 ├─ 收集结果
└─ 输出测试范围 └─ 输出用例列表 └─ 输出代码文件 └─ 输出报告
StateGraph 驱动:执行失败 → 分析失败原因 → 修复测试代码 → 重新执行(循环最多 3 次)。
复杂度:高。需要代码分析能力、安全执行环境、多语言支持。
创新点:
- 变异测试:自动修改被测代码,验证测试是否能捕获变异
- 基于 AST 的代码分析:不是纯文本理解,而是结构化分析
- 增量测试:只为变更的代码生成新测试
适合团队:有测试工程经验,熟悉 AST/代码分析。
评分预估:技术创新 ★★★★★ / 工程完备度 ★★★★☆ / 实用价值 ★★★★☆ / 文档展示 ★★★☆☆
方向六:知识库自动维护系统
一句话定位:让 AI 像文档管理团队一样,监控变更、更新文档、检查一致性、生成索引。
协作模式:
ChangeDetector ──→ DocUpdater ──→ ConsistencyChecker ──→ Indexer
│ │ │ │
├─ 监控代码变更 ├─ 更新相关文档 ├─ 检查交叉引用 ├─ 重建搜索索引
├─ 识别文档影响 ├─ 生成变更摘要 ├─ 标注过时内容 ├─ 更新导航结构
└─ 输出影响列表 └─ 输出更新PR └─ 输出问题报告 └─ 输出索引文件
Coordinator 按事件触发:代码合并 → 启动 ChangeDetector → 根据影响范围调度后续 Agent。
复杂度:中。文件操作为主,但一致性检查逻辑复杂。
创新点:
- 语义相关性:不是简单关键词匹配,而是理解代码变更的语义影响
- 渐进式更新:大规模变更分批处理,每批验证通过再继续
- 人机协作:AI 生成更新草稿,人审批后合入
适合团队:有文档工程/知识管理兴趣,注重细节。
评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★☆☆ / 实用价值 ★★★★☆ / 文档展示 ★★★★★
选题决策矩阵
方向 多Agent必要性 复杂度可控 创新点清晰 评委友好 推荐指数
──────────────────────────────────────────────────────────────────────
DevOps流水线 ★★★★★ ★★★☆☆ ★★★★☆ ★★★★☆ ████░
翻译校对 ★★★☆☆ ★★★★★ ★★★☆☆ ★★★★★ ███░░
数据分析 ★★★★☆ ★★★☆☆ ★★★★☆ ★★★★☆ ████░
智能客服 ★★★★☆ ★★★★☆ ★★★☆☆ ★★★★★ ███░░
测试生成 ★★★★★ ★★☆☆☆ ★★★★★ ★★★☆☆ ████░
知识库维护 ★★★★☆ ★★★★☆ ★★★★☆ ★★★★☆ ████░
结论:DevOps 流水线、数据分析、测试生成三个方向的综合表现最好。本章后续以 DevOps 智能流水线 为主例进行完整实战演示。
16.3 从零开始的项目搭建
日常类比:装修新房
搭建项目就像装修新房。你不会一上来就买沙发(写业务逻辑),而是先:打好地基(初始化项目)→ 装好水电(引入依赖)→ 验收基础设施(跑通第一个 Agent)→ 再规划房间布局(目录结构)。
16.3.1 Go 项目初始化
# 创建项目目录
mkdir devops-agent && cd devops-agent
# 初始化 Go module
go mod init github.com/your-team/devops-agent
# 确认 Go 版本 >= 1.21(tRPC-Agent-Go 要求)
go version
# go version go1.22.0 linux/amd64
16.3.2 引入 tRPC-Agent-Go 依赖
# 引入 tRPC-Agent-Go 核心包
go get trpc.group/trpc-go/trpc-agent-go
# 引入常用子包
go get trpc.group/trpc-go/trpc-agent-go/agent
go get trpc.group/trpc-go/trpc-agent-go/tool
go get trpc.group/trpc-go/trpc-agent-go/team
go get trpc.group/trpc-go/trpc-agent-go/stategraph
go get trpc.group/trpc-go/trpc-agent-go/plugin
# 整理依赖
go mod tidy
16.3.3 第一个 Agent 跑起来
在写复杂系统之前,先确保框架本身能正常工作。以下是最小可运行的 Agent 示例:
// cmd/hello/main.go
package main
import (
"context"
"fmt"
"log"
"trpc.group/trpc-go/trpc-agent-go/agent"
"trpc.group/trpc-go/trpc-agent-go/runner"
)
func main() {
// 1. 创建一个最简单的 Agent
helloAgent := agent.NewAgent(
agent.WithName("HelloAgent"),
agent.WithSystemPrompt("你是一个友好的助手,用中文回复。"),
agent.WithModel("gpt-4o-mini"), // 开发阶段用便宜的模型
)
// 2. 创建 Runner(Agent 的运行容器)
r := runner.NewRunner(
runner.WithAgent(helloAgent),
)
// 3. 运行一次请求
ctx := context.Background()
result, err := r.Run(ctx, "你好!请介绍一下你自己。")
if err != nil {
log.Fatalf("运行失败: %v", err)
}
fmt.Println("Agent 回复:", result.Content)
}
运行测试:
# 设置 API Key(开发环境)
export OPENAI_API_KEY="your-key-here"
# 运行
go run cmd/hello/main.go
# 输出: Agent 回复: 你好!我是一个友好的助手...
如果这一步能跑通,说明你的环境配置是正确的。接下来才进入正式开发。
16.3.4 项目目录结构建议
devops-agent/
├── cmd/ # 入口文件
│ ├── server/ # 生产服务入口
│ │ └── main.go
│ └── demo/ # Demo/测试入口
│ └── main.go
├── internal/ # 私有包(不对外暴露)
│ ├── agents/ # 各个 Agent 定义
│ │ ├── reviewer.go # CodeReviewer Agent
│ │ ├── testgen.go # TestGenerator Agent
│ │ ├── deployer.go # Deployer Agent
│ │ └── coordinator.go # 协调者 Agent
│ ├── tools/ # 自定义工具
│ │ ├── git.go # Git 相关工具
│ │ ├── ci.go # CI 相关工具
│ │ ├── docker.go # Docker 相关工具
│ │ └── notify.go # 通知工具
│ ├── workflow/ # 工作流定义
│ │ ├── pipeline.go # 主流水线 StateGraph
│ │ └── review_loop.go # Review 循环子图
│ ├── config/ # 配置管理
│ │ └── config.go
│ └── plugin/ # 自定义插件
│ ├── logger.go # 日志插件
│ ├── ratelimit.go # 限流插件
│ └── tracer.go # 追踪插件
├── pkg/ # 公开包(可被其他项目引用)
│ └── models/ # 数据模型
│ ├── review.go
│ └── pipeline.go
├── configs/ # 配置文件
│ ├── dev.yaml
│ └── prod.yaml
├── scripts/ # 辅助脚本
│ ├── setup.sh
│ └── demo.sh
├── docs/ # 文档
│ ├── architecture.md # 架构设计
│ ├── api.md # API 文档
│ └── demo.md # Demo 指南
├── tests/ # 集成测试
│ └── integration_test.go
├── go.mod
├── go.sum
├── Makefile # 构建命令
└── README.md # 项目说明
命名规范要点:
internal/确保外部无法引用你的内部实现(Go 语言约定)- 每个 Agent 一个文件,不要把所有 Agent 塞一个文件
- 工具按领域分组(git/ci/docker),不要按 Agent 分组
- 配置和脚本独立目录,别混在代码里
16.3.5 Makefile 管理常用命令
# Makefile
.PHONY: build run test lint clean demo
# 构建
build:
go build -o bin/devops-agent ./cmd/server
# 运行开发服务
run:
go run ./cmd/server
# 运行 Demo
demo:
go run ./cmd/demo
# 测试
test:
go test ./... -v -cover
# 代码检查
lint:
golangci-lint run ./...
# 清理
clean:
rm -rf bin/
16.4 设计多 Agent 协作架构
日常类比:组建项目团队
设计多 Agent 架构就像组建一个项目团队。你需要回答:这个项目需要几个人?(Agent 数量)每个人负责什么?(职责边界)谁来统筹协调?(Coordinator vs Swarm)人和人之间怎么交接工作?(通信协议)出了问题怎么处理?(错误恢复)
16.4.1 从需求分析到架构设计的方法论
Step 1:列出所有子任务
DevOps 流水线需要做什么?
1. 分析代码变更(哪些文件改了、改了什么)
2. 代码审查(风格/安全/性能/逻辑)
3. 生成测试用例(覆盖新增逻辑)
4. 运行测试(执行并收集结果)
5. 构建镜像(Docker build)
6. 部署上线(灰度/全量)
7. 监控验证(上线后健康检查)
8. 通知相关人(结果通知)
Step 2:按专业领域分组
代码领域:分析变更 + 代码审查 → CodeReviewer Agent
测试领域:生成用例 + 运行测试 → TestGenerator Agent
部署领域:构建镜像 + 部署 + 监控 → Deployer Agent
通用能力:通知 → 所有 Agent 共享的工具
Step 3:确定 Agent 数量
判断标准:
| 问题 | 答案 | 结论 |
|---|---|---|
| 各组需要不同的 system prompt 吗? | 是:审查强调严格标准,部署强调安全 | 应该是不同 Agent |
| 各组的工具集有交集吗? | 很少:审查用 AST 分析,部署用 Docker CLI | 应该分开 |
| 总工具数超 15 个吗? | 是(大约 20+) | 必须分开(避免 token 浪费) |
| 有些组可以并行吗? | 审查和测试可以同时进行 | 分开有利于并行 |
结论:3 个专业 Agent + 1 个协调 Agent = 4 个 Agent。
Step 4:选择协作模式
决策树:
├─ 任务流程固定吗?
│ ├─ 是 → 考虑 StateGraph(精确控制每步)
│ └─ 不完全固定 → 继续判断 ↓
├─ 需要中心调度吗?
│ ├─ 是(有一个"全局视角"决定下一步)→ Coordinator
│ └─ 否(各 Agent 自己决定何时交接)→ Swarm
└─ 两者结合?→ Coordinator + 局部 StateGraph
对于 DevOps 流水线:
- 整体用 Coordinator:PipelineManager 看全局,根据变更类型决定走哪些步骤
- Review 循环用 StateGraph:审查不通过 → 生成修改建议 → 重新审查(这个循环需要精确控制)
- 部署环节用 StateGraph:灰度 → 监控 → 全量(有条件分支和回滚)
16.4.2 如何决定用 Coordinator 还是 Swarm
Coordinator 适合: Swarm 适合:
───────────────────── ─────────────────
有明确的"老板"角色 Agent 之间是平等的
需要全局信息做决策 每个 Agent 有足够信息做局部决策
任务分配逻辑复杂 交接规则简单("做完给下一个")
需要动态调整计划 流程相对固定
容错需要中心协调 Agent 可以独立重试
16.4.3 完整设计文档模板
在竞赛中,一份好的架构设计文档既是你开发的指南针,也是评审时的加分项。以下是推荐的模板结构:
# 架构设计文档:DevOps 智能流水线
## 1. 系统概述
- 一句话描述系统功能
- 解决什么问题
- 面向什么用户
## 2. Agent 设计
### 2.1 Agent 列表
| Agent 名 | 职责 | 工具 | 关键约束 |
|----------|------|------|---------|
| PipelineManager | 全局调度 | notify, status_check | 不直接执行任何 CI/CD 操作 |
| CodeReviewer | 代码审查 | git_diff, ast_analyze, style_check | 必须给出通过/不通过的明确结论 |
| TestGenerator | 测试生成 | code_read, test_write, test_run | 测试必须能独立运行 |
| Deployer | 部署管理 | docker_build, k8s_deploy, health_check | 部署失败必须自动回滚 |
### 2.2 System Prompt 设计原则
- 每个 Agent 的 prompt 不超过 500 tokens
- 明确说"你不负责什么"(负面约束)
- 输出格式必须结构化(JSON/YAML)
## 3. 协作模式
- 整体:Coordinator 模式
- 局部:Review 循环用 StateGraph
- 并行:TestGenerator 和初步构建可以同时进行
## 4. 数据流
(画出数据流图)
## 5. 错误处理
- Agent 超时:30s 后中断,标记失败
- LLM 调用失败:指数退避重试 3 次
- 部署失败:自动回滚到上一个版本
## 6. 扩展计划
- Phase 1:核心三 Agent(Week 3-4)
- Phase 2:StateGraph 集成(Week 5-6)
- Phase 3:Plugin 系统(Week 7)
16.4.4 案例:DevOps 系统的架构设计全过程
让我们完整走一遍设计过程:
输入:一个 Git PR(Pull Request)
期望输出:审查意见 + 测试报告 + 部署状态
数据流:
PR Event → PipelineManager → [并行] CodeReviewer + TestGenerator
↓ ↓
Review Report Test Report
↓ ↓
PipelineManager (汇总)
↓
决策:部署?修改?拒绝?
↓
Deployer (如果批准)
↓
部署报告 + 通知
用 Go 代码表达这个架构骨架:
// internal/workflow/pipeline.go
package workflow
import (
"context"
"trpc.group/trpc-go/trpc-agent-go/team"
"trpc.group/trpc-go/trpc-agent-go/agent"
"github.com/your-team/devops-agent/internal/agents"
"github.com/your-team/devops-agent/internal/tools"
)
// BuildPipeline 构建完整的 DevOps 流水线 Team
func BuildPipeline() *team.Team {
// 创建各个专业 Agent
reviewer := agents.NewCodeReviewer()
testgen := agents.NewTestGenerator()
deployer := agents.NewDeployer()
// 创建协调者
coordinator := agents.NewPipelineManager()
// 组装 Team
pipeline := team.NewTeam(
team.WithMode(team.ModeCoordinator),
team.WithCoordinator(coordinator),
team.WithMembers(reviewer, testgen, deployer),
team.WithMaxRounds(10), // 最多 10 轮交互
)
return pipeline
}
16.5 实现核心 Agent
日常类比:培训新员工
实现一个 Agent 就像培训一个新员工。你需要:给他写岗位职责说明(system prompt)→ 教他使用工具(注册 tools)→ 让他试着做一次(测试运行)→ 根据表现调整培训内容(迭代优化)。
16.5.1 CodeReviewer Agent
设计目标:分析 PR 的代码变更,从风格、安全、性能、逻辑四个维度给出审查意见。
System Prompt 设计:
// internal/agents/reviewer.go
package agents
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"github.com/your-team/devops-agent/internal/tools"
)
const reviewerPrompt = `你是一位资深代码审查员。你的职责是审查 Pull Request 中的代码变更。
## 你的能力
- 分析代码差异(git diff)
- 检查代码风格是否符合团队规范
- 识别潜在的安全问题
- 发现性能瓶颈
- 验证逻辑正确性
## 审查维度(按优先级)
1. 安全:SQL注入、XSS、硬编码密钥、未验证的输入
2. 逻辑:边界条件、空指针、并发安全、资源泄漏
3. 性能:N+1查询、无必要的内存分配、缺少缓存
4. 风格:命名规范、代码组织、注释质量
## 输出格式
你必须以 JSON 格式输出审查结果:
{
"verdict": "approve" | "request_changes" | "reject",
"summary": "一句话总结",
"issues": [
{
"severity": "critical" | "major" | "minor" | "suggestion",
"file": "文件路径",
"line": 行号,
"description": "问题描述",
"suggestion": "修改建议"
}
],
"score": 0-100
}
## 重要约束
- 你只负责审查,不负责修改代码
- 没有 critical 或 major 问题时应该 approve
- 不要吹毛求疵——minor 问题不应阻止合入
- 如果代码变更太大(>500行),建议拆分 PR`
// NewCodeReviewer 创建代码审查 Agent
func NewCodeReviewer() *agent.Agent {
return agent.NewAgent(
agent.WithName("CodeReviewer"),
agent.WithSystemPrompt(reviewerPrompt),
agent.WithModel("gpt-4o"), // 审查需要强推理能力
agent.WithTools(
tools.GitDiff(), // 获取代码差异
tools.FileRead(), // 读取完整文件
tools.ASTAnalyze(), // 语法树分析
tools.StyleCheck(), // 风格检查
tools.SecurityScan(), // 安全扫描
),
agent.WithTemperature(0.1), // 审查需要确定性
)
}
工具实现——GitDiff:
// internal/tools/git.go
package tools
import (
"context"
"encoding/json"
"fmt"
"os/exec"
"strings"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
// GitDiff 创建获取 Git 差异的工具
func GitDiff() *tool.Tool {
return tool.NewTool(
tool.WithName("git_diff"),
tool.WithDescription("获取指定 PR 或 commit 的代码差异。返回 unified diff 格式的变更内容。"),
tool.WithParameters(tool.Parameters{
Type: "object",
Properties: map[string]tool.Property{
"base_branch": {
Type: "string",
Description: "基准分支,默认为 main",
},
"target_branch": {
Type: "string",
Description: "目标分支(PR 的 head branch)",
},
"file_filter": {
Type: "string",
Description: "文件过滤模式(如 '*.go'),可选",
},
},
Required: []string{"target_branch"},
}),
tool.WithHandler(handleGitDiff),
)
}
func handleGitDiff(ctx context.Context, params json.RawMessage) (string, error) {
var input struct {
BaseBranch string `json:"base_branch"`
TargetBranch string `json:"target_branch"`
FileFilter string `json:"file_filter"`
}
if err := json.Unmarshal(params, &input); err != nil {
return "", fmt.Errorf("参数解析失败: %w", err)
}
// 默认基准分支
if input.BaseBranch == "" {
input.BaseBranch = "main"
}
// 构建 git diff 命令
args := []string{"diff", input.BaseBranch + "..." + input.TargetBranch}
if input.FileFilter != "" {
args = append(args, "--", input.FileFilter)
}
cmd := exec.CommandContext(ctx, "git", args...)
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("git diff 执行失败: %w", err)
}
diff := string(output)
// 如果差异太大,截断并提示
if len(diff) > 10000 {
diff = diff[:10000] + "\n\n... [差异内容过长,已截断。请使用 file_filter 缩小范围]"
}
return diff, nil
}
工具实现——SecurityScan:
// internal/tools/security.go
package tools
import (
"context"
"encoding/json"
"fmt"
"regexp"
"strings"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
// SecurityScan 创建安全扫描工具
func SecurityScan() *tool.Tool {
return tool.NewTool(
tool.WithName("security_scan"),
tool.WithDescription("对代码进行安全扫描,检查常见安全问题如硬编码密钥、SQL注入风险等。"),
tool.WithParameters(tool.Parameters{
Type: "object",
Properties: map[string]tool.Property{
"code": {
Type: "string",
Description: "要扫描的代码内容",
},
"language": {
Type: "string",
Description: "编程语言(go/python/javascript)",
},
},
Required: []string{"code", "language"},
}),
tool.WithHandler(handleSecurityScan),
)
}
// 安全规则(简化示例)
var securityRules = []struct {
Name string
Pattern *regexp.Regexp
Severity string
Message string
}{
{
Name: "hardcoded_secret",
Pattern: regexp.MustCompile(`(?i)(password|secret|api_key|token)\s*[:=]\s*["']([^"']{8,})["']`),
Severity: "critical",
Message: "发现硬编码的敏感信息,应使用环境变量或密钥管理服务",
},
{
Name: "sql_injection",
Pattern: regexp.MustCompile(`(?i)(fmt\.Sprintf|string\.format).*?(SELECT|INSERT|UPDATE|DELETE)`),
Severity: "critical",
Message: "可能存在 SQL 注入风险,应使用参数化查询",
},
{
Name: "unsafe_deserialization",
Pattern: regexp.MustCompile(`(?i)json\.Unmarshal.*interface\{\}`),
Severity: "major",
Message: "反序列化到 interface{} 可能导致类型安全问题",
},
}
func handleSecurityScan(ctx context.Context, params json.RawMessage) (string, error) {
var input struct {
Code string `json:"code"`
Language string `json:"language"`
}
if err := json.Unmarshal(params, &input); err != nil {
return "", fmt.Errorf("参数解析失败: %w", err)
}
var findings []map[string]interface{}
lines := strings.Split(input.Code, "\n")
for _, rule := range securityRules {
for i, line := range lines {
if rule.Pattern.MatchString(line) {
findings = append(findings, map[string]interface{}{
"rule": rule.Name,
"severity": rule.Severity,
"line": i + 1,
"message": rule.Message,
"context": strings.TrimSpace(line),
})
}
}
}
result, _ := json.MarshalIndent(map[string]interface{}{
"total_findings": len(findings),
"findings": findings,
}, "", " ")
return string(result), nil
}
16.5.2 TestGenerator Agent
设计目标:根据代码变更自动生成对应的测试用例,并运行验证。
// internal/agents/testgen.go
package agents
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"github.com/your-team/devops-agent/internal/tools"
)
const testgenPrompt = `你是一位测试工程专家。你的职责是为代码变更生成高质量的测试用例。
## 你的能力
- 阅读源代码并理解其逻辑
- 设计覆盖率高的测试用例
- 编写可独立运行的测试代码
- 运行测试并分析结果
## 测试设计原则
1. 每个公开函数至少一个正常路径测试
2. 每个错误分支至少一个测试
3. 边界条件必须覆盖(空值、零值、极大值)
4. 并发场景如果适用必须测试
5. 使用 Table-Driven 风格组织 Go 测试
## 输出格式
生成测试后输出:
{
"test_file": "文件路径",
"test_count": 测试数量,
"coverage_estimate": "预估覆盖率",
"test_code": "完整测试代码"
}
## 重要约束
- 测试必须能独立运行(不依赖外部服务)
- 需要外部依赖时必须 mock
- 测试命名必须描述行为(TestUserLogin_WithInvalidPassword_ReturnsError)
- 不修改被测代码,只生成测试代码`
func NewTestGenerator() *agent.Agent {
return agent.NewAgent(
agent.WithName("TestGenerator"),
agent.WithSystemPrompt(testgenPrompt),
agent.WithModel("gpt-4o"),
agent.WithTools(
tools.FileRead(), // 读取源文件
tools.FileWrite(), // 写入测试文件
tools.RunTests(), // 执行测试
tools.Coverage(), // 获取覆盖率
),
agent.WithTemperature(0.3), // 略有创意但不要太跳
)
}
工具实现——RunTests:
// internal/tools/testing.go
package tools
import (
"context"
"encoding/json"
"fmt"
"os/exec"
"strings"
"time"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
// RunTests 创建测试运行工具
func RunTests() *tool.Tool {
return tool.NewTool(
tool.WithName("run_tests"),
tool.WithDescription("运行指定包或文件的 Go 测试,返回测试结果和覆盖率。"),
tool.WithParameters(tool.Parameters{
Type: "object",
Properties: map[string]tool.Property{
"package_path": {
Type: "string",
Description: "要测试的包路径(如 ./internal/service/...)",
},
"test_name": {
Type: "string",
Description: "指定运行的测试名(正则),可选",
},
"timeout": {
Type: "string",
Description: "超时时间(如 30s),默认 60s",
},
},
Required: []string{"package_path"},
}),
tool.WithHandler(handleRunTests),
)
}
func handleRunTests(ctx context.Context, params json.RawMessage) (string, error) {
var input struct {
PackagePath string `json:"package_path"`
TestName string `json:"test_name"`
Timeout string `json:"timeout"`
}
if err := json.Unmarshal(params, &input); err != nil {
return "", fmt.Errorf("参数解析失败: %w", err)
}
if input.Timeout == "" {
input.Timeout = "60s"
}
// 构建测试命令
args := []string{"test", "-v", "-cover", "-timeout", input.Timeout}
if input.TestName != "" {
args = append(args, "-run", input.TestName)
}
args = append(args, input.PackagePath)
// 设置命令超时
timeout, _ := time.ParseDuration(input.Timeout)
cmdCtx, cancel := context.WithTimeout(ctx, timeout+5*time.Second)
defer cancel()
cmd := exec.CommandContext(cmdCtx, "go", args...)
output, err := cmd.CombinedOutput()
// 解析结果
result := map[string]interface{}{
"command": "go " + strings.Join(args, " "),
"output": string(output),
"success": err == nil,
}
// 提取覆盖率
for _, line := range strings.Split(string(output), "\n") {
if strings.Contains(line, "coverage:") {
result["coverage"] = strings.TrimSpace(line)
break
}
}
resultJSON, _ := json.MarshalIndent(result, "", " ")
return string(resultJSON), nil
}
16.5.3 Deployer Agent
设计目标:管理从构建到部署到监控的全流程,确保安全上线。
// internal/agents/deployer.go
package agents
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"github.com/your-team/devops-agent/internal/tools"
)
const deployerPrompt = `你是一位部署工程专家。你的职责是安全地将代码变更部署到生产环境。
## 你的能力
- 构建 Docker 镜像
- 管理 Kubernetes 部署
- 执行健康检查
- 处理回滚
## 部署策略
1. 先灰度(5%流量),观察 5 分钟
2. 扩大灰度(25%),观察 5 分钟
3. 全量发布
4. 任何阶段异常指标 → 立即回滚
## 回滚条件(触发任一即回滚)
- 错误率 > 1%
- P99 延迟 > 基准的 2 倍
- 健康检查连续 3 次失败
- CPU/内存使用率异常飙升
## 输出格式
{
"action": "deploy" | "rollback" | "abort",
"stage": "build" | "canary" | "full",
"status": "success" | "failed" | "rolled_back",
"details": "详细说明",
"metrics": {
"error_rate": "错误率",
"latency_p99": "P99延迟",
"health_checks_passed": true/false
}
}
## 重要约束
- 安全第一:宁可回滚也不能带病上线
- 每一步都要有健康检查确认
- 部署失败必须自动回滚,不能停在中间状态
- 所有操作都必须有审计日志`
func NewDeployer() *agent.Agent {
return agent.NewAgent(
agent.WithName("Deployer"),
agent.WithSystemPrompt(deployerPrompt),
agent.WithModel("gpt-4o"),
agent.WithTools(
tools.DockerBuild(), // 构建镜像
tools.K8sDeploy(), // Kubernetes 部署
tools.HealthCheck(), // 健康检查
tools.MetricsQuery(), // 查询监控指标
tools.Rollback(), // 回滚
),
agent.WithTemperature(0.0), // 部署必须完全确定性
)
}
工具实现——HealthCheck:
// internal/tools/health.go
package tools
import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"
"trpc.group/trpc-go/trpc-agent-go/tool"
)
// HealthCheck 创建健康检查工具
func HealthCheck() *tool.Tool {
return tool.NewTool(
tool.WithName("health_check"),
tool.WithDescription("对目标服务执行健康检查,验证服务是否正常运行。"),
tool.WithParameters(tool.Parameters{
Type: "object",
Properties: map[string]tool.Property{
"endpoint": {
Type: "string",
Description: "健康检查的 URL(如 http://service:8080/health)",
},
"retries": {
Type: "integer",
Description: "重试次数,默认 3",
},
"interval_seconds": {
Type: "integer",
Description: "重试间隔秒数,默认 5",
},
},
Required: []string{"endpoint"},
}),
tool.WithHandler(handleHealthCheck),
)
}
func handleHealthCheck(ctx context.Context, params json.RawMessage) (string, error) {
var input struct {
Endpoint string `json:"endpoint"`
Retries int `json:"retries"`
IntervalSeconds int `json:"interval_seconds"`
}
if err := json.Unmarshal(params, &input); err != nil {
return "", fmt.Errorf("参数解析失败: %w", err)
}
if input.Retries <= 0 {
input.Retries = 3
}
if input.IntervalSeconds <= 0 {
input.IntervalSeconds = 5
}
client := &http.Client{Timeout: 10 * time.Second}
var lastErr error
for i := 0; i < input.Retries; i++ {
resp, err := client.Get(input.Endpoint)
if err != nil {
lastErr = err
time.Sleep(time.Duration(input.IntervalSeconds) * time.Second)
continue
}
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
result, _ := json.MarshalIndent(map[string]interface{}{
"healthy": true,
"status_code": resp.StatusCode,
"attempts": i + 1,
"endpoint": input.Endpoint,
}, "", " ")
return string(result), nil
}
lastErr = fmt.Errorf("HTTP %d", resp.StatusCode)
time.Sleep(time.Duration(input.IntervalSeconds) * time.Second)
}
result, _ := json.MarshalIndent(map[string]interface{}{
"healthy": false,
"error": lastErr.Error(),
"attempts": input.Retries,
"endpoint": input.Endpoint,
}, "", " ")
return string(result), nil
}
16.5.4 PipelineManager(协调者)
// internal/agents/coordinator.go
package agents
import (
"trpc.group/trpc-go/trpc-agent-go/agent"
"github.com/your-team/devops-agent/internal/tools"
)
const coordinatorPrompt = `你是 DevOps 流水线的总调度员。你负责分析代码变更,决定流水线的执行策略,并协调各专业 Agent 的工作。
## 你的团队
- CodeReviewer:负责代码审查
- TestGenerator:负责生成和运行测试
- Deployer:负责构建和部署
## 你的决策逻辑
### 变更类型判断
1. 文档变更(只改 .md/.txt)→ 只走 CodeReviewer(轻量审查)
2. 配置变更(改 .yaml/.env)→ CodeReviewer + Deployer(跳过测试)
3. 代码变更(改 .go/.py 等)→ 全流程
4. 紧急修复(hotfix 分支)→ 简化审查 + 快速部署
### 决策流程
1. 获取 PR 信息
2. 判断变更类型
3. 指派合适的 Agent 执行任务
4. 收集执行结果
5. 做出最终决策(部署/修改/拒绝)
## 输出格式
在每个决策点输出:
{
"decision": "你的决策",
"reasoning": "决策原因",
"next_action": "下一步动作",
"assigned_to": "指派给谁"
}
## 重要约束
- 你不直接执行代码审查、测试、部署——这是专业 Agent 的工作
- 你负责全局视角和决策
- 如果 CodeReviewer 给出 reject,流程立即终止
- 测试覆盖率 < 80% 不允许部署`
func NewPipelineManager() *agent.Agent {
return agent.NewAgent(
agent.WithName("PipelineManager"),
agent.WithSystemPrompt(coordinatorPrompt),
agent.WithModel("gpt-4o"),
agent.WithTools(
tools.GetPRInfo(), // 获取 PR 元信息
tools.Notify(), // 发送通知
),
agent.WithTemperature(0.1),
)
}
16.6 组装 Team 并调试
日常类比:乐队排练
单个乐器练得再好,乐队合奏时还是会出问题——节奏对不上、音量不平衡、接力时机把握不好。多 Agent 也一样:单独测试都正常,组合起来就可能出现”对话串了”、”工具冲突”、”死循环”等问题。
16.6.1 Team 创建
// internal/workflow/pipeline.go
package workflow
import (
"context"
"fmt"
"log"
"trpc.group/trpc-go/trpc-agent-go/team"
"trpc.group/trpc-go/trpc-agent-go/runner"
"github.com/your-team/devops-agent/internal/agents"
)
// DevOpsPipeline 封装完整的 DevOps 流水线
type DevOpsPipeline struct {
team *team.Team
runner *runner.Runner
}
// NewDevOpsPipeline 创建 DevOps 流水线
func NewDevOpsPipeline() *DevOpsPipeline {
// 1. 创建各 Agent
coordinator := agents.NewPipelineManager()
reviewer := agents.NewCodeReviewer()
testgen := agents.NewTestGenerator()
deployer := agents.NewDeployer()
// 2. 组装 Team
t := team.NewTeam(
team.WithName("DevOps Pipeline"),
team.WithMode(team.ModeCoordinator),
team.WithCoordinator(coordinator),
team.WithMembers(reviewer, testgen, deployer),
team.WithMaxRounds(15), // 最多 15 轮
team.WithTimeout(300), // 5 分钟超时
team.WithParallelExecution(true), // 允许并行
)
// 3. 创建 Runner
r := runner.NewRunner(
runner.WithAgent(t), // Team 也实现了 Agent 接口
)
return &DevOpsPipeline{
team: t,
runner: r,
}
}
// Execute 执行流水线
func (p *DevOpsPipeline) Execute(ctx context.Context, prInfo string) (*PipelineResult, error) {
prompt := fmt.Sprintf("请处理以下 Pull Request:\n%s", prInfo)
result, err := p.runner.Run(ctx, prompt)
if err != nil {
return nil, fmt.Errorf("流水线执行失败: %w", err)
}
// 解析结果
return parsePipelineResult(result), nil
}
16.6.2 集成测试
// tests/integration_test.go
package tests
import (
"context"
"testing"
"time"
"github.com/your-team/devops-agent/internal/workflow"
)
func TestPipeline_SimpleCodeChange(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
pipeline := workflow.NewDevOpsPipeline()
// 模拟一个简单的代码变更 PR
prInfo := `
PR #42: 添加用户登录日志
分支: feature/login-logging -> main
变更文件:
- internal/auth/login.go (+15, -2)
- internal/auth/login_test.go (+30, -0)
变更内容摘要:在登录成功/失败时记录日志,包含用户名和时间戳。
`
result, err := pipeline.Execute(ctx, prInfo)
if err != nil {
t.Fatalf("流水线执行失败: %v", err)
}
// 验证结果
if result == nil {
t.Fatal("结果不应为 nil")
}
// 验证所有 Agent 都被调用了
if !result.ReviewCompleted {
t.Error("代码审查未完成")
}
if !result.TestsGenerated {
t.Error("测试未生成")
}
t.Logf("流水线结果: %+v", result)
}
func TestPipeline_DocOnlyChange(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
pipeline := workflow.NewDevOpsPipeline()
// 只改文档——应该走轻量路径
prInfo := `
PR #43: 更新 README
分支: docs/update-readme -> main
变更文件:
- README.md (+5, -3)
- docs/api.md (+20, -0)
变更内容摘要:更新安装说明和 API 文档。
`
result, err := pipeline.Execute(ctx, prInfo)
if err != nil {
t.Fatalf("流水线执行失败: %v", err)
}
// 文档变更不应该触发部署
if result.DeployTriggered {
t.Error("文档变更不应触发部署")
}
}
16.6.3 调试多 Agent 交互
问题一:Agent 对话串了(一个 Agent 看到了另一个 Agent 的内部思考)
// 问题代码:所有 Agent 共享一个 session
t := team.NewTeam(
team.WithSharedMemory(true), // 这会导致对话混乱
)
// 修复:每个 Agent 独立 session,只通过 Coordinator 传递结构化结果
t := team.NewTeam(
team.WithSharedMemory(false), // 各自独立
team.WithMessageFilter(func(msg Message) bool {
// 只传递最终结论,不传内部推理过程
return msg.Role == "result"
}),
)
问题二:死循环(Review 不通过 → 建议修改 → 重新 Review → 还是不通过)
// 解决:添加循环计数器和退出条件
type ReviewState struct {
Attempts int
MaxAttempts int // = 3
}
func reviewDecision(state ReviewState) string {
if state.Attempts >= state.MaxAttempts {
return "escalate_to_human" // 超过最大次数,转人工
}
if state.ReviewPassed {
return "proceed_to_deploy"
}
return "retry_review" // 继续循环
}
问题三:超时(某个 Agent 的 LLM 调用卡住了)
// 解决:为每个 Agent 设置独立超时
reviewer := agent.NewAgent(
agent.WithName("CodeReviewer"),
agent.WithTimeout(60 * time.Second), // 单次 LLM 调用超时
agent.WithMaxRetries(2), // 重试 2 次
agent.WithRetryDelay(5 * time.Second), // 重试间隔
)
16.6.4 常见问题速查表
| 症状 | 可能原因 | 解决方案 |
|---|---|---|
| Agent A 调用了 Agent B 的工具 | 工具注册到了错误的 Agent | 检查 WithTools 绑定 |
| Coordinator 不分配任务 | system prompt 不够明确 | 加入”你必须把任务分配给团队成员” |
| 执行很慢 | 串行执行 | 开启 WithParallelExecution |
| 某个 Agent 重复执行 | 没有明确的”完成”信号 | 要求 Agent 输出特定结束标记 |
| Token 超限 | 历史消息太长 | 添加消息摘要/截断策略 |
16.7 加入 StateGraph 复杂工作流
日常类比:地铁换乘系统
Team 模式像出租车——告诉司机目的地,他来选路线。StateGraph 像地铁——你自己规划路线,在哪站换乘、哪站下车都由你精确控制。两者可以结合:大部分时候坐地铁(StateGraph 控制主流程),某些站出去打个车(调用 Agent 做灵活决策)。
16.7.1 哪些场景需要 StateGraph?
需要 StateGraph: Team 就够了:
──────────────── ──────────────
有明确的条件分支 "帮我做X"然后让 Agent 自己决定
需要循环重试(最多N次) 一次性任务
必须按特定顺序执行 顺序不重要
需要精确的错误恢复 简单的"失败就报错"
有并行分支汇合 简单的并行分配
需要中断和恢复(human-in-the-loop) 全自动不需要人介入
16.7.2 Review 循环的 StateGraph 实现
// internal/workflow/review_loop.go
package workflow
import (
"context"
"fmt"
"trpc.group/trpc-go/trpc-agent-go/stategraph"
)
// ReviewState 审查循环的状态
type ReviewState struct {
PRDiff string `sg:"type:string"`
ReviewResult string `sg:"type:string"`
Suggestions []string `sg:"type:list,reducer:append"`
Attempts int `sg:"type:int"`
MaxAttempts int `sg:"type:int"`
FinalVerdict string `sg:"type:string"`
}
// BuildReviewGraph 构建代码审查循环图
func BuildReviewGraph() *stategraph.CompiledGraph {
// 1. 创建图
graph := stategraph.NewStateGraph[ReviewState]()
// 2. 添加节点
graph.AddNode("review", reviewNode) // 执行审查
graph.AddNode("generate_fix", generateFixNode) // 生成修复建议
graph.AddNode("apply_fix", applyFixNode) // 应用修复
graph.AddNode("final_report", finalReportNode) // 生成最终报告
// 3. 设置入口
graph.SetEntryPoint("review")
// 4. 添加条件路由(关键!)
graph.AddConditionalEdge("review", reviewRouter)
// 5. 添加普通边
graph.AddEdge("generate_fix", "apply_fix")
graph.AddEdge("apply_fix", "review") // 回到审查(循环!)
graph.AddEdge("final_report", stategraph.END)
// 6. 编译
compiled, err := graph.Compile()
if err != nil {
panic(fmt.Sprintf("图编译失败: %v", err))
}
return compiled
}
// reviewRouter 条件路由:决定审查后走哪个分支
func reviewRouter(state ReviewState) string {
// 超过最大尝试次数 → 强制出报告
if state.Attempts >= state.MaxAttempts {
return "final_report"
}
// 审查通过 → 出报告
if state.FinalVerdict == "approve" {
return "final_report"
}
// 审查不通过但还有机会 → 生成修复
return "generate_fix"
}
// reviewNode 执行代码审查
func reviewNode(ctx context.Context, state ReviewState) (ReviewState, error) {
state.Attempts++
// 这里调用 CodeReviewer Agent 做实际审查
reviewer := agents.NewCodeReviewer()
result, err := reviewer.Run(ctx, fmt.Sprintf(
"请审查以下代码变更(第 %d 次审查):\n%s",
state.Attempts, state.PRDiff,
))
if err != nil {
return state, err
}
state.ReviewResult = result.Content
// 解析 verdict
state.FinalVerdict = parseVerdict(result.Content)
return state, nil
}
// generateFixNode 生成修复建议
func generateFixNode(ctx context.Context, state ReviewState) (ReviewState, error) {
// 基于审查意见生成修复建议
suggestion := fmt.Sprintf("基于第 %d 次审查的修复建议: ...", state.Attempts)
state.Suggestions = append(state.Suggestions, suggestion)
return state, nil
}
// applyFixNode 应用修复(模拟)
func applyFixNode(ctx context.Context, state ReviewState) (ReviewState, error) {
// 在真实场景中,这里会修改代码并更新 PRDiff
// Demo 中我们直接更新 diff 的描述
state.PRDiff = state.PRDiff + "\n[已应用修复建议]"
return state, nil
}
// finalReportNode 生成最终报告
func finalReportNode(ctx context.Context, state ReviewState) (ReviewState, error) {
// 汇总所有审查轮次的结果
return state, nil
}
16.7.3 完整流水线的 StateGraph 实现
// internal/workflow/full_pipeline.go
package workflow
import (
"context"
"fmt"
"trpc.group/trpc-go/trpc-agent-go/stategraph"
)
// PipelineState 完整流水线的状态
type PipelineState struct {
// 输入
PRInfo string `sg:"type:string"`
ChangeType string `sg:"type:string"` // code/docs/config/hotfix
// 中间状态
ReviewReport string `sg:"type:string"`
TestReport string `sg:"type:string"`
BuildStatus string `sg:"type:string"`
DeployStatus string `sg:"type:string"`
// 控制流
ReviewPassed bool `sg:"type:bool"`
TestsPassed bool `sg:"type:bool"`
ShouldDeploy bool `sg:"type:bool"`
// 输出
FinalReport string `sg:"type:string"`
}
// BuildFullPipeline 构建完整流水线图
func BuildFullPipeline() *stategraph.CompiledGraph {
graph := stategraph.NewStateGraph[PipelineState]()
// 节点
graph.AddNode("classify", classifyChangeNode) // 分类变更类型
graph.AddNode("review", reviewPipelineNode) // 代码审查
graph.AddNode("test", testPipelineNode) // 生成+运行测试
graph.AddNode("build", buildNode) // 构建镜像
graph.AddNode("deploy_canary", deployCanaryNode) // 灰度部署
graph.AddNode("monitor", monitorNode) // 监控验证
graph.AddNode("deploy_full", deployFullNode) // 全量部署
graph.AddNode("rollback", rollbackNode) // 回滚
graph.AddNode("report", generateReportNode) // 生成报告
// 入口
graph.SetEntryPoint("classify")
// 条件路由
graph.AddConditionalEdge("classify", func(s PipelineState) string {
switch s.ChangeType {
case "docs":
return "review" // 文档只需审查
case "hotfix":
return "build" // 紧急修复跳过审查
default:
return "review" // 正常流程
}
})
graph.AddConditionalEdge("review", func(s PipelineState) string {
if !s.ReviewPassed {
return "report" // 审查不通过,直接出报告
}
if s.ChangeType == "docs" {
return "report" // 文档变更审查通过就结束
}
return "test"
})
graph.AddConditionalEdge("test", func(s PipelineState) string {
if !s.TestsPassed {
return "report" // 测试不通过
}
return "build"
})
graph.AddEdge("build", "deploy_canary")
graph.AddConditionalEdge("monitor", func(s PipelineState) string {
if s.DeployStatus == "healthy" {
return "deploy_full"
}
return "rollback"
})
graph.AddEdge("deploy_canary", "monitor")
graph.AddEdge("deploy_full", "report")
graph.AddEdge("rollback", "report")
graph.AddEdge("report", stategraph.END)
compiled, _ := graph.Compile()
return compiled
}
// classifyChangeNode 分析 PR 判断变更类型
func classifyChangeNode(ctx context.Context, state PipelineState) (PipelineState, error) {
// 简化的分类逻辑
// 实际项目中会调用 PipelineManager Agent 做智能分类
if containsOnlyDocs(state.PRInfo) {
state.ChangeType = "docs"
} else if isHotfix(state.PRInfo) {
state.ChangeType = "hotfix"
} else if isConfigChange(state.PRInfo) {
state.ChangeType = "config"
} else {
state.ChangeType = "code"
}
return state, nil
}
// 其余节点函数的实现类似,各自调用对应的 Agent
// reviewPipelineNode 调用 CodeReviewer
// testPipelineNode 调用 TestGenerator
// buildNode 调用 DockerBuild 工具
// deployCanaryNode/deployFullNode 调用 Deployer
// monitorNode 调用 HealthCheck + MetricsQuery
// rollbackNode 调用 Rollback 工具
// generateReportNode 汇总所有结果
16.7.4 运行 StateGraph
// cmd/demo/main.go
package main
import (
"context"
"fmt"
"log"
"github.com/your-team/devops-agent/internal/workflow"
)
func main() {
// 构建图
pipeline := workflow.BuildFullPipeline()
// 初始状态
initialState := workflow.PipelineState{
PRInfo: `PR #100: 重构用户认证模块
分支: refactor/auth -> main
变更文件: internal/auth/*.go (8 files, +200, -150)`,
}
// 执行
ctx := context.Background()
finalState, err := pipeline.Invoke(ctx, initialState)
if err != nil {
log.Fatalf("流水线执行失败: %v", err)
}
fmt.Println("最终报告:")
fmt.Println(finalState.FinalReport)
}
16.8 生产化加固
日常类比:从毛坯房到精装修
前面的工作相当于把毛坯房盖好了——功能能用但还很粗糙。生产化加固就像精装修:加防盗门(安全)、装烟雾报警器(监控)、接自来水电力(可靠性)、装空调(性能)。
16.8.1 Plugin 集成
tRPC-Agent-Go 的 Plugin 系统让你可以在 Agent 执行的各个阶段插入自定义逻辑:
// internal/plugin/logger.go
package plugin
import (
"context"
"encoding/json"
"log"
"time"
agentplugin "trpc.group/trpc-go/trpc-agent-go/plugin"
)
// LoggerPlugin 日志插件——记录每一步的输入输出
type LoggerPlugin struct{}
func NewLoggerPlugin() *LoggerPlugin {
return &LoggerPlugin{}
}
// BeforeLLMCall 在 LLM 调用前执行
func (p *LoggerPlugin) BeforeLLMCall(ctx context.Context, req *agentplugin.LLMRequest) error {
log.Printf("[LLM请求] Agent=%s Model=%s Messages=%d Tools=%d",
req.AgentName,
req.Model,
len(req.Messages),
len(req.Tools),
)
return nil
}
// AfterLLMCall 在 LLM 调用后执行
func (p *LoggerPlugin) AfterLLMCall(ctx context.Context, resp *agentplugin.LLMResponse) error {
log.Printf("[LLM响应] Agent=%s Duration=%v TokensUsed=%d ToolCalls=%d",
resp.AgentName,
resp.Duration,
resp.TokensUsed,
len(resp.ToolCalls),
)
return nil
}
// BeforeToolCall 在工具调用前执行
func (p *LoggerPlugin) BeforeToolCall(ctx context.Context, call *agentplugin.ToolCall) error {
log.Printf("[工具调用] Agent=%s Tool=%s Params=%s",
call.AgentName,
call.ToolName,
string(call.Parameters),
)
return nil
}
// AfterToolCall 在工具调用后执行
func (p *LoggerPlugin) AfterToolCall(ctx context.Context, result *agentplugin.ToolResult) error {
log.Printf("[工具结果] Agent=%s Tool=%s Duration=%v Success=%v",
result.AgentName,
result.ToolName,
result.Duration,
result.Error == nil,
)
return nil
}
限流插件——防止 LLM API 被打爆:
// internal/plugin/ratelimit.go
package plugin
import (
"context"
"fmt"
"sync"
"time"
agentplugin "trpc.group/trpc-go/trpc-agent-go/plugin"
)
// RateLimitPlugin 限流插件
type RateLimitPlugin struct {
maxRPM int // 每分钟最大请求数
window time.Duration // 滑动窗口
requests []time.Time // 请求时间记录
mu sync.Mutex
}
func NewRateLimitPlugin(maxRPM int) *RateLimitPlugin {
return &RateLimitPlugin{
maxRPM: maxRPM,
window: time.Minute,
requests: make([]time.Time, 0),
}
}
func (p *RateLimitPlugin) BeforeLLMCall(ctx context.Context, req *agentplugin.LLMRequest) error {
p.mu.Lock()
defer p.mu.Unlock()
now := time.Now()
// 清理过期记录
cutoff := now.Add(-p.window)
valid := p.requests[:0]
for _, t := range p.requests {
if t.After(cutoff) {
valid = append(valid, t)
}
}
p.requests = valid
// 检查是否超限
if len(p.requests) >= p.maxRPM {
waitTime := p.requests[0].Add(p.window).Sub(now)
return fmt.Errorf("限流:每分钟最多 %d 次请求,请等待 %v", p.maxRPM, waitTime)
}
// 记录本次请求
p.requests = append(p.requests, now)
return nil
}
追踪插件——分布式追踪:
// internal/plugin/tracer.go
package plugin
import (
"context"
"fmt"
"time"
agentplugin "trpc.group/trpc-go/trpc-agent-go/plugin"
)
// TracerPlugin 追踪插件——记录完整的执行链路
type TracerPlugin struct {
spans []Span
}
type Span struct {
TraceID string
SpanID string
ParentID string
Operation string
Agent string
StartTime time.Time
Duration time.Duration
Status string
Tags map[string]string
}
func NewTracerPlugin() *TracerPlugin {
return &TracerPlugin{
spans: make([]Span, 0),
}
}
func (p *TracerPlugin) BeforeLLMCall(ctx context.Context, req *agentplugin.LLMRequest) error {
span := Span{
TraceID: getTraceID(ctx),
SpanID: generateSpanID(),
Operation: "llm_call",
Agent: req.AgentName,
StartTime: time.Now(),
Tags: map[string]string{
"model": req.Model,
"messages": fmt.Sprintf("%d", len(req.Messages)),
},
}
p.spans = append(p.spans, span)
return nil
}
// GetTimeline 获取完整执行时间线(用于调试和展示)
func (p *TracerPlugin) GetTimeline() []Span {
return p.spans
}
注册 Plugin:
// 在创建 Runner 时注册所有 Plugin
r := runner.NewRunner(
runner.WithAgent(pipeline),
runner.WithPlugins(
plugin.NewLoggerPlugin(),
plugin.NewRateLimitPlugin(60), // 每分钟最多 60 次 LLM 调用
plugin.NewTracerPlugin(),
),
)
16.8.2 Ralph Loop 验证
Ralph Loop 的核心思想:Agent 的每次输出都要经过独立的验证,确保正确性。
// internal/workflow/ralph.go
package workflow
import (
"context"
"encoding/json"
"fmt"
)
// RalphValidator 验证器接口
type RalphValidator interface {
Validate(ctx context.Context, output string) (*ValidationResult, error)
}
// ValidationResult 验证结果
type ValidationResult struct {
Valid bool `json:"valid"`
Confidence float64 `json:"confidence"` // 0.0 - 1.0
Issues []string `json:"issues"`
Suggestion string `json:"suggestion"`
}
// ReviewValidator 验证代码审查结果
type ReviewValidator struct{}
func (v *ReviewValidator) Validate(ctx context.Context, output string) (*ValidationResult, error) {
// 1. 检查输出格式是否正确(必须是有效 JSON)
var review map[string]interface{}
if err := json.Unmarshal([]byte(output), &review); err != nil {
return &ValidationResult{
Valid: false,
Confidence: 1.0,
Issues: []string{"输出格式不是有效 JSON"},
Suggestion: "请以 JSON 格式输出审查结果",
}, nil
}
// 2. 检查必要字段
requiredFields := []string{"verdict", "summary", "issues", "score"}
var missing []string
for _, field := range requiredFields {
if _, ok := review[field]; !ok {
missing = append(missing, field)
}
}
if len(missing) > 0 {
return &ValidationResult{
Valid: false,
Confidence: 1.0,
Issues: []string{fmt.Sprintf("缺少必要字段: %v", missing)},
Suggestion: "请确保输出包含 verdict/summary/issues/score 字段",
}, nil
}
// 3. 检查 verdict 值是否合法
verdict, _ := review["verdict"].(string)
validVerdicts := map[string]bool{"approve": true, "request_changes": true, "reject": true}
if !validVerdicts[verdict] {
return &ValidationResult{
Valid: false,
Confidence: 1.0,
Issues: []string{fmt.Sprintf("非法 verdict 值: %s", verdict)},
Suggestion: "verdict 必须是 approve/request_changes/reject 之一",
}, nil
}
// 4. 逻辑一致性检查
score, _ := review["score"].(float64)
if verdict == "approve" && score < 60 {
return &ValidationResult{
Valid: false,
Confidence: 0.8,
Issues: []string{"逻辑矛盾:verdict=approve 但 score<60"},
Suggestion: "分数较低时应该 request_changes",
}, nil
}
return &ValidationResult{
Valid: true,
Confidence: 0.95,
}, nil
}
// WithRalphLoop 包装 Agent 执行,加入 Ralph Loop 验证
func WithRalphLoop(agent AgentRunner, validator RalphValidator, maxRetries int) AgentRunner {
return &ralphWrappedAgent{
inner: agent,
validator: validator,
maxRetries: maxRetries,
}
}
type ralphWrappedAgent struct {
inner AgentRunner
validator RalphValidator
maxRetries int
}
func (a *ralphWrappedAgent) Run(ctx context.Context, input string) (*Result, error) {
for i := 0; i < a.maxRetries; i++ {
result, err := a.inner.Run(ctx, input)
if err != nil {
return nil, err
}
// 验证输出
validation, err := a.validator.Validate(ctx, result.Content)
if err != nil {
return nil, fmt.Errorf("验证失败: %w", err)
}
if validation.Valid {
return result, nil // 验证通过,返回结果
}
// 验证不通过,把验证反馈加入下一轮输入
input = fmt.Sprintf("%s\n\n[验证反馈] 你的上一次输出有问题:%v\n建议:%s\n请修正后重新输出。",
input,
validation.Issues,
validation.Suggestion,
)
}
return nil, fmt.Errorf("经过 %d 次重试仍未通过验证", a.maxRetries)
}
16.8.3 错误处理
// internal/config/errors.go
package config
import (
"errors"
"fmt"
"time"
)
// 自定义错误类型
var (
ErrLLMTimeout = errors.New("LLM 调用超时")
ErrLLMRateLimit = errors.New("LLM 请求被限流")
ErrToolFailed = errors.New("工具执行失败")
ErrValidationFailed = errors.New("输出验证失败")
ErrMaxRetries = errors.New("超过最大重试次数")
)
// RetryConfig 重试配置
type RetryConfig struct {
MaxRetries int
InitialDelay time.Duration
MaxDelay time.Duration
Multiplier float64 // 退避倍数
}
// DefaultRetryConfig 默认重试配置
var DefaultRetryConfig = RetryConfig{
MaxRetries: 3,
InitialDelay: 1 * time.Second,
MaxDelay: 30 * time.Second,
Multiplier: 2.0,
}
// RetryWithBackoff 指数退避重试
func RetryWithBackoff(config RetryConfig, fn func() error) error {
delay := config.InitialDelay
for i := 0; i < config.MaxRetries; i++ {
err := fn()
if err == nil {
return nil
}
// 判断是否可重试
if !isRetryable(err) {
return err
}
// 等待后重试
time.Sleep(delay)
delay = time.Duration(float64(delay) * config.Multiplier)
if delay > config.MaxDelay {
delay = config.MaxDelay
}
}
return fmt.Errorf("%w: 经过 %d 次重试后仍然失败", ErrMaxRetries, config.MaxRetries)
}
func isRetryable(err error) bool {
return errors.Is(err, ErrLLMTimeout) || errors.Is(err, ErrLLMRateLimit)
}
16.8.4 Session 持久化
// internal/storage/session.go
package storage
import (
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"sync"
"time"
)
// SessionStore 会话持久化存储
type SessionStore struct {
baseDir string
mu sync.RWMutex
}
// Session 会话数据
type Session struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Messages []Message `json:"messages"`
State map[string]interface{} `json:"state"`
}
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
Timestamp time.Time `json:"timestamp"`
Agent string `json:"agent,omitempty"`
}
func NewSessionStore(baseDir string) *SessionStore {
os.MkdirAll(baseDir, 0755)
return &SessionStore{baseDir: baseDir}
}
// Save 保存会话
func (s *SessionStore) Save(ctx context.Context, session *Session) error {
s.mu.Lock()
defer s.mu.Unlock()
session.UpdatedAt = time.Now()
data, err := json.MarshalIndent(session, "", " ")
if err != nil {
return fmt.Errorf("序列化失败: %w", err)
}
path := filepath.Join(s.baseDir, session.ID+".json")
return os.WriteFile(path, data, 0644)
}
// Load 加载会话
func (s *SessionStore) Load(ctx context.Context, sessionID string) (*Session, error) {
s.mu.RLock()
defer s.mu.RUnlock()
path := filepath.Join(s.baseDir, sessionID+".json")
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, fmt.Errorf("会话 %s 不存在", sessionID)
}
return nil, err
}
var session Session
if err := json.Unmarshal(data, &session); err != nil {
return nil, fmt.Errorf("反序列化失败: %w", err)
}
return &session, nil
}
16.9 竞赛文档与展示
日常类比:求职面试
你的代码再好,如果简历写得一塌糊涂、面试时表达不清,也很难拿到 offer。竞赛也是一样——评委只有有限的时间看你的项目,你必须在最短时间内让他们理解你做了什么、为什么重要、有什么创新。
16.9.1 README 写法
一个好的竞赛 README 应该遵循”倒金字塔”结构——最重要的信息放最前面:
# DevOps 智能流水线 🔧
> 基于 tRPC-Agent-Go 的多 Agent DevOps 自动化系统,让 AI 像资深 SRE 团队一样管理你的 CI/CD。
## 亮点(30秒能看完)
- **3 个专业 Agent + 1 个协调者** 组成完整 DevOps 团队
- **Ralph Loop 验证**:每次 Agent 输出都经过独立校验,拒绝幻觉
- **条件循环**:Code Review 不通过自动修复重试(最多3次)
- **灰度部署**:5% → 25% → 100% 渐进式发布,异常自动回滚
## 快速体验
```bash
# 克隆并安装
git clone https://github.com/your-team/devops-agent
cd devops-agent && go mod tidy
# 配置 API Key
export OPENAI_API_KEY="your-key"
# 运行 Demo
make demo
```
## 架构概览
```
PR Event → PipelineManager(协调者)
↓
┌───────────┼───────────┐
↓ ↓ ↓
CodeReviewer TestGen Deployer
↓ ↓ ↓
└───────────┼───────────┘
↓
StateGraph 编排
(条件路由 + 循环 + 灰度)
↓
最终报告 + 通知
```
## 技术栈
- 语言:Go 1.22
- Agent 框架:tRPC-Agent-Go
- LLM:GPT-4o (主) / GPT-4o-mini (辅)
- 图引擎:tRPC-Agent-Go StateGraph
- 验证:Ralph Loop 模式
## 详细文档
- [架构设计](docs/architecture.md)
- [API 文档](docs/api.md)
- [开发指南](docs/development.md)
- [Demo 演示](docs/demo.md)
16.9.2 技术文档结构
docs/
├── architecture.md # 架构设计(含决策原因)
│ ├── 系统概述
│ ├── Agent 设计(每个 Agent 的职责/prompt/工具)
│ ├── 协作模式选择(为什么用 Coordinator + StateGraph)
│ ├── 数据流图
│ ├── 错误处理策略
│ └── 扩展性设计
├── api.md # API 文档
│ ├── 接口列表
│ ├── 请求/响应格式
│ ├── 错误码
│ └── 使用示例
├── development.md # 开发指南
│ ├── 环境配置
│ ├── 目录结构说明
│ ├── 添加新 Agent 的步骤
│ ├── 添加新工具的步骤
│ └── 测试指南
└── demo.md # Demo 指南
├── 前置条件
├── 快速启动
├── Demo 场景说明
└── 预期输出
16.9.3 Demo 视频脚本
一个好的 Demo 视频结构(3-5 分钟):
[0:00 - 0:30] 开场
- 项目名称和一句话定位
- 解决什么问题
- "接下来我将演示完整的使用流程"
[0:30 - 1:30] 问题背景
- 展示"没有本系统时"的痛点:手动 Review → 忘记写测试 → 部署出错
- 数据支持:"平均每次人工 Review 需要 30 分钟,本系统只需 2 分钟"
[1:30 - 3:30] 核心演示
- 实际操作:提交一个 PR,触发流水线
- 展示每个 Agent 的工作过程(日志/面板)
- 重点展示创新点:
- Ralph Loop 如何捕获并修正 Agent 的错误输出
- Review 循环如何自动修复再审查
- 灰度部署如何自动回滚
[3:30 - 4:30] 技术亮点
- 架构图快速过一遍
- 强调与其他方案的区别
- 展示性能数据(执行时间/成本/准确率)
[4:30 - 5:00] 结尾
- 总结核心价值
- 未来展望
- "感谢观看,期待您的反馈"
16.9.4 评委视角——他们在看什么
根据历年竞赛评审经验,评委通常关注:
第一眼(10秒):README 第一屏
→ 看懂了吗?有意思吗?想继续看吗?
第二眼(1分钟):架构图 + 技术栈
→ 设计合理吗?有过度工程吗?有创新吗?
第三眼(3分钟):核心代码
→ 代码质量如何?命名清晰吗?有测试吗?
第四眼(5分钟):运行 Demo
→ 能跑起来吗?结果符合预期吗?响应快吗?
加分项:
- 有对比数据(对比人工/对比没有 Ralph Loop)
- 有错误处理(故意触发异常,展示优雅降级)
- 有监控面板(展示 Agent 执行链路)
- 代码注释清晰
16.10 常见陷阱与经验总结
日常类比:新手开车的十个坑
新手上路时犯的错误都很类似——起步忘松手刹、倒车不看后视镜、弯道不减速。竞赛新手也有典型的”十大坑”,避开它们就已经超过 60% 的队伍了。
陷阱一:选题太大
表现:”我们要做一个通用的 AI 操作系统!” 结果:第六周还在画饼,第八周交了个半成品。 解法:
- 核心功能 3 个以内
- 第二周必须有能跑的 Demo
- 宁可做一个方向做到极致,不要三个方向都浅尝辄止
陷阱二:为了多 Agent 而多 Agent
表现:一个 Agent 能做的事拆成三个,每个 Agent 只有一个工具。 结果:评委一眼看出是凑数的,反而扣分。 解法:
- 问自己:如果合成一个 Agent,context window 够用吗?
- 问自己:各 Agent 的 system prompt 有本质区别吗?
- 如果只有两个 Agent 是合理的,就用两个,不要为了”多”而拆
陷阱三:忽视 Prompt 工程
表现:System prompt 写了一句话——”你是一个 XX 助手”。 结果:Agent 输出不稳定、格式混乱、经常偏题。 解法:
- System prompt 是你最重要的代码——花 30% 的时间在上面
- 必须包含:角色定义、能力边界、输出格式、负面约束
- 迭代测试:同一个输入跑 10 次,看输出稳定性
陷阱四:没有错误处理
表现:Demo 时 LLM API 超时,整个系统崩溃。 结果:评委现场看到错误堆栈,印象大减。 解法:
- 每个外部调用都有 timeout + retry
- 优雅降级:LLM 挂了不意味着整个系统挂
- Demo 前跑一遍”混沌测试”——故意断网、故意让 LLM 返回垃圾
陷阱五:忽视成本控制
表现:每次请求都用 GPT-4,一个 Demo 跑下来花了 50 美金。 结果:评委问”这个成本可接受吗?”无法回答。 解法:
- 分级使用模型:简单判断用 mini,复杂推理用 4o
- 记录并展示成本:每次请求的 token 用量
- 添加缓存:相同的审查请求不重复调用 LLM
陷阱六:测试覆盖为零
表现:所有测试都是”人肉跑一遍看看”。 结果:改一个地方,另一个地方悄悄坏了,提交前发现不了。 解法:
- 工具函数必须有单元测试
- Agent 的输出必须有格式验证测试
- 集成测试至少覆盖”正常路径”和”一个异常路径”
陷阱七:没有版本控制纪律
表现:所有人在 main 分支直接推代码,互相覆盖。 结果:第五周有人误删了关键文件,无法恢复。 解法:
- 主分支保护:必须通过 PR 合入
- 每人一个 feature branch
- 每天 merge 一次 main 到自己的分支(避免大冲突)
陷阱八:Demo 临时准备
表现:提交前一天才开始录 Demo。 结果:Demo 中 API 超时、环境变量没配、各种意外。 解法:
- 第六周就开始准备 Demo 脚本
- Demo 用录屏 + 旁白,不要现场操作
- 准备”Plan B”:如果实时 API 不可用,用预录好的输出
陷阱九:文档事后补
表现:代码写完了才开始写文档。 结果:文档和代码不一致、遗漏关键信息、架构图过时。 解法:
- 架构设计文档先行——它是你的开发指南
- 每完成一个模块,立即更新对应文档
- README 每周更新一次
陷阱十:忽视”可演示性”
表现:系统功能很强,但 Demo 时只能看到一堆日志。 结果:评委看不到价值,分数上不去。 解法:
- 开发一个简单的 Web UI(或 TUI)
- 让 Agent 的思考和决策过程可视化
- 输出彩色日志、进度条、最终报告
时间管理策略
Week 1:选题 + 快速验证
├── Day 1-2:头脑风暴 + 调研
├── Day 3-4:确定方向 + 写设计文档
└── Day 5-7:跑通最小原型(Proof of Concept)
Week 2:架构定稿 + 骨架代码
├── Day 1-3:定义所有 Agent、工具、数据流
├── Day 4-5:搭好项目结构 + 第一个 Agent 跑通
└── Day 6-7:设计集成测试用例
Week 3-4:核心功能实现
├── 每个 Agent 独立开发和测试
├── 每完成一个 Agent 立即写单元测试
└── 每天跑一次全量测试,保持代码可用
Week 5-6:集成 + 复杂工作流
├── 组装 Team / StateGraph
├── 端到端测试
├── 调试多 Agent 交互问题
└── 开始写 Demo 脚本
Week 7:生产化加固
├── Plugin 集成(日志/限流/追踪)
├── 错误处理完善
├── 性能优化(并行/缓存)
└── 录制 Demo 视频初版
Week 8:文档 + 提交
├── Day 1-3:完善所有文档
├── Day 4-5:录制最终 Demo
├── Day 6:最终检查 + 提交
└── Day 7:准备答辩(如果有)
MVP 策略
MVP(Minimum Viable Product)不是”最烂的版本”,而是”用最少的代码证明你的想法可行”。
好的 MVP:
- 一个 Agent 能正确完成代码审查
- 审查结果格式正确、内容合理
- 有基本的错误处理
- 能在 30 秒内给出结果
不是 MVP 该做的:
- 精美的 Web UI
- 完善的权限系统
- 多种部署模式
- 国际化支持
MVP 验证清单:
□ 核心流程能跑通(正常路径)
□ 输出格式稳定(跑 10 次结果一致性 > 80%)
□ 响应时间可接受(< 30 秒)
□ 错误不会导致崩溃(至少不 panic)
□ 能给非技术人员演示清楚
完整可运行的 Demo 入口
把前面所有组件串起来,这是最终的 Demo 代码:
// cmd/demo/main.go
package main
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/your-team/devops-agent/internal/workflow"
"github.com/your-team/devops-agent/internal/plugin"
)
func main() {
// 检查环境变量
if os.Getenv("OPENAI_API_KEY") == "" {
log.Fatal("请设置 OPENAI_API_KEY 环境变量")
}
fmt.Println("========================================")
fmt.Println(" DevOps 智能流水线 Demo")
fmt.Println("========================================")
fmt.Println()
// 创建流水线
pipeline := workflow.NewDevOpsPipeline()
// 模拟 PR 输入
prInfo := `
PR #100: 重构用户认证模块
分支: refactor/auth -> main
作者: developer@example.com
变更文件:
- internal/auth/handler.go (+45, -20)
- internal/auth/middleware.go (+30, -10)
- internal/auth/token.go (+60, -0) [新文件]
- internal/auth/handler_test.go (+80, -0) [新文件]
变更摘要:
1. 将 JWT token 验证逻辑从 handler 抽取到独立的 token.go
2. 添加 token 刷新机制
3. 中间件支持配置化白名单路径
4. 新增单元测试覆盖 token 生成和验证逻辑
`
fmt.Println("[输入] PR 信息:")
fmt.Println(prInfo)
fmt.Println()
// 执行流水线
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
defer cancel()
fmt.Println("[执行] 流水线开始运行...")
fmt.Println()
startTime := time.Now()
result, err := pipeline.Execute(ctx, prInfo)
duration := time.Since(startTime)
if err != nil {
log.Fatalf("流水线执行失败: %v", err)
}
// 输出结果
fmt.Println("========================================")
fmt.Println(" 执行结果")
fmt.Println("========================================")
fmt.Printf("耗时: %v\n", duration)
fmt.Println()
resultJSON, _ := json.MarshalIndent(result, "", " ")
fmt.Println(string(resultJSON))
}
拓展阅读与资源
框架文档:
- tRPC-Agent-Go 官方文档(最权威的 API 参考)
- 本系列第 2-5 章(Agent/Team/StateGraph 深入理解)
- 第 11 章 Ralph Loop(可验证执行的详细理论)
竞赛策略:
- 历年犀牛鸟获奖项目的 README 和架构图(学习”怎么展示”)
- YC Startup School 的 Pitch 课程(学习”如何在 30 秒内卖出你的想法”)
工程实践:
- Google SRE Book 中的灰度部署章节
- The Pragmatic Programmer 中的”Tracer Bullets”概念
- Clean Architecture 中的依赖反转原则
思考题
第一题:选题评估
假设你的团队有 4 人(2 名后端、1 名前端、1 名 ML 方向),8 周时间。请评估以下选题的可行性并给出建议:
- “基于多 Agent 的智能法律咨询系统:能处理合同审查、纠纷分析、法规查询”
思考维度:多 Agent 必要性、复杂度、团队技术栈匹配度、评委友好度。如果你觉得需要调整,如何缩小范围使其可行?
第二题:架构决策
在数据分析场景中,有一个步骤是”验证 SQL 查询的安全性”(防止 DELETE/DROP 等危险操作)。请思考:
- 这个验证应该作为一个独立 Agent、一个工具(Tool)、还是一个 StateGraph 节点?
- 如果用户的自然语言请求中包含了”删除去年之前的数据”这样的合理需求,系统应该如何处理”安全”与”需求满足”之间的矛盾?
第三题:Ralph Loop 设计
为”翻译校对系统”中的 Translator Agent 设计一个 Ralph Loop 验证器。你的验证器需要检查什么?哪些属于”可自动验证的硬性规则”(如格式、长度),哪些属于”需要另一个 Agent 验证的软性规则”(如语义准确性)?请给出验证器的接口设计和关键检查项。
第四题:Coordinator vs Swarm 的灰色地带
一个智能客服系统中,当”技术支持 Agent”发现问题需要退款时,它需要把控制权交给”财务 Agent”。请分析:
- 在 Coordinator 模式下,这个场景如何实现?(技术 Agent 向 Coordinator 报告 → Coordinator 指派财务 Agent)
- 在 Swarm 模式下如何实现?(技术 Agent 直接 handoff 给财务 Agent)
- 哪种模式下,”技术 Agent 误判需要退款”(实际上只是配置问题)更容易被拦截?为什么?
第五题:生产化取舍
你的团队在第七周,面临以下取舍(只能选择做其中两项):
- A:添加分布式追踪(Jaeger 集成),让执行链路完全可视化
- B:实现 Session 持久化,支持中断后恢复
- C:添加 Web UI 面板,展示实时执行状态
- D:编写完整的集成测试套件(覆盖率 > 80%)
请说明你的选择和理由。考虑因素:对 Demo 的帮助程度、对代码质量的影响、实现难度、评委视角。
快速参照(竞赛速通者折叠查阅)
### 环境矩阵 | 环境 | 能做什么 | 不能做什么 / 注意 | |------|---------|-------------------| | 任意 OS + Go 1.21+ | Agent 开发、多 Agent 编排、工具插件、StateGraph、单元测试 | 无明显限制(纯 Go + LLM API) | | 无 LLM API Key | 代码阅读、架构分析、单元测试(mock LLM) | 端到端 Agent 运行、真实多轮对话测试 | | Docker(可选) | 集成测试环境、部署演示 | 非必需;本地 go run 即可开发 | ### 第一周行动清单 | 天 | 目标 | 产出 | |----|------|------| | Day 1-2 | 头脑风暴 + 调研可行方向 + 读 tRPC-Agent-Go 示例 | 3-5 个候选选题 | | Day 3-4 | 确定方向 + 写设计文档(含架构图 + 接口定义) | 设计文档 1 份 | | Day 5-7 | `go mod init` → 引入框架 → `OPENAI_API_KEY` → 跑通 HelloAgent → 最小 PoC | 可运行原型 | ### 竞赛评判速查 | 维度 | 权重 | 关键得分点 | |------|------|-----------| | 技术创新性 | 30% | 新颖协作模式 / 工具设计 / 非平凡 Agent 拓扑 | | 工程完备度 | 25% | 测试覆盖 / 错误处理 / 日志 / CI | | 实际应用价值 | 25% | 解决真实需求 / 不是学术玩具 | | 文档展示 | 20% | Demo 清晰 / README 完备 / 架构图 | ### 已知风险速查 | 风险 | 应对 | |------|------| | 选题太大(第五周还在改选题) | Week 1 必须锁定方向,Week 2 必须有 PoC | | 伪多 Agent(一个能搞定硬拆三个) | 用"去掉任何一个 Agent 系统是否退化"自检 | | 过度依赖 LLM(不做确定性验证) | 每个 Agent 输出加 Ralph Loop 验证 | | 成本失控(每次调 GPT-4) | 开发用 DeepSeek/本地模型;只在最终 Demo 用强模型 | | Demo 临时准备 | Week 6 起每周跑一次完整 Demo 录屏 | ### 读完本章你应能 - [ ] 从零设计一个多 Agent 系统的完整架构(含 Agent 职责划分 + 通信拓扑) - [ ] 用 tRPC-Agent-Go 实现 HelloAgent → 多 Agent 协作的完整代码路径 - [ ] 为你的方案选择合适的协作模式(Coordinator / Pipeline / Swarm)并说明理由 - [ ] 设计 Ralph Loop 验证器确保 Agent 输出质量 - [ ] 按评判维度优化你的提交(技术创新 30% / 工程完备 25% / 应用价值 25% / 展示 20%)上一章:第十五章 Agent 的 Prompt Engineering 实践
返回目录:Agent 框架深度导读系列