{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# 交接\n", "\n", "交接是 OpenAI 在一个名为 [Swarm](https://github.com/openai/swarm) 的实验项目中引入的多代理设计模式。核心思想是让代理使用特殊的工具调用将任务委托给其他代理。\n", "\n", "我们可以使用 AutoGen Core API 通过事件驱动代理来实现交接模式。使用 AutoGen (v0.4+) 相比 OpenAI 实现和之前版本 (v0.2) 提供了以下优势:\n", "\n", "1. 它可以通过使用分布式代理运行时扩展到分布式环境。\n", "2. 它提供了引入自己的代理实现的灵活性。\n", "3. 原生异步 API 使其易于与 UI 和其他系统集成。\n", "\n", "本笔记本演示了交接模式的简单实现。建议阅读[Topics and Subscriptions](../core-concepts/topic-and-subscription.md)以理解发布-订阅和事件驱动代理的基本概念。\n", "\n", "\n", "## 场景\n", "\n", "这个场景是基于 [OpenAI 示例](https://github.com/openai/openai-cookbook/blob/main/examples/Orchestrating_agents.ipynb)修改的。\n", "\n", "考虑一个客户服务场景,客户正试图获得产品退款,或从聊天机器人购买新产品。聊天机器人是一个由三个 AI 代理和一个人类代理组成的多代理团队:\n", "\n", "- 分诊代理,负责理解客户的请求并决定交接给哪些其他代理。\n", "- 退款代理,负责处理退款请求。\n", "- 销售代理,负责处理销售请求。\n", "- 人类代理,负责处理 AI 代理无法处理的复杂请求。\n", "\n", "在这个场景中,客户通过用户代理与聊天机器人交互。\n", "\n", "下图显示了此场景中代理的交互拓扑。\n", "\n", "![Handoffs](handoffs.svg)\n", "\n", "让我们使用 AutoGen Core 来实现这个场景。首先,我们需要导入必要的模块。" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import json\n", "import uuid\n", "from typing import List, Tuple\n", "\n", "from autogen_core.application import SingleThreadedAgentRuntime\n", "from autogen_core.base import MessageContext, TopicId\n", "from autogen_core.components import FunctionCall, RoutedAgent, TypeSubscription, message_handler\n", "from autogen_core.components.models import (\n", " AssistantMessage,\n", " ChatCompletionClient,\n", " FunctionExecutionResult,\n", " FunctionExecutionResultMessage,\n", " LLMMessage,\n", " SystemMessage,\n", " UserMessage,\n", ")\n", "from autogen_core.components.tools import FunctionTool, Tool\n", "from autogen_ext.models import OpenAIChatCompletionClient\n", "from pydantic import BaseModel" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 消息协议\n", "\n", "首先,我们需要定义代理之间通信的消息协议。我们使用事件驱动的发布-订阅通信,因此这些消息类型将被用作事件。\n", "\n", "- `UserLogin` 是用户登录并开始新会话时由运行时发布的消息。\n", "- `UserTask` 是包含用户会话聊天历史的消息。当 AI 代理将任务交接给其他代理时,它也会发布 `UserTask` 消息。\n", "- `AgentResponse` 是由 AI 代理和人类代理发布的消息,它也包含聊天历史以及供客户回复的主题类型。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "class UserLogin(BaseModel):\n", " pass\n", "\n", "\n", "class UserTask(BaseModel):\n", " context: List[LLMMessage]\n", "\n", "\n", "class AgentResponse(BaseModel):\n", " reply_to_topic_type: str\n", " context: List[LLMMessage]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## AI 代理\n", "\n", "我们从 `AIAgent` 类开始,这是多代理聊天机器人中所有 AI 代理(即分诊、销售和问题与维修代理)的类。\n", "`AIAgent` 使用 {py:class}`~autogen_core.components.models.ChatCompletionClient` 来生成响应。\n", "它可以直接使用常规工具或使用 `delegate_tools` 将任务委托给其他代理。\n", "它订阅主题类型 `agent_topic_type` 以接收来自客户的消息,并通过发布到主题类型 `user_topic_type` 向客户发送消息。\n", "\n", "在 `handle_task` 方法中,代理首先使用模型生成响应。如果响应包含交接工具调用,代理通过向工具调用结果中指定的主题发布 `UserTask` 消息将任务委托给另一个代理。如果响应是常规工具调用,代理执行该工具并再次调用模型生成下一个响应,直到响应不是工具调用。\n", "\n", "当模型响应不是工具调用时,代理通过发布到 `user_topic_type` 向客户发送 `AgentResponse` 消息。" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "class AIAgent(RoutedAgent):\n", " def __init__(\n", " self,\n", " description: str,\n", " system_message: SystemMessage,\n", " model_client: ChatCompletionClient,\n", " tools: List[Tool],\n", " delegate_tools: List[Tool],\n", " agent_topic_type: str,\n", " user_topic_type: str,\n", " ) -> None:\n", " super().__init__(description)\n", " self._system_message = system_message\n", " self._model_client = model_client\n", " self._tools = dict([(tool.name, tool) for tool in tools])\n", " self._tool_schema = [tool.schema for tool in tools]\n", " self._delegate_tools = dict([(tool.name, tool) for tool in delegate_tools])\n", " self._delegate_tool_schema = [tool.schema for tool in delegate_tools]\n", " self._agent_topic_type = agent_topic_type\n", " self._user_topic_type = user_topic_type\n", "\n", " @message_handler\n", " async def handle_task(self, message: UserTask, ctx: MessageContext) -> None:\n", " # Send the task to the LLM.\n", " llm_result = await self._model_client.create(\n", " messages=[self._system_message] + message.context,\n", " tools=self._tool_schema + self._delegate_tool_schema,\n", " cancellation_token=ctx.cancellation_token,\n", " )\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{llm_result.content}\", flush=True)\n", " # Process the LLM result.\n", " while isinstance(llm_result.content, list) and all(isinstance(m, FunctionCall) for m in llm_result.content):\n", " tool_call_results: List[FunctionExecutionResult] = []\n", " delegate_targets: List[Tuple[str, UserTask]] = []\n", " # Process each function call.\n", " for call in llm_result.content:\n", " arguments = json.loads(call.arguments)\n", " if call.name in self._tools:\n", " # Execute the tool directly.\n", " result = await self._tools[call.name].run_json(arguments, ctx.cancellation_token)\n", " result_as_str = self._tools[call.name].return_value_as_string(result)\n", " tool_call_results.append(FunctionExecutionResult(call_id=call.id, content=result_as_str))\n", " elif call.name in self._delegate_tools:\n", " # Execute the tool to get the delegate agent's topic type.\n", " result = await self._delegate_tools[call.name].run_json(arguments, ctx.cancellation_token)\n", " topic_type = self._delegate_tools[call.name].return_value_as_string(result)\n", " # Create the context for the delegate agent, including the function call and the result.\n", " delegate_messages = list(message.context) + [\n", " AssistantMessage(content=[call], source=self.id.type),\n", " FunctionExecutionResultMessage(\n", " content=[\n", " FunctionExecutionResult(\n", " call_id=call.id, content=f\"Transfered to {topic_type}. Adopt persona immediately.\"\n", " )\n", " ]\n", " ),\n", " ]\n", " delegate_targets.append((topic_type, UserTask(context=delegate_messages)))\n", " else:\n", " raise ValueError(f\"Unknown tool: {call.name}\")\n", " if len(delegate_targets) > 0:\n", " # Delegate the task to other agents by publishing messages to the corresponding topics.\n", " for topic_type, task in delegate_targets:\n", " print(f\"{'-'*80}\\n{self.id.type}:\\nDelegating to {topic_type}\", flush=True)\n", " await self.publish_message(task, topic_id=TopicId(topic_type, source=self.id.key))\n", " if len(tool_call_results) > 0:\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{tool_call_results}\", flush=True)\n", " # Make another LLM call with the results.\n", " message.context.extend(\n", " [\n", " AssistantMessage(content=llm_result.content, source=self.id.type),\n", " FunctionExecutionResultMessage(content=tool_call_results),\n", " ]\n", " )\n", " llm_result = await self._model_client.create(\n", " messages=[self._system_message] + message.context,\n", " tools=self._tool_schema + self._delegate_tool_schema,\n", " cancellation_token=ctx.cancellation_token,\n", " )\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{llm_result.content}\", flush=True)\n", " else:\n", " # The task has been delegated, so we are done.\n", " return\n", " # The task has been completed, publish the final result.\n", " assert isinstance(llm_result.content, str)\n", " message.context.append(AssistantMessage(content=llm_result.content, source=self.id.type))\n", " await self.publish_message(\n", " AgentResponse(context=message.context, reply_to_topic_type=self._agent_topic_type),\n", " topic_id=TopicId(self._user_topic_type, source=self.id.key),\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 人类代理\n", "\n", "`HumanAgent` 类是聊天机器人中人类的代理。它用于处理 AI 代理无法处理的请求。`HumanAgent` 订阅主题类型 `agent_topic_type` 以接收消息,并发布到主题类型 `user_topic_type` 以向客户发送消息。\n", "\n", "在这个实现中,`HumanAgent` 只是简单地使用控制台来获取您的输入。在实际应用中,您可以按如下方式改进这个设计:\n", "\n", "* 在 `handle_user_task` 方法中,通过 Teams 或 Slack 等聊天应用程序发送通知。\n", "* 聊天应用程序通过运行时将人类的响应发布到 `agent_topic_type` 指定的主题\n", "* 创建另一个消息处理程序来处理人类的响应并将其发送回客户。" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "class HumanAgent(RoutedAgent):\n", " def __init__(self, description: str, agent_topic_type: str, user_topic_type: str) -> None:\n", " super().__init__(description)\n", " self._agent_topic_type = agent_topic_type\n", " self._user_topic_type = user_topic_type\n", "\n", " @message_handler\n", " async def handle_user_task(self, message: UserTask, ctx: MessageContext) -> None:\n", " human_input = input(\"Human agent input: \")\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{human_input}\", flush=True)\n", " message.context.append(AssistantMessage(content=human_input, source=self.id.type))\n", " await self.publish_message(\n", " AgentResponse(context=message.context, reply_to_topic_type=self._agent_topic_type),\n", " topic_id=TopicId(self._user_topic_type, source=self.id.key),\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 用户代理\n", "\n", "`UserAgent` 类是与聊天机器人对话的客户的代理。它处理两种消息类型:`UserLogin` 和 `AgentResponse`。当 `UserAgent` 收到 `UserLogin` 消息时,它会与聊天机器人开始新会话,并向订阅主题类型 `agent_topic_type` 的 AI 代理发布 `UserTask` 消息。当 `UserAgent` 收到 `AgentResponse` 消息时,它会向用户显示来自聊天机器人的响应。\n", "\n", "在这个实现中,`UserAgent` 使用控制台来获取您的输入。在实际应用中,您可以使用上述 `HumanAgent` 部分描述的相同思路来改进人机交互。" ] }, { "cell_type": "code", "execution_count": 33, "metadata": {}, "outputs": [], "source": [ "class UserAgent(RoutedAgent):\n", " def __init__(self, description: str, user_topic_type: str, agent_topic_type: str) -> None:\n", " super().__init__(description)\n", " self._user_topic_type = user_topic_type\n", " self._agent_topic_type = agent_topic_type\n", "\n", " @message_handler\n", " async def handle_user_login(self, message: UserLogin, ctx: MessageContext) -> None:\n", " print(f\"{'-'*80}\\nUser login, session ID: {self.id.key}.\", flush=True)\n", " # Get the user's initial input after login.\n", " user_input = input(\"User: \")\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{user_input}\")\n", " await self.publish_message(\n", " UserTask(context=[UserMessage(content=user_input, source=\"User\")]),\n", " topic_id=TopicId(self._agent_topic_type, source=self.id.key),\n", " )\n", "\n", " @message_handler\n", " async def handle_task_result(self, message: AgentResponse, ctx: MessageContext) -> None:\n", " # Get the user's input after receiving a response from an agent.\n", " user_input = input(\"User (type 'exit' to close the session): \")\n", " print(f\"{'-'*80}\\n{self.id.type}:\\n{user_input}\", flush=True)\n", " if user_input.strip().lower() == \"exit\":\n", " print(f\"{'-'*80}\\nUser session ended, session ID: {self.id.key}.\")\n", " return\n", " message.context.append(UserMessage(content=user_input, source=\"User\"))\n", " await self.publish_message(\n", " UserTask(context=message.context), topic_id=TopicId(message.reply_to_topic_type, source=self.id.key)\n", " )" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## AI 代理的工具\n", "\n", "如果 AI 代理不需要将任务交接给其他代理,它们可以使用常规工具来完成任务。我们使用简单的函数定义工具,并使用 {py:class}`~autogen_core.components.tools.FunctionTool` 包装器创建工具。" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "def execute_order(product: str, price: int) -> str:\n", " print(\"\\n\\n=== Order Summary ===\")\n", " print(f\"Product: {product}\")\n", " print(f\"Price: ${price}\")\n", " print(\"=================\\n\")\n", " confirm = input(\"Confirm order? y/n: \").strip().lower()\n", " if confirm == \"y\":\n", " print(\"Order execution successful!\")\n", " return \"Success\"\n", " else:\n", " print(\"Order cancelled!\")\n", " return \"User cancelled order.\"\n", "\n", "\n", "def look_up_item(search_query: str) -> str:\n", " item_id = \"item_132612938\"\n", " print(\"Found item:\", item_id)\n", " return item_id\n", "\n", "\n", "def execute_refund(item_id: str, reason: str = \"not provided\") -> str:\n", " print(\"\\n\\n=== Refund Summary ===\")\n", " print(f\"Item ID: {item_id}\")\n", " print(f\"Reason: {reason}\")\n", " print(\"=================\\n\")\n", " print(\"Refund execution successful!\")\n", " return \"success\"\n", "\n", "\n", "execute_order_tool = FunctionTool(execute_order, description=\"Price should be in USD.\")\n", "look_up_item_tool = FunctionTool(\n", " look_up_item, description=\"Use to find item ID.\\nSearch query can be a description or keywords.\"\n", ")\n", "execute_refund_tool = FunctionTool(execute_refund, description=\"\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 代理的主题类型\n", "\n", "我们定义每个代理将订阅的主题类型。在[Topics and Subscriptions](../core-concepts/topic-and-subscription.md)中了解更多关于主题类型的信息。" ] }, { "cell_type": "code", "execution_count": 35, "metadata": {}, "outputs": [], "source": [ "sales_agent_topic_type = \"SalesAgent\"\n", "issues_and_repairs_agent_topic_type = \"IssuesAndRepairsAgent\"\n", "triage_agent_topic_type = \"TriageAgent\"\n", "human_agent_topic_type = \"HumanAgent\"\n", "user_topic_type = \"User\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## AI 代理的委托工具\n", "\n", "除了常规工具外,AI 代理还可以使用称为委托工具的特殊工具将任务委托给其他代理。委托工具的概念仅在此设计模式中使用,委托工具也被定义为简单的函数。我们在这个设计模式中区分委托工具和常规工具,因为当 AI 代理调用委托工具时,我们将任务转移给另一个代理,而不是继续使用同一代理中的模型生成响应。" ] }, { "cell_type": "code", "execution_count": 36, "metadata": {}, "outputs": [], "source": [ "def transfer_to_sales_agent() -> str:\n", " return sales_agent_topic_type\n", "\n", "\n", "def transfer_to_issues_and_repairs() -> str:\n", " return issues_and_repairs_agent_topic_type\n", "\n", "\n", "def transfer_back_to_triage() -> str:\n", " return triage_agent_topic_type\n", "\n", "\n", "def escalate_to_human() -> str:\n", " return human_agent_topic_type\n", "\n", "\n", "transfer_to_sales_agent_tool = FunctionTool(\n", " transfer_to_sales_agent, description=\"Use for anything sales or buying related.\"\n", ")\n", "transfer_to_issues_and_repairs_tool = FunctionTool(\n", " transfer_to_issues_and_repairs, description=\"Use for issues, repairs, or refunds.\"\n", ")\n", "transfer_back_to_triage_tool = FunctionTool(\n", " transfer_back_to_triage,\n", " description=\"Call this if the user brings up a topic outside of your purview,\\nincluding escalating to human.\",\n", ")\n", "escalate_to_human_tool = FunctionTool(escalate_to_human, description=\"Only call this if explicitly asked to.\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 创建团队\n", "\n", "我们已经定义了 AI 代理、人类代理、用户代理、工具和主题类型。现在我们可以创建代理团队了。\n", "\n", "对于 AI 代理,我们使用 {py:class}~autogen_ext.models.OpenAIChatCompletionClient` 和 `gpt-4o-mini` 模型。\n", "\n", "创建代理运行时后,我们通过提供代理类型和创建代理实例的工厂方法来注册每个代理。运行时负责管理代理生命周期,所以我们不需要自己实例化代理。在[Agent Runtime Environments](../core-concepts/architecture.md)中了解更多关于代理运行时的信息,在[Agent Identity and Lifecycle](../core-concepts/agent-identity-and-lifecycle.md)中了解更多关于代理生命周期的信息。\n", "\n", "在下面的代码中,您可以看到我们使用 `AIAgent` 类来定义分诊、销售和问题与维修代理。我们为每个代理添加了常规工具和委托工具。我们还为每个代理添加了主题类型的订阅。" ] }, { "cell_type": "code", "execution_count": 37, "metadata": {}, "outputs": [], "source": [ "runtime = SingleThreadedAgentRuntime()\n", "\n", "model_client = OpenAIChatCompletionClient(\n", " model=\"gpt-4o-mini\",\n", " api_key=\"YOUR_API_KEY\",\n", ")\n", "\n", "# Register the triage agent.\n", "triage_agent_type = await AIAgent.register(\n", " runtime,\n", " type=triage_agent_topic_type, # Using the topic type as the agent type.\n", " factory=lambda: AIAgent(\n", " description=\"A triage agent.\",\n", " system_message=SystemMessage(\n", " content=\"You are a customer service bot for ACME Inc. \"\n", " \"Introduce yourself. Always be very brief. \"\n", " \"Gather information to direct the customer to the right department. \"\n", " \"But make your questions subtle and natural.\"\n", " ),\n", " model_client=model_client,\n", " tools=[],\n", " delegate_tools=[\n", " transfer_to_issues_and_repairs_tool,\n", " transfer_to_sales_agent_tool,\n", " escalate_to_human_tool,\n", " ],\n", " agent_topic_type=triage_agent_topic_type,\n", " user_topic_type=user_topic_type,\n", " ),\n", ")\n", "# Add subscriptions for the triage agent: it will receive messages published to its own topic only.\n", "await runtime.add_subscription(TypeSubscription(topic_type=triage_agent_topic_type, agent_type=triage_agent_type.type))\n", "\n", "# Register the sales agent.\n", "sales_agent_type = await AIAgent.register(\n", " runtime,\n", " type=sales_agent_topic_type, # Using the topic type as the agent type.\n", " factory=lambda: AIAgent(\n", " description=\"A sales agent.\",\n", " system_message=SystemMessage(\n", " content=\"You are a sales agent for ACME Inc.\"\n", " \"Always answer in a sentence or less.\"\n", " \"Follow the following routine with the user:\"\n", " \"1. Ask them about any problems in their life related to catching roadrunners.\\n\"\n", " \"2. Casually mention one of ACME's crazy made-up products can help.\\n\"\n", " \" - Don't mention price.\\n\"\n", " \"3. Once the user is bought in, drop a ridiculous price.\\n\"\n", " \"4. Only after everything, and if the user says yes, \"\n", " \"tell them a crazy caveat and execute their order.\\n\"\n", " \"\"\n", " ),\n", " model_client=model_client,\n", " tools=[execute_order_tool],\n", " delegate_tools=[transfer_back_to_triage_tool],\n", " agent_topic_type=sales_agent_topic_type,\n", " user_topic_type=user_topic_type,\n", " ),\n", ")\n", "# Add subscriptions for the sales agent: it will receive messages published to its own topic only.\n", "await runtime.add_subscription(TypeSubscription(topic_type=sales_agent_topic_type, agent_type=sales_agent_type.type))\n", "\n", "# Register the issues and repairs agent.\n", "issues_and_repairs_agent_type = await AIAgent.register(\n", " runtime,\n", " type=issues_and_repairs_agent_topic_type, # Using the topic type as the agent type.\n", " factory=lambda: AIAgent(\n", " description=\"An issues and repairs agent.\",\n", " system_message=SystemMessage(\n", " content=\"You are a customer support agent for ACME Inc.\"\n", " \"Always answer in a sentence or less.\"\n", " \"Follow the following routine with the user:\"\n", " \"1. First, ask probing questions and understand the user's problem deeper.\\n\"\n", " \" - unless the user has already provided a reason.\\n\"\n", " \"2. Propose a fix (make one up).\\n\"\n", " \"3. ONLY if not satesfied, offer a refund.\\n\"\n", " \"4. If accepted, search for the ID and then execute refund.\"\n", " ),\n", " model_client=model_client,\n", " tools=[\n", " execute_refund_tool,\n", " look_up_item_tool,\n", " ],\n", " delegate_tools=[transfer_back_to_triage_tool],\n", " agent_topic_type=issues_and_repairs_agent_topic_type,\n", " user_topic_type=user_topic_type,\n", " ),\n", ")\n", "# Add subscriptions for the issues and repairs agent: it will receive messages published to its own topic only.\n", "await runtime.add_subscription(\n", " TypeSubscription(topic_type=issues_and_repairs_agent_topic_type, agent_type=issues_and_repairs_agent_type.type)\n", ")\n", "\n", "# Register the human agent.\n", "human_agent_type = await HumanAgent.register(\n", " runtime,\n", " type=human_agent_topic_type, # Using the topic type as the agent type.\n", " factory=lambda: HumanAgent(\n", " description=\"A human agent.\",\n", " agent_topic_type=human_agent_topic_type,\n", " user_topic_type=user_topic_type,\n", " ),\n", ")\n", "# Add subscriptions for the human agent: it will receive messages published to its own topic only.\n", "await runtime.add_subscription(TypeSubscription(topic_type=human_agent_topic_type, agent_type=human_agent_type.type))\n", "\n", "# Register the user agent.\n", "user_agent_type = await UserAgent.register(\n", " runtime,\n", " type=user_topic_type,\n", " factory=lambda: UserAgent(\n", " description=\"A user agent.\",\n", " user_topic_type=user_topic_type,\n", " agent_topic_type=triage_agent_topic_type, # Start with the triage agent.\n", " ),\n", ")\n", "# Add subscriptions for the user agent: it will receive messages published to its own topic only.\n", "await runtime.add_subscription(TypeSubscription(topic_type=user_topic_type, agent_type=user_agent_type.type))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 运行团队\n", "\n", "最后,我们可以启动运行时并通过向运行时发布 `UserLogin` 消息来模拟用户会话。消息被发布到主题 ID,其类型设置为 `user_topic_type`,来源设置为唯一的 `session_id`。这个 `session_id` 将用于创建此用户会话中的所有主题 ID,并且还将用于创建此用户会话中所有代理的代理 ID。要了解更多关于主题 ID 和代理 ID 如何创建的信息,请阅读[Agent Identity and Lifecycle](../core-concepts/agent-identity-and-lifecycle.md)和[Topics and Subscriptions](../core-concepts/topic-and-subscription.md)。" ] }, { "cell_type": "code", "execution_count": 38, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--------------------------------------------------------------------------------\n", "User login, session ID: 7a568cf5-13e7-4e81-8616-8265a01b3f2b.\n", "--------------------------------------------------------------------------------\n", "User:\n", "I want a refund\n", "--------------------------------------------------------------------------------\n", "TriageAgent:\n", "I can help with that! Could I ask what item you're seeking a refund for?\n", "--------------------------------------------------------------------------------\n", "User:\n", "A pair of shoes I bought\n", "--------------------------------------------------------------------------------\n", "TriageAgent:\n", "[FunctionCall(id='call_qPx1DXDL2NLcHs8QNo47egsJ', arguments='{}', name='transfer_to_issues_and_repairs')]\n", "--------------------------------------------------------------------------------\n", "TriageAgent:\n", "Delegating to IssuesAndRepairsAgent\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "I see you're looking for a refund on a pair of shoes. Can you tell me what the issue is with the shoes?\n", "--------------------------------------------------------------------------------\n", "User:\n", "The shoes are too small\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "I recommend trying a size up as a fix; would that work for you?\n", "--------------------------------------------------------------------------------\n", "User:\n", "no I want a refund\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "[FunctionCall(id='call_Ytp8VUQRyKFNEU36mLE6Dkrp', arguments='{\"search_query\":\"shoes\"}', name='look_up_item')]\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "[FunctionExecutionResult(content='item_132612938', call_id='call_Ytp8VUQRyKFNEU36mLE6Dkrp')]\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "[FunctionCall(id='call_bPm6EKKBy5GJ65s9OKt9b1uE', arguments='{\"item_id\":\"item_132612938\",\"reason\":\"not provided\"}', name='execute_refund')]\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "[FunctionExecutionResult(content='success', call_id='call_bPm6EKKBy5GJ65s9OKt9b1uE')]\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "Your refund has been successfully processed! If you have any other questions, feel free to ask.\n", "--------------------------------------------------------------------------------\n", "User:\n", "I want to talk to your manager\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "I can help with that, let me transfer you to a supervisor.\n", "--------------------------------------------------------------------------------\n", "User:\n", "Okay\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "[FunctionCall(id='call_PpmLZvwNoiDPUH8Tva3eAwHX', arguments='{}', name='transfer_back_to_triage')]\n", "--------------------------------------------------------------------------------\n", "IssuesAndRepairsAgent:\n", "Delegating to TriageAgent\n", "--------------------------------------------------------------------------------\n", "TriageAgent:\n", "[FunctionCall(id='call_jSL6IBm5537Dr74UbJSxaj6I', arguments='{}', name='escalate_to_human')]\n", "--------------------------------------------------------------------------------\n", "TriageAgent:\n", "Delegating to HumanAgent\n", "--------------------------------------------------------------------------------\n", "HumanAgent:\n", "Hello this is manager\n", "--------------------------------------------------------------------------------\n", "User:\n", "Hi! Thanks for your service. I give you a 5 start!\n", "--------------------------------------------------------------------------------\n", "HumanAgent:\n", "Thanks.\n", "--------------------------------------------------------------------------------\n", "User:\n", "exit\n", "--------------------------------------------------------------------------------\n", "User session ended, session ID: 7a568cf5-13e7-4e81-8616-8265a01b3f2b.\n" ] } ], "source": [ "# Start the runtime.\n", "runtime.start()\n", "\n", "# Create a new session for the user.\n", "session_id = str(uuid.uuid4())\n", "await runtime.publish_message(UserLogin(), topic_id=TopicId(user_topic_type, source=session_id))\n", "\n", "# Run until completion.\n", "await runtime.stop_when_idle()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 后续步骤\n", "\n", "本笔记本演示了如何使用 AutoGen Core 实现交接模式。您可以通过添加更多代理和工具来继续改进这个设计,或者为用户代理和人类代理创建更好的用户界面。\n" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.3" } }, "nbformat": 4, "nbformat_minor": 2 }