目录
语音app一般都会添加问答功能.最常见的几deepseek.本文就以它为例.
申请api不说了.
https://api.deepseek.com/chat/completions 这是接口.
Authorization:Bearer sk-xxxxxxx( 这是你的key)
最简的调用方式:rest客户端的
{
"messages": [
{
"content": "用中文回复:北京常住人口,各区人口.",
"role": "user"
}
],
"model": "deepseek-chat",
"stream": true
}
"stream": true表示以流的方式返回数据.一般,流的方式让人感觉快一些.等流全部返回了,时间也差不多了.如果不用流的方式,一次调用可能一分钟.
deepseek-chat和deepseek-reasoner是不一样的.deepseek-reasoner是r1.有思考的过程.
1.基础请求:
先有一个okhttp:
fun streamPost(json: String, callback: Callback) {
val client = OkHttpClient.Builder()
.connectTimeout(120, TimeUnit.SECONDS)
.writeTimeout(180, TimeUnit.SECONDS)
.readTimeout(180, TimeUnit.SECONDS)
.callTimeout(180, TimeUnit.SECONDS)
.build()
val builder = Request.Builder()
.url("https://api.deepseek.com/chat/completions")
builder.addHeader(
"Authorization", "Bearer sk-xxxxxxxxxx"
)
builder.addHeader("content-type", "application/json")
if (!TextUtils.isEmpty(json.toString())) {
val body = json.toString().toRequestBody(JSON)
builder.post(body)
}
client.newCall(builder.build()).enqueue(callback)
}
接着调用:
OKHttpHelper.streamPost(
json.toString(),
object : Callback {
override fun onFailure(call: Call, e: IOException) {
Timber.tag(TAG).d("sendToDsStreamAI请求失败: ${e.message}")
setErrorMsg()
isLoading = false
}
override fun onResponse(call: Call, response: Response) {
if (!response.isSuccessful) {
Timber.tag(TAG).e("sendToDsStreamAI请求返回失败: ${response.code}")
setErrorMsg()
isLoading = false
return
}
// 使用源码流读取 SSE 数据
response.body?.source()?.use { source ->
streamResp(source, type_msg_assistant)
isLoading = false
} ?: {
isLoading = false
}
}
})
解析:
private fun streamResp(source: BufferedSource, type: Int) {
footerUserView?.hide()
val builder = StringBuilder()
try {
while (!source.exhausted()) {
try {
val line = source.readUtf8LineStrict()
if (line.startsWith("data:")) {
//Timber.tag(TAG).d("接收到数据: $line")
val data = line.substringAfter("data:")
if (TextUtils.equals("[DONE]", data.trim())) {
Timber.tag(TAG).d("接收到结束: $builder")
addAssistant(builder, "")
return
}
val resp = Gson().fromJson<DSResp>(data, DSResp::class.java)
//Timber.tag(TAG).d("useDs.result:%s", resp)
if (resp?.choices?.isNotEmpty() == true) {
AppExecutors.instance.mainThread().execute {
val content = resp.choices!![0].delta?.content
if (!TextUtils.isEmpty(content)) {
builder.append(content)
footerView?.setText(builder.toString())
}
}
}
}
} catch (e: Exception) {
Timber.tag(TAG).e(e)
setErrorMsg()
}
}
} catch (e: Exception) {
Timber.tag(TAG).e(e)
setErrorMsg()
}
}
关于流返回的格式,从官方的文档可以得到.
2.关联上下文
文档里说明了,要想在刚才的问题继续下去,需要把前面的问题一起给后端.
val json = JSONObject()
json.put("model", "deepseek-chat")
json.put("stream", true)
val messages = getHistories(input, oldList)
json.put("messages", messages)
Timber.tag(TAG).d("请求json: $json")
定义一个实体:
class Assistant:
var type: Int = type_msg_user
const val type_msg_user = 0
const val type_msg_assistant = -2
这定义了两种,一种是用户的问题,一种是ai的回答.
在列表中,可能一个ai,一个用户问题.
private fun getHistories(input: String, list: List<Assistant>): JSONArray {
var messages = JSONArray()
//只能一个ai,一个用户这样间隔的对话.把不符合的过滤了.
if (list.isNotEmpty()) {
val msgList = arrayListOf<Assistant>()
var count = list.size
var last: Assistant? = null
for (i in count - 1 downTo 0) {
val item = list[i]
if (item.type == type_msg_assistant) {
if (last != null && last.type == type_msg_assistant) {
Timber.tag(TAG).d("上一个消息是助手消息: $last")
continue
}
if (!item.success) {
Timber.tag(TAG).d("助手失败的消息: $item, last:$last")
last = null
continue
} else {
last = item
msgList.add(item)
}
} else {
if (last == null) {
Timber.tag(TAG).d("上一个消息是空,用户消息跳过,可能是出错的消息: $last")
continue
}
if (last.type == type_msg_user) {
Timber.tag(TAG).d("上一个消息是用户消息,跳过: $last")
continue
}
if (!item.success) {
Timber.tag(TAG).d("上一个消息是用户失败的消息,跳过: $last")
continue
} else {
last = item
msgList.add(item)
}
}
}
if (msgList.isNotEmpty()) {
count = msgList.size
for (i in count - 1 downTo 0) {
val item = msgList[i]
val content = JSONObject()
content.put("content", item.content)
if (item.type == type_msg_assistant) {
content.put("role", "assistant")
} else {
content.put("role", "user")
}
messages.put(content)
}
}
}
val content = JSONObject()
content.put("role", "user")
content.put("content", input)
messages.put(content)
return messages
}
这样得到数据,发送,ai就会有上下文了.