构建LangChain应用程序的示例代码:55、如何实现多代理模拟,其中特权代理决定谁发言。这遵循与多代理分散发言者选择相反的选择方案
示例展示了如何实现一个多代理模拟,其中一个特权代理决定谁来发言。
这遵循与多代理分散式发言人选择相反的选择方案。
我们在一个虚构的新闻网络模拟环境中展示这种方法的一个例子。这个例子将展示我们如何实现能够:
- 在说话前思考
- 终止对话
的代理。
导入LangChain相关模块
import functools
import random
from collections import OrderedDict
from typing import Callable, Listimport tenacity
from langchain.output_parsers import RegexParser
from langchain.prompts import (PromptTemplate,
)
from langchain.schema import (HumanMessage,SystemMessage,
)
from langchain_openai import ChatOpenAI# 导入所需的Python模块和LangChain组件
# 包括功能性工具、随机数生成、有序字典、类型提示
# 以及LangChain的输出解析器、提示模板、消息模式和OpenAI聊天模型
DialogueAgent
和 DialogueSimulator
类
我们将使用在其他示例多人龙与地下城和分散式发言人选择中定义的相同 DialogueAgent
和 DialogueSimulator
类。
class DialogueAgent:def __init__(self,name: str,system_message: SystemMessage,model: ChatOpenAI,) -> None:self.name = nameself.system_message = system_messageself.model = modelself.prefix = f"{self.name}: "self.reset()def reset(self):self.message_history = ["Here is the conversation so far."]def send(self) -> str:"""应用聊天模型到消息历史并返回消息字符串"""message = self.model.invoke([self.system_message,HumanMessage(content="\n".join(self.message_history + [self.prefix])),])return message.contentdef receive(self, name: str, message: str) -> None:"""将{name}说的{message}连接到消息历史中"""self.message_history.append(f"{name}: {message}")class DialogueSimulator:def __init__(self,agents: List[DialogueAgent],selection_function: Callable[[int, List[DialogueAgent]], int],) -> None:self.agents = agentsself._step = 0self.select_next_speaker = selection_functiondef reset(self):for agent in self.agents:agent.reset()def inject(self, name: str, message: str):"""用{name}的{message}启动对话"""for agent in self.agents:agent.receive(name, message)# 增加时间步self._step += 1def step(self) -> tuple[str, str]:# 1. 选择下一个说话者speaker_idx = self.select_next_speaker(self._step, self.agents)speaker = self.agents[speaker_idx]# 2. 下一个说话者发送消息message = speaker.send()# 3. 每个人接收消息for receiver in self.agents:receiver.receive(speaker.name, message)# 4. 增加时间步self._step += 1return speaker.name, message# 定义对话代理类,包含初始化、重置、发送和接收消息的方法
# 定义对话模拟器类,包含初始化、重置、注入消息和执行一步模拟的方法
DirectorDialogueAgent
类
DirectorDialogueAgent
是一个特权代理,负责选择其他代理中的哪一个下一个发言。这个代理负责
- 通过选择何时让哪个代理发言来引导对话
- 终止对话。
为了实现这样一个代理,我们需要解决几个问题。
首先,为了引导对话,DirectorDialogueAgent
需要在一条消息中(1)反思已经说过的内容,(2)选择下一个代理,以及(3)提示下一个代理发言。虽然可能可以提示LLM在同一个调用中执行所有三个步骤,但这需要编写自定义代码来解析输出的消息以提取选择的下一个代理。这不太可靠,因为LLM可以用不同的方式表达它如何选择下一个代理。
相反,我们可以做的是将步骤(1-3)明确地分成三个单独的LLM调用。首先,我们会要求DirectorDialogueAgent
反思到目前为止的对话并生成一个响应。然后我们提示DirectorDialogueAgent
输出下一个代理的索引,这很容易解析。最后,我们将选定的下一个代理的名字传回DirectorDialogueAgent
,要求它提示下一个代理发言。
其次,简单地提示DirectorDialogueAgent
决定何时终止对话通常会导致DirectorDialogueAgent
立即终止对话。为了解决这个问题,我们随机抽样一个伯努利变量来决定对话是否应该终止。根据这个变量的值,我们将注入一个自定义提示,告诉DirectorDialogueAgent
继续对话或终止对话。
class IntegerOutputParser(RegexParser):def get_format_instructions(self) -> str:return "Your response should be an integer delimited by angled brackets, like this: <int>."# 定义整数输出解析器,用于解析输出中的整数class DirectorDialogueAgent(DialogueAgent):def __init__(self,name,system_message: SystemMessage,model: ChatOpenAI,speakers: List[DialogueAgent],stopping_probability: float,) -> None:super().__init__(name, system_message, model)self.speakers = speakersself.next_speaker = ""self.stop = Falseself.stopping_probability = stopping_probabilityself.termination_clause = "Finish the conversation by stating a concluding message and thanking everyone."self.continuation_clause = "Do not end the conversation. Keep the conversation going by adding your own ideas."# 1. 有一个用于生成对前一个说话者的响应的提示self.response_prompt_template = PromptTemplate(input_variables=["message_history", "termination_clause"],template=f"""{{message_history}}Follow up with an insightful comment.
{{termination_clause}}
{self.prefix}""",)# 2. 有一个用于决定下一个说话者的提示self.choice_parser = IntegerOutputParser(regex=r"<(\d+)>", output_keys=["choice"], default_output_key="choice")self.choose_next_speaker_prompt_template = PromptTemplate(input_variables=["message_history", "speaker_names"],template=f"""{{message_history}}Given the above conversation, select the next speaker by choosing index next to their name:
{{speaker_names}}{self.choice_parser.get_format_instructions()}Do nothing else.""",)# 3. 有一个用于提示下一个说话者发言的提示self.prompt_next_speaker_prompt_template = PromptTemplate(input_variables=["message_history", "next_speaker"],template=f"""{{message_history}}The next speaker is {{next_speaker}}.
Prompt the next speaker to speak with an insightful question.
{self.prefix}""",)def _generate_response(self):# 如果self.stop = True,那么我们将注入带有终止条款的提示sample = random.uniform(0, 1)self.stop = sample < self.stopping_probabilityprint(f"\tStop? {self.stop}\n")response_prompt = self.response_prompt_template.format(message_history="\n".join(self.message_history),termination_clause=self.termination_clause if self.stop else "",)self.response = self.model.invoke([self.system_message,HumanMessage(content=response_prompt),]).contentreturn self.response@tenacity.retry(stop=tenacity.stop_after_attempt(2),wait=tenacity.wait_none(), # 重试之间无等待时间retry=tenacity.retry_if_exception_type(ValueError),before_sleep=lambda retry_state: print(f"ValueError occurred: {retry_state.outcome.exception()}, retrying..."),retry_error_callback=lambda retry_state: 0,) # 当所有重试都用尽时的默认值def _choose_next_speaker(self) -> str:speaker_names = "\n".join([f"{idx}: {name}" for idx, name in enumerate(self.speakers)])choice_prompt = self.choose_next_speaker_prompt_template.format(message_history="\n".join(self.message_history + [self.prefix] + [self.response]),speaker_names=speaker_names,)choice_string = self.model.invoke([self.system_message,HumanMessage(content=choice_prompt),]).contentchoice = int(self.choice_parser.parse(choice_string)["choice"])return choicedef select_next_speaker(self):return self.chosen_speaker_iddef send(self) -> str:"""应用聊天模型到消息历史并返回消息字符串"""# 1. 生成并保存对前一个说话者的响应self.response = self._generate_response()if self.stop:message = self.responseelse:# 2. 决定下一个说话者self.chosen_speaker_id = self._choose_next_speaker()self.next_speaker = self.speakers[self.chosen_speaker_id]print(f"\tNext speaker: {self.next_speaker}\n")# 3. 提示下一个说话者发言next_prompt = self.prompt_next_speaker_prompt_template.format(message_history="\n".join(self.message_history + [self.prefix] + [self.response]),next_speaker=self.next_speaker,)message = self.model.invoke([self.system_message,HumanMessage(content=next_prompt),]).contentmessage = " ".join([self.response, message])return message# 定义导演对话代理类,继承自DialogueAgent
# 包含生成响应、选择下一个说话者和发送消息的方法
# 使用tenacity库进行重试处理
定义参与者和话题
topic = "The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze"
director_name = "Jon Stewart"
agent_summaries = OrderedDict({"Jon Stewart": ("Host of the Daily Show", "New York"),"Samantha Bee": ("Hollywood Correspondent", "Los Angeles"),"Aasif Mandvi": ("CIA Correspondent", "Washington D.C."),"Ronny Chieng": ("Average American Correspondent", "Cleveland, Ohio"),}
)
word_limit = 50# 定义讨论话题、主持人名称、参与者信息和字数限制
生成系统消息
agent_summary_string = "\n- ".join([""]+ [f"{name}: {role}, located in {location}"for name, (role, location) in agent_summaries.items()]
)conversation_description = f"""This is a Daily Show episode discussing the following topic: {topic}.The episode features {agent_summary_string}."""agent_descriptor_system_message = SystemMessage(content="You can add detail to the description of each person."
)def generate_agent_description(agent_name, agent_role, agent_location):agent_specifier_prompt = [agent_descriptor_system_message,HumanMessage(content=f"""{conversation_description}Please reply with a creative description of {agent_name}, who is a {agent_role} in {agent_location}, that emphasizes their particular role and location.Speak directly to {agent_name} in {word_limit} words or less.Do not add anything else."""),]agent_description = ChatOpenAI(temperature=1.0)(agent_specifier_prompt).contentreturn agent_descriptiondef generate_agent_header(agent_name, agent_role, agent_location, agent_description):return f"""{conversation_description}Your name is {agent_name}, your role is {agent_role}, and you are located in {agent_location}.Your description is as follows: {agent_description}You are discussing the topic: {topic}.Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
"""def generate_agent_system_message(agent_name, agent_header):return SystemMessage(content=(f"""{agent_header}
You will speak in the style of {agent_name}, and exaggerate your personality.
Do not say the same things over and over again.
Speak in the first person from the perspective of {agent_name}
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of {agent_name}.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to {word_limit} words!
Do not add anything else."""))agent_descriptions = [generate_agent_description(name, role, location)for name, (role, location) in agent_summaries.items()
]
agent_headers = [generate_agent_header(name, role, location, description)for (name, (role, location)), description in zip(agent_summaries.items(), agent_descriptions)
]
agent_system_messages = [generate_agent_system_message(name, header)for name, header in zip(agent_summaries, agent_headers)
]# 生成代理描述、代理头部和系统消息
# 使用OpenAI聊天模型生成创意描述
# 组合生成每个代理的完整系统消息
for name, description, header, system_message in zip(agent_summaries, agent_descriptions, agent_headers, agent_system_messages
):print(f"\n\n{name} Description:")print(f"\n{description}")print(f"\nHeader:\n{header}")print(f"\nSystem Message:\n{system_message.content}")# 打印每个代理的描述、头部和系统消息
使用LLM详细阐述讨论话题
topic_specifier_prompt = [SystemMessage(content="You can make a task more specific."),HumanMessage(content=f"""{conversation_description}Please elaborate on the topic. Frame the topic as a single question to be answered.Be creative and imaginative.Please reply with the specified topic in {word_limit} words or less. Do not add anything else."""),
]
specified_topic = ChatOpenAI(temperature=1.0)(topic_specifier_prompt).contentprint(f"Original topic:\n{topic}\n")
print(f"Detailed topic:\n{specified_topic}\n")# 使用OpenAI聊天模型生成更详细的话题描述
# 将话题转化为一个需要回答的问题
定义说话者选择函数
最后我们将定义一个说话者选择函数 select_next_speaker
,它接受每个代理的出价并选择出价最高的代理(随机打破平局)。
我们将定义一个 ask_for_bid
函数,它使用我们之前定义的 bid_parser
来解析代理的出价。我们将使用 tenacity
来装饰 ask_for_bid
,以便在代理的出价无法正确解析时多次重试,并在最大尝试次数后产生默认出价0。
def select_next_speaker(step: int, agents: List[DialogueAgent], director: DirectorDialogueAgent
) -> int:"""如果步骤是偶数,则选择导演否则,由导演选择下一个说话者。"""# 导演在奇数步骤说话if step % 2 == 1:idx = 0else:# 这里导演选择下一个说话者idx = director.select_next_speaker() + 1 # +1 因为我们排除了导演return idx# 定义选择下一个说话者的函数
# 在奇数步骤选择导演,否则由导演选择
主循环
director = DirectorDialogueAgent(name=director_name,system_message=agent_system_messages[0],model=ChatOpenAI(temperature=0.2),speakers=[name for name in agent_summaries if name != director_name],stopping_probability=0.2,
)agents = [director]
for name, system_message in zip(list(agent_summaries.keys())[1:], agent_system_messages[1:]
):agents.append(DialogueAgent(name=name,system_message=system_message,model=ChatOpenAI(temperature=0.2),))# 创建导演代理和其他对话代理
simulator = DialogueSimulator(agents=agents,selection_function=functools.partial(select_next_speaker, director=director),
)
simulator.reset()
simulator.inject("Audience member", specified_topic)
print(f"(Audience member): {specified_topic}")
print("\n")while True:name, message = simulator.step()print(f"({name}): {message}")print("\n")if director.stop:break# 创建对话模拟器并运行模拟
# 注入指定话题并循环执行模拟步骤
# 直到导演决定停止对话
总结
本文介绍了如何实现一个多代理模拟系统,其中一个特权代理决定谁来发言。这与分散式发言人选择方案相反。文章以一个虚构的新闻网络模拟为例,展示了如何实现能够在说话前思考并终止对话的代理。
扩展知识
-
多代理系统:这是人工智能和自然语言处理领域的一个重要研究方向,涉及多个AI代理之间的交互和协作。这种系统可以模拟复杂的社会互动,用于各种应用,如虚拟助手、游戏AI和社会模拟。
-
对话管理:在这个示例中,导演代理负责管理对话流程。这反映了现实世界中的对话管理策略,如在会议或访谈中由主持人引导讨论。
-
自然语言生成(NLG):系统中的每个代理都使用NLG技术来生成响应。这涉及到根据上下文和角色生成连贯、相关和自然的语言输出。
-
角色扮演AI:每个代理都被赋予了特定的角色和个性。这种技术可以用于创建更真实和引人入胜的AI角色,用于娱乐、教育或训练目的。
-
概率决策:使用随机抽样来决定是否结束对话是一种简单但有效的方法,可以引入不可预测性和多样性。在更复杂的系统中,这可能涉及更高级的概率模型和决策理论。
相关文章:

构建LangChain应用程序的示例代码:55、如何实现多代理模拟,其中特权代理决定谁发言。这遵循与多代理分散发言者选择相反的选择方案
示例展示了如何实现一个多代理模拟,其中一个特权代理决定谁来发言。 这遵循与多代理分散式发言人选择相反的选择方案。 我们在一个虚构的新闻网络模拟环境中展示这种方法的一个例子。这个例子将展示我们如何实现能够: 在说话前思考终止对话 的代理。 导入LangChain相关模块…...

船舶雷达与导航系统选择7/8防水插座的原因分析
概述 船舶雷达与导航系统在现代航海中扮演着至关重要的角色,它们为船舶提供准确的导航信息,确保航行的安全和效率。在这些系统中,7/8防水插座的使用尤为重要,因为它们能够在恶劣的海上环境中提供稳定的电力和信号连接。接下来&am…...

墨烯的C语言技术栈-C语言基础-006
六.常量 C语言的常量为 字面常量 const修饰的常变量 #define定义的 枚举常量 int main() { // 四个都是字面常量 30; 3.14; "w"; // 字符常量 "abc"; // const修饰的常变量 const int a 10; // 在C语言中,const修饰的a,本质是变量,但不能直…...

常用SHELL命令
在 Unix/Linux 系统中,除了基本的文件和目录操作命令外,还有许多强大的工具命令,用于文本处理、系统监控、文件操作等。以下是一些常用的 Shell 命令,特别是类似 sed 和 awk 的文本处理工具: 文本处理命令 sed - 流编…...

Python脚本:将Word文档转换为Excel文件
引言 在文档处理中,我们经常需要将Word文档中的内容转换成其他格式,如Excel,以便更好地进行数据分析和报告。针对这一需求,我编写了一个Python脚本,能够批量处理指定目录下的Word文档,将其内容结构化并转换…...

【单链表】03 设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。
🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux算法题上机准备 😘欢迎 ❤️关注 👍点赞 🙌收藏 ✍️留言 题目 设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值。 算法…...

鸿蒙开发设备管理:【@ohos.vibrator (振动)】
振动 说明: 开发前请熟悉鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 本模块首批接口从API version 8开始支持。后续版本的新增接口,采用上角标单独标记接口的起始版本。 导入模块 imp…...

【信息学奥赛】CSP-J/S初赛07 排序算法及其他算法在初赛中的考察
本专栏👉CSP-J/S初赛内容主要讲解信息学奥赛的初赛内容,包含计算机基础、初赛常考的C程序和算法以及数据结构,并收集了近年真题以作参考。 如果你想参加信息学奥赛,但之前没有太多C基础,请点击👉专栏&#…...

第N7周:seq2seq翻译实战-pytorch复现-小白版
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 理论基础 seq2seq(Sequence-to-Sequence)模型是一种用于机器翻译、文本摘要等序列转换任务的框架。它由两个主要的递归神经网络&#…...

java集合(1)
目录 一.集合概述 二. 集合体系概述 1. Collection接口 1.1 List接口 1.2 Set接口 2. Map接口 三. ArrayList 1.ArrayList常用方法 2.ArrayList遍历 2.1 for循环 2.2 增强for循环 2.3 迭代器遍历 一.集合概述 我们经常需要存储一些数据类型相同的元素,之前我们学过…...

分布式数据库HBase:从零开始了解列式存储
在接触过大量的传统关系型数据库后你可能会有一些新的问题: 无法整理成表格的海量数据该如何储存? 在数据非常稀疏的情况下也必须将数据存储成关系型数据库吗? 除了关系型数据库我们是否还有别的选择以应对Web2.0时代的海量数据? 如果你也曾经想到过这些问题, 那么HBase将是…...

接口测试流程及测试点!
一、什么时候开展接口测试 1.项目处于开发阶段,前后端联调接口是否请求的通?(对应数据库增删改查)--开发自测 2.有接口需求文档,开发已完成联调(可以转测),功能测试展开之前 3.专…...

已经安装deveco-studio-4.1.3.500的基础上安装deveco-studio-3.1.0.501
目录标题 1、执行exe文件后安装即可2、双击devecostudio64_3.1.0.501.exe2.1、安装Note (注意和4.1的Note放不同目录)2.2、安装ohpm (注意和4.1版本的ohpm放不同目录)2.3、安装SDK (注意和4.1版本的SDK放不同目录) 1、执行exe文件后安装即可 2、双击devecostudio64_3.1.0.501.e…...

【C++】 解决 C++ 语言报错:Use of Uninitialized Variable
文章目录 引言 使用未初始化的变量(Use of Uninitialized Variable)是 C 编程中常见且危险的错误之一。它通常在程序试图使用尚未赋值的变量时发生,导致程序行为不可预测,可能引发运行时错误、数据损坏,甚至安全漏洞。…...

2024年7月6日 十二生肖 今日运势
小运播报:2024年7月6日,星期六,农历六月初一 (甲辰年庚午月辛未日),法定节假日。 红榜生肖:猪、马、兔 需要注意:狗、鼠、牛 喜神方位:西南方 财神方位:正…...

ubuntu丢失网络/网卡的一种原因解决方案
现象 开机进入ubuntu后发现没有网络,无论是在桌面顶部状态栏的快捷键 还是 系统设置中,都没有”有线网“和”无线网“的选项,”代理“的选项是有的使用数据线连接电脑和手机,手机开启”通过usb共享网络“,还是没有任何…...

第6篇 共识机制深度解析:PoW、PoS、DPoS和PBFT
在区块链的世界里,有一个非常重要的概念叫做“共识机制”。它就像是区块链的心脏,保证大家在这条链上的信息是可靠的、不可篡改的。今天,我们就来通俗易懂地聊聊区块链里的四大共识机制:工作量证明(PoW)、权益证明(PoS)、委托权益证明(DPoS)和拜占庭容错(PBFT)。为…...

Windows环境使用SpringBoot整合Minio平替OSS
目录 配置Minio环境 一、下载minio.exe mc.exe 二、设置用户名和密码 用管理员模式打开cmd 三、启动Minio服务器 四、访问WebUI给的地址 SpringBoot整合Minio 一、配置依赖,application.yml 二、代码部分 FileVO MinioConfig MinioUploadService MinioController 三…...

LeetCode 196, 73, 105
目录 196. 删除重复的电子邮箱题目链接表要求知识点思路代码 73. 矩阵置零题目链接标签简单版思路代码 优化版思路代码 105. 从前序与中序遍历序列构造二叉树题目链接标签思路代码 196. 删除重复的电子邮箱 题目链接 196. 删除重复的电子邮箱 表 表Person的字段为id和email…...

在Apache HTTP服务器上配置 TLS加密
安装mod_ssl软件包 [rootlocalhost conf.d]# dnf install mod_ssl -y此时查看监听端口多了一个443端口 自己构造证书 [rootlocalhost conf.d]# cd /etc/pki/tls/certs/ [rootlocalhost certs]# openssl genrsa > jiami.key [rootlocalhost certs]# openssl req -utf8 -n…...

C语言力扣刷题11——打家劫舍1——[线性动态规划]
力扣刷题11——打家劫舍1和2——[线性动态规划] 一、博客声明二、题目描述三、解题思路1、线性动态规划 a、什么是动态规划 2、思路说明 四、解题代码(附注释) 一、博客声明 找工作逃不过刷题,为了更好的督促自己学习以及理解力扣大佬们的解…...

房屋租赁管理小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,中介管理,房屋信息管理,房屋类型管理,租房订单管理,租房信息管理 微信端账号功能包括:系统首页,房屋信息&am…...

oracle sql语句 排序 fjd = ‘0101‘ 排在 fjd = ‘0103‘ 的前面
要实现这个排序需求,你可以使用 CASE 表达式来自定义排序逻辑。假设你有一个表格名为 your_table,并且有一个字段 fjd 存储类似 ‘0101’, ‘0103’ 这样的值,你可以这样编写 SQL 查询: SELECT * FROM your_table ORDER BY CASE …...

初试成绩占比百分之70!计算机专硕均分340+!华中师范大学计算机考研考情分析!
华中师范大学(Central China Normal University)简称“华中师大”或“华大”,位于湖北省会武汉,是中华人民共和国教育部直属重点综合性师范大学,国家“211工程”、“985工程优势学科创新平台”重点建设院校,…...

【面向就业的Linux基础】从入门到熟练,探索Linux的秘密(十)-git(2)
下面是一些git的常用命令和基本操作,可以当做平常的笔记查询,用于学习!!! 文章目录 前言 一、git 二、git常用命令 总结 前言 下面是一些git的常用命令和基本操作,可以当做平常的笔记查询,用于…...

JMH320【亲测】【御剑九歌】唯美仙侠手游御剑九歌+WIN学习手工端+视频教程+开服清档+运营后台+授权GM物品充值后台
资源介绍: 这也是仙梦奇缘的一个游戏 注意:外网14位IP或域名 ———————————————————————————————————– ps后台介绍: 1区运营后台:http://ip:9981/admin/admintool/ 2区运营后台:http://ip…...

【matlab】信号分解/故障诊断——智能优化算法优化VMD
目录 引言 应用领域 VMD代码实现 智能优化算法优化VMD 引言 VMD(变分模态分解)是一种新的非线性自适应信号分解方法,它通过变分原理将复杂信号分解为若干个具有不同频率中心和带宽的本征模态函数(Intrinsic Mode Functions, …...

【重磅】万能模型-直接能换迪丽热巴的模型
万能模型,顾名思义,不用重新训练src,直接可以用的模型,适应大部分原视频脸 模型用法和正常模型一样,但可以跳过训练阶段!直接到合成阶段使用该模型 本模型没有做Xseg,对遮挡过多的画面不会自动适…...

Web基础和HTTP协议
web基础与HTTP协议: web:就是我们所说的网页。打开网站展示的页面。(全球广域网,万维网) world wide web 分布式图形信息系统 http https 超文本传输协议 分布式:计算机系统或者应用程序分布在多台计算机或者服务器上。通过计算机网络互相通信和协作。共同完成任…...

Mini-L-CTF-2022 minispringboot Thymeleaf模板注入 spel的绕过
Mini-L-CTF-2022 minispringboot Thymeleaf模板注入 spel的绕过 就是一个低版本的Thymeleaf注入 漏洞点 public class MainController {GetMapping({"/{language}"})public String test(PathVariable(name "language") String language, RequestParam(…...