AI业务开发实战——教你如何在业务开发中玩转AI(附代码示例)!

729 阅读11分钟

引言

在实际业务开发中,我们经常需要使用到各种工具和技术来提高效率和优化代码质量。本文将介绍一些与AI开发相关的工具和技术,来教你如何在业务开发实战中,玩转AI!

原则:

不要使用任何你看不懂的代码

工具准备:

Chatgpt

新世代万物之始——ChatGPT是一个新世代的语言模型,它可以产生逼真的对话,并提供人机交互的能力。详细信息可访问官网:Introducing ChatGPT (openai.com)

Mermaid

支持Markdown的流程图js语言工具,文档地址: Mermaid | Diagramming and charting tool

Copilot

Copilot是一个集成在IDE中的代码提示和生成工具,它利用AI技术来辅助开发人员编写代码。GitHub Copilot · Your AI pair programmer

Bito

Bito是一个功能强大的AI工具,它内置了GPT3和GPT4模型,并提供代码生成、代码分析、注释生成和单元测试生成等功能Homepage - Bito

实战

在介绍以上的工具之后,我们话不多说,进入到实际的实战环节。

在实际开发中,我们需要对需求进行分析,并绘制流程图以展示整个流程。下面是一个需求分析的实例:

一,需求分析&流程图Mermaid

1,第一步,不用多说,肯定是按照我们的需求,进行需求拆分和分析。

举一个企业微信登录以及对应的后台需求为例,我们的需求流程可以拆分为如下

- 用户成功添加好友,接收企微添加企业客户事件回调通知,获得用户external_userid、员工userid
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段
- 写进用户记录表(后台)
- 用户“企微登录成功”,将uid和unionid绑定,更新记录表
1、包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间
2、37uid、员工userid、external_userid的组合只新增,不修改
3、接收企微删除跟进成员回调通知,状态变更为用户删除,不删除记录
4、接收企微删除企业客户回调通知,状态变更为员工删除,不删除记录
5、需支持查询项:37UID、添加时间、状态;
6、后台可显示总记录条数、筛选后的记录条数

WeChatWorkScreenshot_34cee631-0d89-463a-bdad-3dba925d12bc.png

2,在梳理完需求后,我们便可以根据上面的需求,提炼出GPT提示词:

- 用户成功添加好友,接收企微添加企业客户事件回调通知,获得用户external_userid、员工userid    
- 使用企微获取客户详情接口,带external_userid请求unionid    
- 使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段    
- 写进用户记录表(后台)    
- 用户“企微登录成功”,将uid和unionid绑定,更新记录表    
1、包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间    
2、37uid、员工userid、external_userid的组合只新增,不修改    
3、接收企微删除跟进成员回调通知,状态变更为用户删除,不删除记录    
4、接收企微删除企业客户回调通知,状态变更为员工删除,不删除记录    
5、需支持查询项:37UID、添加时间、状态;    
6、后台可显示总记录条数、筛选后的记录条数 

---- 
你是一个业务开发工程师,业务提了一个上述产品逻辑,请将它总结成逻辑性较强的语句

3,将上述词汇喂给GPT后,会得到更加明确而且清晰的流程

当用户成功添加好友时,系统将接收到企微添加企业客户事件回调通知,并获取用户的external_userid和员工的userid。
接着,使用企微的获取客户详情接口,带上external_userid请求unionid。再使用读取成员接口,获得员工所在的主部门(main_department),根据主部门名称的顶级游戏ID,更新顶级游戏字段。
接下来,将这些信息写入用户记录表(后台)。
如果用户“企微登录成功”,将uid和unionid绑定,并更新记录表。
记录表包含字段:37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、用户删除、员工删除、已转接)、添加时间、状态更新时间。
37uid、员工userid、external_userid的组合只新增,不修改。
当接收到企微删除跟进成员回调通知时,状态变更为用户删除,但不会删除记录。
当接收到企微删除企业客户回调通知时,状态变更为员工删除,但不会删除记录。
查询项包括:37UID、添加时间、状态。后台可以显示总记录条数和筛选后的记录条数。

4,由于此时GPT返回的词汇还有可能是有误的,所以这里需要再进一步,优化我们的语义/逻辑化我们的语义(类似于把整个流程都用文字清晰出来)

1、起始点为:“企业微信回调”
2、“企业微信回调”请求到 “平台企微服务”
3、“平台企微服务” 对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件做“解密xml,重构成json协议”处理,其他事件忽略掉
4、对上述三个处理后的事件进行分发到kafka
5、“平台企微事件消费者”消费kafka数据对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件进行业务处理

6、其中对于处理 “用户添加联系人” 事件有以下流程
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
- 将“37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间”字段写进用户记录表

7、其中对于处理“企微员工删除联系人”,“用户自行删除联系”事件有以下流程
- 查询记录是否存在
- 更新用户记录表状态为“已删除”

最终为喂给GPT的提示词为如下

Output all the following requests as an whole Mermaid flowchart and 请将内容里的中文标点符号替换成英文标点符号,但不要把文字内容替换成英文

1、起始点为:“企业微信回调”
2、“企业微信回调”请求到 “平台企微服务”
3、“平台企微服务” 对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件做“解密xml,重构成json协议”处理,其他事件忽略掉
4、对上述三个处理后的事件进行分发到kafka
5、“平台企微事件消费者”消费kafka数据对“用户添加联系人”,“企微员工删除联系人”,“用户自行删除联系”三个事件进行业务处理

6、其中对于处理“用户添加联系人”事件有以下流程
- 使用企微获取客户详情接口,带external_userid请求unionid
- 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
- 将“37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间”字段写进用户记录表

7、其中对于处理“企微员工删除联系人”,“用户自行删除联系”事件有以下流程
- 查询记录是否存在
- 更新用户记录表状态为“已删除”

根据上面这个,我们便可以用mermaid产生具体的流程图,以供我们开发的时候查看使用

graph TD
    A[企业微信回调] 
    B[平台企微服务] 
    C{事件类型} 
    D[解密xml,重构成json协议] 
    E[忽略掉] 
    F[分发到kafka] 
    G[平台企微事件消费者] 
    H{事件类型} 
    I[使用企微获取客户详情接口,带external_userid请求unionid] 
    J[使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段] 
    K["将37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间字段写进用户记录表"] 
    L[查询记录是否存在] 
    M["更新用户记录表状态为“已删除”"] 

A-->B 
B-->C 
C-- 用户添加联系人 -->D 
C-- 企微员工删除联系人 -->D 
C-- 用户自行删除联系 -->D 
C-- 其他事件 -->E 
D-->F 
F-->G 
G-->H 
H-- 用户添加联系人 -->I 
I-->J 
J-->K 
H-- 企微员工删除联系人, 用户自行删除联系 -->L 
L-->M

二、伪代码生成

此时,我们可以将上面的提示语喂给GPT,就能出的出一个具体的伪代码

请用golang代码实现这个流程
.....
package main

import (
    "encoding/xml"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"

    "github.com/confluentinc/confluent-kafka-go/kafka"
)

// 定义企业微信回调的结构体
type Callback struct {
    XMLName        xml.Name `xml:"xml"`
    ToUserName     string
    FromUserName   string
    CreateTime     string
    MsgType        string
    Event          string
    ChangeType     string
    UserID         string
    ExternalUserID string
}

// 定义用户记录表的结构体
type User struct {
    UID              int       // 37uid
    UserID           string    // 员工userid
    MainDepartment   int       // 员工主部门
    CorpID           string    // 员工归属企微主体
    UnionID          string    // unionid
    ExternalUserID   string    // external_userid
    Status           string    // 状态(正常、已删除)
    AddTime          time.Time // 添加时间
    StatusUpdateTime time.Time // 状态更新时间
}

// 定义顶级游戏结构体
type Game struct {
    ID   int    // 游戏ID
    Name string // 游戏名称
}

func main() {
    // 监听企业微信回调
    http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
        if r.Method == "POST" {
            // 读取请求体
            body, err := ioutil.ReadAll(r.Body)
            if err != nil {
                fmt.Println("读取请求体失败:", err)
                return
            }
            // 解析xml
            var cb Callback
            err = xml.Unmarshal(body, &cb)
            if err != nil {
                fmt.Println("解析xml失败:", err)
                return
            }
            // 处理事件
            handleEvent(cb)
        }
    })
    // 启动监听
    http.ListenAndServe(":8080", nil)
}

// 处理事件
func handleEvent(cb Callback) {
    // 判断事件类型
    switch cb.Event {
    case "change_external_contact":
        // 处理用户添加联系人事件
        handleAddContact(cb.ExternalUserID)
    case "del_external_contact":
        // 处理用户自行删除联系人事件
        handleDeleteContact(cb.ExternalUserID)
    case "change_external_chat":
        // 处理企微员工删除联系人事件
        handleDeleteContact(cb.UserID)
    default:
        // 忽略其他事件
        fmt.Println("忽略事件:", cb.Event)
    }
}

// 处理用户添加联系人事件
func handleAddContact(externalUserID string) {
    // 使用企微获取客户详情接口,带external_userid请求unionid
    // TODO: 调用企微获取客户详情接口
    unionID := "1234567890"
    // 使用读取成员接口,获得员工所在的主部门main_department,根据主部门名称的顶级游戏ID,解析顶级游戏字段
    mainDepartment := 1
    gameID := getTopGame(mainDepartment)
    // 将37uid、员工userid、员工主部门、员工归属企微主体、unionid、external_userid、状态(正常、已删除)、添加时间、状态更新时间字段写进用户记录表
    addUser(externalUserID, mainDepartment, unionID, gameID)
}

// 处理用户自行删除联系人事件和企微员工删除联系人事件
func handleDeleteContact(externalUserID string) {
    // 查询记录是否存在
    user, err := getUserByExternalUserID(externalUserID)
    if err != nil {
        fmt.Println("查询用户记录失败:", err)
        return
    }
    if user == nil {
        fmt.Println("用户记录不存在")
        return
    }
    // 更新用户记录表状态为“已删除”
    user.Status = "已删除"
    user.StatusUpdateTime = time.Now()
    updateUser(user)
}

// 获取顶级游戏
func getTopGame(mainDepartment int) int {
    // TODO: 调用读取部门详情接口,获取主部门名称
    departmentName := "游戏部"
    // TODO: 根据主部门名称的顶级游戏ID,解析顶级游戏字段
    switch departmentName {
    case "游戏部":
        return 1
    case "财务部":
        return 2
    default:
        return 0
    }
}

// 添加用户记录
func addUser(externalUserID string, mainDepartment int, unionID string, gameID int) {
    // TODO: 生成37uid和员工userid
    uid := 123456
    userID := "123456"
    // TODO: 获取员工归属企微
    addTime := time.Now()
    user := User{
        UID:              uid,
        UserID:           userID,
        MainDepartment:   mainDepartment,
        CorpID:           "123456",
        UnionID:          unionID,
        ExternalUserID:   externalUserID,
        Status:           "正常",
        AddTime:          addTime,
        StatusUpdateTime: addTime,
    }
    // TODO: 将用户记录写入数据库中
    // ...
    // 发送消息到Kafka
    sendMessage(user, gameID)
}

// 获取用户记录
func getUserByExternalUserID(externalUserID string) (*User, error) {
    // TODO: 从数据库中查询用户记录
    // ...
    // 模拟返回数据
    user := &User{
        UID:              123456,
        UserID:           "123456",
        MainDepartment:   1,
        CorpID:           "123456",
        UnionID:          "1234567890",
        ExternalUserID:   externalUserID,
        Status:           "正常",
        AddTime:          time.Now(),
        StatusUpdateTime: time.Now(),
    }
    return user, nil
}

// 更新用户记录
func updateUser(user *User) {
    // TODO: 更新数据库中的用户记录
    // ...
    // 发送消息到Kafka
    sendMessage(*user, 0)
}

// 发送消息到Kafka
func sendMessage(user User, gameID int) {
    // 构建消息
    message := fmt.Sprintf("UID:%d,UserID:%s,MainDepartment:%d,CorpID:%s,UnionID:%s,ExternalUserID:%s,Status:%s,AddTime:%s,StatusUpdateTime:%s,GameID:%d",
        user.UID, user.UserID, user.MainDepartment, user.CorpID, user.UnionID, user.ExternalUserID, user.Status, user.AddTime.Format("2006-01-02 15:04:05"), user.StatusUpdateTime.Format("2006-01-02 15:04:05"), gameID)
    // 发送消息到Kafka
    p, err := kafka.NewProducer(&kafka.ConfigMap{"bootstrap.servers": "localhost"})
    if err != nil {
        fmt.Printf("创建Kafka生产者失败:%s\n", err)
        return
    }
    defer p.Close()
    deliveryChan := make(chan kafka.Event)
    err = p.Produce(&kafka.Message{
        TopicPartition: kafka.TopicPartition{Topic: &topic, Partition: kafka.PartitionAny},
        Value:          []byte(message),
    }, deliveryChan)
    if err != nil {
        fmt.Printf("发送Kafka消息失败:%s\n", err)
        return
    }
    e := <-deliveryChan
    m := e.(*kafka.Message)
    if m.TopicPartition.Error != nil {
        fmt.Printf("发送Kafka消息失败:%s\n", m.TopicPartition.Error)
        return
    }
    fmt.Printf("发送Kafka消息成功:%s\n", message)
}

三、Copliot快速开发

生成了伪代码之后,我们就可以进行实际的业务开发(往里面补充纠正逻辑),在实际的业务开发中,我们还可以用运用copliot去快速生成我们的代码。

这里可以牢记一个关键点—————方法名提现核心逻辑

2023-06-25-20-46-32-image.png

2023-06-25-20-47-45-image.png

四、Bito代码注释、分析和单元测试

在开发完代码之后,必不可少的一件事就是自测,如何提升自测的效率?这里就可以用到我们上面所提到的Bito了

安装教程这里省略,成功后可以先在IDE右侧点击 2023-06-25-20-51-26-image.png

它可以生成对应注释

2023-06-25-20-51-03-image.png

也可以根据提示词,生成对应的单元测试: 具体提示词:

func TestAddUserInfo(t *testing.T) { 
    addParam := service.PtWecomManagerAddUserInfoParam{ 
        Tgid:                    1, 
        WecomUserId:             "Test123", 
        WecomExternalUserId:     "wmX5*******0Q", 
        WecomUserDepartmentId:   "13", 
        WecomUserDepartmentName: "测试组组1-1", 
        WecomCorpId:             "ww*********e", 
        WecomCorpName:           "*******公司", 
        Unionid:                 "**************", 
        WecomExternalUsername:   "测试1234", 
    } 


    _, err := s.AddUserInfo(ctx, addParam) 
    if err != nil { 
        t.Error(err) 
    } 

    fmt.Println(err) 
} 

---- 

请为上述逻辑代码生成类似这种风格的单元测试

总结

现在是AI大时代,AI的发展正呈现出不可逆转,爆发式增长的趋势。在时代的浪潮中,我们只有紧跟时代的脚步,甚至站在浪尖才能不被时代落下。