One能聊天接入百度千帆AppBuilder
One能聊天介绍:基于ChatGPT实现的微信小程序,适配H5和WEB端。包含前后端,支持打字效果输出流式输出,支持AI聊天次数限制,支持分享增加次数等功能
One能聊天开源地址:https://github.com/oldinaction/ChatGPT-MP
One能聊天演示环境:可关注【阿壹族】公众号,并回复【One能聊天】查看
下文将介绍在One能聊天项目中接入百度千帆AppBuilder功能
千帆AppBuilder介绍
百度云千帆AppBuilder(以下简称AppBuilder)是基于大模型搭建AI原生应用的工作台,提供RAG、Agent、GBI等应用框架,文档问答、表格问答、对话、创作等应用组件,以及文生图、语音等传统AI组件,降低AI原生应用的开发门槛,赋能开发者快速实现应用搭建!
官方文档:https://cloud.baidu.com/doc/AppBuilder/s/6lq7s8lli
千帆AppBuilder是提供开发者基于文心大模型可以快速开发出一个AI应用
创建的应用可以集成一些官方的组件(如天气查询、快递查询等),也可以集成自定义组件(通过画布拖拽,自行编排组件逻辑,如调用企业内部API或调用大模型接口)
另外还可导入知识库供大模型使用(支持txt/pdf/doc/url等模式)
通过AppBuilder创建的应用官方提供一个访问链接供普通用户使用(界面是通用的AI聊天界面),开发者也可以通过SDK调用创建的AI应用从而集成到实际的业务系统中
AppBuilder产品地址 https://cloud.baidu.com/product/AppBuilder,点击立即使用进入主页,初次使用会赠送一定额度的大模型调用次数。如下图手动创建应用,或通过AI对话创建应用
创建应用

创建应用:填写应用名称和描述,角色指令(及prompt,可通过AI优化),也可以引用组件和知识库(后文做相关说明);右侧为预览界面,可在此处设置调用的大模型类型

试用一下

使用组件
创建的应用可以集成一些官方的组件(如天气查询、快递查询等),部分组件会按调用量进行额外收费,官方组件使用比较简单;此处主要说明下自定义组件
本应用目前还没有引入组件,下面自定义一个组件然后引入到本应用中
点击"个人空间 - 组件 - 创建组件"进行创建,此处预置画布选择空画布,也可选择其他画布类型(会自动创建一个画布案例)

画布逻辑说明:
如下图可创建多个逻辑节点,节点间通过连线进行关联。本画布的逻辑为:
- (用户)输入内容放到question参数中
- 分支器(判断):如果输入内容包含"笑话"则调用API节点,否则调用大模型节点
- API节点调用的是一个互联网上的免费的API:通过GET访问
http://api.qingyunke.com/api.php?key=free&appid=0&msg=讲个笑话,即可会返回一个笑话如{"result":0,"content":"★ 迫不得已{br}“妈妈,我刚刚把花园里的梯子碰倒了。”{br}“把这件事去跟父亲说一下。”{br}“他知道,他现在正抓着天窗,吊在墙上呢。”{br}提示:按分类看笑话请发送“笑话分类”"}(说明:这个接口的笑话都是以 “★” 开头,而且使用 “{br}” 进行文本换行,并且以"按分类看笑话请发送…"结束。后面可以看到大模型会对此接口返回的结果进行二次处理:自动去掉 {br} 换行符,并进行一定改写) - 大模型节点则定义了一个提示词
你是我的女朋友,你很温柔,幽默。我现在说:{{question}} 你的回答是:(其中{{question}}为变量,会自动替换为输入内容) - 最后将结果做为组件的输出(并不一定是应用的输出,应用调用组件拿到组件的输出后,会调用再次调用应用默认大模型进行处理后再输出)
组件设置好后,需要通过调试按钮将每个分支调试通过才能引入到应用中

继续编辑应用信息,在组件处添加自定义的组件"闲聊",然后更新发布应用。此时我们再来测试应用
如下图可看到,当发送你今天干嘛了呀?,应用会自动调用"闲聊"组件,并且可能多次调用并对结果进行处理(相当于一个AI Agent),最后再返回给用户。从返回的还忙着想你哦!可看出组件的提示词你是我的女朋友,你很温柔,幽默。起到了一定的作用

当发送我想听个笑话,应用调用组件时自动做了API节点分支(调用笑话API接口),从返回结果中包含"按分类看笑话请发送"就可看出是触发了笑话API接口,而且还在后面加上了"希望你喜欢这个笑话…"的改写文本

引入SDK
2024年4月2日前发布的rag类应用可直接通过官方提供的API调用对应的应用,之后则推荐通过AppBuilder-SDK进行调用
官方提供Python、Java、Go等语言SDK:https://github.com/baidubce/app-builder
下文将结合One能聊天项目调用SDK为案例进行说明
pom.xml中引入依赖
<!-- 百度 AppBuilder -->
<dependency>
<groupId>com.baidubce</groupId>
<artifactId>appbuilder</artifactId>
<version>0.6.0</version>
</dependency>
代码解读
简单SDK调用
@SneakyThrows
@RequestMapping("/baidu/appbuilder")
public Result baiduAppBuilder(@RequestBody Map<String, Object> params) {
BaiduConfig baiduConfig = SpringU.getBean(BaiduConfig.class);
// app-token获取:进入 https://console.bce.baidu.com/ai_apaas/secretKey 创建API秘钥
System.setProperty("APPBUILDER_TOKEN", baiduConfig.getAppToken());
// app-id获取:个人空间 - 应用 - 应用ID
AgentBuilder agentBuilder = new AgentBuilder(baiduConfig.getAppId());
String conversationId = (String) params.get("conversationId");
if(ValidU.isEmpty(conversationId)) {
conversationId = agentBuilder.createConversation();
}
AgentBuilderIterator itor = agentBuilder.run((String) params.get("content"), conversationId, null, true);
StringBuilder sb = new StringBuilder();
while(itor.hasNext()) {
AgentBuilderResult response = itor.next();
log.info("response: {}", response);
sb.append(response.getAnswer());
}
return Result.success(MiscU.Instance.toMap("content", sb.toString(), "conversationId", conversationId));
}
代码解读
调用测试:第一次访问自动创建一个会话编号conversationId

第2次访问,带上会话编号conversationId,从而识别会话上下文

多轮对话流式输出
- One能聊天中进行接收用户消息处理
private void onMessageBaiduAppBuilder(String msg, Map<String, Object> promptData, String messageContext) {
BaiduConfig baiduConfig = SpringU.getBean(BaiduConfig.class);
System.setProperty("APPBUILDER_TOKEN", baiduConfig.getAppToken());
String appId = baiduConfig.getAppId();
if(promptData != null && ValidU.isNotEmpty(promptData.get("baidu_ab_appid"))) {
appId = (String) promptData.get("baidu_ab_appid");
}
AgentBuilder agentBuilder = new AgentBuilder(appId);
String conversationId = messageContext;
if(ValidU.isEmpty(conversationId)) {
conversationId = agentBuilder.createConversation();
}
MessageLocalCache.CACHE.put(uid, conversationId, MessageLocalCache.TIMEOUT);
AgentBuilderIterator agentBuilderIterator = agentBuilder.run(msg, conversationId, null, true);
BaiduAppBuilderEventSourceListener baiduAppBuilderEventSourceListener = new BaiduAppBuilderEventSourceListener(this.session);
baiduAppBuilderEventSourceListener.onOpen(agentBuilderIterator);
}
代码解读
- 将AppBuilder应用返回的消息推送给用户
@Slf4j
public class BaiduAppBuilderEventSourceListener {
private Session session;
public BaiduAppBuilderEventSourceListener(Session session) {
this.session = session;
}
@SneakyThrows
public void onOpen(AgentBuilderIterator agentBuilderIterator) {
while (agentBuilderIterator.hasNext()) {
session.getBasicRemote().sendText("{\"role\": \"assistant\"}");
break;
}
while (agentBuilderIterator.hasNext()) {
this.onEvent(agentBuilderIterator.next());
}
this.onClosed();
}
@SneakyThrows
public void onEvent(AgentBuilderResult abr) {
// AgentBuilderResult{answer='如果您还有其他需求,欢迎随时告诉我。', events=[Event{code='0', message='', eventType='ChatAgent', status='running', contentType='text', detail={text=如果您还有其他需求,欢迎随时告诉我。}}]}
// AgentBuilderResult{answer='', events=[Event{code='0', message='', eventType='function_call', status='done', contentType='function_call', detail={text={arguments={question=你希望写一首关于什么主题的诗句呢?有没有特别喜欢的诗句风格或者诗句中的某些元素?}, component_code=Workflow, component_name=闲聊}}}]}
// AgentBuilderResult{answer='', events=[Event{code='0', message='', eventType='function_call', status='done', contentType='function_call', detail={text={arguments={question=好的,那你能告诉我你希望诗句表达什么样的情感或主题吗?比如爱情、友情、人生哲理等。这样我可以更好地帮助你构思诗句。}, component_code=Workflow, component_name=闲聊}}}]}
// AgentBuilderResult{answer='', events=[Event{code='0', message='', eventType='function_call', status='done', contentType='function_call', detail={text={arguments={question=根据你的需求,我为你构思了这样一句诗句:'你是我心中永恒的日月星辰,甜蜜与幸福交织,默契与玩笑并存。'你觉得这句诗句符合你的期望吗?如果有什么需要修改或补充的地方,请随时告诉我。}, component_code=Workflow, component_name=闲聊}}}]}
// AgentBuilderResult{answer='根据你的需求', events=[Event{code='0', message='', eventType='ChatAgent', status='running', contentType='text', detail={text=根据你的需求}}]}
// ...
log.info("baidu appbuilder返回数据:{}", abr);
String uid = session.getPathParameters().get("uid");
ObjectMapper mapper = new ObjectMapper();
String content = abr.getAnswer();
if(content == null || "".equals(content)) {
return;
}
Message deltaMessage = Message.builder()
.content(content)
.build();
String data = mapper.writeValueAsString(deltaMessage);
session.getBasicRemote().sendText(data);
// 缓存返回消息
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(content);
}
}
@SneakyThrows
public void onClosed() {
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"));
}
}
MessageBackLocalCache.CACHE.remove(uid);
}
}
代码解读
效果展示

