为系统接入 Kimi AI 智能大模型(附源码)
火眼9988 2024-08-25 08:01:02 阅读 80
前言
Kimi 是月之暗面公司出品的人工智能大模型助手,在国内中文大模型中算还是不错的。接下来我们将详细介绍,如何接入 Kimi AI 智能大模型,并提供可使用的生产代码(Java)。
Kimi 官方的 API 文档有点过于简单了,不过它的 API 可兼容 OpenAI 的 SDK,使用支持 OpenAI 的 SDK 可以直接使用 Kimi API 的基本能力。但我们接下来介绍如何直接与 Kimi API 交互,以及介绍它的主要服务接口。
获取令牌
要使用 Kimi 的 API,首先需要注册并获取 API Key。注册账号非常简单,就不过多赘述了。
注册完成后,进入控制台,在 API Key 管理中新建 Key,输入一个名字。
注意自己保存好 Key,控制台上不能再次获取到,如果丢失了只能重新生成一个。
这个 Key 就是我们调用 Kimi API 的令牌。
使用
Kimi API 支持 Restful 请求,我们可以包装构建自己的客户端,并像 Kimi 服务器发起请求。
客户端
首先,我们用 OKHttp 来构建一个请求客户端。
<code>public class MoonShotClient { -- -->
public static final String MOONSHOT_BASE = "https://api.moonshot.cn/v1";
private final OkHttpClient httpClient;
// 服务端基础路径
private final String baseUrl;
// API Key
private final String accessToken;
// json 序列化
private final ObjectMapper objectMapper;
public MoonShotClient(String baseUrl, String accessToken, OkHttpClient httpClient) {
this.baseUrl = baseUrl;
this.accessToken = accessToken;
if (Objects.isNull(httpClient)) {
// 初始化 okhttp 客户端,可自定义配置参数,或从其他配置文件获取
this.httpClient =
new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.callTimeout(30, TimeUnit.SECONDS)
.build();
} else {
this.httpClient = httpClient;
}
this.objectMapper = new ObjectMapper();
this.objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
}
public MoonShotClient(String accessToken, OkHttpClient httpClient) {
this(MOONSHOT_BASE, accessToken, httpClient);
}
public MoonShotClient(String accessToken) {
this(accessToken, null);
}
}
单轮对话
单轮对话就是一次性的问题,需要 Kimi 返回问题的答案。
请求路径是 /chat/completions
,请求体是一个 JSON 对象,类似下面这样:
{
model: "moonshot-v1-8k",
messages: [
{ "role": "system", "content": "你是 Kimi,由 Moonshot AI 提供的人工智能助手,你更擅长中文和英文的对话。你会为用户提供安全,有帮助,准确的回答。同时,你会拒绝一切涉及恐怖主义,种族歧视,黄色暴力等问题的回答。Moonshot AI 为专有名词,不可翻译成其他语言。"},
{ "role": "user", "content": "你好,我叫李雷,1+1等于多少?"}
],
temperature: 0.3,
}
其中,model 是模型 ID,目前支持 moonshot-v1-8k,moonshot-v1-32k,moonshot-v1-128k。
messages 是对话的消息数组,其中的 role 是角色,只支持 system,user,assistant,content 是消息内容,不能为空。
有个 stream 控制返回类型,默认 false,是非流式返回,设置为 true 则采用流式返回。
只有 model 和 messages 是必选参数,其他的都是可选参数,详细的参数说明可参考字段说明。
我们先来构建请求体对象(下面使用了 Lombok 简化代码)。
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class ChatMessage {
private String role;
private String content;
}
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class ChatCompletionsRequestBody {
private String model;
private List<ChatMessage> messages;
private double temperature;
// ... 其他参数
}
Kimi 返回的响应体也是一个 JSON 对象。
对于非流式返回,一次返回完整的数据,结构如下所示:
{
"id": "cmpl-04ea926191a14749b7f2c7a48a68abc6",
"object": "chat.completion",
"created": 1698999496,
"model": "moonshot-v1-8k",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": " 你好,李雷!1+1等于2。如果你有其他问题,请随时提问!"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 19,
"completion_tokens": 21,
"total_tokens": 40
}
}
对于流式返回,结果会一条条返回,每条是按顺序的几个词,适合于生产环境中较大的结果。
data: { "id":"cmpl-1305b94c570f447fbde3180560736287","object":"chat.completion.chunk","created":1698999575,"model":"moonshot-v1-8k","choices":[{ "index":0,"delta":{ "role":"assistant","content":""},"finish_reason":null}]}
data: { "id":"cmpl-1305b94c570f447fbde3180560736287","object":"chat.completion.chunk","created":1698999575,"model":"moonshot-v1-8k","choices":[{ "index":0,"delta":{ "content":"你好"},"finish_reason":null}]}
...
data: [DONE]
我们也构建一个 Java 对象来接收响应体。
@Data
@ToString
public class ChatCompletionsResponseBody {
private String id;
private String object;
private long created;
private String model;
private List<Choice> choices;
private Usage usage;
@Data
@ToString
public static class Choice {
private long index;
private ChatMessage message;
private String finishReason;
}
@Data
@ToString
public static class Usage {
private long promptTokens;
private long completionTokens;
private long totalTokens;
}
}
然后编写调用的方法,在客户端代码中添加对话的方法。
@Nullable
public ChatCompletionsResponseBody chatCompletions(@Nullable ChatCompletionsRequestBody body)
throws IOException {
if (Objects.isNull(body)) {
return null;
}
// 构建请求体
RequestBody requestBody =
RequestBody.create(
this.objectMapper.writeValueAsString(body), MediaType.parse("application/json;charset=utf-8"));
Request request =
new Request.Builder()
.url(this.baseUrl + "/chat/completions")
.addHeader("Authorization", "Bearer " + this.accessToken)
.post(requestBody)
.build();
// 发起请求
final Call call = this.httpClient.newCall(request);
Response response = call.execute();
if (response.isSuccessful()) {
// 请求成功
ResponseBody responseBody = response.body();
if (Objects.nonNull(responseBody)) {
String content = responseBody.string();
return this.objectMapper.readValue(content, ChatCompletionsResponseBody.class);
}
} else {
// 请求异常
ResponseBody responseBody = response.body();
if (Objects.nonNull(responseBody)) {
String content = responseBody.string();
throw new MoonShotRequestException(content);
}
}
return null;
}
返回的响应体就包含了 Kimi 给我们的答案。
多轮对话
多轮对话可以让 Kimi 掌握之前对话的上下文,提供更准备的回答。我们可以将之前对话的结果传递给下一个对话。
// history 是上一个请求响应体中 choices 的 message
ChatMessage history = new ChatMessage("system", "...");
ChatMessage chatMessage1 = new ChatMessage("system", "...");
ChatMessage chatMessage2 = new ChatMessage("user", "...");
ChatCompletionsRequestBody body = new ChatCompletionsRequestBody("moonshot-v1-8k", Arrays.asList(history, chatMessage1, chatMessage2), 0.3);
然后一起发送请求即可。
上传文件
Kimi 支持从文件中学习并回答问题,支持多种格式的文件,如 PDF、Word、Excel、PPT、图片文件、文本文件等,同时 Kimi 的 API 提供了强大的 OCR 能力,对于 PDF、图片等格式的文件可自动提取识别文字。然后就可以将提取的文字发送给对话接口作为上下文。上传文件的接口路径是 /files
,通过 multipart 表单上传文件。我们在客户端类中添加上传文件的方法。
@Nullable
public FileUploadResponseBody uploadFile(byte[] bytes, String filename, String contentType)
throws IOException {
if (Objects.isNull(bytes) || !StringUtils.hasText(filename)) {
return null;
}
// 创建请求体
RequestBody fileBody = RequestBody.create(bytes, MediaType.parse(contentType));
MultipartBody requestBody =
new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("file", filename, fileBody)
.addFormDataPart("purpose", "file-extract")
.build();
Request request =
new Request.Builder()
.url(this.baseUrl + "/files")
.addHeader("Authorization", "Bearer " + this.accessToken)
.header("Content-Type", "multipart/form-data")
.post(requestBody)
.build();
// 发起请求
final Call call = this.httpClient.newCall(request);
try (Response response = call.execute()) {
ResponseBody body = response.body();
if (response.isSuccessful()) {
// 请求成功
if (Objects.nonNull(body)) {
String content = body.string();
return this.objectMapper.readValue(content, FileUploadResponseBody.class);
}
} else {
// 请求失败
if (Objects.nonNull(body)) {
String content = body.string();
throw new MoonShotRequestException(content);
}
}
}
return null;
}
响应对象的类型如下,其中 id 为上传后生成的文件 ID。
@Data
@ToString
public class FileUploadResponseBody {
private String id;
private String object;
private long bytes;
private long createdAt;
private String filename;
private String purpose;
private String status;
private String status_details;
}
然后使用文件 ID 获取文件内容,接口路径是 /files/{file_id}/content
。我们在客户端类中添加获取文件内容的方法。
@Nullable
public FileContentResponseBody getFileContent(String fileId) throws IOException {
if (!StringUtils.hasText(fileId)) {
return null;
}
// 构建请求体
Request request =
new Request.Builder()
.url(this.baseUrl + "/files/" + fileId + "/content")
.addHeader("Authorization", "Bearer " + this.accessToken)
.get()
.build();
// 发起请求
final Call call = this.httpClient.newCall(request);
try (Response response = call.execute()) {
ResponseBody body = response.body();
if (response.isSuccessful()) {
// 请求成功
if (Objects.nonNull(body)) {
String content = body.string();
return this.objectMapper.readValue(content, FileContentResponseBody.class);
}
} else {
// 请求失败
if (Objects.nonNull(body)) {
String content = body.string();
throw new MoonShotRequestException(content);
}
}
}
return null;
}
响应对象如下,其中 content 即是文件的内容。
@Data
@ToString
public class FileContentResponseBody {
private String content;
private String fileType;
private String filename;
private String title;
private String type;
}
另外也支持文件的 Restful 查询列表、详情和删除接口,路径分别是 GET /files
、 GET /files/{file_id}
和 DELETE /files/{file_id}
。
上面提供了发起对话的接口对接,同时也支持文件上传,就可以自己实现一个和官方对话窗口一样的界面了。
最后,提醒大家注意 Kimi 的 API 已经开始收费了,具体的收费政策请参考官网。
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。