Coze Loop 架构学习指南 🚀
👋 欢迎来到 Coze Loop 的学习之旅!我是你的技术教练,将带你从零开始,一步步揭开这个 AI Agent 开发与运维平台的神秘面纱。
这份指南不是冰冷的技术文档,而是一位经验丰富的架构师与你面对面的交流。让我们像探险家一样,用好奇心和系统思维,一起探索这个项目的每一个角落!
📋 目录导航
第一部分:项目架构深度解析(像架构师一样俯瞰全景)🔍
本部分目标: 让你站在架构师的高度,理解 Coze Loop 的设计哲学、技术选型背后的考量,以及各个组件是如何协同工作的。
💡 学习心法: 不要一头扎进代码细节!先建立宏观视野,就像先看地图再开始旅行。
1. 项目架构概览 🏗️
🎭 用一个类比理解 Coze Loop
想象 Coze Loop 是一家现代化的"AI 智能体 4S 店":
- 展厅(Prompt 开发):这里是你设计和调试 AI 智能体的地方,就像汽车设计师在这里打磨每一个细节
- 试驾场(评测模块):在这里对你的 AI 智能体进行全方位测试,确保它能应对各种场景
- 监控中心(观测模块):实时追踪 AI 智能体的运行状态,就像汽车的黑匣子一样记录每一次"行程"
这个类比很贴切吧?😊 Coze Loop 就是这样一个为 AI Agent 提供全生命周期服务的平台。
🎯 核心设计特征
1. 前后端分离的现代架构
Coze Loop 采用了经典的前后端分离架构:
- 前端:基于 React 18 + TypeScript 的单页应用(SPA),使用 Rsbuild 构建
- 后端:Go 1.24+ 微服务架构,基于字节跳动的 CloudWeGo 技术栈
这种设计让前后端团队可以独立开发、独立部署,就像两个乐队同时演奏,最后完美合奏 🎵
2. 领域驱动设计(DDD)的后端架构
这是 Coze Loop 最闪耀的设计亮点!后端采用了严格的 DDD 三层架构:
backend/modules/
├── data/ # 数据集管理领域
├── evaluation/ # 评测领域
├── foundation/ # 基础服务领域
├── llm/ # LLM 模型管理领域
├── observability/ # 观测领域
└── prompt/ # Prompt 管理领域
每个领域内部都遵循:
├── application/ # 应用层(用例编排)
├── domain/ # 领域层(核心业务逻辑)
│ ├── entity/ # 实体
│ ├── service/ # 领域服务
│ └── repo/ # 仓储接口
├── infra/ # 基础设施层(技术实现)
│ ├── repo/ # 仓储实现
│ ├── rpc/ # RPC 调用
│ └── mq/ # 消息队列
└── pkg/ # 工具包
💡 为什么这样设计? DDD 架构的核心理念是"让业务逻辑独立于技术细节"。想象一下:
- Domain 层:纯粹的业务规则,就像象棋的规则,不管你用木制棋盘还是电子屏幕,规则都不变
- Application 层:用例协调器,像一个指挥家,协调各个领域服务完成复杂任务
- Infra 层:技术实现,像乐器,可以随时更换(比如从 MySQL 换到 PostgreSQL)而不影响业务逻辑
3. 微服务通信:Thrift IDL
Coze Loop 使用 Thrift 作为接口定义语言(IDL),定义了所有服务间的通信契约:
idl/thrift/
├── base.thrift # 基础类型定义
└── coze/ # Coze Loop 各模块的接口定义
├── dataset.thrift
├── evaluation.thrift
├── prompt.thrift
└── ...
这就像团队之间签署的"协议书",确保大家说的是同一种语言 📝
4. Monorepo 的前端架构
前端使用 Rush.js 管理 Monorepo,这是一个超级项目管理器:
frontend/
├── apps/
│ └── cozeloop/ # 主应用
├── packages/
│ └── cozeloop/ # 业务组件库
├── infra/ # 基础设施
│ ├── idl/ # 从 Thrift 自动生成的 API Schema
│ └── utils/ # 工具库
└── config/ # 共享配置(ESLint、TypeScript等)
💡 Monorepo 的优势:所有前端代码在一个仓库中,依赖管理、代码复用、原子化提交都变得轻而易举!
🔗 技术栈分析
让我们看看 Coze Loop 的"工具箱"里都有哪些"利器":
后端核心技术栈:
技术组件 | 作用 | 为什么选它? |
---|---|---|
Go 1.24+ | 编程语言 | 高性能、天生并发、编译型语言,适合构建高吞吐量服务 |
CloudWeGo Kitex | RPC 框架 | 字节跳动开源,高性能、支持多协议(Thrift/Protobuf) |
CloudWeGo Hertz | HTTP 框架 | 高性能 HTTP 框架,兼容 net/http 生态 |
Eino | LLM 统一接入层 | 字节跳动开源,屏蔽不同 LLM 的差异,统一调用接口 |
MySQL | 关系型数据库 | 存储结构化数据(用户、项目、配置等) |
Redis | 缓存 + 分布式锁 | 高速缓存、分布式 ID 生成、限流 |
ClickHouse | 列式数据库 | 存储海量观测数据(Trace),支持高效分析查询 |
RocketMQ | 消息队列 | 异步任务处理(评测任务、数据导入等) |
MinIO | 对象存储 | 存储文件(数据集、导出文件等) |
GORM | ORM 框架 | 数据库访问层,简化 SQL 操作 |
Wire | 依赖注入 | Google 开源,编译时依赖注入,零运行时开销 |
前端核心技术栈:
技术组件 | 作用 | 为什么选它? |
---|---|---|
React 18 | UI 框架 | 主流前端框架,生态丰富 |
TypeScript 5.8 | 编程语言 | 类型安全,大型项目必备 |
Rsbuild | 构建工具 | 字节跳动开源,基于 Rspack(Rust 版 Webpack) |
Rush.js | Monorepo 工具 | 微软开源,企业级 Monorepo 管理 |
Zustand | 状态管理 | 轻量级状态管理,比 Redux 简单 |
React Router v6 | 路由 | SPA 路由管理 |
TailwindCSS | CSS 框架 | 原子化 CSS,快速构建 UI |
🌐 外部系统集成
Coze Loop 并非孤岛,它与多个外部系统协同工作:
graph LR
A[Coze Loop] --> B[LLM 模型服务]
A --> C[对象存储 S3/MinIO]
A --> D[消息队列 RocketMQ]
A --> E[缓存 Redis]
A --> F[数据库 MySQL]
A --> G[分析数据库 ClickHouse]
B --> B1[OpenAI]
B --> B2[火山方舟]
B --> B3[千帆]
B --> B4[通义千问]
B --> B5[Gemini]
B --> B6[Claude]
style A fill:#ff6b6b,stroke:#333,stroke-width:2px
style B fill:#4ecdc4,stroke:#333,stroke-width:2px
💡 集成方式亮点:
- LLM 统一接入:通过 Eino 框架,无缝支持 6+ 种主流 LLM 提供商
- 配置外部化:所有敏感信息(API Key、数据库密码)通过环境变量注入
- 健康检查机制:每个依赖服务都有完善的健康检查,确保系统稳定性
🔄 架构流程描述
让我们跟随一个"用户创建并执行 Prompt"的典型场景,看看数据是如何在系统中流转的:
sequenceDiagram
participant User as 👤 用户浏览器
participant Nginx as 🌐 Nginx
participant Hertz as 🚀 Hertz (HTTP Gateway)
participant PromptApp as 📝 Prompt Application
participant PromptDomain as 🧠 Prompt Domain Service
participant LLMService as 🤖 LLM Service
participant DB as 💾 MySQL
participant Redis as ⚡ Redis
participant Eino as 🔌 Eino Framework
participant LLM as 🧙 LLM API
participant Tracer as 📊 Tracer Service
User->>Nginx: 1. 创建 Prompt
Nginx->>Hertz: 转发请求
Hertz->>PromptApp: 调用 CreatePrompt
PromptApp->>PromptDomain: 校验并保存
PromptDomain->>DB: 持久化 Prompt
DB-->>PromptDomain: 返回 ID
PromptDomain-->>User: 返回创建成功
User->>Nginx: 2. 执行 Prompt
Nginx->>Hertz: 转发请求
Hertz->>PromptApp: 调用 ExecutePrompt
PromptApp->>Redis: 检查限流
Redis-->>PromptApp: 通过
PromptApp->>PromptDomain: 执行业务逻辑
PromptDomain->>DB: 查询 Prompt 配置
DB-->>PromptDomain: 返回配置
PromptDomain->>LLMService: 调用 LLM
LLMService->>Eino: 统一接口调用
Eino->>LLM: 调用实际 LLM API
LLM-->>Eino: 返回结果
Eino-->>LLMService: 返回结果
LLMService->>Tracer: 记录 Trace
Tracer->>DB: 保存 Trace 元数据
Tracer->>Redis: 缓存热数据
LLMService-->>PromptDomain: 返回结果
PromptDomain-->>User: 流式返回结果
Note over User,LLM: 整个过程约 200-500ms(取决于 LLM 响应)
📝 关键节点说明:
- Nginx 反向代理:负责静态文件服务和 API 请求转发
- Hertz HTTP 网关:统一的 HTTP 入口,处理认证、日志、限流等横切关注点
- Application 层:编排多个 Domain Service 完成复杂用例
- Domain 层:核心业务逻辑,如 Prompt 参数校验、版本管理等
- Eino 框架:屏蔽不同 LLM 的差异,提供统一的调用接口
- Tracer 服务:实时记录每次调用的完整链路,便于问题排查和性能分析
2. 目录结构与核心流程 📂
🗂️ 目录组织逻辑
Coze Loop 的目录结构非常清晰,遵循"约定优于配置"的原则。让我带你深入理解每个目录的职责:
后端目录结构:
backend/
├── cmd/ # 程序入口
│ ├── main.go # 主程序入口 🚪
│ ├── consumer.go # 消息消费者启动
│ └── script/ # 启动脚本
│
├── api/ # HTTP API 层
│ ├── router/ # 路由定义
│ ├── handler/ # HTTP 请求处理器
│ │ └── coze/loop/apis/ # 各领域的 API Handler
│ └── router_gen.go # 自动生成的路由代码
│
├── modules/ # 业务模块(DDD 核心)
│ ├── data/ # 数据集管理领域 📊
│ ├── evaluation/ # 评测领域 🧪
│ ├── foundation/ # 基础服务(用户、文件、空间)🏛️
│ ├── llm/ # LLM 模型管理 🤖
│ ├── observability/ # 观测(Trace)📈
│ └── prompt/ # Prompt 管理 📝
│
├── infra/ # 基础设施组件
│ ├── db/ # 数据库连接池
│ ├── redis/ # Redis 客户端
│ ├── mq/ # 消息队列
│ ├── ck/ # ClickHouse 客户端
│ ├── fileserver/ # 文件存储(S3/MinIO)
│ ├── looptracer/ # 分布式追踪
│ ├── metrics/ # 指标监控
│ ├── limiter/ # 限流器
│ └── ...
│
├── pkg/ # 共享工具包
│ ├── errorx/ # 错误处理
│ ├── logs/ # 日志
│ ├── lang/ # 语言工具(类型转换等)
│ └── ...
│
├── kitex_gen/ # Kitex 自动生成的代码
│ └── coze/loop/ # Thrift 定义对应的 Go 代码
│
├── loop_gen/ # 业务层生成的代码
│ └── coze/loop/ # GORM 生成的 DAO 代码
│
├── conf/ # 配置文件目录
│ ├── infrastructure.yaml # 基础设施配置
│ └── locales/ # 国际化文件
│
├── idl -> ../idl/ # 软链接到根目录的 IDL
└── go.mod # Go 模块定义
前端目录结构:
frontend/
├── apps/
│ └── cozeloop/ # 主应用 🎯
│ ├── src/
│ │ ├── pages/ # 页面组件
│ │ ├── layouts/ # 布局组件
│ │ ├── routes/ # 路由配置
│ │ └── App.tsx # 根组件
│ ├── rsbuild.config.ts # 构建配置
│ └── package.json
│
├── packages/
│ └── cozeloop/ # 业务组件库
│ ├── account/ # 账号相关
│ ├── auth-pages/ # 认证页面
│ ├── prompt-pages/ # Prompt 相关页面
│ ├── evaluate-pages/# 评测相关页面
│ ├── observation-pages/ # 观测相关页面
│ ├── components/ # 通用组件
│ ├── stores/ # 状态管理
│ └── api-schema/ # API 类型定义(自动生成)
│
├── infra/ # 基础设施
│ ├── idl/ # IDL 自动生成工具
│ ├── utils/ # 工具库
│ └── plugins/ # 构建插件
│
├── config/ # 共享配置
│ ├── eslint-config/ # ESLint 配置
│ ├── ts-config/ # TypeScript 配置
│ ├── tailwind-config/ # TailwindCSS 配置
│ └── ...
│
└── rush.json # Rush Monorepo 配置
🔑 关键文件定位
"第一个应阅读"的文件清单:
后端启动入口:
backend/cmd/main.go
- 这是一切的开始!了解系统如何初始化组件、启动服务
后端配置示例:
backend/conf/infrastructure.yaml
- 看看系统需要哪些配置项,每个配置的作用
API 路由定义:
backend/api/router/register.go
- 了解系统提供了哪些 HTTP 接口
前端入口:
frontend/apps/cozeloop/src/App.tsx
- 前端的根组件,了解路由和全局状态管理
前端路由配置:
frontend/apps/cozeloop/src/routes/
- 看看前端有哪些页面,URL 路径规划
部署配置:
release/deployment/docker-compose/docker-compose.yml
- 了解系统的部署架构和依赖的基础服务
🧩 模块依赖关系
Coze Loop 的各模块之间有清晰的依赖关系,遵循单向依赖原则:
依赖层次(从上到下):
┌─────────────────────────────────────┐
│ api (HTTP API 层) │ ← 最外层,依赖所有
├─────────────────────────────────────┤
│ modules (业务领域模块) │ ← 业务核心
│ - data │
│ - evaluation │
│ - foundation │
│ - llm │
│ - observability │
│ - prompt │
├─────────────────────────────────────┤
│ infra (基础设施层) │ ← 被所有业务模块依赖
│ - db, redis, mq, ck │
│ - fileserver, tracer, metrics │
│ - limiter, idgen, etc. │
├─────────────────────────────────────┤
│ pkg (通用工具包) │ ← 最底层,零依赖
│ - errorx, logs, lang, etc. │
└─────────────────────────────────────┘
💡 依赖规则:
- ✅ 上层可以依赖下层
- ✅ 同层模块之间通过接口(interface)解耦
- ❌ 下层不能依赖上层(否则会循环依赖)
- ❌ 业务模块之间不能直接依赖(通过 RPC 或事件通信)
模块间通信方式:
通信场景 | 通信方式 | 示例 |
---|---|---|
API 调用业务模块 | 直接调用 | api/handler → modules/prompt/application |
业务模块访问基础设施 | 依赖注入接口 | prompt/domain/service → infra/db.Provider (接口) |
业务模块间通信 | RPC 或消息队列 | evaluation/domain → RPC → prompt/domain |
异步任务 | 消息队列 | evaluation/application → MQ → evaluation/infra/mq/consumer |
🎬 典型业务流程深度剖析
让我们选择"创建并执行一个 Prompt 评测实验"这个核心场景,深入理解数据流和控制流:
场景:用户在前端创建一个评测实验,测试 Prompt 在多个测试用例下的表现
步骤 1:前端发起请求
// frontend/packages/cozeloop/evaluate-pages/src/components/ExperimentForm.tsx
import {
createExperiment } from '@cozeloop/api-schema';
const handleSubmit = async (values) => {
const result = await createExperiment({
name: values.name,
promptId: values.promptId,
evaluationSetId: values.evaluationSetId,
evaluators: values.evaluators,
});
// 跳转到实验详情页
navigate(`/evaluation/experiments/${
result.id}`);
};
步骤 2:HTTP API 层接收请求
// backend/api/handler/coze/loop/apis/experiment_service.go
func (h *APIHandler) CreateExperiment(ctx context.Context, req *loop.CreateExperimentReq) (*loop.CreateExperimentResp, error) {
// 1. 参数校验
if err := validator.Validate(req); err != nil {
return nil, errorx.Wrap(err, errorx.CodeInvalidParam)
}
// 2. 调用 Application 层
expt, err := h.ExperimentApp.Create(ctx, &application.CreateExperimentParam{
Name: req.Name,
PromptID: req.PromptId,
EvaluationSetID: req.EvaluationSetId,
Evaluators: req.Evaluators,
})
if err != nil {
return nil, err
}
// 3. 返回响应
return &loop.CreateExperimentResp{
Experiment: convertor.ToExperimentVO(expt),
}, nil
}
📝 实现文件:backend/api/handler/coze/loop/apis/experiment_service.go
步骤 3:Application 层编排业务逻辑
// backend/modules/evaluation/application/experiment_app.go
func (app *ExperimentApplication) Create(ctx context.Context, param *CreateExperimentParam) (*entity.Experiment, error) {
// 1. 校验 Prompt 是否存在(跨领域调用)
prompt, err := app.promptRPC.GetPrompt(ctx, param.PromptID)
if err != nil {
return nil, errorx.Wrap(err, errno.PromptNotFound)
}
// 2. 校验评测集是否存在
evalSet, err := app.evalSetService.GetByID(ctx, param.EvaluationSetID)
if err != nil {
return nil, errorx.Wrap(err, errno.EvaluationSetNotFound)
}
// 3. 创建实验实体
expt := entity.NewExperiment(param.Name, prompt, evalSet)
// 4. 保存到数据库
if err := app.exptService.Create(ctx, expt); err != nil {
return nil, err
}
// 5. 发送异步评测任务到消息队列
if err := app.mqProducer.SendExperimentRunTask(ctx, &mq.ExperimentRunTask{
ExperimentID: expt.ID,
}); err != nil {
log.Error(ctx, "failed to send experiment run task", "err", err)
// 任务发送失败不影响创建
}
return expt, nil
}
📝 实现文件:backend/modules/evaluation/application/experiment_app.go
步骤 4:Domain Service 执行核心业务逻辑
// backend/modules/evaluation/domain/service/experiment_service.go
func (s *ExperimentService) Create(ctx context.Context, expt *entity.Experiment) error {
// 1. 业务规则校验
if err := expt.Validate(); err != nil {
return errorx.Wrap(err, errno.InvalidExperiment)
}
// 2. 生成唯一 ID
id, err := s.idgen.GenerateID(ctx)
if err != nil {
return err
}
expt.ID = id
// 3. 设置默认值
expt.Status = entity.ExperimentStatusPending
expt.CreatedAt = time.Now()
// 4. 调用仓储层保存
return s.repo.Create(ctx, expt)
}
📝 实现文件:backend/modules/evaluation/domain/service/experiment_service.go
步骤 5:Infra 层持久化数据
// backend/modules/evaluation/infra/repo/experiment_repo.go
func (r *ExperimentRepo) Create(ctx context.Context, expt *entity.Experiment) error {
// 转换为 PO(Persistent Object)
po := &po.Experiment{
ID: expt.ID,
Name: expt.Name,
PromptID: expt.PromptID,
EvaluationSetID: expt.EvaluationSetID,
Status: int(expt.Status),
CreatedAt: expt.CreatedAt,
}
// 使用 GORM 保存
return r.db.WithContext(ctx).Create(po).Error
}
📝 实现文件:backend/modules/evaluation/infra/repo/experiment_repo.go
步骤 6:消息队列异步处理评测任务
// backend/modules/evaluation/infra/mq/rocket/experiment_consumer.go
func (c *ExperimentConsumer) ConsumeMessage(ctx context.Context, msgs ...*primitive.MessageExt) (consumer.ConsumeResult, error) {
for _, msg := range msgs {
task := &mq.ExperimentRunTask{
}
if err := json.Unmarshal(msg.Body, task); err != nil {
log.Error(ctx, "failed to unmarshal task", "err", err)
continue
}
// 执行评测
if err := c.experimentApp.Run(ctx, task.ExperimentID); err != nil {
log.Error(ctx, "failed to run experiment", "experimentID", task.ExperimentID, "err", err)
return consumer.ConsumeRetryLater, err
}
}
return consumer.ConsumeSuccess, nil
}
📝 实现文件:backend/modules/evaluation/infra/mq/rocket/experiment_consumer.go
步骤 7:评测执行(核心业务逻辑)
// backend/modules/evaluation/application/experiment_app.go
func (app *ExperimentApplication) Run(ctx context.Context, experimentID string) error {
// 1. 获取实验配置
expt, err := app.exptService.GetByID(ctx, experimentID)
if err != nil {
return err
}
// 2. 获取评测集的所有测试用例
items, err := app.evalSetService.GetItems(ctx, expt.EvaluationSetID)
if err != nil {
return err
}
// 3. 并发执行所有测试用例
var wg sync.WaitGroup
results := make([]*entity.ExperimentResult, len(items))
for i, item := range items {
wg.Add(1)
go func(idx int, testItem *entity.EvaluationSetItem) {
defer wg.Done()
// 3.1 调用 Prompt 执行
output, err := app.promptRPC.Execute(ctx, &rpc.ExecutePromptReq{
PromptID: expt.PromptID,
Input: testItem.Input,
})
if err != nil {
log.Error(ctx, "failed to execute prompt", "err", err)
return
}
// 3.2 调用评估器
scores, err := app.evaluatorService.Evaluate(ctx, &EvaluateParam{
Input: testItem.Input,
Output: output.Content,
Expected: testItem.Expected,
Evaluators: expt.Evaluators,
})
if err != nil {
log.Error(ctx, "failed to evaluate", "err", err)
return
}
// 3.3 保存结果
results[idx] = &entity.ExperimentResult{
ExperimentID: experimentID,
ItemID: testItem.ID,
Output: output.Content,
Scores: scores,
}
}(i, item)
}
wg.Wait()
// 4. 保存所有结果
if err := app.resultService.BatchCreate(ctx, results); err != nil {
return err
}
// 5. 更新实验状态为完成
return app.exptService.UpdateStatus(ctx, experimentID, entity.ExperimentStatusCompleted)
}
📝 实现文件:backend/modules/evaluation/application/experiment_app.go
📊 流程图总览
flowchart TD
A[前端用户操作] -->|HTTP POST| B[Nginx 反向代理]
B --> C[Hertz HTTP 网关]
C --> D[API Handler]
D --> E[Application 层]
E --> F{校验 Prompt}
F -->|RPC| G[Prompt Service]
G --> F
F --> H{校验评测集}
H --> I[EvaluationSet Service]
I --> H
H --> J[Domain Service]
J --> K[生成 ID]
J --> L[业务规则校验]
L --> M[Infra Repo]
M --> N[(MySQL)]
J --> O[发送 MQ 消息]
O --> P{
{RocketMQ}}
P --> Q[Consumer 消费任务]
Q --> R[执行评测]
R --> S[并发调用 Prompt]
R --> T[并发调用 Evaluator]
S --> U[LLM Service]
U --> V[Eino 框架]
V --> W[实际 LLM API]
W --> V
V --> U
U --> S
T --> Y[保存评测结果]
Y --> N
R --> Z[更新实验状态]
Z --> N
style A fill:#ff6b6b
style N fill:#4ecdc4
style P fill:#ffe66d
style W fill:#a8dadc
3. 代码结构观察 🔍
💎 代码组织模式
Coze Loop 的代码组织体现了多种优秀的设计模式和编码实践:
1. 依赖注入(Dependency Injection)
Coze Loop 使用 Google Wire 进行编译时依赖注入。Wire 的优势是零运行时开销,所有依赖关系在编译期就确定了。
// backend/modules/evaluation/application/wire.go
//go:build wireinject
// +build wireinject
package application
import (
"github.com/google/wire"
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/service"
"github.com/coze-dev/coze-loop/backend/modules/evaluation/infra/repo"
)
// ProviderSet 定义依赖集合
var ProviderSet = wire.NewSet(
// Domain Services
service.NewExperimentService,
service.NewEvaluatorService,
// Repositories
repo.NewExperimentRepo,
repo.NewEvaluatorRepo,
// Application Services
NewExperimentApplication,
NewEvaluatorApplication,
)
// InitExperimentApplication 自动生成的构造函数
func InitExperimentApplication(
db db.Provider,
redis redis.Cmdable,
idgen idgen.IIDGenerator,
mqFactory mq.IFactory,
) (*ExperimentApplication, error) {
wire.Build(ProviderSet)
return nil, nil
}
运行 wire gen
后,会自动生成 wire_gen.go
,包含完整的依赖注入代码:
// backend/modules/evaluation/application/wire_gen.go
// Code generated by Wire. DO NOT EDIT.
func InitExperimentApplication(
db db.Provider,
redis redis.Cmdable,
idgen idgen.IIDGenerator,
mqFactory mq.IFactory,
) (*ExperimentApplication, error) {
experimentRepo := repo.NewExperimentRepo(db)
experimentService := service.NewExperimentService(experimentRepo, idgen)
evaluatorRepo := repo.NewEvaluatorRepo(db)
evaluatorService := service.NewEvaluatorService(evaluatorRepo)
mqProducer := mqFactory.NewProducer()
experimentApplication := NewExperimentApplication(
experimentService,
evaluatorService,
mqProducer,
)
return experimentApplication, nil
}
💡 为什么使用 Wire?
- ✅ 编译期检查,避免运行时依赖错误
- ✅ 零反射,性能无损耗
- ✅ 代码可读性强,依赖关系一目了然
2. 仓储模式(Repository Pattern)
Domain 层定义接口,Infra 层实现,实现了"依赖倒置":
// backend/modules/evaluation/domain/repo/experiment_repo.go
package repo
import (
"context"
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity"
)
// IExperimentRepo 实验仓储接口(由 Domain 层定义)
type IExperimentRepo interface {
Create(ctx context.Context, expt *entity.Experiment) error
GetByID(ctx context.Context, id string) (*entity.Experiment, error)
Update(ctx context.Context, expt *entity.Experiment) error
Delete(ctx context.Context, id string) error
List(ctx context.Context, query *ExperimentQuery) ([]*entity.Experiment, int64, error)
}
// backend/modules/evaluation/infra/repo/experiment_repo.go
package repo
import (
"context"
"github.com/coze-dev/coze-loop/backend/infra/db"
domainRepo "github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/repo"
"github.com/coze-dev/coze-loop/backend/modules/evaluation/domain/entity"
)
// ExperimentRepo 实验仓储实现(由 Infra 层实现)
type ExperimentRepo struct {
db db.Provider
}
func NewExperimentRepo(db db.Provider) domainRepo.IExperimentRepo {
return &ExperimentRepo{
db: db}
}
func (r *ExperimentRepo) Create(ctx context.Context, expt *entity.Experiment) error {
// 实现细节...
}
💡 仓储模式的好处:
- Domain 层不依赖具体的数据库实现
- 可以轻松切换数据库(如从 MySQL 换到 PostgreSQL)
- 便于单元测试(Mock Repo 接口即可)
3. 工厂模式(Factory Pattern)
LLM 服务的创建使用了工厂模式:
// backend/modules/llm/domain/service/llm_factory.go
package service
import (
"github.com/cloudwego/eino"
"github.com/coze-dev/coze-loop/backend/modules/llm/domain/entity"
)
type LLMFactory interface {
CreateChatModel(config *entity.LLMConfig) (eino.ChatModel, error)
}
type llmFactory struct {
// 注册的模型提供商
providers map[string]ModelProvider
}
func (f *llmFactory) CreateChatModel(config *entity.LLMConfig) (eino.ChatModel, error) {
provider, ok := f.providers[config.Provider]
if !ok {
return nil, fmt.Errorf("unsupported provider: %s", config.Provider)
}
return provider.CreateChatModel(config)
}
4. 策略模式(Strategy Pattern)
评估器(Evaluator)使用策略模式,支持多种评估策略:
// backend/modules/evaluation/domain/service/evaluator_strategy.go
package service
type EvaluatorStrategy interface {
Evaluate(ctx context.Context, input, output, expected string) (float64, error)
}
// 精确匹配策略
type ExactMatchStrategy struct{
}
func (s *ExactMatchStrategy) Evaluate(ctx context.Context, input, output, expected string) (float64, error) {
if output == expected {
return 1.0, nil
}
return 0.0, nil
}
// LLM 评分策略
type LLMJudgeStrategy struct {
llmService *LLMService
}
func (s *LLMJudgeStrategy) Evaluate(ctx context.Context, input, output, expected string) (float64, error) {
// 调用 LLM 进行评分...
}
🎨 设计模式识别
让我们汇总一下 Coze Loop 中使用的主要设计模式:
设计模式 | 应用场景 | 位置 | 作用 |
---|---|---|---|
依赖注入(DI) | 整个项目 | 所有模块的 wire.go |
解耦依赖,便于测试 |
仓储模式 | 数据访问层 | domain/repo + infra/repo |
隔离数据访问逻辑 |
工厂模式 | LLM 模型创建 | llm/domain/service/llm_factory.go |
统一创建接口 |
策略模式 | 评估器 | evaluation/domain/service/evaluator_strategy.go |
灵活切换评估算法 |
观察者模式 | 事件发布订阅 | evaluation/domain/events |
解耦模块间通信 |
单例模式 | 全局组件 | pkg/logs/default.go |
确保唯一实例 |
适配器模式 | LLM 接口适配 | Eino 框架 | 统一不同 LLM 的接口 |
建造者模式 | 复杂对象构建 | entity.NewExperiment() |
分步构建复杂对象 |
📏 代码质量观察
优点:
清晰的分层架构 ✅
- 每层职责明确,依赖关系单向
- 符合 SOLID 原则中的单一职责和依赖倒置
丰富的单元测试 ✅
- 几乎每个 Service 都有对应的
_test.go
文件 - 使用
go-mock
进行依赖 Mock
- 几乎每个 Service 都有对应的
完善的错误处理 ✅
- 统一的错误码体系(
pkg/errorx
) - 错误信息包含上下文,便于排查
- 统一的错误码体系(
国际化支持 ✅
- 使用
go-i18n
实现多语言 - 错误信息、UI 文案都支持中英文
- 使用
代码生成工具 ✅
- Thrift 自动生成 RPC 代码
- GORM Gen 自动生成 DAO 代码
- Wire 自动生成依赖注入代码
可观察的改进机会:
部分文件较长 ⚠️
- 如
backend/modules/evaluation/domain/service/experiment_service.go
超过 1000 行 - 💡 学习机会:思考如何将大文件拆分为多个职责单一的小文件
- 如
部分注释不足 ⚠️
- 有些复杂的业务逻辑缺少注释说明
- 💡 学习机会:为关键业务逻辑补充文档注释
部分重复代码 ⚠️
- 如多个模块都有类似的分页逻辑
- 💡 学习机会:提取通用的分页工具函数
TODO 和 FIXME 📝
- 搜索
// TODO:
和// FIXME:
可以找到一些潜在的改进点 - 💡 学习机会:尝试修复这些标记的问题,作为贡献的起点
- 搜索
第二部分:技能需求清单(你的学习弹药库)📚
好了,我们已经对 Coze Loop 的架构有了全景式的理解。现在让我们务实一点,看看要真正掌握这个项目,你需要具备哪些技能 💪
💡 心法提示:不要被技能清单吓倒!没有人一开始就会所有技能。学习是渐进的过程,先掌握基础,再逐步深入。
1. 基础技能要求 🎯
这些是你"入场"必须具备的基础技能。如果你对某项技能还不熟悉,先暂停,去把它学会再回来。
🐹 Go 语言(后端核心)
必须掌握的语法特性:
基础语法:
- 变量声明、数据类型、控制流
- 函数、方法、接口
- 结构体(Struct)和嵌入(Embedding)
- 指针和值类型的区别
并发编程 ⭐⭐⭐(重要!):
- Goroutine 和 Channel
sync.WaitGroup
、sync.Mutex
、sync.RWMutex
context.Context
的使用(超级重要!)select
语句和超时控制
错误处理:
error
接口- 错误包装(
fmt.Errorf
、errors.Wrap
) defer
、panic
、recover
接口和多态:
- 接口定义和实现
- 空接口
interface{}
- 类型断言和类型转换
项目特定要求:
- Go 版本:Go 1.24+ (项目使用了最新的 Go 特性)
- Go Modules:理解
go.mod
和go.sum
- 编码规范:遵循《阿里巴巴 Go 开发手册》
📚 学习资源推荐:
- 官方教程:A Tour of Go
- 经典书籍:《Go 程序设计语言》(The Go Programming Language)
- 进阶阅读:《Go 语言高级编程》
📜 TypeScript(前端核心)
必须掌握的语法特性:
类型系统:
- 基本类型、联合类型、交叉类型
- 接口(Interface)和类型别名(Type Alias)
- 泛型(Generics)
- 类型守卫和类型收窄
ES6+ 特性:
- 箭头函数、解构、展开运算符
- Promise、async/await
- 模块化(import/export)
React 相关:
- 函数组件和 Hooks
useState
、useEffect
、useCallback
、useMemo
- Context API
- 自定义 Hooks
项目特定要求:
- TypeScript 版本:5.8+
- 编码规范:遵循项目的 ESLint 配置
📚 学习资源推荐:
- 官方文档:TypeScript Handbook
- React 官方文档:React Docs
🗄️ 数据库基础
必须掌握的概念:
SQL 基础:
- SELECT、INSERT、UPDATE、DELETE
- JOIN(INNER、LEFT、RIGHT)
- 索引、主键、外键
- 事务(ACID)
MySQL 特定:
- InnoDB 存储引擎
- 事务隔离级别
- 锁机制(行锁、表锁)
Redis 基础:
- 基本数据类型(String、Hash、List、Set、Sorted Set)
- 过期时间和 TTL
- Pub/Sub 机制
ClickHouse 基础:
- 列式存储的概念
- 适用场景(OLAP)
- 基本查询语法
📚 学习资源推荐:
- MySQL 官方文档:MySQL Documentation
- Redis 命令参考:Redis Commands
- ClickHouse 文档:ClickHouse Docs
🐳 基础工具
必须掌握的工具:
Git:
- 基本操作(add、commit、push、pull)
- 分支管理(branch、checkout、merge)
- 冲突解决
Docker:
- 基本概念(镜像、容器、网络、卷)
docker
命令(run、exec、logs、ps)docker-compose
的使用
命令行工具:
- Linux 基本命令(ls、cd、grep、ps、kill)
- 文本处理(cat、head、tail、awk、sed)
- 进程管理
📚 学习资源推荐:
- Git 教程:Pro Git 中文版
- Docker 官方文档:Docker Documentation
2. 进阶技能要求 🚀
这些技能不是"入场券",但掌握它们会让你对项目理解更深,能做更复杂的开发。
🏛️ 架构模式
领域驱动设计(DDD):
Coze Loop 的核心架构模式,理解它是深入项目的关键!
核心概念:
- 实体(Entity)、值对象(Value Object)、聚合(Aggregate)
- 领域服务(Domain Service)、应用服务(Application Service)
- 仓储(Repository)、工厂(Factory)
- 领域事件(Domain Event)
分层架构:
- User Interface 层
- Application 层
- Domain 层
- Infrastructure 层
💡 学习路径:
- 先理解"为什么需要 DDD"(解决什么问题)
- 再学习核心概念
- 最后在 Coze Loop 代码中寻找对应的实现
📚 学习资源推荐:
- 经典书籍:《领域驱动设计》(Eric Evans)
- 入门书籍:《实现领域驱动设计》(Vaughn Vernon)
- 博客文章:DDD 实践系列
微服务架构:
核心概念:
- 服务拆分原则
- 服务间通信(RPC、消息队列)
- 服务发现和负载均衡
- 分布式事务
CloudWeGo 技术栈:
- Kitex(RPC 框架)
- Hertz(HTTP 框架)
- Netpoll(网络库)
📚 学习资源推荐:
- 官方文档:CloudWeGo 官网
- Kitex 教程:Kitex Tutorials
消息驱动架构:
- RocketMQ 的使用
- 发布-订阅模式
- 消息幂等性和重试机制
🔧 设计原则
SOLID 原则:
Coze Loop 代码中处处体现这些原则,理解它们会让你"看懂"代码的设计意图。
S - 单一职责原则(SRP):
- 每个类/模块只做一件事
- 示例:Coze Loop 中每个 Service 只负责一个领域实体
O - 开闭原则(OCP):
- 对扩展开放,对修改封闭
- 示例:通过接口定义 Evaluator,新增评估策略无需修改现有代码
L - 里氏替换原则(LSP):
- 子类可以替换父类
- 示例:所有 Repo 实现都可以替换接口
I - 接口隔离原则(ISP):
- 接口应该小而专注
- 示例:不同的 Repo 接口有不同的方法集
D - 依赖倒置原则(DIP):
- 高层模块不应依赖低层模块,都应依赖抽象
- 示例:Domain 层定义接口,Infra 层实现
📚 学习资源推荐:
- 经典书籍:《代码整洁之道》(Clean Code)
- 进阶阅读:《重构:改善既有代码的设计》
🧪 测试驱动开发(TDD)
Coze Loop 有完善的测试体系,学习如何编写和运行测试是必须的。
单元测试:
- 使用 Go 的
testing
包 - Mock 依赖(
gomock
、testify
) - 表驱动测试(Table-Driven Tests)
- 使用 Go 的
集成测试:
- 使用
miniredis
(内存 Redis)和sqlmock
(Mock 数据库) - 测试多个模块的协作
- 使用
📚 学习资源推荐:
- 官方文档:Go Testing
- 最佳实践:Go Test Best Practices
3. 领域特定知识 🌐
🤖 AI 和 LLM 基础
Coze Loop 是 AI Agent 开发平台,理解 AI 和 LLM 的基础知识会让你更好地理解业务需求。
必知概念:
LLM(Large Language Model):
- 什么是 LLM?(GPT、Claude、Gemini 等)
- Prompt 和 Completion 的概念
- Token 和计费模型
Prompt Engineering:
- 什么是 Prompt?
- System Prompt、User Prompt、Assistant Prompt
- Few-Shot Learning、Chain-of-Thought
AI Agent:
- 什么是 Agent?
- Agent 的工作流(Planning、Tool Use、Reflection)
- 多 Agent 协作
📚 学习资源推荐:
- OpenAI Cookbook:OpenAI Cookbook
- Prompt Engineering Guide:Prompt Engineering Guide
📊 观测和监控
Coze Loop 的核心功能之一是"观测",理解这些概念很重要。
核心概念:
Tracing(链路追踪):
- Span、Trace 的概念
- OpenTelemetry 标准
Logging(日志):
- 结构化日志
- 日志级别和分类
Metrics(指标):
- Counter、Gauge、Histogram
- 监控指标的设计
📚 学习资源推荐:
- OpenTelemetry 文档:OpenTelemetry Docs
4. 技能掌握程度建议 🎯
不同阶段的学习者应该把精力放在不同的技能上。这里给出三个阶段的建议:
🌱 阶段一:初学者(0-3 个月经验)
目标:能跑起来项目,看懂基本代码逻辑
必修技能(按优先级):
- ✅ Go 基础语法(70% 掌握)
- ✅ TypeScript 基础语法(60% 掌握)
- ✅ Git 基本操作(80% 掌握)
- ✅ Docker 基本使用(60% 掌握)
- ✅ SQL 基础(60% 掌握)
- ✅ HTTP 协议基础(70% 掌握)
选修技能:
- Redis 基础命令
- Linux 基本命令
学习重点:
- 先不要纠结架构细节,能跑起来就是胜利!
- 跟着文档一步步操作,记录遇到的问题和解决方法
- 尝试修改一些简单的配置,观察效果
🌿 阶段二:有经验的开发者(3-12 个月经验)
目标:理解架构设计,能独立完成功能开发
必修技能(按优先级):
- ✅ Go 高级特性(90% 掌握)
- Context、Goroutine、Channel
- Interface 和多态
- ✅ DDD 核心概念(70% 掌握)
- 分层架构
- Entity、Service、Repo
- ✅ 微服务基础(60% 掌握)
- RPC 通信
- 服务拆分原则
- ✅ 数据库进阶(80% 掌握)
- 事务、锁、索引优化
- ✅ 消息队列基础(60% 掌握)
- RocketMQ 的使用
选修技能:
- SOLID 设计原则
- 常见设计模式
- 单元测试编写
学习重点:
- 阅读核心模块的代码,理解每层的职责
- 尝试实现一个简单的新功能(如新增一个评估器类型)
- 为你编写的代码添加单元测试
🌳 阶段三:意欲贡献代码的进阶者(12 个月+ 经验)
目标:深度理解系统,能优化架构和性能
必修技能(按优先级):
- ✅ Go 性能优化(80% 掌握)
- Profiling(pprof)
- 内存管理和 GC
- 并发优化
- ✅ DDD 深度实践(90% 掌握)
- 领域建模
- 事件驱动架构
- ✅ 分布式系统(70% 掌握)
- CAP 理论
- 一致性算法
- 分布式事务
- ✅ CloudWeGo 深度使用(80% 掌握)
- Kitex 高级特性
- 性能调优
- ✅ 可观测性(70% 掌握)
- OpenTelemetry 集成
- 性能分析
选修技能:
- 编译原理(理解 Thrift IDL)
- 代码生成工具开发
- CI/CD 流水线设计
学习重点:
- 分析系统的性能瓶颈,提出优化方案
- 阅读项目的 TODO 和 FIXME,尝试修复
- 参与社区讨论,提交 Pull Request
呼~ 第二部分就到这里!技能清单看起来很长,但不要慌 😊 学习是一个渐进的过程,按照阶段一步步来就好。
接下来我们进入最激动人心的部分——学习路径规划!我会手把手教你如何快速上手 Coze Loop 🚀
第三部分:学习路径规划(你的专属教练计划)🎯
本部分目标:提供清晰、可执行的学习路径,让你从"一脸懵逼"到"运筹帷幄"。
💡 心法提示:学习路径不是线性的!遇到困难时可以跳到其他章节,回头再来挑战。
1. 项目运行入口定位(快速上手)⚡
让我们先把项目跑起来!这是建立信心的第一步 💪
🎯 一键启动指南
前置准备(10 分钟)
在开始之前,请确保你的电脑上已安装:
✅ Docker Desktop(版本 20.10+)
- Windows: 从 Docker 官网 下载
- macOS: 使用 Homebrew
brew install --cask docker
- Linux: 使用包管理器安装(如
apt install docker.io
)
✅ Git(版本 2.30+)
- 检查是否已安装:
git --version
- 未安装则从 Git 官网 下载
- 检查是否已安装:
✅ 至少 8GB 内存和 20GB 磁盘空间
- Coze Loop 会启动多个服务容器(MySQL、Redis、ClickHouse 等)
步骤 1:克隆代码(2 分钟)
打开终端,执行以下命令:
# 克隆项目
git clone https://github.com/coze-dev/coze-loop.git
# 进入项目目录
cd coze-loop
# 查看目录结构,确认克隆成功
ls -la
步骤 2:配置 LLM 模型(3 分钟)
Coze Loop 需要连接 LLM 服务才能运行。这里以火山方舟为例(你也可以用 OpenAI、千帆等):
# 编辑模型配置文件
vim release/deployment/docker-compose/conf/model_config.yaml
# 或者用你喜欢的编辑器(VS Code、Sublime Text 等)
修改以下内容:
models:
- model_id: "default-model"
provider: "volcengine_ark" # 或 "openai"、"qianfan" 等
model: "ep-xxxxx" # 你的模型 Endpoint ID
api_key: "your-api-key" # 你的 API Key
💡 如何获取 API Key?
- 火山方舟(国内):https://www.volcengine.com/docs/82379/1541594
- OpenAI(国外):https://platform.openai.com/api-keys
步骤 3:启动服务(5-10 分钟)
# 方式一:使用默认模式启动(推荐)
make compose-up
# 方式二:使用开发模式启动(支持代码热更新)
make compose-up-dev
⏳ 等待时间:首次启动需要下载多个 Docker 镜像,大约 5-10 分钟。你可以:
- 泡杯咖啡 ☕
- 看看 README 文档 📖
- 思考一下今天的晚饭 🍱
步骤 4:验证成功(1 分钟)
看到以下日志,说明启动成功了 🎉
[+] Running 13/13
✔ Network coze-loop-network Created
✔ Container coze-loop-redis Healthy
✔ Container coze-loop-mysql Healthy
✔ Container coze-loop-clickhouse Healthy
✔ Container coze-loop-minio Healthy
✔ Container coze-loop-rmq-namesrv Healthy
✔ Container coze-loop-rmq-broker Healthy
✔ Container coze-loop-python-faas Healthy
✔ Container coze-loop-js-faas Healthy
✔ Container coze-loop-app Healthy
✔ Container coze-loop-nginx Started
打开浏览器,访问:http://localhost:8082
看到 Coze Loop 的登录页面,恭喜你,第一关通过了! 🚀
🛠️ 环境配置清单
让我们深入理解一下启动过程中发生了什么:
Docker Compose 启动的服务:
服务名 | 容器名 | 端口 | 作用 | 启动优先级 |
---|---|---|---|---|
redis | coze-loop-redis | 6379 | 缓存、分布式锁 | 1️⃣ |
mysql | coze-loop-mysql | 3306 | 主数据库 | 1️⃣ |
clickhouse | coze-loop-clickhouse | 9000 | 观测数据存储 | 1️⃣ |
minio | coze-loop-minio | 9000 | 对象存储(文件) | 1️⃣ |
rocketmq-namesrv | coze-loop-rmq-namesrv | 9876 | 消息队列注册中心 | 2️⃣ |
rocketmq-broker | coze-loop-rmq-broker | 10911 | 消息队列 Broker | 2️⃣ |
python-faas | coze-loop-python-faas | 8890 | Python 代码执行沙箱 | 2️⃣ |
js-faas | coze-loop-js-faas | 8891 | JavaScript 代码执行沙箱 | 2️⃣ |
app | coze-loop-app | 8888 | Coze Loop 后端服务 | 3️⃣ |
nginx | coze-loop-nginx | 8082 | 前端静态文件 + API 反代 | 4️⃣ |
💡 启动顺序说明:
- Docker Compose 会按照
depends_on
和healthcheck
确保服务按正确顺序启动 - 每个服务都有健康检查,只有健康后才会启动依赖它的服务
⚠️ 常见配置陷阱及解决方案
陷阱 1:端口被占用
现象:启动时报错 bind: address already in use
排查方法:
# 查看 8082 端口被谁占用
# Windows
netstat -ano | findstr :8082
# macOS/Linux
lsof -i :8082
解决方案:
方案 A:停止占用端口的进程
# 假设 PID 是 12345
kill 12345 # macOS/Linux
taskkill /PID 12345 /F # Windows
方案 B:修改 Coze Loop 的端口
# 编辑 .env 文件
vim release/deployment/docker-compose/.env
# 修改端口
COZE_LOOP_NGINX_PORT=18082 # 改成其他未被占用的端口
陷阱 2:Docker 内存不足
现象:容器反复重启,日志显示 OOMKilled
排查方法:
# 查看 Docker 内存限制
docker info | grep Memory
解决方案:
- Docker Desktop:设置 → Resources → Memory → 增加到至少 8GB
- Linux:检查系统内存是否充足,关闭其他占用内存的程序
陷阱 3:网络问题导致镜像拉取失败
现象:Error response from daemon: Get https://registry-1.docker.io/...
解决方案:
配置 Docker 镜像加速器(国内用户):
# 编辑 Docker 配置
# macOS: ~/.docker/daemon.json
# Linux: /etc/docker/daemon.json
# Windows: Docker Desktop 设置 → Docker Engine
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
]
}
# 重启 Docker
# macOS/Windows: Docker Desktop 重启
# Linux:
sudo systemctl restart docker
陷阱 4:LLM API 配置错误
现象:项目启动成功,但执行 Prompt 时报错 invalid api_key
排查方法:
# 查看后端日志
docker logs coze-loop-app -f
# 搜索 "LLM" 关键字
docker logs coze-loop-app 2>&1 | grep LLM
解决方案:
检查 model_config.yaml
中的配置:
# 正确的配置示例
models:
- model_id: "default-model"
provider: "volcengine_ark"
model: "ep-20241201234567-xxxxx" # 确保是正确的 Endpoint ID
api_key: "xxxxxxxxxxxxxxxxxxxxxx" # 确保 API Key 没有多余的空格或换行
✅ 验证成功标志
启动成功后,你应该能看到:
1. 访问前端页面
- 打开 http://localhost:8082
- 看到登录页面
- 可以注册新账号(开源版无需真实邮箱验证)
2. 查看容器状态
# 所有容器都应该是 "Up" 状态
docker ps --format "table {
{.Names}}\t{
{.Status}}"
3. 测试后端 API
# 健康检查接口
curl http://localhost:8888/ping
# 应该返回:
# {"message":"pong"}
4. 登录并创建第一个 Prompt
- 注册账号
- 登录后进入"Prompt 管理"
- 创建一个简单的 Prompt:
你好,请介绍一下自己
- 点击"执行",看到 LLM 返回结果
看到 LLM 的回复,说明整个链路打通了!🎉
2. 循序渐进学习计划(四阶段法)📈
好了,项目跑起来了,现在让我们制定一个清晰的学习计划。我把学习分为四个阶段,每个阶段都有明确的目标和可验证的成果。
🌱 阶段一:环境搭建和项目启动(1-2 天)
目标:成功运行项目并能打个断点 🎯
学习任务清单:
- [x] ✅ 完成项目启动(上一节已经完成)
- [ ] 📖 阅读 README.md 和 README.cn.md
- [ ] 🔍 理解 Docker Compose 配置文件
- [ ] 🐛 学会查看容器日志和排查问题
- [ ] 💻 配置 IDE(VS Code 或 GoLand)
- [ ] 🔧 尝试修改一个配置并重启服务
详细步骤:
步骤 1:阅读官方文档(30 分钟)
# 在浏览器中打开 README
# 或者用 Markdown 阅读器打开
cat README.cn.md | less
关注这些关键信息:
- 项目的核心功能
- 部署方式(Docker Compose 和 Helm)
- 开发指南链接
步骤 2:理解 Docker Compose 配置(1 小时)
打开 release/deployment/docker-compose/docker-compose.yml
,逐行阅读并理解:
# 示例:理解 app 服务的配置
app:
container_name: "coze-loop-app"
image: "..." # 使用的镜像
networks:
- coze-loop-network # 网络配置
ports:
- "8888:8888" # 端口映射
volumes:
- ./conf:/coze-loop/conf # 配置文件挂载
environment: # 环境变量
COZE_LOOP_REDIS_DOMAIN: "coze-loop-redis"
depends_on: # 依赖的服务
redis:
condition: service_healthy
💡 小练习:尝试画出服务依赖图,理解启动顺序。
步骤 3:学会查看日志(30 分钟)
# 查看所有容器状态
docker ps -a
# 查看特定容器的日志
docker logs coze-loop-app
# 实时跟踪日志(Ctrl+C 退出)
docker logs coze-loop-app -f
# 查看最近 100 行日志
docker logs coze-loop-app --tail 100
# 查看带时间戳的日志
docker logs coze-loop-app -t
💡 小练习:故意停止 Redis 容器,观察 app 容器的日志输出。
# 停止 Redis
docker stop coze-loop-redis
# 查看 app 日志,应该看到连接失败的错误
docker logs coze-loop-app -f
# 重启 Redis
docker start coze-loop-redis
步骤 4:配置 IDE(1 小时)
VS Code 配置(推荐):
安装必要的扩展:
- Go (官方)
- Docker
- ESLint
- Prettier
- GitLens
打开项目:
# 用 VS Code 打开项目
code .
- 配置 Go 开发环境:
# 在 VS Code 中打开 backend 目录
cd backend
# 安装 Go 工具(VS Code 会自动提示)
# 点击右下角的 "Install All"
- 配置断点调试:
创建 .vscode/launch.json
:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Coze Loop",
"type": "go",
"request": "launch",
"mode": "debug",
"program": "${workspaceFolder}/backend/cmd",
"env": {
"COZE_LOOP_REDIS_DOMAIN": "localhost",
"COZE_LOOP_REDIS_PORT": "6379",
"COZE_LOOP_MYSQL_DOMAIN": "localhost",
"COZE_LOOP_MYSQL_PORT": "3306",
"COZE_LOOP_MYSQL_USER": "root",
"COZE_LOOP_MYSQL_PASSWORD": "coze_loop",
"COZE_LOOP_MYSQL_DATABASE": "coze_loop"
},
"args": []
}
]
}
- 尝试打断点:
- 打开
backend/cmd/main.go
- 在
main()
函数的第一行设置断点(点击行号左侧) - 按 F5 启动调试
- 程序会在断点处暂停
💡 小练习:在断点处使用调试控制台查看变量值。
步骤 5:修改配置并重启(30 分钟)
尝试修改一些配置,观察效果:
练习 A:修改日志级别
# 编辑配置文件
vim backend/conf/infrastructure.yaml
# 修改日志级别
infra:
log_level: "debug" # 改为 debug 级别
# 重启 app 容器
docker restart coze-loop-app
# 查看日志,应该看到更详细的调试信息
docker logs coze-loop-app -f
练习 B:修改前端端口
# 编辑 .env 文件
vim release/deployment/docker-compose/.env
# 修改前端端口
COZE_LOOP_NGINX_PORT=18082
# 重启所有服务
make compose-down
make compose-up
# 访问新端口
open http://localhost:18082
阶段一完成标志:
- ✅ 能独立启动和停止项目
- ✅ 能查看和理解容器日志
- ✅ IDE 配置完成,能打断点调试
- ✅ 能修改配置并观察效果
🌿 阶段二:核心流程理解(3-5 天)
目标:追踪一个完整业务流程,画出自己的流程图 🎯
学习任务清单:
- [ ] 📖 理解后端的 DDD 分层架构
- [ ] 🔍 追踪"创建 Prompt"的完整流程
- [ ] 🎨 绘制流程图和时序图
- [ ] 💻 阅读核心代码并添加注释
- [ ] 🧪 运行单元测试
详细步骤:
步骤 1:理解 DDD 分层架构(2 小时)
阅读本文档的"第一部分 → 1. 项目架构概览",重点理解:
- Application 层的职责
- Domain 层的职责
- Infra 层的职责
然后选择一个模块深入研究,推荐从最简单的 foundation
模块开始:
cd backend/modules/foundation
# 查看目录结构
tree -L 2
# 输出:
# ├── application # 用例层
# │ ├── space.go
# │ └── user.go
# ├── domain # 领域层
# │ ├── user/
# │ └── file/
# └── infra # 基础设施层
# └── repo/
步骤 2:追踪"创建 Prompt"流程(4 小时)
这是一个完整的业务流程,涉及多个层次。让我们一步步追踪:
2.1 找到 HTTP 入口
# 搜索 CreatePrompt 相关代码
cd backend
grep -r "CreatePrompt" --include="*.go"
# 找到 API Handler
vim api/handler/coze/loop/apis/prompt_manage_service.go
阅读 CreatePrompt
方法:
func (h *APIHandler) CreatePrompt(ctx context.Context, req *loop.CreatePromptReq) (*loop.CreatePromptResp, error) {
// 💡 这里是入口!理解参数校验、权限检查等
}
2.2 追踪到 Application 层
vim modules/prompt/application/manage.go
找到 Create
方法:
func (app *ManageApplication) Create(ctx context.Context, param *CreatePromptParam) (*entity.Prompt, error) {
// 💡 这里编排了整个创建流程
// 1. 参数校验
// 2. 调用 Domain Service
// 3. 发布事件(如果需要)
}
2.3 追踪到 Domain 层
vim modules/prompt/domain/service/prompt_service.go
找到核心业务逻辑:
func (s *PromptService) Create(ctx context.Context, prompt *entity.Prompt) error {
// 💡 这里是核心业务规则
// 1. 生成 ID
// 2. 校验业务规则
// 3. 调用 Repo 保存
}
2.4 追踪到 Infra 层
vim modules/prompt/infra/repo/prompt_repo.go
找到数据库操作:
func (r *PromptRepo) Create(ctx context.Context, prompt *entity.Prompt) error {
// 💡 这里是技术实现
// 使用 GORM 保存到数据库
}
步骤 3:绘制流程图(1 小时)
使用你喜欢的工具绘制流程图:
- draw.io(免费)
- Excalidraw(手绘风格)
- Mermaid(Markdown 语法)
示例:创建 Prompt 流程图
sequenceDiagram
participant User as 👤 用户
participant API as 🌐 API Handler
participant App as 📦 Application
participant Domain as 🧠 Domain Service
participant Repo as 💾 Repository
participant DB as 🗄️ Database
User->>API: POST /api/prompt/create
API->>API: 1. 参数校验
API->>API: 2. 权限检查
API->>App: 3. 调用 Application.Create
App->>Domain: 4. 调用 Domain.Create
Domain->>Domain: 5. 生成 ID
Domain->>Domain: 6. 业务规则校验
Domain->>Repo: 7. 调用 Repo.Create
Repo->>DB: 8. INSERT INTO prompts
DB-->>Repo: 9. 返回成功
Repo-->>Domain: 10. 返回成功
Domain-->>App: 11. 返回 Prompt 实体
App-->>API: 12. 返回 Prompt 实体
API->>API: 13. 转换为 VO
API-->>User: 14. 返回 JSON 响应
💡 小练习:对比你画的图和本文档的图,看看有什么不同。
步骤 4:阅读核心代码并添加注释(3 小时)
选择一个你感兴趣的功能,深入阅读代码并添加中文注释:
// CreatePrompt 创建一个新的 Prompt
// 流程:
// 1. 参数校验(必填字段、格式校验)
// 2. 权限检查(用户是否有权限创建)
// 3. 调用 Application 层创建
// 4. 返回结果(转换为 VO)
func (h *APIHandler) CreatePrompt(ctx context.Context, req *loop.CreatePromptReq) (*loop.CreatePromptResp, error) {
// 参数校验
if req.Name == "" {
return nil, errorx.New(errno.InvalidParam, "name is required")
}
// 调用 Application 层
prompt, err := h.PromptApp.Create(ctx, &application.CreatePromptParam{
Name: req.Name,
Content: req.Content,
})
if err != nil {
return nil, err
}
// 转换为 VO(View Object)返回给前端
return &loop.CreatePromptResp{
Prompt: convertor.ToPromptVO(prompt),
}, nil
}
💡 小练习:为 3-5 个关键方法添加详细注释。
步骤 5:运行单元测试(1 小时)
# 进入后端目录
cd backend
# 运行所有测试
go test ./...
# 运行特定模块的测试
go test ./modules/prompt/...
# 运行单个测试文件
go test ./modules/prompt/application/manage_test.go
# 运行并显示详细输出
go test -v ./modules/prompt/...
# 运行并显示覆盖率
go test -cover ./modules/prompt/...
阅读测试代码,理解如何测试:
vim modules/prompt/application/manage_test.go
示例测试代码:
func TestManageApplication_Create(t *testing.T) {
// 1. 准备 Mock 对象
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockRepo := mock_repo.NewMockIPromptRepo(ctrl)
mockIDGen := mock_idgen.NewMockIIDGenerator(ctrl)
// 2. 设置 Mock 期望
mockIDGen.EXPECT().
GenerateID(gomock.Any()).
Return(int64(123456), nil)
mockRepo.EXPECT().
Create(gomock.Any(), gomock.Any()).
Return(nil)
// 3. 创建被测对象
app := NewManageApplication(mockRepo, mockIDGen)
// 4. 执行测试
prompt, err := app.Create(context.Background(), &CreatePromptParam{
Name: "test prompt",
})
// 5. 验证结果
assert.NoError(t, err)
assert.NotNil(t, prompt)
assert.Equal(t, int64(123456), prompt.ID)
}
💡 小练习:尝试编写一个简单的测试用例。
阶段二完成标志:
- ✅ 能说清楚 Application、Domain、Infra 三层的职责
- ✅ 能追踪一个完整的业务流程(从 HTTP 请求到数据库)
- ✅ 画出了自己的流程图或时序图
- ✅ 理解了核心代码的逻辑
- ✅ 能运行和理解单元测试
🌳 阶段三:模块深入和定制开发(1-2 周)
目标:能修改或扩展一个现有功能 🎯
学习任务清单:
- [ ] 🎨 选择一个感兴趣的模块深入研究
- [ ] 💻 实现一个小功能(如新增字段、新增接口)
- [ ] 🧪 为你的代码编写单元测试
- [ ] 📝 编写简单的设计文档
- [ ] 🔍 学习前端代码(可选)
详细步骤:
步骤 1:选择研究方向(1 小时)
根据你的兴趣选择一个模块深入研究:
方向 A:Prompt 管理模块 📝
- 适合:对 AI、LLM 感兴趣的开发者
- 核心代码:
backend/modules/prompt
- 学习重点:Prompt 版本管理、LLM 调用、流式输出
方向 B:评测模块 🧪
- 适合:对测试、质量保障感兴趣的开发者
- 核心代码:
backend/modules/evaluation
- 学习重点:评估器设计、并发任务执行、结果分析
方向 C:观测模块 📊
- 适合:对监控、性能分析感兴趣的开发者
- 核心代码:
backend/modules/observability
- 学习重点:Trace 采集、ClickHouse 查询、数据可视化
方向 D:数据集管理模块 📊
- 适合:对数据处理、ETL 感兴趣的开发者
- 核心代码:
backend/modules/data
- 学习重点:文件解析、Schema 管理、数据导入导出
步骤 2:实现一个小功能(5-7 天)
让我们以"为 Prompt 添加标签功能"为例:
2.1 设计阶段(1 天)
编写简单的设计文档:
# Prompt 标签功能设计
## 需求
- 用户可以为 Prompt 添加多个标签
- 可以通过标签筛选 Prompt
- 标签支持增删改查
## 数据模型
- 新增 `prompt_tags` 表:
- id (bigint)
- prompt_id (bigint)
- tag_name (varchar)
## API 设计
- POST /api/prompt/{id}/tags # 添加标签
- DELETE /api/prompt/{id}/tags/{tag} # 删除标签
- GET /api/prompt/tags # 获取所有标签
## 实现步骤
1. 修改数据库 Schema
2. 添加 Domain Entity
3. 实现 Repository
4. 实现 Domain Service
5. 实现 Application Service
6. 添加 API Handler
7. 编写单元测试
2.2 修改数据库 Schema(1 小时)
# 创建迁移文件
vim release/deployment/docker-compose/sql/001_add_prompt_tags.sql
CREATE TABLE IF NOT EXISTS `prompt_tags` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`prompt_id` bigint(20) NOT NULL,
`tag_name` varchar(64) NOT NULL,
`created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_prompt_id` (`prompt_id`),
KEY `idx_tag_name` (`tag_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
2.3 添加 Domain Entity(2 小时)
vim backend/modules/prompt/domain/entity/prompt_tag.go
package entity
import "time"
// PromptTag Prompt 标签实体
type PromptTag struct {
ID int64
PromptID int64
TagName string
CreatedAt time.Time
}
// NewPromptTag 创建新标签
func NewPromptTag(promptID int64, tagName string) *PromptTag {
return &PromptTag{
PromptID: promptID,
TagName: tagName,
CreatedAt: time.Now(),
}
}
// Validate 校验标签
func (t *PromptTag) Validate() error {
if t.PromptID <= 0 {
return errors.New("invalid prompt_id")
}
if t.TagName == "" {
return errors.New("tag_name is required")
}
if len(t.TagName) > 64 {
return errors.New("tag_name too long")
}
return nil
}
2.4 实现 Repository(3 小时)
定义接口:
vim backend/modules/prompt/domain/repo/prompt_tag_repo.go
package repo
import (
"context"
"github.com/coze-dev/coze-loop/backend/modules/prompt/domain/entity"
)
// IPromptTagRepo Prompt 标签仓储接口
type IPromptTagRepo interface {
Create(ctx context.Context, tag *entity.PromptTag) error
Delete(ctx context.Context, promptID int64, tagName string) error
GetByPromptID(ctx context.Context, promptID int64) ([]*entity.PromptTag, error)
GetAll(ctx context.Context) ([]string, error)
}
实现接口:
vim backend/modules/prompt/infra/repo/prompt_tag_repo.go
package repo
import (
"context"
"github.com/coze-dev/coze-loop/backend/infra/db"
domainRepo "github.com/coze-dev/coze-loop/backend/modules/prompt/domain/repo"
"github.com/coze-dev/coze-loop/backend/modules/prompt/domain/entity"
)
type PromptTagRepo struct {
db db.Provider
}
func NewPromptTagRepo(db db.Provider) domainRepo.IPromptTagRepo {
return &PromptTagRepo{
db: db}
}
func (r *PromptTagRepo) Create(ctx context.Context, tag *entity.PromptTag) error {
po := &po.PromptTag{
PromptID: tag.PromptID,
TagName: tag.TagName,
CreatedAt: tag.CreatedAt,
}
return r.db.WithContext(ctx).Create(po).Error
}
// ... 其他方法实现
2.5 实现 Domain Service(2 小时)
vim backend/modules/prompt/domain/service/prompt_tag_service.go
package service
import (
"context"
"github.com/coze-dev/coze-loop/backend/modules/prompt/domain/entity"
"github.com/coze-dev/coze-loop/backend/modules/prompt/domain/repo"
)
type PromptTagService struct {
repo repo.IPromptTagRepo
}
func NewPromptTagService(repo repo.IPromptTagRepo) *PromptTagService {
return &PromptTagService{
repo: repo}
}
func (s *PromptTagService) AddTag(ctx context.Context, promptID int64, tagName string) error {
// 1. 创建标签实体
tag := entity.NewPromptTag(promptID, tagName)
// 2. 校验
if err := tag.Validate(); err != nil {
return err
}
// 3. 保存
return s.repo.Create(ctx, tag)
}
// ... 其他方法
2.6 实现 Application Service(2 小时)
vim backend/modules/prompt/application/tag_app.go
package application
import (
"context"
"github.com/coze-dev/coze-loop/backend/modules/prompt/domain/service"
)
type TagApplication struct {
tagService *service.PromptTagService
}
func NewTagApplication(tagService *service.PromptTagService) *TagApplication {
return &TagApplication{
tagService: tagService}
}
func (app *TagApplication) AddTag(ctx context.Context, promptID int64, tagName string) error {
return app.tagService.AddTag(ctx, promptID, tagName)
}
// ... 其他方法
2.7 添加 API Handler(2 小时)
vim backend/api/handler/coze/loop/apis/prompt_tag_service.go
package apis
import (
"context"
"github.com/coze-dev/coze-loop/backend/kitex_gen/coze/loop"
)
func (h *APIHandler) AddPromptTag(ctx context.Context, req *loop.AddPromptTagReq) (*loop.AddPromptTagResp, error) {
// 1. 参数校验
if req.PromptId <= 0 {
return nil, errorx.New(errno.InvalidParam, "invalid prompt_id")
}
if req.TagName == "" {
return nil, errorx.New(errno.InvalidParam, "tag_name is required")
}
// 2. 调用 Application 层
err := h.PromptTagApp.AddTag(ctx, req.PromptId, req.TagName)
if err != nil {
return nil, err
}
// 3. 返回结果
return &loop.AddPromptTagResp{
}, nil
}
2.8 编写单元测试(1 天)
vim backend/modules/prompt/application/tag_app_test.go
package application_test
import (
"context"
"testing"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/coze-dev/coze-loop/backend/modules/prompt/application"
mock_repo "github.com/coze-dev/coze-loop/backend/modules/prompt/domain/repo/mocks"
)
func TestTagApplication_AddTag(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
mockRepo := mock_repo.NewMockIPromptTagRepo(ctrl)
mockRepo.EXPECT().
Create(gomock.Any(), gomock.Any()).
Return(nil)
tagService := service.NewPromptTagService(mockRepo)
app := application.NewTagApplication(tagService)
err := app.AddTag(context.Background(), 123, "test-tag")
assert.NoError(t, err)
}
运行测试:
go test ./modules/prompt/application -v -run TestTagApplication_AddTag
步骤 3:前端代码学习(可选,2-3 天)
如果你对前端感兴趣,可以学习如何调用新增的 API:
cd frontend/packages/cozeloop/prompt-pages/src/components
# 创建新组件
vim PromptTagManager.tsx
import React, {
useState } from 'react';
import {
addPromptTag, deletePromptTag } from '@cozeloop/api-schema';
export const PromptTagManager: React.FC<{
promptId: number }> = ({
promptId }) => {
const [tagName, setTagName] = useState('');
const [tags, setTags] = useState<string[]>([]);
const handleAddTag = async () => {
try {
await addPromptTag({
promptId, tagName });
setTags([...tags, tagName]);
setTagName('');
} catch (error) {
console.error('Failed to add tag:', error);
}
};
return (
<div>
<input
value={
tagName}
onChange={
(e) => setTagName(e.target.value)}
placeholder="输入标签名"
/>
<button onClick={
handleAddTag}>添加标签</button>
<div>
{
tags.map(tag => (
<span key={
tag}>{
tag}</span>
))}
</div>
</div>
);
};
阶段三完成标志:
- ✅ 实现了一个完整的新功能
- ✅ 代码遵循项目的架构规范
- ✅ 编写了单元测试,覆盖率 > 80%
- ✅ 功能在本地环境运行正常
- ✅ 编写了简单的设计文档
🚀 阶段四:架构理解和贡献指南(2 周+)
目标:能理解技术选型原因,并尝试修复一个简单 issue 🎯
学习任务清单:
- [ ] 📖 深入理解 CloudWeGo 技术栈
- [ ] 🔍 研究 Eino 框架的设计
- [ ] 💻 理解 DDD 在项目中的实践
- [ ] 🐛 修复一个 Good First Issue
- [ ] 📝 提交 Pull Request
详细步骤:
步骤 1:深入理解技术选型(3-5 天)
1.1 为什么选择 Go?
阅读资料:
- Go at Google: Language Design in the Service of Software Engineering
- Why Go is Perfect for Microservices
思考问题:
- Go 的并发模型如何帮助 Coze Loop 处理大量请求?
- 为什么不选择 Java 或 Node.js?
1.2 为什么选择 CloudWeGo?
阅读官方文档:
对比研究:
- Kitex vs gRPC
- Hertz vs Gin/Echo
1.3 为什么选择 DDD?
阅读资料:
- 领域驱动设计在字节跳动的实践(搜索相关文章)
思考问题:
- 如果不用 DDD,代码会变成什么样?
- DDD 的优势和劣势是什么?
步骤 2:研究 Eino 框架(2-3 天)
Eino 是字节跳动开源的 LLM 统一接入框架,理解它的设计对深入 Coze Loop 很重要。
# 克隆 Eino 仓库
git clone https://github.com/cloudwego/eino.git
cd eino
# 阅读文档
cat README.md
关键概念:
- ChatModel 接口:统一的 LLM 调用接口
- Provider 模式:支持多个 LLM 提供商
- Middleware:拦截器,用于日志、限流等
在 Coze Loop 中找到 Eino 的使用:
cd backend
grep -r "eino" --include="*.go" | head -20
阅读 LLM Service 的实现:
vim modules/llm/domain/service/llm_service.go
步骤 3:寻找贡献机会(1-2 天)
3.1 阅读贡献指南
cat CONTRIBUTING.md
cat CODE_OF_CONDUCT.md
3.2 寻找 Good First Issue
访问 GitHub Issues:https://github.com/coze-dev/coze-loop/issues
筛选条件:
- Label:
good first issue
- Status:
open
- 最好是你感兴趣的模块
3.3 理解 Issue
选择一个 Issue 后:
- 仔细阅读 Issue 描述
- 复现问题(如果是 Bug)
- 查看相关代码
- 思考解决方案
步骤 4:提交 Pull Request(3-5 天)
4.1 Fork 项目
在 GitHub 上点击 Fork 按钮
4.2 创建分支
# 添加上游仓库
git remote add upstream https://github.com/coze-dev/coze-loop.git
# 同步最新代码
git fetch upstream
git checkout main
git merge upstream/main
# 创建特性分支
git checkout -b fix/issue-123
4.3 实现修复
按照项目的编码规范实现修复:
- 遵循 DDD 架构
- 编写单元测试
- 确保所有测试通过
# 运行测试
go test ./...
# 运行 linter
golangci-lint run
4.4 提交代码
# 添加修改
git add .
# 提交(遵循 Conventional Commits 规范)
git commit -m "fix: resolve issue #123 - xxx"
# 推送到你的 Fork
git push origin fix/issue-123
4.5 创建 Pull Request
在 GitHub 上创建 PR,描述清楚:
- 解决了什么问题
- 如何解决的
- 测试情况
4.6 代码审查
- 回复 Reviewer 的评论
- 及时修改代码
- 保持耐心和礼貌
阶段四完成标志:
- ✅ 能说清楚 Coze Loop 的技术选型原因
- ✅ 理解 Eino 框架的设计
- ✅ 成功提交了一个 Pull Request
- ✅ PR 被 Merge 或得到了有价值的反馈
- ✅ 成为 Coze Loop 社区的贡献者 🎉
3. 学习路径流程图 🗺️
为了让学习路径更清晰,我画了一个完整的流程图:
flowchart TD
Start([开始学习 Coze Loop]) --> Stage1{阶段一<br/>环境搭建}
Stage1 -->|1-2 天| Task1_1[克隆代码]
Task1_1 --> Task1_2[配置 LLM]
Task1_2 --> Task1_3[启动服务]
Task1_3 --> Task1_4[配置 IDE]
Task1_4 --> Check1{能否成功<br/>运行项目?}
Check1 -->|否| Debug1[查看日志<br/>排查问题]
Debug1 --> Task1_3
Check1 -->|是| Stage2{阶段二<br/>核心流程理解}
Stage2 -->|3-5 天| Task2_1[学习 DDD 架构]
Task2_1 --> Task2_2[追踪创建 Prompt 流程]
Task2_2 --> Task2_3[绘制流程图]
Task2_3 --> Task2_4[阅读核心代码]
Task2_4 --> Task2_5[运行单元测试]
Task2_5 --> Check2{能否画出<br/>完整流程图?}
Check2 -->|否| Task2_2
Check2 -->|是| Stage3{阶段三<br/>模块深入}
Stage3 -->|1-2 周| Task3_1[选择研究方向]
Task3_1 --> Task3_2[设计新功能]
Task3_2 --> Task3_3[实现功能]
Task3_3 --> Task3_4[编写测试]
Task3_4 --> Task3_5[本地验证]
Task3_5 --> Check3{功能是否<br/>正常运行?}
Check3 -->|否| Task3_3
Check3 -->|是| Stage4{阶段四<br/>贡献代码}
Stage4 -->|2 周+| Task4_1[深入技术栈]
Task4_1 --> Task4_2[寻找 Issue]
Task4_2 --> Task4_3[实现修复]
Task4_3 --> Task4_4[提交 PR]
Task4_4 --> Task4_5[代码审查]
Task4_5 --> Check4{PR 是否<br/>被接受?}
Check4 -->|需要修改| Task4_3
Check4 -->|是| Success([成为 Contributor 🎉])
style Start fill:#ff6b6b,stroke:#333,stroke-width:2px
style Success fill:#51cf66,stroke:#333,stroke-width:2px
style Stage1 fill:#339af0,color:#fff
style Stage2 fill:#339af0,color:#fff
style Stage3 fill:#339af0,color:#fff
style Stage4 fill:#339af0,color:#fff
好了,第三部分就到这里!你现在有了一个清晰的学习路径,按照这个路径一步步来,你会发现学习 Coze Loop 并没有那么难 😊
接下来让我们进入第四部分——实践建议和进阶指导,我会分享一些实用的技巧和常见陷阱 💡
第四部分:实践建议和进阶指导(从会用到精通)💡
本部分目标:分享实用的调试技巧、扩展练习建议和参与贡献的途径,帮助你从"会用"走向"精通"。
💡 心法提示:实践是最好的老师!不要害怕犯错,每个 Bug 都是一次学习机会。
1. 调试技巧和常见陷阱 🐛
🔧 调试技巧速查表
后端调试:
技巧 1:使用 Go Delve 调试器
# 安装 Delve
go install github.com/go-delve/delve/cmd/dlv@latest
# 调试运行
dlv debug ./backend/cmd -- [args]
# 远程调试(连接到 Docker 容器)
dlv attach <pid> --headless --listen=:2345 --api-version=2
在 VS Code 中配置远程调试:
{
"version": "0.2.0",
"configurations": [
{
"name": "Remote Debug",
"type": "go",
"request": "attach",
"mode": "remote",
"remotePath": "/coze-loop",
"port": 2345,
"host": "localhost"
}
]
}
技巧 2:结构化日志查询
Coze Loop 使用结构化日志,可以使用 jq
工具进行高效查询:
# 查看所有错误日志
docker logs coze-loop-app 2>&1 | grep '"level":"error"'
# 使用 jq 格式化输出
docker logs coze-loop-app 2>&1 | grep '^{' | jq '.
# 查询特定用户的请求
docker logs coze-loop-app 2>&1 | grep '^{
' | jq 'select(.user_id == "123")'
# 统计错误类型
docker logs coze-loop-app 2>&1 | grep '^{
' | jq -r '.error_code' | sort | uniq -c
技巧 3:性能分析(pprof)
# 开启 pprof(在代码中添加)
import _ "net/http/pprof"
# 访问 pprof 端点
# CPU 分析
curl http://localhost:8888/debug/pprof/profile?seconds=30 > cpu.prof
# 内存分析
curl http://localhost:8888/debug/pprof/heap > heap.prof
# 分析结果
go tool pprof -http=:8080 cpu.prof
技巧 4:数据库查询监控
# 进入 MySQL 容器
docker exec -it coze-loop-mysql mysql -u root -p
# 查看慢查询
SHOW VARIABLES LIKE 'slow_query%';
SHOW FULL PROCESSLIST;
# 分析表结构
EXPLAIN SELECT * FROM prompts WHERE user_id = 123;
前端调试:
技巧 1:React DevTools
安装 React DevTools 浏览器扩展,可以:
- 查看组件树
- 查看组件 Props 和 State
- 分析组件渲染性能
技巧 2:Redux DevTools(如果使用 Zustand)
虽然 Coze Loop 使用 Zustand,但可以通过中间件连接到 Redux DevTools:
import {
devtools } from 'zustand/middleware';
export const useStore = create(
devtools(
(set) => ({
// your state
}),
{
name: 'CozeLoopStore' }
)
);
技巧 3:网络请求监控
使用浏览器开发者工具的 Network 面板:
- Filter by
XHR
查看 API 请求 - 查看请求头、响应体
- 分析请求耗时
⚠️ 常见陷阱和解决方案
陷阱 1:Goroutine 泄露
现象:系统运行一段时间后内存持续增长
原因:启动的 Goroutine 没有正确退出
示例代码(有问题):
func (s *Service) Start(ctx context.Context) {
go func() {
// 这个 goroutine 没有监听 ctx.Done()
for {
s.doWork()
time.Sleep(time.Second)
}
}()
}
修复方案:
func (s *Service) Start(ctx context.Context) {
go func() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
// 正确处理 context 取消
return
case <-ticker.C:
s.doWork()
}
}
}()
}
陷阱 2:数据库连接泄露
现象:一段时间后报错 too many connections
原因:没有正确释放数据库连接
示例代码(有问题):
func (r *Repo) GetByID(ctx context.Context, id int64) (*entity.Prompt, error) {
rows, err := r.db.Query("SELECT * FROM prompts WHERE id = ?", id)
// 忘记 defer rows.Close()
// ...
}
修复方案:
func (r *Repo) GetByID(ctx context.Context, id int64) (*entity.Prompt, error) {
rows, err := r.db.Query("SELECT * FROM prompts WHERE id = ?", id)
if err != nil {
return nil, err
}
defer rows.Close() // 确保释放连接
// ...
}
更好的方案:使用 GORM
func (r *Repo) GetByID(ctx context.Context, id int64) (*entity.Prompt, error) {
var po po.Prompt
err := r.db.WithContext(ctx).First(&po, id).Error
// GORM 会自动管理连接
return toDomain(&po), err
}
陷阱 3:Context 传递中断
现象:请求超时没有正确传播到下游
原因:创建了新的 context.Background()
而不是传递原有 context
示例代码(有问题):
func (s *Service) ProcessRequest(ctx context.Context) error {
// 错误:创建了新的 context,丢失了超时信息
go s.asyncTask(context.Background())
}
修复方案:
func (s *Service) ProcessRequest(ctx context.Context) error {
// 正确:传递原有 context 或派生 context
go s.asyncTask(ctx)
// 或者
go s.asyncTask(context.WithoutCancel(ctx)) // Go 1.21+
}
陷阱 4:并发读写 Map
现象:程序偶尔崩溃,报错 concurrent map read and map write
原因:多个 Goroutine 同时读写普通 map
示例代码(有问题):
type Cache struct {
data map[string]string
}
func (c *Cache) Get(key string) string {
return c.data[key] // 并发读写会 panic
}
修复方案:
import "sync"
type Cache struct {
mu sync.RWMutex
data map[string]string
}
func (c *Cache) Get(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data[key]
}
// 或者使用 sync.Map
type Cache struct {
data sync.Map
}
func (c *Cache) Get(key string) (string, bool) {
val, ok := c.data.Load(key)
if !ok {
return "", false
}
return val.(string), true
}
陷阱 5:React 组件无限渲染
现象:页面卡死,控制台报错 Maximum update depth exceeded
原因:在 useEffect
中修改了依赖项,导致无限循环
示例代码(有问题):
function MyComponent({
data }) {
const [list, setList] = useState([]);
useEffect(() => {
// 错误:每次渲染都会创建新的对象
setList(data.map(item => ({
...item })));
}, [data, list]); // list 作为依赖会导致无限循环
}
修复方案:
function MyComponent({
data }) {
const [list, setList] = useState([]);
useEffect(() => {
setList(data.map(item => ({
...item })));
}, [data]); // 只依赖 data
// 或者使用 useMemo
const list = useMemo(() => {
return data.map(item => ({
...item }));
}, [data]);
}
2. 扩展练习建议 📝
以下是一些从易到难的练习题,帮助你巩固所学知识:
🌱 入门级练习(难度:⭐)
练习 1:修改欢迎消息
- 目标:修改登录后的欢迎消息
- 涉及文件:前端组件
- 预计时间:30 分钟
- 学习要点:熟悉前端代码结构
练习 2:添加日志
- 目标:在 Prompt 创建时添加详细日志
- 涉及文件:
backend/modules/prompt/domain/service/prompt_service.go
- 预计时间:1 小时
- 学习要点:理解日志记录规范
练习 3:修改配置项
- 目标:添加一个新的配置项,控制日志级别
- 涉及文件:
backend/conf/infrastructure.yaml
- 预计时间:1 小时
- 学习要点:配置管理和环境变量
🌿 进阶级练习(难度:⭐⭐)
练习 4:新增 API 接口
- 目标:为 Prompt 添加"归档"功能
- 涉及文件:
backend/modules/prompt/domain/entity/prompt.go
(添加 Archived 字段)backend/modules/prompt/domain/service/prompt_service.go
(添加 Archive 方法)backend/api/handler/coze/loop/apis/prompt_manage_service.go
(添加 API)
- 预计时间:半天
- 学习要点:完整的功能开发流程
练习 5:添加缓存
- 目标:为 Prompt 查询添加 Redis 缓存
- 涉及文件:
backend/modules/prompt/infra/repo/prompt_repo.go
- 预计时间:1 天
- 学习要点:缓存策略、缓存失效
练习 6:导出功能
- 目标:支持将 Prompt 列表导出为 CSV
- 涉及文件:
- 后端:添加导出接口
- 前端:添加导出按钮
- 预计时间:1-2 天
- 学习要点:文件生成、前后端协作
🌳 高级练习(难度:⭐⭐⭐)
练习 7:实现定时任务
- 目标:实现一个定时任务,每天清理过期的 Trace 数据
- 涉及技术:
- 使用
cron
库 - 批量删除 ClickHouse 数据
- 添加监控指标
- 使用
- 预计时间:2-3 天
- 学习要点:定时任务、大数据清理
练习 8:优化查询性能
- 目标:优化 Prompt 列表查询,支持 10w+ 数据
- 涉及技术:
- 添加索引
- 分页优化(游标分页)
- 查询缓存
- 预计时间:3-5 天
- 学习要点:数据库优化、性能调优
练习 9:实现权限控制
- 目标:实现基于角色的权限控制(RBAC)
- 涉及技术:
- 设计权限模型
- 实现权限中间件
- 前端权限判断
- 预计时间:1-2 周
- 学习要点:权限系统设计、中间件模式
3. 参与贡献的途径 🤝
📍 社区位置
官方渠道:
GitHub 仓库:https://github.com/coze-dev/coze-loop
- 主要用于:代码贡献、Bug 报告、功能建议
Discord 社区:Coze Community
- 主要用于:技术讨论、问题求助、社区交流
Telegram 群组:Coze
- 主要用于:即时沟通、快速问答
飞书群聊(国内用户):扫描 README 中的二维码加入
- 主要用于:中文技术交流
🐛 如何寻找 Good First Issue
步骤 1:访问 Issues 页面
https://github.com/coze-dev/coze-loop/issues
步骤 2:使用标签筛选
点击 Labels,选择:
good first issue
:适合新手的问题help wanted
:需要社区帮助的问题documentation
:文档类问题(最容易上手)
步骤 3:评估 Issue
选择 Issue 时考虑:
- ✅ 问题描述清晰
- ✅ 有复现步骤(如果是 Bug)
- ✅ 涉及的模块你比较熟悉
- ✅ 预计工作量在 1-3 天
步骤 4:认领 Issue
在 Issue 下评论:
Hi, I'd like to work on this issue. Could you assign it to me?
I plan to:
1. [你的解决方案简述]
2. [预计时间]
Thanks!
📝 代码规范要求
Coze Loop 的代码规范主要参考《阿里巴巴 Go 开发手册》,以下是关键要点:
命名规范:
// ✅ 正确
type UserService struct {
}
func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) {
}
// ❌ 错误
type userservice struct {
} // 类型名应该大写
func (s *UserService) get_user(ctx context.Context, id int64) (*User, error) {
} // 不应使用下划线
注释规范:
// ✅ 正确:导出的函数/类型必须有注释
// UserService 提供用户相关服务
type UserService struct {
}
// GetUser 根据 ID 获取用户
func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) {
}
// ❌ 错误:缺少注释
type UserService struct {
}
func (s *UserService) GetUser(ctx context.Context, id int64) (*User, error) {
}
错误处理:
// ✅ 正确:包装错误并添加上下文
func (s *Service) DoSomething(ctx context.Context) error {
if err := s.repo.Save(ctx); err != nil {
return errorx.Wrap(err, "failed to save data")
}
return nil
}
// ❌ 错误:直接返回错误,丢失上下文
func (s *Service) DoSomething(ctx context.Context) error {
return s.repo.Save(ctx)
}
测试规范:
// ✅ 正确:使用表驱动测试
func TestUserService_GetUser(t *testing.T) {
tests := []struct {
name string
userID int64
want *User
wantErr bool
}{
{
name: "existing user",
userID: 123,
want: &User{
ID: 123, Name: "Alice"},
wantErr: false,
},
{
name: "non-existing user",
userID: 999,
want: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// test logic
})
}
}
🚀 提交流程
1. 提交前检查清单
- [ ] 代码通过所有测试:
go test ./...
- [ ] 代码符合规范:
golangci-lint run
- [ ] 添加了必要的单元测试
- [ ] 更新了相关文档(如果需要)
- [ ] Commit 消息符合 Conventional Commits 规范
2. Commit 消息规范
格式:<type>(<scope>): <subject>
类型(type):
feat
:新功能fix
:Bug 修复docs
:文档更新style
:代码格式(不影响代码运行)refactor
:重构test
:测试相关chore
:构建/工具相关
示例:
# 新功能
git commit -m "feat(prompt): add archive functionality"
# Bug 修复
git commit -m "fix(evaluation): resolve memory leak in concurrent execution"
# 文档更新
git commit -m "docs(readme): update quick start guide"
3. PR 描述模板
## Description
[简要描述这个 PR 的目的]
## Motivation and Context
[为什么需要这个改动?解决了什么问题?]
## Type of Change
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## How Has This Been Tested?
[描述测试方法]
- [ ] Unit tests
- [ ] Integration tests
- [ ] Manual tests
## Checklist
- [ ] My code follows the code style of this project
- [ ] I have added tests to cover my changes
- [ ] All new and existing tests passed
- [ ] I have updated the documentation accordingly
第五部分:技术栈学习指引(你的知识地图)🌐
本部分目标:为你提供系统化的技术栈学习路径,构建完整的知识体系。
💡 心法提示:学习技术栈不要贪多求全,先掌握核心,再逐步拓展。
1. 官方文档定位(学习的基石)📖
🎯 后端技术栈文档
Go 语言核心:
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
Go 语言 | golang.org | Tour of Go、Effective Go | ⭐⭐⭐⭐⭐ |
Go Modules | Go Modules | 依赖管理、版本控制 | ⭐⭐⭐⭐ |
Go Testing | Testing Package | 单元测试、基准测试 | ⭐⭐⭐⭐ |
CloudWeGo 技术栈:
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
Kitex | Kitex Docs | Getting Started、Thrift IDL | ⭐⭐⭐⭐⭐ |
Hertz | Hertz Docs | 路由、中间件、请求处理 | ⭐⭐⭐⭐ |
Netpoll | Netpoll Docs | 网络模型(可选) | ⭐⭐ |
Eino 框架:
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
Eino | Eino GitHub | README、Examples | ⭐⭐⭐⭐⭐ |
数据库相关:
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
MySQL | MySQL Docs | 事务、索引、查询优化 | ⭐⭐⭐⭐⭐ |
GORM | GORM Docs | CRUD、关联、Hooks | ⭐⭐⭐⭐ |
Redis | Redis Docs | 数据类型、持久化、集群 | ⭐⭐⭐⭐ |
ClickHouse | ClickHouse Docs | 表引擎、查询语法 | ⭐⭐⭐ |
消息队列:
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
RocketMQ | RocketMQ Docs | 快速开始、Go Client | ⭐⭐⭐ |
🎨 前端技术栈文档
技术 | 官方文档 | 重点章节 | 学习优先级 |
---|---|---|---|
React | React Docs | Hooks、Context、Performance | ⭐⭐⭐⭐⭐ |
TypeScript | TypeScript Handbook | 类型系统、泛型、工具类型 | ⭐⭐⭐⭐⭐ |
Rsbuild | Rsbuild Docs | 配置、插件 | ⭐⭐⭐ |
Rush | Rush Docs | Monorepo 管理 | ⭐⭐⭐ |
Zustand | Zustand GitHub | 状态管理 | ⭐⭐⭐⭐ |
React Router | React Router Docs | 路由配置、导航 | ⭐⭐⭐⭐ |
TailwindCSS | Tailwind Docs | 工具类、配置 | ⭐⭐⭐ |
📚 Coze Loop 自身文档
文档 | 链接 | 内容 | 学习优先级 |
---|---|---|---|
快速开始 | Wiki | 部署安装 | ⭐⭐⭐⭐⭐ |
系统架构 | Wiki | 架构设计 | ⭐⭐⭐⭐⭐ |
代码开发与测试 | Wiki | 开发指南 | ⭐⭐⭐⭐ |
模型配置 | Wiki | LLM 配置 | ⭐⭐⭐⭐ |
📖 权威技术书籍
Go 语言:
《Go 程序设计语言》(The Go Programming Language)
- 作者:Alan A. A. Donovan, Brian W. Kernighan
- 适合:Go 初学者
- 重点:语言基础、并发编程
《Go 语言实战》(Go in Action)
- 作者:William Kennedy, Brian Ketelsen, Erik St. Martin
- 适合:有一定基础的开发者
- 重点:实战经验、最佳实践
《Go 语言高级编程》
- 作者:柴树杉、曹春晖
- 适合:进阶开发者
- 重点:CGO、汇编、RPC
架构设计:
《领域驱动设计》(Domain-Driven Design)
- 作者:Eric Evans
- 适合:架构师、技术Lead
- 重点:DDD 核心概念、战略设计、战术设计
《实现领域驱动设计》(Implementing Domain-Driven Design)
- 作者:Vaughn Vernon
- 适合:想实践 DDD 的开发者
- 重点:DDD 实践、代码示例
《微服务架构设计模式》(Microservices Patterns)
- 作者:Chris Richardson
- 适合:微服务架构师
- 重点:微服务拆分、通信模式、数据一致性
代码质量:
《代码整洁之道》(Clean Code)
- 作者:Robert C. Martin
- 适合:所有开发者
- 重点:命名、函数、注释、测试
《重构:改善既有代码的设计》(Refactoring)
- 作者:Martin Fowler
- 适合:有经验的开发者
- 重点:重构手法、代码坏味道
2. 学习路径建议(社区智慧)🛤️
📚 技能学习顺序
阶段一:基础扫盲(2-4 周)
第 1 周:Go 语言基础
├── Day 1-2:基本语法、数据类型
├── Day 3-4:函数、方法、接口
├── Day 5-6:Goroutine、Channel
└── Day 7:Context、错误处理
第 2 周:Web 开发基础
├── Day 1-2:HTTP 协议、RESTful API
├── Day 3-4:Hertz 框架入门
├── Day 5-6:数据库操作(GORM)
└── Day 7:Redis 基础
第 3 周:前端基础
├── Day 1-2:TypeScript 基础
├── Day 3-4:React Hooks
├── Day 5-6:状态管理(Zustand)
└── Day 7:路由和导航
第 4 周:综合实战
├── Day 1-3:跑通 Coze Loop 项目
├── Day 4-5:阅读核心代码
└── Day 6-7:实现一个小功能
阶段二:深入理解(1-2 个月)
DDD 架构(2 周)
├── Week 1:理论学习(阅读《领域驱动设计》前 3 章)
└── Week 2:实践分析(分析 Coze Loop 的 DDD 实现)
微服务架构(2 周)
├── Week 1:CloudWeGo 技术栈(Kitex + Hertz)
└── Week 2:Thrift IDL、RPC 通信
性能优化(2 周)
├── Week 1:Go 性能分析(pprof)
└── Week 2:数据库优化(索引、查询优化)
阶段三:工程化实践(持续)
测试(2 周)
├── Week 1:单元测试(gomock、testify)
└── Week 2:集成测试、表驱动测试
可观测性(2 周)
├── Week 1:日志、指标
└── Week 2:分布式追踪(OpenTelemetry)
DevOps(2 周)
├── Week 1:Docker、Docker Compose
└── Week 2:Kubernetes、Helm
🎯 核心概念优先级
必须掌握(⭐⭐⭐⭐⭐):
Go 并发编程
- Goroutine 和 Channel 的使用
- Context 的传递和取消
- 常见并发模式(Worker Pool、Pipeline)
DDD 分层架构
- Application、Domain、Infra 三层职责
- 依赖倒置原则
- 仓储模式
HTTP API 设计
- RESTful 规范
- 参数校验
- 错误处理
数据库事务
- ACID 特性
- 事务隔离级别
- 锁机制
重要掌握(⭐⭐⭐⭐):
Thrift IDL
- 类型定义
- 服务定义
- 代码生成
Redis 应用
- 缓存策略
- 分布式锁
- Pub/Sub
消息队列
- 发布-订阅模式
- 消息幂等性
- 重试机制
前端状态管理
- Zustand 的使用
- 异步状态处理
- 状态持久化
选修拓展(⭐⭐⭐):
ClickHouse
- 列式存储原理
- 查询优化
- 数据导入
性能优化
- Go 性能分析(pprof)
- 内存优化
- 并发控制
监控告警
- Prometheus + Grafana
- 指标设计
- 告警规则
🏗️ 实践项目推荐
初级项目(熟悉技术栈):
Todo API
- 技术栈:Go + GORM + MySQL
- 学习要点:CRUD、RESTful API
- 预计时间:3-5 天
- 参考:Building a RESTful API with Go
博客系统
- 技术栈:Go + React + MySQL
- 学习要点:前后端分离、用户认证
- 预计时间:1-2 周
- 参考:Go Blog Example
中级项目(理解架构):
URL 短链服务
- 技术栈:Go + Redis + MySQL
- 学习要点:分布式 ID、缓存策略
- 预计时间:1-2 周
- 参考:URL Shortener Go
任务调度系统
- 技术栈:Go + RocketMQ + MySQL
- 学习要点:消息队列、异步处理
- 预计时间:2-3 周
- 参考:Temporal Workflow
高级项目(深入实践):
- Mini Coze Loop
- 技术栈:完整技术栈
- 学习要点:DDD 架构、微服务
- 预计时间:1-2 个月
- 参考:Coze Loop 本身 😊
3. 工具与环境配置指南 🛠️
💻 开发环境搭建
后端开发环境:
# 1. 安装 Go
# macOS
brew install go@1.24
# Linux
wget https://go.dev/dl/go1.24.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.24.0.linux-amd64.tar.gz
# Windows:从官网下载安装包
# 2. 配置环境变量
export GOPATH=$HOME/go
export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin
# 3. 配置 Go Proxy(国内用户)
go env -w GOPROXY=https://goproxy.cn,direct
# 4. 安装开发工具
go install golang.org/x/tools/gopls@latest # LSP
go install github.com/go-delve/delve/cmd/dlv@latest # 调试器
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest # Linter
前端开发环境:
# 1. 安装 Node.js
# macOS
brew install node@18
# Linux (使用 nvm)
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash
nvm install 18
nvm use 18
# Windows:从官网下载安装包
# 2. 安装 pnpm 和 Rush
npm install -g pnpm@8.15.8
npm install -g @microsoft/rush@5.147.1
# 3. 进入项目安装依赖
cd frontend
rush update
🔧 IDE 配置
VS Code(推荐):
必装扩展:
- Go(官方)
- Docker
- ESLint
- Prettier - Code formatter
- GitLens
- Error Lens
- Thunder Client(API 测试)
配置示例(.vscode/settings.json
):
{
"go.useLanguageServer": true,
"go.lintTool": "golangci-lint",
"go.lintOnSave": "workspace",
"go.formatTool": "goimports",
"[go]": {
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": true
}
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true
}
}
GoLand(专业用户):
优势:
- 更强大的代码补全
- 内置调试器
- 更好的重构支持
配置要点:
- 启用 Go Modules:Settings → Go → Go Modules
- 配置 Linter:Settings → Tools → File Watchers → golangci-lint
- 配置测试:Run → Edit Configurations → Go Test
🐳 Docker 和数据库工具
Docker 管理工具:
- Docker Desktop(Windows/macOS):官方 GUI 工具
Lazydocker:终端 UI 工具
# macOS brew install lazydocker # 使用 lazydocker
数据库客户端:
- DBeaver(免费,全能):https://dbeaver.io/
- TablePlus(付费,美观):https://tableplus.com/
- Redis Desktop Manager:https://resp.app/
API 测试工具:
- Postman:https://www.postman.com/
- Insomnia:https://insomnia.rest/
- Thunder Client(VS Code 扩展):轻量级选择
4. 进阶拓展方向 🚀
📝 技术博客与专家观点
CloudWeGo 技术团队:
Go 语言社区:
- Go 官方博客:https://go.dev/blog/
- Dave Cheney 的博客:https://dave.cheney.net/
- 鸟窝(Go 中文社区):https://colobu.com/
DDD 和架构设计:
- Martin Fowler:https://martinfowler.com/
- ThoughtWorks 洞见:https://insights.thoughtworks.cn/
🎤 相关技术大会
国内大会:
GopherChina
- 时间:每年 4-5 月
- 内容:Go 语言实践、性能优化
- 官网:https://gopherchina.org/
QCon
- 时间:每年 10-11 月
- 内容:架构设计、微服务
- 官网:https://qconshanghai.com/
字节跳动技术沙龙
- 时间:不定期
- 内容:CloudWeGo、Eino 等技术分享
国际大会:
GopherCon
- 时间:每年 11 月
- 内容:Go 语言前沿技术
KubeCon
- 时间:每年 3 月、11 月
- 内容:云原生、Kubernetes
💬 社区与论坛
Coze Loop 官方社区:
- GitHub Discussions:https://github.com/coze-dev/coze-loop/discussions
- Discord:https://discord.gg/a6YtkysB
- Telegram:https://t.me/+pP9CkPnomDA0Mjgx
Go 语言社区:
- Go Forum:https://forum.golangbridge.org/
- Go 中国:https://gocn.vip/
- Reddit r/golang:https://www.reddit.com/r/golang/
架构设计社区:
- DDD Community:https://ddd-crew.github.io/
- InfoQ 中文:https://www.infoq.cn/
- 掘金:https://juejin.cn/
🎉 结语
恭喜你读到这里!这份学习指南凝聚了 Coze Loop 项目的精华和我的经验总结。希望它能成为你学习路上的好伙伴 🤝
记住这几点:
- 学习是一个循序渐进的过程,不要急于求成
- 动手实践永远比纸上谈兵更重要
- 遇到问题不要害怕,每个 Bug 都是学习机会
- 积极参与社区,分享你的经验和困惑
- 保持好奇心和学习热情
下一步行动:
- [ ] ⭐ Star 这个项目:https://github.com/coze-dev/coze-loop
- [ ] 🚀 按照第三部分的学习路径开始实践
- [ ] 💬 加入社区,认识更多志同道合的朋友
- [ ] 🎯 设定一个小目标(如实现一个新功能)
- [ ] 🤝 提交你的第一个 Pull Request
保持联系:
如果你在学习过程中有任何问题或建议,欢迎:
- 在 GitHub 上提 Issue
- 在社区讨论区发帖
- 在 Discord/Telegram 群组提问
祝你学习愉快,期待在 Coze Loop 社区见到你的贡献!🎊
附录:常用命令速查表 📋
# Docker 常用命令
docker ps # 查看运行中的容器
docker logs -f <container> # 查看容器日志
docker exec -it <container> bash # 进入容器
docker restart <container> # 重启容器
# Go 常用命令
go test ./... # 运行所有测试
go test -v -run TestName # 运行特定测试
go test -cover ./... # 查看测试覆盖率
go mod tidy # 整理依赖
golangci-lint run # 运行 Linter
# Git 常用命令
git status # 查看状态
git add . # 添加所有修改
git commit -m "message" # 提交
git push origin branch-name # 推送
git fetch upstream # 同步上游
git rebase upstream/main # 变基到最新
# Make 常用命令(Coze Loop 项目)
make compose-up # 启动服务
make compose-down # 停止服务
make helm-up # Helm 部署
make helm-pod # 查看 Pod 状态
祝你在 Coze Loop 的学习之旅一帆风顺!🚀💪✨