One能聊天接入百度千帆大模型 —— 文心一言
发布时间
阅读量:
阅读量
阿壹族
采用基于ChatGPT的技术构建了一个微信小程序,并适用于H5端和Web端应用。该小程序包含前端与后端功能模块,并支持实时打字界面并采用流式输出技术传递信息。具体功能包括但不限于AI驱动的自动回复机制、消息数量限制设置以及分享权限调整功能等
阿壹族
开通服务
请简要介绍百度AI生态产品的全貌
- 千帆大模型平台:其中大模型开发即为自行训练一个大模型的过程,具有一定技术门槛,少部分企业才会采用这一技术;而大模型调用则包括百度开放文心系列(即文心一言,ERNIE 4.0及ERNIE 3.5作为具体版本参考ChatGPT),同时还提供第三方可用的大模型供调用
- 千帆AppBuilder:这是一个帮助开发者基于文心大模型快速构建一个AI应用的工具,创建的应用不仅可以集成官方组件(如天气查询、快递查询等)、还可以自定义组件(通过画布拖拽的方式进行功能编排,如调用企业内部API或调用大模型接口),另外还支持导入知识库文件并采用多种格式(txt、pdf、doc、url等多种形式)。通过AppBuilder生成的应用官方会提供访问链接供普通用户使用(界面设计通用的AI聊天界面),同时开发者也可通过SDK接口将创建的应用整合到实际业务系统中。这部分将在后续文章中进行详细讲解
- 千帆AI原生应用商店:这是百度自身开发的一套AI应用生态系统,例如超级助理等产品,安装后直接在浏览器中使用即可实现各种功能(包括划词翻译、网页解读、OCR识别等功能)
本文主要对文心大模型ERNIE的API调用做详细说明

生成一个新应用时,请访问 https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application 进行操作。如果您希望提高性能和资源利用率,请选择启用以下几种预设模型:包括但不限于ERNIE系列中的不同版本以及Yi系列中的中大型对话模型。

部分模型计费说明如下

单次API调用案例
@RequestMapping("/baidu/ernieBotTurbo")
public Result baiduErnieBotTurbo(@RequestBody Map<String, Object> params) {
BaiduConfig baiduConfig = SpringU.getBean(BaiduConfig.class);
BaiduService baiduService = new BaiduService(baiduConfig.getApiKey(), baiduConfig.getApiSecret());
BaiduChatMessage chatMessage = BaiduChatMessage.builder()
.content((String) params.get("content"))
.role("user")
.build();
ErnieBotTurboStreamParam postParam = ErnieBotTurboStreamParam.builder()
.user_id(StpUtil.getLoginIdAsString())
.messages(MiscU.Instance.toList(chatMessage))
.build();
ErnieBotTurboResponse ernieBotTurboResponse = baiduService.ernieBotTurbo(postParam);
return Result.success(ernieBotTurboResponse);
}
// 该方法是同步请求API,会等大模型将数据完全生成之后,返回响应结果,可能需要等待较长时间,视生成文本长度而定
public ErnieBotTurboResponse ernieBotTurbo(ErnieBotTurboStreamParam param) {
if (param == null) {
log.error("参数异常:param不能为空");
throw new RuntimeException("参数异常:param不能为空");
}
if (param.isStream()) {
param.setStream(false);
}
String fullChatUrl = SpringU.getBean(BaiduConfig.class).getFullChatUrl();
String post = HttpUtil.post(fullChatUrl + BaiduConfig.getToken(appKey, secretKey), JSONUtil.toJsonStr(param));
return JSONUtil.toBean(post, ErnieBotTurboResponse.class);
}
public class BaiduConfig {
@Value("${aezo-chat-gpt.baidu.api-key:}")
private String apiKey;
@Value("${aezo-chat-gpt.baidu.api-secret:}")
private String apiSecret;
@Value("${aezo-chat-gpt.baidu.chat-url:yi_34b_chat}")
private String chatUrl;
/** * Yi-34B-Chat 免费使用 https://cloud.baidu.com/doc/WENXINWORKSHOP/s/vlpteyv3c
* 模型对应路径如,更多参考官方文档:
* Yi-34B-Chat: yi_34b_chat
* ERNIE-Lite-8K-0922: eb-instant
* ERNIE-Speed-8K: ernie_speed
*/
private static final String CHAT_URL_TPL = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/%s?access_token=";
public static String getToken(String appKey, String secretKey) {
String url = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=" + appKey + "&client_secret=" + secretKey;
String s = HttpUtil.get(url);
Token bean = JSONUtil.toBean(s, Token.class);
return bean.getAccess_token();
}
public String getFullChatUrl() {
return String.format(CHAT_URL_TPL, chatUrl);
}
}
调用测试

多轮对话流式输出
- One能聊天中进行接收用户消息处理
private void onMessageBaidu(String msg, Map<String, Object> promptData, String messageContext) {
BaiduConfig baiduConfig = SpringU.getBean(BaiduConfig.class);
BaiduService baiduService = new BaiduService(baiduConfig.getApiKey(), baiduConfig.getApiSecret());
BaiduEventSourceListener baiduEventSourceListener = new BaiduEventSourceListener(this.session);
List<Message> messages = new ArrayList<>();
if (StrUtil.isNotBlank(messageContext)) {
messages = JSONUtil.toList(messageContext, Message.class);
// 要求最终请求的会话条数必须是奇数,且必须是 U1 A1 U2 A2 U3 A3...的对话形式
if(messages.size() % 2 != 0) {
// 原始会话是奇数(加上新的一条输入就变成偶数了)
int index = 0;
Iterator<Message> iterator = messages.iterator();
while (iterator.hasNext()) {
Message next = iterator.next();
if(index % 2 == 0) {
if(!"user".equals(next.getRole())) {
iterator.remove();
} else {
index++;
}
} else {
if(!"assistant".equals(next.getRole())) {
iterator.remove();
} else {
index++;
}
}
}
}
if(messages.size() >= 10) {
messages.remove(0);
messages.remove(1);
}
Message currentMessage = Message.builder().content(msg).role(Message.Role.USER).build();
messages.add(currentMessage);
} else {
if(promptData != null && ValidU.isNotEmpty(promptData.get("description"))) {
String prompt = (String) promptData.get("description");
msg = "请按以下要求和我对话:" + prompt + "(如果前面的提示词中漏掉说明返回的语音,请默认使用中文返回结果即respond in Chinese)。\n我:" + msg;
}
Message currentMessage = Message.builder().content(msg).role(Message.Role.USER).build();
messages.add(currentMessage);
}
List<BaiduChatMessage> baiduChatMessages = messages.stream().map(x -> {
BaiduChatMessage baiduChatMessage = new BaiduChatMessage();
BeanUtil.copyProperties(x, baiduChatMessage);
return baiduChatMessage;
}).collect(Collectors.toList());
ErnieBotTurboStreamParam postParam = ErnieBotTurboStreamParam.builder()
.user_id(this.uid)
.messages(baiduChatMessages)
.build();
baiduService.ernieBotTurboStream(postParam, baiduEventSourceListener);
MessageLocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), MessageLocalCache.TIMEOUT);
}
// 该方法是通过流的方式请求API,大模型每生成一些字符,就会通过流的方式相应给客户端,
// 我们是在 BaiduEventSourceListener.java 的 onEvent 方法中获取大模型响应的数据,其中data就是具体的数据,
// 我们获取到数据之后,就可以通过 SSE/webscocket 的方式实时相应给前端页面展示
public void ernieBotTurboStream(ErnieBotTurboStreamParam param, EventSourceListener eventSourceListener) {
if (Objects.isNull(eventSourceListener)) {
log.error("参数异常:EventSourceListener不能为空");
throw new RuntimeException("参数异常:EventSourceListener不能为空");
}
if (param == null) {
log.error("参数异常:param不能为空");
throw new RuntimeException("参数异常:param不能为空");
}
if (!param.isStream()) {
param.setStream(true);
}
try {
EventSource.Factory factory = EventSources.createFactory(this.okHttpClient);
ObjectMapper mapper = new ObjectMapper();
String fullChatUrl = SpringU.getBean(BaiduConfig.class).getFullChatUrl();
String requestBody = mapper.writeValueAsString(param);
Request request = new Request.Builder()
.url(fullChatUrl + BaiduConfig.getToken(appKey, secretKey))
.post(RequestBody.create(MediaType.parse(ContentType.JSON.getValue()), requestBody))
.build();
//创建事件
EventSource eventSource = factory.newEventSource(request, eventSourceListener);
} catch (JsonProcessingException e) {
log.error("请求参数解析是失败!", e);
throw new RuntimeException("请求参数解析是失败!", e);
}
}
- 将文心一言返回的消息推送给用户
@Slf4j
public class BaiduEventSourceListener extends EventSourceListener {
private Session session;
public BaiduEventSourceListener(Session session) {
this.session = session;
}
@SneakyThrows
@Override
public void onOpen(EventSource eventSource, Response response) {
log.info("baidu建立sse连接...");
session.getBasicRemote().sendText("{\"role\": \"assistant\"}");
}
@SneakyThrows
@Override
public void onEvent(EventSource eventSource, String id, String type, String data) {
// {"id":"as-jddwwxm2j3","object":"chat.completion","created":1712913324,"sentence_id":0,"is_end":false,"is_truncated":false,"result":"你好!","need_clear_history":false,"usage":{"prompt_tokens":1,"completion_tokens":2,"total_tokens":3}}
// {"id":"as-jddwwxm2j3","object":"chat.completion","created":1712913325,"sentence_id":1,"is_end":false,"is_truncated":false,"result":"有什么我可以帮助你的吗?","need_clear_history":false,"usage":{"prompt_tokens":1,"completion_tokens":2,"total_tokens":3}}
// {"id":"as-jddwwxm2j3","object":"chat.completion","created":1712913325,"sentence_id":2,"is_end":true,"is_truncated":false,"result":"","need_clear_history":false,"usage":{"prompt_tokens":1,"completion_tokens":8,"total_tokens":9}}
log.info("baidu返回数据:{}", data);
String uid = session.getPathParameters().get("uid");
ObjectMapper mapper = new ObjectMapper();
// 读取Json
ErnieBotTurboResponse completionResponse = mapper.readValue(data, ErnieBotTurboResponse.class);
Message deltaMessage = Message.builder()
.content(completionResponse.getResult())
.build();
String delta = mapper.writeValueAsString(deltaMessage);
session.getBasicRemote().sendText(delta);
// 缓存返回消息
if(!"assistant".equals(deltaMessage.getRole()) && deltaMessage.getContent() != null && !"".equals(deltaMessage.getContent())) {
StringBuffer msgBuffer = MessageBackLocalCache.CACHE.get(uid);
if(msgBuffer == null) {
msgBuffer = new StringBuffer();
MessageBackLocalCache.CACHE.put(uid, msgBuffer);
}
msgBuffer.append(deltaMessage.getContent());
}
}
@SneakyThrows
@Override
public void onClosed(EventSource eventSource) {
log.info("baidu关闭sse连接...");
session.getBasicRemote().sendText("[DONE]");
// 记录返回消息
String uid = session.getPathParameters().get("uid");
StringBuffer msgBuffer = MessageBackLocalCache.CACHE.get(uid);
if(msgBuffer != null) {
JdbcTemplate jdbcTemplate = SpringU.getBean(JdbcTemplate.class);
List<Map<String, Object>> list = jdbcTemplate.queryForList("select id, create_time " +
" from chat_msg_his where user_id = ? and msg_ai is null order by id desc limit 1", uid);
if(ValidU.isNotEmpty(list)) {
Map<String, Object> chatInfo = list.get(0);
Date createTime = (Date) chatInfo.get("create_time");
Date now = new Date();
long useTime = (now.getTime() - createTime.getTime()) / 1000;
jdbcTemplate.update("update chat_msg_his set msg_ai = ?, update_time = ?, use_time = ? where id = ?",
msgBuffer.toString(), now, useTime, chatInfo.get("id"));
}
// 需要保留原始会话
String messageStr = (String) MessageLocalCache.CACHE.get(uid);
List<Message> messages = JSONUtil.toList(messageStr, Message.class);
messages.add(Message.builder().role("assistant").content(msgBuffer.toString()).build());
MessageLocalCache.CACHE.put(uid, JSONUtil.toJsonStr(messages), MessageLocalCache.TIMEOUT);
}
MessageBackLocalCache.CACHE.remove(uid);
}
@SneakyThrows
@Override
public void onFailure(EventSource eventSource, Throwable t, Response response) {
session.getBasicRemote().sendText("机器人出小差了~");
String uid = session.getPathParameters().get("uid");
MessageBackLocalCache.CACHE.remove(uid);
if (Objects.isNull(response)) {
return;
}
ResponseBody body = response.body();
if (Objects.nonNull(body)) {
log.error("baidu sse连接异常data:{},异常:{}", body.string(), t);
} else {
log.error("baidu sse连接异常data:{},异常:{}", response, t);
}
eventSource.cancel();
}
}
效果展示

全部评论 (0)
还没有任何评论哟~
