犀牛鸟 2026 研究笔记 estelledc.github.io

第十六章:竞赛实战——从零搭建多 Agent 系统

本章目标:把前十五章学到的所有概念整合为一个完整的竞赛项目。从选题、架构设计、代码实现、调试、生产化加固到最终文档与展示,走完”从零到提交”的全流程。


16.1 腾讯犀牛鸟 2026 Agent 赛道概述

日常类比:一场有主题的烹饪比赛

想象你参加了一个烹饪比赛。比赛不只是”做一道菜”这么简单——它有特定的主题(比如”融合菜”)、评判维度(色香味+创新+实用)、时间限制(两小时)、以及展示环节(给评委讲解你的思路)。你不能只会做菜,还要会选菜、会规划时间、会讲故事。

Agent 赛道也一样:它不只是”写一个 Agent”,而是一个完整的工程竞赛,考验你的选题眼光、架构能力、编码实力和表达功力。

赛道背景

腾讯犀牛鸟开源人才培养计划是腾讯与高校合作的年度项目。2026 年的 Agent 赛道聚焦于:

  1. 多 Agent 协作系统:不是写一个简单的 chatbot,而是让多个 Agent 分工合作解决复杂问题
  2. 基于开源框架:使用 tRPC-Agent-Go(或其他指定框架)进行开发
  3. 实际应用场景:解决方案必须面向真实需求,而非纯学术玩具
  4. 工程质量:代码质量、测试覆盖、文档完备度都会被评审

评判维度

评判维度         权重    含义
─────────────────────────────────────────────────────
技术创新性       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 就能搞定,硬拆成三个互相传话。好的选题应该满足:

原则二:复杂度可控(MVP 优先)

8 周时间 = 大约 320 小时有效工作时间(4人团队,每人每周约 10 小时)。你需要:

原则三:创新点可解释

“我用了 tRPC-Agent-Go”不是创新。创新必须是:

原则四:评委视角(30 秒内能理解价值)

评委一天看几十个项目。如果看完你的 README 前三段还不明白你在做什么、为什么重要、有什么创新——基本就没戏了。选题时就要想好”一句话卖点”。

六个候选方向详细分析

方向一:DevOps 智能流水线

一句话定位:让 AI 像高级 SRE 一样管理从代码提交到上线的全流程。

协作模式

CodeReviewer Agent ──→ TestGenerator Agent ──→ Deployer Agent
   │                        │                       │
   ├─ 读代码差异            ├─ 生成测试用例          ├─ 编排部署脚本
   ├─ 检查风格/安全/性能    ├─ 运行并验证覆盖率      ├─ 灰度/回滚决策
   └─ 输出 review 意见     └─ 输出测试报告          └─ 输出部署状态

CoordinatorPipelineManager,它根据代码变更的范围和风险等级决定走哪条路径:小改动可能跳过部分环节,大改动则走完全流程。

复杂度:中高。需要对接 Git API、CI 系统、容器编排。但核心逻辑清晰。

创新点

适合团队:有后端开发经验,了解 CI/CD 概念。

评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★★★ / 实用价值 ★★★★★ / 文档展示 ★★★★☆

方向二:多语言翻译校对系统

一句话定位:让 AI 像专业翻译团队一样,初译、校对、润色三步分工,保证翻译质量。

协作模式

Translator Agent ──→ Reviewer Agent ──→ Polisher Agent
   │                      │                    │
   ├─ 源语言→目标语言    ├─ 对照原文检查      ├─ 优化表达流畅度
   ├─ 保持术语一致       ├─ 标注误译/漏译     ├─ 统一全文风格
   └─ 输出初译稿        └─ 输出修改建议      └─ 输出终稿

使用 Swarm 模式 更合适——Reviewer 发现严重误译时,可以把控制权交还 Translator 重译该段落,而不需要通过中心调度。

复杂度:中。不需要复杂的外部系统集成,重点在 prompt 工程和质量验证。

创新点

适合团队:对 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 执行沙箱、图表生成。

创新点

适合团队:有数据分析/BI 背景,熟悉 SQL。

评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★★☆ / 实用价值 ★★★★★ / 文档展示 ★★★★☆

方向四:智能客服系统

一句话定位:让 AI 像客服团队一样,分诊、解答、升级、回访全流程协作。

协作模式

Triage Agent ──→ FAQ Agent / Technical Agent / Escalation Agent
     │                │              │                  │
     ├─ 理解用户意图  ├─ 查知识库    ├─ 排查技术问题    ├─ 转人工+摘要
     ├─ 分类问题类型  ├─ 回答常见问  ├─ 调用诊断工具    ├─ 记录工单
     └─ 路由到对应Agent└─ 确认满意度 └─ 给出修复方案   └─ 通知相关人

使用 Swarm 模式:Triage 判断类型后直接 handoff 给对应 Agent,如果该 Agent 发现问题超出能力范围,再 handoff 给 Escalation。

复杂度:中。知识库检索是成熟技术(RAG),重点在路由准确性和用户体验。

创新点

适合团队:有产品思维,关注用户体验。

评分预估:技术创新 ★★★☆☆ / 工程完备度 ★★★★☆ / 实用价值 ★★★★★ / 文档展示 ★★★★★

方向五:智能测试生成系统

一句话定位:让 AI 像 QA 团队一样,分析需求、设计用例、生成代码、执行验证。

协作模式

SpecAnalyzer ──→ TestDesigner ──→ CodeGenerator ──→ Executor
      │                │               │               │
      ├─ 解析需求文档  ├─ 设计测试策略  ├─ 生成测试代码  ├─ 运行测试
      ├─ 识别边界条件  ├─ 覆盖率规划   ├─ Mock 数据生成  ├─ 收集结果
      └─ 输出测试范围 └─ 输出用例列表  └─ 输出代码文件  └─ 输出报告

StateGraph 驱动:执行失败 → 分析失败原因 → 修复测试代码 → 重新执行(循环最多 3 次)。

复杂度:高。需要代码分析能力、安全执行环境、多语言支持。

创新点

适合团队:有测试工程经验,熟悉 AST/代码分析。

评分预估:技术创新 ★★★★★ / 工程完备度 ★★★★☆ / 实用价值 ★★★★☆ / 文档展示 ★★★☆☆

方向六:知识库自动维护系统

一句话定位:让 AI 像文档管理团队一样,监控变更、更新文档、检查一致性、生成索引。

协作模式

ChangeDetector ──→ DocUpdater ──→ ConsistencyChecker ──→ Indexer
      │                │                  │                  │
      ├─ 监控代码变更  ├─ 更新相关文档    ├─ 检查交叉引用   ├─ 重建搜索索引
      ├─ 识别文档影响  ├─ 生成变更摘要    ├─ 标注过时内容   ├─ 更新导航结构
      └─ 输出影响列表 └─ 输出更新PR      └─ 输出问题报告  └─ 输出索引文件

Coordinator 按事件触发:代码合并 → 启动 ChangeDetector → 根据影响范围调度后续 Agent。

复杂度:中。文件操作为主,但一致性检查逻辑复杂。

创新点

适合团队:有文档工程/知识管理兴趣,注重细节。

评分预估:技术创新 ★★★★☆ / 工程完备度 ★★★☆☆ / 实用价值 ★★★★☆ / 文档展示 ★★★★★

选题决策矩阵

方向          多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                   # 项目说明

命名规范要点

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 流水线:

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 操作系统!” 结果:第六周还在画饼,第八周交了个半成品。 解法

陷阱二:为了多 Agent 而多 Agent

表现:一个 Agent 能做的事拆成三个,每个 Agent 只有一个工具。 结果:评委一眼看出是凑数的,反而扣分。 解法

陷阱三:忽视 Prompt 工程

表现:System prompt 写了一句话——”你是一个 XX 助手”。 结果:Agent 输出不稳定、格式混乱、经常偏题。 解法

陷阱四:没有错误处理

表现:Demo 时 LLM API 超时,整个系统崩溃。 结果:评委现场看到错误堆栈,印象大减。 解法

陷阱五:忽视成本控制

表现:每次请求都用 GPT-4,一个 Demo 跑下来花了 50 美金。 结果:评委问”这个成本可接受吗?”无法回答。 解法

陷阱六:测试覆盖为零

表现:所有测试都是”人肉跑一遍看看”。 结果:改一个地方,另一个地方悄悄坏了,提交前发现不了。 解法

陷阱七:没有版本控制纪律

表现:所有人在 main 分支直接推代码,互相覆盖。 结果:第五周有人误删了关键文件,无法恢复。 解法

陷阱八:Demo 临时准备

表现:提交前一天才开始录 Demo。 结果:Demo 中 API 超时、环境变量没配、各种意外。 解法

陷阱九:文档事后补

表现:代码写完了才开始写文档。 结果:文档和代码不一致、遗漏关键信息、架构图过时。 解法

陷阱十:忽视”可演示性”

表现:系统功能很强,但 Demo 时只能看到一堆日志。 结果:评委看不到价值,分数上不去。 解法

时间管理策略

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))
}

拓展阅读与资源

框架文档

竞赛策略

工程实践


思考题

第一题:选题评估

假设你的团队有 4 人(2 名后端、1 名前端、1 名 ML 方向),8 周时间。请评估以下选题的可行性并给出建议:

思考维度:多 Agent 必要性、复杂度、团队技术栈匹配度、评委友好度。如果你觉得需要调整,如何缩小范围使其可行?

第二题:架构决策

在数据分析场景中,有一个步骤是”验证 SQL 查询的安全性”(防止 DELETE/DROP 等危险操作)。请思考:

第三题:Ralph Loop 设计

为”翻译校对系统”中的 Translator Agent 设计一个 Ralph Loop 验证器。你的验证器需要检查什么?哪些属于”可自动验证的硬性规则”(如格式、长度),哪些属于”需要另一个 Agent 验证的软性规则”(如语义准确性)?请给出验证器的接口设计和关键检查项。

第四题:Coordinator vs Swarm 的灰色地带

一个智能客服系统中,当”技术支持 Agent”发现问题需要退款时,它需要把控制权交给”财务 Agent”。请分析:

第五题:生产化取舍

你的团队在第七周,面临以下取舍(只能选择做其中两项):

请说明你的选择和理由。考虑因素:对 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 框架深度导读系列