虽然我们在很多情况下都在强调LangChain在设计上要优于MAF,但是不可否认,MAF在一些方法也有其独到之处,其中就包括针对服务端托管并运行工具的支持。也就是说,我们不仅仅可以利用AIFunctionFactory创建在Agent端本地执行的AIFunction,或者利用McpClientTool来远程调用MCP服务提供的工具外,还可以使用一系列预定义在服务端托管并运行工具。这些工具的一般以Hosted为前缀,由于它们的作用仅仅是告诉托管服务Agent种注册了此服务,所以这些工具都没有具体的实现,仅仅是一个标记而已,我们不要妄图从定义这些工具类的代码中去寻找它们的实现逻辑。

1. HostedWebSearchTool

顾名思义,HostedWebSearchTool是一个托管在服务端的Web搜索工具,它的作用是告诉托管服务Agent种注册了此服务。我们可以利用HostedWebSearchTool来调用托管服务Agent种注册的Web搜索工具。HostedWebSearchTool的定义如下,其携带的有效信息基本上只有工具名称(web_search)。

public class HostedWebSearchTool : AITool
{
	public override string Name => "web_search";
	public override IReadOnlyDictionary<string, object?> AdditionalProperties { get; }

	public HostedWebSearchTool();
	public HostedWebSearchTool(IReadOnlyDictionary<string, object?>? additionalProperties);
}

在如下这个演示程序中,我们针对指定的Microsoft Foundry项目创建了一个AIProjectClient实例,并利用它创建了一个托管服务Agent。在创建Agent时,我们将HostedWebSearchTool作为工具注册到Agent中。然后我们调用Agent的RunAsync方法来执行一个搜索任务,要求它帮我们搜索2026年世界杯战况如何?

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Extensions.AI;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;


var agent = new AIProjectClient(new Uri(projectUrl),new AzureCliCredential())
    .AsAIAgent(
        model: model, 
        instructions: "You are a helpful assistant." , 
        tools: [new HostedWebSearchTool()]
        );

var response = await agent.RunAsync("2026年世界杯战况如何?");
Console.WriteLine(response);

输出:

截至 2026 年 7 月 11 日,2026 美加墨世界杯已经进入半决赛阶段。 ([fifa.com](https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/standings))

目前已确定的四强球队包括:
- 法国
- 摩洛哥
- 西班牙
- 比利时 ([deccanchronicle.com](https://www.deccanchronicle.com/fifa-world-cup-2026/fifa-world-cup-2026-quarterfinals-full-schedule-qualified-teams-and-fixtures-1969250))

淘汰赛关键结果:
- 法国 2:0 摩洛哥(半决赛)
- 西班牙 vs 比利时 的另一场半决赛正在进行中,当前比分一度为 1:1。 ([fifa.com](https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/standings))

此前八强阶段的主要结果包括:
- 法国淘汰巴拉圭
- 摩洛哥淘汰加拿大
- 西班牙淘汰葡萄牙
- 比利时淘汰美国 ([fifa.com](https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/standings))

本届世界杯是历史上首次扩军到 48 支球队,共有 104 场比赛,由美国、加拿大、墨西哥联合举办。决赛将在美国新泽西州的大都会人寿体育场举行,时间是 2026 年 7 月 19 日。 ([fifa.com](https://www.fifa.com/en/tournaments/mens/worldcup/canadamexicousa2026/articles/match-schedule-fixtures-results-teams-stadiums?os=av.&ref=app))

如果你愿意,我也可以继续给你:
- 最新射手榜
- 中国球迷最关注球队表现
- 阿根廷 / 巴西 / 英格兰等强队战况
- 完整淘汰赛对阵图
- 今日比赛时间表

2. HostedCodeInterpreterTool

HostedCodeInterpreterTool这个工具用于LLM服务商(如 OpenAI、Azure AI)在云端沙箱中运行代码的能力。作为一个标记类的工具,其本身不包含任何代码解释和执行的逻辑,其本质是一个向AI服务的示意信号。当我们在Agent中注册此工具时,即代表授权LLM在处理当前任务时,可以自由编写并自主运行所需的代码(通常为Python),以解决复杂计算或数据分析问题。如下面的代码所示,HostedCodeInterpreterTool将工具命名为code_interpreter

public class HostedCodeInterpreterTool : AITool
{
	public override string Name => "code_interpreter";
	public override IReadOnlyDictionary<string, object?> AdditionalProperties  { get; }
	public IList<AIContent>? Inputs { get; set; }

	public HostedCodeInterpreterTool();
	public HostedCodeInterpreterTool(IReadOnlyDictionary<string, object?>? additionalProperties);
}

除了在初始化时利用参数additionalProperties来设置附加属性外,我们还可以通过Inputs属性来指定输入内容。我们可以利用它以AIContent列表的形式为代码解析器提供输入数据。这是该工具最关键的属性。代码解释器往往需要读取文件(例如让AI分析一个Excel表格或CSV数据)。支持的AIContent类型包括:

  • HostedFileContent: 指向已经上传到云端服务商那里的文件ID);
  • DataContent:直接传递二进制数据(并非所AI平台都支持直接传二进制流,不支持的会被IChatClient自动忽略)。

在如下的演示程序中,我们针对指定的Microsoft Foundry项目创建了一个AIProjectClient实例,并利用它创建了一个托管服务Agent。在创建Agent时,我们将HostedCodeInterpreterTool作为工具注册到Agent中。然后我们调用Agent的RunAsync方法来执行一个数据分析任务。

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Extensions.AI;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;

var instructions = """
你是一个资深的数据分析师。请通过编写和运行Python代码来解决用户的问题。
如果遇到数据缺失,请先编写代码进行合理的清洗(例如用均值填充)。。
""";

var agent = new AIProjectClient(new Uri(projectUrl), new AzureCliCredential())
    .AsAIAgent(
        model: model,
        instructions: instructions,
        tools: [new HostedCodeInterpreterTool()]
        );

var complex_query = ("""
如下这个CSV格式的表格存储了2025年上半年PC和Phone的月度销售数据。
                 
请帮我完成以下任务:
- 1. 检查数据,如果某月的销售数据有缺失,请用该列的平均值填充;
- 2. 分别计算PC和Phone的季度销售额和半年总销售额;
                 
```csv
Month, PC, Phone
1,20000.0,12000.0
2,25000.0,15000.0
3,23000.0,
4,27000.0,22000.0
5,,19000.0
6,32000.0,26000.0
```
""");

var response = await agent.RunAsync(complex_query);
Console.WriteLine(response);

具体来说,我们将一个不完整CSV格式的表格作为输入数据传递给HostedCodeInterpreterTool,并要求Agent帮我们完成以下任务:

  • 检查数据,如果某月的销售数据有缺失,请用该列的平均值填充;
  • 分别计算PC和Phone的季度销售额和半年总销售额。

从如下的输出结果可以看出,LLM在云端沙箱中成功运行了Python代码,并完成了数据清洗和统计计算的任务。

已完成数据检查、缺失值填充以及季度和半年销售额统计。

缺失值处理结果:
- Phone 第3月缺失,用 Phone 列平均值 18800.0 填充
- PC 第5月缺失,用 PC 列平均值 25400.0 填充

清洗后的数据:

| Month | PC | Phone |
|---|---:|---:|
| 1 | 20000.0 | 12000.0 |
| 2 | 25000.0 | 15000.0 |
| 3 | 23000.0 | 18800.0 |
| 4 | 27000.0 | 22000.0 |
| 5 | 25400.0 | 19000.0 |
| 6 | 32000.0 | 26000.0 |

销售汇总结果:

| 产品 | Q1 | Q2 | Half Year Total |
|---|---:|---:|---:|
| PC | 68000.0 | 84400.0 | 152400.0 |
| Phone | 45800.0 | 67000.0 | 112800.0 |

3. HostedFileSearchTool

HostedFileSearchTool用来告诉支持RAG功能的AI服务:我允许你检索我指定的文件、知识库或向量数据库,用来回答用户的问题”。当LLM面对需要特定知识库才能回答的问题时,如果Agent注册了此工具,LLM服务商(如OpenAI)就会在它们自家的服务器端启动检索机制,找出相关的文件片段喂给大模型。

public class HostedFileSearchTool : AITool
{
	public override string Name => "file_search";
	public override IReadOnlyDictionary<string, object?> AdditionalProperties { get; }

	public IList<AIContent>? Inputs { get; set; }
	public int? MaximumResultCount { get; set; }

	public HostedFileSearchTool();
	public HostedFileSearchTool(IReadOnlyDictionary<string, object?>? additionalProperties);
}

除了表示工具名称的Name属性(返回file_search)和附加属性AdditionalProperties外,HostedFileSearchTool还提供了两个重要的属性:

  • Inputs:指定大模型可以去哪里搜索。这是一个非常灵活的设计,支持多种云端知识源:
    • HostedFileContent:指向已上传到云端的特定文件 ID;
    • HostedVectorStoreContent:指向云端已经建好的向量存储的ID;
    • DataContent:直接随请求上传的原始二进制数据;
  • MaximumResultCount:一个可选的整数,用来指定检索结果的最大数量。

如下程序演示了一个典型的RAG应用场景。如代码片段所示,我们根据指定的Microsoft Foundry项目创建了一个AIProjectClient实例,并利用它提供的OpenAIFileClient上传了一则描述吴宜泽2026斯诺克世界赛夺冠的新闻稿。然后我们利用这个文件创建了一个向量存储,并根据向量存储的ID创建了HostedVectorStoreContent对象。我们利用这个HostedVectorStoreContent对象创建了一个HostedFileSearchTool对象,并将它注册到根据AIProjectClient对象构建的Agent中。

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Extensions.AI;
using OpenAI.Files;
using OpenAI.VectorStores;
using System.Text;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;


var aiProjectClient = new AIProjectClient(new Uri(projectUrl), new AzureCliCredential());
var openAIClient = aiProjectClient.GetProjectOpenAIClient();
var fileClient = openAIClient.GetOpenAIFileClient();

var news = """
北京时间202655日,英国谢菲尔德克鲁斯堡剧院,决胜局最后一颗黑球落袋后,00后中国球员吴宜泽挥拳庆祝。18:17,吴宜泽击败肖恩·墨菲,拿下2026年斯诺克世锦赛冠军。

社交平台上,**吴宜泽夺冠**迅速登上热搜,不少球迷将这场胜利形容为**中国斯诺克新的接力时刻**。这是继2025年赵心童夺冠之后,中国选手再次问鼎这项赛事最高荣誉。这也是在经历2023年前后相关禁赛与争议事件后,中国球员重新回到世界顶级竞争序列的重要节点。

相比十多年前,丁俊晖在斯诺克领域的单点突破,中国近几年开始稳定涌现世界级斯诺克选手。从个体突破到群体崛起,这项运动在中国已经进入新的发展阶段。
""";


var uploadResult = await fileClient.UploadFileAsync(
    BinaryData.FromBytes(Encoding.UTF8.GetBytes(news)),
    "news.md",
    FileUploadPurpose.Assistants);

var storeClient = openAIClient.GetVectorStoreClient();
var storeResult = await storeClient.CreateVectorStoreAsync(options: new VectorStoreCreationOptions()
{
    Name = "document-knowledge-base",
    FileIds = { uploadResult.Value.Id }
});
var vectorStoreId = storeResult.Value.Id;
while (true)
{
    var vectorStore = await storeClient.GetVectorStoreAsync(vectorStoreId);
    if (vectorStore.Value.Status == VectorStoreStatus.Completed)
    {
        break;
    }
    await Task.Delay(1000);
}

var agent = aiProjectClient
    .AsAIAgent(
        model: model,
        instructions: "You are a helpful assistant.",
        tools: [new HostedFileSearchTool(){ Inputs = [new HostedVectorStoreContent(storeResult.Value.Id)] }]
        );

var response = await agent.RunAsync("2026年斯诺克世锦赛冠军是谁?");
Console.WriteLine(response);

最后我们调用Agent的RunAsync方法来执行一个RAG任务,要求它帮我们回答2026年斯诺克世锦赛冠军是谁?。如下是输出结果:

2026年斯诺克世锦赛冠军是中国球员吴宜泽。他在决赛中以18比17击败肖恩·墨菲夺冠。

4. HostedMcpServerTool

HostedMcpServerTool的作用非常明确,就是为LLM的托管方挂载一个MCP服务器。作为一个声明型标记工具,HostedMcpServerTool自己不运行 MCP 协议,而是作为一张委托协议书,把远程MCP的地址告诉LLP托管方,由它在云端服务直接发起HTTP/SSE请求去连接该MCP服务器,彻底解放本地算力与带宽。

public class HostedMcpServerTool : AITool
{
	public override string Name => "mcp";
	public override IReadOnlyDictionary<string, object?> AdditionalProperties { get; }

	public string ServerName { get; }
	public string ServerAddress { get; }
	public string? ServerDescription { get; set; }
	public IList<string>? AllowedTools { get; set; }
	public HostedMcpServerToolApprovalMode? ApprovalMode { get; set; }
	public IDictionary<string, string>? Headers { get; set; }

	public HostedMcpServerTool(string serverName, string serverAddress);
	public HostedMcpServerTool(string serverName, string serverAddress, IReadOnlyDictionary<string, object?>? additionalProperties);
	public HostedMcpServerTool(string serverName, Uri serverAddress);
	public HostedMcpServerTool(string serverName, Uri serverAddress, IReadOnlyDictionary<string, object?>? additionalProperties);
}

HostedMcpServerTool相关属性说明如下:

  • ServerName:MCP Server名称;
  • ServerAddress:MCP Server地址;
  • ServerDescription:MCP Server描述信息;
  • AllowedTools:允许使用的工具列表。一个远程MCP Server可能自带了非常多工具。为了防止滥用,你可以通过此属性精确白名单指定只允许调用其中的某几个工具。若为null则全量开放;
  • ApprovalMode:针对工具执行决定的审批模式;
  • Headers:请求头信息字典。可以把Authorization: Bearer <Token>或自定义API Key塞进这里。云端LLM服务在替你请求该MCP时,会自动带上这些请求头。

ApprovalMode属性是HostedMcpServerTool的一个重要属性,它决定了托管服务Agent在调用远程MCP Server时,是否需要对工具执行进行审批。ApprovalMode有三种模式:

  • AlwaysRequire:总是需要审批。每次调用远程MCP Server的工具时,都会触发审批流程;
  • NeverRequire:从不需要审批。每次调用远程MCP Server的工具时,都会直接执行,无需审批;
  • RequireSpecific:只对指定的工具需要审批。你可以通过AlwaysRequireApprovalToolNamesNeverRequireApprovalToolNames属性来指定哪些工具需要审批,哪些工具不需要审批。
public class HostedMcpServerToolApprovalMode
{
	public static HostedMcpServerToolAlwaysRequireApprovalMode AlwaysRequire { get; } 
	public static HostedMcpServerToolNeverRequireApprovalMode NeverRequire { get; } 

	public static HostedMcpServerToolRequireSpecificApprovalMode RequireSpecific(
        IList<string>? alwaysRequireApprovalToolNames, 
        IList<string>? neverRequireApprovalToolNames);
}

public HostedMcpServerToolAlwaysRequireApprovalMode(){}
public HostedMcpServerToolNeverRequireApprovalMode(){}

public sealed class HostedMcpServerToolRequireSpecificApprovalMode : HostedMcpServerToolApprovalMode
{
	public IList<string>? AlwaysRequireApprovalToolNames { get; set; }
	public IList<string>? NeverRequireApprovalToolNames { get; set; }

	public HostedMcpServerToolRequireSpecificApprovalMode(
        IList<string>? alwaysRequireApprovalToolNames, 
        IList<string>? neverRequireApprovalToolNames);
}

如果没有对ApprovalMode进行设置,意味着该属性默认值为null,此时采用怎样的审批模式取决于LLM服务商的默认策略。不同的LLM服务商可能会有不同的默认策略,有些可能默认总是需要审批,有些可能默认从不需要审批。在如下的演示程序中,我们利用创建的HostedMcpServerTool挂载了一个指向https://mcp.deepwiki.com/mcp地址的MCP Server。这是由知名AI公司Cognition Labs官方推出的远程、完全免费且免认证的公共MCP服务器。它的核心功能是让AI助手具备秒级阅读、检索和理解任何GitHub公开开源项目文档与架构的能力。

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Extensions.AI;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;

var tool = new HostedMcpServerTool(
            serverName: "deepwiki",
            serverAddress: "https://mcp.deepwiki.com/mcp")
{
    ApprovalMode = HostedMcpServerToolApprovalMode.NeverRequire
};

var agent = new AIProjectClient(new Uri(projectUrl), new AzureCliCredential())
    .AsAIAgent(
        model: model,
        instructions: "You are a helpful assistant.",
        tools: [tool]);

var sesson = await agent.CreateSessionAsync();
var response = await agent.RunAsync("简单介绍一下microsoft/agent-framework这个github repository。",sesson);
Console.WriteLine(response);

我们调用Agent的RunAsync方法来执行一个任务,要求它帮我们简单介绍一下microsoft/agent-framework这个GitHub仓库。Agent会调用远程MCP Server来获取相关信息,并最终返回如下的输出:

microsoft/agent-framework 是微软开源的一个 AI Agent 开发框架,目标是帮助开发者构建“可编排、多智能体、可部署”的 AI 应用。它同时支持 .NET 和 Python,定位有点类似于一个企业级 Agent Runtime + Workflow 平台。

这个项目的核心特点主要有几个方向:

- Agent 抽象
  提供统一的 Agent 接口,可以接入 OpenAI、Azure OpenAI、Azure AI Foundry 等模型服务。Agent 不只是简单聊天,还能管理上下文、状态、工具调用和多轮会话。

- Workflow / Orchestration(编排)
  这是它比较强调的能力。框架支持:
  - 多 Agent 协作
  - 顺序 / 并行执行
  - Handoff(任务转交)
  - Group Chat
  - Human-in-the-loop(人工审批)
  - Checkpoint(断点恢复)

  本质上是在做 AI 工作流引擎,而不只是一个 prompt 封装库。

- Tool & MCP 支持
  支持 Function Calling、Hosted Tools,以及 MCP(Model Context Protocol)。也就是说 Agent 可以调用外部工具、数据库、API,甚至和其他 MCP Server 集成。

- Memory 与状态管理
  内置 Session、Chat History、Context Provider 等机制,适合做长期对话或企业 Copilot 类应用。

- 企业级能力
  项目里明显带有微软企业产品风格,包括:
  - Observability(监控)
  - Middleware
  - Data Governance
  - Azure Functions / ASP.NET Core 部署
  - 容器化部署

从架构上看,它更像:
“Semantic Kernel + LangGraph + 企业工作流引擎”的结合体。

适合的场景包括:
- 企业内部 Copilot
- 多 Agent 自动化系统
- AI 工作流平台
- AI 客服 / 办公助手
- 需要审批和状态管理的 Agent 系统

如果你之前接触过:
- LangChain
- LangGraph
- AutoGen
- Semantic Kernel

那么 Agent Framework 可以理解为微软正在推动的新一代统一 Agent 基础设施,而且明显更偏“生产级”和“Azure 生态”。

仓库地址:
https://github.com/microsoft/agent-framework

如果你愿意,我还可以进一步帮你介绍:
- 它和 AutoGen / Semantic Kernel 的关系
- 整体架构设计
- 一个简单 demo 的运行流程
- 它相比 LangGraph 的差异
- Python 与 .NET 两套实现的区别

5. HostedToolSearchTool

HostedToolSearchTool是发给LLM的工具检索卡。当你的应用拥有成百上千个工具时,它告诉LLM不要一口气吞下所有工具的定义,而是让它在需要时按需搜索并加载特定工具。在复杂的Agent系统或大型企业应用中,我们可能会向大模型提供几十甚至几百个自定义工具。如果每次对话都把这几百个工具的JSON Schema(名称、描述、参数列表)塞进提示词里,会导致:

  • Token成本飙升:即使用户只是说一句你好,也会白白消耗几万个输入Tokens;
  • 大模型注意力分散:工具描述过多会导致大模型产生长上下文迷失,甚至误调用不相关的工具。

通过加入HostedToolSearchTool,不会直接发送完整的工具定义给LLM,而是只向AI提供一个概览或告诉它你可以去搜索工具。当大模型发现解决问题需要某个特定能力时,它会调用工具函数tool_search检索到具体工具定义后,再动态加载并调用它。HostedToolSearchTool的定义如下,其携带的有效信息基本上只有工具名称(tool_search),以及可选的附加属性。

public class HostedToolSearchTool : AITool
{
	public override string Name => "tool_search";
	public override IReadOnlyDictionary<string, object?> AdditionalProperties { get; }

	public IList<string>? DeferredTools { get; set; }
	public string? Namespace { get; set; }
	public string? NamespaceDescription { get; set; }

	public HostedToolSearchTool();
	public HostedToolSearchTool(IReadOnlyDictionary<string, object?>? additionalProperties);
}

三个额外的属性说明如下:

  • DeferredTools:一个字符串列表,指定哪些工具是延迟加载的。也就是说,这些工具不会在初始对话中直接发送给大模型,而是需要在运行时按需检索和加载;
  • Namespace:一个可选的字符串,用于指定工具的命名空间;
  • NamespaceDescription:一个可选的字符串,用于描述命名空间的用途或功能。

通过下面演示程序创建的Agent注册了两个工具:工具函数GetWeather用于返回指定城市的天气信息,而HostedToolSearchTool则用于按需检索工具。Agent在处理用户请求时,如果需要获取天气信息,它会调用HostedToolSearchTool来检索GetWeather工具,并使用它来获取所需的天气数据。

using Azure.AI.Projects;
using Azure.Identity;
using DotNetEnv;
using Microsoft.Extensions.AI;
using OpenAI.Chat;
using OpenAI.Responses;
using System.ClientModel.Primitives;
using System.ComponentModel;
using System.Text.Encodings.Web;
using System.Text.Json;

Env.Load();
var projectUrl = Environment.GetEnvironmentVariable("PROJECT_URL")!;
var apiKey = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
var model = Environment.GetEnvironmentVariable("MODEL")!;

var GetWeather = AIFunctionFactory.Create(
    method: ([Description("The city")] string city) => $"晴,气温25摄氏度。",
    name: "GetWeather",
    description: "获取所在城市天气信息"
    );

var agent = new AIProjectClient(new Uri(projectUrl), new AzureCliCredential())  
    .AsAIAgent(
        model: "gpt-5.4-mini",
        instructions: "你是一个天气信息的只能助手。",
        tools: [
            new HostedToolSearchTool{
                Namespace = "weather",
                NamespaceDescription = "提供获取工具获取天气相关信息",
                DeferredTools = [GetWeather.Name]
            },
            GetWeather
            ]);

var response = await agent.RunAsync("根据目前北京天气提供一些着装建议");
var rawContents = response.Messages
    .SelectMany(m => m.Contents)
    .Where(c => c.RawRepresentation is not null)
    .Select(c => ModelReaderWriter.Write(c.RawRepresentation!, ModelReaderWriterOptions.Json).ToString())
    .ToList();

var options = new JsonSerializerOptions
{
    WriteIndented = true,
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
var index  = 1;
foreach (var rawJson in rawContents)
{
    Console.WriteLine($"{new string('-',20)}Content {index++}{new string('-', 20)}");
    using JsonDocument document = JsonDocument.Parse(rawJson);
    string formattedJson = JsonSerializer.Serialize(document, options);
    Console.WriteLine(formattedJson);
}

通过输入任务根据目前北京天气提供一些着装建议调用Agent,并得到对应的响应结果后,我们将所有响应消息的原始内容(RawRepresentation)提取出来,并将其格式化为易读的JSON格式进行输出:

--------------------Content 1--------------------
{
  "type": "tool_search_call",
  "id": "tsc_0c88c1d3088ccf9d006a524c4c35c881909cde7dc284ac7c3f",
  "response_id": "resp_0c88c1d3088ccf9d006a524c4be03481908a9102e6419958a3",
  "status": "completed",
  "arguments": {
    "paths": [
      "weather"
    ]
  },
  "call_id": null,
  "execution": "server"
}
--------------------Content 2--------------------
{
  "type": "tool_search_output",
  "id": "tso_0c88c1d3088ccf9d006a524c4c45c88190aab3ebb382f74ad8",
  "response_id": "resp_0c88c1d3088ccf9d006a524c4be03481908a9102e6419958a3",
  "status": "completed",
  "call_id": null,
  "execution": "server",
  "tools": [
    {
      "type": "namespace",
      "description": "提供获取工具获取天气相关信息",
      "name": "weather",
      "tools": [
        {
          "type": "function",
          "defer_loading": true,
          "description": "获取所在城市天气信息",
          "name": "GetWeather",
          "parameters": {
            "type": "object",
            "required": [
              "city"
            ],
            "properties": {
              "city": {
                "description": "The city",
                "type": "string"
              }
            },
            "additionalProperties": false
          },
          "strict": false
        }
      ]
    }
  ]
}
--------------------Content 3--------------------
{
  "type": "function_call",
  "id": "fc_0c88c1d3088ccf9d006a524c4c80188190b54f2fbfe21af954",
  "status": "completed",
  "call_id": "call_dHaO8YOHTrlDCoqogex7gPjB",
  "name": "GetWeather",
  "arguments": "{\"city\":\"北京\"}",
  "response_id": "resp_0c88c1d3088ccf9d006a524c4be03481908a9102e6419958a3",
  "namespace": "weather"
}
--------------------Content 4--------------------
{
  "type": "output_text",
  "text": "北京现在天气是晴,气温约 25℃。  \n着装建议:\n\n- 上身:短袖 T 恤、薄衬衫都合适\n- 下身:长裤或轻薄休闲裤即可\n- 外搭:如果早晚要出门,可以带一件薄外套\n- 鞋子:透气运动鞋、休闲鞋比较舒服\n- 其他:阳光较强的话,建议带墨镜和防晒用品\n\n如果你要出门时间较长,我也可以继续帮你按“上班/通勤/运动/约会”场景细化搭配。",
  "annotations": [],
  "logprobs": []
}

从上面的输出接口可以看出任务处理的整个流程:

  • 由于Agent注册了HostedToolSearchTool,并且将GetWeather工具标记为延迟加载(DeferredTools),所以最终调用LLM的提示词中并没有直接包含GetWeather工具的定义。所以LLM会调用工具tool_search来检索工具定义,而作为检索的查询文本来源于我们在HostedToolSearchTool中指定的Namespace属性("paths": ["weather"]);
  • 检索的工具列表会作为tool_search执行结果的一部分,其中就包括我们注册在Agent中的GetWeather工具;
  • LLM在获取到GetWeather工具的定义后,会根据用户的请求(北京天气)调用GetWeather工具,并将参数传递给它;
  • 最终,GetWeather工具返回了北京的天气信息,LLM根据返回的天气信息生成了着装建议,并将其作为最终的输出文本返回给用户。
Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐