第二章 使用Android完成KIMI API流式输出

AIGC大模型从入门到放弃之路

第一章 使用Android完成KIMI API简单使用
第二章 使用Android完成KIMI API流式输出
第三章 Android使用KIMI API模拟小说网络作家写大纲
第四章 Android上使用阿里的通义万相大模型完成小说的封面图片制作



前言

前面第一章主要介绍了怎么申请Kimi的API Key和怎么通过API调用。
这一章主要介绍怎么使用Kimi的流式输出。流式相对与非流式来说,用户体验会更好一点。当用户输入一个问题或者一个对话时,机器人一个一个字生成回答,有一种拟人化。同时如果机器人需要回到的内容比较大时,一次性输出需要等待的时间非比较长,用户体验就比较差了。


一、流式是什么?

在计算机编程和API(应用程序编程接口)的上下文中,“流式”(Streaming)通常指的是一种数据传输方式,其中数据被分成小块连续地发送和接收,而不是一次性发送或接收整个数据集。这种技术在处理大量数据或实时数据传输时非常有用。

对于KIMI API或任何其他API来说,如果提供"流式输出"的选项,这意味着API可以:

  1. 实时数据传输:API可以立即开始发送数据,而不是等待所有数据都准备好后再发送。
  2. 节省内存:对于客户端,不必一次性加载全部数据,从而节省内存资源。
  3. 处理大数据:允许客户端逐步处理数据,而不是一次性处理大量数据,这在处理大数据集时尤其有用。
  4. 连续更新:适用于需要连续更新数据的应用场景,如实时监控、社交媒体动态流等。

选择流式返回可以提高应用性能和用户体验,特别是在网络延迟较高或数据集较大的情况下。然而,流式处理也要求客户端能够处理这种连续的数据流,并且可能需要额外的逻辑来正确地解析和使用数据。

二、使用步骤

1.引入okhttp库

相对于使用Java的HttpURLConnection,okhttp使用起来更加方便,所以我这里把HttpURLConnection换成了okhttp。

implementation 'com.squareup.okhttp3:okhttp:4.9.0'

2.新建对应的请求数据类和响应数据类

详见官方的demo:https://github.com/MoonshotAI/MoonshotAI-Cookbook/tree/master
因为我这里使用的是kotlin,代码可能跟官方有点区别,但大体上是一致的。

ChatCompletionChoice

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

data class ChatCompletionChoice(val index: Int,
                                val message:ChatCompletionMessage,
                                @SerializedName("finish_reason")
                                val finishReason: String)

ChatCompletionMessage

package com.ckt.kimi.demo.bean

data class ChatCompletionMessage(
    val role: String,
    val content: String,
    var name: String? = null,
    var partial: Boolean = false
)

ChatCompletionRequest

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

data class ChatCompletionRequest(val model: String,
                                 /**
                                  * 包含迄今为止对话的消息列表
                                  */
                                 val messages: MutableList<ChatCompletionMessage>,
                                 /**
                                  * 这个值建议按需给个合理的值,如果不给的话,我们会给一个不错的整数比如 1024。
                                  * 特别要注意的是,这个 max_tokens 是指您期待我们返回的 token 长度,
                                  * 而不是输入 + 输出的总长度。比如对一个 moonshot-v1-8k 模型,它的最大输入 + 输出总长度是 8192,
                                  * 当输入 messages 总长度为 4096 的时候,您最多只能设置为 4096,
                                  * 否则我们服务会返回不合法的输入参数( invalid_request_error ),并拒绝回答。
                                  * 如果您希望获得“输入的精确 token 数”,可以使用下面的“计算 Token” API 使用我们的计算器获得计数
                                  */
                                 @SerializedName("max_tokens")
                                 val maxTokens: Int,
                                 /**
                                  * 使用什么采样温度,介于 0 和 1 之间。较高的值(如 0.7)将使输出更加随机,
                                  * 而较低的值(如 0.2)将使其更加集中和确定性
                                  */
                                 @SerializedName("temperature")
                                 val temperature: Float,
                                 /**
                                  * 另一种采样方法,即模型考虑概率质量为 top_p 的标记的结果。因此,
                                  * 0.1 意味着只考虑概率质量最高的 10% 的标记。
                                  * 一般情况下,我们建议改变这一点或温度,但不建议 同时改变
                                  */
                                 val topP: Float,
                                 /**
                                  * 为每条输入消息生成多少个结果,默认为 1,不得大于 5。
                                  * 特别的,当 temperature 非常小靠近 0 的时候,我们只能返回 1 个结果,
                                  * 如果这个时候 n 已经设置并且 > 1,我们的服务会返回不合法的输入参数(invalid_request_error)
                                  */
                                 val n: Int = 1,
                                 /**
                                  * 是否流式返回.
                                  * 默认 false, 可选 true
                                  */
                                 var stream: Boolean = false,
                                 var top: MutableList<String>? = null,
                                 /**
                                  * 存在惩罚,介于-2.0到2.0之间的数字。
                                  * 正值会根据新生成的词汇是否出现在文本中来进行惩罚,增加模型讨论新话题的可能性
                                  * 默认为 0
                                  */
                                 @SerializedName("presence_penalty")
                                 var presencePenalty: Float = 0F,
                                 /**
                                  * 频率惩罚,介于-2.0到2.0之间的数字。
                                  * 正值会根据新生成的词汇在文本中现有的频率来进行惩罚,减少模型一字不差重复同样话语的可能性
                                  * 默认为 0
                                  */
                                 @SerializedName("frequency_penalty")
                                 var frequencyPenalty: Float = 0F,
                                 var user: String?= null)

ChatCompletionResponse

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

data class ChatCompletionResponse(val id: String,
                                  @SerializedName("object")
                                  val obj: String,
                                  val created: Long,
                                  val model: String,
                                  val choices: MutableList<ChatCompletionChoice>,
                                  val usage: Usage)

ChatCompletionStreamChoice

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

data class ChatCompletionStreamChoice(val index: Int,
                                      val delta:ChatCompletionStreamChoiceDelta,
                                      @SerializedName("finish_reason")
                                      val finishReason: String,
                                      val usage: Usage)

ChatCompletionStreamChoiceDelta

package com.ckt.kimi.demo.bean

data class ChatCompletionStreamChoiceDelta(val content:String,
                                           val role: String)

ChatCompletionStreamResponse

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

data class ChatCompletionStreamResponse(val id: String,
                                        @SerializedName("object")
                                        val obj: String,
                                        val created: Long,
                                        val model: String,
                                        val choices:MutableList<ChatCompletionStreamChoice>)

ChatMessageRole

package com.ckt.kimi.demo.bean

enum class ChatMessageRole(val value: String) {
    SYSTEM("system"),
    USER("user"),
    ASSISTANT("assistant");
}

Model

package com.ckt.kimi.demo.bean

import com.google.gson.annotations.SerializedName

/**
 * 目前是 moonshot-v1-8k,moonshot-v1-32k,moonshot-v1-128k 其一
 */
data class Model(val id: String,
                 @SerializedName("object")
                 val obj: String,
                 @SerializedName("owner_by")
                 val ownedBy: String,
                 val root: String,
                 val parent: String)

ModelsList

package com.ckt.kimi.demo.bean

data class ModelsList(val data: MutableList<Model>)

Usage

package com.ckt.kimi.demo.bean

data class Usage(val promptTokens: Int,
                 val completionTokens: Int,
                 val totalTokens: Int)

3.完成流式请求

将参数stream设置为true即可,默认值为false

    @JvmStatic
    @Throws(IOException::class)
    fun doChatCompletionStream(request: ChatCompletionRequest, streamCallback: OnStreamCallback) {
        request.stream = true
        val client = OkHttpClient()
        val mediaType: MediaType? = "application/json".toMediaTypeOrNull()
        val body = RequestBody.create(mediaType, Gson().toJson(request))
        val httpRequest: Request = Request.Builder()
            .url(getChatCompletionUrl())
            .addHeader("Authorization", "Bearer " + getApiKey())
            .addHeader("Content-Type", "application/json")
            .post(body)
            .build()
        val response = client.newCall(httpRequest).execute()
        if (response.code != 200) {
            throw RuntimeException ("Failed to start stream: " + response.body?.string())
        }
        val responseBody = response.body ?: throw RuntimeException("Response body is null")
        var line: String
        streamCallback.onStart()
        while (responseBody.source().readUtf8Line().also { line = it!! } != null) {
            Log.d(MainActivity.TAG, "read Line : ${line}")
            if (line.startsWith("data:")) {
                line = line.substring(5)
                line = line.trim { it <= ' ' }
            }
            if (Objects.equals(line, "[DONE]")) {
                streamCallback.onComplete()
                return
            }
            line = line.trim { it <= ' ' }
            if (line.isEmpty()) {
                continue
            }
            val gson = Gson()
            val streamResponse = gson.fromJson(
                line,
                ChatCompletionStreamResponse::class.java
            )
            streamCallback.onNext(streamResponse)
        }
    }

4. 效果展示

功能展示详见下图:
在这里插入图片描述


总结

功能还是挺简单,主要就是通过Kimi的api接口根据回调依次显示即可。
代码如果有需要参考的,我也上传了,仅供参考:https://download.csdn.net/download/alishaoxiong/89517864

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值