我开发了一个温柔的智能客服聊天机器人ChatBot,并回答为什么不是ChatGPT(附思路和代码)

news2024/11/14 17:36:46

前言

若问2023年科技领域什么最火,那当然是ChatGPT了,这么智能的对话机器人,给人带来无限的想象,围绕着ChatpGPT的各种热点和创意层出不穷。作为一个多年从事编程开发的程序员,我对于这么大的一个热点也很兴奋,每天琢磨着围绕ChatGPT干点啥。

当然还是先要认清现实,心再高也不能想着去开发一个ChatGPT一样的东西。这个投入太大,难度太高,成果太不可预料,团队、成本、技术、模型、算力、数据、安全、法规等等,每一项对于中小型企业都是重大挑战。国内也只有几个IT巨头能玩,能玩到什么程度不知道,就像某度啊,纯属瞎凑热闹。那么多企业非得去凑热闹,非的要去沾个亲,这个是无可厚非的,不管是博人眼球,还为了是抬高身价,作用还是立竿见影的,凑热闹就凑吧。

思量一番,ChatGPT开发不出来,开发一个它的近亲ChatBot还是没有多大难度的。于是亲自考察了一下公司线上客服的聊天内容,大致如下:

问:你好
答:您好
问:你们有xxx产品吗
答:有啊
问:多少钱
答:您留个电话吧,打给您
问:怎么联系你们
答:我们电话138...
...

再细细分析一下近年来的客服聊天记录,竟然大部分对话都相似,相同的问题和答复,时刻都在重复上演!我的天啊,这也太程式化了吧,让我们客服天天干这种事请也太浪费资源了!不行,我要改变这种情形!

问一下客服们,之前用过智能客服对话机器人吗?她们说是用过啊,用过阿某云的、华某云的,不太好用,经常是答非所问、似是而非,很少获客。那就定了,我要给你们开发一个好用的Chatbot智能客服,彻底解放生产力!

大话说出来了,就只有去干了。经过两周的努力,终于实现了一个线上智能客服系统。虽然不是太智能,还算温柔可人,能回答常见的客户问题,比如:

你好
你好,我在呢
你在哪
我在北京啊
你贵姓
我叫云云啊,你呢?
发个产品报价吧
报价你还是打电话问吧
电话多少
电话是 136xxxxxxxx

哈哈,跟我们客服回答的有点像啊,有兴趣可以跟她聊聊: ​​http://v.ruiboyun.cn/chat/​​

我建议您先不要急着去跟这个机器人聊天,她并不是很聪明,还是希望您继续往下看。

技术选型

理想很丰满,现实须努力,为难于易,为大于细,那就一步一步的来吧。

技术架构

终端类型:浏览器,客户通过浏览器访问公司网站和浏览公司产品,聊天对话的场所就是浏览器中。

通信协议:Websocket,最适合进行双向交互,低延迟,高效率,没有更好的选择了。

后端框架:Java、PHP、Nodejs,好像都可以,但若要是做即时通信,前端面向浏览器,那Nodejs应该是不二之选。为啥?前后端用一种语言,前后端可以共用代码,前后端工程师可以复用,配合起来就像左手拉右手,你说香不香。使用Nodejs,就可以使用Socket.IO来进行聊天通信,那就更简单了。当然还有别的理由,往后看。

技术选型确定后,后面的工作都是基于这个技术框架来开展,我就不再做解释。

全文搜索引擎(full-text search)

做客服型Chatbot,全文搜索引擎是必须的,大量的聊天语料包准备好后,需要有高效灵活的搜索引擎来查找对应的内容。

可用的全文搜索引擎系统很多,最终我把选择范围缩小到如下两个:

1)RediSearch

2)FlexSearch

RediSearch是Redis的一个插件模块,用于实现全文搜索,具有开源、高效、多字段检索、精确短语匹配、搜索结果聚集等特点。

参考网址:​​https://redis.io/docs/stack/search/​​

FlexSearch是一个Nodejs模块,具有开源、轻量、纯JS、零依赖、内存内索引等特点。

参考网址:​​https://github.com/nextapps-de/flexsearch​​

仔细对比两个产品,RediSearch明显具有成熟、灵活、参考资源多等优势,如果要做商用产品或长远打算,那就应该选择RediSearch。

再看FlexSearch的介绍和API接口,除了参考文档少的可怜、成熟案例少之外,好像也没有什么大的不足,也能满足我的要求。更有吸引力的是轻量、JS源码、有了问题可以追根溯源的去修改,这恰恰符合我的脾气。

那就选择FlexSearch吧,虽然我选择FlexSearch,并不代表它是最好的,如果你要做更高级的商用系统,我还是建议你用RediSearch。

中文分词(tokenization&segmentation)

使用全文搜索引擎,一个重要的工作就是分词。索引过程是先对原始文本进行分词,然后对分词结果索引;搜索过程是先对问句进行分词,在使用分词结果去查询。

中文分词相对于拉丁语等其他语言的分词有很大的不同,拉丁语使用空格、标点符号作为分词标记,每个单词具有明确意义,因此分词过程要简单的多。

而中文分词则要复杂抽象一些,每个独立汉字放到不同的词语中意义就不同,若以每个汉字作为分词结果,想要在海量的全文中搜索你期望的结果是不可预期的,干扰噪声会完全淹没你的期望结果。

中文分词需要向后扫描要处理的文本序列,将单字、多字组合与字典中的词语(或你自己预定义的词语、术语)来对比,以此确定分词位置和分词结果。

于是开始找中文分词器,还好,我们要选择的两个全文搜索系统都有可用的中文分词器。

RediSearch使用了​​Friso​​库进行中文分词,Friso 是使用 c 语言开发的一款开源的高性能中文分词器,使用流行的mmseg算法实现。完全基于模块化设计和实现,可以很方便的植入其他程序中。到Github浏览了一下这个库,发现相当不错!

参考网址:​​https://github.com/lionsoul2014/friso​​

FlexSearch则要更加灵活,可以自己选择好用的中文分词库。上述Friso之封装了了php5, php7, ocaml, lua的插件,并没有封装Nodejs包,若要使用需要自己封装,这个可以参考Nodejs的N-API或Node-Addon-API。对于我目前的开发项目,当然还用不着再去将Friso封装成Nodejs插件,这个有点走弯路了。在Nodejs家族里只要找一款中文分词库,那应该是很容易的,果不其然,github搜一下就有很多选择。

nodejieba星最多,拿来测试一下,感觉分词效果还可以,就用它吧。

文档结构

技术选型做好之后,开始设计聊天语料的文档结构。我们把每一轮聊天互动(一问一答)的内容定义为一个文档,使用json对象来描述。文档结构定义如下:

{
  id:1,										//文档编号,用于唯一标记一个文档
  tag:"business",         //标签,用于将语料进行分组归类
  query:"你好",           //问句
  answer:"你好,有何吩咐?"//答句
}

所有文档保存在数组中:

[
  {
    id:1,										//文档编号,用于唯一标记一个文档
    tag:"business",         //标签,用于将语料进行分组归类
    query:"你好",           //问句
    answer:"你好,有何吩咐?"//答句
	},
  {
    id:2,										//文档编号,用于唯一标记一个文档
    tag:"business",         //标签,用于将语料进行分组归类
    query:"你贵姓",          //问句
    answer:"我叫云云"       //答句
	}
]

文档的query属性会交给全文搜索引擎进行索引,搜索过程就是搜索匹配的问句的过程,搜索到匹配问句后,将answer内容反馈给提问方。

对于海量的语料,文档问句词语的重复会是常见的,一个问句可能会返回多个结果,因此需要有手段选择最佳匹配,尽量让答复接近提问者的期望。这是一个较为复杂的问题,后面会有进一步说明。

实现细节

技术选型做好了,文档结构也设计好了,下面就是编码实现了,好激动啊。前一周参考了那么多技术,论证也比较充分了,所以我计划再花1周时间来开发实现。

后端服务

Nodejs+socketio,主要实现即时文本通信,还需要实现一些其他WebAPI接口,这个对于熟悉Nodejs的工程师是小菜一碟。

主逻辑代码app.js:

const chatter= require('./chatter');
const loader = require("./searcher/loader");
var express  = require('express');
var app   = express();
var http  = require('http');

//http + socketio 
var server = http.createServer(app);
var io = require('socket.io')(server);

const serverPort = 3000;
server.listen(serverPort, function(){
  console.log("接口服务已启动,端口:",serverPort);
});

/**
 * 消息结构体  答句时code非0时表示没有对应答案,问句时省略
 * {
 *    code:0,
 *    msg:"hello"
 * }
 */

//加载语料
loader.loadall(chatter);

io.on("connection", (socket) => {
  socket.emit("message", {msg:"你好"});

  // receive a message from the client
  socket.on("message", (data,callback) => {
    let msg = data.msg;
    /**
     * 问句 交给聊天机器人处理,返回 答句
     */
    let response_msg = chatter.chat(msg);
    let response_data = {
      code:response_msg?0:1,
      msg:response_msg
    };
    callback(response_data);
  });
});

下面就是聊天机器人的核心逻辑了。实现一个全文搜索引擎封装文件 chatter.js

//搜索引擎
const { Index, Document, Worker } = require("flexsearch");
//分词器
var nodejieba = require("nodejieba");

const regex = /[\x00-\x7F]+/g;
function encode(str) {
    str = ("" + str).replace(regex, "");
    let len = str.length;
    if (len < 0) return [];
    if (len == 1) return [str];
    let arr = nodejieba.cut(str);
    return arr;
}

const limit = 5;   //搜索结果最多返回数

const documents = [];   //存储所有文档
var id = 0;    //id递增器

//创建全文索引器,具体参见flexsearch用法
const options = {
    preset: "default",
    tokenize: "strict",
    language: "zh",
    encode: encode,            //引入分词器
    bool: "or",
    document: {
        id: "id",
        tag: "tag",
        index: "q"
    }
};
const index = new Document(options);

/**
 * 添加语料接口
 */
function add(tag, q, a) {
    id++;
    documents[id] = { id: id, tag: tag, q: q, a: a };
    return index.add({ id: id, tag: tag, q: q });
}

/**
 * 查找
 * @param {string} text 
 * @param {string} tag 
 */
function search(text, tag) {
    let opt = {
        index: "q",
        limit: limit,
    };
    if (tag) opt.tag = tag;
    return index.search(text, opt);
}

//获取文档
function get(id) {
    return documents[id];
}

function chat(msg) {
    let result = search(msg, tag);
    if (result.length > 0) {
        let ids = result[0].result;
        let id  = ids[0];
        let doc = get(id);
        return doc.a;
    } else {
        return null;
    }
}

module.exports = { add, get, chat }

可以看到,在这个module中实现了较多的内容,包括创建全文索引系统、创建分词器,以及添加语料接口、查询接口、文档获取接口等。

最终能力的输出通过chat接口实现。看chat函数代码,原理简单,根据问句检索答句,如果有多条,则返回第一条。

语料的加载,在app.js通过实现一个loader来实现的:

//加载语料
loader.loadall(chatter);

loader的任务是将语料库加载到内存中,然后逐条传递给全文索引系统进行索引。

前端页面

前端页面找我们前端美女实现,设计一个聊天界面那是分分钟的事情了,机器人的回答有一个汽包等待动画,就像ChatGPT那样,那是机器人在检索数据。

前端代码,通信部分大致是这样的:

import { io } from "socket.io-client";

const socket = io("ws://localhost:3000");

// send a message to the server
socket.emit("message",{msg:"你好啊"},(data)=>{
  if(data.code==0){
    //渲染聊天数据
  }
});

效果提升

人任何时候都不能高估自己啊,要时刻提醒自己爬得高摔得重。通过几天的努力,我的Chatbot已经可以给你聊条对话了,可是效果如何呢,评测一下大致如下:

你好
你好
你在哪里
我在北京
上海在哪里
我在北京
科灵顿在哪里
我在北京

简直是无聊,很多问题的答复落在要给答案上!

其实这是预料之中的事情,为啥,一是因为语料库内容有限,二是检索结果没有经过任何优化。Flexsearch对多关键字的检索(multi-search)打分,偏离的离谱,只能自己去优化。既然是开源的,那就可以优化,或者变着法子使用你的优点,绕开你的缺点。

优化一,找最佳匹配结果

如果查到多个记录,那就看看哪个匹配度最高,怎么计算匹配度高低呢?用了与i个简单的方法,那就是问句中的所有分词在哪个答案里出现的次数最多,就选则那个答案。

于是对查询过程进行优化:

1)搜索前主动分词、去重、排除干扰字词

2)搜索结果对比,寻找出现频次最多的结果

于是,在chatter中实现如下搜索接口:

/**
 * 复杂搜索入口,输入一个整句,这里进行分词、去重、搜索、合并
 * 同步函数
 * @param {string} text 
 * @param {string} tag 
 * @returns 
 */
function complexSearch(text,tag){
    let words = encoder.encode2(text);
    let arrIds= [];
    let keys  = [];

    if(!words ||words.length==0){
        return null;
    }

    if(words.length>keyLimit){
        //去掉1字词
        words.forEach(element => {
            if(element.length>1){
                keys.push(element);
            }
        });
    }else{
        keys = words;
    }
   
    //限制搜索词个数
    if(keys.length>keyLimit){
        keys = keys.slice(0,keyLimit);
    }

    //多次搜索,不使用flexsearch的multi-search
    keys.forEach(key=>{
        let result = search(key,tag);
        if(result.length>0){
            let ids = result[0].result;
            arrIds.push(ids);
        }
    });
    
    //取最优结果
    let result = null;
    if(arrIds.length==1){
        result = arrIds[0];
    }else if(arrIds.length>1){
        result = arrUtils.mixmix(arrIds);
    }

    return result;
}

优化后,测试一下,果然准确度大幅攀升,基本上答复符合预期。

优化二,优化商务语料包,定义自有词库

这一步也很重要,商务语料包是用来回答客户问题的,尽量要简洁、通俗(符合大众问句习惯)、去除干扰词。

例如,下面问句,显然第二句更好些:

你们公司的产品资料给我发一份吧
产品资料发一份

然后就是定义词库,把客户常用词、公司的产品和术语做成一个词典给分词器,让按照自定义词典分词,这样检索命中率就会更好。

优化三,上下文相关搜索

上下文相关搜索是把相关的内容放置在更优先的反馈结果里,这样智能机器人返回的结果就更像真人的聊天内容,想想看,如果一次聊天对话能够仅仅围绕相同或相近的话题,是不是更有趣。

语料库包

语料库里放着所有的问句和答句,如果要让chatbot更加博学和聪明,就要不断丰富你的语料库。语料库的答句风格也就是chatbot的风格,她可以是个温柔的助理,也可以是个野蛮的匹夫,这些取决于你的语料内容。

语料库我把它分成两个部分,用tag进行标注。一个是专用商务语料包,里面涵盖了各类客户常见问题和答案,这部分由我峨嵋你公司的商务人员负责提供和优化,并且不断丰富。一个是闲聊语料包,用于与客户闲聊,应对一些商务问题之外的问题,这个可以从网上搜,也可以通过商业渠道获取。

最终,我们编辑了1000条商务语料,并从网上找了100万条闲聊语料。100万条多吗,其实不多,全文搜索引擎几个毫秒就可以索索一边,不用担心效率问题。

最后的问题,为什么不是ChatGPT

前面已经说过了,研发类ChatGPT系统,需要的是人才、投入和海量的语料/资料,以及大量的语料清洗校正工作,所以一般的企业炒炒概念也就算了,这个艰巨的任务还是留给我们的科技巨头吧。

ChatGPT是美国人工智能研究实验室OpenAI新推出的一种人工智能技术驱动的自然语言处理工具,是全球技术精英经过多年技术积累,花费数十亿美刀研发出的东西,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。ChatGPT使用了Transformer神经网络架构,这是一种用于处理序列数据的模型,拥有语言理解和文本生成能力,尤其是它会通过连接大量的语料库来训练模型,这些语料库包含了真实世界中的对话,使得ChatGPT具备上知天文下知地理,还能根据聊天的上下文进行互动的能力,做到与真正人类几乎无异的聊天场景进行交流。

ChatGPT受到关注的重要原因是引入新技术RLHF (Reinforcement Learning with Human Feedback,即基于人类反馈的强化学习)。RLHF 解决了生成模型的一个核心问题,即如何让人工智能模型的产出和人类的常识、认知、需求、价值观保持一致。ChatGPT是AIGC(AI- Generated Content,人工智能生成内容)技术进展的成果。

抛开算法、算力的技术壁垒和投入,我们单从语料说起,ChatGPT使用的语料是数百亿条,除了聊天语料,还有海量的新闻资讯、天文地理、音乐绘画、财经政治等等书籍资料,也包括我们中文的大部分历史文集、诗词歌赋等。

传统的Chatbot不具备、或者具备有限的智能,主要任务还是搜索和回答,再进一步就是关联上下文环境的互动聊天,以及可插入任务中间件的互动聊天,譬如问天气、问路况、问航班、问行情、电器设备控制、行业问题答疑等等,都在传统聊天机器人的范畴之内。而ChatGPT,则是具有了学习、模仿、关联、归纳总结、创造等能力。

好了,聊到这里也算结束了,我的Chatbot还有很多地方需要优化提升,希望您给出宝贵意见哦,您现在可以给她聊几句了:​​http://v.ruiboyun.cn/chat/​​

这个机器人脑路有限,多多包涵 ~:)

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

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

相关文章

视频虚拟主播怎们搞?体验报告全记录;一图掌握SD应用精髓;Chat效率工具大汇总;品牌营销进入AI时代 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 『Stable Diffusion界面解读』一张图掌握SD使用精髓 ShowMeAI资源编号&#xff1a;No.R052 图片可能被平台压缩&#xff0c;加入知识星…

安装配置 JupyterLab ubuntu20.04

目录 ​编辑 &#xff08;1&#xff09;安装 &#xff08;2&#xff09;配置 &#xff08;1&#xff09;生成配置文件 &#xff08;2&#xff09;生成jupyterlab的登录密码 &#xff08;3&#xff09;修改 jupyter 的配置文件 &#xff08;4&#xff09;安装 jupyterlab…

Mybatis(三)

1、mybatis中的连接池以及事务控制 原理部分了解&#xff0c;应用部分会用 mybatis中连接池使用及分析 mybatis事务控制的分析2、mybatis基于XML配置的动态SQL语句使用 会用即可 mappers配置文件中的几个标签&#xff1a; <if> …

Linux网络编程 第八天

目录 学习目标 内容回顾 完善网页服务器 中文乱码问题 服务器中断处理 读取目录文件 BS模式示意图 Web服务器开发流程图 日志服务器 Libevent下的网页服务器 学习目标 第八天主要是在第七天的基础上&#xff0c;完善网页服务器的设计&#xff0c;学习日志服务器以及li…

MySQL中distinct和group by性能比较

distinc的使用 用法 select distinct columns from table_name where where_conditions;示例&#xff1a; DISTINCT 用于返回唯一不同的值&#xff08;即去重后的值&#xff09; &#xff0c;使用时需要放在查询语句中第一个查询字段前使用。如果列有NULL值&#xff0c;会将所…

C语言/C++随机数生成,程序运行时间计时器(含高精度计时器),包括Windows环境与Linux环境

&#x1f38a;【数据结构与算法】专题正在持续更新中&#xff0c;各种数据结构的创建原理与运用✨&#xff0c;经典算法的解析✨都在这儿&#xff0c;欢迎大家前往订阅本专题&#xff0c;获取更多详细信息哦&#x1f38f;&#x1f38f;&#x1f38f; &#x1fa94;本系列专栏 -…

工厂模式概述

通常有三种形态: 简单工厂模式&#xff0c;不属于23种设计模式之一 工厂方法模式&#xff0c;是23种设计模式之一 抽象工厂模式&#xff0c;是23种设计模式之一 1.简单工厂模式是工厂模式的一种特殊实现&#xff0c;又被称为静态工厂方法模式 2.简单工厂模式解决的问题:客户端不…

【Verilog HDL】FPGA-Verilog文件的基本结构

&#x1f389;欢迎来到FPGA专栏~Verilog文件的基本结构 ☆* o(≧▽≦)o *☆嗨~我是小夏与酒&#x1f379; ✨博客主页&#xff1a;小夏与酒的博客 &#x1f388;该系列文章专栏&#xff1a;FPGA学习之旅 文章作者技术和水平有限&#xff0c;如果文中出现错误&#xff0c;希望大…

SSM框架MyBatis 三种分页查询 PageHlper的使用以及五个参数的简单解释

SSM框架MyBatis 三种简单的分页查询 1. 基础分页查询&#xff08;环境在第一天的配置中有&#xff09; mapper也就是dao //查询总数Select("select count(*) from book;")int selectCount();//分页查询Select("select * from book limit #{currpage},#{size}&q…

windows快捷键汇总

Windows 系统中有很多常用的快捷键&#xff0c;这些快捷键可以帮助我们快速完成一些操作&#xff0c;提高我们的工作效率。下面是一些使用 Windows 快捷键的好处和长期利弊&#xff1a; 好处&#xff1a; 可以快速完成一些操作&#xff0c;提高工作效率。可以让我们的工作更加…

2023年RHCE第二次作业

1.配置ntp时间服务器&#xff0c;确保客户端主机能和服务主机同步时间 2.配置ssh免密登陆&#xff0c;能够通过客户端主机通过redhat用户和服务端主机基于公钥验证方式进行远程连接 1配置Chrony服务器 先下载chrony--------dnf install -y chrony 查看和配置chrony.conf文件 …

Centos7 安装mysql 8.0.32版本(解压glibc版本)

Centos 7 安装 MySQL 8.0.32 glibc 版本总结 Centos7中安装MySQL服务时&#xff0c;首先需要卸载掉mariadb&#xff0c;mariadb可能会与MySQL产生冲突。 1、卸载mariadb 查找mariadb是否已经安装&#xff08;默认已经安装&#xff09; rpm -qa | grep mariadb接下来将查找到…

QWidget改变背景图的方法和坑

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、添加背景图资源文件二、使用 QPalette三、重写paintEvent() 函数四、使用QT的设计师界面总结 前言 本篇文章将讲解QWidget改变背景图的方法和会遇到的问题…

Flutter插件开发-(基础篇)

在开发flutter项目的时分通常会运用一些三方的的packages或许plugin&#xff0c;二者的区别&#xff1a;packages主要是包括的Dart代码块&#xff0c;而plugin则包括iOS和android的代码。 因此来说创立plugin和packages的流程是相似的&#xff0c;下面就以创立plugin为例进行展…

Spring原理学习(六):Spring实现动态代理时对jdk和cglib的选择

目录 〇、前言 一、AOP中的一些基本概念 二、两个切面的概念 三、advisor的使用 3.1 前置知识 3.2 使用步骤 四、spring对jdk和cglib的统一 〇、前言 对jdk和cglib 实现动态代理的原理不清楚的兄弟们&#xff0c;可以参考前文&#xff1a;Spring原理学习&#xff08;…

Python+Qt人脸识别职工录入管理系统

程序示例精选 PythonQt人脸识别职工录入管理系统 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对<<PythonQt人脸识别职工录入管理系统>>编写代码&#xff0c;代码整洁&#xff0c…

【FTP】——文件传输协议

文章目录 1.FTP简介1.1 FTP概述1.2 FTP主动模式1.3 FTP被动模式 2. 实例&#xff1a;匿名用户访问FTP服务3. 实例&#xff1a;本地用户访问FTP服务 1.FTP简介 1.1 FTP概述 FTP服务——用来传输文件的协议。 FTP服务器默认使用TCP协议的20、21端口与客户端进行通信. 20端口…

【学习笔记】Linux基础

Linux基础 一、操作系统1、什么是操作系统2、Linux操作系统3、Linux系统目录&#xff0c;Linux倒挂树型目录结构&#xff1a;4、安装Xshell与Xftp5、Linux文件操作命令6、vim文本编辑器&#xff08;1&#xff09;vim三种模式&#xff08;2&#xff09;vim重要快捷键&#xff08…

Dell Inspiron 5570电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板Dell Inspiron 5570 处理器Intel(R) Core(TM) i7-8550U CPU 1.80GHz已驱动 内存8 GB 2400 MHz DDR4已驱动 硬盘samsung ssd 850 evo 250 go已驱…

汽车跨界还能这么玩?解锁汽车跨界新模式

跨界&#xff0c;是一种商业思维&#xff0c;是出圈方式&#xff0c;是进入流量红海的手段之一。近几年来&#xff0c;国内掀起了一股跨界风&#xff0c;“跨界增值宣传”似乎成为了品牌年轻化的必经之路&#xff0c;众多品牌通过跨界的方式实现互相补足、用户渗透&#xff0c;…