阶段 1: tRPC-Go 基础与微服务入门
了解 tRPC-Go 的基本概念和开发流程,创建并启动一个简单的微服务
预计学时: 1–2 周
Go 语言基础回顾与环境搭建
学习目标
- 安装并配置 Go 开发环境
- 理解 Go 模块系统与包管理
- 掌握 Go 基本语法:变量、函数、结构体、接口
- 了解 Go 的并发模型基础(goroutine、channel)
Go 语言核心特性
> 本节目标:搭建 Go 开发环境,理解 Go 与 TypeScript 的核心差异,掌握并发编程基础,学会用 pprof 排查性能问题。
Go(Golang)是 Google 开发的静态类型、编译型语言。对于前端工程师而言,Go 最显著的特点是:
- 简洁的语法:没有类继承、没有泛型泛滥(Go 1.18+ 支持基础泛型),学习曲线平缓
- 内置并发:goroutine 和 channel 是语言级别的并发原语
- 快速编译:大型项目编译速度远超 C++/Java
- 单二进制部署:编译产物是单个可执行文件,无需运行时依赖
环境搭建
> 版本要求: 本教程推荐使用 Go 1.21 或更高版本。tRPC-Go 最新功能可能需要 Go 1.22+。
常见问题
Q: 安装后 go version 显示版本过低?
A: macOS 自带的 Go 版本通常较旧,建议使用 Homebrew 安装最新版:
Q: 国内下载依赖速度慢?
A: 已配置 GOPROXY 代理,如仍有问题,可尝试其他国内镜像:
Q: VSCode 提示 "could not import..."?
A: 确保安装 Go 扩展插件,并在 VSCode 中设置 指向正确的 Go 安装路径。
模块初始化
Go 使用 管理依赖,类似 npm/package.json。
会生成 文件,等同于 。
基本语法对照
> 💡 快速记忆:Go 的语法设计原则是
结构体与方法
> struct(结构体):Go 中定义数据结构的方式,相当于 TypeScript 的 + 的结合体。struct 定义数据字段,method 定义行为。
Go 没有 class,使用 struct + method 实现面向对象:
接口
> interface(接口):定义一组方法签名的契约。Go 的接口是隐式实现的——只要一个类型拥有接口要求的所有方法,它就自动实现了该接口,无需显式声明 。这与 TypeScript 的 关键字不同,但与 TypeScript 的结构化类型(structural typing)理念相似。
Go 的接口是隐式实现的(duck typing),不需要 关键字:
深入理解:GMP 调度模型
> 这是 Go 并发的核心原理,理解它能让你写出更高效的并发代码,也是面试必考点。
一句话解释 GMP
想象一个餐厅厨房的工作场景:
- G (Goroutine) = 厨师(做菜的goroutine),轻量到可以同时开成千上万个
- M (Machine) = 真实的厨师的身体(操作系统线程),需要休息,有限数量
- P (Processor) = 厨房的工位(逻辑处理器),分配任务给厨师的中间人
没有 P,厨师(M)不知道该做什么;没有 M,厨师(P)只是空架子。
GMP 三要素
调度流程:一个 G 的生命周期
关键机制
- *:P 的数量,默认等于 CPU 核数,控制真正的并行度
- CPU 密集型任务:保持等于 CPU 核数
- IO 密集型任务:可以设大一些(如 2 倍核数)
- Work Stealing(任务窃取):当 P0 的队列空了,会去 P1 的队列"偷"任务
- Hand Off(交接):当 M 阻塞时(如系统调用),P 会解绑这个 M,去找其他 M 继续工作
为什么 Goroutine 这么轻量?
Goroutine 创建和切换如此廉价,使得 Go 可以轻松创建数十万个并发任务,而线程办不到。
面试必答
- Goroutine 初始栈 2KB(线程通常 1-8MB),可动态增长到 1GB
- 默认等于 CPU 核数,控制真正的并行度
- M 的数量上限由 控制,默认 10000
- Work Stealing 保证负载均衡,Hand Off 保证阻塞不浪费 P 的能力
并发基础
> ⚠️ 前端工程师注意
>
> Go 与 JavaScript/TypeScript 有几个容易踩坑的关键差异:
>
> 1. 值传递 vs 引用传递
> - JavaScript 中对象默认是引用传递(修改参数会影响原对象)
> - Go 中 struct 默认是值传递(函数收到的是副本,修改不影响原对象)
> - 如果需要修改原对象,必须传递指针( 而非 )
>
>
>
> 2. nil vs undefined/null
> - Go 的 只能用于指针、接口、slice、map、channel、函数类型
> - 对 指针调用方法会 panic(类似 JS 的 )
> - 始终在使用前检查:
>
> 3. 没有隐式类型转换
> - 和 是不同类型,不能直接相加,必须显式转换:
> goroutine:Go 的轻量级并发单元,类似于 JavaScript 的 函数,但更轻量(初始只需 2KB 内存,而系统线程需要 1-8MB)。在函数调用前加 关键字即可启动一个 goroutine,它会在后台并发执行。
> channel:goroutine 之间安全传递数据的管道,类似于 JavaScript 的 或 。Go 的并发哲学是
2. WaitGroup - 等待一组任务完成
3. Channel - goroutine 之间的通信
无缓冲 vs 有缓冲:
- — 无缓冲,发送和接收同步阻塞(握手)
- — 有缓冲,缓冲满时才阻塞
4. Select - 多路复用
> 🔧 动手试试:
> 1. 将上面 WaitGroup 示例中的 注释掉,观察程序输出有什么变化
> 2. 将 改为 (无缓冲),观察程序是否会死锁
> 3. 运行 ,故意制造一个数据竞争,观察 race detector 的输出
深入理解:Goroutine 泄漏检测与预防
> Goroutine 泄漏是 Go 服务最常见的内存问题,生产事故的重要来源。
什么是 Goroutine 泄漏?
当 goroutine 因为 channel 阻塞、锁等待或无限循环而永远无法退出时,就发生了泄漏。
正确做法:使用 context 控制生命周期
检测 Goroutine 泄漏
深入理解:Go 内存模型与 happens-before
> 理解内存模型是写出正确并发代码的基础,避免数据竞争。
happens-before 规则
Go 内存模型定义了哪些操作"先于"其他操作可见:
数据竞争检测
生产级并发模式:Worker Pool
> Worker Pool 是最常用的并发控制模式,控制并发数量,避免资源耗尽。
错误处理最佳实践
> Go 的错误处理哲学:错误是值,不是异常。
错误包装与链式追踪
自定义错误类型
panic 与 recover
pprof 性能分析入门
> pprof:Go 内置的性能分析工具,可以分析 CPU 使用、内存分配、goroutine 堆栈等。类似于 Chrome DevTools 的 Performance 面板,但功能更强大,可以精确到每一行代码的资源消耗。
> pprof 是 Go 内置的性能分析工具,生产排查必备技能。
开启 pprof 端点
常用分析命令
基准测试与 pprof 结合
> 🔧 动手试试:
> 1. 在你的项目中添加 导入,启动服务后访问
> 2. 运行 ,在交互界面输入 查看 goroutine 最多的函数
> 3. 编写一个故意低效的函数(如在循环中拼接字符串),用基准测试 + pprof 找出性能瓶颈
常见问题
Q: goroutine 会泄漏吗?
A: 是的。channel 无人接收、锁永远等待、无限循环都会导致泄漏。使用 超时控制 + 测试库检测。
Q: 如何控制并发数量?
A: 使用 Worker Pool 模式(见上文),或 包。
Q: 为什么不直接用线程?
A: goroutine 比线程轻量得多(2KB vs 1-8MB),切换快,Go 运行时自动调度。
Q: sync.Mutex 和 channel 如何选择?
A: 保护共享状态用 Mutex,传递数据/信号用 channel。Go 谚语:"不要通过共享内存来通信,而要通过通信来共享内存。"
Q: GOMAXPROCS 设置多少合适?**
A: CPU 密集型任务设为 CPU 核数(默认值);IO 密集型可适当调大,但通常不超过 CPU 核数的 2 倍。
课后检查清单
- Go 环境安装完成,go version 输出正常
- 理解 go mod init 的作用
- 能够声明变量、定义函数
- 理解结构体和方法绑定
- 理解接口的隐式实现
- 能编写简单的 goroutine 示例
Protocol Buffers 与服务定义
学习目标
- 理解 Protocol Buffers 的作用和语法
- 学会使用 .proto 文件定义 RPC 服务
- 理解序列化/反序列化概念
- 了解 proto3 与 JSON 的对比
Protocol Buffers 简介
> 本节目标:理解 Protobuf 的核心概念和语法,掌握字段编号管理规则,学会定义生产级的 .proto 文件。学完本节后,你将能够独立编写 .proto 文件并理解为什么微服务通信要用 Protobuf 而不是 JSON。
> Protocol Buffers(Protobuf):Google 开发的一种二进制序列化协议,用于在服务之间高效传输结构化数据。你可以把它理解为
为什么用 Protobuf?
Proto3 语法
关键概念
- message:类似 TypeScript 的 ,定义数据结构。Protobuf 的 就是你的数据模型。
- service:定义 RPC 方法集合,类似 Express 的路由定义()
- repeated:数组字段,等同于 TypeScript 的
- 字段编号:每个字段有唯一编号(如 ),用于二进制编码。这是 Protobuf 最重要的概念——一旦使用,永远不可更改(详见下文)
- stream:流式传输,用于大数据或实时推送,类似 Web 的 SSE/WebSocket
> ⚠️ 前端工程师注意
>
> Protobuf 的字段编号管理类似于数据库表的列 ID——一旦某个编号被使用,就不能再改变它代表的含义。这与 REST API 的版本管理不同:REST API 可以通过 路径来区分版本,但 Protobuf 的兼容性是通过字段编号来保证的。
>
> 如果你随意修改字段编号,就像把数据库的 列改成存 ——所有旧数据都会被错误解析。
与 TypeScript 类型的对照
代码生成
tRPC-Go 使用 工具从 .proto 文件自动生成 Go 代码:
生成的文件包括:
- 消息类型的 Go 结构体
- 服务接口定义
- 客户端 stub 代码
- 服务端 skeleton 代码
深入理解:字段编号与向后兼容性
> 这是 Protobuf 最容易踩坑的地方,也是生产环境中 API 演进的核心规则。
字段编号的本质
Protobuf 的二进制格式不存储字段名,只存储字段编号。这意味着:
黄金规则:
1. 永远不要修改已使用的字段编号
2. 永远不要修改已使用字段的类型(有限例外:int32/int64/uint32/uint64/bool 可互换)
3. 新增字段使用新编号,旧版本会忽略未知字段
> ✅ 正确做法 vs ❌ 错误做法
>
>
reserved 关键字:安全删除字段
当你需要删除一个字段时,不能直接删除,必须用 标记:
为什么需要 reserved?
- 防止新字段误用旧编号,导致数据解析错误
- 防止新字段误用旧名称,导致代码混淆
- 是 API 演进的安全网
兼容性规则速查表
高级类型:oneof、map 与 enum
oneof:互斥字段
当一个消息中只有一个字段会被设置时,使用 节省内存:
Go 中使用 oneof:
map:键值对字段
> 注意:map 字段不能用 ,且 key 只能是整数或字符串类型。
enum:枚举类型
命名规范:枚举值使用 格式,避免命名冲突。
字段编号分配策略
> 生产级 proto 文件的字段编号管理规范。
字段编号范围:
- :编码 1 字节,用于高频字段(如 id、name、timestamp)
- :编码 2 字节
- :编码 3 字节
- :Protobuf 内部保留,禁止使用
性能对比基准测试
> 用数据说话,理解 Protobuf 的性能优势。
预期输出(参考值,实际因机器而异):
结论:Protobuf 序列化速度约为 JSON 的 6-7 倍,内存分配减少 75%。
常见问题
Q: protoc 命令找不到?
A: 需要安装 protoc 编译器:
Q: trpc create 命令失败?
A: 确保在项目目录下运行,且 .proto 文件语法正确:
Q: 如何查看 Protobuf 二进制内容?
A: 使用 命令:
Q: oneof 字段能设置默认值吗?
A: 不能。oneof 字段未设置时, 返回 nil,需要在代码中处理 nil 情况。
Q: 字段编号 1-15 和 16+ 的性能差异有多大?
A: 字段编号 1-15 的 tag 编码只需 1 字节,16+ 需要 2 字节。对于高频消息(如每秒百万次),这个差异是可测量的。建议将 id、timestamp 等核心字段放在 1-15 范围内。
> 🔧 动手试试
>
> 1. 在你的 中新增一个 字段(编号 4),重新运行 生成代码,观察生成的 Go 结构体有什么变化
> 2. 故意将 字段的编号从 改为 ,重新生成代码,思考:如果有旧版本的客户端发来数据,会发生什么?
> 3. 运行性能基准测试:,对比 Protobuf 和 JSON 的序列化速度差异
课后检查清单
- 理解 Protobuf 的作用:类型安全的跨语言序列化
- 能编写基本的 .proto 文件
- 理解 message、service、repeated 等关键字
- 理解字段编号的重要性
- 了解 trpc create 代码生成流程
创建第一个 tRPC-Go 微服务
学习目标
- 使用 trpc-cmdline 生成项目代码
- 理解 tRPC-Go 项目结构
- 编写服务端业务逻辑
- 配置 trpc_go.yaml 启动服务
- 使用客户端测试调用服务
tRPC-Go 项目结构
> 本节目标:创建第一个可运行的 tRPC-Go 服务,掌握优雅关闭、健康检查和配置管理三项生产必备技能。学完本节后,你的服务将能够在 Kubernetes 中安全滚动更新,而不丢失任何请求。
通过 生成的项目结构:
trpcgo.yaml 配置
这是 tRPC-Go 的核心配置文件,类似于 Node.js 项目的 :
服务端实现
服务启动
客户端调用
tRPC-Go 支持两种调用方式:
1. tRPC 协议调用(高性能,内部服务间通信)
2. HTTP/JSON 调用(兼容性强,适合前端直接调用)
深入理解:优雅关闭(Graceful Shutdown)
> 优雅关闭:服务关闭时,先停止接收新请求,等待正在处理的请求完成,再退出进程。类似于前端的 ——在页面关闭前做清理工作。
> SIGTERM:Linux 系统信号,表示请求进程正常终止。Kubernetes 在滚动更新时会先发送 SIGTERM,等待 秒后再强制杀死进程。
> signal.NotifyContext:Go 1.16+ 引入的函数,将操作系统信号转化为 context 取消事件。当收到 SIGTERM 时,ctx.Done() 通道会被关闭,你可以用 监听它。
> ⚠️ 前端工程师注意
>
> 优雅关闭对前端工程师来说并不陌生:
> - 前端:
> - 后端: 监听关闭信号
>
> 为什么后端更重要?因为后端服务可能正在处理数据库事务、消息队列确认等操作,粗暴关闭会导致数据不一致。
> 优雅关闭是生产服务的必备能力。粗暴 kill 进程会导致正在处理的请求丢失、数据库连接未释放、消息未确认等问题。
为什么需要优雅关闭?
使用 signal.NotifyContext 实现优雅关闭
Kubernetes 中的优雅关闭配置
健康检查端点
> 健康检查:Kubernetes 和负载均衡器判断服务是否可用的标准机制。类似于前端的 属性,但更精细——分为三种探针:
> Liveness 探针:检查服务是否还活着(是否需要重启)。如果失败,Kubernetes 会重启 Pod。
> Readiness 探针:检查服务是否就绪(是否可以接收流量)。如果失败,Kubernetes 会从 Service 摘除该 Pod,但不重启。
Liveness vs Readiness
实现健康检查端点
在 tRPC-Go 服务中集成健康检查
配置管理最佳实践
> 12-Factor App 原则:配置与代码分离,通过环境变量注入。
配置结构体设计
.env 文件(本地开发)
单元测试示例
> 好的测试是代码质量的保障,也是重构的安全网。
测试服务实现
测试覆盖率
> 🔧 动手试试
>
> 1. 将上面的单元测试保存为 ,运行 ,观察输出格式
> 2. 为你的 方法添加一个新的测试用例(如:名称包含特殊字符)
> 3. 运行 并用 查看哪些代码未被测试覆盖
常见问题
Q: 服务收到 SIGTERM 后没有优雅关闭?
A: 确保使用 监听信号,并在收到信号后调用 。
Q: 健康检查返回 503 但服务正常?
A: 检查 是否在服务完全初始化后才调用,以及依赖检查(DB、Redis)是否正常。
Q: 环境变量配置如何在 Kubernetes 中注入?
A: 使用 ConfigMap 和 Secret:
Q: 单元测试如何 mock 外部依赖?**
A: 使用接口 + mock 实现,推荐 或 。
课后检查清单
- 理解 tRPC-Go 项目目录结构
- 能配置 trpc_go.yaml 文件
- 理解服务注册与启动流程
- 理解 context.Context 在请求中的作用
- 能编写服务端业务逻辑
- 理解客户端 Proxy 调用方式
阶段 2: 微服务进阶与 tRPC-Go 特性
掌握多服务交互、拦截器机制和 tRPC-Go 高级配置
预计学时: 2 周
多服务交互与服务发现
学习目标
- 理解微服务拆分原则
- 掌握服务间 RPC 调用模式
- 了解服务发现机制
- 实现 UserService 和 OrderService 的交互
微服务拆分
> 本节目标:理解微服务架构中服务发现的必要性,掌握服务注册、心跳机制和负载均衡策略的选择方法。学完本节后,你将能够设计一个具备服务治理能力的微服务系统。
> 微服务架构:将一个大型应用拆分为多个小型、独立运行的服务的架构模式。类似于前端的组件化开发——将一个大页面拆分为多个独立的组件,但尺度更大。
> 为什么需要服务发现?
>
> 想象一个微服务系统有 50 个服务实例,每个实例的 IP 地址会随着扩缩容、滚动更新而变化。如果硬编码 IP:
> - 每次扩容都要修改配置文件并重启服务
> - 滚动更新时旧 IP 失效,请求会失败
> - 无法实现负载均衡
>
> 服务发现 = DNS + 负载均衡器:类似于浏览器访问 时,DNS 会返回实际 IP,而不是硬编码 IP。服务发现就是微服务世界的 DNS。
在单体应用中,所有功能在一个进程内。微服务架构将系统拆分为独立服务,每个服务:
- 独立部署、独立扩缩容
- 拥有自己的数据存储
- 通过 RPC/HTTP 通信
拆分原则
- 单一职责:每个服务只做一件事(用户管理、订单处理等)
- 业务边界:按领域驱动设计(DDD)的限界上下文拆分
- 数据独立:每个服务管理自己的数据,避免共享数据库
服务间调用
在 tRPC-Go 中,服务 A 调用服务 B 非常直观:
服务发现
生产环境中,服务地址不是硬编码的,而是通过服务发现机制动态获取:
tRPC-Go 支持多种服务发现方式:
- 北极星(Polaris):腾讯开源的服务治理平台,适合大规模微服务
- Nacos:阿里巴巴开源的服务发现与配置管理,国内主流方案
- Consul/etcd:社区主流方案
- DNS:最简单的发现方式
- 直连:开发调试用
深入理解:服务注册与心跳机制
> 服务发现:微服务架构中,服务实例动态注册到一个中心化的注册中心,客户端通过查询注册中心来获取可用的服务地址。
> 注册中心:存储所有服务实例信息(服务名、IP、端口、健康状态)的中心化存储。常见实现:北极星(Polaris)、Consul、etcd。
> 心跳机制:服务实例定期向注册中心发送心跳包,证明自己还活着。类似于 WebSocket 的 ping/pong 机制——如果超时没有响应,就认为连接断开了。
> 理解服务注册的底层原理,才能在服务发现失败时快速定位问题。
服务注册的完整流程
心跳机制实现原理
tRPC-Go 中的服务注册配置
深入理解:负载均衡策略
> 不同的负载均衡策略适用于不同场景,选错了会导致性能问题。
主流负载均衡算法对比
一致性哈希:有状态服务的最佳选择
tRPC-Go 中配置负载均衡
服务降级与容错
> 当依赖服务不可用时,如何保证主服务的可用性?
降级策略
超时传递:避免级联超时
常见问题
Q: 服务调用报 "no available service"?
A: 按以下步骤排查:
1. 检查服务是否已正确注册到服务发现中心
2. 确保服务名与配置一致(大小写敏感)
3. 检查心跳是否正常(服务是否被摘除)
4. 开发环境可使用直连模式绕过服务发现:
Q: 负载均衡不均匀,某个实例压力特别大?
A: 检查是否使用了一致性哈希但 key 分布不均匀。可以切换为轮询或加权轮询。
Q: 服务重启后,旧实例还在服务发现列表中?
A: 这是心跳超时时间内的正常现象。可以在服务关闭时主动注销:
Q: 一致性哈希节点增减时,会影响多少请求?
A: 理论上只影响 的请求(N 为节点数)。虚拟节点数越多,影响越均匀。
> 🔧 动手试试
>
> 1. 在 中将 分别设置为 、,启动多个服务实例,观察请求分布情况
> 2. 模拟一个服务实例心跳超时(停止心跳发送),观察注册中心多久后将其摘除
> 3. 尝试实现一个简单的一致性哈希路由:同一用户 ID 的请求始终路由到同一个服务实例
课后检查清单
- 理解微服务拆分的单一职责原则
- 能实现两个服务之间的 RPC 调用
- 了解服务发现的几种方式
- 理解 context.Context 在服务间传递的作用
拦截器与插件机制
学习目标
- 理解拦截器(Filter/Interceptor)的概念
- 编写服务端拦截器:日志、认证
- 编写客户端拦截器:超时、重试
- 理解插件链的执行顺序
拦截器机制
tRPC-Go 的拦截器类似于 Express 的中间件或 Axios 的拦截器。它允许在请求处理前后插入通用逻辑:
拦截器类型
- 服务端拦截器(ServerFilter):处理入站请求(日志、认证、限流)
- 客户端拦截器(ClientFilter):处理出站请求(超时、重试、追踪)
拦截器签名
注册拦截器
生产级实现:令牌桶限流器
> 令牌桶是最常用的限流算法,支持突发流量,比漏桶更灵活。
令牌桶原理
完整令牌桶实现
生产级实现:三态熔断器
> 熔断器防止级联故障:当下游服务不可用时,快速失败而不是等待超时。
熔断器状态机
完整三态熔断器实现
生产级实现:指数退避重试(含 Jitter)
> 重试时加入随机抖动(Jitter),避免"惊群效应"——所有客户端同时重试导致服务雪崩。
JWT 认证拦截器
常见问题
Q: 拦截器执行顺序与预期不符?
A: 拦截器按配置顺序执行,类似洋葱模型。。
Q: 限流器在多实例部署时如何共享状态?
A: 单机令牌桶只适合单实例。多实例需要使用 Redis 实现分布式限流:
Q: 熔断器打开后,如何通知调用方?
A: 返回特定错误码(如 ),调用方可以根据错误码决定是否降级处理。
Q: JWT Token 过期后如何刷新?
A: 实现 Refresh Token 机制:短期 Access Token(15分钟)+ 长期 Refresh Token(7天)。Access Token 过期时,用 Refresh Token 换取新的 Access Token。
> 🔧 动手试试
>
> 1. 将令牌桶限流器的 设置为 5(每秒 5 个请求),用循环快速发送 20 个请求,观察哪些请求被拒绝
> 2. 将熔断器的 设置为 3,模拟下游服务连续失败 3 次,观察熔断器状态变化(Closed → Open → Half-Open)
> 3. 尝试将令牌桶、熔断器、重试三个拦截器组合使用,思考它们的执行顺序应该是怎样的(提示:限流 → 熔断 → 重试)
课后检查清单
- 理解拦截器的洋葱模型执行顺序
- 能编写服务端日志拦截器
- 能编写认证拦截器
- 了解 tRPC-Go 内置的插件生态
- 理解拦截器链的构建方式
阶段 3: LLM 集成与 tRPC-Agent-Go 入门
了解大语言模型集成基础,创建一个简单的智能对话 Agent
预计学时: 2 周
LLM 基础与模型对接
学习目标
- 理解 LLM 的基本概念和调用方式
- 了解 OpenAI API 的请求/响应格式
- 掌握 tRPC-Agent-Go 的 Model 模块
- 实现基本的模型调用
大语言模型(LLM)基础
> 本节目标:理解 LLM 的核心概念,掌握 Token 成本优化、参数调节和结构化输出技巧。学完本节后,你将能够独立调用 LLM API,并写出成本可控、输出可预期的生产级代码。
> 大语言模型(LLM):基于 Transformer 架构训练的大规模语言模型。对于后端开发者而言,LLM 本质上是一个"文本输入→文本输出"的 API 服务。你不需要理解内部的神经网络结构,只需要知道如何有效地调用它。
LLM 是基于 Transformer 架构训练的大规模语言模型。对于后端开发者而言,LLM 本质上是一个"文本输入→文本输出"的 API 服务。
核心概念
> Token:LLM 的最小处理单元。可以把 Token 想象为乐高积木——LLM 不是一个字一个字地处理文本,而是以 Token 为单位。英文中一个单词通常是 1-2 个 Token,中文中一个汉字通常是 1-2 个 Token。
> Context Window(上下文窗口):LLM 单次能处理的最大 Token 数。可以把它想象为人的短期记忆容量——超出这个范围的内容,LLM 就会"忘记"。GPT-4 的 Context Window 是 128K tokens,大约能容纳 10 万个汉字。
> Temperature:控制输出的随机性。可以把它想象为"创造力旋钮"——转到 0 是机器模式(确定性),转到 2 是诗人模式(高度随机)。
> System Prompt:定义 AI 角色和行为规则的指令。类似于前端的全局配置文件——它在每次对话开始时都会被发送给模型。
- Token:LLM 的基本处理单位,约等于 0.75 个英文单词或 0.5 个中文字符
- Context Window:模型单次能处理的最大 Token 数(GPT-4: 128K)
- Temperature:控制输出的随机性,0 = 确定性,1 = 创造性
- System Prompt:定义 AI 角色和行为规则的指令
OpenAI API 格式
tRPC-Agent-Go 的 Model 模块
tRPC-Agent-Go 提供了统一的模型抽象层,支持多种 LLM 后端:
国内模型配置(推荐)
深入理解:Token 计算与成本优化
> Token 是 LLM 的计费单位,理解 Token 计算能帮你大幅降低 API 成本。
Token 计算规律
成本计算示例
成本优化策略
深入理解:Temperature、Top-P、Top-K 参数
> 这三个参数控制 LLM 输出的"创造性",理解它们能让你精确控制输出质量。
Temperature:最常用的参数
使用建议:
Top-P(核采样)
Top-K
参数组合推荐
深入理解:Context Window 管理策略
> Context Window 是 LLM 的"工作记忆",超出限制会导致请求失败或截断。
Context Window 的组成
滑动窗口策略(最常用)
Token 预算管理
结构化输出:JSON Schema
> 让 LLM 输出结构化 JSON,而不是自由文本,是生产环境的最佳实践。
常见问题
Q: API 调用返回 401 错误?
A: 检查 API Key 是否正确,是否过期或已失效。
Q: 返回内容被截断?
A: 检查是否超出模型的 Context Window,或调整 参数。
Q: Temperature 设为 0 但输出还是不一样?
A: 部分模型在 Temperature=0 时仍有少量随机性。如需完全确定性,可以设置 参数(OpenAI 支持)。
Q: 如何估算一段文本的 token 数?
A: 使用 tiktoken 库(OpenAI 官方):
Q: 国内访问 OpenAI API 超时?
A: 使用国内模型(通义千问、文心一言等),或配置代理。推荐使用环境变量 配置代理。
> 🔧 动手试试
>
> 1. 将 Temperature 分别设置为 0、0.7、1.5,对同一个问题(如"给我写一首关于春天的诗")发送 3 次请求,对比输出差异
> 2. 编写一个信息提取的 System Prompt,要求 LLM 以 JSON 格式返回结果,并编写解析代码
> 3. 计算一次对话的成本:用 tiktoken 统计你的 System Prompt 占用多少 Token,尝试将它压缩到原来的 1/3
课后检查清单
- 理解 Token、Context Window、Temperature 等 LLM 概念
- 了解 OpenAI API 的消息格式
- 了解 tRPC-Agent-Go 支持的模型后端
- 理解流式输出的工作原理
创建第一个 LLMAgent
学习目标
- 理解 Agent 的概念:LLM + 工具 + 记忆
- 使用 tRPC-Agent-Go 创建 LLMAgent
- 配置 Runner 管理 Agent 生命周期
- 实现基本的对话交互
Agent = LLM + 工具 + 记忆
Agent 不是简单的 API 调用,而是一个能自主决策和行动的智能体:
tRPC-Agent-Go 的 Agent 体系
- LLMAgent:基础智能 Agent,包含 LLM 推理 + 工具调用能力
- Runner:Agent 的执行器,管理对话生命周期和事件流
创建 LLMAgent
Prompt Engineering 核心技巧
> Prompt 是与 LLM 沟通的语言,掌握这些技巧能让 LLM 输出质量提升 3-10 倍。
技巧1:Chain-of-Thought(思维链)
让 LLM 展示推理过程,而不是直接给出答案。对于复杂问题效果显著。
适用场景:数学计算、逻辑推理、代码调试、多步骤规划
技巧2:Few-Shot(少样本示例)
提供 2-5 个示例,让 LLM 学习你期望的输出格式和风格。
适用场景:格式化输出、分类任务、风格模仿、数据提取
技巧3:Role Prompting(角色扮演)
给 LLM 一个具体的角色,能显著提升专业领域的回答质量。
技巧4:输出格式约束
明确指定输出格式,避免 LLM 自由发挥。
技巧5:负面约束
明确告诉 LLM 不要做什么,往往比正面指令更有效。
完整 SSE 流式响应处理
> Server-Sent Events (SSE) 是 LLM 流式输出的标准协议,实现"打字机效果"。
SSE 协议格式
Go 服务端:实现 SSE 流式响应
TypeScript 前端:消费 SSE 流
常见问题
Q: Agent 响应很慢?
A: 检查网络到 LLM API 的延迟,或使用本地模型(Ollama)。流式响应可以改善用户体验(首字节时间更短)。
Q: CoT 提示让输出变长了,成本增加?
A: 是的,CoT 会增加输出 token 数。可以在最终答案提取后,只保留结论部分。
Q: Few-Shot 示例应该放多少个?
A: 通常 2-5 个足够。太多会占用 Context Window,且效果提升有限。
Q: 流式响应中途断开怎么处理?
A: 前端检测到连接断开后,可以重新发起请求,并在 System Prompt 中告知 LLM 继续之前的回答。
> 🔧 动手试试
>
> 1. 对同一个问题,分别用普通提问和 CoT 提问方式,对比输出质量和准确性的差异
> 2. 编写一个 Few-Shot Prompt,让 LLM 按照你指定的格式输出代码审查结果,并验证输出格式的一致性
> 3. 实现一个完整的 SSE 流式对话:Go 服务端转发 LLM 流式输出,前端展示打字机效果
课后检查清单
- 理解 Agent = LLM + 工具 + 记忆
- 了解 LLMAgent 的创建方式
- 理解 Runner 的职责:会话管理、事件分发、工具调度
- 能实现基本的多轮对话流程
阶段 10: 综合实战:StudyOS — AI 学习教练
融合全部知识,构建一个完整的 AI 驱动个人学习系统
预计学时: 4 周
需求分析与系统架构设计
学习目标
- 理解 StudyOS 产品的核心需求与用户场景
- 设计微服务拆分方案与 tRPC 服务接口
- 规划 API Gateway 路由与拦截器链
- 掌握服务间数据流与依赖关系
StudyOS 产品概述
StudyOS 是一个 AI 驱动的个人学习教练系统,帮助用户完成从目标设定到学习复盘的全流程:
核心功能模块
微服务架构设计
每个服务通过 tRPC 注册,Gateway 根据请求路径分发到对应服务。
服务间通信
- Gateway → 各服务:tRPC 单向调用
- PlanAgent → GoalService:查询用户目标
- QuizAgent → KnowledgeRAG:基于知识库出题
- ProfileService → 所有服务:提供用户画像上下文
拦截器链设计
每个请求经过统一的拦截器链:
1. Auth:验证用户身份,注入 userID
2. RateLimit:防止滥用,保护 LLM 调用成本
3. Logging:记录请求日志,支持链路追踪
Protobuf 服务定义(设计参考)
数据库设计
StudyOS 采用多数据库组合策略:
完整项目参考
实战项目完整代码可参考:
- GitHub: (官方示例)
- 腾讯工蜂: 内部项目仓库(需权限)
运行代码示例
下方代码模拟了 StudyOS 的完整架构骨架——服务注册、Gateway 路由、拦截器链。运行后观察请求如何经过拦截器被分发到各服务。
🎯 学习强化
每日微任务(Day 1)
1. 画出 StudyOS 的服务依赖图(不看文档,凭记忆)
2. 列出每个服务的职责(一句话描述)
3. 思考:如果 Goal Service 宕机,哪些功能会受影响?
费曼复述任务
> 向一位不懂技术的朋友解释:StudyOS 是如何知道你今天该学什么的?
参考要点:用户目标 → 计划拆解 → 知识检索 → 个性化推送
Anki 卡片
Q: StudyOS 中 Gateway 的作用是什么?
A: 统一入口,负责认证鉴权、路由转发、限流熔断,将外部请求分发到内部各微服务。
Q: 为什么知识库要用向量数据库而不是 MySQL?
A: 向量数据库支持语义相似度检索(找"意思相近"的内容),MySQL 只支持精确匹配,无法理解语义。
Q: StudyOS 的数据库选型中,Redis 的作用是什么?
A: 存储热点数据(会话缓存、限流计数器),利用其高速读写特性,避免频繁查询 MySQL。
代码审查 Checklist
- [ ] 服务间通信是否都通过 tRPC 接口,而非直接调用?
- [ ] 每个服务是否有独立的数据库,避免跨服务直接访问数据库?
- [ ] Gateway 是否实现了认证鉴权,防止未授权访问内部服务?
验收准则
- [ ] 能够独立画出 StudyOS 完整架构图(包含所有服务和数据流)
- [ ] 能够解释每个服务的职责和数据库选型理由
- [ ] 能够识别架构中的单点故障并提出改进方案
课后检查清单
- 理解 StudyOS 产品的 5 大功能模块
- 能画出微服务架构图并说明各服务职责
- 掌握 tRPC 服务注册与 Gateway 路由设计
- 理解拦截器在认证、限流中的作用
- 了解 Protobuf 服务定义的最佳实践
目标设定与计划生成 Agent
学习目标
- 实现学习目标的结构化管理服务
- 使用 LLM 推理将目标拆分为学习计划
- 为 Agent 注册日历、进度查询等工具
- 掌握 FunctionTool 的定义与调用流程
从目标到计划:Agent 的核心价值
StudyOS 的第一个智能功能——用户告诉系统"我想学什么",Agent 自动拆分为可执行的学习计划:
GoalService:目标管理
GoalService 是纯 CRUD 服务,不涉及 LLM:
PlannerAgent:智能计划生成
PlannerAgent 是真正的 AI Agent,它:
1. 接收用户目标
2. 调用 LLM 分析目标难度和所需知识点
3. 通过工具查询用户当前进度和可用时间
4. 生成个性化的分阶段学习计划
工具调用设计
PlannerAgent 需要以下工具辅助决策:
Agent 决策流程
tRPC-Agent-Go 中的工具注册
运行代码示例
下方代码完整模拟了 GoalService + PlannerAgent 的协作流程。运行后观察 Agent 如何自主决定调用工具、整合信息并生成学习计划。
🎯 学习强化
每日微任务(Day 2)
1. 手写一个 函数,将用户输入的目标字符串解析为结构化数据
2. 设计 的数据结构,包含:步骤ID、描述、预计时长、依赖步骤
3. 思考:如果用户修改了目标,已生成的计划如何更新?
费曼复述任务
> 向同事解释:Goal-Plan Agent 是如何将"我想学 Go"这句话变成一个具体的学习计划的?
参考要点:目标解析 → LLM 推理 → 计划生成 → 里程碑拆解 → 存储
Anki 卡片
Q: Goal-Plan Agent 使用什么模式将目标转化为计划?
A: Plan-and-Execute 模式:先由 Planner Agent 生成完整计划,再由 Executor Agent 逐步执行每个步骤。
Q: 为什么计划生成需要用 LLM 而不是规则引擎?
A: 用户目标是自然语言,千变万化,规则引擎无法覆盖所有情况。LLM 能理解语义,生成个性化计划。
Q: 计划生成失败时应该如何处理?
A: 降级策略:返回通用模板计划,同时记录错误日志,异步重试生成个性化计划。
常见坑与修复
坑:LLM 生成的计划格式不稳定**
验收准则
- [ ] Goal-Plan Agent 能将自然语言目标转化为包含至少 5 个步骤的学习计划
- [ ] 计划生成失败时有降级处理,不影响用户体验
- [ ] 单元测试覆盖率 ≥ 80%
课后检查清单
- 能实现 GoalService 管理用户学习目标
- 理解 PlannerAgent 如何调用 LLM 生成计划
- 掌握 FunctionTool 的注册和自动调用机制
- 能实现日历工具和进度查询工具
- 理解 Agent 的决策→工具调用→结果整合流程
知识库 RAG 学习助手
学习目标
- 构建课程材料的知识库:加载、分块、向量化
- 实现语义检索与相关性排序
- 将检索结果注入 Prompt 实现 RAG 答疑
- 实现引用溯源,让回答附带文档来源
让 Agent 基于真实文档回答问题
StudyOS 的知识库模块让用户可以导入学习材料,Agent 基于这些材料精准答疑,而不是凭空"幻觉":
RAG 的四个阶段
分块策略
StudyOS 采用滑动窗口分块,每个 chunk 包含部分重叠内容,避免关键信息被截断:
Prompt 模板设计
RAG 的核心在于将检索结果注入 System Prompt:
tRPC-Agent-Go 中的 RAG
运行代码示例
下方代码构建了一个完整的学习知识库 RAG 系统。包含文档加载、滑动窗口分块、TF 向量化、Top-K 检索和带引用的回答生成。
🎯 学习强化
每日微任务(Day 3)
1. 实现一个 函数,将一篇 Markdown 文章按标题分块
2. 计算两个向量的余弦相似度(手写,不用库)
3. 测试:向知识库添加 3 篇文章,然后用不同的查询语句检索,观察结果
费曼复述任务
> 向产品经理解释:为什么 StudyOS 的知识库搜索比普通关键词搜索更智能?
参考要点:向量化 → 语义理解 → 相似度计算 → 混合检索
Anki 卡片
Q: RAG 中 Chunking 的目的是什么?
A: 将长文档切分为适合检索的小片段,避免向量化时语义被稀释,同时控制检索结果的大小。
Q: 混合检索(BM25 + 向量)相比单一检索有什么优势?
A: BM25 擅长精确关键词匹配,向量检索擅长语义理解。混合检索取长补短,提升召回率和精度。
Q: Re-ranking 在 RAG 流水线中的作用是什么?
A: 对粗检索结果进行精排,使用更精确的 Cross-Encoder 模型重新评分,提升最终结果的相关性。
常见坑与修复
坑:知识库更新后搜索不到新内容
验收准则
- [ ] 知识库能正确存储和检索文档(Precision@3 ≥ 0.7)
- [ ] 混合检索比单一向量检索的召回率提升 ≥ 10%
- [ ] 知识库更新后,新内容能在 1 秒内被检索到
课后检查清单
- 理解文档分块策略(固定大小 vs 语义分块)
- 掌握 TF 词频向量化与余弦相似度计算
- 能实现 Top-K 语义检索
- 理解 RAG 的 Prompt 模板设计
- 能为回答添加引用来源标记
练习与测验 Agent
学习目标
- 实现基于知识库内容的自动出题 Agent
- 实现答案批改与讲解 Agent
- 使用 CycleAgent 迭代调整题目难度
- 掌握 ChainAgent 和 ParallelAgent 在测验场景的应用
让 AI 出题、批改、调难度
StudyOS 的练习场是学习闭环的关键——用户学完知识后,Agent 自动生成针对性练习题,批改答案并给出讲解:
三个 Agent 的协作
ChainAgent:出题→批改流水线
这是一个经典的 ChainAgent 模式——前一个 Agent 的输出作为下一个 Agent 的输入。
CycleAgent:难度自适应
当用户连续答对/答错时,CycleAgent 迭代调整难度:
ParallelAgent:多类型题目
同时生成不同类型的题目:
tRPC-Agent-Go 中的实现
运行代码示例
下方代码完整模拟了练习测验系统的三种编排模式。运行后观察 Agent 如何出题、批改、以及通过循环迭代调整题目难度。
🎯 学习强化
每日微任务(Day 4)
1. 设计一个 数据结构,支持单选、多选、填空三种题型
2. 实现 函数,对用户答案进行评分
3. 思考:如何根据用户的历史答题记录,自动调整题目难度?
费曼复述任务
> 向朋友解释:StudyOS 的 Quiz Agent 是如何知道你哪里没学好,并针对性出题的?
参考要点:学习记录 → 薄弱点识别 → 知识库检索 → 题目生成 → 答案评估
Anki 卡片
Q: Quiz Agent 如何避免重复出相同的题目?
A: 维护已出题目的 ID 集合(存储在 Redis),生成新题目时过滤已出过的题目。
Q: 如何评估 LLM 生成的题目质量?
A: 使用另一个 LLM 作为评审(Judge LLM),对题目的准确性、难度、清晰度进行评分,低于阈值的题目丢弃重新生成。
Q: 用户答错题目后,系统应该如何响应?
A: 提供详细解析(解释正确答案),将该知识点标记为"需要复习",在下次学习计划中安排复习。
常见坑与修复
坑:LLM 生成的答案选项不合理
验收准则
- [ ] Quiz Agent 能根据学习内容生成相关题目(题目与内容相关性 ≥ 90%)
- [ ] 答案评估准确率 ≥ 95%(与标准答案对比)
- [ ] 同一知识点不重复出题(在 24 小时内)
课后检查清单
- 理解出题 Agent 如何基于知识库生成题目
- 掌握批改 Agent 的评分与反馈逻辑
- 能使用 CycleAgent 实现难度自适应
- 理解 ChainAgent(出题→答题→批改)的流水线
- 了解 ParallelAgent 并行出多类型题目的模式
学习画像与记忆系统
学习目标
- 设计学习者画像数据模型(薄弱点、偏好、进度)
- 实现短期会话记忆与长期记忆的分层存储
- 基于记忆实现个性化学习推荐
- 掌握记忆的提取、更新和衰减策略
让 Agent 真正"了解"用户
StudyOS 的个性化能力来自记忆系统——Agent 记住用户的学习偏好、薄弱知识点和历史进度,从而提供针对性辅导:
记忆分层架构
学习画像模型
记忆提取策略
Agent 在每次交互时自动从用户消息中提取有价值的信息:
个性化推荐
基于画像数据,Agent 可以:
1. 推荐薄弱知识点的练习题
2. 按用户偏好格式(文字/视频/代码)推送内容
3. 动态调整学习计划的节奏和难度
tRPC-Agent-Go 中的记忆
运行代码示例
下方代码完整模拟了 StudyOS 的记忆系统——短期会话、长期记忆、学习画像构建和个性化推荐。运行后观察 Agent 如何跨会话记住用户信息。
🎯 学习强化
每日微任务(Day 5)
1. 设计 数据结构,包含:学习风格、知识掌握度、学习节奏偏好
2. 实现 函数,根据一次学习行为更新用户画像
3. 思考:如何用 A/B 测试验证个性化推荐的效果?
费曼复述任务
> 向产品经理解释:StudyOS 的用户画像是如何影响学习体验的?
参考要点:行为数据收集 → 画像更新 → 个性化推荐 → 效果反馈
Anki 卡片
Q: 用户画像数据应该存储在哪里?
A: 基础画像(学习风格、偏好)存 MySQL;实时行为数据存 Redis(快速读写);历史行为日志存 MongoDB/ES(分析用)。
Q: 如何防止用户画像数据泄露?
A: 数据脱敏(不存储原始行为,只存聚合统计);访问控制(只有 Profile Service 能读写画像数据);传输加密(HTTPS/TLS)。
Q: 冷启动问题:新用户没有历史数据,如何个性化推荐?
A: 使用协同过滤(找相似用户的行为)或基于内容的推荐(根据用户填写的目标和背景)。
常见坑与修复
坑:用户画像更新频率过高导致数据库压力大
验收准则
- [ ] 用户画像能正确反映学习行为(完成 5 次学习后,画像准确率 ≥ 80%)
- [ ] 个性化推荐的点击率比随机推荐高 ≥ 20%
- [ ] 用户画像数据有访问控制,不同用户无法互相访问
课后检查清单
- 理解短期会话记忆 vs 长期记忆的区别
- 能实现 SessionStore 管理多轮对话上下文
- 能实现 MemoryStore 存储用户长期偏好
- 掌握学习画像的自动提取与更新
- 理解个性化推荐的实现思路
全链路集成与可观测性
学习目标
- 将 StudyOS 所有服务整合为完整的微服务系统
- 接入 OpenTelemetry 实现全链路追踪
- 收集关键业务指标(学习时长、测验通过率等)
- 设计 LLM 调用成本治理与上线部署方案
把所有服务串起来
前 5 节课我们分别实现了 StudyOS 的各个模块。现在将它们整合为一个完整的微服务系统,并接入可观测性:
集成架构
OpenTelemetry 接入要点
Span 创建与传播:每个服务入口创建 Span,跨服务调用时传递 TraceID:
关键指标(Metrics):
LLM 成本治理
在生产环境中,LLM 调用是主要成本来源。StudyOS 需要:
1. Token 计量:每次 LLM 调用记录 input/output token 数
2. 预算控制:单用户每日 Token 上限
3. 缓存策略:相似问题复用缓存结果
4. 降级方案:超预算时降级为规则引擎
部署方案
运行代码示例
下方代码是 StudyOS 的完整联调模拟——一个用户从设定目标到完成测验的全流程,包含链路追踪和指标收集。这是课程的终极代码示例。
🎯 学习强化(阶段10综合)
每日微任务(Day 6)
1. 为 StudyOS 的关键接口添加 OTel Span(至少 3 个服务)
2. 在 Grafana 中创建一个仪表盘,展示:QPS、P99 延迟、错误率
3. 模拟一次故障:关闭 Knowledge Service,观察系统如何降级
费曼复述任务
> 向新同事解释:当 StudyOS 出现性能问题时,你会如何用可观测性工具定位问题?
参考步骤:
1. 查看 Grafana 指标,确认哪个服务的延迟/错误率异常
2. 在 Jaeger 中找到慢请求的 Trace,定位到具体的 Span
3. 查看对应时间段的结构化日志(通过 traceid 关联)
4. 使用 pprof 分析 CPU/内存热点
Anki 卡片
Q: Liveness 探针和 Readiness 探针的区别是什么?
A: Liveness 失败 → 重启 Pod(检查进程是否存活);Readiness 失败 → 从 Service 摘除(检查是否可以接收流量)。
Q: 为什么生产环境不能 100% 采样链路追踪?
A: 100% 采样会产生大量数据,占用存储和网络带宽,影响服务性能。生产环境通常采样 1%-10%,对错误请求 100% 采样。
Q: 结构化日志(JSON 格式)相比普通文本日志有什么优势?
A: 可以被日志收集系统(ELK/Loki)自动解析,支持按字段过滤和聚合,便于自动化告警。
Q: pprof 中 和 的区别?
A: 是当前正在使用的内存(排查内存泄漏); 是历史总分配量(排查频繁分配导致的 GC 压力)。
代码审查 Checklist(阶段10综合)
- [ ] 所有 tRPC Handler 是否都有 OTel Span?
- [ ] 错误是否通过 记录到 Trace?
- [ ] 关键业务指标(请求数、延迟、Token 用量)是否有 Metrics?
- [ ] 日志是否包含 字段,便于与 Trace 关联?
- [ ] 健康检查端点是否区分了 liveness 和 readiness?
- [ ] 是否有告警规则(错误率 > 1% 或 P99 > 5s 时告警)?
常见坑与修复
坑1:Span 没有正确传播
坑2:Defer 导致 Span 提前结束
阶段10验收准则(完整)
功能验收
- [ ] StudyOS 所有核心功能正常运行(目标设定、计划生成、知识检索、测验、画像)
- [ ] 端到端流程:用户设定目标 → 生成计划 → 学习内容 → 完成测验 → 更新画像
性能验收
- [ ] API 响应时间 P99 < 3 秒(LLM 调用除外)
- [ ] 并发 100 用户时,系统无明显性能下降
可观测性验收
- [ ] Jaeger 中能看到完整的跨服务调用链路
- [ ] Grafana 仪表盘展示 QPS、错误率、P99 延迟
- [ ] 日志中包含 traceid,能与 Jaeger 关联
工程质量验收**
- [ ] 单元测试覆盖率 ≥ 80%
- [ ] 所有服务有 liveness/readiness 健康检查
- [ ] 服务宕机时有降级处理,不影响其他服务
课后检查清单
- 能将多个服务通过 Gateway 统一调度
- 理解 OpenTelemetry Span 的创建与传播
- 能为业务流程添加链路追踪标记
- 掌握 Metrics 指标收集(计数器、直方图)
- 了解 LLM Token 成本监控与预算控制
- 理解微服务容器化部署的基本流程
阶段 11: 综合实战:AppForge — AI 应用生成平台
使用 tRPC-Go + tRPC-Agent-Go + CodeBuddy Agent SDK,构建一个 AI 驱动的 Web 应用生成平台服务端
预计学时: 2–3 周
需求分析与 AppForge 架构设计
学习目标
- 理解 AI 应用生成平台(AppForge)的核心产品需求与用户场景
- 设计 AppForge 的微服务拆分方案:Gateway、DialogService、CodeGenService、PreviewService
- 掌握 tRPC-Go 服务骨架的搭建:proto 定义、服务注册、HTTP 网关配置
- 理解 AppForge 与 tRPC-Agent-Go、CodeBuddy Agent SDK 的集成关系
AppForge 是什么?
AppForge(应用锻造厂)是一个 AI 驱动的 Web 应用生成平台服务端。用户通过自然语言描述需求,平台自动生成完整的 React 应用代码,并提供实时预览和代码导出功能。
本阶段是课程的进阶综合实战,将前 10 阶段的所有技能融合在一个真实的生产级项目中:
与前 10 阶段的技能映射
AppForge 微服务架构
AppForge 由 4 个核心微服务组成:
各服务职责
tRPC-Go 服务骨架
Proto 定义
AppForge 的核心接口定义( 文件):
trpcgo.yaml 配置
运行代码示例
下方代码完整模拟了 AppForge 的微服务架构——服务注册、Gateway 路由、拦截器链和服务间调用。运行后观察一个完整的"用户创建项目→生成代码→获取预览"请求链路。
🎯 学习强化
每日微任务(Day 1)
1. 画出 AppForge 的完整数据流图(用户输入 → 代码生成 → 预览)
2. 对比 AppForge 和 StudyOS 的架构差异,列出 3 个关键不同点
3. 思考:AppForge 的代码生成服务如何保证生成结果的安全性?
Anki 卡片
Q: AppForge 中 CodeGen Service 的核心职责是什么?
A: 接收用户的应用描述,通过 Agent Pipeline 生成可运行的代码,并将代码存储到文件系统或对象存储。
Q: 为什么 AppForge 需要 Sandbox 环境?**
A: 用户生成的代码可能包含恶意代码或无限循环,Sandbox 提供隔离的执行环境,防止影响主系统。
验收准则
- [ ] 能够独立描述 AppForge 的完整架构(服务、数据流、存储)
- [ ] 能够解释 AppForge 与 StudyOS 的架构差异
课后检查清单
- 能画出 AppForge 的微服务架构图并说明各服务职责
- 理解 DialogService(对话管理)、CodeGenService(代码生成)、PreviewService(预览)的分工
- 掌握 tRPC-Go 服务注册与 HTTP 网关路由设计
- 理解 AppForge 与 Lovable.dev 的功能对应关系
- 能运行架构模拟代码,观察服务注册与请求路由流程
CodeBuddy Agent SDK 集成 —— Go 服务端的 AI 引擎
学习目标
- 理解 CodeBuddy Agent SDK 的核心 API:query()、流式响应、会话管理
- 掌握 Go 服务端通过子进程调用 TypeScript SDK 的桥接模式
- 实现 Go 服务端与 CodeBuddy SDK 的完整桥接层(Bridge Pattern)
- 理解 SDK 的权限控制(permissionMode)、Hook 系统和会话恢复机制
为什么需要桥接层?
CodeBuddy Agent SDK 目前仅提供 TypeScript 和 Python 版本,而 AppForge 的服务端使用 Go 编写。这意味着我们需要设计一个桥接层(Bridge),让 Go 服务端能够调用 TypeScript SDK 的能力。
这是一个非常实用的工程模式,在实际生产中广泛使用:
CodeBuddy Agent SDK 核心 API
TypeScript SDK 基础用法
核心参数说明
permissionMode 三种模式
> AppForge 使用 :因为服务端运行在隔离的沙箱环境中,不需要人工确认。
Hook 系统
SDK 提供 Hook 机制,可以在 Agent 执行的关键节点插入自定义逻辑:
会话恢复
SDK 支持通过 恢复之前的对话上下文:
Go 桥接层设计
方案一:子进程调用(推荐)
Go 通过 启动 Node.js 子进程,执行 TypeScript 脚本:
方案二:HTTP 微服务(适合高并发)
将 TypeScript SDK 封装为独立的 HTTP 微服务:
优点:可独立扩缩容,支持连接池复用
缺点:增加网络开销,需要额外维护一个服务
运行代码示例
下方代码完整模拟了 AppForge 的 SDK 桥接层——Go 服务端如何通过子进程调用 TypeScript SDK,以及如何处理超时、错误和重试。运行后观察完整的桥接调用流程。
🎯 学习强化
每日微任务(Day 2)
1. 实现一个 接口,包含 、、 三个方法
2. 为 编写单元测试,使用 Mock 替代真实 SDK
3. 思考:如何设计 SDK 版本升级策略,保证向后兼容?
Anki 卡片
Q: 为什么需要 SDK Bridge 层而不是直接调用 SDK?
A: 解耦业务逻辑和 SDK 实现,便于替换 SDK 版本、Mock 测试、添加统一的重试/限流/日志逻辑。
Q: SDK Bridge 中如何处理 SDK 版本不兼容的问题?
A: 使用适配器模式(Adapter Pattern),为不同版本的 SDK 实现相同的接口,业务代码只依赖接口。
常见坑与修复
坑:SDK 调用没有超时控制
验收准则
- [ ] SDK Bridge 接口定义清晰,有完整的注释
- [ ] 单元测试使用 Mock,不依赖真实 SDK
- [ ] 所有 SDK 调用都有超时控制
课后检查清单
- 理解 CodeBuddy Agent SDK 的 TypeScript API 设计
- 掌握 Go os/exec 子进程调用 TypeScript 脚本的方式
- 能实现 SDKBridge:Go → 子进程 → TypeScript SDK → AI 响应
- 理解 permissionMode 的三种模式及其适用场景
- 能处理 SDK 调用的超时、错误和重试逻辑
- 运行代码示例,观察 Go 服务端调用 SDK 桥接层的完整流程
多 Agent 协作流水线 —— 需求分析到代码生成
学习目标
- 使用 tRPC-Agent-Go 构建三阶段 Agent 流水线:需求分析→代码生成→质量审查
- 掌握 ChainAgent 串联多个 Agent 并传递上下文的方式
- 理解 ParallelAgent 并行生成多个组件的应用场景
- 学会在 CodeGenAgent 中调用 CodeBuddy SDK 桥接层
为什么需要多 Agent 流水线?
直接让一个 AI 生成完整应用,往往会出现以下问题:
- 需求理解不准确:AI 直接生成代码,可能误解用户意图
- 代码质量参差不齐:没有审查环节,生成的代码可能有 bug
- 大型项目生成慢:串行生成所有文件,用户等待时间长
AppForge 使用三阶段 Agent 流水线解决这些问题:
tRPC-Agent-Go 中的实现
ChainAgent:串联三个阶段
ParallelAgent:并行生成组件
Agent 间的上下文传递
ChainAgent 的核心机制:前一个 Agent 的输出作为下一个 Agent 的输入。
流水线设计要点
1. 结构化输出
每个 Agent 的输出必须是结构化的 JSON,而不是自由文本,这样下一个 Agent 才能可靠地解析:
2. 错误传播
流水线中任何一个 Agent 失败,整个流水线应该优雅地停止并返回错误:
3. 并行生成的合并策略
ParallelAgent 并行生成多个组件后,需要一个合并函数将结果整合:
运行代码示例
下方代码完整模拟了 AppForge 的三阶段 Agent 流水线——需求分析、代码生成(含并行组件生成)和质量审查。运行后观察每个 Agent 的输入输出,以及并行生成的加速效果。
🎯 学习强化
每日微任务(Day 3)
1. 画出 AppForge Agent Pipeline 的完整流程图(需求分析 → 架构设计 → 代码生成 → 测试)
2. 为 Pipeline 的每个阶段设计错误处理策略(重试/跳过/终止)
3. 实现一个 结构,记录每个阶段的耗时和 Token 用量
Anki 卡片
Q: AppForge 的 Agent Pipeline 包含哪几个阶段?
A: 需求分析 Agent → 架构设计 Agent → 代码生成 Agent → 代码审查 Agent → 测试生成 Agent
Q: Pipeline 中某个阶段失败时,应该如何处理?
A: 根据阶段重要性决定:关键阶段(需求分析)失败则终止;非关键阶段(测试生成)失败则跳过,记录警告。
常见坑与修复
坑:Pipeline 阶段间数据传递不清晰
验收准则
- [ ] Pipeline 能完整执行所有阶段,生成可运行的代码
- [ ] 每个阶段的耗时和 Token 用量有记录
- [ ] 单个阶段失败不影响其他阶段(有降级处理)
课后检查清单
- 理解三阶段流水线:RequirementsAgent → CodeGenAgent → ReviewAgent
- 掌握 ChainAgent 的输入输出传递机制
- 能使用 ParallelAgent 并行生成 Header、Main、Footer 等组件
- 理解 CodeGenAgent 如何调用 CodeBuddy SDK 桥接层
- 能运行代码示例,观察完整的流水线执行过程
项目上下文管理 —— 会话、记忆与版本快照
学习目标
- 设计 AppForge 的项目上下文模型(ProjectContext:描述、技术栈、文件、对话历史)
- 使用 tRPC-Agent-Go 的 SessionStore 管理多轮对话,关联 CodeBuddy SDK 的 session_id
- 实现跨轮次的代码迭代:第一轮生成基础组件,第二轮基于上下文添加功能
- 设计项目快照(Snapshot)机制,支持版本回滚
为什么需要项目上下文管理?
AppForge 的核心体验是持续迭代——用户不是一次性生成完整应用,而是通过多轮对话逐步完善:
这需要一个完善的项目上下文管理系统。
ProjectContext 数据模型
会话关联:tRPC-Agent-Go SessionStore ↔ CodeBuddy SDK
AppForge 需要将两个会话系统关联起来:
关联策略:在 中存储 ,每次调用 SDK 时传入该 ID,实现上下文恢复:
版本快照机制
每次成功生成代码后,自动创建快照:
快照存储策略
AppForge 使用全量快照 + 定期清理策略,保留最近 20 个版本。
运行代码示例
下方代码完整模拟了 AppForge 的项目上下文管理系统——多轮迭代修改、版本快照创建和回滚。运行后观察每轮修改如何基于上一轮的上下文,以及版本回滚的效果。
🎯 学习强化
每日微任务(Day 4)
1. 实现一个 ,支持多轮迭代时的上下文压缩
2. 测试:生成一个应用后,进行 5 轮迭代修改,观察 Token 用量变化
3. 思考:如何在保留关键上下文的同时,最大化压缩 Token 用量?
Anki 卡片
Q: AppForge 中为什么需要 Context Management?
A: 多轮迭代时,历史代码和对话会不断累积,超出 LLM 的 Context Window 限制。需要智能压缩,保留关键信息。
Q: 代码生成场景中,哪些上下文是"关键信息"必须保留?
A: 当前版本的完整代码、用户的核心需求、最近 2-3 轮的修改指令。历史的中间版本可以压缩为摘要。
验收准则
- [ ] 10 轮迭代后,Token 用量不超过单次生成的 3 倍
- [ ] 上下文压缩后,生成质量不明显下降(人工评估)
课后检查清单
- 理解 ProjectContext 的数据模型设计
- 掌握 SessionStore 与 CodeBuddy SDK session_id 的关联方式
- 能实现多轮迭代:每轮修改都基于上一轮的代码上下文
- 理解版本快照的创建、存储和回滚机制
- 能运行代码示例,观察跨轮次的上下文传递和版本管理
流式响应与实时预览服务
学习目标
- 理解 tRPC-Go Server Streaming 的实现方式
- 掌握将 CodeBuddy SDK 的 AsyncGenerator 流式输出桥接到 tRPC-Go Server Streaming
- 实现逐 chunk 输出代码片段并附带进度信息的流式生成体验
- 设计预览服务:代码沙箱、静态文件服务和预览 URL 生成
为什么需要流式响应?
代码生成是一个耗时操作(通常需要 10-60 秒)。如果等待全部生成完成再返回,用户体验很差:
AppForge 使用 tRPC-Go Server Streaming 实现流式代码生成。
tRPC-Go Server Streaming
Proto 定义
服务端实现
客户端接收
TypeScript SDK 流式输出桥接
CodeBuddy SDK 使用 返回流式消息,需要桥接到 Go 的 channel:
预览服务设计
沙箱隔离
每个项目的预览运行在独立的沙箱中,防止代码互相干扰:
预览流程
预览 URL 策略
AppForge 使用子域名策略,每个项目有独立的预览域名。
运行代码示例
下方代码完整模拟了 AppForge 的流式代码生成和预览服务——逐文件流式输出代码片段、进度追踪,以及预览 URL 的生成和沙箱管理。运行后观察流式输出的实时效果。
🎯 学习强化
每日微任务(Day 5)
1. 实现一个 SSE 端点,将代码生成进度实时推送给前端
2. 在前端实现 SSE 消费者,展示实时生成进度条
3. 测试:模拟网络中断,验证 SSE 重连机制是否正常工作
Anki 卡片
Q: SSE 和 WebSocket 的主要区别是什么?
A: SSE 是单向(服务端→客户端),基于 HTTP,自动重连;WebSocket 是双向,需要升级协议,适合实时交互。代码生成进度推送用 SSE 更合适。
Q: SSE 断线重连时,如何避免重复接收已处理的事件?
A: 使用 头,客户端重连时携带最后收到的事件 ID,服务端从该 ID 之后继续发送。
常见坑与修复
坑:SSE 连接被 Nginx 缓冲
验收准则
- [ ] 代码生成进度能实时推送到前端(延迟 < 500ms)
- [ ] 网络中断后,SSE 能自动重连并继续接收
- [ ] 生成完成后,SSE 连接正常关闭
课后检查清单
- 理解 tRPC-Go Server Streaming 与普通 RPC 的区别
- 掌握 Go channel 桥接 TypeScript AsyncGenerator 的方式
- 能实现流式代码生成:逐文件输出,附带进度百分比
- 理解预览服务的沙箱隔离设计
- 能运行代码示例,观察流式输出的实时效果
生产化 —— 成本控制、限流与全链路可观测性
学习目标
- 设计 AI 代码生成服务的成本模型:Token 计量、用户配额和缓存策略
- 在 tRPC-Go 中实现基于用户 ID 的令牌桶限流中间件
- 接入 OpenTelemetry 追踪从用户请求到代码生成的完整链路
- 掌握 AppForge 的 Docker Compose 多服务编排部署方案
生产化的三大挑战
AppForge 上线前需要解决三个核心问题:
成本模型设计
Token 计量
AppForge 的成本主要来自 CodeBuddy SDK 的 AI 调用:
用户配额系统
缓存策略
相似的代码生成请求可以复用缓存,大幅降低成本:
限流中间件
令牌桶算法
多级限流策略
OpenTelemetry 接入
关键埋点位置
AppForge 的链路追踪需要覆盖以下关键节点:
Span 属性设计
Docker Compose 部署
运行代码示例
下方代码是 AppForge 的完整生产化模拟——限流中间件、Token 成本追踪、链路追踪、缓存策略和成本报告。这是本阶段的终极代码示例,展示了一个生产级 AI 服务的完整可观测性体系。
🎯 学习强化(阶段11综合)
每日微任务(Day 6)
1. 为 AppForge 编写 docker-compose.yml,一键启动所有服务
2. 配置 GitHub Actions CI,在 PR 时自动运行测试
3. 执行端到端测试:从创建项目到生成代码,验证完整流程
费曼复述任务
> 向面试官解释:AppForge 的代码生成流程是如何工作的?从用户点击"生成"到看到代码,经历了哪些步骤?
参考步骤:
1. 前端发送请求 → Gateway 鉴权
2. CodeGen Service 接收请求 → 创建异步任务
3. Agent Pipeline 执行(需求分析 → 架构 → 代码生成)
4. SSE 实时推送进度
5. 代码存储 → 返回预览 URL
Anki 卡片
Q: AppForge 为什么使用异步任务队列而不是同步 API?
A: 代码生成耗时 30-120 秒,超过 HTTP 超时限制。异步队列让请求立即返回任务 ID,客户端通过 SSE 接收进度。
Q: 如何保证 AppForge 生成的代码不包含恶意内容?
A: 多层防护:Prompt 中明确禁止生成恶意代码;代码审查 Agent 检查安全问题;Sandbox 隔离执行;静态分析工具扫描。
Q: AppForge 的版本回滚是如何实现的?
A: 每次生成/修改都保存完整快照(版本号递增),回滚时直接恢复指定版本的代码快照。
代码审查 Checklist(阶段11综合)
- [ ] Agent Pipeline 的每个阶段是否有独立的错误处理?
- [ ] 异步任务是否有超时控制(防止任务永久运行)?
- [ ] SSE 连接是否有心跳机制(防止连接超时断开)?
- [ ] 代码生成结果是否有版本管理(支持回滚)?
- [ ] 是否有 Token 用量监控和成本告警?
- [ ] 生成的代码是否经过安全扫描?
常见坑与修复
坑:异步任务没有超时,导致任务永久运行
坑:SSE 连接没有心跳,被代理服务器断开
阶段11验收准则(完整)
功能验收
- [ ] 用户能创建项目并生成完整的前端应用代码
- [ ] 支持多轮迭代修改(至少 10 轮不出现 Context 溢出)
- [ ] 支持版本回滚(回滚到任意历史版本)
- [ ] 代码生成进度实时展示(SSE)
性能验收
- [ ] 代码生成任务在 2 分钟内完成(简单应用)
- [ ] 并发 10 个生成任务时,系统正常运行
工程质量验收
- [ ] 单元测试覆盖率 ≥ 75%
- [ ] 有端到端测试脚本()
- [ ] CI 流水线在 PR 时自动运行测试
课后检查清单
- 理解 AI 服务的成本模型:Token 计量与用户配额
- 能实现令牌桶限流中间件(基于用户 ID)
- 掌握 OpenTelemetry Span 在 AI 服务中的关键埋点位置
- 理解缓存策略如何降低 AI 调用成本
- 能运行代码示例,观察完整的可观测性报告和成本分析
阶段 12: AppForge 实战:生产级工程落地
从零构建可部署的 AI 应用生成平台——真实工程代码、完整测试覆盖、容器化部署与 CI/CD 流水线
预计学时: 3–4 周
工程脚手架与仓库规范
学习目标
- 掌握生产级 Go 项目的目录结构规范(Standard Go Project Layout)
- 理解 AppForge 的完整仓库组织:monorepo 结构、服务边界、共享库
- 能够编写并维护 README.md、ARCHITECTURE.md、DEVELOPER_GUIDE.md、SECURITY.md 四份核心文档
- 掌握 go.mod workspace 多模块管理与 Makefile 自动化工具链
- 理解 .env 管理、密钥轮换策略与 pre-commit hook 防止密钥泄露
为什么工程规范是生产级项目的基石?
> "代码是写给人看的,顺便让机器执行。" —— Harold Abelson
一个 10 年经验的架构师在开始写第一行业务代码之前,会先花 1-2 天建立工程规范。这不是浪费时间,而是为后续数月的开发节省数倍的时间。
本课将建立 AppForge 的完整工程基础:目录结构、文档体系、工具链、密钥管理。
AppForge 仓库结构(Standard Go Project Layout)
关键设计决策
为什么用 monorepo?
AppForge 4 个服务高度协作,选择 monorepo。
go.work 多模块 Workspace
本地开发时, 让所有模块互相引用本地代码,无需发布到 pkg registry:
Makefile 统一工具链
本地快速开始:
预期输出:
预期输出:
四份核心文档规范
README.md 结构
ARCHITECTURE.md 结构(C4 模型)
DEVELOPERGUIDE.md 结构
SECURITY.md 结构
密钥管理与 pre-commit Hook
.env.example(提交到仓库)
.gitignore(防止 .env 提交)
pre-commit Hook(自动扫描密钥)
安装 pre-commit hook:
学习强化元素
📅 每日微任务
Day 1:执行 ,观察生成的目录结构,理解每个目录的职责
Day 2:阅读 文件,在 中引用 ,验证本地引用生效
Day 3:安装 pre-commit,故意在代码中写入一个假 API Key,验证 hook 能拦截
Day 4:完成 ARCHITECTURE.md 的 Level 1 和 Level 2 图(用 PlantUML 或 Mermaid)
Day 5:向同事做 5 分钟费曼复述:解释 monorepo 的优缺点和 go.work 的工作原理
🧠 费曼复述任务
> 用自己的话解释:为什么 目录是 Go 的"访问控制"机制?它和 关键字有什么本质区别?
参考答案要点:
- 是编译器强制的包可见性限制,不是语言关键字
- 只有同一模块(或父目录)下的代码才能导入 包
- 这防止了外部用户依赖内部实现细节,强制通过公共 API 交互
🃏 Anki 卡片题库
Q: 文件的作用是什么?何时使用?
A: 管理 Go workspace,让多个本地模块互相引用而无需发布。适用于 monorepo 中多个紧密协作的 Go 模块。
Q: 和 的区别?
A: 是公共库,可被外部模块导入; 是私有实现,只能被同模块代码导入,编译器强制执行。
Q: 12-Factor App 的第三条原则是什么?在 AppForge 中如何体现?
A: 配置(Config)—— 将配置存储在环境变量中,不硬编码。AppForge 通过 文件和 加载所有配置。
Q: pre-commit hook 如何防止密钥泄露?
A: 在 执行前运行 gitleaks 扫描,检测代码中的 API Key、密码等模式,若发现则阻止提交。
✅ 代码审查 Checklist
- [ ] 所有密钥通过环境变量注入,代码中无硬编码密钥
- [ ] 目录正确划分,无不必要的公开暴露
- [ ] 中依赖版本固定(无 )
- [ ] Makefile 中每个 target 有注释说明
- [ ] 已更新,包含所有新增的环境变量
- [ ] pre-commit hook 已安装并通过测试
⚠️ 常见坑与修复
坑 1:go.work 导致 CI 构建失败
坑 2:.env 文件意外提交
坑 3:internal/ 包被外部引用导致编译错误
🎯 学习强化
每日微任务(Day 1)
1. 克隆项目脚手架,在本机运行 ,确认所有服务启动成功
2. 阅读 ,画出服务依赖图
3. 修改 ,添加一个 命令运行代码检查
Anki 卡片
Q: 生产级项目脚手架中,为什么要分离 、、 目录?
A: 存放可执行入口(main.go); 存放私有业务逻辑(不对外暴露); 存放可复用的公共库。这种分层避免了循环依赖,明确了代码边界。
Q: 文件的作用是什么?**
A: 提供环境变量的模板,让新开发者知道需要配置哪些变量,同时避免将真实密钥提交到代码仓库。
验收准则
- [ ] 能成功启动所有服务(无报错)
- [ ] 能运行所有单元测试(通过率 100%)
- [ ] 能成功构建所有 Docker 镜像
课后检查清单
- 能用 `make scaffold` 一键生成完整项目骨架
- 理解 internal/ vs pkg/ 的边界:internal 不可被外部导入,pkg 是公共库
- 能解释 go.work 多模块 workspace 的优势与适用场景
- 能写出 ARCHITECTURE.md 中的 C4 模型四层图(Context/Container/Component/Code)
- 能配置 pre-commit hook 扫描 .env 文件防止密钥提交
- 理解 12-Factor App 原则在 AppForge 中的体现
- 完成费曼复述:向同事解释为什么要用 monorepo 而不是多仓库
Proto 定义、tRPC-Go 微服务骨架与单元测试
学习目标
- 掌握生产级 Protobuf 设计规范:字段命名、版本管理、向后兼容性
- 实现 AppForge 四个微服务的完整 tRPC-Go 骨架:handler、middleware、config
- 理解 tRPC-Go 的拦截器(Filter)链:认证、限流、追踪、日志的正确顺序
- 掌握 Go 单元测试最佳实践:表驱动测试、mock 接口、testify 断言
- 能够用 go test -race -cover 验证并发安全性和测试覆盖率
生产级 Protobuf 设计规范
Protobuf 是 AppForge 服务间通信的契约。一旦上线,字段编号不可修改,否则会破坏向后兼容性。
字段编号规则
AppForge 完整 Proto 定义
tRPC-Go 微服务骨架实现
DialogService Handler
Filter 链(拦截器)
单元测试:表驱动 + Mock
Store 接口定义(便于 Mock)
表驱动单元测试
运行测试与验证
预期输出:
学习强化元素
📅 每日微任务
Day 1:在本地克隆仓库,执行 生成 Go 代码,观察 目录的变化
Day 2:实现 ,包含分页逻辑和单元测试
Day 3:为 编写单元测试,覆盖:有效 token、过期 token、格式错误 token 三种情况
Day 4:用 发现并修复一个并发 bug(提示:mockStore 的 map 不是并发安全的)
Day 5:费曼复述:向同事解释 tRPC-Go Filter 的洋葱模型
🧠 费曼复述任务
> 解释:为什么 要定义为包级变量,而不是每次 ?
参考答案:
- 包级变量允许调用方用 精确匹配错误类型
- 每次 创建的是不同的错误实例,无法用 或 比较
- 这是 Go 错误处理的最佳实践:哨兵错误(Sentinel Error)
🃏 Anki 卡片题库
Q: tRPC-Go Filter 的执行顺序是什么?
A: 洋葱模型。注册顺序 [A, B, C],请求进入:A→B→C→Handler,响应返回:Handler→C→B→A。
Q: 为什么 Auth Filter 要放在 RateLimit Filter 外层?
A: 未认证的请求不应消耗限流配额。如果 RateLimit 在外层,攻击者可以用未认证请求耗尽配额,导致合法用户被限流。
Q: 表驱动测试(Table-Driven Test)的核心优势是什么?
A: 1) 新增测试用例只需添加一行数据;2) 测试逻辑与测试数据分离;3) 失败时能清晰看到是哪个 case 失败。
✅ 代码审查 Checklist
- [ ] 所有 handler 方法都有输入校验,且错误信息清晰
- [ ] 错误返回使用 ,包含正确的错误码
- [ ] 所有公共接口都有 OTel span
- [ ] 单元测试覆盖率 ≥ 80%(核心业务逻辑)
- [ ] 测试使用 mock 接口,不依赖真实 Redis/数据库
- [ ] 没有 跳过的测试(除非有明确注释说明原因)
⚠️ 常见坑与修复
坑 1:Protobuf 字段编号冲突
坑 2:并发测试中 map 竞态
坑 3:Filter 中忘记调用 next
🎯 学习强化
每日微任务(Day 2)
1. 运行单元测试并查看覆盖率报告:
2. 为 添加一个新的测试用例:项目名称包含特殊字符
3. 使用 pprof 分析服务的内存使用情况
Anki 卡片
Q: 为什么单元测试要加 标志?
A: 开启竞态检测器,能发现并发代码中的数据竞争问题(多个 goroutine 同时读写同一变量)。这类 bug 在生产环境中很难复现,但竞态检测器能在测试时捕获。
Q: 和手写 Mock 相比有什么优势?
A: 提供了 (验证所有期望的调用都发生了)、(验证调用次数)等断言,减少手写 Mock 的样板代码。
常见坑与修复
坑:测试中使用真实数据库,导致测试不稳定
验收准则
- [ ] 单元测试覆盖率 ≥ 85%()
- [ ] 所有测试通过竞态检测()
- [ ] 测试不依赖外部服务(使用 Mock)
课后检查清单
- 能解释 Protobuf 字段编号为什么不能随意修改(向后兼容性)
- 理解 tRPC-Go Filter 的洋葱模型:请求进入和响应返回的顺序
- 能写出 DialogService.CreateProject 的完整 handler 实现(含错误处理)
- 能用 mockery 或手写 mock 对 handler 进行单元测试
- 测试覆盖率达到核心业务逻辑 80% 以上
- 完成费曼复述:解释为什么 tRPC-Go 的 Filter 比 HTTP middleware 更强大
CodeBuddy SDK 桥接层:TypeScript HTTP 微服务
学习目标
- 理解为什么选择 HTTP 微服务模式而非子进程模式作为生产方案
- 实现完整的 TypeScript SDK Bridge 服务:Express + CodeBuddy SDK + 健康检查
- 掌握 Go 侧的 HTTP 客户端封装:连接池、超时、重试、熔断器
- 理解 SDK 会话管理:session_id 的创建、复用与过期清理
- 编写集成测试:用 testcontainers-go 启动真实 SDK Bridge 服务进行测试
为什么选择 HTTP 微服务而非子进程?
第 11 阶段介绍了两种桥接方案。在生产环境中,HTTP 微服务模式是更优选择:
结论:生产环境使用 HTTP 微服务模式,开发环境可用子进程模式快速验证。
TypeScript SDK Bridge 服务实现
项目结构
package.json
types.ts
runner.ts(核心 SDK 调用)
server.ts(Express HTTP 服务器)
Go 侧 HTTP 客户端(含熔断器)
集成测试
预期输出:
学习强化元素
📅 每日微任务
Day 1:在本地启动 SDK Bridge(),用 调用 和 ,观察响应
Day 2:实现 的 TTL 清理逻辑,确保过期会话被自动删除
Day 3:为熔断器编写单元测试,验证 closed→open→half-open→closed 的状态转换
Day 4:用 或 对 SDK Bridge 进行压测,观察连接池的效果
Day 5:费曼复述:向同事解释熔断器的三种状态和转换条件
🧠 费曼复述任务
> 解释:熔断器(Circuit Breaker)如何防止级联故障?如果没有熔断器,会发生什么?
参考答案:
- 没有熔断器:下游服务(SDK Bridge)变慢时,上游(Go 服务)的请求会堆积,线程/goroutine 耗尽,最终整个系统崩溃
- 有熔断器:检测到连续失败后立即"断路",后续请求直接返回错误,不再等待超时,保护上游资源
- 半开状态:定期发送探测请求,检测下游是否恢复,避免永久熔断
🃏 Anki 卡片题库
Q: 熔断器的三种状态分别是什么?
A: Closed(正常通过)、Open(熔断拒绝)、Half-Open(探测恢复)。
Q: 和 的区别?
A: 是存活检查(进程是否在运行), 是就绪检查(是否能处理请求)。K8s 用 liveness probe 调用 ,用 readiness probe 调用 。
Q: 为什么 HTTP 客户端要设置 ?
A: 控制连接池大小,复用 TCP 连接,避免每次请求都建立新连接(三次握手开销)。
✅ 代码审查 Checklist
- [ ] SDK Bridge 的 检查了 API Key 是否配置
- [ ] Go HTTP 客户端设置了合理的超时(不能无限等待)
- [ ] 熔断器的 和 通过配置注入,不硬编码
- [ ] 响应体读取有大小限制(防止内存耗尽)
- [ ] 集成测试有 tag,不影响普通
⚠️ 常见坑与修复
坑 1:忘记关闭 HTTP 响应体
坑 2:SDK Bridge 的 workDir 权限问题
坑 3:熔断器状态竞争
🎯 学习强化
每日微任务(Day 3)
1. 为 SDK Bridge 添加请求/响应日志中间件(记录每次 LLM 调用的 Token 用量)
2. 实现一个 ,对相同的请求缓存结果(TTL 5 分钟)
3. 压测 SDK Bridge:使用 测量吞吐量
Anki 卡片
Q: SDK Bridge 中为什么要实现重试机制?
A: LLM API 可能因网络抖动、限流(429)、服务器错误(500/503)而失败。重试机制能自动恢复,提升可用性。
Q: 指数退避重试中,为什么要加 jitter(随机抖动)?
A: 避免"惊群效应":多个客户端同时失败后,如果都在相同时间重试,会同时打到服务器,加剧压力。加入随机抖动让重试时间分散。
验收准则
- [ ] SDK Bridge 能正确处理 LLM API 的各种错误(超时、限流、服务器错误)
- [ ] 重试机制在 3 次失败后停止,不无限重试
- [ ] Token 用量有记录,便于成本分析
课后检查清单
- 能解释 HTTP 微服务模式相比子进程模式的优势(连接复用、独立扩缩容、故障隔离)
- 理解 SDK Bridge 的健康检查端点设计:/healthz vs /readyz 的区别
- 能实现 Go 侧的熔断器(Circuit Breaker):closed/open/half-open 三种状态
- 理解 session_id 的生命周期:创建→复用→过期→清理
- 集成测试能验证:正常生成、超时处理、SDK 错误三种场景
- 完成费曼复述:解释熔断器如何防止级联故障
多 Agent 流水线、上下文管理与快照系统
学习目标
- 实现生产级三阶段 Agent 流水线:需求分析→代码生成→质量审查
- 掌握 Agent 间结构化输出的设计:JSON Schema 约束 + 解析容错
- 实现完整的 ProjectContext 持久化:Redis 存储 + 内存缓存 + 版本快照
- 理解快照的存储策略:全量快照 vs 增量 diff,以及 LRU 清理
- 编写 Agent 流水线的集成测试:用 mock SDK Bridge 验证完整流程
生产级 Agent 流水线设计原则
第 11 阶段介绍了三阶段流水线的概念。本课将其实现为生产可用的代码,重点解决:
1. 结构化输出的可靠性:AI 输出不稳定,如何解析容错?
2. 并发安全:多个用户同时生成代码,如何保护共享状态?
3. 快照存储效率:每次生成都保存全量文件,存储成本如何控制?
三阶段流水线架构
结构化输出设计
每个 Agent 的输出必须是严格的 JSON,并有容错解析:
ProjectContext 持久化设计
数据模型
Redis 存储实现
快照创建与回滚
流水线执行器
验收准则
手动验证步骤
学习强化元素
📅 每日微任务
Day 1:实现 的容错解析,编写测试覆盖:纯 JSON、JSON 前有文字、完全无效三种情况
Day 2:为 编写集成测试(使用 启动真实 Redis)
Day 3:实现快照的增量 diff 存储(只保存变更的文件),对比全量快照的存储节省
Day 4:为流水线执行器编写并发测试:10 个 goroutine 同时执行流水线,验证无竞态
Day 5:费曼复述:解释为什么 ReviewAgent 失败不应该让整个流水线失败
🧠 费曼复述任务
> 解释:为什么快照要"深拷贝"文件内容,而不是直接保存引用?
参考答案:
- Go 的 是引用类型,直接赋值只是复制了指针
- 如果快照保存的是引用,后续修改 会同时修改快照内容
- 深拷贝确保快照是独立的不可变副本,回滚时能恢复到准确的历史状态
🃏 Anki 卡片题库
Q: 为什么 Agent 间通信要用结构化 JSON 而非自由文本?
A: 自由文本无法可靠解析,AI 输出格式不稳定。JSON 有明确的 Schema,解析失败可以快速定位问题,且便于版本化管理。
Q: 快照的 LRU 清理策略是什么?
A: 保留最新的 N 个快照(如 20 个),超出时删除最旧的。这平衡了存储成本和回滚能力。
Q: 为什么回滚后要重置 ?
A: SDK 的 session 包含了代码生成的上下文历史。回滚后代码状态变了,但 SDK session 还记得旧的上下文,会导致后续生成基于错误的上下文。重置后 SDK 会建立新会话。
✅ 代码审查 Checklist
- [ ] 使用深拷贝,不是浅拷贝
- [ ] 快照数量有上限(maxSnapshots),防止无限增长
- [ ] 流水线中 Stage 3 失败有降级处理(使用未审查的文件)
- [ ] 有容错解析(处理 AI 在 JSON 前后加文字)
- [ ] Redis 操作有超时 context,不会无限等待
⚠️ 常见坑与修复
坑 1:快照浅拷贝导致数据污染
坑 2:流水线 context 超时设置不当
坑 3:并发修改 ProjectContext
🎯 学习强化
每日微任务(Day 4)
1. 为 Pipeline 添加进度回调:每个阶段完成时通过 channel 发送进度事件
2. 实现 Pipeline 的快照功能:每个阶段完成后保存中间结果,支持从断点恢复
3. 编写集成测试:使用 Mock LLM 测试完整 Pipeline 流程
Anki 卡片
Q: Pipeline 快照(Snapshot)的作用是什么?
A: 保存每个阶段的中间结果,当 Pipeline 在某阶段失败时,可以从上一个成功的快照恢复,避免重新执行已完成的阶段(节省时间和 Token 费用)。
Q: 如何测试 Pipeline 而不消耗真实的 LLM API 费用?
A: 使用 Mock LLM:预先定义每个 Agent 的输入和输出,测试时返回预设的响应,不调用真实 API。
常见坑与修复
坑:Pipeline 阶段间的数据结构不一致
验收准则
- [ ] Pipeline 能完整执行所有阶段,生成可运行的代码
- [ ] 某阶段失败时,能从快照恢复(不重新执行已完成阶段)
- [ ] 集成测试覆盖 Pipeline 的主要路径(成功、失败、恢复)
课后检查清单
- 能解释为什么 Agent 间必须使用结构化 JSON 输出而非自由文本
- 理解 ProjectContext 的并发安全设计:读写锁保护 + Redis 乐观锁
- 能实现版本快照的 LRU 清理:保留最近 N 个版本
- 理解 SDK session_id 与 tRPC session 的关联策略
- 流水线集成测试覆盖:正常流程、Stage 2 失败、并发请求三种场景
- 完成费曼复述:解释为什么需要 ReviewAgent,它能发现什么问题
流式响应、预览沙箱与端到端测试
学习目标
- 实现 tRPC-Go Server Streaming 的生产级流式代码生成接口
- 设计预览沙箱:文件系统隔离、npm build 执行、静态文件服务
- 掌握 SSE(Server-Sent Events)将 tRPC 流式响应桥接到 HTTP 客户端
- 编写完整的端到端测试脚本:从创建项目到预览 URL 的全链路验证
- 理解沙箱安全边界:资源限制、超时控制、清理策略
流式响应的必要性
代码生成是一个耗时 10-120 秒的操作。非流式响应会导致:
- 用户看到空白页面,不知道是否在处理
- 网关超时(通常 30-60 秒)
- 无法实现"打字机效果"的实时体验
AppForge 使用两层流式架构:
tRPC-Go Server Streaming 实现
Proto 定义
服务端实现
进度推送策略
SSE 桥接(HTTP 网关)
前端通过 SSE 接收流式进度,网关将 tRPC 流转换为 SSE:
前端接收 SSE:
预览沙箱设计
沙箱目录结构
沙箱管理器
端到端测试脚本
学习强化元素
📅 每日微任务
Day 1:在本地运行 ,然后执行 ,观察每个测试步骤
Day 2:为 添加资源限制(使用 或 cgroup),防止 npm build 消耗过多资源
Day 3:实现沙箱的定时清理任务(每小时清理过期沙箱),用 实现
Day 4:为 SSE 端点添加心跳(每 15 秒发送一个 ),防止连接超时
Day 5:费曼复述:解释为什么 SSE 比 WebSocket 更适合单向流式推送
🧠 费曼复述任务
> 解释:为什么预览沙箱需要路径穿越检查()?如果不检查会发生什么?
参考答案:
- 恶意用户可以提交 这样的文件路径
- 如果不检查, 会将内容写入沙箱目录之外的系统文件
- 确保所有文件路径都在沙箱目录内,防止文件系统逃逸
🃏 Anki 卡片题库
Q: SSE 和 WebSocket 的主要区别是什么?
A: SSE 是单向(服务器→客户端),基于 HTTP,自动重连;WebSocket 是双向,需要协议升级。代码生成进度推送只需单向,SSE 更简单。
Q: 响应头的作用是什么?
A: 告诉 Nginx 不要缓冲响应,立即将数据转发给客户端。没有这个头,Nginx 会等待响应完成才发送,破坏流式效果。
Q: 端到端测试和集成测试的区别?
A: 集成测试测试多个组件的协作(如 handler + store);端到端测试测试完整的用户流程(从 HTTP 请求到数据库到响应),通常需要真实的运行环境。
✅ 代码审查 Checklist
- [ ] SSE 端点设置了 (防止 Nginx 缓冲)
- [ ] 有路径穿越检查
- [ ] 有超时控制(防止 npm install 无限等待)
- [ ] 沙箱有 TTL 和定时清理机制
- [ ] 端到端测试脚本有 (任何错误立即退出)
⚠️ 常见坑与修复
坑 1:SSE 连接被 Nginx 缓冲
坑 2:npm build 超时后进程残留
坑 3:并发构建时沙箱目录冲突
🎯 学习强化
每日微任务(Day 5)
1. 运行端到端测试脚本:,确认所有测试通过
2. 在 Postman 或 curl 中手动测试 SSE 端点,观察流式输出
3. 模拟网络延迟(使用 命令),测试 SSE 在高延迟下的表现
Anki 卡片
Q: 端到端测试和单元测试的主要区别是什么?
A: 单元测试测试单个函数/模块(使用 Mock 隔离依赖);端到端测试测试完整的用户流程(使用真实服务)。端到端测试更接近真实场景,但速度慢、维护成本高。
Q: 如何在 CI 中运行端到端测试?
A: 在 CI 中使用 docker-compose 启动所有服务,等待健康检查通过后运行端到端测试脚本,测试完成后清理容器。
验收准则
- [ ] 所有测试通过(8/8)
- [ ] SSE 流式输出延迟 < 500ms(从服务端发送到客户端接收)
- [ ] 鉴权测试:无 Token 的请求返回 401
课后检查清单
- 能解释 Server Streaming 和 SSE 的区别,以及为什么需要两层转换
- 理解沙箱的资源限制:CPU/内存/磁盘/网络的隔离策略
- 能实现预览 URL 的生成策略:短链接 + 有效期 + 访问统计
- 端到端测试覆盖:正常流程、超时、并发 5 个项目同时生成
- 理解 npm build 的超时控制和错误处理
- 完成费曼复述:解释为什么预览沙箱需要文件系统隔离
生产化部署:Dockerfile、docker-compose、Kubernetes 与 GitHub Actions CI
学习目标
- 编写生产级多阶段 Dockerfile:最小化镜像体积、非 root 用户、安全扫描
- 设计完整的 docker-compose.yml:服务依赖、健康检查、资源限制、网络隔离
- 掌握 Kubernetes manifests:Deployment、Service、ConfigMap、Secret、HPA
- 实现 GitHub Actions CI/CD 流水线:测试→构建→安全扫描→推送→部署
- 理解生产环境的可观测性:Prometheus 指标、Grafana 仪表盘、告警规则
生产部署的核心原则
> "任何在本地能运行的东西,都应该能在生产环境中以相同的方式运行。" —— 12-Factor App
本课将 AppForge 从"本地可运行"变为"生产可部署",覆盖:
- 容器化:多阶段 Dockerfile,最小化攻击面
- 编排:docker-compose(开发)+ Kubernetes(生产)
- CI/CD:GitHub Actions 自动化测试、构建、部署
- 可观测性:Prometheus + Grafana 监控
多阶段 Dockerfile
Go 服务 Dockerfile(以 codegen 为例)
镜像体积对比:
TypeScript SDK Bridge Dockerfile
docker-compose.yml(完整生产配置)
Kubernetes Manifests
Namespace 与 ConfigMap
Secret(密钥管理)
CodeGen Deployment + HPA
GitHub Actions CI/CD
Prometheus 监控配置
本地验证命令
预期输出:
预期输出:
学习强化元素
📅 每日微任务
Day 1:执行 ,观察多阶段构建的层缓存效果
Day 2:用 扫描镜像漏洞,对比 alpine 和 distroless 的漏洞数量
Day 3:在本地配置 GitHub Actions 的 工具,本地运行 CI 流水线
Day 4:为 codegen 服务添加 Prometheus 指标: 直方图
Day 5:费曼复述:解释 K8s HPA 如何根据自定义指标(活跃生成任务数)扩缩容
🧠 费曼复述任务
> 解释:为什么 GitHub Actions 要用 检测变更的服务?如果不用会怎样?
参考答案:
- 不用 paths-filter:每次任何文件变更都会触发所有服务的 CI,浪费时间和资源
- 用 paths-filter:只有 下的文件变更才触发 codegen 的 CI
- 对于 monorepo 尤其重要:避免改一个服务触发所有服务的重新构建和部署
🃏 Anki 卡片题库
Q: 多阶段 Dockerfile 的核心优势是什么?
A: 构建环境(含编译器、依赖)与运行环境分离。最终镜像只包含运行时需要的文件,体积小、攻击面小。
Q: K8s 的 在 RollingUpdate 中意味着什么?
A: 更新时不允许任何 Pod 不可用,即先启动新 Pod,确认健康后再删除旧 Pod。实现零停机更新,但需要更多资源。
Q: 和 的区别?
A: 只有 不等待服务就绪,只等待容器启动;加上 才会等待 healthcheck 通过,确保依赖服务真正可用。
✅ 代码审查 Checklist
- [ ] Dockerfile 使用多阶段构建,最终镜像基于 distroless 或 alpine
- [ ] 容器以非 root 用户运行( 或 )
- [ ] docker-compose 中所有服务都有 和资源限制
- [ ] K8s Secret 不包含真实密钥(通过 CI/CD 注入)
- [ ] GitHub Actions 有 ,避免不必要的构建
- [ ] CI 流水线包含安全扫描(gitleaks + govulncheck)
⚠️ 常见坑与修复
坑 1:docker-compose dependson 不等待服务就绪
坑 2:K8s Secret 提交到 Git
坑 3:镜像 tag 使用 latest 导致部署不可追溯
🎯 学习强化(阶段12综合 · 课程终章)
每日微任务(Day 6)
1. 执行完整部署流程:,验证所有服务健康
2. 运行端到端测试:,确认 8/8 通过
3. 查看 Grafana 仪表盘,确认指标正常收集
费曼复述任务
> 向技术总监汇报:AppForge 的生产部署方案是什么?如何保证高可用和安全性?
参考要点:
1. 容器化:Docker 多阶段构建,镜像 1% 或 P99 > 5s)
工程质量
- [ ] 单元测试覆盖率 ≥ 80%
- [ ] 有集成测试和端到端测试
- [ ] CI 流水线在 PR 时自动运行测试
- [ ] 代码有清晰的注释和文档(README、ARCHITECTURE、DEVELOPERGUIDE)
性能
- [ ] 多阶段 Docker 构建,镜像 🎉 你已经具备了构建生产级 Go + AI Agent 系统的完整能力!**
课后检查清单
- 能解释多阶段 Dockerfile 的优势:构建环境与运行环境分离
- 理解 docker-compose 的 depends_on + healthcheck 的正确用法
- 能写出 HPA(Horizontal Pod Autoscaler)的配置:基于 CPU 和自定义指标
- 理解 GitHub Actions 的 path filter:只有相关服务变更时才触发对应的 CI
- 能配置 Prometheus 抓取 AppForge 的自定义指标(Token 消耗、生成耗时)
- 完成费曼复述:解释 distroless 镜像相比 alpine 的安全优势