第10讲(最终讲):毕业项目——构建一个生产级的 Agent 系统

第10讲(最终讲):毕业项目——构建一个生产级的 Agent 系统 一、项目概述前面 9 讲我们从 Agent 的原理讲到沙箱安全从评估方法讲到生产化部署。现在是时候把所有知识串联起来了。项目目标构建一个名为CodeReviewBot​ 的生产级 Agent 系统它可以接收 GitHub PR 链接或代码片段自动分析代码质量、安全漏洞、性能问题生成结构化的 Review 报告支持在 PR 上直接发表评论具备完整的监控、安全、审计能力二、系统架构┌──────────────────────┐ │ GitHub Webhook │ └──────────┬───────────┘ │ ┌──────────▼───────────┐ │ API Gateway │ │ (限流/鉴权/路由) │ └──────────┬───────────┘ │ ┌──────────────────┼──────────────────┐ │ │ │ ┌────────▼────────┐ ┌──────▼───────┐ ┌───────▼────────┐ │ Agent Service │ │ Auth Service│ │ Webhook Svc │ │ (核心编排) │ │ (用户认证) │ │ (事件处理) │ └────────┬────────┘ └──────────────┘ └────────────────┘ │ ┌────────┼────────┐ │ │ │ ┌────▼───┐ ┌──▼───┐ ┌─▼────┐ │LLM │ │Tool │ │Memory│ │Proxy │ │Registry│ │Store │ └────────┘ └──┬───┘ └──────┘ │ ┌────────┼────────┐ │ │ │ ┌────▼───┐ ┌──▼───┐ ┌─▼────┐ │GitHub │ │Static│ │Sand- │ │API Tool│ │Analyz│ │box │ └────────┘ └──────┘ └──────┘ │ ┌────────┼────────┐ │ │ │ ┌────▼───┐ ┌──▼───┐ ┌─▼────┐ │Redis │ │Post- │ │Object│ │(缓存) │ │greSQL│ │Store │ └────────┘ └──────┘ └──────┘三、核心代码实现3.1 项目结构codereview-bot/ ├── cmd/ │ └── server/ │ └── main.go # 入口 ├── internal/ │ ├── agent/ │ │ ├── agent.go # Agent 核心 │ │ ├── orchestrator.go # 任务编排 │ │ └── planner.go # 计划生成 │ ├── tools/ │ │ ├── github.go # GitHub API 工具 │ │ ├── analyzer.go # 代码分析工具 │ │ ├── security_scanner.go # 安全扫描工具 │ │ └── reporter.go # 报告生成工具 │ ├── sandbox/ │ │ ├── docker.go # Docker 沙箱 │ │ └── wasm.go # WASM 沙箱 │ ├── security/ │ │ ├── sanitizer.go # 输入净化 │ │ ├── validator.go # 工具调用验证 │ │ ├── auditor.go # 审计日志 │ │ └── detector.go # 异常检测 │ ├── store/ │ │ ├── memory.go # 记忆存储 │ │ └── cache.go # 缓存 │ ├── monitor/ │ │ ├── metrics.go # 指标采集 │ │ └── alert.go # 告警 │ └── api/ │ ├── handler.go # HTTP 处理器 │ └── middleware.go # 中间件 ├── config/ │ └── config.yaml # 配置文件 ├── deploy/ │ ├── Dockerfile │ ├── k8s/ │ │ ├── deployment.yaml │ │ ├── service.yaml │ │ └── hpa.yaml │ └── prometheus/ │ └── rules.yml ├── tests/ │ ├── integration/ │ └── e2e/ └── docs/ └── architecture.md3.2 Agent 核心// internal/agent/agent.go package agent type CodeReviewAgent struct { llm LLMClient tools *ToolRegistry planner *Planner orchestrator *Orchestrator memory *MemoryStore security *SecurityLayer monitor *Monitor } func NewCodeReviewAgent(cfg *Config) *CodeReviewAgent { return CodeReviewAgent{ llm: NewLLMClient(cfg.LLM), tools: NewToolRegistry(), planner: NewPlanner(), orchestrator: NewOrchestrator(), memory: NewMemoryStore(cfg.Redis), security: NewSecurityLayer(cfg.Security), monitor: NewMonitor(cfg.Monitoring), } } func (a *CodeReviewAgent) ReviewPR(ctx context.Context, prURL string) (*ReviewReport, error) { start : time.Now() a.monitor.IncActiveReviews() defer a.monitor.DecActiveReviews() // 1. 输入净化 cleanURL, err : a.security.SanitizeInput(prURL) if err ! nil { a.monitor.RecordFailure(sanitization) return nil, fmt.Errorf(输入验证失败: %w, err) } // 2. 检查缓存 if cached, ok : a.memory.GetCachedReview(cleanURL); ok { a.monitor.RecordCacheHit() return cached, nil } // 3. 生成审查计划 plan, err : a.planner.CreatePlan(ctx, cleanURL) if err ! nil { return nil, fmt.Errorf(计划生成失败: %w, err) } // 4. 编排执行 result, err : a.orchestrator.Execute(ctx, plan) if err ! nil { a.monitor.RecordFailure(execution) return nil, fmt.Errorf(执行失败: %w, err) } // 5. 生成报告 report, err : a.generateReport(ctx, result) if err ! nil { return nil, fmt.Errorf(报告生成失败: %w, err) } // 6. 缓存结果 a.memory.CacheReview(cleanURL, report, 1*time.Hour) // 7. 记录指标 a.monitor.RecordReview(time.Since(start), report.IssueCount) a.security.Audit(review_completed, map[string]interface{}{ url: cleanURL, issues: report.IssueCount, duration: time.Since(start), }) return report, nil }3.3 工具注册表// internal/tools/registry.go type ToolRegistry struct { tools map[string]Tool mu sync.RWMutex } type Tool interface { Name() string Description() string Parameters() []Parameter Execute(ctx context.Context, args map[string]interface{}) (interface{}, error) Validate(args map[string]interface{}) error } type Parameter struct { Name string Type string Required bool Description string Example string } func NewToolRegistry() *ToolRegistry { tr : ToolRegistry{ tools: make(map[string]Tool), } // 注册工具 tr.Register(GitHubTool{}) tr.Register(StaticAnalyzer{}) tr.Register(SecurityScanner{}) tr.Register(Reporter{}) return tr } // GitHub 工具 type GitHubTool struct { client *github.Client } func (g *GitHubTool) Name() string { return github_api } func (g *GitHubTool) Description() string { return 访问 GitHub API获取 PR 详情、文件变更、提交历史 } func (g *GitHubTool) Parameters() []Parameter { return []Parameter{ {Name: action, Type: string, Required: true, Description: 操作类型: get_pr, get_files, get_diff, post_comment}, {Name: owner, Type: string, Required: true, Description: 仓库所有者}, {Name: repo, Type: string, Required: true, Description: 仓库名}, {Name: pr_number, Type: integer, Required: true, Description: PR 编号}, } } func (g *GitHubTool) Execute(ctx context.Context, args map[string]interface{}) (interface{}, error) { action : args[action].(string) owner : args[owner].(string) repo : args[repo].(string) prNumber : args[pr_number].(int) switch action { case get_pr: return g.client.PullRequests.Get(ctx, owner, repo, prNumber) case get_files: return g.client.PullRequests.ListFiles(ctx, owner, repo, prNumber, nil) case post_comment: comment : args[comment].(string) return g.postComment(ctx, owner, repo, prNumber, comment) default: return nil, fmt.Errorf(未知操作: %s, action) } } func (g *GitHubTool) Validate(args map[string]interface{}) error { // 安全检查不允许修改仓库设置 if action, ok : args[action].(string); ok { forbidden : []string{delete, merge, close, update_branch} for _, f : range forbidden { if strings.Contains(action, f) { return fmt.Errorf(操作 %s 被安全策略禁止, action) } } } return nil }3.4 编排引擎// internal/agent/orchestrator.go type Orchestrator struct { maxSteps int timeout time.Duration retryCount int } type ExecutionPlan struct { Steps []Step } type Step struct { ID string Tool string Args map[string]interface{} DependsOn []string RetryOnFail bool } type ExecutionResult struct { StepID string Output interface{} Error string Duration time.Duration TokenUsed int } func (o *Orchestrator) Execute(ctx context.Context, plan *ExecutionPlan) (*AggregatedResult, error) { results : make(map[string]*ExecutionResult) completed : make(map[string]bool) for stepIndex : 0; stepIndex len(plan.Steps); stepIndex { select { case -ctx.Done(): return nil, ctx.Err() default: } step : plan.Steps[stepIndex] // 检查依赖是否完成 depsMet : true for _, dep : range step.DependsOn { if !completed[dep] { depsMet false break } } if !depsMet { continue // 依赖未满足跳过本轮 } // 执行步骤带重试 var result *ExecutionResult var err error for attempt : 0; attempt o.retryCount; attempt { result, err o.executeStep(ctx, step) if err nil || !step.RetryOnFail { break } time.Sleep(time.Duration(math.Pow(2, float64(attempt))) * 100 * time.Millisecond) } results[step.ID] result completed[step.ID] (err nil) if err ! nil !step.RetryOnFail { return nil, fmt.Errorf(步骤 %s 失败: %w, step.ID, err) } } return o.aggregate(results), nil }3.5 安全层// internal/security/layer.go type SecurityLayer struct { sanitizer *InputSanitizer validator *ToolValidator auditor *Auditor detector *AnomalyDetector breaker *CircuitBreaker } func NewSecurityLayer(cfg SecurityConfig) *SecurityLayer { return SecurityLayer{ sanitizer: NewInputSanitizer(cfg.Sanitizer), validator: NewToolValidator(cfg.Validator), auditor: NewAuditor(cfg.Audit), detector: NewAnomalyDetector(cfg.Detection), breaker: NewCircuitBreaker(cfg.CircuitBreaker), } } func (sl *SecurityLayer) ValidateToolCall(toolName string, args map[string]interface{}) error { // 1. 熔断检查 if err : sl.breaker.Check(); err ! nil { return err } // 2. 工具调用验证 if err : sl.validator.Validate(toolName, args); err ! nil { sl.auditor.Record(tool_validation_failed, map[string]interface{}{ tool: toolName, args: args, error: err.Error(), }) return err } // 3. 异常检测 if score : sl.detector.Score(toolName, args); score 0.8 { sl.breaker.RecordFailure() sl.auditor.Record(anomaly_detected, map[string]interface{}{ tool: toolName, score: score, }) return fmt.Errorf(异常行为检测到操作已阻止) } return nil }3.6 API 处理器// internal/api/handler.go type Handler struct { agent *CodeReviewAgent security *SecurityLayer monitor *Monitor } func (h *Handler) HandleReview(w http.ResponseWriter, r *http.Request) { start : time.Now() // 1. 解析请求 var req ReviewRequest if err : json.NewDecoder(r.Body).Decode(req); err ! nil { http.Error(w, invalid request, http.StatusBadRequest) return } // 2. 用户认证 userID, err : h.authenticate(r) if err ! nil { http.Error(w, unauthorized, http.StatusUnauthorized) return } // 3. 限流检查 if !h.monitor.CheckRateLimit(userID) { http.Error(w, rate limit exceeded, http.StatusTooManyRequests) return } // 4. 执行审查 ctx, cancel : context.WithTimeout(r.Context(), 5*time.Minute) defer cancel() report, err : h.agent.ReviewPR(ctx, req.PRURL) if err ! nil { h.monitor.RecordError(userID, err) http.Error(w, err.Error(), http.StatusInternalServerError) return } // 5. 返回结果 resp : ReviewResponse{ Report: report, Duration: time.Since(start).String(), RequestID: r.Header.Get(X-Request-ID), } w.Header().Set(Content-Type, application/json) json.NewEncoder(w).Encode(resp) }四、部署配置4.1 DockerfileFROM golang:1.22-alpine AS builder RUN apk add --no-cache git WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY . . RUN CGO_ENABLED0 GOOSlinux go build -o codereview-bot ./cmd/server FROM alpine:3.19 RUN apk add --no-cache ca-certificates docker-cli WORKDIR /app COPY --frombuilder /app/codereview-bot . COPY config/config.yaml ./config/ EXPOSE 8080 HEALTHCHECK --interval30s --timeout5s --retries3 \ CMD wget -qO- http://localhost:8080/health || exit 1 ENTRYPOINT [./codereview-bot]4.2 K8s 部署apiVersion: apps/v1 kind: Deployment metadata: name: codereview-bot namespace: agent-system spec: replicas: 3 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 0 selector: matchLabels: app: codereview-bot template: metadata: labels: app: codereview-bot spec: containers: - name: agent image: registry.example.com/codereview-bot:v1.0.0 ports: - containerPort: 8080 env: - name: GITHUB_TOKEN valueFrom: secretKeyRef: name: agent-secrets key: github-token - name: LLM_API_KEY valueFrom: secretKeyRef: name: agent-secrets key: llm-api-key - name: REDIS_ADDR value: redis-cluster:6379 - name: DATABASE_URL valueFrom: secretKeyRef: name: agent-secrets key: database-url resources: requests: memory: 512Mi cpu: 500m limits: memory: 2Gi cpu: 2 volumeMounts: - name: docker-socket mountPath: /var/run/docker.sock readOnly: true - name: config mountPath: /app/config readOnly: true livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 30 periodSeconds: 15 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 volumes: - name: docker-socket hostPath: path: /var/run/docker.sock - name: config configMap: name: agent-config --- apiVersion: v1 kind: Service metadata: name: codereview-bot namespace: agent-system spec: selector: app: codereview-bot ports: - port: 80 targetPort: 8080 type: ClusterIP4.3 监控与告警# prometheus/rules.yml groups: - name: agent_alerts rules: - alert: HighErrorRate expr: rate(agent_errors_total[5m]) 0.05 for: 5m labels: severity: critical annotations: summary: Agent 错误率超过 5% - alert: SlowReviews expr: histogram_quantile(0.95, rate(agent_review_duration_seconds_bucket[5m])) 120 for: 5m labels: severity: warning annotations: summary: P95 审查耗时超过 2 分钟 - alert: HighTokenUsage expr: rate(agent_token_usage_total[1h]) 500000 for: 10m labels: severity: warning annotations: summary: Token 消耗过高每小时超过 50 万 - alert: QueueBacklog expr: agent_queue_depth 50 for: 2m labels: severity: warning annotations: summary: 任务队列积压超过 50五、CI/CD 流水线# .github/workflows/deploy.yml name: Deploy CodeReview Bot on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - uses: actions/setup-gov5 with: go-version: 1.22 - name: Run unit tests run: go test ./... -coverprofilecoverage.out - name: Run security scan run: | go install github.com/securego/gosec/v2/cmd/goseclatest gosec ./... - name: Build run: go build ./cmd/server evaluate: needs: test runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Run evaluation suite run: | go run cmd/evaluate/main.go --dataset tests/eval_dataset.json - name: Check quality gate run: | SUCCESS_RATE$(cat eval_report.json | jq .success_rate) if (( $(echo $SUCCESS_RATE 85 | bc -l) )); then echo 成功率 $SUCCESS_RATE% 低于阈值 85% exit 1 fi deploy-canary: needs: evaluate runs-on: ubuntu-latest if: github.ref refs/heads/main environment: production steps: - uses: actions/checkoutv4 - name: Build and push Docker image run: | docker build -t registry.example.com/codereview-bot:${{ github.sha }} . docker push registry.example.com/codereview-bot:${{ github.sha }} - name: Deploy canary run: | kubectl set image deployment/codereview-bot-canary \ agentregistry.example.com/codereview-bot:${{ github.sha }} kubectl rollout status deployment/codereview-bot-canary --timeout5m promote: needs: deploy-canary runs-on: ubuntu-latest steps: - name: Promote to stable run: | kubectl set image deployment/codereview-bot \ agentregistry.example.com/codereview-bot:${{ github.sha }} kubectl rollout status deployment/codereview-bot --timeout5m六、评估与验证6.1 评估数据集[ { id: cr_001, category: security, difficulty: medium, pr_url: https://github.com/example/app/pull/42, expected_issues: [ {type: sql_injection, severity: critical}, {type: hardcoded_secret, severity: high} ], golden_path: [ get_pr_details, get_changed_files, run_security_scan, analyze_results, generate_report ] }, { id: cr_002, category: performance, difficulty: hard, pr_url: https://github.com/example/api/pull/128, expected_issues: [ {type: n_plus_one_query, severity: high}, {type: missing_index, severity: medium} ], golden_path: [ get_pr_details, get_changed_files, run_static_analysis, analyze_performance, generate_report ] } ]6.2 质量门禁type QualityGate struct { minSuccessRate float64 maxLatencyP99 time.Duration maxCriticalIssues int } func (qg *QualityGate) Evaluate(report *EvalReport) error { errors : []string{} if report.SuccessRate qg.minSuccessRate { errors append(errors, fmt.Sprintf( 成功率 %.1f%% 低于阈值 %.1f%%, report.SuccessRate, qg.minSuccessRate)) } if report.LatencyP99 qg.maxLatencyP99 { errors append(errors, fmt.Sprintf( P99 延迟 %v 超过阈值 %v, report.LatencyP99, qg.maxLatencyP99)) } if report.CriticalIssues qg.maxCriticalIssues { errors append(errors, fmt.Sprintf( 严重问题 %d 个超过阈值 %d, report.CriticalIssues, qg.maxCriticalIssues)) } if len(errors) 0 { return fmt.Errorf(质量门禁未通过:\n%s, strings.Join(errors, \n)) } return nil }七、总结与展望7.1 我们已经走过的路讲次主题核心产出第1讲Agent 是什么PRA 循环、Chatbot vs Agent、Go 最小骨架第2讲模型选择与推理策略模型对比、Temperature/Top-P、结构化输出第3讲工具调用Tool Registry、Function Calling、Go 实现第4讲记忆层三层记忆架构、持久化、Go 实现第5讲编排DAG 编排引擎、并行执行、错误处理第6讲沙箱与安全分层防御、Docker/WASM 沙箱、Go 实现第7讲评估三层次评估、评估数据集、自动化流水线第8讲生产化微服务架构、K8s 部署、监控告警第9讲安全加固七层防线、敏感信息保护、应急响应第10讲毕业项目CodeReviewBot 完整实现7.2 Agent 的未来趋势站在 2026 年 8 月的节点Agent 领域有几个明显的方向MCP 协议标准化工具调用协议正在走向统一Agent 之间的互操作性越来越强多 Agent 协作单一 Agent 的局限性明显多个专业 Agent 协同工作是主流Agent 原生安全安全不再是附加功能而是 Agent 架构的内置属性边缘 AgentAgent 正在从云端走向边缘设备离线能力越来越重要Agent 评估标准化行业正在形成统一的 Agent 评估标准和基准7.3 给你的建议如果你想把 Agent 开发作为职业方向动手做读完这 10 讲只是开始真正的理解来自亲手实现关注安全Agent 安全工程师会是未来两年的热门岗位参与社区关注 LangChain、AutoGPT、CrewAI 等开源项目的动态建立作品集把你的 Agent 项目放到 GitHub 上用 zz365.top 这样的工具站展示你的成果持续学习这个领域变化太快每周都要跟进最新的论文和技术博客八、课后实践最终任务基于本讲的 CodeReviewBot 架构实现你自己的生产级 Agent。要求选择一个真实的业务场景代码审查、文档生成、数据分析、客服等实现至少 3 个自定义工具集成安全防护层输入净化、工具验证、审计日志容器化并编写 K8s 部署配置编写至少 10 个评估用例配置基本的监控和告警交付物完整的项目代码GitHub 仓库架构设计文档部署文档评估报告九、延伸阅读论文《The Rise and Potential of Large Language Model Based Agents: A Survey》2025- Agent 领域的全面综述论文《Tool Learning with Foundation Models》2024- 工具学习的系统性研究开源项目LangGraph / CrewAI / AutoGen - 多 Agent 编排框架标准MCP (Model Context Protocol) - Anthropic 提出的工具调用标准书籍《Building LLM Apps》- 2025 年出版涵盖 Agent 工程实践十、致谢感谢你一路跟完这 10 讲。从最基础的Agent 是什么开始到现在你已经能构建一个生产级的 Agent 系统。这中间的每一步都对应着真实世界中的工程挑战。Agent 不是魔法它是工程。就像我们在这 10 讲中反复强调的理解原理、动手实现、不断评估、持续改进。希望这些内容对你的工作有帮助。如果在实践中遇到问题欢迎随时交流。Happy building!