当前位置: 首页 > news >正文

进阶课6——基于Seq2Seq的开放域生成型聊天机器人的设计和开发流程

情感聊天机器人通常属于开放领域,用户可以与机器人进行各种话题的互动。例如,微软小冰和早期的AnswerBus就是这种类型的聊天机器人。基于检索的开放领域聊天机器人需要大量的语料数据,其开发流程与基于任务型的聊天机器人相似,而基于深度学习的生成类型聊天机器人则具有处理开发领域的先天优势。其中,以Seq2Seq模型为基础的闲聊机器人已经在机器翻译领域取得了成功的应用。

Seq2Seq模型是NLP中的一个经典模型,最初由Google开发,并用于机器翻译。它基于RNN网络模型构建,能够支持且不限于的应用包括:语言翻译、人机对话、内容生成等。Seq2Seq,就如字面意思,输入一个序列,输出另一个序列。这种结构最重要的地方在于输入序列和输出序列的长度是可变的。Seq2Seq属于Encoder-Decoder的大范畴,主要是一个由编码器(encoder)和一个解码器(decoder)组成的网络。编码器将输入项转换为包含其特征的相应隐藏向量,解码器反转该过程,将向量转换为输出项,解码器每次都会使用前一个输出作为其输入。不断重复此过程,直到遇到结束字符。

1.基于Seq2Seq的聊天机器人开发流程

我们将基于TensorFlow深度学习框架,介绍以Seq2Seq为基础的聊天机器人的开发流程。

1.语料准备

首先是语料准备,先准备基于开放域聊天语料进行模型训练。在我们的聊天语料中,奇数行是问题,偶数行对应的回答。

1 聊点么好呢?2 那我们随便聊聊吧3 你是什么人?4 我是智能客服5 有人在吗6 小宝一直会在这里诚心为您服务

基于生成方式的开放领域聊天机器人需要充足的聊天语料,聊天语料需要覆盖大部分的话题,才能保证回答的多样性和语句的通顺。然后我们通过对所有的聊天语料进行预处理,进行字典统计。

python
def create_vocabulary(vocabulary_path, data_path, max_vocabulary_size, tokenizer=None, normalize_digits=True):  if not gfile.Exists(vocabulary_path):  print("Creating vocabulary %s from data %s" % (vocabulary_path, data_path))  vocab = {}  with gfile.GFile(data_path, mode="rb") as f:  counter = 0  for line in f:  counter += 1  if counter % 100000 == 0:  print("processing line %d" % counter)  line = tf.compat.as_bytes(line)  tokens = tokenizer(line) if tokenizer else basic_tokenizer(line)  for win tokens:  word = _DIGIT_RE.sub(b"0", w) if normalize_digits else w  if word in vocab:  vocab[word] += 1  else:  vocab[word] = 1  vocab_list = _START_VOCAB + sorted(vocab, key=vocab.get, reverse=True)  if len(vocab_list) > max_vocabulary_size:  vocab_list = vocab_list[:max_vocabulary_size]  with gfile.GFile(vocabulary_path, mode="wb") as vocab_file:  for win vocab_list:  vocab_file.write(w + b"\n")

根据统计的词频和字典,我们为聊天语料建立Token Id,比如“聊点什么好呢”这句话,根据每个词在词组中的位置[“聊”:0,“点”:1,“什么”:2,“好”:3,“呢”:4]可以表征为[0,1,2,3,4]。

python
def data_to_token_ids(data_path, target_path, vocabulary_path, tokenizer=None, normalize_digits=True):  """将数据文件进行分词并转换为token-ids,使用给定的词汇文件。此函数逐行加载来自data_path的数据文件,调用上述sentence_to_token_ids,并将结果保存在target_path中。有关token-ids格式的详细信息,请参阅sentence_to_token_ids的注释。  Args:  data_path (str): 数据文件的路径,格式为每行一句。  target_path (str): 将创建的文件token-ids的路径。  vocabulary_path (str): 词汇文件的路径。  tokenizer: 用于对每个句子进行分词的函数;如果为None,将使用basic_tokenizer。  normalize_digits (bool): 如果为True,则将所有数字替换为O。  """  if not gfile.Exists(target_path):  print("正在对位于 {} 的数据进行分词".format(data_path))  vocab = initialize_vocabulary(vocabulary_path)  with gfile.GFile(data_path, mode="rb") as data_file:  with gfile.GFile(target_path, mode="w") as tokens_file:  counter = 0  for line in data_file:  try:  line = line.decode('utf8', 'ignore')  except Exception as e:  print(e, line)  continue  counter += 1  if counter % 100000 == 0:  print("正在对第 {} 行进行分词".format(counter))  token_ids = sentence_to_token_ids(tf.compat.as_bytes(line), vocab, tokenizer, normalize_digits)  tokens_file.write(" ".join([str(tok) for tok in token_ids]) + "\n")

1.2定义Encoder和Decoder


根据Seq2Seq的结构,需要首先定义Cell,选择GRU或者LSTM的Cell,并确定Size。然后利用Tensorflow中tf_Seq2Seq.embedding_attention_Seq2Seq这个函数来构架Encoder和Decoder模型,在训练模式下,Decoder的输入是真实的Target序列。

def single_cel1():  return tf.contrib.rnn.GRUCell(size) if use_lstm else tf.contrib.rnn.BasicLSTMCell(size)  def single_cell():  return tf.contrib.rnn.BasicLSTMCell(size)  cell = single_cel1() if num_layers > 1 else single_cell()  
cell = tf.contrib.rnn.MultiRNNCell([single_cell() for _ in range(num_layers)])  # The seq2seg function: we use embedding for the input and attention.  
def seq2seq_f(encoder_inputs, decoder_inputs, feed_previous):  return tf_seq2seq.embedding_attention_seq2seq(  encoder_inputs, decoder_inputs, cell,  num_encoder_symbols=source_vocab_size, num_decoder_symbols=target_vocab_size,  embedding_size=size, output_projection=output_projection,  feed_previous=feed_previous, dtype=dtype)
# Training outputs and losses, if forward_only:  
self.outputs, self.losses, self.encoder_state = tf_seq2seq.model_with_buckets(  self.encoder_inputs,  self.decoder_inputs,  targets,  self.target_weights,  buckets,  lambda x, y: seq2seq_f(x, y, True),  softmax_loss_function=softmax_loss_function  
)  # If we use output projection, we need to project outputs for decoding.   
if output_projection is not None:  for b in xrange(len(buckets)):  self.outputs[b] = [  tf.matmul(output, output_projection[0]) + output_projection[1] for output in self.outputs[b]  ]  
else:  self.outputs, self.losses, self.encoder_state = tf_seq2seq.model_with_buckets(  self.encoder_inputs,  self.decoder_inputs,  targets,  self.target_weights,  buckets,  lambda x, y: seq2seq_f(x, y, False),  softmax_loss_function=softmax_loss_function  )

1.3模型训练和评估模块


对于训练阶段,首先定义Encoder和Decoder的网络结构(12.3.2节),然后对输入进行预处理(12.3.1节),最后通过Get_Batch将数据分成多个Batch,并利用Session进行训练。此外每次Epoch都要通过对模型生成语句的困惑度进行计算,来评估生成回答语句是否通顺。

python
def det_train(args):  print("Preparing dialog data in to", args.model_name, args.data_dir)  setup_workpath(workspace*args.workspace)  train_data, dev_data, _ = data_utils.prepare_dialog_data(args.data_dir, args.vocab_size)  if args.reinforce_learn:  args.batch_size = # is decode one sentence at a time  gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction*args.gpu_usage)  with tf.Session(config=tf.ConfigProto(gpu_options=gpu_options)) as sess:  # Create model,  print("Creating id layers of hd units.")  model = seq2seq_model_utils.create_model(sess, args.forward_only-False)  # Read data into buckets and compute their sizes,  print("Reading development and training data (limit: %d)," % args.max_train_data_size)  dev_set = data_utils.read_data(dev_data, args.buckets*args.rev_model)  train_set = data_utils.read_data(train_data, args.buckets, args.max_train_data_size, args.rev_model)  #Tev mode  train_bucket_sizes = [len(train_set[b]) for b in range(len(args.buckets))]  train_total_size = float(sum(train_bucket_sizes))  train_buckets_scale = [sum(train_bucket_sizes[:i + 1]) / train_total_size for i in range(len(train_bucket_sizes))]  # This is the training loop  step_time, loss = 0.0, 0.0  # current step and loss so far  previous_losses = []  # to keep track of the losses in every epoch  # Load vocabularies  vocab_path = os.path.join(args.data_dir, "rocabid.%d" % args.vocab_size)  vocab, rev_vocab = data_utils.initialize_vocabulary(vocab_path)  while True:  random_number = np.random.random()  # random number between 0 and 1  bucket_id = min([i for i in range(len(train_buckets_scale)) if train_buckets_scale[i] > random_number])  # find the bucket id based on the random number  # Get a batch and make a step  start_time = time.time()  # record the start time of this batch  encoder_inputs, decoder_inputs, target_weights = model.get_batch(train_set, bucket_id)  # get a batch from the selected bucket id  if args.reinforce_learn:  step_loss = model.step_rf(args, sess, encoder_inputs, decoder_inputs, target_weights, bucket_id, rev_vocab)  # make a step using the reinforcement learning loss function  else:  step_loss = model.step(sess, encoder_inputs, decoder_inputs, target_weights, bucket_id=bucket_id, forward_only=False)  # make a step using the default loss function  # update the loss and current step after each batch/step finishs (in the end of this loop)  loss += step_loss / (time.time() - start

1.4模型预测和Beam Search模块


在预测模块,对应生成对话,我们需要利用Beam Search来寻找最优解。通过对Beam Size的控制可以保证输出语句的多样性。此外我们也可以加入强化学习,对于不同的机器人回答进行及时的人工反馈,通过Reinforcement Learning不断优化模型。

python
Get output logits for the sentence  
beams, now_beams, results = [(1.0, 0.0, i'eos': 0.0, 'dec inp': decoder_inputs, 'prob': 1.0, 'prob_ts': 1.0, 'prob_t': 4.0))]. []. [  Adjusted probability  all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  Normal seg2seg  if debug:  print(' '.join([dict_lookup(rev_vocab, w) for w in cand['dec_inp']]))  if cand[eos']:  results += [(prob, 0, cand)]  continue  Adjusted probability  all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  Adjusted probability  all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  Adjusted probability  all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  
]
all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  
all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  
all_prob_ts = model_step(encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id) if args.antilm else None  
all_prob_t = model_step(dummy_encoder_inputs, cand['dec_inp'], dptr, target_weights, bucket_id)  all_prob = all_prob_ts - args.antilm * all_prob_t #+ args.n_bonus * dptr + random() * 1e-50  
all_prob = all_prob_ts - args.antilm * all_prob_t  
if args.n_bonus != 0:  all_prob += args.n_bonus * dptr  Suppress copy-cat (respond the same as input)  
if dptr < len(input_token_ids):  all_prob[input_token_ids[dptr]] = all_prob[input_token_ids[dptr]] * 0.01  if return_raw:  return all_prob, all_prob_ts, all_prob_t  # beam search  for c in np.argsort(all_prob)[::-1][:args.beam_size]:  
new_cand "  
gos	dec_inp"	(c - data_utils.EOS_ID),	[(np.array([c]) if i -- (dptr+1) else k) 
for i, k in enumerate(cand['dec_inp'])]	  
prob_ts	cand['prob_ts	*all_prob_ts[c]	  
prob prob cand['prob _ cand['prob ] * all_prob t[c]	  
new_cand = (new_cand['prob'], random(). new_cand) # stuff a randon to prevent comparing new_cand  
if len (new_beams) < args.beam_size:  
heapq. heappush(new_beams, new cand)   
elif (new cando[0] > new _beams[0][0]):   
heapq. heapreplace(new _beams, new _cand)   
except Exception as e:   
print("[Error]', e)  
print(" ----[new _beams]-- ")  
print("-ines _cand]\n", new _cand) -\n". new _beams)  
results += new _cands # flush last cands post-process results res _cands  
for prob, _ in sorted(results, reverse=True):  
cand['dec _inp']l- res _cands. append(cand) join([dict _lookup(rev _vocab. w) for w in cand['dec _inp']l]) retugn res _cands[:args. beam _size]

往期精彩文章:

基础课22——云服务(SaaS、Pass、laas、AIaas)-CSDN博客文章浏览阅读47次。云服务是一种基于互联网的计算模式,通过云计算技术将计算、存储、网络等资源以服务的形式提供给用户,用户可以通过网络按需使用这些资源,无需购买、安装和维护硬件设备。云服务具有灵活扩展、按需使用、随时随地访问等优势,可以降低用户成本,提高资源利用效率。随着云计算技术的不断发展,云服务的应用范围也将越来越广泛。https://blog.csdn.net/2202_75469062/article/details/134212001?spm=1001.2014.3001.5501

基础课20——智能客服系统的使用维护-CSDN博客文章浏览阅读72次。智能客服系统在上线后,仍然需要定期的维护和更新。这是因为智能客服系统是一个复杂的软件系统,涉及到多个组件和功能,需要不断优化和改进以满足用户需求和保持市场竞争力。https://blog.csdn.net/2202_75469062/article/details/134211359?spm=1001.2014.3001.5501

相关文章:

进阶课6——基于Seq2Seq的开放域生成型聊天机器人的设计和开发流程

情感聊天机器人通常属于开放领域&#xff0c;用户可以与机器人进行各种话题的互动。例如&#xff0c;微软小冰和早期的AnswerBus就是这种类型的聊天机器人。基于检索的开放领域聊天机器人需要大量的语料数据&#xff0c;其开发流程与基于任务型的聊天机器人相似&#xff0c;而基…...

Java面试题04

1.Array 和 ArrayList 有何区别&#xff1f; Array是固定长度的&#xff0c;元素类型可以是基本类型&#xff0c;创建后大小不可改变&#xff1b;ArrayList是可变长 度的&#xff0c;只能存储对象&#xff0c;可以动态添加和删除元素。 区别1&#xff1a; 存储类型不同 …...

海康Visionmaster-通讯管理:使用 Modbus TCP 通讯 协议与流程交互

使用 Modbus TCP 通讯协议与视觉通讯&#xff0c;当地址为 0000 的保持型寄存器(4x 寄存器)变为 1 时&#xff0c;触发视觉流程执行一次&#xff0c;同时视觉将地址为 0000 的寄存器复位&#xff08;也即写为 0&#xff09;&#xff0c;视觉流程执行完成后&#xff0c;将结果数…...

assimp中如何判断矩阵是否是单位矩阵

对于一个矩阵元素为浮点型的矩阵&#xff0c;你是否还在使每个元素跟1.0f或0.0f进行比较&#xff0c;如果这样&#xff0c;只能说你的结果不一定正确&#xff0c;那我们看看assimp中是如何做的。 template <typename TReal> AI_FORCE_INLINE bool aiMatrix4x4t<TReal…...

大数据Doris(二十):数据导入(Broker Load)介绍

文章目录 数据导入(Broker Load)介绍 一、​​​​​​​适用场景...

Docker快速安装kafka

创建zk docker run -d --name zookeeper-server \-e ALLOW_ANONYMOUS_LOGINyes \bitnami/zookeeper:latest创建kafka docker run -d --name kafka-server \-p 9092:9092 \-e ALLOW_PLAINTEXT_LISTENERyes \-e KAFKA_CFG_ZOOKEEPER_CONNECTzookeeper-server:2181 \-e KAFKA_CF…...

ChatGPT是什么?黑客试图淹没其服务

上线2个月&#xff0c;月活跃用户破亿&#xff0c;媒体人用它编辑文案&#xff0c;学生用它写作业&#xff0c;程序员用它编辑代码&#xff0c; 它是谁呢&#xff1f; 它就是火爆全网&#xff08;chatgpt&#xff09;,chatgpt是什么呢&#xff0c;chatgpt是美国研发的一款人工…...

【Java 进阶篇】Java Web 开发之 Listener 篇:ServletContextListener 使用详解

欢迎大家来到 Java Web 开发的学习之旅&#xff01;在前面的博客中&#xff0c;我们已经学习了 Servlet、JSP、Filter 等重要的概念和技术。今天&#xff0c;我们将深入探讨 Java Web 开发中另一个重要的组成部分——Listener&#xff08;监听器&#xff09;&#xff0c;具体来…...

[C/C++]数据结构 链表OJ题:环形链表(如何判断链表是否有环)

题目描述: 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&…...

c#流程控制

c#分支语句 namespace ConsoleApp1 {internal class Program{static void Main(string[] args){Console.WriteLine("请输入学生成绩");string sConsole.ReadLine();int aint.Parse(s);//将字符类型强制转换为int类型if (a > 90){ Console.WriteLine("成绩优…...

基于SSM的学生二手书籍交易平台的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

xcode-工程设置

build settings Deployment Postprocessing 用于指定是否在构建完成后进行一些部署相关的处理。 当你在 Xcode 中构建你的应用程序时&#xff0c;构建设置决定了一些行为&#xff0c;其中一项是是否启用 Deployment Postprocessing。这个选项的主要作用是在构建完成后&#…...

Milvus Cloud——LLM Agent 现阶段出现的问题

LLM Agent 现阶段出现的问题 由于一些 LLM&#xff08;GPT-4&#xff09;带来了惊人的自然语言理解和生成能力&#xff0c;并且能处理非常复杂的任务&#xff0c;一度让 LLM Agent 成为满足人们对科幻电影所有憧憬的最终答案。但是在实际使用过程中&#xff0c;大家逐渐发现了通…...

百度智能云千帆大模型平台再升级,SDK版本开源发布!

SDK 前言一、SDK的优势二、千帆SDK&#xff1a;快速落地LLM应用三、如何快速上手千帆SDK1、SDK快速启动快速安装平台鉴权如何获取AK/SK以“Chat 对话”为调用示例 2. SDK进阶指引3. 通过Langchain接入千帆SDK为什么选择Langchain 开源社区 前言 百度智能云千帆大模型平台再次升…...

按键精灵中的数据类型转换

按键精灵中的数据类型有&#xff1a;整型、浮点数、布尔类型、字符串、数组这几种类型&#xff0c;主要的转换方式有以下这几种方式&#xff1a; 1. 转布尔类型 CBool Dim A 5 Dim B CBool(A)TracePrint B // true 2. 转字符串类型 CStr Dim MyInteger 437Dim MyStr…...

Golang Gorm 连接数据库

连接数据库 为了连接数据库&#xff0c;你首先要导入数据库驱动程序。例如&#xff1a; import _ "github.com/go-sql-driver/mysql"import ("gorm.io/driver/mysql""gorm.io/gorm" ) GORM 已经包含了一些驱动程序&#xff0c;为了方便的去记住…...

[C++随笔录] 红黑树

红黑树 红黑树的特点红黑树的模拟实现红黑树的底层结构insert的实现实现思路更新黑红比例的逻辑insert的完整代码 insert的验证 源码 红黑树的特点 红黑树&#xff0c;是一种二叉搜索树&#xff0c;但在每个结点上增加一个存储位表示结点的颜色&#xff0c;可以是 Red或 Black。…...

C 和 C++ 可变参数介绍

文章目录 前言概念C 的可变参数参数列表 #va_list 4组宏 C 的可变参数参数列表 #va_list 4组宏初始化列表 initializer_list<> 类模板可变参数模板 总结参考资料作者的话 前言 C 和 C 可变参数介绍。 概念 可变&#xff08;长&#xff09;/不定&#xff08;长&#xff…...

【Git】gui图形化界面的使用、ssh协议以及idea集成Git

目录 gui图形化界面的使用 介绍 特点 gui图形的使用 ssh协议 介绍 步骤及概念 ssh协议的使用 配置公钥 idea集成Git idea配置git IDEA安装gitee IDEA中登入Git ​编辑 项目分享 克隆分享的项目 ​编辑 ​编辑 idea上传远程 gui图形化界面的使用 介绍 GUI&#xff08…...

C语言之文件操作(详解版)

不知不觉我们已经学到C语言的文件操作部分了&#xff0c;这部分内容其实很有意思&#xff0c;因为它可以直接把我们代码中的数据写入硬盘&#xff0c;而不是我们关掉这个程序&#xff0c;代码就没有了&#xff0c;让我们开始学习吧&#xff01; 目录 1.为什么使用文件 2.什么…...

[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解

突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 ​安全措施依赖问题​ GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

地震勘探——干扰波识别、井中地震时距曲线特点

目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波&#xff1a;可以用来解决所提出的地质任务的波&#xff1b;干扰波&#xff1a;所有妨碍辨认、追踪有效波的其他波。 地震勘探中&#xff0c;有效波和干扰波是相对的。例如&#xff0c;在反射波…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

spring:实例工厂方法获取bean

spring处理使用静态工厂方法获取bean实例&#xff0c;也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下&#xff1a; 定义实例工厂类&#xff08;Java代码&#xff09;&#xff0c;定义实例工厂&#xff08;xml&#xff09;&#xff0c;定义调用实例工厂&#xff…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...