One能聊天接入百度千帆大模型 —— 文心一言

news2025/4/24 0:20:58

One能聊天介绍:基于ChatGPT实现的微信小程序,适配H5和WEB端。包含前后端,支持打字效果输出流式输出,支持AI聊天次数限制,支持分享增加次数等功能
One能聊天开源地址:https://github.com/oldinaction/ChatGPT-MP
One能聊天演示环境:可关注【阿壹族】公众号,并回复【One能聊天】查看
下文将介绍在One能聊天项目中接入百度千帆大模型 — 文心一言

开通服务

首先介绍一下百度AI相关产品矩阵
如下图百度力推的千帆大模型超级工厂,他包含

  • 千帆大模型平台:其中大模型开发就是自己训练一个大模型,这种比较有技术含量,少部分企业才会用到;大模型调用则包含百度开放的文心大模型(即文心一言,ERNIE 4.0和ERNIE 3.5为模型版本分类,对标ChatGPT),还包括一些第三方模型供调用
  • 千帆AppBuilder:是提供开发者基于文心大模型可以快速开发出一个AI应用,创建的应用可以集成一些官方的组件(如天气查询、快递查询等),也可以集成自定义组件(通过画布拖拽,自行编排组件逻辑,如调用企业内部API或调用大模型接口),另外还可导入知识库供大模型使用(支持txt/pdf/doc/url等模式)。通过AppBuilder创建的应用官方提供一个访问链接供普通用户使用(界面是通用的AI聊天界面),开发者也可以通过SDK调用创建的AI应用从而集成到实际的业务系统中。这部分会在后续文章中做详细说明
  • 千帆AI原生应用商店:就是百度自己开发的AI应用。如超级助理,下载浏览器插件即可使用,支持划词翻译、网页解读、OCR识别等功能

本文主要对文心大模型ERNIE的API调用做详细说明
image.png
创建应用:进入 https://console.bce.baidu.com/qianfan/ais/console/applicationConsole/application 创建,可勾选启用的模型,如ERNIE-3.5-8KERNIE-4.0-8KYi-34B-Chat(免费)等
image.png
部分模型计费说明如下
image.png

单次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);
    }
}

调用测试
image.png

多轮对话流式输出

  • 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();
    }
}

效果展示

image.png

  • 相关文章:One能聊天接入百度千帆AppBuilder

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1823626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【算法专题--链表】相交链表--高频面试题(图文详解,小白一看就会!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐双指针 --- 数学思维 ⭐双指针 --- 按链表长度计算 &#x1f95d; 判断相交 &#x1f347; 求出交点 &#x1f34d;实现步骤 四、总结与提炼 五、共勉 一、前言 相交链表这道题&#xff0c;可以说是--链表专题--&#xf…

docker-compose部署FastDFS分布式文件系统

文章目录 一、技术选型二、fastDFS组成部分三、docker-compose文件四、客户端nginx配置五、存储器spring Boot集成参考文献 一、技术选型 还有一个更好的google FS&#xff08;但是他不开源&#xff0c;我也没找到社区版一类的可以不要钱使用的&#xff09;。 最后考虑到我们存…

通信设备的网卡

一、网卡的作用 将计算机或者路由器连接到传输介质上的接口&#xff0c;传输介质可以是有线也可以是无线的。 &#xff08;1&#xff09;计算机的网卡 现在的计算机大多有两个网卡&#xff0c;一个是有线网卡一个无线网卡&#xff0c;比如以我们的台式电脑为例 台式电脑千兆网…

推进现代化的财务计划,打造可持续的企业发展

现阶段一个重大问题是&#xff0c;大多数企业无法保持决策与规划水平的一致性&#xff0c;财务团队的现状难以支持复杂环境下的新型决策&#xff0c;从而造成了劳动力和资源的错误匹配。由于财务功能的过度扩展&#xff0c;企业难以持续发展。财务团队不得不为了企业的存亡不断…

【代码】数据类型之复合数据类型

Hello&#xff01;大家好&#xff0c;我是学霸小羊&#xff0c;今天讲讲数据类型之复合数据类型。 上一个博客讲了讲基本数据类型&#xff0c;今天讲讲复合数据类型&#xff0c;没学过基本数据类型的建议先去学一学哈。 【代码】数据类型之基本数据类型https://blog.csdn.net…

AI论文速读 | 2024[SIGIR]基于大语言模型的下一个兴趣点推荐

论文标题&#xff1a;Large Language Models for Next Point-of-Interest Recommendation 作者&#xff1a;Peibo Li ; Maarten de Rijke ; Hao Xue &#xff08;薛昊&#xff09;; Shuang Ao ; Yang Song ; Flora D. Salim 机构&#xff1a;新南威尔士大学(UNSW)&#xff0c…

MAC M1系统编译ffmpeg-gl-transition

MAC M1系统编译ffmpeg-gl-transition 1. 本人系统2. 编译&#x1f4b0;系统准备2.1. 下载【ffmpeg-gl-transition】到用户家目录下&#xff0c;并解压2.2 下载ffmpeg源码2.3. brew安装GLEW glfw32.4 复制vf_gltransition.c文件到ffmpeg2.5 修改ffmpeg源码文件2.6 设置库目录和…

这4款国产软件,因为太良心好用,甚至被误认为是外国人开发的

说起国产软件&#xff0c;大家总是容易给它们打上“流氓、要钱、广告满天飞”的标签&#xff0c;其实&#xff0c;有些小众的软件超级好用&#xff0c;功能强大又不耍流氓&#xff0c;真心不该被一棍子打死。 1、sunlight studio Sunlight Studio是一个开源、免费、无广告的硬…

【成品设计】基于物联网的停车管理系统设计与实现

《基于物联网的停车管理系统设计与实现》 整体功能&#xff1a; 本次课题中&#xff0c;主要设计的是一款基于物联网技术的校园停车的管理系统&#xff0c;该系统能更方便得让管理员对停车场进行管理&#xff0c;同时也能够满足和方便用户使用。针对此种现象&#xff0c;就需…

【面试干货】抽象类和接口的区别

【面试干货】抽象类和接口的区别 1、抽象类1.1、什么是抽象类&#xff1f;1.2、示例代码 2、接口2.1、什么是接口&#xff1f;2.2、示例代码 3、比较和总结3.1、使用场景3.2、关键区别3.3、代码示例比较 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&am…

大规模装箱问题:蜣螂优化算法DBO求解二维装箱问题(MATLAB代码)

一、问题描述 装载率:所有选择的箱子的总面积与夹板面积之比 假设一共有300个箱子&#xff0c;如何设计算法&#xff0c;使得选择部分箱子放入80*80的甲板上&#xff0c;让甲板的装载率越大&#xff0c;要求箱子间不得重叠。 二、蜣螂优化算法求解二维装箱问题 蜣螂优化算法…

八 、VS的调试技巧

--- 24.4.20 目录 1、什么是Bug&#xff1f; 2、什么是调试&#xff08;Debug&#xff09;&#xff1f; 3、Debug和Release 4、VS基础调试快捷键 4.1、环境准备 4.2、调试快捷键 5、监视和内存观察 5.1、监视窗口 5.2、内存窗口 6、调试举例1 7、调试举例2 8、调试…

AI宣传文案软件有哪些?5款AI软件推荐

AI宣传文案软件有哪些&#xff1f;AI宣传文案软件在现代营销和创意产业中扮演着越来越重要的角色&#xff0c;它们凭借先进的自然语言处理、机器学习和深度学习技术&#xff0c;不仅解放了创作者的双手&#xff0c;还大大提升了文案的生成效率和质量。这些软件能够精准捕捉用户…

Polarion重启时自动删除.config

Polarion二次开发的插件中&#xff0c;对Servlet新增、修改或删除时&#xff0c;发布到应用需要删除.config文件才能使修改生效&#xff0c;每次需要手动执行&#xff0c;不如加到restart启动脚本中。只需修改start函数即可。 代码如下&#xff1a; vi /opt/polarion/bin/pola…

(科学:某天是星期几)泽勒一致性是由克里斯汀·泽勒开发的用于计算某天是星期几的算法。

(科学:某天是星期几)泽勒一致性是由克里斯汀泽勒开发的用于计算某天是星期几的算法。这个公式是: 其中: h是一个星期中的某一天(0 为星期六;1 为星期天;2 为星期一;3 为星期二;4 为 星期三;5 为星期四;6为星期五)。 q 是某月的第几天。 m 是月份(3 为三月&#xff0c;4 为四月,…

包装类的应用

一.什么是包装类 基本数据类型所对应的引用数据类型 二.集合中不能存储基本数据类型 三.JDK5以后对包装类新增了什么特性&#xff1f; // 自动装箱:把基本数据类型会自动的变成对应的包装类 // 自动拆箱:把包装类自动的变成其对象的基本数据类型 四.我们以后如何获取包…

课设--学生成绩管理系统

欢迎来到 Papicatch的博客 文章目录 &#x1f349;技术核心 &#x1f349;引言 &#x1f348;标识 &#x1f348;背景 &#x1f348;项目概述 &#x1f348; 文档概述 &#x1f349;可行性分析的前提 &#x1f348;项目的要求 &#x1f348;项目的目标 &#x1f348;…

全面分析kimi和ChatGPT在论文写作方向的差别

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 Kimi和ChatGPT都是人工智能助手&#xff0c;致力于提供帮助和信息&#xff0c;但在论文写作方面&#xff0c;他们在下面几个方向的能力存在一些差别&#xff1a; 1. 语言支持&#xff…

英语学习笔记35——Our village

Our village 我们的村庄 词汇 Vocabulary photograph n. 照片 通常说&#xff1a;photo 复数&#xff1a;photos     picture 复数&#xff1a;pictures 搭配&#xff1a;take a photo 照相 以o结尾的单词复数es的&#xff1a; potato —— potatoes tomato —— tomatoe…

AI + Web3 Social Hackathon 来啦!报名参赛瓜分超 700,000+ 美元奖金池

“我们的目标是将 Web3 优质开发者聚集在一起&#xff0c;共同打造创新思维驱动的链上世界。无论你是经验丰富的开发者&#xff0c;还是刚踏上开发旅程的小白新手&#xff0c;都可以加入我们&#xff0c; 不断提升实战技能&#xff0c;让 AIWeb3 成为赋能未来的重要砝码。” 为…