前言
最近国产大模型 KIMI 备受关注。目前国内大模型领域竞争激烈,我们本次不与其他国家产品或收费服务对比,仅针对免费的 KIMI、文心一言和通义千问进行横向测试。
本文通过六个实际场景问题,包括天气查询、房产政策、法律问答及 Java 代码生成(部门树构建、数据处理、多线程下载),对 KIMI、文心一言和通义千问三款国产大模型进行了横向对比。测试结果显示,各模型在不同任务上表现各异:文心一言在实时信息获取和逻辑条理性上表现较好;KIMI 依赖搜索引擎,部分数据存在滞后或错误;通义千问在代码生成简洁性和执行稳定性上具有一定优势。文章旨在客观评估三款模型的实际应用能力,供开发者参考。

最近国产大模型 KIMI 备受关注。目前国内大模型领域竞争激烈,我们本次不与其他国家产品或收费服务对比,仅针对免费的 KIMI、文心一言和通义千问进行横向测试。
在此说明:本文测试题目为原创,测试结果仅代表这三家大模型在特定问题上的表现,不能全面代表整体优劣程度。




今天西湖边实际天气实拍:温度 18-28 度,多云,15 点 10% 概率有雨,空气有点污染。
对比分析:
文心一言 > KIMI > 通义千问



背景:杭州在 3 月 14 日取消了二手房限购。
KIMI > 文心一言 > 通义千问



法律问题测试。
文心一言 > KIMI > 通义千问
[
{"id": 1, "pid": 0, "name": "产品研发中心"},
{"id": 2, "pid": 1, "name": "后端"},
{"id": 3, "pid": 1, "name": "前端"},
{"id": 4, "pid": 2, "name": "JAVA"},
{"id": 5, "pid": 3, "name": "JS"},
{"id": 6, "name": "测试数据"}
]
注意最后一条是故意造的错误数据。请通过 id=pid 合成部门树,给出 JAVA 代码。
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
class Department {
int id;
String name;
List<Department> children = new ArrayList<>();
public Department(int id, String name) {
this.id = id;
this.name = name;
}
public void addChild(Department child) {
children.add(child);
}
public static Department fromJson(Map<String, Object> jsonMap) {
return new Department((Integer) jsonMap.get("id"), (String) jsonMap.get("name"));
}
}
public class DepartmentTreeBuilder {
public static Department buildTree(List<Map<String, Object>> departments) {
Map<Integer, Department> departmentMap = new HashMap<>();
List<Department> roots = new ArrayList<>();
for (Map<String, Object> deptJson : departments) {
int id = (Integer) deptJson.get("id");
String name = (String) deptJson.get("name");
Department dept = Department.fromJson(deptJson);
departmentMap.put(id, dept);
if (deptJson.get("pid").equals(0)) {
roots.add(dept);
}
}
for (Map<String, Object> deptJson : departments) {
int id = (Integer) deptJson.get("id");
int pid = (Integer) deptJson.get("pid");
if (pid != 0 && departmentMap.containsKey(pid)) {
Department dept = departmentMap.get(id);
Department parent = departmentMap.get(pid);
if (parent != null) {
parent.addChild(dept);
}
}
}
return roots.isEmpty() ? null : roots.get(0);
}
public static void main(String[] args) {
List<Map<String, Object>> departments = new ArrayList<>();
departments.add(Map.of("id", 1, "pid", 0, "name", "产品研发中心"));
departments.add(Map.of("id", 2, "pid", 1, "name", "后端"));
departments.add(Map.of("id", 3, "pid", 1, "name", "前端"));
departments.add(Map.of("id", 4, "pid", 2, "name", "JAVA"));
departments.add(Map.of("id", 5, "pid", 3, "name", "JS"));
Department root = buildTree(departments);
String json = new Gson().toJson(root);
System.out.println(json);
}
}
public class DepartmentTreeBuilder {
public static void main(String[] args) {
List<Department> departments = new ArrayList<>();
departments.add(new Department(1, 0, "产品研发中心"));
departments.add(new Department(2, 1, "后端"));
departments.add(new Department(3, 1, "前端"));
departments.add(new Department(4, 2, "JAVA"));
departments.add(new Department(5, 3, "JS"));
departments.add(new Department(6, 0, "测试数据")); // 注意这里 pid 应为 0 或其他实际父节点 id,否则无法正确构建树形结构
List<Department> tree = buildDepartmentTree(departments);
for (Department dept : tree) {
printDepartmentTree(dept, 0);
}
}
public static List<Department> buildDepartmentTree(List<Department> departments) {
Map<Integer, Department> idMap = new HashMap<>();
List<Department> tree = new ArrayList<>();
// 首先,将所有部门按照 id 放入 Map 中,方便后续查找
for (Department dept : departments) {
idMap.put(dept.getId(), dept);
}
// 遍历所有部门,构建树形结构
for (Department dept : departments) {
if (dept.getPid() == 0) {
// pid 为 0 的作为树的根节点
tree.add(dept);
} else {
// 查找父节点,并添加到父节点的子节点列表中
Department parent = idMap.get(dept.getPid());
if (parent != null) {
if (parent.getChildren() == null) {
parent.setChildren(new ArrayList<>());
}
parent.getChildren().add(dept);
}
}
}
return tree;
}
public static void printDepartmentTree(Department dept, int level) {
for (int i = 0; i < level; i++) {
System.out.print("--");
}
System.out.println(dept.getName());
if (dept.getChildren() != null) {
for (Department child : dept.getChildren()) {
printDepartmentTree(child, level + 1);
}
}
}
static class Department {
private int id;
private int pid;
private String name;
private List<Department> children;
public Department(int id, int pid, String name) {
this.id = id;
this.pid = pid;
this.name = name;
}
public int getId() {
return id;
}
public int getPid() {
return pid;
}
public String getName() {
return name;
}
public List<Department> getChildren() {
return children;
}
public void setChildren(List<Department> children) {
this.children = children;
}
}
}
class Department {
int id;
int pid;
String name;
List<Department> children = new ArrayList<>();
Department(int id, int pid, String name) {
this.id = id;
this.pid = pid;
this.name = name;
}
@Override
public String toString() {
return "Department{" +
"id=" + id +
", pid=" + pid +
", name='" + name + '\'' +
", children=" + children +
'}';
}
static Department buildDepartmentTree(List<Department> depts) {
Map<Integer, Department> map = new HashMap<>();
for (Department dept : depts) {
map.put(dept.id, dept);
}
for (Department dept : depts) {
if (map.containsKey(dept.pid)) {
map.get(dept.pid).children.add(dept);
}
}
// 返回根节点(pid 为 0 的部门)
return map.values().stream()
.filter(d -> d.pid == 0)
.findFirst()
.orElse(null);
}
public static void main(String[] args) {
List<Department> depts = new ArrayList<>();
depts.add(new Department(1, 0, "产品研发中心"));
depts.add(new Department(2, 1, "后端"));
depts.add(new Department(3, 1, "前端"));
depts.add(new Department(4, 2, "JAVA"));
depts.add(new Department(5, 3, "JS"));
depts.add(new Department(6, -1, "测试数据")); // 设置无效的 pid 为 -1,表示该节点无父节点
Department root = buildDepartmentTree(depts);
System.out.println(root);
}
}
通义千问 > 文心一言 > KIMI
要求:帮我把这两条部门数据去除根组织和部门 ID,将部门名用 - 分隔输出,请给出基于 JDK8 的 JAVA 代码。
输入数据:
EveryOne#根组织/50#侠客集团/96332#工作机动/103965#cy 专属组织/103967#策划部门十一个非常厉害的部门你知道吗
EveryOne#根组织/50#侠客集团/96332#工作机动/103965#cy 专属组织/104014#技术部门懂技术会生活的好部门呢你想来吗

运行结果:
侠客集团 -96332#工作机动 -103965#cy 专属组织 -103967#策划部门十一个非常厉害的部门你知道吗
侠客集团 -96332#工作机动 -103965#cy 专属组织 -104014#技术部门懂技术会生活的好部门呢你想来吗

运行结果:
根组织 - 侠客集团 - 工作机动 -cy 专属组织 - 策划部门十一个非常厉害的部门你知道吗
根组织 - 侠客集团 - 工作机动 -cy 专属组织 - 技术部门懂技术会生活的好部门呢你想来吗

运行结果:
cy 专属组织 - 策划部门十一个非常厉害的部门你知道吗
cy 专属组织 - 技术部门懂技术会生活的好部门呢你想来吗
文心一言 > 通义千问 > KIMI
输入:
http://pic.people.com.cn/NMediaFile/2024/0324/MAIN17112471760803BC3VHT0EB.jpg
http://pic.people.com.cn/NMediaFile/2024/0324/MAIN1711247176118UWI7FVR0OP.jpg
要求:请基于 JDK8 编写代码将输入的 URL 下载到 C://img 下,文件名从 URL 中获取,要考虑 URL 中获取文件名重复问题,要求使用多线程下载。
import java.io.*;
import java.net.URL;
import java.nio.file.*;
import java.util.*;
import java.util.concurrent.*;
public class ImageDownloader {
private static final String DOWNLOAD_DIR = "C://img"; // 下载目录
public static void main(String[] args) {
List<String> imageUrls = Arrays.asList(
"http://pic.people.com.cn/NMediaFile/2024/0324/MAIN17112471760803BC3VHT0EB.jpg",
"http://pic.people.com.cn/NMediaFile/2024/0324/MAIN1711247176118UWI7FVR0OP.jpg"
);
ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
try {
List<Future<Path>> futures = new ArrayList<>();
for (String imageUrl : imageUrls) {
Path path = Paths.get(DOWNLOAD_DIR, getFileNameFromUrl(imageUrl));
futures.add(executorService.submit(() -> downloadImage(imageUrl, path)));
}
for (Future<Path> future : futures) {
future.get(); // 等待所有下载任务完成
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
private static String getFileNameFromUrl(String url) {
String fileName = url.substring(url.lastIndexOf('/') + 1);
return fileName;
}
private static Path downloadImage(String imageUrl, Path path) throws IOException, InterruptedException {
URL url = new URL(imageUrl);
try (InputStream in = url.openStream();
OutputStream out = new FileOutputStream(path.toFile())) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
Files.createDirectories(path.getParent()); // 确保父目录存在
Files.move(path, path.resolveSibling(getUniqueFileName(path.getFileName().toString()))); // 重命名文件以避免重复
}
return path;
}
private static String getUniqueFileName(String fileName) {
Path path = Paths.get(DOWNLOAD_DIR, fileName);
int counter = 1;
while (Files.exists(path)) {
path = Paths.get(DOWNLOAD_DIR, fileName.replace(".jpg", "(" + counter + ").jpg"));
counter++;
}
return path.getFileName().toString();
}
}
import java.io.BufferedInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ImageDownloader {
private static final String TARGET_DIRECTORY = "C://img";
private static final int THREAD_POOL_SIZE = 5; // 可以根据需要调整线程池大小
public static void main(String[] args) {
// 输入的 URL 列表
List<String> urls = new ArrayList<>();
urls.add("http://pic.people.com.cn/NMediaFile/2024/0324/MAIN17112471760803BC3VHT0EB.jpg");
urls.add("http://pic.people.com.cn/NMediaFile/2024/0324/MAIN1711247176118UWI7FVR0OP.jpg");
ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
for (String url : urls) {
executor.submit(() -> downloadImage(url));
}
executor.shutdown();
try {
while (!executor.isTerminated()) {
// 等待所有任务完成
Thread.sleep(100);
}
System.out.println("All images downloaded.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void downloadImage(String urlString) {
try {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
try (InputStream in = new BufferedInputStream(connection.getInputStream());
FileOutputStream fos = getUniqueFileOutputStream(urlString)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
} finally {
connection.disconnect();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static FileOutputStream getUniqueFileOutputStream(String urlString) throws IOException {
String fileName = getFileNameFromUrl(urlString);
String filePath = Paths.get(TARGET_DIRECTORY, fileName).toString();
// 确保文件名的唯一性
Path path = Paths.get(filePath);
if (Files.exists(path)) {
fileName = getUniqueFileName(fileName);
filePath = Paths.get(TARGET_DIRECTORY, fileName).toString();
}
Path targetPath = Paths.get(filePath);
Files.createDirectories(targetPath.getParent());
return new FileOutputStream(targetPath.toFile());
}
private static String getFileNameFromUrl(String url) {
String[] parts = url.split("/");
return parts[parts.length - 1];
}
private static String getUniqueFileName(String fileName) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] hashBytes = md.digest(fileName.getBytes());
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return fileName + "_" + hexString.toString();
}
}
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ImageDownloader {
private static final String DOWNLOAD_DIR = "C://img";
public static void main(String[] args) throws IOException {
String[] imageUrls = {
"http://pic.people.com.cn/NMediaFile/2024/0324/MAIN17112471760803BC3VHT0EB.jpg",
"http://pic.people.com.cn/NMediaFile/2024/0324/MAIN1711247176118UWI7FVR0OP.jpg"
};
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小的线程池
for (String imageUrl : imageUrls) {
Runnable worker = () -> downloadImage(imageUrl);
executor.execute(worker);
}
executor.shutdown(); // 关闭线程池,等待所有任务完成
while (!executor.isTerminated()) {
// 等待所有线程执行完毕
}
System.out.println("所有图片下载完成!");
}
private static void downloadImage(String imageUrl) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.connect();
if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {
String fileName = extractFileNameFromUrl(url);
Path targetPath = Paths.get(DOWNLOAD_DIR, ensureUniqueFileName(fileName));
Files.createDirectories(targetPath.getParent()); // 如果目录不存在则创建
try (ReadableByteChannel rbc = Channels.newChannel(connection.getInputStream());
FileOutputStream fos = new FileOutputStream(targetPath.toFile())) {
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
}
System.out.println("成功下载图片:" + targetPath.toString());
} else {
System.out.println("无法下载图片,响应码:" + connection.getResponseCode());
}
} catch (IOException e) {
System.err.println("图片下载失败:" + imageUrl);
e.printStackTrace();
}
}
// 从 URL 中提取文件名
private static String extractFileNameFromUrl(URL url) {
String path = url.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
// 确保文件名唯一,比如通过添加时间戳后缀
private static String ensureUniqueFileName(String fileName) {
Path path = Paths.get(DOWNLOAD_DIR, fileName);
if (!Files.exists(path)) {
return fileName;
}
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss_SSS");
LocalDateTime now = LocalDateTime.now();
String timestamp = now.format(formatter);
String extension = fileName.substring(fileName.lastIndexOf('.'));
return fileName.replace(extension, "_" + timestamp + extension);
}
}
通义千问 > 文心一言 > KIMI
从这六道题目来看,KIMI 并没有达到预期的惊艳效果。大体结论如下:
关于其火爆的原因可能涉及市场运作,但作为开发者,应关注技术本身的实用性与稳定性。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online