Skip to content

6. 代码开发与测试

LSY357 edited this page Jul 4, 2025 · 5 revisions

在 CozeLoop 社区版中,前后端代码均需要遵守本文档提供的编码风格与规范。

项目结构

├── backend/          # 后端代码
│   ├── api/          # API 接口定义和实现
│   │   ├── handler/  # API 处理
│   │   └── router/   # API 路由
│   ├── cmd/          # 应用入口和服务启动
│   │   ├── conf      # 各模块配置文件
│   │   └── main.go   # 入口函数
│   ├── modules/      # 核心业务模块
│   │   ├── data/     # 数据集模块
│   │   │   ├── application/ # 应用服务层
│   │   │   ├── domain/      # 领域模型层
│   │   │   ├── pkg /        # 公共工具层
│   │   │   └── infra/       # 基础设施层
│   │   ├── evaluation/    # 评测模块
│   │   ├── foundation/    # 基建模块
│   │   ├── llm/           # LLM模块
│   │   ├── observability/ # 观测模块
│   │   └── prompt/        # PE模块
│   ├── pkg/            # 通用工具包和库
│   └── script/         # 脚本
│       ├── errorx/     # 错误码定义及生成工具
│       └── kitex/      # Kitex代码生成工具
├── frontend/         # 前端代码
├── conf/             # 基础组件配置文件
├── docs/             # 文档
└── idl/              # IDL接口定义文件

开发规范

代码结构

仓库采用 Monorepo 的方式,前后端的代码都在同一个仓库。 后端的代码设计采用 DDD 的方式,遵循分层架构,每个业务模块都遵循以下分层架构:

  • application:应用服务层,协调领域对象完成业务流程
  • domain:领域模型层,定义核心业务实体和业务逻辑
  • infra:基础设施层,提供技术实现和外部服务集成
  • pkg:模块特定的公共包

Go 规范

Go 语言的代码规范可参考Google规范。建议使用gofmt等格式化工具进行代码格式化。

IDL 规范

类别 说明
Service 定义 * 服务命名采用驼峰命名
* 一个Thrift文件只定义一个Service, extends聚合除外
Method 定义 * 接口命名采用驼峰命名
* 接口只能拥有一个参数和一个返回值,且是自定义Struct类型
* 入参须命名为{Method}Request,返回值命名为{Method}Response
* 每个Request类型须包含Base字段,类型base.Base,字段序号为255,optional类型
* 每个Response类型须包含BaseResp字段,类型base.BaseResp,字段序号为255
Struct 定义 * 结构体命名采用驼峰命名
* 字段命名采用蛇形命名
* 新增字段设置为optional,禁止required
* 禁止修改现有字段的ID和类型
枚举定义 * 推荐使用typdef来定义枚举值
* 枚举值命名采用驼峰命名,类型和名字之间用下划线连接
API定义 * 使用Restful风格定义API
* 参考现有模块的API定义,风格保持一致
注解定义 * 可参考Kitex支持的注解
* 可参考Hertz支持的注解

规范示例如下:

 # 单个Service
 typedef string EnumType(ts.enum="true") 

 const EnumType EnumType_Text = "Text"

 struct ExampleRequest {
     1: optional i64 id
 
     255: optional base.Base base
 }

 struct ExampleResponse {
     1: optional string name
     2: optional EnumType enum_type

     255: base.BaseResp base_resp
 }

 service ExampleService {
     ExampleMethod(1: ExampleRequest) (2: ExampleResponse)
 }

 # 多个Service
 service ExampleAService extends idl_a.AService{}
 service ExampleBService extends idl_b.BService{}

单测规范

类别 规范说明
UT 函数命名 * 普通函数命名为Test{FunctionName}(t *testing.T)
* 对象方法命名为Test{ObjectName}{MethodName}(t *testing.T)
* 基准测试函数命名为Benchmark{FunctionName}(b *testing.B)
* 基准测试对象命名为Benchmark{ObjectName}
{MethodName}(b *testing.B)
文件命名 测试文件与被测试文件同名,后缀为_test.go,处于同一目录下
测试设计 * 推荐使用 Table-Driven 的方式定义输入/输出,覆盖多种场景
* 使用github.com/stretchr/testify简化断言逻辑
* 使用github.com/uber-go/mock生成Mock对象,尽量避免Patch打桩的方式

测试示例如下:

func TestRetryWithMaxTimes1(t *testing.T) {
    type args struct {
        ctx context.Context
        max int
        fn  func() error
    }
    tests := []struct {
        name    string
        args    args
        wantErr bool
    }{
        {
            name: "test1",
            args: args{
                max: 3,
                fn: func() error {
                    return nil
                }
            },
            wantErr: false,
        }
        // Add more test cases.
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := RetryWithMaxTimes(tt.args.ctx, tt.args.max, tt.args.fn)
            assert.Equal(t, tt.wantErr, err != nil)
        })
    }
}

前端开发规范

开发准备

修改 CozeLoop 社区版前端代码之前,请确认开发环境已具备以下要求:

  • Node.js 18+ (推荐 lts/iron 版本)
  • pnpm 8.15.8
  • Rush 5.147.1

你可以参考以下步骤准备前端开发环境。

  1. 安装 Node.js 18+。

    nvm install lts/iron
    nvm alias default lts/iron # 设置默认 Node 版本
    nvm use lts/iron
  2. 打开 frontend 目录。

    # 切换目录
    cd frontend
  3. 安装全局依赖。

  4. 安装或更新项目依赖。

    rush update

开发前端代码

运行

在 CozeLoop 社区版目录下执行以下命令启动开发任务,运行开发环境。

  • 推荐使用 rushx 而不是 pnpm runnpm run
  • CozeLoop 项目位于 apps/cozeloop 目录,是一个 React 应用。
cd apps/cozeloop

rushx dev

执行完毕后,在浏览器中打开 http://localhost:8090/ 以查看页面。

构建

CozeLoop 项目由 Rsbuild 构建,配置文件是 apps/cozeloop/rsbuild.config.ts。 执行以下命令以构建项目。

cd apps/cozeloop

rushx build

workspace 依赖

如你所见,apps/cozeloop/package.json 中有许多依赖是 workspace:* 版本,这意味着它们是在此仓库内维护。 CozeLoop 项目依赖这些项目的源代码而非构建产物,通常情况下修改这些 workspace 依赖的源代码,变更会立刻生效。极个别场景下需要重新运行项目。

开发流程

  1. 创建功能分支。 建议每个功能对应一个功能分支。

    git checkout -b feat/your-feature-name
  2. 开发新功能

    • 如果需要修改 IDL,按照规范修改,然后使用脚本生成代码,这时候会生成 kitex 以及 hertz 的代码。

      cd ./backend/script/cloudwego
      ./code_gen.sh
    • 如果需要修改依赖注入,遵循当前使用 wire 依赖注入的方式,修改对应目录下 wire.go 后重新生成代码。

      # 修改总体初始化的依赖注入
      cd ./backend/api/handler/coze/loop/apis
      wire
      # 修改子模块的依赖注入
      cd ./backend/modules/observability/application
      wire
    • 如果需要新增数据库/新增 MQ Topic:

      • 新增MySQL表: 在conf/default/mysql/init-sql下新增建表SQL,注意需要增加IF NOT EXISTS
      • 新增Clickhouse表: 在conf/default/clickhouse/init-sql下新增建表SQL,注意需要增加IF NOT EXISTS
      • 新增RocketMQ Topic: 在conf/default/rocketmq/broker/topics.cnf下新增Topic,格式为{topic}={consumer}
    • 按照Go的开发规范进行后端服务开发,如果是在开发模式下运行服务,代码修改后端服务会自动重新进行编译然后启动。其他模式则需要重新进行镜像编译运行,建议使用开发模式进行开发。

      # 重新编译镜像运行,编译时间会比较长
      docker compose up app --build
    • 提交代码前,需要先添加单测,增量覆盖率在80%以上,确保存量单测都能通过。

      cd backend/
      go test -gcflags="all=-N -l" -count=1 -v ./...
  3. 提交代码

       git add .
       git commit -m "feat: add new feature"
  4. 合并请求

    1. 推送到远程仓库
    2. 创建 Pull Request
    3. 确保代码的CI通过
    4. 等待Code Review

测试流程

单元测试

  1. 运行测试。 执行以下命令进行单元测试。

    # 运行所有测试确保通过
    cd backend/
    go test -gcflags="all=-N -l" -count=1 -v ./...
  2. 测试覆盖率。 执行以下命令生成测试覆盖率报告。

    # 生成测试覆盖率报告
    cd backend/
    go test -gcflags="all=-N -l" -coverprofile=coverage.out ./...
    go tool cover -html=coverage.out

功能测试

本地部署后可以打开平台,测试各模块的功能是否正常:

功能模块 说明
Prompt开发与调试 * Playground能够正常进行调试
* Prompt创建与管理符合预期
评测实验 * 新建数据集
* 新增评估器
* 新增实验,对刚创建的Prompt进行评测
* 实验完成并查看分析报告
Trace 上报与查询 在 Trace 界面选择 Prompt 页签,查看是否有 Trace 展示。

Clone this wiki locally