山东大学软件学院创新实训---通义千问大模型的Java调用

关键网址:

如何快速开始通义千问_模型服务灵积(DashScope)-阿里云帮助中心

如何使用通义千问API_模型服务灵积(DashScope)-阿里云帮助中心

在官网文档中提及了通过Messages调用(推荐)和通过prompt调用两种方式,于是我调查了这两种调用方式的区别:

通过 prompt 调用和通过 messages 调用的主要区别在于输入数据的方式和适用场景:

  1. 通过 prompt 调用

    • 使用单个文本作为输入,称为提示文本(prompt),代表用户的一个请求或问题。
    • 适用于单次性的、独立的文本生成任务,不需要考虑上下文或历史消息。
    • 例如,当用户提出一个问题或请求时,可以直接将该问题或请求作为提示文本传递给模型进行文本生成。
  2. 通过 messages 调用

    • 使用消息管理器(MessageManager)来管理多个消息,包括用户消息和系统消息,形成消息队列。
    • 适用于需要考虑上下文、历史消息、多轮对话等情况。
    • 可以在多轮对话中保持上下文,模拟与用户的自然对话,根据之前的交互进行文本生成。
    • 例如,在一个聊天机器人的场景中,用户提出的问题可能需要结合之前的对话历史来进行回答,此时就需要使用消息管理器来管理上下文信息。

通过上面对比可以看出Messages通用性更高,因为智慧医疗问诊确实需要使用到累计的对话记录。

所以下面就重点研究通过Messages调用的方法。

首先是官方的示例代码,但是官方代码不能直接运行,还需要做相关配置。下面是我调试好的代码,为了保险,我把key值隐藏了,替换掉即可。

package com.A611699;// Copyright (c) Alibaba, Inc. and its affiliates.

import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.generation.models.QwenParam;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.MessageManager;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;



public class Maintest {
    public static void callWithMessage()
            throws NoApiKeyException, ApiException, InputRequiredException {
        Generation gen = new Generation();
        com.alibaba.dashscope.utils.Constants.apiKey = "*********";
        MessageManager msgManager = new MessageManager(10);
        Message systemMsg =
                Message.builder().role(Role.SYSTEM.getValue()).content("You are a helpful assistant.").build();
        Message userMsg = Message.builder().role(Role.USER.getValue()).content("如何做西红柿鸡蛋?").build();
        msgManager.add(systemMsg);
        msgManager.add(userMsg);
        QwenParam param =
                QwenParam.builder().model(Generation.Models.QWEN_TURBO).messages(msgManager.get())
                        .resultFormat(QwenParam.ResultFormat.MESSAGE)
                        .build();
        GenerationResult result = gen.call(param);
        System.out.println(result);
    }


    public static void main(String[] args){
        try {
            callWithMessage();
        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
            System.out.println(e.getMessage());
        }
        System.exit(0);
    }
}

通义千问模型的调用有两种方式:使用DashScope SDK和HTTP接口

一、使用DashScope SDK调用

1、首先需要  安装DashScope SDK

2、开通服务并且获得通义千问的API-KEY  开通DashScope并创建API-KEY

3、推荐将API-KEY配置到环境变量中来降低API-KEY的泄露风险,可参考通过环境变量配置API-KEY,当然也可以在代码中配置API-KEY,但是这样泄露了话就可能就烧钱了。

官方的单轮对话代码:已经调试好了,替换掉API-KEY就行

package com.A611699;// Copyright (c) Alibaba, Inc. and its affiliates.

// 建议dashscope SDK的版本 >= 2.12.0

import java.util.Arrays;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;

public class Maintest {

    public static GenerationResult callWithMessage() throws ApiException, NoApiKeyException, InputRequiredException {
        Generation gen = new Generation();
        com.alibaba.dashscope.utils.Constants.apiKey = "**********";

        Message systemMsg = Message.builder()
                .role(Role.SYSTEM.getValue())
                .content("You are a helpful assistant.")
                .build();

        Message userMsg = Message.builder()
                .role(Role.USER.getValue())
                .content("如何做西红柿炒鸡蛋?")
                .build();

        GenerationParam param = GenerationParam.builder()
                .model("qwen-turbo")
                .messages(Arrays.asList(systemMsg, userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .topP(0.8)
                .build();

        return gen.call(param);
    }

    public static void main(String[] args) {
        try {
            GenerationResult result = callWithMessage();
            System.out.println(result);
        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
            // 使用日志框架记录异常信息
            // Logger.error("An error occurred while calling the generation service", e);
            System.err.println("An error occurred while calling the generation service: " + e.getMessage());
        }
        System.exit(0);
    }
}

相对于单轮对话,多轮对话可以参考历史聊天信息,更符合日常交流的场景。但由于调用时会引入历史聊天信息,使用的token量会增多。下面是多轮对话的代码。但是多轮的提问问题已经在代码中写死了。

package com.A611699;// Copyright (c) Alibaba, Inc. and its affiliates.

import java.util.ArrayList;
import java.util.List;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.JsonUtils;

public class Maintest {

    // 设置 API key
    static {
        com.alibaba.dashscope.utils.Constants.apiKey = "sk****************";
    }

    public static GenerationParam createGenerationParam(List<Message> messages) {
        return GenerationParam.builder()
                .model("qwen-turbo")
                .messages(messages)
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .topP(0.8)
                .build();
    }

    public static GenerationResult callGenerationWithMessages(GenerationParam param) throws ApiException, NoApiKeyException, InputRequiredException {
        Generation gen = new Generation();

        return gen.call(param);
    }

    public static void main(String[] args) {
        try {
            List<Message> messages = new ArrayList<>();
            messages.add(createMessage(Role.SYSTEM, "You are a helpful assistant."));
            messages.add(createMessage(Role.USER, "如何做西红柿炖牛腩?"));

            GenerationParam param = createGenerationParam(messages);
            GenerationResult result = callGenerationWithMessages(param);
            printResult(result);

            // 添加assistant返回的消息到列表
            messages.add(result.getOutput().getChoices().get(0).getMessage());

            // 添加新的用户消息
            messages.add(createMessage(Role.USER, "不放糖可以吗?"));

            result = callGenerationWithMessages(param);
            printResult(result);
            printResultAsJson(result);
        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    private static Message createMessage(Role role, String content) {
        return Message.builder().role(role.getValue()).content(content).build();
    }

    private static void printResult(GenerationResult result) {
        System.out.println(result);
    }

    private static void printResultAsJson(GenerationResult result) {
        System.out.println(JsonUtils.toJson(result));
    }
}

下面再做改进,体验实时在线多轮对话功能,但是对话轮数已经写固定了(10轮),可以在代码中修改。

package com.A611699;

import java.util.ArrayList;
import java.util.List;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import java.util.Scanner;

public class Maintest {

    // 设置 API key
    static {
        com.alibaba.dashscope.utils.Constants.apiKey = "sk-****************";
    }

    public static GenerationParam createGenerationParam(List<Message> messages) {
        return GenerationParam.builder()
                .model("qwen-turbo")
                .messages(messages)
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .topP(0.8)
                .build();
    }

    public static GenerationResult callGenerationWithMessages(GenerationParam param) throws ApiException, NoApiKeyException, InputRequiredException {
        Generation gen = new Generation();
        return gen.call(param);
    }

    public static void main(String[] args) {
        try {
            List<Message> messages = new ArrayList<>();

            messages.add(createMessage(Role.SYSTEM, "You are a helpful assistant."));
            for (int i = 0; i < 10;i++) {
                Scanner scanner = new Scanner(System.in);
                System.out.print("请输入:");
                String userInput = scanner.nextLine();
                if ("exit".equalsIgnoreCase(userInput)) {
                    break;
                }
                messages.add(createMessage(Role.USER, userInput));
                GenerationParam param = createGenerationParam(messages);
                GenerationResult result = callGenerationWithMessages(param);
                System.out.println("模型输出:"+result.getOutput().getChoices().get(0).getMessage().getContent());
                messages.add(result.getOutput().getChoices().get(0).getMessage());
            }
        } catch (ApiException | NoApiKeyException | InputRequiredException e) {
            e.printStackTrace();
        }
        System.exit(0);
    }

    private static Message createMessage(Role role, String content) {
        return Message.builder().role(role.getValue()).content(content).build();
    }
}

大模型并不是一次性生成最终结果,而是逐步地生成中间结果,最终结果由这些中间结果拼接而成。在非流式输出方式中,等待模型生成结束后,所有中间结果将被拼接成最终结果,然后返回。相比之下,流式输出可以实时地将中间结果返回,您可以在模型继续进行输出的同时进行阅读,从而减少等待模型回复的时间。要使用流式输出,您需要进行一些配置。在 DashScope Python SDK 中,您需要设置 stream 参数为 True;在 DashScope Java SDK 中,您需要使用 streamCall 接口调用。下面是调试好的代码:

package com.A611699;// Copyright (c) Alibaba, Inc. and its affiliates.

import java.util.Arrays;
import java.util.concurrent.Semaphore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Semaphore;
import com.alibaba.dashscope.aigc.generation.Generation;
import com.alibaba.dashscope.aigc.generation.GenerationParam;
import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.InputRequiredException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.utils.JsonUtils;
import io.reactivex.Flowable;

public class Maintest {


    static {
        // 设置 API key
        com.alibaba.dashscope.utils.Constants.apiKey = "sk-***********************";
    }
    private static final Logger logger = LoggerFactory.getLogger(Main.class);

    private static void handleGenerationResult(GenerationResult message, StringBuilder fullContent) {
        fullContent.append(message.getOutput().getChoices().get(0).getMessage().getContent());
        logger.info("Received message: {}", JsonUtils.toJson(message));
    }

    public static void streamCallWithMessage(Generation gen, Message userMsg)
            throws NoApiKeyException, ApiException, InputRequiredException {
        GenerationParam param = buildGenerationParam(userMsg);
        Flowable<GenerationResult> result = gen.streamCall(param);
        StringBuilder fullContent = new StringBuilder();

        result.blockingForEach(message -> handleGenerationResult(message, fullContent));

        logger.info("Full content: \n{}", fullContent.toString());
    }

    public static void streamCallWithCallback(Generation gen, Message userMsg)
            throws NoApiKeyException, ApiException, InputRequiredException, InterruptedException {
        GenerationParam param = buildGenerationParam(userMsg);
        Semaphore semaphore = new Semaphore(0);
        StringBuilder fullContent = new StringBuilder();

        gen.streamCall(param, new ResultCallback<GenerationResult>() {
            @Override
            public void onEvent(GenerationResult message) {
                handleGenerationResult(message, fullContent);
            }

            @Override
            public void onError(Exception err) {
                logger.error("Exception occurred: {}", err.getMessage());
                semaphore.release();
            }

            @Override
            public void onComplete() {
                logger.info("Completed");
                semaphore.release();
            }
        });

        semaphore.acquire();
        logger.info("Full content: \n{}", fullContent.toString());
    }

    private static GenerationParam buildGenerationParam(Message userMsg) {
        return GenerationParam.builder()
                .model("qwen-turbo")
                .messages(Arrays.asList(userMsg))
                .resultFormat(GenerationParam.ResultFormat.MESSAGE)
                .topP(0.8)
                .incrementalOutput(true)
                .build();
    }

    public static void main(String[] args) {
        try {
            Generation gen = new Generation();
            Message userMsg = Message.builder().role(Role.USER.getValue()).content("如何做西红柿炖牛腩?").build();

            streamCallWithMessage(gen, userMsg);
            streamCallWithCallback(gen, userMsg);
        } catch (ApiException | NoApiKeyException | InputRequiredException | InterruptedException e) {
            logger.error("An exception occurred: {}", e.getMessage());
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值