你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

使用连接代理构建协作多代理系统

借助 Azure AI Foundry 代理服务中的连接代理,可以将复杂的任务分解为协调的专用角色,而无需自定义业务流程协调程序或手动编码的路由逻辑。 借助此功能,你可以设计一个系统,其中主要代理智能地委托给专用的子代理,简化客户支持、市场研究、法律摘要和财务分析等工作流。

与其让一个代理负担过多技能,不如构建一些专注且可重用的代理,它们可以无缝协作,从而提升性能和维护性。

特点

  • 简化的工作流设计:跨专用代理分解复杂的任务,以减少复杂性并提高清晰度。
  • 无需自定义业务流程:主代理使用自然语言路由任务,无需硬编码逻辑。
  • 轻松扩展性:添加新连接代理(例如转换或风险评分),而无需修改主代理。
  • 提高了可靠性和可跟踪性:为每个代理分配重点责任,以便更轻松地调试和提高可审核性。
  • 灵活的设置选项:在 Foundry 门户中使用无代码接口或通过 Python SDK 以编程方式配置代理。

示例:使用连接的代理构建模块化合同评审代理

随着用例的复杂性增加,可以通过向多个连接的代理分配特定职责来扩展 AI 解决方案。 这样,每个代理都可以专门处理较窄的任务,而主代理协调整个工作流。 这种模块化设计增强了准确性、可维护性和可跟踪性,尤其是对于法律、合规性和采购等文档密集型域。 让我们演练一个真实示例,演示如何使用连接的代理构建 合同评审助手

体系结构概述

主代理 - 合同协调者

充当中心接口。 它解释用户提示(如“总结条款”、“比较草稿”或“检查合规性”),确定任务类型,并将其委托给相应的连接代理。

  • 使用的工具:没有直接使用的工具

  • 职责:意向分类和委派

  • 代理说明示例

    “你是合同审查助理。 根据用户查询,确定任务是否涉及子句摘要、文档比较或符合性检查,并相应地路由。

连接的代理 1:子句摘要生成器

从合同中提取关键部分(如终止、赔偿或保密),并用纯语言汇总它们。

  • 使用的工具

    • 文件搜索以检索上传的协定
    • 代码解释器扫描文档中的条款标题并总结内容。
  • 职责:信息提取和汇总

  • 代理说明示例

    “从提供的合同中提取并汇总”终止“、”付款条款“和”赔偿条款”。

连接的代理 2:合规性验证程序

根据内部标准或上传的准则检查合同,以确定有风险或不符合的语言。

  • 使用的工具

    • 文件搜索以访问内部策略文档或合同模板
    • OpenAPI 工具调用内部符合性规则 API
    • 使用 Azure Function 或 Azure 逻辑应用运行简单的逻辑检查(例如必需的子句是否存在,或执行阈值验证)
  • 职责:策略匹配和风险标记

  • 示例提示说明

    “根据公司合规性准则查看本文档,并标记与已批准的模板的任何偏差。

局限性

  • 连接的代理无法使用函数调用工具调用本地函数。 建议改用 OpenAPI 工具Azure Functions
  • 目前无法保证引用能够从连接的代理传递。 你可以尝试将提示工程与不同的模型结合使用,以尝试并改善引文将由主代理输出的可能性,但结果会受到可变性的影响。

创建多代理设置

  1. 导航到门户中的 “代理 ”页
  2. 从列表中选择现有代理或创建新代理。
  3. 向下滚动到代理设置面板中的 “已连接代理 ”部分,然后选择“ 添加 +”。

Azure AI Foundry 中代理页的屏幕截图。

  1. 在出现的对话框中,选择主代理要将任务委托到的代理,并描述:

    • 从下拉列表中选择 现有代理 。 这是主代理要将任务委托到的已连接代理。
    • 输入连接代理 的唯一名称 (仅字母和下划线)。 此名称用于 API 级函数调用。 保持其描述性和机器可读性,以最大程度地提高召回率(例如,summarize_textlookup_product_info)。
    • 添加一个明确的 说明 ,说明何时以及为何应调用连接的代理。 这有助于指导主代理在运行时何时将任务移交给连接代理的决策。
  2. 选择“添加 +”

  3. 重复步骤 3-5,将其他专用代理添加到主代理。

  4. 连接代理出现在设置面板中后,向上滚动并选择“在 Playground 中尝试

  5. 使用 Agent Playground 中的测试提示来验证主代理在适用时是否正确将任务路由到连接的代理。 例如,如果已创建名为research_agent的主代理,并且该代理未配置任何工具,而且连接了名为stock_price_bot的代理,请尝试如下所示的提示:

    “目前Microsoft的股价是多少?

    research_agent 应该根据定义的路由说明将此请求委托给 stock_price_bot

连接的代理屏幕的屏幕截图

使用 .NET SDK

注释

这显示了同步用法。 可以在 GitHub 上找到异步示例

要让您的代理能够使用已连接的代理,您需要使用 ConnectedAgentToolDefinition 以及代理 ID、名称和描述。

  1. 首先,我们需要创建代理客户端并读取环境变量,这将在后续步骤中使用。

    var projectEndpoint = configuration["ProjectEndpoint"];
    var modelDeploymentName = configuration["ModelDeploymentName"];
    
    PersistentAgentsClient client = new(projectEndpoint, new DefaultAzureCredential());
    
  2. 接下来,我们将使用代理客户端创建主代理 mainAgent和连接的 stockAgent 代理。 此连接代理将用于初始化ConnectedAgentToolDefinition

    PersistentAgent stockAgent = client.Administration.CreateAgent(
            model: modelDeploymentName,
            name: "stock_price_bot",
            instructions: "Your job is to get the stock price of a company. If you don't know the realtime stock price, return the last known stock price."
            // tools: [...] tools that would be used to get stock prices
        );
    ConnectedAgentToolDefinition connectedAgentDefinition = new(new ConnectedAgentDetails(stockAgent.Id, stockAgent.Name, "Gets the stock price of a company"));
    
    PersistentAgent mainAgent = client.Administration.CreateAgent(
            model: modelDeploymentName,
            name: "stock_price_bot",
            instructions: "Your job is to get the stock price of a company, using the available tools.",
            tools: [connectedAgentDefinition]
        );
    
    
    
  3. 现在,我们将创建线程,添加消息,该消息包含对代理的提问,然后开始执行。

    PersistentAgentThread thread = client.Threads.CreateThread();
    
    // Create message to thread
    PersistentThreadMessage message = client.Messages.CreateMessage(
        thread.Id,
        MessageRole.User,
        "What is the stock price of Microsoft?");
    
    // Run the agent
    ThreadRun run = client.Runs.CreateRun(thread, agent);
    do
    {
        Thread.Sleep(TimeSpan.FromMilliseconds(500));
        run = client.Runs.GetRun(thread.Id, run.Id);
    }
    while (run.Status == RunStatus.Queued
        || run.Status == RunStatus.InProgress);
    
    // Confirm that the run completed successfully
    if (run.Status != RunStatus.Completed)
    {
        throw new Exception("Run did not complete successfully, error: " + run.LastError?.Message);
    }
    
  4. 按时间顺序将代理消息打印到控制台。

    Pageable<PersistentThreadMessage> messages = client.Messages.GetMessages(
        threadId: thread.Id,
        order: ListSortOrder.Ascending
    );
    
    foreach (PersistentThreadMessage threadMessage in messages)
    {
        Console.Write($"{threadMessage.CreatedAt:yyyy-MM-dd HH:mm:ss} - {threadMessage.Role,10}: ");
        foreach (MessageContent contentItem in threadMessage.ContentItems)
        {
            if (contentItem is MessageTextContent textItem)
            {
                string response = textItem.Text;
                if (textItem.Annotations != null)
                {
                    foreach (MessageTextAnnotation annotation in textItem.Annotations)
                    {
                        if (annotation is MessageTextUriCitationAnnotation urlAnnotation)
                        {
                            response = response.Replace(urlAnnotation.Text, $" [{urlAnnotation.UriCitation.Title}]({urlAnnotation.UriCitation.Uri})");
                        }
                    }
                }
                Console.Write($"Agent response: {response}");
            }
            else if (contentItem is MessageImageFileContent imageFileItem)
            {
                Console.Write($"<image from ID: {imageFileItem.FileId}");
            }
            Console.WriteLine();
        }
    }
    
  5. 通过删除线程和代理来清理资源。

    agentClient.DeleteThread(threadId: thread.Id);
    agentClient.DeleteAgent(agentId: agent.Id);
    agentClient.DeleteAgent(agentId: connectedAgent.Id);
    

创建多代理设置

若要创建多代理设置,请执行以下步骤:

  1. 初始化客户端对象。

    import os
    from azure.ai.projects import AIProjectClient
    from azure.ai.agents.models import ConnectedAgentTool, MessageRole
    from azure.identity import DefaultAzureCredential
    
    
    project_client = AIProjectClient(
    endpoint=os.environ["PROJECT_ENDPOINT"],
    credential=DefaultAzureCredential(),
    )
    
  2. 创建一个将连接到“主”代理的代理。

    stock_price_agent = project_client.agents.create_agent(
        model=os.environ["MODEL_DEPLOYMENT_NAME"],
        name="stock_price_bot",
        instructions="Your job is to get the stock price of a company. If you don't know the realtime stock price, return the last known stock price.",
        #tools=... # tools to help the agent get stock prices
    )
    
  3. 使用代理 ID、名称和说明初始化连接的代理工具

    connected_agent = ConnectedAgentTool(
        id=stock_price_agent.id, name=stock_price_agent.name, description="Gets the stock price of a company"
    )
    
  4. 创建将使用已连接代理的“主”代理。

    agent = project_client.agents.create_agent(
        model=os.environ["MODEL_DEPLOYMENT_NAME"],
        name="my-agent",
        instructions="You are a helpful agent, and use the available tools to get stock prices.",
        tools=connected_agent.definitions,
    )
    
    print(f"Created agent, ID: {agent.id}")
    
  5. 创建线程并向其添加消息。

    thread = project_client.agents.threads.create()
    print(f"Created thread, ID: {thread.id}")
    
    # Create message to thread
    message = project_client.agents.messages.create(
        thread_id=thread.id,
        role=MessageRole.USER,
        content="What is the stock price of Microsoft?",
    )
    print(f"Created message, ID: {message.id}")
    
    
  6. 创建一个运行并等待它完成。

    
    # Create and process Agent run in thread with tools
    run = project_client.agents.runs.create_and_process(thread_id=thread.id, agent_id=agent.id)
    print(f"Run finished with status: {run.status}")
    
    if run.status == "failed":
        print(f"Run failed: {run.last_error}")
    
    # Delete the Agent when done
    project_client.agents.delete_agent(agent.id)
    print("Deleted agent")
    
    # Delete the connected Agent when done
    project_client.agents.delete_agent(stock_price_agent.id)
    print("Deleted connected agent")
    
  7. 打印代理的响应。 主代理将编译来自连接的代理的响应并提供响应。 连接的代理响应仅对主代理可见,而对最终用户不可见。

    # Print the Agent's response message with optional citation
    response_message = project_client.agents.messages.list(thread_id=thread.id).get_last_message_by_role(
        MessageRole.AGENT
    )
    if response_message:
        for text_message in response_message.text_messages:
            print(f"Agent response: {text_message.text.value}")
        for annotation in response_message.url_citation_annotations:
            print(f"URL Citation: [{annotation.url_citation.title}]({annotation.url_citation.url})")