前言

我想要用代码中调用扣子的智能体帮我完成点事,我可不想每次都到页面上去调用查看结果,并想在代码中使用,可是要完成这个功能并不简单。

本篇使用Kotlin配合OKhttp进行调用,主要还是写起来爽。

免责声明:

写本篇博客时扣子版本为2025年7月18日,如果后面有更新,不能保证这篇博客中的内容还能正常使用。

创建令牌

进入扣子的控制台,找到扣子API,点击授权,在服务身份及凭证处创建一个令牌,不建议使用个人访问令牌,因为一个月就过期了,没人想每个月都更新一遍令牌吧。

创建智能体

这个就不用多说了,按照自己想法自己创建一个智能体就行了

由于我要的功能比较简单,只需要插件,都不需要工作流,因此我这边就不详细说明了,创建完成后记得要发布才行,不然会无法拿到bot_id,导致无法进行API调用。

注意!发布的时候需要勾选API,否则可能无法通过API调用

API调用

最重要的一步就是通过API调用接口了,扣子官方文档里其实有说调用接口的过程

扣子

一共涉及到3个接口,只需要按照这个流程图调用即可

调用chat接口

第一步调用chat接口,创建聊天,注意!这个聊天返回的数据中不包含任何聊天结果,其中返回体中的msg不是聊天结果数据,而且接口是否异常的数据,只有在code不等于0时即接口报错时msg才会有结果

chat接口调用代码如下:

ChatBo是入参信息,我后面会给代码,secretKey就是令牌中的数据,conversation_id是上一次对话的对话id。由于这里的智能体调用是个耗时操作,因此我将整个过程都放在线程池中进行操作了

    fun chat(chatBo: ChatBo, onSuccess: (resultVo: ChatResultVo) -> Unit, onFail: (e: Exception) -> Unit) {
        retrieveRetryCount = 0
        CozeThreadPool.threadPool.submit {
            val requestJson = JSON.toJSONString(chatBo)
            val toRequestBody = requestJson.toRequestBody("application/json".toMediaType())
            var url = "https://api.coze.cn/v3/chat"
            chatBo.conversationId?.also {
                url = "https://api.coze.cn/v3/chat?conversation_id=$it"
            }
            val request = Request.Builder().url(url)
                .header("Authorization", "Bearer $secretKey")
                .header("Content-Type", "application/json")
                .post(toRequestBody)
                .build()
            val call = okHttpClient.newCall(request)
            call.enqueue(object : Callback {
                override fun onFailure(call: Call, e: IOException) {
                    onFail(e)
                }

                override fun onResponse(call: Call, response: Response) {
                    try {
                        retrieve(response, onSuccess, onFail)
                    } catch (e: Exception) {
                        onFail(e)
                    }
                }
            })
        }
    }

ChatBo

这个就是我的入参信息,代码如下:

class ChatBo {

    @JSONField(name = "additional_messages")
    var additionalMessages = ArrayList<AdditionalMessage>()


    @JSONField(name = "user_id")
    var userId: String? = null

    var stream = false


    @JSONField(name = "bot_id")
    var botId: String? = null


    @JSONField(name = "auto_save_history")
    var autoSaveHistory = true


    var conversationId:String? = null
}

class AdditionalMessage {
    var role = "user"

    @JSONField(name = "content_type")
    var contentType = "text"

    @JSONField(name = "meta_data")
    var metaData: HashMap<String, String>? = null
    var type: String? = null
    var content: String? = null
}

实际就是接口文档中的请求数据,其中要完成这个功能,autoSaveHistory必须为true,stream必须为false

additionalMessages必填,需要传入的对话内容在其中的content中。

userId必填,但是可以随便写。

botId就是你的智能体的id,可以在页面的URL中找到。

conversationId为上一次对话id,选填。

扣子

调用retrieve接口

这个接口用于查看对话信息,查看这个对话ai是否正在生成结果,代码如下:

其中conversationId和chatId能在上一个chat接口的返回值中拿到,用于查询这里的状态,主要就是判断返回值中的status是否为completed,如果是progress状态则需要再次请求,我这里偷懒直接使用递归调用了,限制调用10次,超出次数则直接报错,不再执行。

    private fun retrieve(response: Response, onSuccess: (resultVo: ChatResultVo) -> Unit, onFail: (e: IOException) -> Unit) {
        response.body?.string()?.let {
            println(it)
            val chatVo = JSON.parseObject(it, ChatVo::class.java)
            if (chatVo.code != 0L) {
                error("code not null, message is ${chatVo.msg}")
            }
            chatVo
        }?.let {
            it.data?.let {
                val chatId = it.id ?: throw RuntimeException("chat_id is null")
                val conversationId = it.conversationId ?: throw RuntimeException("conversation_id is null")
                retrieveData(conversationId, chatId, onSuccess, onFail)
            }

        } ?: run {
            error("chat_data is null")
        }
    }

    private fun retrieveData(
        conversationId: String,
        chatId: String,
        onSuccess: (resultVo: ChatResultVo) -> Unit,
        onFail: (e: IOException) -> Unit
    ) {
        val retrieveRequest = Request.Builder().url("https://api.coze.cn/v3/chat/retrieve?conversation_id=${conversationId}&chat_id=${chatId}")
            .header("Authorization", "Bearer $secretKey")
            .header("Content-Type", "application/json")
            .get()
            .build()
        val retrieveCall = okHttpClient.newCall(retrieveRequest)
        retrieveCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                onFail(e)
            }

            override fun onResponse(call: Call, response: Response) {
                response.body?.string()?.let {
                    val retrieveVo = JSON.parseObject(it, RetrieveVo::class.java)
                    retrieveVo.code?.let {
                        if (it != 0L) {
                            error("code not null, message is ${retrieveVo.msg}")
                        }
                    }
                    retrieveVo.data?.let {
                        it.status?.let {
                            if (it == "completed") {
                                // INFO: Zhouwx: 2025/7/18 如果是已完成的,则进入下一步,否则继续请求,直到已完成为止
                                listMessage(conversationId, chatId, onSuccess, onFail)
                            } else {
                                Thread.sleep(2000)
                                retrieveRetryCount++
                                if (retrieveRetryCount > 10) {
                                    error("retrieve_retry_count > 10")
                                } else {
                                    retrieveData(conversationId, chatId, onSuccess, onFail)
                                }
                            }
                        }
                    }
                } ?: run {
                    error("retrieve data is null")
                }
            }
        })
    }

调用message list接口

这是本篇博客的最后一步了,在这一步可以使用ai返回的结果了

代码如下:

    private fun listMessage(conversationId: String, chatId: String, onSuccess: (messageList: ChatResultVo) -> Unit, onFail: (e: IOException) -> Unit) {
        val listMessageRequest = Request.Builder().url("https://api.coze.cn/v3/chat/message/list?conversation_id=${conversationId}&chat_id=${chatId}")
            .header("Authorization", "Bearer $secretKey")
            .header("Content-Type", "application/json")
            .get()
            .build()
        val listMessageCall = okHttpClient.newCall(listMessageRequest)
        listMessageCall.enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                onFail(e)
            }

            override fun onResponse(call: Call, response: Response) {
                response.body?.string()?.let {
                    val listMessageVo = JSON.parseObject(it, MessageListVo::class.java)
                    listMessageVo.data?.let {
                        val chatResultVo = ChatResultVo()
                        chatResultVo.conversationId = conversationId
                        chatResultVo.messageList = it
                        chatResultVo.detail = listMessageVo
                        onSuccess(chatResultVo)
                    }
                }
            }
        })
    }
}

要说明一下,这里的ChatResultVo有点复杂,其中包含了多个内容,甚至包含了考虑过程信息,取data[0]中的content即可,就是智能体返回的结果数据。

结语

一开始对着这堆API研究了半天,就是不出数据,没想到官方把流程图等信息放在最佳实践里了,这个调用流程才迎刃而解,我也是服了,具体完整代码我在下方开源了,大家可以看一下,只是一个demo而已,写得有点潦草,请多包涵!

项目地址:

https://gitee.com/decentant/coze-chat-demo

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐