Day 20 / 共 20 天 · 结营 🎓

收官串讲:把 20 天拼成一张全景图

恭喜你走到最后一天!今天不学新代码,而是把 20 天读过的六大模块、六层前端、IDL 契约、本地 Kitex、Adapter 模式全部串起来,形成一张"以后随时能调出来复习"的全景图,再看看 CI 是怎么替我们守住这些设计约定的,最后聊聊接下来往哪个方向深入。

📍 你在整门课的位置 · 第 4 周 前端与工程收官(共 4 周 · 20 天)· 最后一天
D17 前端分层 D18 IDL 端到端 D19 动手改功能 D20 收官串讲 🎓
L01

20 天回顾:你走过的路

先花两分钟,看看自己实际走过的路线——这条时间线本身就是最好的学习地图:

第 1 周 D01-D05:从"这是什么项目"开始,摸清 backend 六大模块的分工,读懂 foundation 基础设施与 llm 网关。
第 2 周 D06-D10:深入 prompt 模块的版本管理、调试、发布链路,理解 Prompt 作为核心业务对象的建模方式。
第 3 周 D11-D15:走完 data(数据集)→ evaluation(评测集+评估器+实验)→ observability(Trace 观测)三大核心业务模块,最后在 D15 用 api.goInit 函数把它们全部串成一张依赖图。
第 4 周 D16-D20:切到前端——React 壳、Rush 单体仓、六层依赖结构、IDL 到 TS 的生成链路,D19 动手设计一次真实的小功能改动,今天做总结。
🎉 你现在具备的能力 拿到任何一个陌生的 Go/TS 大型仓库,你现在知道该先找 application 层摸清业务动作,再找 domain 层理解核心概念,再找 infra 层看真正落地技术,最后看 IDL 或 API 契约理解模块边界——这套"读码方法论"比记住 Coze Loop 具体的每一行代码更有价值。
L02

全景图:六大后端模块 + 六层前端结构

后端 backend/modules/ 六大模块及依赖顺序(ARCHITECTURE.md):

foundation

空间/用户/权限/审计

被所有模块依赖的地基

llm

大模型网关、多厂商适配

Day 05 讲过

prompt

Prompt 版本管理与调试

第 2 周主角

data

Dataset/Item/Version/Job

Day 11

evaluation

EvaluationSet/Evaluator/Experiment

Day 12-13

observability

Trace/Span 观测链路

Day 14,与 evaluation 双向依赖

前端 frontend/packages/ 六层结构(docs/reference/frontend-packages.md):

L1 foundation

arch/toolkit 基础工具

L2 base

api-schema(IDL 生成)

L3 biz-hooks

业务无关的通用 hooks

L4 components

业务相关 UI 组件

L5 pages

prompt-pages/evaluate-pages 等

L6 apps

cozeloop 应用壳

💡 两套分层的共同规律:从"通用"到"具体"单向依赖 后端从 foundation 到 evaluation/observability 是"基础能力→核心业务";前端从 L1 到 L6 是"通用工具→具体页面"。两套系统的设计哲学高度一致:越底层越稳定越通用,越上层越贴近具体业务、变化越频繁——这不是巧合,而是大型工程普遍遵循的分层规律。
L03

设计哲学一:DDD 分层——为什么每个模块都是四件套

ARCHITECTURE.md 中"Backend DDD 分层"一节定义的标准结构,20 天里你在六个模块里反复见到同一套四层:

modules/<module>/
├── application/   # 编排:鉴权→转换→审核→调用domain,不含业务规则细节
├── domain/        # 核心:entity + repo接口(interface) + service(领域服务)
├── infra/         # 实现:repo接口的具体落地(mysql/redis/mq/rpc)
└── (可选)consumer/ # MQ消费者,异步任务的入口
💡 一句话看懂 DDD 分层的价值:让"业务规则"和"技术实现"互不干扰 Day 11-14 你在 dataset、evaluation_set、evaluator、trace 等完全不同的业务概念里,反复看到同样的四层套路——这正是 DDD(领域驱动设计)想要的效果:不管业务多复杂,代码组织方式保持一致,新人可以"预判"陌生模块的代码在哪里。domain 层定义 interface、infra 层实现它,是让"业务逻辑不关心用 MySQL 还是 Redis"的关键技巧。

👶 小白问:Coze Studio(我之前学的另一个仓库)也是这样分层吗?

👨‍🏫 老师:核心思路一致(application/domain/infra 三层 + repo 接口),但 Coze Studio 偏"Agent 平台",模块以 workflow/agent/knowledge 等为核心;Coze Loop 偏"Prompt 工程与评测平台",模块以 prompt/data/evaluation/observability 为核心。如果你学过 Coze Studio 系列,这套分层思维应该会有强烈的既视感——这也说明字节内部的后端架构风格是有统一范式的。

L04

设计哲学二:IDL 契约驱动——一份 Thrift 定义两端代码

Day 18 学过的核心链路,今天再强调一次它的战略意义:

idl/thrift/**/*.thrift   (唯一真相源 Single Source of Truth)
   │  kitex 生成
   ▼
backend/kitex_gen/**/*.go       (Go 结构体 + RPC client/server 骨架)
   │  rush update-api 生成
   ▼
frontend/packages/loop-base/api-schema/**/*.ts   (TS 接口 + axios 调用函数)
💡 IDL 契约驱动解决的根本问题:前后端"说的是不是同一件事" 在没有 IDL 的项目里,前端和后端各自维护一份"这个接口传什么参数、返回什么字段"的理解,容易随着时间推移逐渐"说不同的语言"。Coze Loop 把 Thrift 文件作为唯一真相源,前后端类型都从它生成——字段改名、加字段、改类型,都从改一份 .thrift 文件开始,Go 和 TS 两端永远同步,这是大型全栈仓库能长期保持前后端一致性的核心机制。
L05

设计哲学三:本地 Kitex 与 Adapter——同一套代码两种"人格"

Day 15 的"本地客户端"(backend/loop_gen/*/local_*.go)与 Day 17 的 Adapter 模式(docs/reference/frontend-packages.md "Adapter 模式"一节)看似讲的是两件事,其实解决的是同一类问题:

💡 共同问题:"同一套接口,不同场景需要不同实现" 本地 Kitex:接口是 RPC 语义(ctx, req → resp, err),但实现是直接函数调用——让模块间调用"看起来像跨服务,实际是同进程",未来要拆成微服务时,只需替换实现,调用方代码不用改。

Adapter 模式:同一个业务组件,在开源版和商业版下逻辑分支不同——用 Adapter 把"哪个版本用哪套实现"这一决策从业务代码里剥离出来,业务代码永远只 import 抽象接口。
这两个设计的共同口诀:接口稳定不变,实现按场景切换 这是软件工程里"依赖倒置原则"最朴素的体现——只要接口的形状不变,实现可以是本地函数、可以是真实 RPC、可以是开源逻辑、可以是商业逻辑,调用方完全无感。这个思路不局限于 Coze Loop,是你今后设计任何可扩展系统都用得上的通用武器。
L06

CI 工作流:这些设计约定是怎么被"强制执行"的

光有设计哲学不够,Coze Loop 用 .github/workflows/ 里的多条流水线把这些约定变成"提交代码时自动检查",而不是靠人自觉:

backend-ci.yaml

golangci-lint 静态检查 + go build + go test -race 单测,覆盖率上报 Codecov。

frontend-ci.yaml

Rush 单体仓的 rebuild 全量构建 + 按改动文件做增量 lint/style 检查。

idl.yaml

kitex 工具真实跑一遍 .thrift 文件的代码生成,语法错了直接挡在 PR 外——呼应 Day 18 讲的"IDL 是契约"。

mysql-schema-check.yaml

schemadiff 比对 docker-compose 和 helm-chart 两套 mysql-init SQL 是否一致——呼应 Day 19 讲的"双路径同步"红线。

license-check.yaml

检查每个源码文件是否带有开源协议头,保证 License 合规。

semantic-pull-request.yaml

检查 PR 标题是否符合 Conventional Commits 规范(feat:/fix: 等),方便自动生成 CHANGELOG。

⚠️ 提示:CI 是"设计约定"的最后一道防线,不是唯一防线 CI 能挡住语法错误、Schema 不一致这类可自动检测的问题,但挡不住"某个新人把业务逻辑写进了 infra 层"这种架构层面的问题——这也是为什么这 20 天反复强调"读懂设计哲学"比"记住某个文件在哪"更重要:CI 保证代码能跑,架构理解保证代码"长得对"。
L07

接下来往哪个方向深入

路径一 · 真正给仓库提一个 PR:从 Day 19 设计的"加一个筛选维度"这类小功能开始,实际改代码、跑通单测、按 CONTRIBUTING.mdsemantic-pull-request 的要求发起 PR,体会真实贡献开源项目的完整流程。
路径二 · 深挖一个 Evaluator 类型的实现:Day 12 只讲了 Evaluator 的四种类型分工,挑一种(比如 Code Evaluator)深入看它怎么真正"跑代码打分"——这是通往"给 Coze Loop 加一种新评估器类型"这类中等难度贡献的路径。
路径三 · 对比 Coze Studio 的模块划分:如果你学过 coze-studio-dayNN.html 系列,试着把两个仓库的模块清单摆在一起对比——同样是字节的开源 Agent/AI 基建项目,模块边界怎么划分、哪些基础设施是复用的思路,会加深你对"中大型 AI 平台该怎么切模块"的理解。
路径四 · 读一遍 ClickHouse 查询链路的性能设计:Day 14 只讲了写入路径(Trace 上报→ClickHouse),查询路径(前端观测页面怎么高效查询海量 Span 数据)是一块值得单独深挖的硬核内容。
L08

结营:20 天小结 + 记忆口诀

🎓 20 天你到底学会了什么

  • 看到一个陌生的大型 Go/TS 仓库,知道先摸模块清单,再进每个模块找 application/domain/infra 三层
  • 理解为什么复杂业务要靠异步(RocketMQ/消费者)而不是全用同步调用撑住耗时任务。
  • 理解模块间跨调用为什么走"本地客户端"而不是直接 import,以及 api.goInit 函数如何拼出整个依赖图。
  • 理解前端 Rush 单体仓的分层依赖规则和 Adapter 模式,知道去哪找一个页面真正用到的底层组件。
  • 理解IDL 契约驱动开发的价值,知道改一个字段该走哪条生成链路、哪些文件绝对不能手改。
  • 具备了"顺着一个字段名读通全链路"的实战读码能力,能对着一个真实需求设计出改动路径。

六模块看清分工,四层套路走天下
IDL 是契约,生成代码不能碰
本地客户端连模块,Adapter 分场景
CI 守约定,读码先顺一根线

✋ 结营动手(推荐)

# 用一句话向别人介绍 Coze Loop 的六大模块,然后打开这两个文件核对答案:
open /Users/bitmart/work/codes/github/AI_WORK/coze-loop/ARCHITECTURE.md
open /Users/bitmart/work/codes/github/AI_WORK/coze-loop/docs/reference/frontend-packages.md

# 挑一个你还没完全读透的模块,用 Day 19 的"搜字段名读全链路"方法,
# 自己找一个真实字段,顺着 IDL → application → infra 走一遍,
# 检验一下 20 天学到的方法论是否已经变成了你自己的能力。
🎉 结营寄语:源码阅读没有"读完"的那一天——20 天带你搭起的是一套方法论和一张全景图,真正的掌握发生在你自己动手改第一个 PR、修第一个 bug 的那一刻。祝你在 Coze Loop(或者任何其他大型仓库)的探索之旅中收获满满!
← 上一天 Day 19 返回 Coze Loop 总目录 →