第4周:生产化、部署、面试准备 - 完整概览

周目标

把v0.3的系统打磨成可上线的生产级产品,准备面试。

交付物: v1.0 - Production Ready版本 + 完整的面试准备

整体架构

Docker Compose 部署架构

graph TB
    Internet["🌍 Internet"]
    
    subgraph Docker["🐳 Docker Network"]
        LB["⚖️ Load Balancer<br/>nginx<br/>port 80"]
        
        subgraph APIServers["API Servers"]
            API1["API 1<br/>:8080"]
            API2["API 2<br/>:8080"]
            API3["API 3<br/>:8080"]
        end
        
        subgraph Workers["Background Workers"]
            Worker1["Worker 1<br/>approval executor"]
            Worker2["Worker 2<br/>async jobs"]
        end
        
        subgraph DataStores["Data Stores"]
            PG["🐘 PostgreSQL<br/>port 5432<br/>agent_db"]
            Redis["🔴 Redis<br/>port 6379<br/>cache"]
            Qdrant["🎯 Qdrant<br/>port 6333<br/>vectors"]
        end
    end
    
    Internet -->|HTTP| LB
    
    LB -->|Route| API1
    LB -->|Route| API2
    LB -->|Route| API3
    
    API1 -->|Query| PG
    API1 -->|Cache| Redis
    API1 -->|Vector| Qdrant
    
    API2 -->|Query| PG
    API2 -->|Cache| Redis
    API2 -->|Vector| Qdrant
    
    API3 -->|Query| PG
    API3 -->|Cache| Redis
    API3 -->|Vector| Qdrant
    
    Worker1 -->|Listen| Redis
    Worker1 -->|Update| PG
    
    Worker2 -->|Listen| Redis
    Worker2 -->|Update| PG
    
    PG -->|Persist| Volume1["📦 postgres_data<br/>volume"]
    Redis -->|Persist| Volume2["📦 redis_data<br/>volume"]
    Qdrant -->|Persist| Volume3["📦 qdrant_data<br/>volume"]
    
    style Docker fill:#f9f9f9
    style APIServers fill:#fff4e1
    style Workers fill:#e1f5ff
    style DataStores fill:#e1ffe1
    style LB fill:#ffe1f5

可观测性架构(Day 23)

graph LR
    subgraph App["🔵 Application"]
        API["API<br/>Handler"]
        RAG["RAG<br/>Engine"]
        Tool["Tool<br/>Executor"]
        Approval["Approval<br/>Service"]
    end
    
    subgraph Instrumentation["📊 Instrumentation"]
        OTel["OpenTelemetry<br/>SDK"]
        Metrics["Metrics<br/>Exporter"]
        Traces["Trace<br/>Exporter"]
        Logs["Log<br/>Collector"]
    end
    
    subgraph Backends["🖥️ Backends"]
        Prometheus["📈 Prometheus<br/>metrics storage"]
        Jaeger["🔍 Jaeger<br/>trace storage"]
        Loki["📝 Loki<br/>log storage"]
    end
    
    subgraph Visualization["🎨 Visualization"]
        Grafana["📊 Grafana<br/>dashboards"]
        JaegerUI["🔍 Jaeger UI<br/>trace explorer"]
    end
    
    App -->|Emit| OTel
    
    OTel -->|Metrics| Metrics
    OTel -->|Traces| Traces
    OTel -->|Logs| Logs
    
    Metrics -->|Push| Prometheus
    Traces -->|Export| Jaeger
    Logs -->|Ship| Loki
    
    Prometheus -->|Query| Grafana
    Jaeger -->|Query| JaegerUI
    Loki -->|Query| Grafana
    
    style App fill:#fff4e1
    style Instrumentation fill:#e1f5ff
    style Backends fill:#e1ffe1
    style Visualization fill:#ffe1f5

评估和CI流程(Day 24)

graph LR
    Code["💻 Code<br/>main branch"]
    
    Code -->|Push| CI["🤖 GitHub Actions"]
    
    CI -->|Test| UnitTest["✅ Unit Tests<br/>go test ./..."]
    CI -->|Test| IntTest["✅ Integration Tests<br/>with real DB"]
    CI -->|Test| RaceTest["✅ Race Detector<br/>go test -race"]
    
    UnitTest -->|Pass?| Result1{"OK?"}
    IntTest -->|Pass?| Result2{"OK?"}
    RaceTest -->|Pass?| Result3{"OK?"}
    
    Result1 -->|Fail| Stop1["🛑 CI Fail<br/>Fix and retry"]
    Result2 -->|Fail| Stop2["🛑 CI Fail<br/>Fix and retry"]
    Result3 -->|Fail| Stop3["🛑 CI Fail<br/>Fix and retry"]
    
    Result1 -->|Pass| Build["Build<br/>Docker image"]
    Result2 -->|Pass| Build
    Result3 -->|Pass| Build
    
    Build -->|Success| Eval["📊 Run Evals<br/>go run cmd/eval"]
    
    Eval -->|Test| EvalRun["Run 20 test cases<br/>against system"]
    
    EvalRun -->|Report| Report["Generate<br/>eval-report.md"]
    
    Report -->|Check| PassRate{"Pass Rate<br/>>80%?"}
    
    PassRate -->|No| EvalFail["🛑 Eval Fail<br/>Metrics declined"]
    PassRate -->|Yes| Deploy["✅ Ready to Deploy"]
    
    Deploy -->|Artifact| Image["Docker image"]
    
    style Code fill:#fff4e1
    style CI fill:#e1f5ff
    style Eval fill:#e1ffe1
    style Deploy fill:#e1ffe1
    style EvalFail fill:#ffe1e1

逐日进度

📌 Day 22: Docker Compose

你将学到:

  • 完整的容器编排
  • 服务间通信
  • 环境配置管理

关键代码:

# docker-compose.yml
version: '3.8'

services:
  # API Service
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      PORT: "8080"
      ENV: "production"
      DATABASE_URL: "postgres://user:pass@postgres:5432/agent"
      REDIS_URL: "redis://redis:6379"
      VECTOR_DB_URL: "http://qdrant:6333"
      OPENAI_API_KEY: "${OPENAI_API_KEY}"
      MODEL_NAME: "gpt-4-turbo"
    depends_on:
      - postgres
      - redis
      - qdrant
    networks:
      - agent-net
    volumes:
      - ./logs:/app/logs
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
      interval: 10s
      timeout: 5s
      retries: 3

  # Worker Service(处理approval、async tasks)
  worker:
    build:
      context: .
      dockerfile: Dockerfile.worker
    environment:
      DATABASE_URL: "postgres://user:pass@postgres:5432/agent"
      REDIS_URL: "redis://redis:6379"
      OPENAI_API_KEY: "${OPENAI_API_KEY}"
    depends_on:
      - postgres
      - redis
    networks:
      - agent-net

  # PostgreSQL
  postgres:
    image: pgvector/pgvector:pg15-latest
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: agent
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./migrations:/docker-entrypoint-initdb.d
    networks:
      - agent-net

  # Redis
  redis:
    image: redis:7-alpine
    networks:
      - agent-net
    volumes:
      - redis_data:/data

  # Qdrant Vector DB
  qdrant:
    image: qdrant/qdrant:latest
    ports:
      - "6333:6333"
    volumes:
      - qdrant_data:/qdrant/storage
    networks:
      - agent-net

volumes:
  postgres_data:
  redis_data:
  qdrant_data:

networks:
  agent-net:
    driver: bridge

使用方式:

# 启动所有服务
docker-compose up -d

# 查看日志
docker-compose logs -f api

# 停止服务
docker-compose down

验证清单:

  • 所有服务都能启动
  • 服务间通信正常(api能连数据库)
  • 健康检查工作
  • 环境变量正确传递

关键问题:

  1. 为什么需要worker service?(处理async任务、approval)
  2. 如何处理数据库迁移?(migrations目录)
  3. 日志怎样持久化?(volumes映射)

📌 Day 23: OpenTelemetry + Metrics

你将学到:

  • 分布式追踪(Tracing)
  • 指标收集(Metrics)
  • 日志相关性

关键代码:

// internal/observability/tracer.go
import (
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/jaeger"
	"go.opentelemetry.io/otel/sdk/trace"
)

func InitTracer() (*trace.TracerProvider, error) {
	exporter, err := jaeger.New(
		jaeger.WithAgentHost("localhost"),
		jaeger.WithAgentPort("6831"),
	)
	if err != nil {
		return nil, err
	}
	
	tp := trace.NewTracerProvider(
		trace.WithBatcher(exporter),
	)
	otel.SetTracerProvider(tp)
	
	return tp, nil
}

// 在handler中使用
func (h *Handler) ChatHandler(w http.ResponseWriter, r *http.Request) {
	ctx, span := tracer.Start(r.Context(), "chat_handler")
	defer span.End()
	
	span.SetAttributes(
		attribute.String("request_id", requestID),
		attribute.String("user_id", userID),
	)
	
	// Retrieval span
	retrievalCtx, retrievalSpan := tracer.Start(ctx, "retrieval")
	chunks, _ := ragEngine.Search(retrievalCtx, query)
	retrievalSpan.SetAttributes(
		attribute.Int("chunk_count", len(chunks)),
	)
	retrievalSpan.End()
	
	// LLM span
	llmCtx, llmSpan := tracer.Start(ctx, "llm_generation")
	answer, _ := llmClient.Generate(llmCtx, request)
	llmSpan.SetAttributes(
		attribute.Int("input_tokens", tokens.Input),
		attribute.Int("output_tokens", tokens.Output),
	)
	llmSpan.End()
}

// internal/observability/metrics.go
import (
	"go.opentelemetry.io/otel/metric"
	"go.opentelemetry.io/otel/exporters/prometheus"
)

type Metrics struct {
	requestCount     metric.Int64Counter
	requestLatency   metric.Float64Histogram
	llmLatency       metric.Float64Histogram
	toolFailureRate  metric.Float64Gauge
	retrievalHitRate metric.Float64Gauge
	citationRate     metric.Float64Gauge
	approvalRate     metric.Float64Gauge
	evalPassRate     metric.Float64Gauge
}

func (m *Metrics) RecordRequest(duration time.Duration) {
	m.requestCount.Add(context.Background(), 1)
	m.requestLatency.Record(context.Background(), duration.Milliseconds())
}

func (m *Metrics) RecordLLMLatency(duration time.Duration) {
	m.llmLatency.Record(context.Background(), duration.Milliseconds())
}

// Prometheus导出
exporter, _ := prometheus.New()
provider := metric.NewMeterProvider(metric.WithReader(exporter))

需要跟踪的指标:

1. Request级 - request count(total) - request latency(p50/p95/p99) - request error rate 2. LLM级 - LLM latency - token count (input/output) - model selection(如果有fallback) 3. Tool级 - tool call count(per tool) - tool latency - tool failure rate 4. Retrieval级 - retrieval latency - chunk count returned - hit rate(是否有相关文档) 5. Citations级 - citation rate(多少答案有引用) - citation accuracy(引用的chunk是否真的支持答案) 6. Approval级 - pending approval count - approval rate(multi-level approval的pass rate) - approval latency 7. Eval级 - eval pass rate(与baseline对比) - eval pass rate trend(趋势)

验证清单:

  • Tracer能初始化
  • Request能被span包装
  • 关键操作都有span(retrieval、llm、tool call)
  • Prometheus能导出metrics
  • Jaeger能看到完整的trace

关键问题:

  1. 为什么要记录这些指标?(问题诊断和优化)
  2. p99延迟为什么比平均延迟重要?(tail latency)
  3. 怎样关联trace和日志?(request_id)

📌 Day 24: Eval Runner + CI

你将学到:

  • 自动化测试框架
  • GitHub Actions集成
  • 部署前验证

关键代码:

// cmd/eval/main.go
package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"os"
	"time"
)

type EvalSuite struct {
	Cases []EvalCase `json:"cases"`
}

type EvalCase struct {
	ID         string   `json:"id"`
	Input      string   `json:"input"`
	Expected   string   `json:"expected"`
	Sources    []string `json:"expected_sources"`
	MustCite   bool     `json:"must_cite"`
}

type EvalReport struct {
	Timestamp       time.Time
	TotalCases      int
	PassedCases     int
	FailedCases     int
	PassRate        float32
	AverageLatency  time.Duration
	DetailedResults []CaseResult
}

func main() {
	datasetPath := flag.String("dataset", "evals/dataset.jsonl", "Path to eval dataset")
	outputPath := flag.String("output", "eval-report.md", "Output report path")
	flag.Parse()
	
	// 1. 加载eval dataset
	cases := loadDataset(*datasetPath)
	
	// 2. 运行eval
	results := runEval(cases)
	
	// 3. 生成报告
	report := generateReport(results)
	
	// 4. 输出为Markdown(易于CI展示)
	writeMarkdownReport(*outputPath, report)
	
	// 5. 根据通过率决定CI pass/fail
	if report.PassRate < 0.80 {
		fmt.Printf("FAILED: Pass rate %.2f%% is below threshold 80%%\n", report.PassRate*100)
		os.Exit(1)
	}
	
	fmt.Printf("PASSED: Pass rate %.2f%%\n", report.PassRate*100)
	os.Exit(0)
}

func generateReport(results []CaseResult) *EvalReport {
	passed := 0
	for _, r := range results {
		if r.Passed {
			passed++
		}
	}
	
	return &EvalReport{
		Timestamp:      time.Now(),
		TotalCases:     len(results),
		PassedCases:    passed,
		FailedCases:    len(results) - passed,
		PassRate:       float32(passed) / float32(len(results)),
		DetailedResults: results,
	}
}

func writeMarkdownReport(path string, report *EvalReport) {
	md := fmt.Sprintf(`# Eval Report

Generated: %s

## Summary
- **Total Cases**: %d
- **Passed**: %d
- **Failed**: %d
- **Pass Rate**: %.2f%%
- **Average Latency**: %v

## Detailed Results

| Case ID | Input | Status | Latency |
|---------|-------|--------|---------|
`,
		report.Timestamp.Format(time.RFC3339),
		report.TotalCases,
		report.PassedCases,
		report.FailedCases,
		report.PassRate*100,
		report.AverageLatency,
	)
	
	for _, r := range report.DetailedResults {
		status := "✅ PASS"
		if !r.Passed {
			status = "❌ FAIL"
		}
		md += fmt.Sprintf("| %s | %s | %s | %v |\n",
			r.CaseID, r.Input[:30]+"...", status, r.Latency)
	}
	
	os.WriteFile(path, []byte(md), 0644)
}

GitHub Actions配置:

# .github/workflows/ci.yml
name: CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      postgres:
        image: pgvector/pgvector:pg15-latest
        env:
          POSTGRES_PASSWORD: pass
          POSTGRES_DB: agent_test
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - uses: actions/checkout@v3
      
      - uses: actions/setup-go@v4
        with:
          go-version: '1.22'
      
      - name: Run unit tests
        run: go test ./... -v -race
      
      - name: Run eval
        run: |
          go run cmd/eval/main.go \
            --dataset evals/dataset.jsonl \
            --output eval-report.md
      
      - name: Upload eval report
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: eval-report
          path: eval-report.md
      
      - name: Build Docker image
        run: docker build -t agent-runtime:latest .
      
      - name: Check eval passed
        run: |
          # 检查pass rate,失败则CI fail
          if [ $? -ne 0 ]; then
            exit 1
          fi

验证清单:

  • 能加载evals/dataset.jsonl
  • eval runner能逐个case测试
  • 能生成markdown报告
  • GitHub Actions能运行
  • 失败的case不影响其他case(要测完所有)

关键问题:

  1. Pass rate的门槛是多少?(80%?90%?)
  2. 新feature怎样对eval dataset?(先提高baseline)
  3. 如果eval太慢怎么办?(并行运行;或smoke test)

📌 Day 25: Cost、Latency、Caching

你将学到:

  • 成本分析和优化
  • 延迟优化的杠杆
  • 缓存策略

关键代码:

// internal/cost/cost_tracker.go
type CostTracker struct {
	embeddingCostPer1KTokens float64  // $0.00002
	llmCostPerToken          float64  // varies by model
	totalCost                float64
	tokenUsage               int
}

func (ct *CostTracker) TrackEmbedding(tokenCount int) {
	cost := float64(tokenCount) / 1000.0 * ct.embeddingCostPer1KTokens
	ct.totalCost += cost
	ct.tokenUsage += tokenCount
}

func (ct *CostTracker) TrackLLM(inputTokens, outputTokens int) {
	// GPT-4-turbo: $0.01 per 1K input, $0.03 per 1K output
	inputCost := float64(inputTokens) / 1000.0 * 0.01
	outputCost := float64(outputTokens) / 1000.0 * 0.03
	ct.totalCost += inputCost + outputCost
	ct.tokenUsage += inputTokens + outputTokens
}

// internal/cache/cache_strategies.go
type CacheStrategy string

const (
	CacheEmbedding    CacheStrategy = "embedding"    // 缓存embedding结果
	CacheRetrieval    CacheStrategy = "retrieval"    // 缓存RAG检索结果
	CacheLLMSafeAnswer CacheStrategy = "llm_safe"     // 缓存确定的答案
)

type CacheManager struct {
	redis *redis.Client
	ttl   map[CacheStrategy]time.Duration
}

// 1. Embedding缓存
func (cm *CacheManager) GetOrComputeEmbedding(ctx context.Context, text string) ([]float32, error) {
	cacheKey := fmt.Sprintf("embedding:%s", hashText(text))
	
	// 先查缓存
	if cached, err := cm.redis.Get(ctx, cacheKey).Result(); err == nil {
		var embedding []float32
		json.Unmarshal([]byte(cached), &embedding)
		return embedding, nil
	}
	
	// 调OpenAI
	embedding, _ := embeddingClient.Embed(ctx, text)
	
	// 缓存1周
	cm.redis.Set(ctx, cacheKey, marshalJSON(embedding), 7*24*time.Hour)
	
	return embedding, nil
}

// 2. Retrieval缓存 - 相同query返回缓存结果
func (cm *CacheManager) GetOrSearch(ctx context.Context, query string, topK int) ([]Chunk, error) {
	cacheKey := fmt.Sprintf("retrieval:%s:%d", query, topK)
	
	if cached, err := cm.redis.Get(ctx, cacheKey).Result(); err == nil {
		var chunks []Chunk
		json.Unmarshal([]byte(cached), &chunks)
		return chunks, nil
	}
	
	chunks, _ := retriever.Search(ctx, query, topK)
	
	// 缓存1小时
	cm.redis.Set(ctx, cacheKey, marshalJSON(chunks), 1*time.Hour)
	
	return chunks, nil
}

// 3. Safe Answer缓存 - 对于high-confidence答案
type SafeAnswer struct {
	Query      string
	Answer     string
	Confidence string
	CreatedAt  time.Time
}

func (cm *CacheManager) TryGetSafeAnswer(ctx context.Context, query string) (*SafeAnswer, error) {
	cacheKey := fmt.Sprintf("answer:%s", query)
	
	if cached, err := cm.redis.Get(ctx, cacheKey).Result(); err == nil {
		var ans SafeAnswer
		json.Unmarshal([]byte(cached), &ans)
		
		// 只返回最近一天的答案
		if time.Since(ans.CreatedAt) < 24*time.Hour {
			return &ans, nil
		}
	}
	
	return nil, nil
}

// 成本-质量权衡
type ModelStrategy struct {
	RouterModel     string  // 分类用,小且快
	RetrievalModel  string  // Rerank用,小且快
	GenerationModel string  // 答案生成,大且准
}

// 设想:用GPT-4 turbo只给 high-risk 问题
// 用GPT-4O mini给routine问题
func (h *Handler) ChatHandler(w http.ResponseWriter, r *http.Request) {
	userInput := getInput(r)
	
	// 1. 先用小模型分类
	classification, _ := smallLLMClient.Classify(context.Background(), userInput)
	
	if classification.IsHighRisk {
		// 用大模型
		answer, _ := largeLLMClient.Generate(context.Background(), request)
	} else {
		// 用小模型,省钱
		answer, _ := smallLLMClient.Generate(context.Background(), request)
	}
}

成本优化的杠杆:

1. Caching(减少API调用) - Embedding缓存:一次计算,多次使用 - Retrieval缓存:热点query共用结果 - Safe answer缓存:高置信度答案复用 2. Model Selection(用小模型) - 分类用小模型(GPT-4O mini) - Rerank用小模型 - 只有高难度用大模型(GPT-4 turbo) 3. Context Compression(减少token) - 只传必要的chunks - 压缩prompt(remove redundancy) - 截断长输出 4. Request Batching(批量处理) - embedding多条文本一起 - 减少往返次数 成本下降估计:50-70%

验证清单:

  • 能跟踪成本
  • Embedding缓存有效
  • Retrieval缓存命中率高
  • 模型选择策略合理

关键问题:

  1. 为什么要用不同模型?(成本vs质量的权衡)
  2. Cache TTL怎样设置?(频变数据用短TTL)
  3. 如何测量cache有效性?(hit rate指标)

📌 Day 26: Failure Mode Review

你将学到:

  • 系统性地识别风险点
  • 故障检测和恢复
  • 事后分析(post-mortem)

关键代码:

// docs/failure-modes.md structure

## 线上常见故障模式

### 1. LLM Timeout
**Symptom**: 请求返回504,用户看到"Service Unavailable"
**Root Cause**: OpenAI API超时(可能网络慢、queue长)
**Detection**: 
  - Metrics: LLM latency > threshold
  - Alert: p99 latency > 30s
**Mitigation**:
  - Day 6: Retry with exponential backoff
  - Day 25: Fallback to smaller model
  - Day 22: circuit breaker pattern
  
Code:
```go
if isOpenAITimeout(err) {
    // 降级到本地模型或小模型
    answer, _ = fallbackLLMClient.Generate(ctx, request)
}

2. Retrieval Miss

Symptom: 答案完全错误或拒绝回答有效问题 Root Cause:

  • Chunking粒度不对
  • Embedding模型不好
  • Query和文档表述不一致 Detection:
  • Metrics: retrieval_hit_rate < 80%
  • Eval: answer_correctness < threshold Mitigation:
  • 重新评估chunking策略
  • 试试更好的embedding模型
  • 增加keyword search权重
  • 更新知识库

3. Permission Bypass

Symptom: L1员工能看到HR敏感文档 Root Cause: Retrieval guardrail没做好 Detection:

  • Audit log分析
  • 定期权限审计 Mitigation:
  • Day 19: 强化RBAC
  • 在retrieval阶段过滤,不是最后过滤
  • 定期测试权限

4. Hallucination

Symptom: 答案有编造的内容(引用不存在的policy) Root Cause: LLM缺乏真实信息,编造了 Detection:

  • Citation verification
  • Human review
  • Eval dataset Mitigation:
  • 强制citation(Day 11)
  • 降低confidence
  • 使用smaller context window
  • 更好的retrieval

5. Approval Lost

Symptom: 用户批准了但操作没执行 Root Cause: Worker crash、DB写失败、race condition Detection:

  • Approval age > threshold
  • Worker健康检查失败 Mitigation:
  • Idempotent executor(Day 6)
  • Checkpoint recovery(Day 16)
  • Worker health monitoring

6. 故障排查路径

用户反馈:Agent答错了。

排查步骤:

  1. 看request_id的trace

    • 输入内容
    • retrieval结果(是否相关?)
    • rerank分数
    • LLM的prompt和output
    • citation是否正确
  2. 如果retrieval miss

    • 检查embedding是否抓到语义
    • 关键词是否匹配
    • Hybrid权重是否合理
  3. 如果hallucination

    • Citation是否存在
    • 对应的chunk是否支持答案
  4. 如果permission issue

    • 用户权限是什么?
    • 返回的chunk应该被过滤吗?
  5. 改进

    • 修改chunking/embedding/权重
    • 添加到eval dataset防止回归
    • 重跑整个eval
**验证清单:** - [ ] 能识别常见故障(至少6种) - [ ] 每个故障有symptom和root cause - [ ] 有detection策略 - [ ] 有mitigation方案 - [ ] 有排查路径文档 **关键问题:** 1. 怎样区分"系统bug"和"LLM限制"? 2. 怎样从failure mode推导出metrics? 3. 什么样的故障应该alert? --- ### 📌 Day 27: Go Agent System Design 专项 **你将学到:** - 3道高难度系统设计题 - 完整的回答框架 - 应对follow-up的能力 **3道题目:** **题目1:Design a customer support copilot with Go backend**

考察重点:

  • API层设计(并发、timeout)
  • Agent层设计(tool orchestration)
  • RAG设计(信息准确)
  • 权限控制(L1/L2分级)
  • 用户体验(streaming?)

高分回答包含: ✓ Architecture diagram ✓ Component responsibilities ✓ Data flow ✓ Failure handling ✓ Scaling策略 ✓ 与existing系统的integration ✓ Metrics/alerting

**题目2:Design an internal enterprise agent platform**

考察重点:

  • Multi-tenant support
  • Complex approval workflows
  • Audit和compliance
  • Tool ecosystem
  • Cost control
  • Safety(guardrails)

需要讲清:

  • 怎样支持不同部门的不同agent
  • 权限模型
  • Cost allocation
  • Tool governance
**题目3:Design an MCP-based tool gateway for company systems**

考察重点:

  • Tool abstraction
  • Authentication/Authorization
  • Rate limiting
  • Tool discovery
  • Error handling

核心问题:

  • 怎样让企业工具安全地暴露给LLM?
  • 怎样处理工具的错误?
  • 怎样audit谁调了什么?
**回答框架(HIRED模式):**

H = Hear and clarify 问问题:日均用户数?访问模式? 澄清非功能性需求:延迟、可用性、成本

I = Initial architecture 画高层architecture 识别关键组件

R = Reason about tradeoffs 为什么选这个而不是那个? 什么时候这个设计会fail?

E = Enumerate alternatives 考虑过其他方案吗? 为什么最后选了这个?

D = Drive deep dives pick one area and go deep 比如:怎样处理approval workflow的并发?

--- ### 📌 Day 28: 项目包装 **你将学到:** - 如何present项目 - 3个版本的pitch - 回答follow-up的技巧 **30秒Pitch(电梯演讲):**

我做了一个Go版企业客服Agent。它包含Agent Runtime、 RAG检索、Tool Gateway、审批流、Guardrails、RBAC、MCP Server、 Tracing和Eval Runner。我的重点不是做一个聊天demo,而是把 agent做成可控、可观测、可评估、可上线的后端系统。

**2分钟Pitch:**

这个项目用Go实现了一个企业知识库和工单执行Agent。

架构方面,我把确定性workflow和agent决策分开: 认证、权限、风险判断、审批和审计由Go workflow控制; 检索、工具选择和回答生成由agent runtime处理。

具体来说:

  • RAG部分包含chunking、metadata、vector search、 keyword search、rerank和citations。这样既有语义相似度 又有关键词精确匹配。

  • 工具部分通过Tool Registry管理,每个工具都有schema、 risk level、timeout、retry和audit log。

  • 写操作不会直接执行,只生成draft进入human approval。 这样可追踪、可审计、可回滚。

  • Guardrails分四层:input(防prompt injection)、 output(防敏感信息)、tool(防高风险操作)、 retrieval(防越权访问)。

  • 最后我加入eval dataset、trace log、latency/cost指标 和Docker部署,用于模拟生产环境。

这不是聊天机器人,而是一个可信的企业系统。

**面试常见follow-up:**

Q1: 为什么不用Python/LangChain? A: Go的强项是并发和内存效率。Agent系统需要处理 多并发请求和大量状态管理,Go更合适。而且Go的type safety 让分布式tracing更容易实现。

Q2: 你的Agent Loop怎么处理tool call? A: LLM判断是否需要工具后,我从Tool Registry查询工具的schema、 risk level、timeout。然后执行工具,把结果反馈给LLM。 关键是处理错误情况:工具超时就重试、tool not found就告诉LLM。

Q3: 如何防止重复创建工单? A: 两层防护。第一层用idempotency key去重(Day 6)。 第二层工单创建生成draft,需要人工审批后才执行。

Q4: RAG如何做权限过滤? A: 在retrieval阶段就过滤。LLM看不到没权限的文档。 这比在答案输出时过滤更安全,因为LLM可能已经在推理中用上了 那些文档。

Q5: MCP server怎么鉴权? A: MCP和我们的API通过共享的token认证。每个MCP工具调用 都带上用户context,我们在工具层检查权限。

Q6: 如何debug一次失败的回答? A: 用request_id追踪整个flow。看retrieval返回了什么、 rerank的分数、LLM的prompt和输出、citation是否正确。 每个阶段都有span和metrics,可以准确定位问题。

Q7: 如何控制成本? A: 三个方面。缓存:embedding结果、热点query结果。 模型选择:用GPT-4O mini做分类和rerank,只有高难度用GPT-4 Turbo。 Context compression:只传必要的chunks,压缩prompt。

--- ### 📌 Day 29: 全真模拟 **上午 - Coding (45分钟):**

一道Medium LeetCode题目,用Go写。 示例:Number of Islands

题目: 给一个2D网格,包含'1'(陆地)和'0'(水)。 计算岛屿的数量。岛屿由四周(上下左右)的1连接。

输入: grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ]

输出:1

要求:

  • 时间复杂度
  • 空间复杂度
  • 边界测试(empty grid、单个cell等)
**下午 - System Design (1小时):**

Design an enterprise AI agent platform.

给定条件:

  • 10000+员工企业
  • 多个部门,不同的知识库和工具
  • 日均10K请求
  • SLA: 99.9% availability, <2s latency

需要设计:

  1. Multi-tenant architecture
  2. Knowledge ingestion
  3. Agent runtime
  4. Approval workflow
  5. Permission model
  6. Cost control
  7. Monitoring
**晚上 - Project Deep Dive (1小时):**

准备被问:

  1. 系统的最复杂部分是什么? → 权限模型的实现。需要在retrieval、 tool execution、approval三个地方都检查权限。

  2. 最难debug的issue是什么? → Hallucination。表现是答案错了,但root cause很难找。 需要看retrieval是否hit、citation是否正确、LLM是否编造。

  3. 如果QPS翻倍,系统怎样扩展? → API层用load balancer + horizontal scaling。 数据库用read replicas和sharding(按user或doc)。 缓存扩大TTL。Vector DB改cluster mode。

  4. 最自豪的设计决定是什么? → Draft-then-approve模式。简单但有力,让写操作可控。

  5. 如果再做一遍,什么会做不同? → 早点加eval dataset。前期埋太多technical debt,后期debug困难。

  6. Fallback策略是什么? → API level: circuit breaker + graceful degradation。 LLM level: 用smaller model或预设回答。 Retrieval level: 没有相关文档就拒绝而不是编造。

  7. 怎样衡量agent的可信度? → 几个指标:citation accuracy, refusal accuracy, approval rejection rate(人工审批多少被拒)。

--- ### 📌 Day 30: 最终检查 **清单:**

代码部分:

  • GitHub repo上传
  • README完整(怎样跑、架构图、key decisions)
  • Architecture diagram有(用Mermaid或图片)
  • cmd/api 完整可运行
  • internal/ 各模块清晰
  • go test ./... 全部通过
  • go test -race 无race condition
  • Docker build成功
  • docker-compose up能启动

文档部分:

  • docs/agent-runtime.md (Day 1-7总结)
  • docs/rag-pipeline.md (Day 8-14总结)
  • docs/workflow.md (Day 15-21总结)
  • docs/failure-modes.md (Day 26)
  • docs/system-design-notes.md (Day 27)

演讲准备:

  • 30秒版本 - 背熟,能任何场景讲
  • 2分钟版本 - 自然流利
  • 7道常见follow-up - 能应对
  • Deep dive问题 - 能讲深

代码质量:

  • 无obvious bugs(review过)
  • 命名清晰(能一眼看出意图)
  • 注释简洁(only WHY, not WHAT)
  • Error handling完整
  • 单元测试覆盖关键路径

面试准备:

  • 算法刷了45道
  • 能讲清每个Week的关键设计
  • 能应对"为什么这样设计"的问题
  • 有backup plan(临时遇到不会的)

最终检查问题:

  • 我能给别人讲清这个系统吗?
  • 系统的架构有没有obvious缺陷?
  • 如果这是面试考题,我的回答够comprehensive吗?
  • 代码质量能show off吗?
  • 我能从零重新写一遍吗?
--- ## 📊 第4周学习成果检验 ### 工程化能力 - [ ] 理解完整的容器编排 - [ ] 能设计可观测系统 - [ ] 能实现自动化测试 - [ ] 能做成本优化和性能调优 ### 系统设计能力 - [ ] 能讲3道high-level设计题 - [ ] 能处理follow-up questions - [ ] 能从多个角度思考设计trade-offs ### 沟通能力 - [ ] 能清晰表达复杂概念 - [ ] 能根据听众调整讲法 - [ ] 能回答challenging questions --- ## ⏱ 推荐时间分配 | 日期 | Task | 时间 | |-----|------|------| | Day 22 | Docker Compose | 1.5h | | Day 23 | OpenTelemetry | 2h | | Day 24 | Eval + CI | 2h | | Day 25 | Cost/Cache优化 | 2.5h | | Day 26 | Failure Mode Review | 2h | | Day 27 | 系统设计题 | 3h | | Day 28 | 项目包装 | 2h | | Day 29 | 全真模拟 | 4h | | Day 30 | 最终检查 | 2.5h | **周总计:** 22小时 --- ## 🎁 Day 30完成后 你有: - 一个生产级的Go Agent系统 - 30天的深度学习记录 - 面试的充分准备 - 可上线的代码 简历可以写: > Designed and built a production-ready Go agent platform with RAG, workflow automation, approval systems, and comprehensive observability. Implemented hybrid retrieval with 90%+ accuracy on custom eval dataset. Achieved 50% cost reduction through intelligent caching and model selection. 这不是夸大。这就是你做过的东西。 准备面试吧!🚀