第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
验证清单:
关键问题:
- 为什么需要worker service?(处理async任务、approval)
- 如何处理数据库迁移?(migrations目录)
- 日志怎样持久化?(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(趋势)
验证清单:
关键问题:
- 为什么要记录这些指标?(问题诊断和优化)
- p99延迟为什么比平均延迟重要?(tail latency)
- 怎样关联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
验证清单:
关键问题:
- Pass rate的门槛是多少?(80%?90%?)
- 新feature怎样对eval dataset?(先提高baseline)
- 如果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%
验证清单:
关键问题:
- 为什么要用不同模型?(成本vs质量的权衡)
- Cache TTL怎样设置?(频变数据用短TTL)
- 如何测量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答错了。
排查步骤:
-
看request_id的trace
- 输入内容
- retrieval结果(是否相关?)
- rerank分数
- LLM的prompt和output
- citation是否正确
-
如果retrieval miss
- 检查embedding是否抓到语义
- 关键词是否匹配
- Hybrid权重是否合理
-
如果hallucination
- Citation是否存在
- 对应的chunk是否支持答案
-
如果permission issue
-
改进
- 修改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谁调了什么?
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做成可控、可观测、可评估、可上线的后端系统。
这个项目用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部署,用于模拟生产环境。
这不是聊天机器人,而是一个可信的企业系统。
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
需要设计:
- Multi-tenant architecture
- Knowledge ingestion
- Agent runtime
- Approval workflow
- Permission model
- Cost control
- Monitoring
**晚上 - Project Deep Dive (1小时):**
准备被问:
-
系统的最复杂部分是什么?
→ 权限模型的实现。需要在retrieval、
tool execution、approval三个地方都检查权限。
-
最难debug的issue是什么?
→ Hallucination。表现是答案错了,但root cause很难找。
需要看retrieval是否hit、citation是否正确、LLM是否编造。
-
如果QPS翻倍,系统怎样扩展?
→ API层用load balancer + horizontal scaling。
数据库用read replicas和sharding(按user或doc)。
缓存扩大TTL。Vector DB改cluster mode。
-
最自豪的设计决定是什么?
→ Draft-then-approve模式。简单但有力,让写操作可控。
-
如果再做一遍,什么会做不同?
→ 早点加eval dataset。前期埋太多technical debt,后期debug困难。
-
Fallback策略是什么?
→ API level: circuit breaker + graceful degradation。
LLM level: 用smaller model或预设回答。
Retrieval level: 没有相关文档就拒绝而不是编造。
-
怎样衡量agent的可信度?
→ 几个指标:citation accuracy, refusal accuracy,
approval rejection rate(人工审批多少被拒)。
---
### 📌 Day 30: 最终检查
**清单:**
代码部分:
文档部分:
演讲准备:
代码质量:
面试准备:
最终检查问题:
---
## 📊 第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.
这不是夸大。这就是你做过的东西。
准备面试吧!🚀