为系统接入 Kimi AI 智能大模型(附源码)

前言

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 来构建一个请求客户端。

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 /filesGET /files/{file_id}DELETE /files/{file_id}

上面提供了发起对话的接口对接,同时也支持文件上传,就可以自己实现一个和官方对话窗口一样的界面了。

在这里插入图片描述

最后,提醒大家注意 Kimi 的 API 已经开始收费了,具体的收费政策请参考官网。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值