news 2026/6/28 2:13:19

利用FunctionInvokingChatClient实现ReAct循环

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
利用FunctionInvokingChatClient实现ReAct循环

(Reasoning and Acting,推理与行动)是一种结合了推理工具使用的LLM工作流模式。 它通过交替进行推理(Reasoning)行动(Acting),让AI能够像人类一样,一边分析问题一边寻找外部信息,从而解决复杂的、需要实时数据的任务。ReAct 的核心循环机制ReAct循环通常由三个核心步骤组成,不断重复直到得出最终答案:

  • Thought(思考):模型分析当前状态,决定下一步该做什么;
  • Action(行动):模型选择并调用外部工具(如搜索引擎、数据库、计算器);
  • Observation(观察):模型读取工具返回的结果,并将其作为新的上下文;

比如我最常用的“根据某地天气提供着装建议”的场景,ReAct循环的执行流程如下。这是一个简单的只涉及单次迭代的ReAct循环,实际的ReAct循环可能会涉及多次迭代,模型在每次迭代中都会根据新的上下文来分析下一步该做什么。

  • Thought:模型分析当前状态,发现缺少天气信息,决定需要调用工具来获取天气信息;
  • Action:模型调用工具(如天气API)来获取天气信息;
  • Observation:模型读取工具返回的天气信息,并将其作为新的上下文来分析天气情况,最终得出着装建议;

下面这个演示程序直接利用FunctionInvokingChatClient将上述的ReAct循环落地。如代码所示,我们创建了一个基于OpenAIClientIChatClient对象,并在调用AsBuilder扩展方法将ChatClientBuilder构建出来后,通过调用UseFunctionInvocation方法来注册FunctionInvokingChatClient中间件。由于我们在调用GetResponseAsync方法的时候传入了一个工具函数,所以在执行过程中会触发ReAct循环,模型会先分析当前状态,发现缺少天气信息,然后调用工具函数来获取天气信息,最后根据获取到的天气信息来分析天气情况并得出着装建议。

using Azure; using dotenv.net; using Microsoft.Extensions.AI; using OpenAI; using System.ComponentModel; DotEnv.Load(); var model = Environment.GetEnvironmentVariable("MODEL")!; var apiKey = Environment.GetEnvironmentVariable("API_KEY")!; var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!; var tool = AIFunctionFactory.Create(method: GetWeather); var client = new OpenAIClient( credential: new AzureKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) }) .GetChatClient(model:model) .AsIChatClient() .AsBuilder() .UseFunctionInvocation() .Build(); var response = await client.GetResponseAsync( messages: [new ChatMessage(ChatRole.User,content: "根据苏州当前天气情况,给出一些穿衣建议")], options: new ChatOptions { Tools = [tool] }); Console.WriteLine(response.Text); [Description("获取指定城市的天气信息")] static string GetWeather(string city)=> $"{city}今天的天气是晴天,气温是25°C。";

输出:

苏州今天**晴天,气温25°C**,体感整体比较舒适,稍微偏暖一些。给你一些穿衣建议: ### 👕 上衣 - 短袖T恤、薄衬衫都很合适 - 如果长时间在空调房,可以带一件**薄外套或防晒衫** ### 👖 下装 - 牛仔裤、休闲裤、薄款长裤 - 女生也可以选择半身裙、连衣裙 ### 👟 鞋子 - 运动鞋、休闲鞋、帆布鞋 - 不建议穿太厚重或闷脚的鞋子 ### ☀️ 其他建议 - 晴天紫外线较强,外出可以做好**防晒(帽子、墨镜、防晒霜)** - 白天气温较暖,但早晚可能稍微凉一点,怕冷的话可带薄外套 如果你是要通勤、旅游或者运动,我也可以帮你细化搭配 😊

2. 利用FunctionInvokingChatClient实现人机交互的审批流程

在某些场景下,工具函数可能会涉及一些敏感操作,比如访问用户的个人信息、执行一些可能产生副作用的操作等。对于这些敏感的工具函数,我们可能需要引入一个人机交互的审批流程,在模型调用工具函数之前先征求用户的同意。在如下的演示程序中,我们创建了一个工具函数Transfer,它模拟了一个银行转账的操作。由于这个操作比较敏感,所以我们在调用UseFunctionInvocation方法注册FunctionInvokingChatClient中间件的时候,并没有直接将这个工具函数传入,而是通过一个包装类ApprovalRequiredAIFunction来包装这个工具函数。ApprovalRequiredAIFunction会在模型调用工具函数之前先生成一个审批请求,并将其作为响应的一部分返回给用户。用户可以根据这个审批请求来决定是否批准执行这个工具函数。如果用户批准了,那么模型就会继续执行这个工具函数;如果用户拒绝了,那么模型就会放弃执行这个工具函数。

using Azure; using dotenv.net; using Microsoft.Extensions.AI; using OpenAI; using System.ComponentModel; DotEnv.Load(); var model = Environment.GetEnvironmentVariable("MODEL")!; var apiKey = Environment.GetEnvironmentVariable("API_KEY")!; var endpoint = Environment.GetEnvironmentVariable("OPENAI_URL")!; AIFunction transfer = AIFunctionFactory.Create(method: Transfer, "Transfer"); AIFunction logTool = AIFunctionFactory.Create(method: Log, "Log"); transfer = new ApprovalRequiredAIFunction(transfer); AITool[] tools = [transfer, logTool]; var client = new OpenAIClient( credential: new AzureKeyCredential(apiKey), options: new OpenAIClientOptions { Endpoint = new Uri(endpoint) }) .GetChatClient(model:model) .AsIChatClient() .AsBuilder() .UseFunctionInvocation() .Build(); var prompt = new ChatMessage( role: ChatRole.User, content: "从账号`4242 4242 4242 4242` 转账100块到账号 `5555 5555 5555 4444`"); var options = new ChatOptions { Tools = tools }; var response = await client.GetResponseAsync( messages: [prompt], options: options); while (response is not null) { var lastMessage = response.Messages.Last(); var approvalRequestContents = lastMessage.Contents.OfType<ToolApprovalRequestContent>(); if (!approvalRequestContents.Any()) { Console.WriteLine(lastMessage.Text); break; } Console.WriteLine("如下待执行工具需要你的审批"); foreach (var content in approvalRequestContents) { var toolCall = (FunctionCallContent)content.ToolCall; Console.WriteLine($"工具 `{toolCall.Name}` 正在请求执行,参数如下:"); foreach (var (k, v) in toolCall.Arguments!) { Console.WriteLine($" - {k}: {v}"); } Console.WriteLine(); } Console.Write("是否批准执行 [Y/N]: "); var input = Console.ReadLine(); bool isApproved = input?.Trim().ToUpper() == "Y"; var approvalResponses = approvalRequestContents.Select(it=>it.CreateResponse(isApproved)).ToArray(); var messages = response.Messages.ToList(); messages.Add(new ChatMessage(ChatRole.User, approvalResponses)); response = await client.GetResponseAsync(messages, options); } [Description("执行银行转账操作")] static string Transfer( [Description("转出银行账号")] string from, [Description("转入银行账号")] string to, [Description("转账金额")] decimal amount) => $"从账号 {from} 转账 {amount} 元到账号 {to} 已完成。"; [Description("跟踪记录执行银行转账操作")] static void Log(string message) => Console.WriteLine(message);

如下的两端输出分别对应批准执行和拒绝执行的情况:

如下待执行工具需要你的审批 工具 `Transfer` 正在请求执行,参数如下: - from: 4242 4242 4242 4242 - to: 5555 5555 5555 4444 - amount: 100 工具 `Log` 正在请求执行,参数如下: - message: 从账号 4242 4242 4242 4242 转账 100 元到账号 5555 5555 5555 4444 是否批准执行 [Y/N]: Y 从账号 4242 4242 4242 4242 转账 100 元到账号 5555 5555 5555 4444 ✅ 转账已完成! - **转出账户**:4242 4242 4242 4242 - **转入账户**:5555 5555 5555 4444 - **转账金额**:100 元 📄 交易记录已成功保存。如需继续操作,请告诉我。
如下待执行工具需要你的审批 工具 `Transfer` 正在请求执行,参数如下: - from: 4242 4242 4242 4242 - to: 5555 5555 5555 4444 - amount: 100 工具 `Log` 正在请求执行,参数如下: - message: 从账号 4242 4242 4242 4242 转账 100 元到账号 5555 5555 5555 4444 是否批准执行 [Y/N]: N ❌ 转账失败。 原因:无法确定目标账户的有效性,因此银行转账操作被拒绝执行。 请核对以下信息后重新提交: - 转出账户是否正确 - 转入账户是否正确 - 账户是否为有效银行账号格式 - 是否需要提供更多身份验证信息 如需重新发起转账,请提供正确的账户信息。

从这里例子可以看出,FunctionInvokingChatClient会将LLM返回的所有工具调用视为一个类似于事务的整体,如果所有工具都不需要审批,那么它会采用直接调用这些工具。如果其中有任何一个工具需要审批,它会任务所有工具调用都需要审批。这也很好理解,因为所有的工具都是为了同一个任务服务的,如果其中一个工具需要审批,那么整个任务就需要审批。以本例来说,虽然

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/28 2:13:07

第一个:算力——让AI有了有力的工具。

同样烧开一壶水&#xff0c;用电磁炉5分钟搞定&#xff0c;用蜡烛可能得烧到明天。AI的算力&#xff0c;就是电磁炉和蜡烛的差别。电脑芯片&#xff0c;从几十纳米&#xff0c;到如今的几纳米&#xff0c;能力越来越强。强大的算力&#xff0c;让AI的训练&#xff0c;从几年变成…

作者头像 李华
网站建设 2026/6/28 2:11:34

MrRSS | 基于 AI 的 RSS 阅读器

链接&#xff1a;https://pan.quark.cn/s/8381b883481d基于 AI 的 RSS 阅读器&#xff0c;集成先进 AI 技术&#xff0c;赋能翻译、摘要、推荐等多种功能&#xff0c;支持 URL、XPath、脚本、Newsletter 等多种订阅源类型&#xff0c;支持 Obsidian、FreshRSS、RSSHub 等主流工…

作者头像 李华
网站建设 2026/6/28 2:07:39

P1423 小玉在游泳 题解

题目 小玉在游泳 —————————————————————————————— 题目描述 小玉开心的在游泳&#xff0c;可是她很快难过的发现&#xff0c;自己的力气不够&#xff0c;游泳好累哦。已知小玉第一步能游 2 米&#xff0c;可是随着越来越累&#xff0c;力气越来越…

作者头像 李华
网站建设 2026/6/28 2:07:25

HTTP协作的Web服务器

这篇文章解决什么问题我们平时访问一个网站时&#xff0c;直觉上会以为浏览器直接连到了目标服务器。实际情况往往更复杂&#xff1a;请求可能先经过代理服务器&#xff0c;也可能经过网关、隧道、缓存服务器&#xff0c;最后才到达真正保存资源的服务器。这一章主要讨论的就是…

作者头像 李华
网站建设 2026/6/28 2:00:18

程序员本地开发手机调试连接失败问题

在Win10电脑 开发程序&#xff0c;使用Nginx 代理80端口对外开放。让同局域网&#xff0c;手机、电脑 等设备能访问到开发的程序。前端Vue后端SpringBoot 在Nginx 配置的 开发电脑ipv4地址出现的问题 开发电脑能通过ipv4访问到项目&#xff1b;同局域网手机电脑访问不到&#x…

作者头像 李华
网站建设 2026/6/28 1:59:54

时空协同智能感知 零断点跨镜追踪实现全场景安全智控技术方案

方案出品单位&#xff1a;镜像视界浙江科技有限公司适用场景&#xff1a;智慧军营、智能矿山、港口口岸、司法监所、产业园区、危化能源、交通枢纽、城市综治一、方案概述当前国内全域安防与空间管控领域&#xff0c;长期存在视域孤岛割裂、跨镜追踪断链、盲区管控空白、有源定…

作者头像 李华