找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

2606

积分

0

好友

370

主题
发表于 5 小时前 | 查看: 1| 回复: 0

TRAE 动态logo

概述

在日常的服务端开发中,“在列表页里新增数据指标”是一个高频需求。但在复杂项目中,这些指标的数据源往往非常分散。过去,我们获取数据的方式比较“笨重”:要么调用已经封装好的各个 RPC 接口,要么直接在当前服务中连接底层数据库进行查询。

后来,我们引入了 OneService(后文简称 OS),这个由字节团队开发的数据服务化平台,它能够将各种数据源的 SQL 查询快速包装成 API,让跨数据源取数的工作轻量化了不少。

但新的问题也随之而来:当需要新增多个来自不同数据源的指标时,意味着要调用多个 OS API。这些 API 的调用逻辑高度相似,只是 API ID、入参和出参略有不同。这种机械的重复开发,暴露了团队在 OS API 调用上的一些长期痛点。

面临的痛点

在团队的多款应用服务中,针对 OS API 的调用,普遍存在以下问题:

  1. 新增效率低下:每次新增都需要重复编写高度相似的模板代码,开发效率低且容易出错。
  2. 调用方式不统一:不同开发者对 OS SDK 的使用方法、参数命名、传参方式定义各不相同,导致后续维护困难。
  3. 重复开发与接入:同一个 API ID 被不同开发者写了多份调用方法,逻辑分散,难以收敛。
  4. 难以查找与复用:项目内已有的 API 调用,缺乏统一的接口描述和出入参备注,重度依赖开发者的个人注释习惯。
  5. 命名与文件组织混乱:调用方法命名随意,文件散落在各处,导致同类能力难以聚合,也无法形成约定俗成的目录结构。

为什么选择 SKILL 功能?

为了系统性地解决上述痛点,我们选择利用 TRAE 的 SKILL 功能。一句话概括 SKILL:它是一种结构化的、可复用的能力单元,它告诉 AI 在特定场景下,应该遵循怎样的标准流程来完成一个具体任务。

利用 SKILL 来标准化 OS API 调用,能带来以下核心价值:

  1. 提高增量开发效率:对于已接入 OS 的项目,新增 API 调用只需输入关键信息,TRAE 便能一键生成完整的调用方法。
  2. 降低初始接入成本:对于新项目,SKILL 可以一键完成 OS SDK 的引入、Client 初始化、以及核心方法的封装,确保符合团队统一的初始化规范。
  3. 避免重复与冲突:SKILL 会强制以 API ID 为唯一索引进行查重,优先复用已有的调用方法,从而减少重复代码和逻辑漂移。
  4. 统一产出标准:通过固化代码模板,SKILL 能确保生成的方法在命名、入参出参处理、日志、错误处理和注释风格上保持高度一致,告别因个人习惯导致的混乱。
  5. 提升可维护性与可追溯性:将 API 的用途、参数等信息沉淀为轻量级文档,使得“这个 API 是干什么的、参数是什么、在哪调用”一目了然,大幅降低后续的排查和复用成本。

使用规范

为确保 AI 生成代码的准确性和一致性,我们在 SKILL 中严格定义了生成规范。

前置约定:

  1. OS 依赖库xxx/xxx/sqlclient
  2. 生成结构:TRAE 使用该 SKILL 生成的代码将严格遵循以下目录结构:
    Project Name
    └── infra
        └── one_service/
            ├── client.go
            └── get_xxx_xxx.go
    • infra/one_service/client.go:存放初始化方法,包含 InitOneService(初始化 Client)、SqlQueryQueryWithParam(对 SDK 原生方法进行日志封装的工具方法)。
    • infra/one_service/get_xxx_xxx.go:TRAE 根据 API 名称自动命名的文件,存放具体的 API 调用方法。

开始实践

  1. 更新 TRAE:确保 TRAE 版本在 3.3.21 及以上。
  2. 创建技能:在 TRAE 设置中,进入“规则和技能”页面,点击“创建技能”。

TRAE 规则与技能设置界面

  1. 上传 SKILL 文件:上传包含 SKILL.md.zip 压缩包或单独的 SKILL.md 文件。这些文件最终会被存放在项目的 .trea/skills 目录下。

新建技能上传界面

  1. 使用 SKILL:新建任务,输入特定的 Prompt 来触发 SKILL。Prompt 主要有两种格式:

    // 方式一:输入请求参数和返回参数,SKILL 将生成调用 QueryWithParam 的代码
    我需要生成一个OneService API调用方法
    API ID:xxxxxxxxx
    名称:查询离线广告消耗指标
    请求参数
    ...
    返回参数
    ...
    // 方式二:输入SQL语句,SKILL 将生成调用 SqlQuery 的代码
    我需要生成一个OneService API调用方法
    API ID:xxxxxxxxx
    名称:查询离线广告消耗指标
    SQL:...

成果展示

场景一:在未初始化 OS 的项目中使用
当在一个尚未引入 OS SDK 的项目中使用该 SKILL 时,TRAE 会执行完整的初始化流程。

未初始化项目的生成任务列表

生成结果

  • infra 目录下创建 one_service 目录。
  • 自动导入 OS 依赖,生成 client.go 初始化文件,并在 infra/init.go 中调用初始化方法。
  • infra/one_service 目录下生成包含指定 API 调用方法的新 .go 文件。
  • api_description.md 文件中新增该 API 的描述条目。

未初始化项目的生成产物汇总

场景二:在已初始化 OS 的项目中使用
如果项目已经具备了 OS 的基础设施,TRAE 会智能地跳过初始化步骤。

已初始化项目的生成任务列表

生成结果

  • 直接在 infra/one_service 目录下生成新的 API 调用方法文件。
  • api_description.md 文件中新增 API 描述。

已初始化项目的生成产物汇总

SKILL 设计思路

了解了具体用法,我们深入探讨其背后的设计思路。这套 SKILL 的核心是 “分层设计”“渐进式披露”,旨在保证生成结果一致、可复用、可治理。

整体结构

.TRAE/
└── SKILLs/
    └── generateosmethod/
        ├── api_description.md
        ├── examples.md
        └── SKILL.md

核心思路:将“生成 OS API 调用代码”这件事拆解为三层——规范流程层可检索资产层可复制模板层

  1. SKILL.md 充当“流程编排器”:它定义了严格的前置条件检查(如是否已引入 SDK、初始化 Client)和执行链路。其核心目的是防止 AI “跑偏”或重复造轮子,确保生成代码与项目现状保持一致。
  2. api_description.md 充当“注册表/索引”:这个文件记录了每个 API ID 的用途、SQL、参数表格以及对应的代码文件位置。它承担两个关键职责:一是去重,生成前先按 API ID 检索,避免重复生成;二是知识沉淀,为团队提供一份轻量级的内部 API 文档,提升可维护性。
  3. examples.md 充当“代码模板库”:它提供了两种标准范式(使用 QueryWithParamsSqlQuery)的示例代码。生成代码时,AI 会严格对齐示例中的导包、日志、返回结构聚合等细节,确保整个代码库中所有 OS 调用“长得一样”,极大降低维护成本。
  4. 与业务仓库结构解耦:SKILL 目录本身只存放规则和模板,不包含任何业务 Go 代码。真正生成的代码会输出到项目既定的基础设施目录(如 infra/one_service)。这使得同一套 SKILL 可以复用于不同结构的项目,只要它们遵循相似的基础设施约定即可。

SKILL.md 解析

SKILL.md 是 SKILL 的核心,它规定了执行的 前置条件、使用规定和具体流程。其采用“渐进式披露”策略,AI 在每个阶段只获得完成当前步骤的必要信息,而不是一次性加载所有上下文。这大幅降低了 Token 消耗,并减少了模型产生“幻觉”(即胡乱生成)的概率。

---
name: GenerateOSMethod
description: 生成OnsService API调用方法
---
## 前置条件
#### 1.检查当前项目的infra目录中是否存在OneService的相关目录,并引入OneService的SDK并初始化好Client
#### 2.若当前项目未引入OneService的SDK或未初始化Client,请先引入并初始化好Client,按照以下步骤:
- 导入OneService的工具库,终端输入`go get xxx/xxx/sqlclient`
- 在infra目录下创建one_service目录,用于存放OneService的相关代码,包括Client的初始化代码
- 在one_service目录下创建client.go文件,用于初始化OneService的Client,代码示例如下:
```go
package one_service
import(
    "xxx/xxx/sqlclient"
    "xxx/xxx/logs"
    "xxx/xxx/utils"
    "context"
    "sync"
)
var Client *sqlclient.SQLClient
var once sync.Once
func InitOneService(){
    if Client != nil {
        return
    }
    once.Do(func() {
       // init client
       var err error
       Client, err = sqlclient.NewSqlClient()
       if err != nil {
           panic(err)
       }
    })
}
func SqlQuery(ctx context.Context, id string, sql string, res interface{}) error {
    err := Client.SqlQuery(ctx, id, sql, res)
    if err != nil {
       logs.CtxError(ctx, "[SqlQuery] query sql failed.id:%s,sql:%s,err:%s", id, sql, err.Error())
       return err
    }
    return nil
}
func QueryWithParam(ctx context.Context, id string, param map[string]interface{}, res interface{}) error {
    err := Client.QueryWithParams(ctx, id, param, res)
    if err != nil {
       logs.CtxError(ctx, "[QueryWithParam] query sql failed.id:%s,param:%s,err:%s", id, utils.JsonMarshal(ctx, param), err.Error())
       return err
    }
    return nil
}
```
- 将InitOneService方法添加到infra目录下的init.go文件中,确保Client在项目启动时就被初始化好
- 如果当前项目的infra目录下已经存在OneService的相关目录,并有了初始化好的Client,检查是否有SqlQuery或QueryWithParam方法,没有则新增
## 规定
#### 1.你只有在以下情况下才需要使用GenerateOSMethod SKILL,使用前先检查前置条件
- 用户提到需要使用/新增OnsService接口
- 用户提供的代码生成链路文档、技术方案文档当中提到需要使用/新增OnsService接口
#### 2.你必须严格按照SKILL.md文件中规定的【流程】进行操作,不能偏离流程,只能在流程规定的阶段生成代码
#### 3.生成的API描述,参考api_description.md文件中的示例,保存到api_description.md文件中
#### 4.新增一个go文件,存放到infra/one_service目录(或者已有的infra下的其他OneService相关目录)下,新增的代码(API ID,调用方法,SQL语句,请求参数,响应参数)都存放到新增的go文件中,参考examples.md文件中的示例
---
## 流程
### 1. 检查用户是否输入了需要调用的OnsService的API ID
检查用户输入内容和文档当中是否明确指出了需要调用的OnsService的API ID
如果没有明确列出,请让用户输入确认需要调用的OnsService的API ID
### 2. 遍历当前已有OnsService API调用方法
在api_description.md文件中遍历所有定义好的OnsService的API ID
通过API ID进行匹配,检查是否已存在调用当前OneService API的方法
如果用户输入多个API ID,遍历每个ID进行匹配,然后列出每个API ID的调用方法的存在情况
### 3. 检查用户是否确认调用/新增OnsService接口
上一步列出所有输入的API ID的调用方法的存在情况的列表,发送给用户,让用户确认
- 匹配到已有的OneService API调用方法,提示用户是否直接使用
- 如果没有匹配到已有的OneService API调用方法,提示用户确认是否新增
### 4. 收集OnsService API信息
- 如果用户确认直接使用,直接使用已有的调用方法
- 如果用户确认新增,提示用户输入新增的OneService API的相关信息,包括API ID、SQL语句、请求参数、响应参数等
### 5. 生成OnsService API相关代码
根据用户输入的接口信息,需要生成以下内容
- api_description.md文件中新增对应API ID的描述,参考api_description.md文件中的示例
- 根据用户的输入,结合examples.md文件里的示例,生成新的go文件,存放到infra/one_service目录下
- 最终的效果,应该是只在infra/one_service目录(或者已有的infra下的其他OneService相关目录)下新增一个go文件,在api_description.md文件中新增对应API ID的描述

examples.md 解析

examples.md 提供了两种调用范式的代码模板,是统一代码风格的基石。

---
name:examples
description:OnsService的API调用方法代码示例
---
方式一:用户输入的是请求参数,调用QueryWithParams方法查询示例数据
```go
package one_service
import(
    "context"
    "xxx/xxx/jsonx"
    "xxx/xxx/logs"
)
const(
    // 示例API ID
    OneServiceAppId_Example = xxxxxxxx
)
type ExampleData struct {
    ExampleID int64 `gorm:"column:example_id"` // 示例ID
}
// 查询示例数据
func GetExampleData(ctx context.Context, exampleIds []int64)(map[int64]*ExampleData, error){
    if len(exampleIds) == 0 {
        return nil, nil
    }
    // OneService入参
    params := map[string]interface{}{}
    // 示例ID列表
    params["example_ids"] = exampleIds
    // OneService出参
    var rpcResp []*ExampleData
    // 调用 OneService SQL 查询数据
    err := QueryWithParams(ctx, OneServiceAppId_Example, params, &rpcResp)
    if err != nil {
        logs.CtxError(ctx, "GetExampleData QueryWithParams err:%v", err)
        return nil, err
    }
    logs.CtxInfo(ctx, "GetExampleData appID %v, req is %v, resp is %v", OneServiceAppId_Example, jsonx.ToString(params), jsonx.ToString(rpcResp))
    result := make(map[int64]*ExampleData, len(rpcResp))
    for _, item := range rpcResp {
        if item == nil {
            continue
        }
        // 以 example_id 为 key 聚合
        result[item.ExampleID] = item
    }
    return result, nil
}
```
方式二:用户输入的是SQL语句,调用SqlQuery方法查询示例数据
```go
package one_service
import (
    "context"
    "fmt"
    "strings"
    "xxx/xxx/jsonx"
    "xxx/xxx/logs"
    "xxx/xxx/utils/conv"
)
const (
    // 示例API ID
    OneServiceAppId_Example = xxxxxxxx
)
type ExampleData struct {
    ExampleID int64 `gorm:"column:example_id"` // 示例ID
}
const (
    // 示例SQL
    ExampleSql = "select example_id from example_table where example_id in (:example_ids)"
)
// 查询示例数据
func GetExampleData(ctx context.Context, exampleIds []int64) (map[int64]*ExampleData, error) {
    if len(exampleIds) == 0 {
        return nil, nil
    }
    // OneService出参
    var rpcResp []*ExampleData
    // 调用 OneService SQL 查询数据
    err := SqlQuery(ctx, OneServiceAppId_Example, ExampleSql, &rpcResp)
    if err != nil {
        logs.CtxError(ctx, "GetExampleData SqlQuery err:%v", err)
        return nil, err
    }
    logs.CtxInfo(ctx, "GetExampleData appID %v, req is %v, resp is %v",
        OneServiceAppId_Example,
        fmt.Sprintf(ExampleSql, strings.Join(conv.Int64sToStrs(exampleIds), ",")),
        jsonx.ToString(rpcResp))
    result := make(map[int64]*ExampleData, len(rpcResp))
    for _, item := range rpcResp {
        if item == nil {
            continue
        }
        // 以 example_id 为 key 聚合
        result[item.ExampleID] = item
    }
    return result, nil
}
```

api_description.md 解析

api_description.md 是 API 资产的索引和文档,采用 Markdown 表格记录,清晰易读。

---
name:api_description
description:OnsService的API描述
---
## 接口列表
### API 名称(方式一示例,生成代码参考examples.md)
API ID:用户输入
生成代码文件:根据API名称生成对应的go文件
请求参数
| 参数名称     | 参数类型       | 是否必须 | 参数描述   |
|:--------|:-----------|:-----|:-------|
| xxx_ids | array[int] | 否    | xxid列表 |
|         |            |      |        |
返回参数
| 参数名称    | 参数类型 | 是否必须 | 参数描述 |
|:-------|:-----|:-----|:-----|
| xxx_id | int  | 否    | xxid |
|        |      |      |      |
---
### API 名称(方式二示例,生成代码参考examples.md)
API ID:用户输入
生成代码文件:根据API名称生成对应的go文件
请求SQL:select ...
请求参数
| 参数名称     | 参数类型       | 是否必须 | 参数描述   |
|:--------|:-----------|:-----|:-------|
| xxx_ids | array[int] | 否    | xxid列表 |
|         |            |      |        |
返回参数
| 参数名称    | 参数类型 | 是否必须 | 参数描述 |
|:-------|:-----|:-----|:-----|
| xxx_id | int  | 否    | xxid |
|        |      |      |      |
---

总结

SKILL 适用于【稳定地】执行【多步骤】且具备【固定流程】的任务。 其核心设计亮点在于 “渐进式披露” ,通过分层加载、按需激活和零上下文执行,在保证任务执行深度的同时,极大提升了 Token 利用效率和准确性。

  • 稳定性:任务本身不掺杂过多特殊业务因素,90% 的情况流程是统一的。
  • 多步骤:相比多轮对话,SKILL 能一步到位完成复杂任务。
  • 固定性:AI 严格遵循 SKILL.md 定义的步骤执行,避免无效的“扫盘”和联想,既节省资源,又显著降低了模型“幻觉”导致乱生成代码的概率。

需要强调的是,本文的示例结合了内部工具,相关配置无法直接复用。本文的核心价值在于分享 SKILL 的设计思路。凡是符合“稳定、多步骤、固定流程”特征的任务,都可以被抽象成清晰的 SOP,并通过 SKILL 固化下来,交给 AI 智能体去稳定、高效地执行。

抓住 “先梳理流程,再结构化抽象,最后让工具去执行” 这条主线,我们就能在各种开发、运维乃至日常办公场景中,持续沉淀出属于个人或团队的自动化能力,而不仅仅是停留在调用某个特定 API 的层面。

如果你想了解更多关于技能(Skills)的深层概念和更广泛的实操指南,可以参考这篇社区内的详细技术文档。在 云栈社区 里,也经常有开发者分享他们利用类似思路解决实际工程问题的经验。

AI 编程的未来形态尚未可知,但可以肯定的是,那些现在就积极学习、深入实践、理解其运作原理的人,将最能塑造并适应那个未来。去实验,去失败,去学习。这个过程本身,就是最大的价值所在。




上一篇:深入剖析:DNS与CDN为何只能是AP系统
下一篇:ApolloFish钓鱼演练平台:从部署到实战的企业安全意识赋能指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-27 17:12 , Processed in 0.330668 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表