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

news2024/11/18 1:33:37

       随着大型语言模型的快速发展,构建基于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

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

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

相关文章

259:vue+openlayers: 显示海量多边形数据,10ms加载完成

第259个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中通过WebGLVectorLayerRenderer方式加载海量多边形数据。这里相当于将海量的数据放在同一个层的source中,然后通过webglTile的方式渲染出这一层。 本示例数据为5000个多边形,加载速度超级快。 直接…

D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸

编辑:ll D35XB80-ASEMI整流桥D35XB80参数、封装、尺寸 型号:D35XB80 品牌:ASEMI 封装:GBJ-5 最大重复峰值反向电压:800V 最大正向平均整流电流(Vdss):35A 功率(Pd): 芯片个数&#xff1…

Web 开发 6:Redis 缓存(Flask项目使用Redis并同时部署到Docker详细流程 附项目源码)

大家好!欢迎来到第六篇 Web 开发教程,今天我们将探讨一个非常重要的话题:Redis 缓存。作为一个互联网开发者,你一定知道在处理大量请求时,性能优化是至关重要的。而 Redis 缓存正是帮助我们提升系统性能的利器。Redis …

tcpdump 抓包无法落盘

文章目录 问题背景解决办法 问题背景 在嵌入式设备中(Linux系统),为了分析两个网络节点的通讯问题,往往需要用到tcpdump,抓一个.pcap的包在PC端进行分析。博主在实际操作中发现,抓包无法实时落盘。 解决办法 # 下面的命令是写在…

MATLAB环境下一种贝叶斯稀疏盲反卷积算法

稀疏盲反卷积贝叶斯估计方法通常使用伯努利-高斯分布(BG)先验的稀疏序列建模,并利用马尔可夫链蒙特卡罗(MCMC)方法进行未知估计。然而,BG模型的离散性会有计算瓶颈。为了解决这个问题,提出一个替代方案,采用MCMC方法对稀疏序列进行…

Mac下查看、配置和使用环境变量

Mac下查看、配置和使用环境变量 一:Mac怎么查看环境变量命令 printenv一:这个命令会一次性列出所有环境变量的键值对,输出格式为: VAR1value1 VAR2value2 ...二: 也可以通过给这个命令加上环境变量名参数&#xff0…

Linux CPU 负载说明

一、背景 工作中我们经常遇到CPU 负载高,CPU负载高意味着什么? CPU的负载是怎么计算的? top指令中的各个指标代表什么含义? 二、CPU 负载计算方法 在系统出现负载问题,通常会使用uptime和top确认负载,这两…

云微呼云呼叫中心:颠覆传统客户服务模式的数字化创新

1. 引言 随着云计算技术的不断成熟和普及,云呼叫中心作为一种全新的客户服务解决方案,正在以其高效、灵活和智能的特点,逐渐颠覆传统的客户服务模式。本文将深入探讨云呼叫中心的定义、特点、优势以及对企业的重要意义。 2. 云呼叫中心的定…

龙行龘龘•前程朤朤 | 联诚发2023年度述职大会与年会圆满举行

1月27日上午,以“不忘初心・砥砺前行”为主题的2023年度述职大会在联诚发深圳总部隆重举行,本次大会总结了过去一年的成就与不足,展望了未来一年的方向和目标。联诚发创始人兼总裁龙平芳、董秘毛强军、各部门负责人以及深圳总部全体职工出席了…

JSP概述和基本使用

1,JSP 概述 JSP(全称:Java Server Pages):Java 服务端页面。是一种动态的网页技术,其中既可以定义 HTML、JS、CSS等静态内容,还可以定义 Java代码的动态内容,也就是 JSP HTML Jav…

LabVIEW探测器CAN总线系统

介绍了一个基于FPGA和LabVIEW的CAN总线通信系统,该系统专为与各单机进行系统联调测试而设计。通过设计FPGA的CAN总线功能模块和USB功能模块,以及利用LabVIEW开发的上位机程序,系统成功实现了CAN总线信息的收发、存储、解析及显示功能。测试结…

【更新】省级产业结构合理化+高度化+高级化数据(2000-2022年)

数据指标测算: (1)参考袁航等人(2018)的测算方法,采用泰尔指数来衡量产业结构合理化指标,用产业结构层次系数来表示产业结构高度化指标: (2)参考张欣艳等人&…

免费分享一套微信小程序外卖跑腿点餐(订餐)系统(uni-app+SpringBoot后端+Vue管理端技术实现) ,帅呆了~~

大家好,我是java1234_小锋老师,看到一个不错的微信小程序外卖跑腿点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现) ,分享下哈。 项目视频演示 【免费】微信小程序外卖跑腿点餐(订餐)系统(uni-appSpringBoot后端Vue管理端技术实现)…

解锁亚马逊鲲鹏系统奥秘:自动生成AI角色,随心定制养号计划

亚马逊鲲鹏系统是一款强大的软件,是一款可以批量注册亚马逊买家账号并能自动化养号的软件。其独特的功能和操作流程使得用户能够轻松管理多个账号,实现高效的运营和管理。 首先,该软件提供了AI智能养号功能,用户可以自动生成具有真…

TensorFlow2实战-系列教程4:数据增强:keras工具包/Data Augmentation

🧡💛💚TensorFlow2实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Jupyter Notebook中进行 本篇文章配套的代码资源已经上传 对于图像数据,将其进行翻转、放缩、平移、旋转操作就可以得到一组新的数据…

【JavaScript基础入门】05 JavaScript基础语法(三)

JavaScript基础语法(三) 目录 JavaScript基础语法(三)数组概述数组语法多维数组 操作数组修改数组获取数组长度数组和字符串之间的转换添加和删除数组项 Null 和 Undefined字符串连接字符串字符串转换获取字符串的长度在字符串中查…

Python之代码覆盖率框架coverage使用介绍

Python代码覆盖率工具coverage.py其实是一个第三方的包,同时支持Python2和Python3版本。 安装也非常简单,直接运行: pip install coverage 安装完成后,会在Python环境下的\Scripts下看到coverage.exe; 首先我们编写…

Linux(4)——Linux用户和用户组管理

Linux用户和用户组管理 插播!插播!插播!亲爱的朋友们,我们的Cmake/Makefile/Shell这三个课程上线啦!感兴趣的小伙伴可以去下面的链接学习哦~ 构建工具大师-CSDN程序员研修院 Linux用户和用户组管理是系统管理中非常重…

SharedPreferences卡顿分析

SP的使用及存在的问题 SharedPreferences(以下简称SP)是Android本地存储的一种方式,是以key-value的形式存储在/data/data/项目包名/shared_prefs/sp_name.xml里,SP的使用示例及源码解析参见:Android本地存储之SharedPreferences源码解析。以…

条件变量、线程池以及线程的GDB调试学习笔记

目录 一、条件变量 二、线程池概念和实现 三、线程的GDB调试 一、条件变量 应用场景:生产者消费者问题,是线程同步的一种手段。 必要性:为了实现等待某个资源,让线程休眠,提高运行效率 使用步骤: 初始…