惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
小众软件
小众软件
博客园_首页
博客园 - 聂微东
V
V2EX
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
S
SegmentFault 最新的问题
J
Java Code Geeks
Last Week in AI
Last Week in AI
The Cloudflare Blog
月光博客
月光博客
雷峰网
雷峰网
宝玉的分享
宝玉的分享
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Hugging Face - Blog
Hugging Face - Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
有赞技术团队
有赞技术团队
人人都是产品经理
人人都是产品经理
博客园 - Franky
腾讯CDC
Jina AI
Jina AI
博客园 - 叶小钗
大猫的无限游戏
大猫的无限游戏
阮一峰的网络日志
阮一峰的网络日志
量子位
爱范儿
爱范儿
美团技术团队
T
Tailwind CSS Blog
博客园 - 【当耐特】
D
Docker
IT之家
IT之家
V
Visual Studio Blog
P
Proofpoint News Feed
L
LangChain Blog
Engineering at Meta
Engineering at Meta
C
Check Point Blog
G
Google Developers Blog
Google DeepMind News
Google DeepMind News
云风的 BLOG
云风的 BLOG
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog RSS Feed
Recorded Future
Recorded Future

博客园_首页

Plist 二进制格式 Milvus 和 PGVector,哪个更好? OpenClaw 已过时?在 VS Code 中运行 Hermes Agent! 第30篇文章:一个大三计科生的自白 Manim如何在数学公式中完美显示中文? Docker 部署 RocketMQ 5 并发编程核心概念辨析 C#事务处理最佳实践:别再让“主表存了、明细丢了”的破事发生 CLI 是什么?为什么大厂突然集体卷命令行? 【从0到1构建一个ClaudeAgent】协作-自主Agent UIImageView 设置图片不生效的原因排查 最小二乘问题详解20:无先验约束下的增量式SFM自由网平差 痞子衡嵌入式:大话双核i.MXRT1180之XIP应用里借助MU实现可靠Flash IAP的方法 AI Chat 封装, SemanticKerne.AiProvider.Unified 已发布 Windows下右键编辑js文件无法打开记事本——在注册表中使用环境变量 在后台服务中使用 Scoped 服务,为什么总是报错? H200 安装驱动并使用sglang启动模型 wireshark 抓包Trap上报告警内容 我用 AI 辅助开发了一系列小工具(2):图片压缩工具 [A Primer On MC and CC] 2.1 Memory Consistency 1 - 指令重排序和 SC 模型 Oracle数据库SCN推进技术详解与实践指南 玩转控件:封装个带图片的Label控件 Claude Code 4.7 真正该升级的不是模型,而是你的工作流 前端小白一句话,AI 帮我做了个颜值拉满的桌面媒体播放器。当代码不再是门槛,一句话编程就是现实。 5. WorkBuddy: 小龙虾的灵魂三件套,让你的小龙虾不只是工具 SQLite 分片方案实战:三种分片策略的深度对比 告别简陋 UI!一款基于 Fluent Design 和基于 WinUI 的开源免费、现代化的 Avalonia UI 控件库 关于二进制排列组合枚举的总结 AI开发-python-LangGraph框架(3-27-LangGraph从零实现大模型智能决策工作流) ElasticSearch主分片和副本分片概念详解 【002】HTTPS 粗解:证书、TLS 握手与对后端配置的影响 Hermes Agent 一周暴涨五万 Star,但我劝你别急着追 明明连接的是Redis的DB0,为什么能查到DB3的数据? 【从0到1构建一个ClaudeAgent】协作-Agent团队 熟悉电子元器件之后,电子小白下一步该怎么走? MAF快速入门(23)通过C#类定义Skills .NET 高级开发 | 手写一个对象映射框架 FastAPI数据库ORM怎么选?我肝了三个Demo后,终于不再纠结了 mysqldump 参数拾遗:在遗忘与铭记之间 C# .NET 周刊|2026年3月5期 Claude code入门 - 陈彦斌 一文学习入门 ThingsBoard 开源物联网平台 GitHub 热门项目 | 2026年04月16日 如何为GIT设置全局勾子,为每次提交追加信息 Number.isFinite和isFinite与isNaN()和Number.isNaN的区别 PortSwigger SQL注入LAB2 推荐一个测试人必备的Skills,从功能到性能全搞定(附详细实操和安装下载方式) 筑基期:掌握Odoo基础核心知识点02(Odoo XML 开发方式详解) GLM模型这么火,咱们用vllm也咧一个呗! 深入理解 AbortController:从底层原理到跨语言设计哲学 字符串学习笔记 多租户系统框架的基础模块设计和分析设计 Apache SeaTunnel Zeta 为什么能做到“又快又稳”? AI开发-python-LangGraph框架(3-26-LangGraph基本概念及第一个简单样例) Vue 3 组件通信,别只会用 Props 和 Emits 了,这几个狠活儿你得看看 ElasticSearch7.X版本配置密码 用Manim实现动态交点计算--从一个动点问题说起 团结引擎+Addressable+Instant Game打包抖音小游戏 function call 实战:让 LLM 自动判断 pod 异常、调用日志工具并完成故障分析 bubseek —— 让 Agent 的足迹,变成团队的洞察 通过 C# 读取并导出 PDF 书签 如何用 GitHub Actions 实现 Steam 自动化发布 【从0到1构建一个ClaudeAgent】并发-后台任务 .NET 高级开发 | 定制 ASP.NET Core 框架 电子小白:什么是运算放大器(运放) zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线 堆上的ORW HC32F460 USB CDC通信异常:非对齐访问异常排查 20260413-Hyperbridge 攻击事件:发生在默克尔山上的验证绕过 那些喊着AI 要淘汰你的人,正在靠你的焦虑赚大钱! 深度学习进阶(八)Swin Transformer 最小二乘问题详解19:带先验约束的增量式SFM优化与实现 SnapTranslate 3.0 正式发布:全局划词翻译 + 完整英语学习闭环,一站式搞定查词、记词、复习 工作的意义、工作的困难认知再思考 .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 开了 TUN 模式还是直连?90% 的人都踩过这个坑 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术
[MAF的Agent管道详解-03]连接LLM的IChatClient对象
Artech · 2026-05-28 · via 博客园_首页

IChatClient管道的最末端是一个与LLM进行交互的IChatClient对象,这个对象负责将最终的请求发送给LLM并返回响应结果。这个IChatClient对象的具体类型取决于我们使用的是什么模型以及模型的部署方式。系统提供了很多这样的IChatClient实现来支持不同的模型和部署方式。对于目前主流的LLM,我们都可以直接利用其客户端来创建一个对应的IChatClient对象.

1. 为三种OpenAI客户端创建IChatClient对象

OpenAIClientAzureOpenAIClient是一个与OpenAI的API进行交互的客户端,我们可以指定模型名称调用其GetChatClient方法来获取一个对应的ChatClient对象。虽然名字雷同,但是这个ChatClient类型可没有实现IChatClient接口,我们需要调用为它定义的扩展方法AsIChatClient来将它转换成一个实现了IChatClient接口的对象。

public class AzureOpenAIClient
{
    public override ChatClient GetChatClient(string deploymentName);
    public override ResponsesClient GetResponsesClient();
}

public class OpenAIClient
{
    public virtual ChatClient GetChatClient(string model);
    public virtual ResponsesClient GetResponsesClient();
}

public static class OpenAIClientExtensions
{
    public static IChatClient AsIChatClient(this ChatClient chatClient);
    public static IChatClient AsIChatClient(this ResponsesClient responseClient, string? defaultModelId = null);
}

前面说过,GetChatClient返回的ChatClient对象采用基于文本补全的无状态的Completion API来与模型进行交互,如果需要采用有状态的Responses API,需要调用GetResponsesClient方法来获取一个ResponsesClient对象。系统依然为ResponsesClient对象定义了一个AsIChatClient的扩展方法来将它转换成一个实现了IChatClient接口的对象。

如果使用的是基于Microsoft Foundry的AIProjectClient客户端。由于它的基类是ClientConnectionProviderExtensions,我们可以调用其扩展方法GetProjectOpenAIClient得到一个ProjectOpenAIClient对象。由于ProjectOpenAIClient继承自OpenAIClient,我们同样可以调用为它定义的AsIChatClient扩展方法来将它转换成一个实现了IChatClient接口的对象。

public class AIProjectClient : ClientConnectionProvider

public static class ClientConnectionProviderExtensions
{
    public static ProjectOpenAIClient GetProjectOpenAIClient(
        this ClientConnectionProvider connectionProvider, 
        ProjectOpenAIClientOptions options = null);
}

public class ProjectOpenAIClient : OpenAIClient

2. 模拟Agent的ReAct循环

接下来我们看看一个利用OpenAIClient创建的IChatClient对象在调用LLM的时候,提供的请求和响应内容是什么样子的。下面的代码模拟了一个Agent内部的执行流程(ReAct循环),我们使用这个Agent来根据苏州的天气给出一些着装建议。我们根据OpenAIClient创建了对应的IChatClient对象,整个流程涉及两次针对它的调用。两次调用使用同一个ChatOptions对象,我们为这个ChatOptions设置了系统指令(你是一个深谙养身之道的时尚顾问)并注册了一个用于查询天气的工具GetWeather

using dotenv.net;
using Microsoft.Extensions.AI;
using OpenAI;
using System.ClientModel;
using System.ComponentModel;

DotEnv.Load();
var model = Environment.GetEnvironmentVariable("MODEL")!;
var apiKey = Environment.GetEnvironmentVariable("API_KEY")!;
var openAIUrl = Environment.GetEnvironmentVariable("OPENAI_URL")!;
var openAIClient = new OpenAIClient(
    credential: new ApiKeyCredential(key: apiKey),
    options: new OpenAIClientOptions
    {
        Endpoint = new Uri(openAIUrl)
    });

var chatClient = openAIClient.GetResponsesClient().AsIChatClient(defaultModelId:model);
var options = new ChatOptions
{   
    Instructions = "你是一个深谙养身之道的时尚顾问。",
    Tools = [AIFunctionFactory.Create(GetWeather)]
};
var message = new ChatMessage(role: ChatRole.User, content: "根据苏州的天气给我一些着装建议。");
List<ChatMessage> messages = [message];

// First turn: user -> assistant (with function call)
var response = await chatClient.GetResponseAsync(
    messages: messages,
    options: options);
messages.AddRange(response.Messages);

var functionCall = response.Messages.Last().Contents.OfType<FunctionCallContent>().Single();
var tool = options.Tools.Single(t => t.Name == functionCall.Name);
var toolResult = await ((AIFunction)tool).InvokeAsync(new AIFunctionArguments(functionCall.Arguments));
var toolResultMessage = new ChatMessage(ChatRole.Tool, [new FunctionResultContent(functionCall.CallId, toolResult)]);
messages.Add(toolResultMessage);

// Second turn: user -> assistant (with tool result)
response = await chatClient.GetResponseAsync(
    messages: messages,
    options: options);

Console.WriteLine(response.Messages.Last().Text);
static string GetWeather([Description("Location for weather query")] string location) => $"{location} 当前晴朗,气温为25°C。";

我们指定查询(根据苏州的天气给我一些着装建议)和ChatOptions调用IChatClient对象。LLM经过推理任务需要调用工具函数GetWeather来获取苏州的天气信息,所以响应消息的内容列表会包含一个FunctionCallContent。在手工将响应消息添加到消息列表中后,我们利用FunctionCallContent从注册的工具列表中找到对应的工具。

我们将LLM提供的输入参数从FunctionCallContent提取出来后,调用工具函数GetWeather得到对应的结果。接下来我们针对工具的返回结果创建一个角色为ToolChatMessage对象,并将它添加到消息列表中。最后我们再次调用IChatClient对象来获取LLM的最终回复。此时LLM就可以根据工具的返回结果来生成最终如下所示的答案:

好的,我们就顺着苏州此刻**25°C、晴朗**的状态,从**养身 + 时尚**两个角度来搭配。

---

## 🌤️ 今日苏州着装总思路
**关键词:清爽透气、遮阳不闷、早晚微调**

25°C 属于非常舒适的温度,但苏州湿度通常不低,**选对面料比堆叠衣服更重要**。

---

## 👕 上装建议
- **首选**:
  - 棉麻衬衫(浅色系:米白、浅灰、雾蓝)
  - 薄款针织或天丝T恤
- **养身理由**:
  - 棉麻、天丝透气吸湿,减少湿热闷汗,对皮肤和气血运行更友好
- **小技巧**:
  - 避免紧身、化纤材质,容易“闷火生湿”

---

## 👖 下装建议
- **推荐**:
  - 九分直筒裤 / 轻薄阔腿裤
  - 膝下A字裙或真丝半裙
- **颜色**:
  - 浅卡其、灰绿、烟粉色,有“降燥感”
- **养身点**:
  - 不勒腹、不裹腿,有助于脾胃与下肢血液循环

---

## 👟 鞋履选择
- **白色/浅色透气运动鞋**
- **软底乐福鞋 / 平底凉鞋(包后跟更养脚)**
- 避免全天穿完全平底或过硬的鞋,对足底经络不友好

---

## 🧥 随身加一件(很关键)
- **薄开衫 / 防晒衬衫**
  - 室内空调 + 早晚微风时护住肩颈
  - 肩颈保暖 = 少落枕、少疲劳

---

## 🕶️ 配饰与养身小细节
- **帽子或遮阳伞**:防晒就是防“耗气”
- **天然材质包袋**:帆布、草编,更符合当下季节气场
- **配色不宜过于浓烈**:春夏交替,宜“柔不宜躁”

---

如果你愿意告诉我:
- 是**上班 / 休闲 / 约会 / 出游**
- 或偏**中性、优雅、运动风**

我可以直接帮你搭一整套「今天就能穿出门」的苏州限定穿搭 🌿

这是第一轮调用LLM提供的请求和得到的响应内容:

{
  "model": "gpt-5.2-chat",
  "tools": [
    {
      "type": "function",
      "name": "_Main_g_GetWeather_0_1",
      "description": "",
      "parameters": {
        "type": "object",
        "required": [
          "location"
        ],
        "properties": {
          "location": {
            "description": "Location for weather query",
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "strict": null
    }
  ],
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "根据苏州的天气给我一些着装建议。"
        }
      ]
    }
  ],
  "instructions": "你是一个深谙养身之道的时尚顾问。"
}
{
  "id": "resp_08fd9fcf3071918b006a000a00f53081938f105b04d924cb63",
  "object": "response",
  "created_at": 1778387456,
  "status": "completed",
  "background": false,
  "completed_at": 1778387457,
  "content_filters": [
    {
      "blocked": false,
      "source_type": "prompt",
      "content_filter_raw": [],
      "content_filter_results": {
        "hate": {
          "filtered": false,
          "severity": "safe"
        },
        "sexual": {
          "filtered": false,
          "severity": "safe"
        },
        "violence": {
          "filtered": false,
          "severity": "safe"
        },
        "self_harm": {
          "filtered": false,
          "severity": "safe"
        }
      },
      "content_filter_offsets": {
        "start_offset": 0,
        "end_offset": 49,
        "check_offset": 0
      }
    },
    {
      "blocked": false,
      "source_type": "completion",
      "content_filter_raw": [],
      "content_filter_results": {
        "hate": {
          "filtered": false,
          "severity": "safe"
        },
        "sexual": {
          "filtered": false,
          "severity": "safe"
        },
        "violence": {
          "filtered": false,
          "severity": "safe"
        },
        "self_harm": {
          "filtered": false,
          "severity": "safe"
        }
      },
      "content_filter_offsets": {
        "start_offset": 0,
        "end_offset": 1170,
        "check_offset": 0
      }
    }
  ],
  "error": null,
  "frequency_penalty": 0.0,
  "incomplete_details": null,
  "instructions": "你是一个深谙养身之道的时尚顾问。",
  "max_output_tokens": null,
  "max_tool_calls": null,
  "model": "gpt-5.2-chat",
  "output": [
    {
      "id": "rs_08fd9fcf3071918b006a000a0152f88193b91826c5aa30181a",
      "type": "reasoning",
      "summary": []
    },
    {
      "id": "fc_08fd9fcf3071918b006a000a01c6548193b850d9b453ce47f8",
      "type": "function_call",
      "status": "completed",
      "arguments": "{\"location\":\"苏州\"}",
      "call_id": "call_kYGZgvSLCPipLqtmiIqfnIDT",
      "name": "_Main_g_GetWeather_0_1"
    }
  ],
  "parallel_tool_calls": true,
  "presence_penalty": 0.0,
  "previous_response_id": null,
  "prompt_cache_key": null,
  "prompt_cache_retention": null,
  "reasoning": {
    "effort": "medium",
    "summary": null
  },
  "safety_identifier": null,
  "service_tier": "default",
  "store": true,
  "temperature": 1.0,
  "text": {
    "format": {
      "type": "text"
    },
    "verbosity": "medium"
  },
  "tool_choice": "auto",
  "tools": [
    {
      "type": "function",
      "description": null,
      "name": "_Main_g_GetWeather_0_1",
      "parameters": {
        "type": "object",
        "required": [
          "location"
        ],
        "properties": {
          "location": {
            "description": "Location for weather query",
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "strict": false
    }
  ],
  "top_logprobs": 0,
  "top_p": 0.85,
  "truncation": "disabled",
  "usage": {
    "input_tokens": 83,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 43,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 126
  },
  "user": null,
  "metadata": {}
}

这是第二轮调用LLM提供的请求和得到的响应内容:

{
  "model": "gpt-5.2-chat",
  "tools": [
    {
      "type": "function",
      "name": "_Main_g_GetWeather_0_1",
      "description": "",
      "parameters": {
        "type": "object",
        "required": [
          "location"
        ],
        "properties": {
          "location": {
            "description": "Location for weather query",
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "strict": null
    }
  ],
  "input": [
    {
      "type": "message",
      "role": "user",
      "content": [
        {
          "type": "input_text",
          "text": "根据苏州的天气给我一些着装建议。"
        }
      ]
    },
    {
      "type": "reasoning",
      "id": "rs_08fd9fcf3071918b006a000a0152f88193b91826c5aa30181a",
      "summary": []
    },
    {
      "type": "function_call",
      "id": "fc_08fd9fcf3071918b006a000a01c6548193b850d9b453ce47f8",
      "status": "completed",
      "call_id": "call_kYGZgvSLCPipLqtmiIqfnIDT",
      "name": "_Main_g_GetWeather_0_1",
      "arguments": "{\"location\":\"苏州\"}"
    },
    {
      "type": "function_call_output",
      "call_id": "call_kYGZgvSLCPipLqtmiIqfnIDT",
      "output": "\"苏州 当前晴朗,气温为25°C。\""
    }
  ],
  "instructions": "你是一个深谙养身之道的时尚顾问。"
}
{
  "id": "resp_08fd9fcf3071918b006a000a025fbc8193a1219305fbea8789",
  "object": "response",
  "created_at": 1778387458,
  "status": "completed",
  "background": false,
  "completed_at": 1778387467,
  "content_filters": [
    {
      "blocked": false,
      "source_type": "completion",
      "content_filter_raw": [],
      "content_filter_results": {
        "hate": {
          "filtered": false,
          "severity": "safe"
        },
        "sexual": {
          "filtered": false,
          "severity": "safe"
        },
        "violence": {
          "filtered": false,
          "severity": "safe"
        },
        "self_harm": {
          "filtered": false,
          "severity": "safe"
        }
      },
      "content_filter_offsets": {
        "start_offset": 0,
        "end_offset": 1912,
        "check_offset": 0
      }
    }
  ],
  "error": null,
  "frequency_penalty": 0.0,
  "incomplete_details": null,
  "instructions": "你是一个深谙养身之道的时尚顾问。",
  "max_output_tokens": null,
  "max_tool_calls": null,
  "model": "gpt-5.2-chat",
  "output": [
    {
      "id": "msg_08fd9fcf3071918b006a000a02c53c819385543898398da88e",
      "type": "message",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "annotations": [],
          "logprobs": [],
          "text": "...(同上面展示的LLM最终回复内容)..."
        }
      ],
      "role": "assistant"
    }
  ],
  "parallel_tool_calls": true,
  "presence_penalty": 0.0,
  "previous_response_id": null,
  "prompt_cache_key": null,
  "prompt_cache_retention": null,
  "reasoning": {
    "effort": "medium",
    "summary": null
  },
  "safety_identifier": null,
  "service_tier": "default",
  "store": true,
  "temperature": 1.0,
  "text": {
    "format": {
      "type": "text"
    },
    "verbosity": "medium"
  },
  "tool_choice": "auto",
  "tools": [
    {
      "type": "function",
      "description": null,
      "name": "_Main_g_GetWeather_0_1",
      "parameters": {
        "type": "object",
        "required": [
          "location"
        ],
        "properties": {
          "location": {
            "description": "Location for weather query",
            "type": "string"
          }
        },
        "additionalProperties": false
      },
      "strict": false
    }
  ],
  "top_logprobs": 0,
  "top_p": 0.85,
  "truncation": "disabled",
  "usage": {
    "input_tokens": 157,
    "input_tokens_details": {
      "cached_tokens": 0
    },
    "output_tokens": 612,
    "output_tokens_details": {
      "reasoning_tokens": 0
    },
    "total_tokens": 769
  },
  "user": null,
  "metadata": {}
}