ddl战士——大模型评测作业

ddl战士 2025-10-27 21:45:55
这个作业属于哪个课程2501_CS_SE_FZU
这个作业要求在哪里软件工程——大模型评测作业
这个作业的目标通过设计自动化测试和量化评估来对比分析大模型性能的软件工程实践方法,并培养从技术实现到产品商业化的全链条思维。
其他参考文献《构建之法》、大模型基准与评测综述(CAICT 报告) — 中国信息通信研究院、Evaluating Large Language Models Survey

目录

  • 第一部分 调研,评测
  • 1. 模型体验概述
  • 1.1 Qwen-Max
  • 1.1.1 简介
  • 1.1.2 简单体验
  • 1.1.3 优缺点分析及改进意见
  • 1.1.4 用户采访
  • 1.2 GLM-4-Flash
  • 1.2.1 简介
  • 1.2.2 简单体验
  • 1.2.3 优缺点分析及改进意见
  • 1.2.4 用户采访
  • 2. 自动化测试
  • 2.1 脚本设计
  • 2.2 脚本实现
  • 2.3 脚本分析
  • 3. 测试结果整理
  • 4. 结论
  • 第二部分 分析
  • 1. 同类产品对比排名
  • 2. 软件工程方面的建议
  • 3. 市场概况
  • 4. 产品规划
  • 第三部分 团队绩效

第一部分 调研,评测

1. 模型体验概述

1.1 Qwen-Max

1.1.1 简介

Qwen-Max的定位是一个“全能型”的AI助手和生产力工具。其主要特点可以概括为:

  1. 顶尖的智能水平
    在语言理解、逻辑推理、多轮对话、代码生成、知识问答等核心能力上,都达到了业界领先水平。能够处理非常复杂和专业的任务,例如撰写长篇深度报告、进行多步骤的数学推导、理解和分析复杂的法律条文等。

  2. 超长的上下文处理能力
    支持高达128K tokens 的超长上下文窗口。这意味着它可以一次性处理一本数百页的书籍、一份冗长的财报或长达数小时的会议记录,并在整个上下文中保持高度的理解和一致性,非常适合进行深度文档分析和总结。

  3. 强大的多模态能力
    虽然核心是语言模型,但Qwen-Max具备出色的多模态理解与生成能力。它可以:
    图文识别:准确识别并理解上传的图片中的文字、表格、图表等信息。
    视觉问答:根据图片内容回答相关问题。
    文档处理:直接上传PDF、Word、PPT、Excel等文件,并从中提取和总结信息。
    多模态生成:虽然目前主要聚焦于“文生文”,但其多模态理解能力为更复杂的应用(如文生图、图生文)提供了坚实基础。

  4. 精准的指令遵循与高度可控性
    能够精确地理解用户的复杂指令,并按照要求的格式、风格和深度来生成内容,可控性非常强。

1.1.2 简单体验

img


提供功能选择

img


选择联网搜索时,提供示例问题

img

从截图可以清晰地看到用户与Qwen-Max模型的一次完整交互过程:

  1. 用户输入(提问):用户提出问题“介绍软件工程”。

  2. 模型处理与响应:模型接收并理解了用户的指令,然后在生成了关于“软件工程”的详细介绍。

用户希望了解“软件工程”,模型提供了一个结构清晰、内容准确的概述,涵盖了该学科的核心概念和关键流程,对于需要快速了解某个概念或获取结构化信息的用户来说,这是一个非常高效且成功的解决方案。

1.1.3 优缺点分析及改进意见

优点:

  1. 模型能流畅地输出关于“软件工程”这样专业领域的系统性知识,说明其训练数据量巨大,覆盖了广泛的学术和行业知识。能够根据用户输入的片段(即使不完整),推断出用户意图并给出全面回答。

  2. 布局清晰,核心区域(输入框和输出区)突出,没有多余干扰元素。按钮(发送、点赞、点踩、搜索、新建对话)功能一目了然,易于操作。

  3. 核心功能强大,文本生成、问答、摘要、解释等核心NLP能力出色。反馈机制完善,提供点赞/点踩,有助于模型持续优化。
    本次关于“软件工程”的回答完全正确,术语和步骤都符合行业共识,内容组织有序,层次分明,没有明显的逻辑错误。

缺点:

  1. 模型有时会“编造”看似合理实则错误的信息。用户需要对关键信息进行交叉验证。

  2. 模型的知识截止于其训练数据的时间点,无法提供最新的行业动态或研究进展。

  3. 无多轮对话深度记忆,虽然能维持单次对话,但对于需要长期记忆上下文的复杂任务,表现可能受限。
    非绝对可靠,特别是在处理冷门、前沿或高度专业的问题时。

  4. 缺乏引用来源,回答中没有提供信息来源或参考文献,用户难以追溯和验证。

改进意见:

  1. 增加“引用来源”或“信息溯源”功能

  2. 对于高风险或存疑的信息,自动添加“请注意:此信息基于训练数据,建议核实”等提示语。

  3. 增加“复制”、“分享”、“导出”按钮,提供“重写/简化/扩展”等文本编辑指令。

  4. 提供“满意度评分”后的反馈闭环,例如·当用户点击“点踩”后,弹出一个简短表单,询问“哪里不满意?”(如“不准确”、“不完整”、“太啰嗦”等),收集结构化反馈用于模型迭代。

1.1.4 用户采访

采访用户背景

  • 专业:软件工程大四学生
  • 使用需求:提升编程效率,自动化复杂算法实现,以及优化软件测试和调试过程。

    在这里插入图片描述

1.2 GLM-4-Flash

1.2.1 简介

GLM-4-Flash 是智谱 AI推出的一款高性能、高性价比的大语言模型,属于 GLM-4 系列中的推理优化版本,专为高并发、低延迟、低成本的应用场景设计。以下是其核心特点:

  1. 定位与目标:
    轻量高效:在保持 GLM-4 核心能力的基础上,大幅优化推理速度和资源消耗。
    适合高频调用:适用于客服对话、内容摘要、智能助手、API 服务等需要快速响应和大规模部署的场景。
    成本友好:相比 GLM-4 或 GLM-4V 等全功能版本,适合预算敏感型应用。

  2. 核心能力:
    强大的中文理解与生成:在中文语境下表现优异,尤其擅长日常对话、知识问答、文本创作。
    多轮对话能力:支持上下文记忆,能维持连贯的交互体验。
    代码与逻辑推理:具备基础的代码生成和数学推理能力,虽略逊于 GLM-4 主版本,但足以应对常见任务。
    指令遵循性好:能较好地理解并执行结构化指令。

1.2.2 简单体验

img


提供示例问题,供用户体验

img


提供多种工具选择

img

从截图可以清晰地看到用户与GLM-4-Flash模型的一次完整交互过程:

  1. 用户输入(提问):用户提出要求“生成一首诗”。

  2. 模型处理与响应:模型接收并理解了用户的指令,然后在对话区即时生成并展示结果——一首七言诗。结果下方附带性能指标:“耗时 2s | 消耗 88 tokens”,让用户了解响应速度和资源消耗。

用户要求“生成一首诗”,AI成功输出了一首符合格律、意境连贯的七言诗,主题围绕“岁月”、“江山”等意象,满足了创作需求。对于“快速生成一首诗”这类创意性文本生成任务,该产品表现优秀,成功解决了用户的核心问题。

1.2.3 优缺点分析及改进意见

优点:

  1. 从生成诗歌的质量来看,模型训练数据量庞大且质量高,能准确掌握古典诗词的韵律、用词和意境。

  2. 极简设计,界面干净清爽,无多余元素,聚焦核心交互(输入/输出)。布局符合主流聊天工具习惯,新手无需学习即可上手。信息清晰,性能指标(耗时、token)和操作按钮(复制、重试)位置合理,提升可用性。

  3. 响应速度快,1秒内完成生成,适合实时交互场景。“Flash”命名暗示其轻快特性,适合快速问答。

  4. 语言准确,诗句语法正确,用词典雅,符合中文诗歌规范。

  5. 有重新生成按钮,方便用户使用。

缺点:

  1. 输入框和输出区域样式接近,缺乏视觉区分,长时间使用可能疲劳。

  2. 生成内容虽合格,但可能缺乏独特性和突破性,容易陷入“模板化”。

  3. 用户无法反馈结果好坏。

改进意见:

  1. 添加“常用指令”快捷按钮(如写诗、写文案、总结文章),点击后自动填充示例指令。

  2. 在每条AI回复下方增加满意/不满意”按钮。

  3. AI回复区域使用浅灰色背景或边框,与用户输入区区分。

  4. 关键信息(耗时、token)用图标+颜色强化。

1.2.4 用户采访

采访用户背景

  • 专业:软件工程大四学生
  • 使用需求:提升编程效率,自动化复杂算法实现,以及优化软件测试和调试过程。

    在这里插入图片描述

2. 自动化测试

2.1 脚本设计

脚本严格按照真实购车咨询的逻辑,自动与模型进行多轮交互:

  • 第1轮:用户陈述基本需求(“我最近打算买车,预算大约20万元左右,主要用于城市通勤,希望空间舒适、安全性高。”)

  • 第2轮:模型初步推荐车型

  • 第3~6轮:用户追问关键维度(动力、油耗/电耗、安全配置、保值率等)

  • 第7轮:要求模型输出结构化对比表格(Markdown格式)

  • 第8轮:要求模型给出最终推荐及理由

随后,根据下面的标准自动评分。

评分维度权重判断标准
是否包含推荐车型30%能否从文本中提取车型名称
关键因素覆盖度40%是否提及“安全、预算、空间、油耗、动力、舒适、通勤”等关键词
理由充分性20%是否有因果逻辑、篇幅是否足够(>100 字)
结构清晰度10%是否使用“首先/其次/最后”等逻辑连接词

img

2.2 脚本实现

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.io.File;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

public class CarBuyingSimulator {

    private static final ObjectMapper objectMapper = new ObjectMapper();
    private static final HttpClient httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(30))
            .build();

    //API
    private static final String QWEN_API_KEY = "123";
    private static final String GLM_API_KEY = "123";

    // 车型匹配模式
    private static final Map<String, List<String>> CAR_BRANDS = new ConcurrentHashMap<>();
    static {
        CAR_BRANDS.put("比亚迪", Arrays.asList("宋PLUS", "秦PLUS", "汉", "海豹", "唐", "元PLUS"));
        CAR_BRANDS.put("特斯拉", Arrays.asList("Model 3", "Model Y"));
        CAR_BRANDS.put("丰田", Arrays.asList("凯美瑞", "卡罗拉", "雷凌", "RAV4", "汉兰达"));
        CAR_BRANDS.put("本田", Arrays.asList("雅阁", "思域", "CR-V", "皓影", "飞度"));
        CAR_BRANDS.put("大众", Arrays.asList("朗逸", "帕萨特", "迈腾", "途观", "探岳"));
        CAR_BRANDS.put("吉利", Arrays.asList("星越L", "博越", "帝豪", "缤越"));
        CAR_BRANDS.put("蔚来", Arrays.asList("ET5", "ET7", "ES6"));
        CAR_BRANDS.put("小鹏", Arrays.asList("P5", "P7", "G3", "G9"));
    }

    public static void main(String[] args) throws Exception {
        if (QWEN_API_KEY.trim().isEmpty() || GLM_API_KEY.trim().isEmpty()) {
            System.err.println(" 请先在代码中设置有效的 QWEN_API_KEY 和 GLM_API_KEY!");
            return;
        }

        int repeatCount = 10; // 总测试轮数
        Map<String, List<DetailedResult>> allResults = new LinkedHashMap<>();
        allResults.put("qwen", new ArrayList<>());
        allResults.put("glm", new ArrayList<>());

        for (int run = 1; run <= repeatCount; run++) {
            System.out.println("\n" + "=".repeat(70));
            System.out.println(" 第 " + run + " 轮测试");
            System.out.println("=".repeat(70));

            for (String model : Arrays.asList("qwen", "glm")) {
                try {
                    DetailedResult dr = simulateCarBuying(model, run);
                    allResults.get(model).add(dr);
                    System.out.printf(" %s 第 %d 轮完成,评分: %.1f\n", model.toUpperCase(), run, dr.score * 100);
                } catch (Exception e) {
                    System.err.println(" " + model + " 第 " + run + " 轮失败: " + e.getMessage());
                    allResults.get(model).add(new DetailedResult(model, new ArrayList<>(), "", 0.0,
                            new ArrayList<>(), null, new EvaluationResult(0.0, false, Arrays.asList("异常")), new ArrayList<>()));
                }
                Thread.sleep(5000); // 避免 API 限流
            }
        }

        generateFinalReport(allResults);
    }

    private static DetailedResult simulateCarBuying(String modelName, int run) throws Exception {
        List<Message> conversation = new ArrayList<>();
        List<RoundResult> roundResults = new ArrayList<>();

        // 1: 购车意图
        String intent = "我最近打算买车,预算大约20万元左右,主要用于城市通勤,希望空间舒适、安全性高。";
        conversation.add(new Message("user", intent));
        System.out.println(" 用户: " + intent);

        // 2: 初步推荐
        String resp1 = callModel(modelName, conversation);
        if (resp1.isEmpty()) {
            throw new RuntimeException("模型未返回有效响应");
        }
        System.out.println(" " + modelName + ": " + truncate(resp1, 150));
        conversation.add(new Message("assistant", resp1));

        List<String> candidates = extractCarModels(resp1);
        System.out.println(" 候选车型: " + candidates);
        roundResults.add(new RoundResult("初步推荐", resp1, candidates));

        // 3: 补充需求(多轮)
        List<String> keyPoints = Arrays.asList(
                "请重点分析这些车型的动力性能(如发动机马力、加速能力)。",
                "请比较它们的油耗或电耗表现,适合城市通勤吗?",
                "安全配置方面(如气囊数量、主动安全系统)如何?",
                "这些车的保值率和品牌口碑怎么样?"
        );

        for (int i = 0; i < keyPoints.size(); i++) {
            String point = keyPoints.get(i);
            conversation.add(new Message("user", point));
            System.out.println(" 用户: " + point);

            String resp = callModel(modelName, conversation);
            if (resp.isEmpty()) {
                resp = "(无响应)";
            } else {
                System.out.println(" " + modelName + ": " + truncate(resp, 150));
            }
            conversation.add(new Message("assistant", resp));

            List<String> newCandidates = extractCarModels(resp);
            if (!newCandidates.isEmpty()) {
                candidates = mergeCandidates(candidates, newCandidates);
            }
            roundResults.add(new RoundResult("需求分析-" + (i + 1), resp, new ArrayList<>(candidates)));

            Thread.sleep(2000);
        }

        // 4:请求性能参数对比表
        String compareRequest = "请以 Markdown 表格形式对比候选车型的关键参数,必须包含以下列:" +
                "车型、动力类型(燃油/混动/纯电)、发动机/电机功率(kW)、0-100km/h加速(s)、" +
                "WLTC续航(km)或油耗(L/100km)、轴距(mm)、安全配置(气囊数+主动安全功能)、指导价(万元)。" +
                "只输出表格,不要任何解释文字。";

        conversation.add(new Message("user", compareRequest));
        System.out.println(" 用户: " + compareRequest);

        String compareResp = callModel(modelName, conversation);
        CarComparisonTable parsedTable = null;

        if (compareResp != null && !compareResp.trim().isEmpty()) {
            try {
                parsedTable = parseComparisonTable(compareResp);
                if (parsedTable != null && !parsedTable.rows.isEmpty()) {
                    System.out.println("\n 模型返回的对比表:");
                    System.out.println(parsedTable.toMarkdown());
                } else {
                    System.out.println(" 未能解析出有效对比表");
                }
            } catch (Exception e) {
                System.err.println(" 对比表解析失败: " + e.getMessage());
                System.out.println("原始响应:\n" + truncate(compareResp, 300));
            }
        }
        conversation.add(new Message("assistant", compareResp != null ? compareResp : ""));

        // 5: 最终推荐
        String finalAsk = "请结合以上所有信息,给出你最终推荐的一款车型,并详细说明理由。";
        conversation.add(new Message("user", finalAsk));
        String finalResp = callModel(modelName, conversation);
        if (finalResp == null || finalResp.isEmpty()) {
            finalResp = "(模型未返回推荐)";
        } else {
            System.out.println(" " + modelName + " 最终推荐:\n" + finalResp);
        }

        // 6: 自动评分(传入 parsedTable 用于判断结构化能力)
        boolean hasValidTable = parsedTable != null && !parsedTable.rows.isEmpty();
        EvaluationResult evaluation = evaluateResponse(finalResp, hasValidTable);
        System.out.printf("\n 自动评分: %.1f/100%n", evaluation.totalScore * 100);
        System.out.println(" 评分详情: " + evaluation.details);

        return new DetailedResult(modelName, candidates, finalResp, evaluation.totalScore,
                roundResults, null, evaluation, conversation);
    }

    private static String callModel(String modelName, List<Message> messages) throws Exception {
        List<Message> requestMessages = new ArrayList<>(messages);

        // 精简 Qwen 上下文
        if ("qwen".equals(modelName) && requestMessages.size() > 2) {
            List<Message> trimmed = new ArrayList<>();
            trimmed.add(requestMessages.get(0));
            trimmed.add(requestMessages.get(requestMessages.size() - 1));
            requestMessages = trimmed;
        }

        ModelConfig config = getModelConfig(modelName);

        String requestBody;
        if ("qwen".equals(modelName)) {
            ObjectNode input = objectMapper.createObjectNode();
            ArrayNode msgArray = input.putArray("messages");
            for (Message msg : requestMessages) {
                msgArray.addObject()
                        .put("role", msg.role)
                        .put("content", msg.content);
            }
            ObjectNode payload = objectMapper.createObjectNode();
            payload.put("model", config.model);
            payload.set("input", input);
            ObjectNode params = payload.putObject("parameters");
            params.put("temperature", 0.7);
            params.put("max_tokens", 2000);
            params.put("top_p", 0.8);
            requestBody = payload.toString();
        } else {
            ObjectNode payload = objectMapper.createObjectNode();
            payload.put("model", config.model);
            ArrayNode msgArray = payload.putArray("messages");
            for (Message msg : requestMessages) {
                msgArray.addObject()
                        .put("role", msg.role)
                        .put("content", msg.content);
            }
            payload.put("temperature", 0.7);
            payload.put("max_tokens", 2000);
            requestBody = payload.toString();
        }

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(config.url.trim()))
                .header("Authorization", config.authHeader)
                .header("Content-Type", "application/json")
                .timeout(Duration.ofSeconds(60))
                .POST(HttpRequest.BodyPublishers.ofString(requestBody))
                .build();

        try {
            HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                JsonNode root = objectMapper.readTree(response.body());

                if ("qwen".equals(modelName)) {
                    JsonNode textNode = root.path("output").path("text");
                    if (!textNode.isMissingNode() && textNode.isTextual()) {
                        return textNode.asText();
                    } else {
                        System.err.println("[Qwen] 无效响应: " + response.body());
                    }
                } else if ("glm".equals(modelName)) {
                    JsonNode choices = root.path("choices");
                    if (choices.isArray() && choices.size() > 0) {
                        JsonNode content = choices.get(0).path("message").path("content");
                        if (content.isTextual()) {
                            return content.asText();
                        }
                    }
                    System.err.println("[GLM] 无效响应: " + response.body());
                }
            } else {
                System.err.println("[" + modelName + "] HTTP " + response.statusCode() + ": " + response.body());
            }
        } catch (Exception e) {
            System.err.println("[" + modelName + "] 请求异常: " + e.getMessage());
            throw e;
        }
        return "";
    }

    private static ModelConfig getModelConfig(String modelName) {
        switch (modelName) {
            case "qwen":
                return new ModelConfig(
                        "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation",
                        "Bearer " + QWEN_API_KEY,
                        "qwen-max"
                );
            case "glm":
                return new ModelConfig(
                        "https://open.bigmodel.cn/api/paas/v4/chat/completions",
                        "Bearer " + GLM_API_KEY,
                        "glm-4-flash"
                );
            default:
                throw new IllegalArgumentException("不支持的模型: " + modelName);
        }
    }

    private static List<String> extractCarModels(String text) {
        if (text == null || text.isEmpty()) return new ArrayList<>();

        List<String> found = new ArrayList<>();
        Pattern pattern = Pattern.compile("([\\u4e00-\\u9fa5]+)\\s*([\\w\\d\\-+]+)");
        Matcher matcher = pattern.matcher(text);

        while (matcher.find()) {
            String brand = matcher.group(1);
            String model = matcher.group(2);
            String fullName = brand + " " + model;

            if (CAR_BRANDS.containsKey(brand) && CAR_BRANDS.get(brand).contains(model)) {
                if (!found.contains(fullName)) {
                    found.add(fullName);
                }
            }
        }

        if (found.isEmpty()) {
            for (Map.Entry<String, List<String>> entry : CAR_BRANDS.entrySet()) {
                for (String model : entry.getValue()) {
                    String fullName = entry.getKey() + " " + model;
                    if (text.contains(entry.getKey()) && text.contains(model)) {
                        if (!found.contains(fullName)) {
                            found.add(fullName);
                        }
                    }
                }
            }
        }

        return found.stream().distinct().collect(Collectors.toList());
    }

    private static List<String> mergeCandidates(List<String> existing, List<String> newOnes) {
        Set<String> merged = new LinkedHashSet<>(existing);
        merged.addAll(newOnes);
        return new ArrayList<>(merged);
    }

    // ========== 新增:对比表解析 ==========
    private static CarComparisonTable parseComparisonTable(String response) {
        CarComparisonTable table = new CarComparisonTable();
        String clean = response.trim();

        // 尝试解析 Markdown 表格
        if (clean.contains("|")) {
            String[] lines = clean.split("\n");
            boolean headerDone = false;
            for (String line : lines) {
                line = line.trim();
                if (line.startsWith("|") && line.endsWith("|")) {
                    List<String> cells = Arrays.stream(line.substring(1, line.length() - 1).split("\\|"))
                            .map(String::trim)
                            .filter(cell -> !cell.isEmpty())
                            .collect(Collectors.toList());
                    // 跳过分隔线(如 |---|---|)
                    if (!cells.isEmpty() && !cells.get(0).matches("-+")) {
                        if (!headerDone) {
                            table.headers = cells;
                            headerDone = true;
                        } else {
                            table.rows.add(cells);
                        }
                    }
                }
            }
            if (!table.headers.isEmpty() && !table.rows.isEmpty()) {
                return table;
            }
        }

        // 备用:尝试 JSON
        try {
            JsonNode json = objectMapper.readTree(clean);
            if (json.isArray()) {
                Iterator<JsonNode> it = json.elements();
                while (it.hasNext()) {
                    JsonNode item = it.next();
                    if (item.isObject()) {
                        if (table.headers.isEmpty()) {
                            item.fieldNames().forEachRemaining(table.headers::add);
                        }
                        List<String> row = new ArrayList<>();
                        for (String header : table.headers) {
                            row.add(item.has(header) ? item.get(header).asText() : "");
                        }
                        table.rows.add(row);
                    }
                }
                if (!table.rows.isEmpty()) return table;
            }
        } catch (Exception ignored) {}

        return null;
    }

    //对比表数据结构
    static class CarComparisonTable {
        List<String> headers = new ArrayList<>();
        List<List<String>> rows = new ArrayList<>();

        public String toMarkdown() {
            if (headers.isEmpty()) return "";
            StringBuilder sb = new StringBuilder();
            sb.append("| ").append(String.join(" | ", headers)).append(" |\n");
            sb.append("|").append(headers.stream().map(h -> "---").collect(Collectors.joining("|"))).append("|\n");
            for (List<String> row : rows) {
                sb.append("| ").append(String.join(" | ", row)).append(" |\n");
            }
            return sb.toString();
        }

        public void saveToCsv(String filename) throws Exception {
            try (java.io.PrintWriter out = new java.io.PrintWriter(new File(filename))) {
                out.println(String.join(",", headers));
                for (List<String> row : rows) {
                    out.println(String.join(",", row.stream()
                            .map(cell -> "\"" + cell.replace("\"", "\"\"") + "\"")
                            .collect(Collectors.toList())));
                }
            }
        }

        public void saveToJson(String filename) throws Exception {
            ArrayNode array = objectMapper.createArrayNode();
            for (List<String> row : rows) {
                ObjectNode obj = objectMapper.createObjectNode();
                for (int i = 0; i < headers.size(); i++) {
                    obj.put(headers.get(i), i < row.size() ? row.get(i) : "");
                }
                array.add(obj);
            }
            objectMapper.writerWithDefaultPrettyPrinter()
                    .writeValue(new File(filename), array);
        }
    }

    // 评分函数
    private static EvaluationResult evaluateResponse(String finalResponse, boolean hasValidTable) {
        if (finalResponse == null || finalResponse.isEmpty() || finalResponse.contains("(模型未返回推荐)")) {
            return new EvaluationResult(0.0, false, Arrays.asList("无响应"));
        }

        List<String> details = new ArrayList<>();
        double score = 0.0;

        // 1. 推荐车型(30分)
        List<String> recommended = extractCarModels(finalResponse);
        boolean hasRecommendation = !recommended.isEmpty();
        if (hasRecommendation) {
            score += 0.3;
            details.add("包含推荐车型: " + recommended);
        } else {
            details.add("未明确推荐车型");
        }

        // 2. 关键因素覆盖(40分)
        List<String> keyFactors = Arrays.asList("安全", "预算", "空间", "油耗", "动力", "舒适", "通勤");
        long factorCount = keyFactors.stream().filter(finalResponse::contains).count();
        score += (double) factorCount / keyFactors.size() * 0.4;
        details.add(String.format("覆盖%d/%d个关键因素", factorCount, keyFactors.size()));

        // 3. 理由充分性(20分)
        boolean hasReason = finalResponse.contains("因为") || finalResponse.length() > 100;
        if (hasReason) score += 0.2;
        details.add(hasReason ? "理由充分" : "理由不充分");

        // 4. 结构清晰度(10分)
        boolean hasStructure = finalResponse.contains("首先") || finalResponse.contains("其次");
        if (hasStructure) score += 0.1;
        details.add(hasStructure ? "结构清晰" : "结构松散");



        return new EvaluationResult(score, hasValidTable, details);
    }

    private static void generateFinalReport(Map<String, List<DetailedResult>> allResults) {
        System.out.println("\n" + "=".repeat(80));
        System.out.println(" 最终模型对比报告");
        System.out.println("=".repeat(80));

        for (String model : Arrays.asList("qwen", "glm")) {
            List<DetailedResult> results = allResults.get(model);
            long successCount = results.stream().filter(r -> r.score > 0).count();
            double avgScore = results.stream().mapToDouble(r -> r.score).average().orElse(0.0);
            long tableSuccess = results.stream().filter(r -> r.evaluation.hasValidTable).count();

            Set<String> allRecs = results.stream()
                    .map(r -> extractCarModels(r.finalRecommendation))
                    .flatMap(List::stream)
                    .collect(Collectors.toSet());

            System.out.printf("\n %s:\n", model.toUpperCase());
            System.out.printf("   成功率: %d/%d\n", successCount, results.size());
            System.out.printf("   有效对比表: %d/%d\n", tableSuccess, results.size());
            System.out.printf("   平均评分: %.1f/100\n", avgScore * 100);
            System.out.printf("   常见推荐: %s\n", allRecs.isEmpty() ? "无" : allRecs);
        }

        double qwenAvg = allResults.get("qwen").stream().mapToDouble(r -> r.score).average().orElse(0.0);
        double glmAvg = allResults.get("glm").stream().mapToDouble(r -> r.score).average().orElse(0.0);

        System.out.println("\n" + "-".repeat(50));
        if (qwenAvg > glmAvg) {
            System.out.println(" 胜出模型: Qwen — 更适合你的购车推荐需求");
        } else if (glmAvg > qwenAvg) {
            System.out.println(" 胜出模型: GLM — 更适合你的购车推荐需求");
        } else {
            System.out.println(" 两者表现相当");
        }
    }

    private static String truncate(String str, int len) {
        if (str == null) return "";
        return str.length() > len ? str.substring(0, len) + "..." : str;
    }

    // ========== 内部类 ==========
    static class Message {
        String role; String content;
        Message(String role, String content) { this.role = role; this.content = content; }
    }

    static class ModelConfig {
        String url; String authHeader; String model;
        ModelConfig(String url, String authHeader, String model) {
            this.url = url.trim(); this.authHeader = authHeader; this.model = model;
        }
    }

    static class TestResult {
        String model; List<String> candidates; String finalRecommendation; double score; List<RoundResult> roundHistory;
        TestResult(String model, List<String> candidates, String finalRecommendation, double score, List<RoundResult> roundHistory) {
            this.model = model; this.candidates = candidates; this.finalRecommendation = finalRecommendation;
            this.score = score; this.roundHistory = roundHistory;
        }
    }

    static class DetailedResult {
        String model; List<String> candidates; String finalRecommendation; double score;
        List<RoundResult> roundHistory; JsonNode comparisonTable; EvaluationResult evaluation; List<Message> fullConversation;
        DetailedResult(String model, List<String> candidates, String finalRecommendation, double score,
                       List<RoundResult> roundHistory, JsonNode comparisonTable, EvaluationResult evaluation,
                       List<Message> fullConversation) {
            this.model = model; this.candidates = candidates; this.finalRecommendation = finalRecommendation;
            this.score = score; this.roundHistory = roundHistory; this.comparisonTable = comparisonTable;
            this.evaluation = evaluation; this.fullConversation = fullConversation;
        }
        TestResult toTestResult() {
            return new TestResult(model, candidates, finalRecommendation, score, roundHistory);
        }
    }

    static class RoundResult {
        String roundName; String response; List<String> candidates;
        RoundResult(String roundName, String response, List<String> candidates) {
            this.roundName = roundName; this.response = response; this.candidates = candidates;
        }
    }

    static class EvaluationResult {
        double totalScore;
        boolean hasValidTable; // 标记是否提供有效对比表
        List<String> details;
        EvaluationResult(double totalScore, boolean hasValidTable, List<String> details) {
            this.totalScore = totalScore;
            this.hasValidTable = hasValidTable;
            this.details = details;
        }
    }
}

2.3 脚本分析

  1. 类的设计
    Message:表示对话中的一条消息,包含角色(user/assistant)和内容。
    ModelConfig:存储模型的配置信息,包括URL、认证头和模型名称。
    TestResult:存储单次测试的结果,包括模型名称、候选车型、最终推荐、得分和轮次历史。
    DetailedResult:扩展了TestResult,增加了对比表、评估结果和完整对话。
    RoundResult:存储每一轮对话的结果,包括轮次名称、响应和候选车型。
    EvaluationResult:存储评估结果,包括总分、是否有有效对比表和详细信息。
    CarComparisonTable:用于解析和存储模型返回的对比表。

  2. 主要流程
    主流程:在main方法中,我们进行了多轮测试(这里设置为10轮),每轮测试分别对两个模型进行测试,并收集结果。
    模拟购车对话:simulateCarBuying方法模拟了多轮对话,包括:用户陈述购车需求、模型初步推荐、多轮详细询问(动力、油耗、安全、保值率)、请求对比表、最终推荐
    模型调用:callModel方法根据模型类型构造请求,调用相应的API,并解析响应。
    车型提取:extractCarModels方法使用正则表达式从模型响应中提取车型。
    对比表解析:parseComparisonTable方法尝试解析模型返回的对比表(Markdown或JSON格式)。
    自动评分:evaluateResponse方法根据预设的评分标准对最终推荐进行评分。
    生成报告:generateFinalReport方法汇总多轮测试结果,生成对比报告。

  3. 异常处理
    使用了try-catch来捕获异常,并在出现异常时记录错误信息,避免程序中断。

  4. 并发控制
    为了避免API限流,在每轮测试之间使用了Thread.sleep(5000),在每轮对话之间也使用了Thread.sleep(2000)。这种简单的延迟控制可以避免频繁请求。

  5. 结果可视化
    最终报告以文本形式输出,包括每个模型的成功率、平均评分、常见推荐等。

3. 测试结果整理

Qwen-Max得分GLM-4-Flash得分Qwen-Max推荐车型GLM-4-Flash推荐车型
184.376.8丰田 雷凌丰田 卡罗拉
284.378.6丰田 卡罗拉本田 飞度
39078.6丰田 卡罗拉比亚迪 秦PLUS
484.378.6丰田 雷凌本田 飞度
584.384.3丰田 卡罗拉比亚迪 秦PLUS
684.376.8丰田 雷凌本田 飞度
79084.3丰田 雷凌丰田 卡罗拉
89084.3丰田 卡罗拉比亚迪 秦PLUS
984.378.6丰田 雷凌本田 飞度
1084.384.3丰田 雷凌本田 思域
  • Qwen-Max 所有轮次得分 ≥ 84.3,最高达 90.0,平均 86.2。
  • GLM-4-Flash 最高仅 84.3,最低78.6,平均 80.6。
  • Qwen-Max 平均分高出 GLM-4-Flash 约 5.6分,差距显著。
  • Qwen-Max:始终推荐 丰田系车型(雷凌/卡罗拉),说明其对“20万预算 + 城市通勤 + 安全舒适”的理解非常一致。
  • GLM-4-Flash:推荐了 飞度、思域、秦PLUS、卡罗拉,表明其对“舒适性”和“空间”的权重判断不稳定,或受上下文干扰较大。

4. 结论

Qwen-Max
作为一款定位“全能型”的大语言模型,在本次汽车选购场景的自动化评测中展现出强大的综合能力。它不仅能准确理解用户的核心需求,还能在多轮对话中持续聚焦关键维度(如动力、油耗、安全配置等),并最终给出逻辑清晰、理由充分的购车推荐。

其优势主要体现在:顶尖的语言理解与生成能力,能处理复杂、多步骤的用户指令,良好的领域知识覆盖,对主流汽车品牌与车型有基本认知,较强的指令遵循性,能按要求输出内容,交互体验流畅,界面简洁,反馈机制完善。

GLM-4-Flash
作为一款面向高并发、低延迟场景的轻量化大模型,在本次汽车选购评测中展现出高效、稳定的综合表现。响应迅速、交互流畅:得益于其“Flash”级推理优化,在多轮对话中保持低延迟响应,用户体验轻快。能够准确理解用户分步提问(如动力、油耗、安全等维度),并按要求逐步提供信息。

其主要优势体现在:对汽车领域细节(如具体车型参数、保值率数据、安全评级)的掌握较为泛化,缺乏精准数据支撑。对于需要高精度数据、深度分析或多轮复杂推理的决策场景,其能力边界明显,建议作为“第一轮筛选工具”使用,而非最终决策依据。

第二部分 分析

1. 同类产品对比排名

模型名称开发机构综合排名参考 (截至2025年中)关键能力/亮点
GPT o1-pro / GPT-5OpenAI多榜单综合第1综合能力均衡,在复杂逻辑推理和创意写作方面表现突出
Claude 4.5 SonnetAnthropic多榜单综合第2编程能力断层领先,长文档处理能力强,安全性高
Gemini 2.5Google多榜单综合第3原生多模态能力标杆,响应速度快,与Google生态整合好
DeepSeek R1深度求索多榜单综合第4国产模型综合最优,推理速度快,中文长文本处理专家
Qwen2.5-Max阿里云多榜单综合第5曾在Chatbot Arena全球第7,数学与编程能力单项突出
GLM-4系列智谱AI国内榜单前列,曾登顶HuggingFace总榜全能力均衡,开源版本强大,API性价比高

2. 软件工程方面的建议

核心问题:当前各大模型平台功能同质化严重,主要比拼模型能力和价格,缺乏杀手级应用和极致的用户体验。

提升策略:实施“场景化、低代码、重体验”的差异化竞争策略。

  1. 杀手级功能:打造 “AI智能体工作流”功能。允许用户通过拖拽方式,将多个模型能力(如搜索、数据分析、文档生成)组合成自动化工作流,并一键部署。这不再是单一的模型调用,而是解决复杂问题的方案。

  2. 核心功能:优化 “Prompt调试与分享社区” 。提供可视化的Prompt调试工具,实时看到模型输出的变化,并建立一个高质量的Prompt模板分享平台,降低用户使用门槛。

  3. 基础功能:持续提升模型基础能力与API稳定性。

  4. 明确标准:不以技术发布为终点,而以“每周活跃工作流数量”和“用户自定义工作流创建率”作为核心指标。

3. 市场概况

市场有多大?
全球AI市场正处在爆发式增长阶段。根据权威咨询公司Gartner的预测,2025年全球AI相关支出预计将达到约1.5万亿美元,并且到2026年,这一数字预计将超过2万亿美元。另一份来自Statista的市场展望则显示,仅AI软件和服务等核心市场,其规模在2025年就有望达到2545亿美元,并预计在2031年增长至1.68万亿美元。

直接用户
以中国市场为例,根据中国互联网络信息中心(CNNIC)在2025年10月发布的报告,截至2025年6月,中国生成式AI的用户规模已达到5.15亿人,在半年内增长了超过2.6亿人,普及率达到了36.5%。从全球范围看,单个AI应用也能在短期内吸引数百万用户,例如新加坡的Agnes AI在上线四个月后,全球注册用户就突破了200万。

潜在的用户
尽管用户规模已经很大,但未来的增长空间依然广阔。首先,在尚未使用生成式AI的网民中存在着巨大的转化潜力,例如在中国,仍有约60%的网民(超过6亿人) 是潜在的待开发用户。其次,AI技术正加速从消费领域向传统行业渗透,在工业制造、农业生产、金融服务和科学研究等领域有着广阔的应用前景。

4. 产品规划

新功能构想:推出“行业AI智能体”应用市场

N (Need 需求)
当前企业用户,尤其是传统行业用户,面临巨大挑战:他们知道AI有用,但不知道如何结合自身业务。自己组建团队、调试API、开发应用的成本和门槛极高。他们的核心需求是 “能直接解决我业务问题的AI工具” ,而不是一个需要二次开发的AI模型。

A (Approach 做法)
与行业专家和头部企业合作,预置开发好针对特定场景的AI智能体。例如:

  • 电商智能体:自动生成商品详情、优化广告文案、分析用户评论。
  • 财务智能体:自动识别发票、进行合规审查、生成财报摘要。
  • HR智能体:智能筛选简历、初步电话面试、生成面试报告。

打造一个应用市场,让企业用户像在手机应用商店下载App一样,一键订阅和部署这些AI智能体到自己的业务系统中。提供低代码配置界面,让用户可以根据自身需求,对智能体进行微调。

B (Benefit 好处)

  • 企业用户:极大降低了AI的使用门槛和集成成本,实现了“开箱即用”,能快速看到AI带来的业务价值。
  • 平台方:构建了强大的生态壁垒,从提供“原材料”(模型API)升级为提供“成品菜”(行业解决方案),商业模式从“卖算力”升级为“卖价值”。

创造了新的收入分成模式,吸引更多ISV(独立软件开发商)入驻开发智能体,丰富生态。

C (Competitors 竞争)

  • 国际对手:微软的Copilot Studio、Salesforce的Einstein
    GPT正在朝这个方向努力,但尚未形成完整的应用市场生态。
  • 国内对手:各家仍在主要聚焦于模型能力的比拼和通用平台的建设。此时率先推出垂直行业AI应用市场,是一个巨大的差异化竞争优势,能够抢占市场空白。

D (Delivery 推广)

  • 种子期:选择1-2个重点行业(如电商或内容创作),免费为行业头部客户定制和部署,打造标杆案例。
  • 启动期:正式上线应用市场,举办开发者大赛,吸引独立软件供应商入驻,丰富应用种类。
  • 增长期:与云市场的渠道绑定,通过销售团队向广大云客户进行推广,并提供免费的概念验证服务。

第三部分 团队绩效

组员负责任务贡献度
陈志豪整体统筹、需求拆解、测试方案设计15%
汪涛PPT 终稿制作、自动化测试执行、评分脚本调试、结果可视化17%
阮航宇答辩、GLM-4-Flash 体验与采访、测试数据清洗、第二部分市场与竞品分析初稿17%
张天荣Prompt 设计、评分维度与权重制定、自动化脚本开发、测试用例库维护、异常 case 复盘18%
胡定赟文献及行业数据搜集、用户调研问卷发放与统计、参考文献规范化16%
莫馥玮排版与美工、CSDN 帖子上传与维护、Qwen-Max 体验与采访、会议纪要17%
...全文
202 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

103

社区成员

发帖
与我相关
我的任务
社区描述
2501_CS_SE_FZU
软件工程 高校
社区管理员
  • FZU_SE_LQF
  • 木村修
  • 心态773
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧