Kimi,是月之暗面于2023年10月推出的一款智能助手,主要应用场景为专业学术论文的翻译和理解、辅助分析法律问题、快速理解API开发文档等,是全球首个支持输入20万汉字的智能助手产品

1.登录,获取API Key

打开网址Moonshot AI - 开放平台 ,可以看到官网比较简洁,有两部分构成,api文档和用户中心

点击用户中心,可以用微信登录,然后在API Key管理中新建一个key,记录下来

2.引入依赖 

由于跟kimi对接需要用到SSE(Server Send Event),我们使用okhttp库

        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.25</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.10.0</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp-sse</artifactId>
            <version>4.10.0</version>
        </dependency>

3.实现简单的对话

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class Message {

    private String role;

    private String content;

}
public enum RoleEnum {
    system,
    user,
    assistant;
}

public class MoonshotAiUtils {
    private static final String API_KEY = "api key";
    private static final String MODELS_URL = "https://api.moonshot.cn/v1/models";
    private static final String FILES_URL = "https://api.moonshot.cn/v1/files";
    private static final String ESTIMATE_TOKEN_COUNT_URL = "https://api.moonshot.cn/v1/tokenizers/estimate-token-count";
    private static final String CHAT_COMPLETION_URL = "https://api.moonshot.cn/v1/chat/completions";

    public static String getModelList() {
        return getCommonRequest(MODELS_URL)
                .execute()
                .body();
    }

    public static String uploadFile(@NonNull File file) {
        return getCommonRequest(FILES_URL)
                .method(Method.POST)
                .header("purpose", "file-extract")
                .form("file", file)
                .execute()
                .body();
    }

    public static String getFileList() {
        return getCommonRequest(FILES_URL)
                .execute()
                .body();
    }

    public static String deleteFile(@NonNull String fileId) {
        return getCommonRequest(FILES_URL + "/" + fileId)
                .method(Method.DELETE)
                .execute()
                .body();
    }

    public static String getFileDetail(@NonNull String fileId) {
        return getCommonRequest(FILES_URL + "/" + fileId)
                .execute()
                .body();
    }

    public static String getFileContent(@NonNull String fileId) {
        return getCommonRequest(FILES_URL + "/" + fileId + "/content")
                .execute()
                .body();
    }

    public static String estimateTokenCount(@NonNull String model, @NonNull List<Message> messages) {
        String requestBody = new JSONObject()
                .putOpt("model", model)
                .putOpt("messages", messages)
                .toString();
        return getCommonRequest(ESTIMATE_TOKEN_COUNT_URL)
                .method(Method.POST)
                .header(Header.CONTENT_TYPE, ContentType.JSON.getValue())
                .body(requestBody)
                .execute()
                .body();
    }

    @SneakyThrows
    public static String chat(@NonNull String model, @NonNull List<Message> messages) {
        StringBuilder sb = new StringBuilder();
        String requestBody = new JSONObject()
                .putOpt("model", model)
                .putOpt("messages", messages)
                .putOpt("stream", true)
                .toString();
        Request okhttpRequest = new Request.Builder()
                .url(CHAT_COMPLETION_URL)
                .post(RequestBody.create(requestBody, MediaType.get(ContentType.JSON.getValue())))
                .addHeader("Authorization", "Bearer " + API_KEY)
                .build();
        Call call = new OkHttpClient().newCall(okhttpRequest);
        Response okhttpResponse = call.execute();
        BufferedReader reader = new BufferedReader(okhttpResponse.body().charStream());
        try {
            String line;
            while ((line = reader.readLine()) != null) {
                if (StrUtil.isBlank(line)) {
                    continue;
                }
                if (JSONUtil.isTypeJSON(line)) {
                    Optional.of(JSONUtil.parseObj(line))
                            .map(x -> x.getJSONObject("error"))
                            .map(x -> x.getStr("message"))
                            .ifPresent(x -> System.out.println("error: " + x));
                    JSONObject jsonObject = JSONUtil.parseObj(line);
                    throw new ServiceFailException(jsonObject.getJSONObject("error").getStr("message"));
                }
                line = StrUtil.replace(line, "data: ", StrUtil.EMPTY);
                if (StrUtil.equals("[DONE]", line) || !JSONUtil.isTypeJSON(line)) {
                    return sb.toString();
                }
                Optional.of(JSONUtil.parseObj(line))
                        .map(x -> x.getJSONArray("choices"))
                        .filter(CollUtil::isNotEmpty)
                        .map(x -> (JSONObject) x.get(0))
                        .map(x -> x.getJSONObject("delta"))
                        .map(x -> x.getStr("content"))
                        .ifPresent(x -> sb.append(x));
            }
            return sb.toString();
        } finally {
            IoUtil.close(reader);
        }
    }

    private static HttpRequest getCommonRequest(@NonNull String url) {
        return HttpRequest.of(url).header(Header.AUTHORIZATION, "Bearer " + API_KEY);
    }
}

@RestController
@RequestMapping("/kimi")
@Tag(name = "kimi管理")
public class KimiController extends BaseController {
    // 演示用,实际要存入用户session
    private List<Message> messages = new ArrayList<>();

    @Operation(summary = "聊天")
    @GetMapping("chat")
    public Result chat(String content) {
        Message message = new Message(RoleEnum.user.name(), content);
        messages.add(message);
        // 模型列表 https://platform.moonshot.cn/docs/intro#%E6%A8%A1%E5%9E%8B%E5%88%97%E8%A1%A8
        String result = MoonshotAiUtils.chat("moonshot-v1-8k", messages);
        return resultOk(result);
    }
}

代码都挺简单的,需要注意的是用户的多次问答需要放到一个list中一起发送给kimi,这样kimi才能根据上下文回答问题

4.提取文档摘要 

提取文档摘要我们先要把文档上传到kimi获取一个content,再调用聊天接口给提示词就行了

    @Operation(summary = "提取文档摘要")
    @GetMapping("docSummary")
    public Result docSummary() {
        String hint = "请简要描述文档中的内容";
        String content = MoonshotAiUtils.uploadFile(new File("d:/read.docx"));
        List<Message> messages = CollUtil.newArrayList(
                new Message(RoleEnum.system.name(), content),
                new Message(RoleEnum.user.name(), hint)
        );
        String result = MoonshotAiUtils.chat("moonshot-v1-32k", messages);
        return resultOk(result);
    }

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐