.NET+AI | Agent | MAF 的「手动驾驶」与「自动驾驶」:深入理解 UseProvidedChatClientAsIs
目录一、先从这行代码开始二、「自动驾驶」模式WithDefaultAgentMiddleware 做了什么第一件事注入 FunctionInvokingChatClient第二件事绑定工具Tools三、「自动驾驶」的心脏FunctionInvokingChatClient 的 Loop3.1 启动前处理审批响应预处理3.2 主循环一轮一轮地推进① 快速路径Fast Path② 历史消息管理History Fixup③ 连续错误计数consecutiveErrorCount④ 迭代次数上限MaximumIterationsPerRequest⑤ 审批流转Approval Flow四、「手动驾驶」模式你全权掌控这意味着什么典型的使用场景五、两种模式的对比六、一个真实的场景七、为什么这个设计是合理的八、写在最后想象你刚拿到一辆新车。如果你是老司机技术过硬你会说把方向盘给我我自己来。 你清楚每一个弯道、每一脚油门该怎么踩。但如果你想享受旅程、专注于目的地你会说开启自动驾驶让系统来处理路途中的一切。 系统会自动识别障碍、自动调整车速、自动绕行——你只需要告诉它去哪里。Microsoft Agent FrameworkMAF里有一个不起眼却极其关键的配置项 ChatClientAgentOptions.UseProvidedChatClientAsIs 。 这个布尔值就是那把切换「手动驾驶」与「自动驾驶」的开关。理解它是真正用好 MAF 的第一步。一、先从这行代码开始逻辑清晰只有两条路true手动驾驶直接使用你传进来的IChatClient框架不加任何东西。false或未设置自动驾驶调用WithDefaultAgentMiddleware在你的 client 外面包裹一层或多层中间件组成完整的 Agentic 执行流水线。看起来很简单但这背后的差异是整个 Agent 执行模型的分水岭。二、「自动驾驶」模式WithDefaultAgentMiddleware 做了什么当UseProvidedChatClientAsIs为false时框架会调用WithDefaultAgentMiddleware方法对你的 ChatClient 进行改造这里发生了两件重要的事情第一件事注入FunctionInvokingChatClientFunctionInvokingChatClient是Microsoft.Extensions.AI提供的一个DelegatingChatClient它的职责是自动处理 Function Calling 的完整循环Agentic Loop。构建出来的 pipeline 大致是这样的由于ChatClientBuilder.Build是按注册顺序逆序应用工厂方法的所以第一个Use注册的是最外层。结果就是FunctionInvokingChatClient站在最外面像一个调度员控制整个 loop。第二件事绑定工具Tools如果你在构造 Agent 时通过ChatOptions.Tools提供了一批工具AIFunction这些工具会被绑定为FunctionInvokingChatClient.AdditionalTools在整个 Agent 生命周期内持续可用。三、「自动驾驶」的心脏FunctionInvokingChatClient 的 Loop让我们把镜头推进到最核心的位置——GetResponseAsync里的 Agentic Loop。这段代码文章开头给出的那段展示了一个精心设计的多轮对话执行流程。我们来逐步拆解它3.1 启动前处理审批响应预处理在进入主循环之前框架会先检查传入的消息中是否包含「审批响应」ToolApprovalResponseContent。这是 MAF 支持Human-in-the-Loop的关键机制某些工具被标记为需要人工审批ApprovalRequiredAIFunction在上一轮对话中框架不会直接执行它们而是把它们转换为审批请求返回给调用方。调用方可能是你的 UI 或后端让用户决定批准还是拒绝当用户做出决定把审批结果打包进下一轮消息传回来FunctionInvokingChatClient就在这里处理这些结果——执行被批准的函数或者直接终止流程。3.2 主循环一轮一轮地推进这个循环有几个精妙的设计值得特别说明① 快速路径Fast Path如果第一轮 LLM 就直接给了答案不需要调用任何工具框架会立刻返回不做任何多余的处理。这对于大多数「普通问答」场景非常重要——你不会因为套了一层中间件而付出性能代价。② 历史消息管理History Fixup每一轮迭代框架都会维护一份augmentedHistory把原始消息、LLM 响应、函数调用结果按正确顺序拼接起来传给下一轮。这保证了 LLM 在每一轮都能看到完整的上下文。③ 连续错误计数consecutiveErrorCount框架会追踪连续的函数调用错误次数。如果工具调用持续失败框架可以选择终止循环而不是无限重试避免浪费 token 和产生幻觉。④ 迭代次数上限MaximumIterationsPerRequest防止 Agent 陷入无限循环。当达到上限时框架会移除 ChatOptions 中的工具声明让 LLM 在下一轮只能给出文本回答优雅地结束循环。⑤ 审批流转Approval Flow当某个工具被标记为ApprovalRequiredAIFunction框架不会执行它而是把它包装成ToolApprovalRequestContent返回给调用方。同一轮响应中的其他工具调用也会一并暂停——这是为了安全性一旦有需要审批的操作所有操作都要等人来决定。四、「手动驾驶」模式你全权掌控当UseProvidedChatClientAsIs true时框架什么都不做——你传什么 client 进来就用什么。没有FunctionInvokingChatClient没有自动 loop没有工具绑定没有审批流转。这意味着什么你需要自己处理所有事情如果 LLM 返回了FunctionCallContent你要自己识别、自己调用、自己把结果拼回消息历史、自己决定是否继续下一轮。如果你想要 Human-in-the-Loop你要自己实现审批逻辑。如果你的 client 已经内置了 function calling比如某些 Azure AI Foundry 服务在服务端处理工具调用你就不需要客户端再做一遍——这时候手动驾驶反而是正确选择。典型的使用场景你的 LLM 服务已经在服务端处理 Function Calling比如 Azure AI Foundry 的某些托管 Agent 服务服务端已经执行了工具循环返回的就是最终答案。客户端再套一个FunctionInvokingChatClient反而会造成混乱。你想实现自定义的调度逻辑也许你的 Agentic Loop 需要特殊的错误处理、特定的重试策略或者与外部审批系统深度集成默认的 loop 不能满足你的需求。性能极致敏感的场景你知道自己的 Agent 不会用到工具那么去掉中间层可以减少一点点的调用栈开销虽然通常微不足道。测试与调试你想观察 raw IChatClient 的行为不想让中间件干扰你的观察。五、两种模式的对比维度手动驾驶true自动驾驶false/默认Function Calling Loop❌ 不处理需自行实现✅ 自动循环处理工具绑定❌ 需自行管理✅ 自动绑定 AdditionalToolsHuman-in-the-Loop❌ 需自行实现✅ 内置审批流转机制历史消息管理❌ 需自行维护✅ 自动 augmentedHistoryUsage 统计聚合❌ 仅单次✅ 多轮累加迭代次数限制❌ 无✅ MaximumIterationsPerRequest灵活性⭐⭐⭐⭐⭐ 完全掌控⭐⭐⭐ 框架约束适合场景高级定制、服务端已处理工具调用大多数 Agent 场景六、一个真实的场景假设你在构建一个企业级报销审批 Agent它需要理解员工的报销申请调用财务系统 API 查询余额如果金额超过 5000 元发送审批请求给经理等待经理决定后执行转账或拒绝用自动驾驶模式UseProvidedChatClientAsIs false这个流程几乎开箱即用步骤 2 是一个普通的AIFunctionFunctionInvokingChatClient会自动调用它步骤 3 的发送审批请求工具被标记为ApprovalRequiredAIFunction框架会自动暂停执行并把审批请求返回给你的 UI步骤 4 中经理的决定以ToolApprovalResponseContent的形式传回框架在下一次RunAsync调用时自动处理整个 Agentic Loop 的管理、消息历史的拼接、多轮 Usage 的累计都由框架负责。你只需要关注业务逻辑。如果用手动驾驶模式UseProvidedChatClientAsIs true你要手写 for 循环要手动识别FunctionCallContent要自己设计审批暂停和恢复机制要自己管理augmentedHistory……除非你有非常特殊的理由否则手动驾驶在这里只会让你重新发明一辆方形的轮子。七、为什么这个设计是合理的MAF 这个设计体现了一种叫做「约定优于配置Convention over Configuration」的哲学同时兼顾了逃生舱Escape Hatch。大多数使用场景下开发者希望框架帮他们处理复杂的状态机也就是那个 Agentic Loop。框架的默认行为自动驾驶就是为此优化的。但框架的设计者也清楚没有任何一个框架能预见所有场景。所以他们提供了UseProvidedChatClientAsIs true这个逃生舱让高级用户可以完全绕开框架的默认行为。这种「默认安全、高级可控」的设计思路在FunctionInvokingChatClient的其他地方也有体现AllowConcurrentInvocation默认false保护线程安全需要并发时可以主动开启MaximumIterationsPerRequest防止无限循环的保护机制但可以配置ApprovalRequiredAIFunction默认函数直接执行需要人工介入时才标记八、写在最后一行代码UseProvidedChatClientAsIs切换的是两种完全不同的 Agent 执行哲学自动驾驶把复杂的 Agentic Loop、工具调用管理、审批流转、历史消息维护统统交给框架。你专注于定义工具、编写业务逻辑、设计 Agent 指令。手动驾驶框架退到幕后你拿到一个干净的IChatClient一切尽在掌握代价是你需要自己实现那套复杂的状态机。对于绝大多数构建 AI Agent 的场景自动驾驶是更好的起点。它背后的FunctionInvokingChatClient是一个经过精心设计的生产级组件处理了大量你在手写时很可能遗漏的边界情况错误计数、迭代上限、快速路径优化、消息历史修复、Usage 聚合……当你真正理解了这套机制之后你才能做出明智的选择什么时候应该信任自动驾驶什么时候应该接管方向盘。这才是真正驾驭 MAF 的姿态。引入地址

相关新闻

C#模式匹配:简化条件逻辑的现代编程技巧

C#模式匹配:简化条件逻辑的现代编程技巧

1. 模式匹配:C#条件逻辑的革命性进化在传统C#开发中,我们处理复杂条件逻辑时往往陷入if-else的泥潭。想象一下这样的场景:你需要根据不同类型的输入对象执行不同操作,同时还要处理null检查、类型转换和值范围判断。典型的代码可能…

2026/7/23 23:21:56 阅读更多 →
Golang日志监控实战:从采集到可视化全解析

Golang日志监控实战:从采集到可视化全解析

1. 项目概述:Golang日志监控的核心价值在分布式系统和微服务架构盛行的当下,日志监控已成为保障系统稳定性的生命线。作为运维工程师,我亲历过无数次深夜告警,深刻体会到一套高效的日志监控体系能节省多少故障排查时间。Golang凭借…

2026/7/24 7:44:07 阅读更多 →
成都网站建设木木科技:踩坑无数后,我为什么最终选了这家?

成都网站建设木木科技:踩坑无数后,我为什么最终选了这家?

成都网站建设木木科技:踩坑无数后,我为什么最终选了这家?

2026/7/24 5:39:02 阅读更多 →

最新新闻

114、运动相机影像调优:高动态与电子防抖实战

114、运动相机影像调优:高动态与电子防抖实战

114、运动相机影像调优:高动态与电子防抖实战 去年夏天帮某户外品牌调一款运动相机,在川西海拔4500米的碎石路上跑测试。机器绑在越野自行车把手上,下午三点阳光直射,路面扬尘和阴影交替出现。回放素材时发现两个致命问题:一是过曝的云层和死黑的岩石之间没有任何过渡,二…

2026/7/24 14:56:11 阅读更多 →
服装店复购率做不上去?先避开这三个会员管理误区

服装店复购率做不上去?先避开这三个会员管理误区

相信很多店主跟我有同感:会员人数在涨,复购率却像温水煮青蛙,一年比一年低。直到去年我自己踩过三个大坑,才慢慢想明白问题出在哪里。第一个误区是“会员就是发券”。很多店把会员管理等同于建群、发费用方案券、上新通知。结果呢…

2026/7/24 14:56:11 阅读更多 →
WinUI 3液态玻璃笔刷:LiquidGlassBrush实战指南

WinUI 3液态玻璃笔刷:LiquidGlassBrush实战指南

在 WinUI 3 项目开发中,UI 控件的视觉效果一直是提升用户体验的关键。传统笔刷往往难以实现既流畅又富有质感的界面效果,特别是液态玻璃这类高级视觉效果,很多开发者只能依赖复杂的外部库或自定义绘制。近期在 WinUI 3 社区中,Liq…

2026/7/24 14:56:11 阅读更多 →
2026年服装收银系统选型思考

2026年服装收银系统选型思考

一、行业现状:通用软件为什么不够用2026年的服装零售,已经不是十年前那个“开个店、进点货、等客来”的模式了。我做服装经营顾问这几年,接触过很多店主,大家普遍的痛点是:库存越来越难管,颜色尺码一多就乱…

2026/7/24 14:56:11 阅读更多 →
AI工具提升论文写作效率:文献管理到数据分析全攻略

AI工具提升论文写作效率:文献管理到数据分析全攻略

1. 论文写作效率困境与AI解决方案 每次临近论文截止日期,实验室里总能看到一群蓬头垢面的研究生对着电脑屏幕发呆。我自己读研时就深有体会——文献综述写到凌晨三点,数据分析卡在某个公式推导,英语表达怎么改都觉得生硬。直到去年偶然接触了…

2026/7/24 14:56:11 阅读更多 →
Kimi K2.5模型扩展解析:长上下文与多模态的工程实践

Kimi K2.5模型扩展解析:长上下文与多模态的工程实践

如果你正在关注 AI 大模型的技术演进,特别是那些真正在解决实际工程问题的模型,那么月之暗面(Moonshot AI)的 Kimi 系列绝对值得深入理解。最近,其创始人杨植麟在 GTC 2026 上的分享,详细解析了 Kimi 从 K2…

2026/7/24 14:55:11 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻