配置LLMs(Java SDK)
LLMs模块用于对大语言模型API的适配封装,提供统一的接口快速地调用盘古、开源模型等模型API。
- 初始化:根据相应模型定义LLM类。例如,使用盘古LLM为: LLMs.of(LLMs.PANGU)。
import com.huaweicloud.pangu.dev.sdk.api.llms.LLM; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; // 初始化盘古LLM LLM llm = LLMs.of(LLMs.PANGU);
- 基础问答:基础的模型文本问答,temperature等参数采用模型默认的设置。
llm.ask("你是谁?").getAnswer();
- 同时调用多个不同的LLM。
final LLMConfig config = LLMConfig.builder() .llmModuleConfig( LLMModuleConfig.builder().url(ConfigLoadUtil.getStringConf(null, "custom.llm.url")).build()) .llmParamConfig(LLMParamConfig.builder().temperature(0.9).build()) .build(); // 使用custom.llm.url final LLM llm1 = LLMs.of(LLMs.PANGU, config); log.info(llm1.ask("你好").getAnswer()); // 使用sdk.llm.pangu.url final LLM llm2 = LLMs.of(LLMs.PANGU); log.info(llm2.ask("你好").getAnswer());
上述代码中custom.llm.url为自定义的url地址(名字由开发者任意指定,或直接传入url地址),可以指向不同的模型,因此llm1为一个大模型;而llm2没有指定config,默认使用sdk.llm.pangu.url,若地址与custom.llm.url,则为另外一个大模型。
- 自定义参数问答:自定义设置如temperature等参数,获得对应的效果。
import com.huaweicloud.pangu.dev.sdk.api.llms.LLM; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMConfigGallery; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMParamConfig; // 设置模型参数,temperature为0.9 LLMConfig llmConfig = LLMConfig.builder().llmParamConfig(LLMParamConfig.builder().temperature(0.9).build()).build(); // 如使用Gallery三方模型,使用以下配置 // LLMConfig llmConfig = LLMConfigGallery.builder().llmParamConfig(LLMParamConfig.builder().temperature(0.9).build()).build(); // 初始化带参数的盘古LLM LLM pangu = LLMs.of(LLMs.PANGU, llmConfig); pangu.ask("写一篇五言律诗").getAnswer();
支持调整的参数解释。
private int maxTokens; // 完成时要生成的令牌的最大数量 private double temperature; // 调整随机抽样的程度,温度值越高,随机性越大 private double topP; // 核采样值, 和temperature不同时配置 private double presencePenalty; // 存在惩罚,增加模型谈论新主题的可能性 private double frequencyPenalty; // 频率惩罚,降低模型重复的可能性,提高文本多样性、创造型 private int bestOf; // 服务侧生成优选的回答数 private boolean stream; // 是否开启流式调用
- 流式问答:模型问答,开启流式效果,响应消息流式打印。
import com.huaweicloud.pangu.dev.sdk.api.callback.StreamCallBack; import com.huaweicloud.pangu.dev.sdk.api.callback.StreamResult; import com.huaweicloud.pangu.dev.sdk.api.llms.LLM; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMParamConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.response.LLMResp; import lombok.extern.slf4j.Slf4j; // 设置模型参数,stream为true final LLMConfig llmConfig = LLMConfig.builder().llmParamConfig(LLMParamConfig.builder().stream(true).build()).build(); // 盘古LLM LLM pangu = LLMs.of(LLMs.PANGU, llmConfig); // 设置回调处理逻辑 pangu.setStreamCallback(new StreamCallBackImp()); pangu.ask("写一篇200字的散文").getAnswer(); // 构造回调函数(以log打印为例,业务可自定义) @Slf4j public class StreamCallBackImp implements StreamCallBack { @Override public void onStart(String callBackId) { log.info("StreamCallBack onStart: callBackId ----> {}", callBackId); } @Override public void onEnd(String callBackId, StreamResult streamResult, LLMResp llmResp) { log.info("StreamCallBack onEnd: callBackId ----> {} || llmResp ----> {}", callBackId, llmResp); } @Override public void onError(String callBackId, StreamResult streamResult) { log.error("StreamCallBack onError: callBackId ----> {}", callBackId); } @Override public void onNewToken(String callBackId, LLMResp llmResp) { log.info("StreamCallBack onNewToken: callBackId ----> {} || llmResp ----> {}", callBackId, llmResp); } }
- 多轮对话问答:传递历史问答记录,实现多轮对话问答能力,同时支持自定义参数问答、流式问答。
import com.huaweicloud.pangu.dev.sdk.api.llms.LLM; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; import com.huaweicloud.pangu.dev.sdk.api.llms.request.ConversationMessage; import com.huaweicloud.pangu.dev.sdk.api.llms.request.Role; import java.util.ArrayList; import java.util.List; // 构造多轮对话:历史问答记录 + 最新问题 private List<ConversationMessage> buildMultiTurnChatMessages() { List<ConversationMessage> messages = new ArrayList<>(); messages.add(ConversationMessage.builder().role(Role.SYSTEM).content("You are a helpful assistant.").build()); messages.add(ConversationMessage.builder().role(Role.USER).content("Who won the world series in 2020?").build()); messages.add(ConversationMessage.builder() .role(Role.ASSISTANT) .content("The Los Angeles Dodgers won the World Series in 2020.") .build()); messages.add(ConversationMessage.builder().role(Role.USER).content("Where was it played?").build()); return messages; } // 初始化盘古LLM,开启问答 LLM llm = LLMs.of(LLMs.PANGU); llm.ask(buildMultiTurnChatMessages()).getAnswer();
- 带人设的问答:支持在LLM配置项中设置人设,在LLM问答时系统会自动加上该人设,同时支持以上问答功能(暂不支持GALLERY三方模型)。
import com.huaweicloud.pangu.dev.sdk.api.llms.LLM; import com.huaweicloud.pangu.dev.sdk.api.llms.LLMs; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMConfig; import com.huaweicloud.pangu.dev.sdk.api.llms.config.LLMModuleConfig; // 设置模型系统人设 LLMConfig llmConfig = LLMConfig.builder() .llmModuleConfig(LLMModuleConfig.builder().systemPrompt("You are a helpful assistant.").build()) .build(); // 初始化盘古LLM,开启问答 LLM llm = LLMs.of(LLMs.PANGU, llmConfig); llm.ask("写一首五言律诗").getAnswer();
开源模型
- SDK支持兼容OpenAI-API规范的开源模型。例如,用vllm框架使用OpenAI-API启动推理服务。当前鉴权方式支持AppCode鉴权和华为云的APIG简易认证方式。配置文件需要指定url和key,配置项为:
sdk.llm.openai.url=https://infer-app-modelarts-cn-southwest-2.myhuaweicloud.com/v1/infers/.../v1 sdk.llm.openai.key=your-key
- 初始化LLM:
final OpenAI llm = new OpenAI( LLMConfig.builder().llmModuleConfig(LLMModuleConfig.builder().moduleVersion("llama3-70B").build()).build()); LLMRespOpenAI result = llm.ask("你好");
- 上述moduleVersion根据实际情况传值,也可以使用代码进行url和key的配置:
final OpenAI llm = new OpenAI(LLMConfig.builder() .llmModuleConfig(LLMModuleConfig.builder().moduleVersion("expert_q4").build()) .openAIConfig(OpenAIConfig.builder() .openaiBaseUrl("https://infer-app-modelarts-cn-southwest-2.myhuaweicloud.com/v1/infers/.../v1") .openAiKey("your-key") .build()) .build());
- 使用AppCode鉴权添加的Header:
- 使用APIG简易认证方式添加的Header:
- 当LLM被定义好之后,使用方式与盘古大模型相同,开源模型也支持Agent调用,可参考实例化Agent(Java SDK)。
自定义模型
@Slf4j public class CustomLLM extends AbstractLLM<LLMResp> { /** * 初始化 * * @param llmConfig llm参数配置 */ public CustomLLM(LLMConfig llmConfig) { super(llmConfig); } @Override protected LLMResp getLLMResponse(List<ConversationMessage> chatMessages, LLMParamConfig llmParamConfig) { // 构造请求体 Map<String, Object> request = new HashMap<>(); request.put("temperature", 0.3); request.put("data", chatMessages.stream().map(ConversationMessage::getContent).collect(Collectors.toList())); final String requestBody = JSON.toJSONString(request); log.info("request body : \n{}", JSON.toJSONString(JSON.parseObject(requestBody), true)); // 从配置项读取url,构造post消息 String url = ConfigLoadUtil.getStringConf(null, "llm.custom.api.url"); if (StringUtils.isEmpty(url)) { throw new PanguDevSDKException("the llm.custom.api.url is not config"); } HttpPost httpPost = new HttpPost(url); httpPost.setEntity(new StringEntity(requestBody, ContentType.APPLICATION_JSON)); // 发送消息并处理响应 String responseStr; if (llmConfig.getLlmParamConfig().isStream()) { // 处理流式请求 httpPost.setHeader(new BasicHeader("Inference-Type", "stream")); final CloseableHttpAsyncClient httpclient = HttpUtil.getHttpAsyncClient(false); try { httpclient.start(); final String callBackId = SecurityUtil.getUUID(); final List<PanguChatChunk> panguChatChunks = new ArrayList<>(); Future<HttpResponse> future = httpclient.execute(HttpAsyncMethods.create(httpPost), StreamHelper.getAsyncConsumer(streamCallBack, callBackId, panguChatChunks), StreamHelper.getCallBack(streamCallBack, callBackId, httpPost)); future.get(Optional.ofNullable(llmConfig.getHttpConfig().getAsyncHttpWaitSeconds()).orElse(300), TimeUnit.SECONDS); final PanguChatResp allRespFromChunk = StreamHelper.getAllRespFromChunk(panguChatChunks); return LLMResp.builder().answer(allRespFromChunk.getChoices().get(0).getMessage().getContent()).build(); } catch (Exception e) { throw new PanguDevSDKException(e); } } else { // 处理非流式请求 final CloseableHttpClient httpClient = HttpUtil.getHttpClient(false); try { final CloseableHttpResponse response = httpClient.execute(httpPost); responseStr = EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8); log.info("response: \n{}", JSON.toJSONString(JSON.parseObject(responseStr), true)); // 解析结果 final JSONObject jsonObject = JSON.parseObject(responseStr); JSONObject result = jsonObject.getJSONObject("result"); if (result == null) { result = jsonObject; } final String content = ((JSONObject) result.getJSONArray("answers").get(0)).getString("content"); return LLMResp.builder().answer(content).build(); } catch (IOException e) { throw new PanguDevSDKException(e); } } } @Override protected LLMResp getLLMResponseFromCache(String cache) { return LLMResp.builder().answer(cache).isFromCache(true).build(); } }