一些开发流程中的基本概念 https://learn.microsoft.com/zh-cn/agent-framework/journey/
特点
相比于直接操作底层的 IChatClient 接口,使用 MAF (AIAgent)开发能带来以下三个维度的工程化飞跃:
1. 从“单次对话”到“全能实体” (Skills & Memory)
- 核心优势:你可以为 Agent 装配 AITools(技能) 和 AIContextProviders(记忆与知识库)。
- 复用价值:它不仅仅是连通 AI,而是将向量查询、本地 Skill 和业务背景打包成一个“完全体”。创建一次后,即可在应用的各个模块中多次复用,无需重复配置复杂的调用参数。
2. 从“逻辑硬编码”到“流程拓扑” (Workflows)
- 核心优势:支持 Workflows(工作流) 编排。
- 解耦价值:你可以将多个 AI 代理与本地 C# 执行器(Executors)串联成有向无环图(DAG)。原本需要手写大量
if/else、switch或循环来处理的业务流转,现在通过声明式的拓扑结构即可完成,代码逻辑清晰、易于扩展。
3. 从“手动存取”到“原生持久化” (State & Session)
- 核心优势:内置原生的 Session(会话) 状态管理。
- 运维价值:对话的上下文不再是漂浮在内存里的临时数据。框架支持状态的一键序列化与载入,让 AI 应用具备了“随时存档、随时读档”的能力,彻底解决了长对话维护和跨进程状态恢复的难题。
创建 AIAgent
// 创建 iChatClient
var chatClient = this.CreateChatClient();
var agentOptions = new ChatClientAgentOptions {
Name = "AI Agent",
ChatOptions = new ChatOptions {
Instructions = "你是一位百科小助手,可能解答一些关于计算机、互联网、科学、技术、历史、哲学、艺术、音乐、电影、游戏等领域的问题。当有数据源可用时,优先查询数据源并返回结果。",
Tools = tools, // 装配 tool
},
AIContextProviders = contextProviders, // 配置 context provider, 可以是 rag 或是 memory 或是 skills 等
};
return chatClient.AsAIAgent(agentOptions);
对话
var logger = this.CreateLogger();
var aiAgent = this.CreateAIAgent();
var response = await aiAgent.RunAsync("你好");
logger.LogInformation(response.Text);
流式对话
var logger = this.CreateLogger();
var aiAgent = this.CreateAIAgent();
var enumerable = aiAgent.RunStreamingAsync("什么是 microsoft agent framework");
await foreach(var item in enumerable) {
logger.LogInformation(item.Text);
}
图像处理
var logger = this.CreateLogger();
var aiAgent = this.CreateAIAgent();
const string imagePath = "/Users/iox/Desktop/截图/图片例子.jpg";
var imageData = await AiImageCompressor.TryResizeImageForAiAsync(imagePath);
var messages = new ChatMessage[] {
ChatMessage.CreateUserMessage(
ChatMessageContentPart.CreateTextPart("这张图片中有什么"),
ChatMessageContentPart.CreateImagePart(new BinaryData(imageData), "image/jpeg")
),
};
var response = await aiAgent.RunAsync(messages);
var chatResponse = response.AsChatResponse();
logger.LogInformation(chatResponse.Text);
持续对话
var logger = this.CreateLogger();
var aiAgent = this.CreateAIAgent();
var session = await aiAgent.CreateSessionAsync();
// 进行一次计算
await aiAgent.RunAsync("791 + 197", session);
// 基于 session 进行下一次问答
var response = await aiAgent.RunAsync("前面的计算结果是多少", session);
logger.LogInformation(response.Text);
// 序列化 session
var jsonElement = await aiAgent.SerializeSessionAsync(session);
// 反序列化 session, 并载入
var otherSession = await aiAgent.DeserializeSessionAsync(jsonElement);
// 进行一次计算
response = await aiAgent.RunAsync("是后一次的计算结果(有可能只计算了一次,直接输出这唯一一次的结果)", otherSession);
logger.LogInformation(response.Text);
调用工具
var logger = this.CreateLogger();
AITool[] tools = [AIFunctionFactory.Create(this.GetWeatherTool), ];
var aiAgent = this.CreateAIAgent(tools);
// var aiAgent = client.GetChatClient(model).AsAIAgent(
// tools: [AIFunctionFactory.Create(this.GetWeatherTool),] // 这里添加了工具
// );
// 如果使用 mcp 使用 mcpClient.ListToolsAsync 输出 tools,, 可见 https://blog.xsoft.ltd/2025/10/07/net-mcp-sampling-mcp-server-%e5%9b%9e%e8%b0%83%e5%ae%a2%e6%88%b7%e7%ab%af-llm/
// 这里模拟返回结构化数据
var response = await aiAgent.RunAsync < WeatherData > ("杭州的天气?");
logger.LogInformation(response.Result.ToDebugJson());
// 返回天气的数据结构
private record WeatherData(string City, string Weather);
// 模拟查询天气 tool
[Description("查询城市天气")]
private object GetWeatherTool([Description("城市名称")] string city) {
return new {
city,
weather = "晴天",
};
}
调用 RAG
// 这里有一个使用 sqlite 向量数据库的例子 https://www.bilibili.com/video/BV1A6dDBWETK
// 看起来还没有全部迁移到 agent 上, 旧的手册https://learn.microsoft.com/zh-cn/semantic-kernel/concepts/vector-store-connectors/data-architecture?pivots=programming-language-csharp
// 向量数据库连接器列表 https://learn.microsoft.com/zh-cn/semantic-kernel/concepts/vector-store-connectors/out-of-the-box-connectors/?pivots=programming-language-csharp
// 下面例子中实现一个 SearchAdapter 提示 ai 在搜索数据时,可以调用它
var logger = this.CreateLogger();
// 这里实现了一个SearchAdapter, 内部可以上网查或是数据库查, 或是使用三方向量数据库连接器,进行向量查询
// 提示词中要写明 '当有数据源可用时,优先查询数据源并返回结果' 之类的提示。
var aiAgent = this.CreateAIAgent(contextProviders: [new TextSearchProvider(this.SearchAdapter, new TextSearchProviderOptions()), ]);
var response = await aiAgent.RunAsync("什么是 microsoft agent framework");
logger.LogInformation(response.Text);
private Task < IEnumerable < TextSearchProvider.TextSearchResult >> SearchAdapter(string query, CancellationToken cancellationToken) {
var textSearchResult = new TextSearchProvider.TextSearchResult {
Text = $ "这是关于 {query} 的搜索结果...",
};
return Task.FromResult < IEnumerable < TextSearchProvider.TextSearchResult >> ([textSearchResult, ]);
}
工作流
1. 工作流先创建一些执行器,比如使用 BindAsExecutor 扩展函数, 或是实现 Executor, 或是直接使用 AIAgent(但是我测试不成功,可能与调用的 llm 有关系)
2. 创建工作流创建器, 并调用 edge 将各个执行器创建连接
3. 创建工作流
4. 执行工作流
5. 监听工作流中的事件并进行相应的处理
var logger = this.CreateLogger();
// 1: 直接将函数绑定为执行器, 使用 BindAsExecutor 扩展函数
Func < string, string > uppercaseFunc = s => s.ToUpperInvariant();
var uppercase = uppercaseFunc.BindAsExecutor("UppercaseExecutor");
// 2: 使用 Executor 类创建一个执行器
var reverse = new ReverseTextExecutor();
// 3: 创建一个 AIAgent, 示例是可以以 aiagent 作为执行器,但是我测试无法正常工作,这里包装到一个
// 手册说默认包装为AIAgentHostExecutor,这里有更多说明 https://learn.microsoft.com/zh-cn/agent-framework/workflows/advanced/agent-executor
var chatClient = this.CreateChatClient();
var aiAgent = chatClient.AsAIAgent("你是字符串反向处理专家,请将传入的反向字符串,转为正向字符串返回, 比如 'abc' 直接返回 'abc' => 'cba'", "MyAIAgent");
var aiAgentExecutor = new AiAgentExecutor(aiAgent);
// 创建一个工作流创建器,并使用一个直接的连接器 (edge) 将多个执行器进行串联
var workflowBuilder = new WorkflowBuilder(uppercase);
workflowBuilder.AddEdge(uppercase, reverse);
workflowBuilder.AddEdge(reverse, aiAgentExecutor);
// 创建工作流并执行
var workflow = workflowBuilder.Build();
// 流式执行
await using
var run = await InProcessExecution.RunStreamingAsync(workflow, "Hello World! 呵呵");
// 这里是非流式执行
// await using var run = await InProcessExecution.RunAsync(workflow, "Hello World!");
// 监听事件
await foreach(var @event in run.WatchStreamAsync()) {
if (@event is ExecutorCompletedEvent completedEvent) {
logger.LogInformation($ "Executor {completedEvent.ExecutorId} completed with result: {completedEvent.Data} ");
}
}
// 一个执行器的实现(内部调用 aiagent )
private class AiAgentExecutor(AIAgent agent): Executor < string, string > ("AiStep") {
public override async ValueTask < string > HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken =
default) {
// 显式等待异步结果
var response = await agent.RunAsync(message, cancellationToken: cancellationToken);
return response.Text ?? "";
}
}
// 一个执行器的实现
private class ReverseTextExecutor(): Executor < string, string > ("ReverseTextExecutor") {
public override ValueTask < string > HandleAsync(string message, IWorkflowContext context, CancellationToken cancellationToken =
default) {
return ValueTask.FromResult(string.Concat(message.Reverse()));
}
}
工作流转 AIAgent
// https://learn.microsoft.com/zh-cn/agent-framework/workflows/as-agents?pivots=programming-language-csharp
// 自己创建的测试用例测试不通过,以后有空时研究
// 调用 workflow.AsAIAgent 将工作流转为 AIAgent 进行执行