原文:赵侠客
前言
最近国产大模型KIMI爆了大部分人应该都知道了,从我个人的感受来看这次KIMI爆了我不是从技术领域接触到的,而是从各种金融领域接触到的。目前国内大模型可以说是百模大战,前几年新能源大战,今年资本割完韭菜后留给我们的是一家家倒闭或者即将要倒闭的车企,今年有一句话听了让人非常的无奈:”如果前几年你买了房子,又买了车子,你不仅要担心你的房子什么时候会爆雷还要担心你的车子什么时候会爆雷”。过几年大模型爆雷注定是不可避免不的,想想当年火爆的OFO到现在200多块钱的押金还没退给我。每次风口过后资本割完韭菜留给我们的都是一地鸡毛,所以我们一定要有自己的判断力,真正国货之光我们要去支持,那些套壳只为玩资本游戏的产品我们千万不要跟风,不要理会它就好了。关于KIMI在资本市场已经火了,甚至还产生了KIMI概念股。那么KIMI到底怎么样?听别人说1000遍不如自己实际用一下,而且国产是开箱即用,比国外的使用门槛低多了。这次我们也不和外国的对比,也不和收费的对比,我们就和免费的文心一言和通义千问对比。
在此申明:本文6道题目为本人原创,在本文发表前网上是没有的,测试结果只能代表这三家大模型在这六道题目上的好坏,不能全面代表这三家大模型整体的优劣程度。
问题一:我今天陪女朋友去杭州西湖游玩应该穿什么衣服
KIMI
添加图片注释,不超过 140 字(可选)
文心一言
添加图片注释,不超过 140 字(可选)
通义千问
添加图片注释,不超过 140 字(可选)
对比
添加图片注释,不超过 140 字(可选)
今天西湖边实际天气实拍
首先我们看一下杭州西湖今天的天气,温度是18-28度,多云,15点10%概率有雨,空气有点污染,接下来我们对比三家AI给出的答案:
-
通义千问:最拉垮,主要是没能获取今天杭州西湖的天气,给出的答案只是根据历史经验,所以没有什么参考价值;
-
文心一言:给出了杭州西湖的天气,包括气温、风力、空气质量为轻度污染都完全符合实际情况,而且建议结果分为1、2、3比较有条理,还对衣服的颜色给出来具体的配色,可以说非常好;
-
KIMI:先从网上找到了三份关于杭州天气的网页,然后根据网页内容给出建议,只不过它所查询的天气并不是今天3月24号的,所以给出的答案也就有问题了,最大错误就是空气质量,它给出的是空气质量很好,实际情况是有点污染的,其次结果也没有百度文心一言有条理,对衣服的颜色也没有给出具体的配色,可以看出在这个问题上KIMI是比文心一言差不少了。
小结
文心一言>KIMI>通义千问
问题二:我和老婆户口在杭州,有三个娃,请问今天我在杭州西湖区可以买几套二手房?
KIMI
添加图片注释,不超过 140 字(可选)
文心一言
添加图片注释,不超过 140 字(可选)
通义千问
添加图片注释,不超过 140 字(可选)
对比
杭州在3月14日取消了二手房限购。
-
通义千问:最拉垮的还是通义千问题,给出政策还是2022年5月17日的,也没给出具体可以购买几套房;
-
KIMI:唯一给出正确答案。KIMI这得益于他是实时从网上搜索的,而且正确搜索到了杭州取消二手房限购的网页,所以给出了正确的答案。
-
文心一言:应该没有更新杭州3月14取消二手房限购的内容,所以给出是的3月14日之前的答案,回答是错误的。
小结
KIMI>文心一言>通义千问
问题三、河北邯郸3名不满十四周岁初中生杀害同班同学并掩埋,请问这三名初中生在法律上会受到什么惩罚?
KIMI
添加图片注释,不超过 140 字(可选)
文心一言
添加图片注释,不超过 140 字(可选)
通义千问
添加图片注释,不超过 140 字(可选)
对比
法律问题应该是送分题。
-
通义千问:还是最拉垮,没有给出答案;
-
文心一言:和KIMI回答结果是一样的,但是在细节还是文心一言更强一点,给了具体的法律依据是《刑法》第十七条;
-
KIMI:回答的和文心一言基本差不多。
小结
文心一言>KIMI>通义千问
问题四、使用JAVA代码合成部门树
[{
"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代码
注意这里最后一条是故意造的错误数据。
KIMI
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); // Assuming there is only one root
}
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);
}
}
对比
-
通义千问:代码能正确执行,从写法上面来看通义千问代码量最少,还用了lambda表达示,看起来更高级,在代码生成领域终于胜出了;
-
KIMI:这一题只有KIMI给出答案是错的,Department少了pid;
-
文心一言:代码能正确执行,没有通义千问生成的好。
小结
通义千问>文心一言>KIMI
问题五、使用JAVA处理数据
帮我把这两条部门数据去除根组织和部门ID,将部门名用-分隔输出,请给出基于JDK8的JAVA代码
EveryOne#根组织/50#侠客集团/96332#工作机动/103965#cy专属组织/103967#策划部门十一个非常厉害的部门你知道吗
EveryOne#根组织/50#侠客集团/96332#工作机动/103965#cy专属组织/104014#技术部门懂技术会生活的好部门呢你想来吗
KIMI
添加图片注释,不超过 140 字(可选)
运行结果:
侠客集团-96332#工作机动-103965#cy专属组织-103967#策划部门十一个非常厉害的部门你知道吗
侠客集团-96332#工作机动-103965#cy专属组织-104014#技术部门懂技术会生活的好部门呢你想来吗
文心一言
添加图片注释,不超过 140 字(可选)
运行结果:
根组织-侠客集团-工作机动-cy专属组织-策划部门十一个非常厉害的部门你知道吗
根组织-侠客集团-工作机动-cy专属组织-技术部门懂技术会生活的好部门呢你想来吗
通义千问
添加图片注释,不超过 140 字(可选)
运行结果:
cy专属组织-策划部门十一个非常厉害的部门你知道吗
cy专属组织-技术部门懂技术会生活的好部门呢你想来吗
对比
-
通义千问:多去除了“侠客集团”和工作动机部门;
-
文心一言:只是没有去除根组织,其它都是正确了,表现最好;
-
KIMI:输出错误,没有去除部门ID,表现最差。
小结
文心一言>通义千问>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中获取文件名重复问题,要求使用多线程下载
KIMI
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没有考虑下载目录C://img不存在报错问题,文心一言和通义千问都考虑了;
-
文件名重复问题:KIMI使用后缀加(N),文心一言是使用文件名MD5,通义千问使用时间戳。KIMI思路最正确可是写法错了,文心一言使用文件名MD5思路就错了,通义千问使用时间虽然不完美,但是是可以解决大部分URL文件名相同问题。
小结
通义千问>文心一言>KIMI
总结
从这六道题目来看KIMI并没有多么的让人惊艳,大体上我们可以得出以下结论:
-
依赖数据源;可能是KIMI自己没有能力收集大量数据和花大价钱买显卡去训练,所以回答结果非常依赖它得到的数据;
-
通用问题依赖搜索引擎;想做通用大模型肯定要有大量数据,KIMI使用实时搜索去获取数据源,再给出结果。目前不知道它使用的是哪家搜索引擎,如果非常依赖搜索引擎那在通用大模型领域可能是硬伤,因为国内搜索引擎老大是谁大家都知道;
-
在代码生成领域不是很成熟;这一点应该不用太担心,国为程序员是第一波接触大模型的人,谁手上还没有几个墙外的产品,根本看不上国内的这些产品;
-
“百模大战”中的一员;给我的感觉它只是“百模大战”中的一员,并没有鹤立鸡群,能从“百模大战“中脱颖而出的惊艳。
最后关于它为什么会火起来这个就不得而知了,不过大家可以思考一下去年火起来的淄博烧烤、过年期间火起来的哈尔滨、最近正在火的天水麻辣烫,表明上看谁能火起来是门玄学,不过如果真的去深挖这些现象级火起来网红城市,你会发现玄学后面都是人(资本)在操控。