LLM之Agent(九)| 通过API集成赋能Autogen Multi-Agent系统

       随着大型语言模型的快速发展,构建基于LLM驱动的自治代理(autonomous agents)已经成为一个备受关注的话题。仅在过去一年中,就出现了许多基于这一理念的新技术和框架。

​      本文将探索微软开源的Agent框架:Autogen。它弥补了此类新技术未能解决的空白:允许多个Agent协作以实现共同目标。它在LLM之上添加了最小但重要的功能,以支持多个代理的初始化和协作,允许在多个Agent之间单聊以及群聊。

       但作为一个仍处于早期阶段的框架,通过API把Autogen集成到实际的生产环境中仍然是一个挑战,如Web APP。由于缺乏成熟的文档或资源,在Agent通信流中需要一些变通方法。

       因此,在本文中,我们将讨论Autogen Agent连接到API的详细过程。

一、Autogen介绍

       在之前的LLM之Agent(七)|  AutoGen介绍文章中介绍过Autogen,它是一个基于LLM的Agent通信框架,支持创建具有不同人物角色的代理。然后,这些代理可以进行单聊;也可以进行群聊,每个代理轮流发言。

Autogen提供了一些具有不同功能的内置代理类型,例如:

  • User Proxy Agent:可以检索用户输入并执行代码;
  • Assistant Agent:默认的系统消息代理,该消息允许Agent充当完成任务的助理;
  • Conversable Agent:在user proxy agents和assistant之间构建会话能力。

虽然Autogen主要支持OpenAI LLM,如GPT-3.5和GPT-4来创建代理,但用户也可以与本地或其他托管LLM一起使用。

二、Autogen群聊

       Autogen中的群聊功能允许多个代理在群设置中进行协作,主要特点如下:

  • 每个代理都可以看到组中其他代理发送的所有消息;
  • 一旦启动,群聊将继续,直到满足其中一个终止条件。(例如:代理在回复中使用终止消息,用户选择退出聊天,达到群的最大聊天次数等)
  • 每个群聊都有一个管理代理,负责监督消息广播、发言人选择和聊天终止。
  • Autogen在每轮聊天中选择下一位发言人,目前支持四种方法

        -manual:要求用户手动选择下一个发言人

        -random:随机选择下一个发言人

        -round robin:使用循环方法选择下一个发言人

        -auto:让LLM选择下一个有聊天历史记录作为上下文的发言人

       这些特性使Autogen群聊成为Agent协作的理想选择。然而,如果想控制Agent在此环境中更多的协作方式时,也会带来很多挑战。

三、使用Autogen开发应用程序

      目前,Autogen旨在作为一种工具使用,用户可以完全了解不同代理之间的所有内部通信。这使得将Autogen集成到用户不应该知道这些信息的应用程序中成为一项棘手的工作。

       例如,如果您构建了一个系统,其中多个代理共同担任销售助理,那么在决定对用户查询的最终响应之前,您可能不想公开他们是如何在内部规划和选择销售策略的。您也可能不想让用户暴露在这种内部沟通的复杂性中。

       除此之外,在尝试将Autogen代理系统与API集成时,我们还面临以下问题:

  • Autogen主要是一个CLI工具。(例如:它将代理消息打印到CLI,并提示用户通过CLI提供反馈);
  • Autogen无法在没有明确用户输入的情况下提供一致的方式来结束特定的聊天序列。

但好消息是,我们可以使用Autogen已经支持的某些定制来解决这些问题。我们能够将Autogen集成到API中。

下面通过旅游代理系统来演示一下如何将AutoGen与API进行集成:

四、基于Autogen的旅游代理系统

       该系统将由两个Autogen Assistant Agent和一个User Proxy Agent构建,在群聊中这些代理人中的每一位都有以下职责:

  • Tour Agent:主代理决定如何响应用户查询以及在生成对用户的最终响应之前应收集的信息;
  • Location Researcher:旅游代理的助理,在通过SERP API查询谷歌地图的函数调用的帮助下进行位置研究。它使代理商能够研究用户心目中与目的地相关的景点、餐厅、住宿等;
  • User Proxy:代理群聊天中用户的代理。

由于本教程依赖于OpenAI和SERP API,因此您需要每个服务的API密钥来尝试本示例。

4.1 Autogen config

首先,定义AutoGen的配置:

config_list = [{'model': 'gpt-3.5-turbo-1106','api_key': os.getenv("OPENAI_API_KEY"),}]

4.2 Assistant Agents

然后,创建两个助理代理:Tour AgentLocation Researcher

       Tour Agent是一个简单的Assistant Agent,具有一个自定义的系统提示,用于描述其角色和职责,它指定代理应如何将TERMINATE添加到针对用户的最终回答的末尾。

tour_agent = AssistantAgent("tour_agent",    human_input_mode="NEVER",    llm_config={"config_list": config_list,"cache_seed": None    },    system_message="You are a Tour Agent who helps users plan a trip based on user requirements. You can get help from the Location Researcher to research and find details about a certain location, attractions, restaurants, accommodation, etc. You use those details a answer user questions, create trip itineraries, make recommendations with practical logistics according to the user's requirements. Report the final answer when you have finalized it. Add TERMINATE to the end of this report.")

       另一方面,在创建Location Researcher时,定义一个函数,它可以调用并执行来搜索谷歌地图。将在下一节中介绍该函数的实际schema和具体实现。下面代码片段显示了如何通过自定义提示将它们附加到Assistant Agent。

location_researcher = AssistantAgent(    "location_researcher",    human_input_mode="NEVER",    system_message="You are the location researcher who is helping the Tour Agent plan a trip according to user requirements. You can use the `search_google_maps` function to retrieve details about a certain location, attractions, restaurants, accommodation, etc. for your research. You process results from these functions and present your findings to the Tour Agent to help them with itinerary and trip planning.",    llm_config={        "config_list": config_list,        "cache_seed": None,        "functions": [            SEARCH_GOOGLE_MAPS_SCHEMA,        ]    },    function_map={        "search_google_maps": search_google_maps    })

4.3 User Proxy

       然后,创建User Proxy。尽管User Proxy在该代理系统中没有发挥积极作用,但它对于接受用户消息和在向用户发送响应之前检测何时结束对用户查询的回复序列至关重要。

def terminate_agent_at_reply(        recipient: Agent,        messages: Optional[List[Dict]] = None,        sender: Optional[Agent] = None,        config: Optional[Any] = None,) -> Tuple[bool, Union[str, None]]:return True, Noneuser_proxy = UserProxyAgent("user_proxy",    is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),    human_input_mode="NEVER",    code_execution_config=False)user_proxy.register_reply([Agent, None], terminate_agent_at_reply)

       我已经向User Proxy注册了一个新的回复函数,它只返回TrueNone输出。要了解如何结束聊天序列的,必须了解Autogen是如何使用回复功能的。

       当代理生成回复时,Autogen依赖于注册到代理的回复函数列表。它接受这个列表中的第一个函数,如果它能生成最终的回复,则返回Truereply。如果返回False,表示函数无法生成回复,将转移到列表中的下一个函数。

       Autogen支持代理不同的回复方式,例如请求人工反馈、执行代码、执行函数或生成LLM回复。

       当我将terminate_agent_at_reply注册为回复函数时,它会被添加到此列表的开头,并成为第一个被调用的回复函数。由于默认情况下返回TrueNone,这将阻止用户代理发送自动回复或使用其他回复功能生成LLM回复。使用“None”作为回复会阻止群聊继续进行更多的聊天回合。

4.4 Group chat

       最后,我将创建允许所有这些代理进行协作的群聊和管理代理。

group_chat = GroupChat(    agents=[self.user_proxy, self.location_researcher, self.tour_agent],    messages=[],    allow_repeat_speaker=False,    max_round=20)group_chat_manager = GroupChatManager(self.group_chat,    is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),    llm_config={"config_list": config_list,"cache_seed": None    })

       在这里,我允许群聊使用默认的发言人选择方法,auto,因为这个用例没有其他合适的选项。我还设置了聊天管理器的is_terminate_msg参数,以检查消息内容中是否存在terminate

       那么,当我已经为用户代理使用了以前的terminate_at_agent_reply函数时,为什么我要在这里设置另一个终止条件呢?

       如果LLM在Tour agent的最终回答后选择用户代理以外的代理作为下一个发言人,它应该起到故障保护的作用。

4.5 把上述功能合并到一个class里

       现在,我可以把所有这些代理逻辑放在一个类中,还介绍了一种方法来接受来自API的用户消息,并在回复序列之后发送最终回复。

import osfrom autogen import AssistantAgent, UserProxyAgent, GroupChat, GroupChatManager, Agentfrom typing import Optional, List, Dict, Any, Union, Callable, Literal, Tuplefrom dotenv import load_dotenvfrom functions import search_google_maps, SEARCH_GOOGLE_MAPS_SCHEMAload_dotenv()config_list = [{'model': 'gpt-3.5-turbo-1106','api_key': os.getenv("OPENAI_API_KEY"),}]class AgentGroup:def __init__(self):        self.user_proxy = UserProxyAgent("user_proxy",            is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),            human_input_mode="NEVER",            code_execution_config=False        )        self.user_proxy.register_reply([Agent, None], AgentGroup.terminate_agent_at_reply)        self.location_researcher = AssistantAgent("location_researcher",            human_input_mode="NEVER",            system_message="You are the location researcher who is helping the Tour Agent plan a trip according to user requirements. You can use the `search_google_maps` function to retrieve details about a certain location, attractions, restaurants, accommodation, etc. for your research. You process results from these functions and present your findings to the Tour Agent to help them with itinerary and trip planning.",            llm_config={"config_list": config_list,"cache_seed": None,"functions": [                    SEARCH_GOOGLE_MAPS_SCHEMA,                ]            },            function_map={"search_google_maps": search_google_maps            }        )        self.tour_agent = AssistantAgent("tour_agent",            human_input_mode="NEVER",            llm_config={"config_list": config_list,"cache_seed": None            },            system_message="You are a Tour Agent who helps users plan a trip based on user requirements. You can get help from the Location Researcher to research and find details about a certain location, attractions, restaurants, accommodation, etc. You use those details a answer user questions, create trip itineraries, make recommendations with practical logistics according to the user's requirements. Report the final answer when you have finalized it. Add TERMINATE to the end of this report."        )        self.group_chat = GroupChat(            agents=[self.user_proxy, self.location_researcher, self.tour_agent],            messages=[],            allow_repeat_speaker=False,            max_round=20        )        self.group_chat_manager = GroupChatManager(            self.group_chat,            is_termination_msg=lambda x: "TERMINATE" in x.get("content", ""),            llm_config={"config_list": config_list,"cache_seed": None            }        )def process_user_message(self, message: str) -> str:        self.user_proxy.initiate_chat(self.group_chat_manager, message=message, clear_history=False)return self._find_last_non_empty_message()def _find_last_non_empty_message(self) -> str:        conversation = self.tour_agent.chat_messages[self.group_chat_manager]for i in range(len(conversation) - 1, -1, -1):if conversation[i].get("role") == "assistant":                reply = conversation[i].get("content", "").strip()                reply = reply.replace("TERMINATE", "")if reply:return replyreturn "No reply received"    @staticmethoddef terminate_agent_at_reply(            recipient: Agent,            messages: Optional[List[Dict]] = None,            sender: Optional[Agent] = None,            config: Optional[Any] = None,    ) -> Tuple[bool, Union[str, None]]:return True, None

       这里,每当代理组接收到用户消息时,用户代理都会启动与组管理器的聊天,其中clear_history=False,可以保留以前回复序列的历史记录。

       回复序列结束后,find_last_non_empty_message会从聊天记录中找到旅行社代理发送的最后一条非空消息,并将其作为答案返回。该函数在寻找应该返回答案时,会考虑到与代理回复和回复序列的一些不一致性。

4.6 API endpoint

         现在,我将创建API端点来接收FastAPI用户查询。

from fastapi import FastAPIfrom pydantic import BaseModelfrom typing import Dictfrom agent_group import AgentGroupclass ChatRequest(BaseModel):    session_id: str    message: strapp = FastAPI()sessions: Dict[str, AgentGroup] = {}@app.post("/chat")def chat(request: ChatRequest):    session_id = request.session_id    message = request.messageif session_id not in sessions.keys():        sessions[session_id] = AgentGroup()    agent_group = sessions[session_id]    reply = agent_group.process_user_message(message)return {"reply": reply, "status": "success"}

4.7 函数调用

    最后,回过头来处理在前一步中遗漏的内容:Location Researcher使用的函数调用的模式和实现。

import osfrom serpapi import GoogleSearchfrom dotenv import load_dotenvfrom typing import Dictload_dotenv()SEARCH_GOOGLE_MAPS_SCHEMA = {"name": "search_google_maps","description": "Search google maps using Google Maps API","parameters": {"type": "object","properties": {"query": {"type": "string","description": "A concise search query for searching places on Google Maps"            }        },"required": ["query"]    }}def search_google_maps(query):    params = {"engine": "google_maps","q": query,"type": "search","api_key": os.getenv("SERP_API_KEY")    }    results = _search(params)    results = results["local_results"]    top_results = results[:10] if len(results) > 10 else resultsdata = []for place in top_results:data.append(_populate_place_data(place["place_id"]))return datadef _populate_place_data(place_id: str):    params = {"engine": "google_maps","type": "place","place_id": place_id,"api_key": os.getenv("SERP_API_KEY")    }data = _search(params)return _prepare_place_data(data["place_results"])def _prepare_place_data(place: Dict):return {"name": place.get("title"),"rating": place.get("rating"),"price": place.get("price"),"type": place.get("type"),"address": place.get("address"),"phone": place.get("phone"),"website": place.get("website"),"description": place.get("description"),"operating_hours": place.get("operating_hours"),"amenities": place.get("amenities"),"service_options": place.get("service_options")    }def _search(params: Dict[str, str]):    search = GoogleSearch(params)    results = search.get_dict()return results

       search_google_maps函数获取代理提交的搜索查询,并将其发送到SERP的google maps API。然后,它使用位置ID检索结果中前10个位置的更多详细信息。最后,它使用这些详细信息创建一个简化的对象并将其发送回。

4.8 测试

         终于到了运行这个应用程序的时候了,看看它的效果如何。

        我正在尝试一个例子,用户发送一个请求来计划去巴厘岛旅行的行程。

Create a week long itinerary to Ubud, Bali for myself for May 2024. I’m going solo and I love exploring nature and going on hikes and activities like that. I have a mid-level budget and I want the itinerary to be relaxing not too packed.

       旅行社代理考虑了这一请求,并多次联系位置研究员,以获取岛上景点、餐厅和住宿选择的详细信息。

       旅行社代理根据这些信息提供最终答案。然后,用户对这个答案给出更多的反馈,使用更多的请求继续进行聊天。

五、改进和定制

       在上述的Tour Agent系统中,成功地配置了Autogen,以更好地适应基于API的应用程序,在该应用程序中,内部代理通信对用户是隐蔽的。然而,它仍然有一些缺点和不可预测的行为。

例如:

  • 我假设Tour Agent已经收集了所有所需的详细信息,并在用户代理被选为下一位发言人时完成了对用户查询的最终回答。考虑到使用LLM来选择下一个说话者的不可预测性,情况可能并不总是如此。
  • 之所以选择订单代理,是因为发言人并不总是一致的。有时,在用户查询后,位置研究员会被选为第一个发言人,而不是旅行社代理,等等。

       尽管这些缺点对于这样的用例来说并不重要,但对于另一个场景来说可能会有所不同。随着群聊中代理数量的增加,这些问题也变得更加明显。

       但是,如果你想为代理群聊带来更多的可预测性和一致性,仍然有一些方法可以定制Autogen的内置行为。

5.1 修改群聊发言人选择提示

      Autogen GroupChat类包含一个select_speaker_msg方法,可以覆盖该方法以指定如何管理发言人选择。

这是原始提示(供参考)。

def select_speaker_msg(self, agents: List[Agent]) -> str:"""Return the system message for selecting the next speaker. This is always the *first* message in the context."""return f"""You are in a role play game. The following roles are available:{self._participant_roles(agents)}.Read the following conversation.Then select the next role from {[agent.name for agent in agents]} to play. Only return the role."""

       您可以更新此信息,具体说明在何种情况下选择哪种代理,以使演讲者选择更加一致。

5.2 使用图模型强制执行更严格的说话者转移路径

       此笔记本[2]显示了如何将所有允许的说话者转换路径定义为有向图,并覆盖GroupChat的select_speaker方法来强制执行该行为。例如,对于这个用例,可以创建这样一个简单的图。

       这些只是几个例子。最终,适合您的解决方案将取决于您的特定需求。你还必须在提示和路由方面发挥创意,以确保所有代理的行为都符合你的意愿。

参考文献:

[1] https://levelup.gitconnected.com/harnessing-the-power-of-autogen-multi-agent-systems-via-api-integration-edb0b9651608

[2] https://github.com/microsoft/autogen/blob/main/notebook/agentchat_graph_modelling_language_using_select_speaker.ipynb

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

幻兽帕鲁服务器搭建教程分享,小白有福了

如何自建幻兽帕鲁服务器?基于阿里云服务器搭建幻兽帕鲁palworld服务器教程来了,一看就懂系列。本文是利用OOS中幻兽帕鲁扩展程序来一键部署幻兽帕鲁服务器,阿里云百科aliyunbaike.com分享官方基于阿里云服务器快速创建幻兽帕鲁服务器教程&…

vue 样式隔离原理

日常写单文件组件时&#xff0c;会在style添加scoped属性&#xff0c;如<style scoped>&#xff0c;目的是为了隔离组件与组件之间的样式&#xff0c;如下面的例子&#xff1a; <template><p class"foo">这是foo</p><p class"bar&q…

【SpringCloud Nacos】 微服务治理介绍及Nacos引入初体验

文章目录 前言服务治理介绍什么是服务治理1、服务发现2、服务配置3、服务健康检测 常见的注册中心ZookeeperEurekaConsulNacos Nacos 简介Nacos 实战入门搭建nacos环境1、安装nacos2、配置nacos3、访问nacos 将商品微服务注册到 nacos1、在 pom. xml 中添加 nacos 的依赖2、在主…

华为机考入门python3--(0)测试题1-句子平均重量

分类&#xff1a;字符串 知识点&#xff1a; 获取输入 input().strip().split(" ") 拼接列表 " ".join(list) 输出指定位数的浮点数 print("%.2f" % value) len() 函数对于很多内置的数据类型都适用&#xff0c;它返回对象的元素个数或长度。…

Android App开发基础(3)——App的设计规范

3 App的设计规范 本节介绍了App工程的源码设计规范&#xff0c;首先App将看得见的界面设计与看不见的代码逻辑区分开&#xff0c;然后利用XML标记描绘应用界面&#xff0c;同时使用Java代码书写程序逻辑&#xff0c;从而形成App前后端分离的设计规约&#xff0c;有利于提高App集…

零基础学编程工具简介,中文编程开发工具

零基础学编程工具简介&#xff0c;中文编程开发工具 一、前言 零基础自学编程&#xff0c;中文编程工具下载&#xff0c;中文编程工具构件之扩展系统菜单构件教程 编程系统化教程链接https://jywxz.blog.csdn.net/article/details/134073098?spm1001.2014.3001.5502 给大家…

大创项目推荐 题目:基于FP-Growth的新闻挖掘算法系统的设计与实现

文章目录 0 前言1 项目背景2 算法架构3 FP-Growth算法原理3.1 FP树3.2 算法过程3.3 算法实现3.3.1 构建FP树 3.4 从FP树中挖掘频繁项集 4 系统设计展示5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于FP-Growth的新闻挖掘算法系统的设计与实现…

并行化K-means聚类算法的实现与分析

并行化K-means聚类算法 并行化K-means聚类算法的实现与分析项目背景与意义算法原理与串行实现分析并行化策略与关键细节实验结果与讨论未来改进方向结语 并行化K-means聚类算法的实现与分析 在大数据时代&#xff0c;对数据进行高效的聚类是数据分析与挖掘的重要工具之一。本文…

云轴科技ZStack成为交通运输业上云用云推进中心首批成员单位

近日&#xff0c;中国信息通信研究院、中国交通运输协会信息专业委员会联合发起成立“交通运输业上云用云推进中心”&#xff0c;上海云轴信息科技有限公司&#xff08;简称云轴科技ZStack&#xff09;凭借优秀的产品技术创新能力和在交通运输领域的实践经验成为首批成员单位并…

37、Flink 的CDC 格式:debezium部署以及mysql示例(完整版)

Flink 系列文章 一、Flink 专栏 Flink 专栏系统介绍某一知识点&#xff0c;并辅以具体的示例进行说明。 1、Flink 部署系列 本部分介绍Flink的部署、配置相关基础内容。 2、Flink基础系列 本部分介绍Flink 的基础部分&#xff0c;比如术语、架构、编程模型、编程指南、基本的…

记录yolov8_obb训练自己的数据集

一.数据集制作 1.标注软件&#xff1a;roLabelImg roLabelImg是基于labelImg改进的&#xff0c;是用来标注为VOC格式的数据&#xff0c;但是在labelImg的基础上增加了能够使标注的框进行旋转的功能。 2.数据格式转换 2.1 xml转txt # 文件名称 &#xff1a;roxml_to_dota.p…

Linux | makefile简单教程 | Makefile的工作原理

前言 在学习完了Linux的基本操作之后&#xff0c;我们知道在linux中编写代码&#xff0c;编译代码都是要手动gcc命令&#xff0c;来执行这串代码的。 但是我们难道在以后运行代码的时候&#xff0c;难道都要自己敲gcc命令嘛&#xff1f;这是不是有点太烦了&#xff1f; 在vs中…

接口自动化测试:mock server之Moco工具

什么是mock server mock&#xff1a;英文可以翻译为模仿的&#xff0c;mock server是我们用来解除依赖&#xff08;耦合&#xff09;&#xff0c;假装实现的技术&#xff0c;比如说&#xff0c;前端需要使用某些api进行调试&#xff0c;但是服务端并没有开发完成这些api&#…

SAP 票据批导实现方法

增强结构定义 变量定义 调用bapi前处理相关变量

【JavaScript权威指南第七版】读书笔记速度

JavaScript权威指南第七版 序正文前言&#xff1a;图中笔记重点知识第1章 JavaScript简介第一章总结 第2章 词法结构注释字面量标识符和保留字Unicode可选的分号第二章总结 第3章 类型、值和变量【重要】原始类型特殊类型第三章总结 第4章 表达式与操作符表达式操作符条件式调用…

HTML-框架标签、实体、全局属性和元信息

HTML 1.框架标签 <iframe name"b站" src"https://www.bilibili.com" width"500" height"300" frameborder"0"></iframe>iframe 标签的实际应用&#xff1a; 在网页中嵌入广告。与超链接或表单的 target 配合&a…

林浩然的Java冒险:从单分支到多分支的奇趣编程之旅

林浩然的Java冒险&#xff1a;从单分支到多分支的奇趣编程之旅 Lin Haoran’s Java Adventure: A Whimsical Journey from Single Branch to Multiple Branches 在那个充满神秘色彩的Java编程世界里&#xff0c;林浩然是一位无畏的程序武士。他的江湖历险从单一的分支结构开始&…

链表的应用1--多项式求和

今天学数据结构学到的链表应用于多项式相加&#xff0c;但是书上的代码没看懂&#xff0c;在看了点资料和问ChatGPT以后想到的一个算法&#xff0c;后面有更好的想法在回来更新算法 1.链表相关结构&#xff1a; //链表结点结构 typedef struct linknode {int coef;//系数 int…

磁悬浮轴承行业调研:未来高端市场占比将有所提升

磁悬浮轴承是利用磁力作用将转子悬浮于空中&#xff0c;使转子与定子之间没有机械接触。其原理是磁感应线与磁浮线成垂直&#xff0c;轴芯与磁浮线是平行的&#xff0c;所以转子的重量就固定在运转的轨道上&#xff0c;利用几乎是无负载的轴芯往反磁浮线方向顶撑&#xff0c;形…

【C++修行之道】STL(初识pair、vector)

目录 一、pair 1.1pair的定义和结构 1.2pair的嵌套 1.3pair自带排序规则 1.4代码示例 二、vector 2.1vector的定义和特性 2.2vector的初始化 一维初始化&#xff1a; 2.3vector的常用函数 2.4vector排序去重 排序: 去重&#xff1a; 示例&#xff1a; 一、pair …