[AG-UI详解-05]如何构建一个兼容AG-UI协议的客户端
在AG-UI详解-04如果构建一个兼容AG-UI协议的Agent中我们通过在一个ASP.NET Core应用中注册了一个绑定指定IChatClient的路由终结点使外界可以通过Web调用的方式以AG-UI协议调用对应的LLM。在这篇文章中我们看看AG-UI客户端又该如何开发。和前面一样我们的目的仅仅是作一个简单的演示并非开发一款真正的功能完备的AG-UI客户端所以我们的客户端建立在一个简单的假设上面比如不考虑工具调用、人机交互、多模态消息内容和中断等。1. 流式读取AG-UI事件我们创建的客户端将用于对接AG-UI详解-04如果构建一个兼容AG-UI协议的Agent创建的Agent后端。由于此后端采用SSE输出AG-UI响应的事件所以我们的客户端的核心功能就是读取这些事件并交给上层应用处理。我们创建的AG-UI客户端的实现非常简单。如下面这个AGUIClient所示唯一的InvokeAsync方法将RunAgentInput对象作为输入。我们将RunAgentInput对象序列化成JSON作为远程调用AG-UI服务地址通过构造函数指定的输入。在得到作为响应的HttpResponseMessage对象后我们利用创建的SseParser采用SSE协议将每个Chunk读出来并反序列化成BaseEvent对象。整个方法以IAsyncEnumerableBaseEvent的形式返回读取的AG-UI事件。publicclassAGUIClient(stringserver):IDisposable{privatereadonlyHttpClient_httpClientnew();publicasyncIAsyncEnumerableBaseEventInvokeAsync(RunAgentInputinput,[EnumeratorCancellation]CancellationTokencancellationToken){varresponseawait_httpClient.PostAsJsonAsync(server,input,cancellationToken);usingStreamstreamawaitresponse.Content.ReadAsStreamAsync(cancellationToken);varsseParserSseParser.Create(stream,(eventType,bytes)JsonSerializer.DeserializeBaseEvent(bytes)!);awaitforeach(SseItemBaseEventiteminsseParser.EnumerateAsync(cancellationToken)){yieldreturnitem.Data;}}publicvoidDispose()_httpClient.Dispose();}2. 利用AGUIClient构建一个聊天应用我们在一个控制台上利用上面定义的这个AGUIClient构建一个简单的聊天应用。如下面代码所示我们通过连接AG-UI详解-04如果构建一个兼容AG-UI协议的Agent构建的Agent后端创建出AGUIClient对象后开启了一个对话循环。为了保持对话上下文我们让输入的RunAgentInput对象共享同一个ThreadId。varthreadId$thread-{Guid.NewGuid()};varrunIndex0;varinputnewRunAgentInput{ThreadIdthreadId,};varisFirstDialogtrue;varsystemMessagenewAGUISystemMessage{Content 你是一个深谙中国古代历史的专家善于根据正史以公正客观的态度于人交流历史问题。 对于用户提出的问题请以简介概括性的语言予以答复字数尽量保持在200字以内。};usingvarclientnewAGUIClient(http://localhost:5566);while(true){Console.Write(\n$ (:q or quit to exit): );varmessageConsole.ReadLine();if(messageis:qorquit){break;}input.RunId$run-{runIndex};input.MessagesisFirstDialog?[systemMessage,newAGUIUserMessage{ContentnewAGUIUserContent(message!)}]:[newAGUIUserMessage{ContentnewAGUIUserContent(message!)}];isFirstDialogfalse;Console.WriteLine();awaitforeach(vareventinclient.InvokeAsync(input,CancellationToken.None)){if(eventisTextMessageContentEventcontentEvent!string.IsNullOrEmpty(contentEvent.Delta)){Console.Write(contentEvent.Delta);}}}我们将用户输入转换成一个AGUIUserMessage对象并添加到RunAgentInput的Messages列表中。对于初始调用我们还会添加一个AGUISystemMessage对象作为系统指令。我们将RunAgentInput对象作为输入调用AGUIClient的InvokeAsync方法并得到一个IAsyncEnumerableBaseEvent对象。在异步迭代中我们输出TextMessageContentEvent事件携带的实时生成的文本。运行程序后我们于AI展开如下这段关于战国国君排名的简单对话。$ (:q or quit to exit): 给战国国君排个序你觉得前四位会有谁 若以“综合国力塑造、制度影响、战略眼光、历史结果”衡量我会把战国国君前四大致排为 1. 秦孝公重用商鞅变法彻底改变秦国结构为统一奠基。 2. 齐威王整顿吏治、广纳谏言齐国一度成为东方最强国。 3. 赵武灵王推行“胡服骑射”完成军事革命使赵国跃升强国。 4. 秦昭襄王在位最长重用范雎、白起持续削弱六国统一已见雏形。 若看个人雄才燕昭王、魏文侯、楚悼王也很突出若看最终成果秦始皇当然最耀眼但严格说已属“战国末—秦朝开端”。 $ (:q or quit to exit): 对于这四位赵雍的结局令人唏嘘你觉得是什么造成的 赵武灵王赵雍的悲剧核心还是“改革成功却未处理好权力交接”。 他通过胡服骑射让赵国崛起但晚年过早让位给幼子赵何自称“主父”形成“两套权威并存”。同时他偏爱长子赵章又未彻底明确继承秩序结果引发“沙丘之乱”。赵章失败后赵成、李兑等人担心主父再起变数最终将赵雍围困沙丘宫活活饿死。 赵雍强于开创却失于收束。他敢打破传统却低估了宗室、贵族与继承问题的破坏力。战国很多名君都能“兴国”但“善终”往往比改革更难。赵武灵王正是典型例子。 $ (:q or quit to exit): 如果他能向其余三位一样处理好继承人的问题地位能否排在田婴齐前面 有可能甚至不少后世史家会倾向把赵武灵王排到齐威王之前。 赵武灵王的“胡服骑射”不仅是强兵更是文明层面的制度突破。他直接改变了中原传统战争方式使赵国从二流国家跃升为能与秦抗衡的强国对战国军事格局影响极大。相比之下齐威王的成就更多是“中兴齐国”制度创新和长期影响力略逊一筹。 但赵雍的问题在于他留下的是“强国”却不是“稳固秩序”。他死后赵国内耗加剧国力虽强却逐渐失去持续扩张能力。而齐威王给齐国留下的政治结构相对稳定还开启了稷下学宫的文化高峰。 所以若赵武灵王能平稳完成权力交接让改革成果延续两三代他的历史地位确实有机会进入“战国第一流君主”的更前列。3. 将AGUIClient进一步封装成IChatClient如果将基于AG-UI的Agent后端视为一个AI服务实际上就是我们可以定义一个连接它的IChatClient实现类型以方便上层应用的使用毕竟AG-UI的BaseEvent还偏底层。为此我们定义了如下这个AGUIChatClient类型。如代码片段所示AGUIChatClient实际上是对上面定义的AGUIClient对象的封装。由于作为AG-UI输入的RunAgentInput需要指定ThreadId对于IChatClient接口的定义来看这个ThreadId要么放在传入的消息中要么放在ChatOptions中我选择了后者。在实现的GetStreamingResponseAsync中我们从ChatOptions中取出ThreadId并将传入的ChatMessage消息列表转换成AGUIMessage消息列表两者组合生成的RunAgentInput作为调用AGUIClient对象InvokeAsync方法的输入。publicclassAGUIChatClient(stringserver):IChatClient{privatereadonlyAGUIClient_clientnew(server);privatereadonlyJsonSerializerOptions_jsonSerializerOptionsAGUIJsonSerializerContext.Default.Options;publicvoidDispose()_client.Dispose();publicasyncTaskChatResponseGetResponseAsync(IEnumerableChatMessagemessages,ChatOptions?optionsnull,CancellationTokencancellationTokendefault){ListAgentResponseUpdateupdates[];awaitforeach(varupdateinGetStreamingResponseAsync(messages,options,cancellationToken)){updates.Add(newAgentResponseUpdate(update));}returnnewChatResponse(updates.ToAgentResponse().Messages);}publicobject?GetService(TypeserviceType,object?serviceKeynull)null;publicIAsyncEnumerableChatResponseUpdateGetStreamingResponseAsync(IEnumerableChatMessagemessages,ChatOptions?optionsnull,CancellationTokencancellationTokendefault){varthreadIdoptions?.AdditionalProperties?.TryGetValuestring(agui-thread-id,outvarvalue)true?value:$thread-{Guid.NewGuid()};threadId??$thread-{Guid.NewGuid()};varrunId$run-{Guid.NewGuid()};varinputnewRunAgentInput{ThreadIdthreadId,RunIdrunId,Messagesmessages.Select(AsAGUIMessages).ToList(),};return_client.InvokeAsync(input,cancellationToken).AsChatResponseUpdatesAsync(_jsonSerializerOptions,cancellationToken)}privateAGUIMessageAsAGUIMessages(ChatMessagemessage){varcontentstring.Join(\n\n,message.Contents.OfTypeTextContent().Select(itit.Text));varrolemessage.Role;returnroleChatRole.System?newAGUISystemMessage{Contentcontent}:roleChatRole.User?newAGUIUserMessage{ContentnewAGUIUserContent(content)}:roleChatRole.Assistant?newAGUIAssistantMessage{Contentcontent}:thrownewArgumentException($ChatMessageType{message.GetType()}is not supported.);}}我们调用如下定义的扩展方法AsChatResponseUpdatesAsync将AGUIClient对象InvokeAsync方法返回的IAsyncEnumerableBaseEvent转换成最终返回的IAsyncEnumerableChatResponseUpdate对象。按照我们之前的假设我们只考虑RunStartedEvent、RunFinishedEvent、RunErrorEvent、TextMessageStartEvent和TextMessageEndEvent这五种AG-UI事件。internalstaticclassChatResponseUpdateAGUIExtensions{privatestaticreadonlyMediaTypeHeaderValue?s_jsonPatchMediaTypenew(application/json-patchjson);privatestaticreadonlyMediaTypeHeaderValue?s_jsonnew(application/json);publicstaticasyncIAsyncEnumerableChatResponseUpdateAsChatResponseUpdatesAsync(thisIAsyncEnumerableBaseEventevents,JsonSerializerOptionsjsonSerializerOptions,[EnumeratorCancellation]CancellationTokencancellationTokendefault){string?conversationIdnull;string?responseIdnull;vartextMessageBuildernewTextMessageBuilder();awaitforeach(varevtinevents.WithCancellation(cancellationToken).ConfigureAwait(false)){switch(evt){caseRunStartedEventrunStarted:conversationIdrunStarted.ThreadId;responseIdrunStarted.RunId;textMessageBuilder.SetConversationAndResponseIds(conversationId,responseId);yieldreturnnewChatResponseUpdate(ChatRole.Assistant,[]){ConversationIdrunStarted.ThreadId,ResponseIdrunStarted.RunId,CreatedAtDateTimeOffset.UtcNow};;break;caseRunFinishedEventrunFinished:yieldreturnnewChatResponseUpdate(ChatRole.Assistant,runFinished.Result?.GetRawText()){ConversationIdconversationId,ResponseIdresponseId,CreatedAtDateTimeOffset.UtcNow};break;caseRunErrorEventrunError:yieldreturnnewChatResponseUpdate(ChatRole.Assistant,[(newErrorContent(runError.Message){ErrorCoderunError.Code})]);break;caseTextMessageStartEventtextStart:textMessageBuilder.AddTextStart(textStart);break;caseTextMessageContentEventtextContent:yieldreturntextMessageBuilder.EmitTextUpdate(textContent);break;caseTextMessageEndEventtextEnd:textMessageBuilder.EndCurrentMessage(textEnd);break;}}}privatesealedclassTextMessageBuilder(){privateChatRole_currentRole;privatestring?_currentMessageId;privatestring?_conversationId;privatestring?_responseId;publicvoidSetConversationAndResponseIds(string?conversationId,string?responseId){_conversationIdconversationId;_responseIdresponseId;}publicvoidAddTextStart(TextMessageStartEventtextStart){if(_currentRole!default||this._currentMessageId!null){thrownewInvalidOperationException(Received TextMessageStartEvent while another message is being processed.);}_currentRoleAGUIChatMessageExtensions.MapChatRole(textStart.Role);_currentMessageIdtextStart.MessageId;}internalChatResponseUpdateEmitTextUpdate(TextMessageContentEventtextContent){returnnewChatResponseUpdate(_currentRole,textContent.Delta){ConversationIdthis._conversationId,ResponseIdthis._responseId,MessageIdtextContent.MessageId,CreatedAtDateTimeOffset.UtcNow};}internalvoidEndCurrentMessage(TextMessageEndEventtextEnd){_currentRoledefault;_currentMessageIdnull;}}}4. 利用AGUIChatClient构建聊天应用我们将前面聊天应用使用的AGUIClient替换成AGUIChatClient这样我们可以直接使用ChatMessage进行交流了。varthreadId$thread-{Guid.NewGuid()};varoptionsnewChatOptions{AdditionalPropertiesnewAdditionalPropertiesDictionary([newKeyValuePairstring,object?(agui-thread-id,threadId)])};varisFirstDialogtrue;varsystemMessagenewChatMessage(ChatRole.System, 你是一个深谙中国古代历史的专家善于根据正史以公正客观的态度于人交流历史问题。 对于用户提出的问题请以简介概括性的语言予以答复字数尽量保持在200字以内。);usingvarchatClientnewAGUIChatClient(http://localhost:5566);while(true){Console.Write(\n$ (:q or quit to exit): );varmessageConsole.ReadLine();if(messageis:qorquit){break;}varuserMessagenewChatMessage(ChatRole.User,message);ListChatMessagemessagesisFirstDialog?[systemMessage,userMessage]:[userMessage];isFirstDialogfalse;Console.WriteLine();awaitforeach(varupdateinchatClient.GetStreamingResponseAsync(messages,options)){foreach(AIContentcontentinupdate.Contents){if(contentisTextContenttextContent){Console.Write(textContent.Text);}}}}这是我们运行程序后进行的一段对话。$ (:q or quit to exit): 可否认为周公旦是传统中国文化的奠基人之一 可以这样认为但需加限定。周公旦并非“传统中国文化”的唯一奠基人却是早期核心塑造者之一。西周初年他制礼作乐、确立宗法与封建秩序并以“敬天保民”强化政治伦理对后世儒家所重视的礼制、德治、名分影响极深。孔子尤其推崇周公称“郁郁乎文哉吾从周”使周公在后世被视为礼乐文明的代表人物。不过中国传统文化的形成还经历了春秋战国诸子、秦汉制度化等长期发展因此更准确的说法是周公旦是中华礼乐政治与儒家传统的重要奠基者之一。 $ (:q or quit to exit): 有没有可能因为周文化的强势导致商王朝尤其是帝辛这段历史包括他本人被错误评价 有这种可能而且学界普遍承认“胜者书写历史”对商亡周兴的叙述影响很大。现存关于帝辛商纣王的主要记载多出自周人及后世儒家文献带有“以暴君证明革命合理性”的政治目的因此诸如“酒池肉林”等故事很可能存在夸张和道德化加工。 但这不等于帝辛一定是“明君”。甲骨文与考古材料显示商后期长期战争频繁、赋役沉重政治压力确实存在。周人能够联合诸侯与部分商人反商也说明商王朝内部并非毫无矛盾。 因此较稳妥的看法是帝辛形象大概率被妖魔化了但商末统治出现严重问题也并非完全虚构。 $ (:q or quit to exit): 但是如何理解“泰誓”记载的伐纣的理由无非是今殷王纣乃用其妇人之言自绝于天毁坏其三正离其王父母弟乃断弃其先祖之乐乃为淫声用变乱正声怡说妇人。作为战斗檄文列举的这些罪行都仅限于此后世史书记载的那些是否是欲加之罪呢 这是研究商周之际时常被讨论的问题。《尚书·泰誓》确实很值得注意其中对帝辛的指责主要集中于“乱礼”“弃祖制”“亲妇人”“坏正声”等并未出现后世最著名的炮烙、酒池肉林等极端暴政叙述。 这说明两点第一西周初年的政治宣传核心是强调纣“失德失礼”因为周人的合法性建立在“天命转移”与礼制正统上第二后世关于纣的暴虐形象很可能是在长期儒家伦理化叙事中不断累积、强化的。 不过也不能据此完全否定商末问题。对古代王朝而言“弃礼”“疏宗族”“重声色”本身就是严重政治指控意味着统治集团离心。周人能迅速灭商也说明商内部确有危机。 因此更合理的理解是早期文献中的纣主要是“失德之君”而后世则逐渐将其塑造成极端暴君两者之间存在明显的历史层累。

相关新闻

GraphRAG技术深度解析:知识图谱驱动的检索增强生成

GraphRAG技术深度解析:知识图谱驱动的检索增强生成

GraphRAG技术深度解析:知识图谱驱动的检索增强生成 随着大语言模型在企业知识管理场景的深入应用,传统向量RAG在处理需要多跳推理、全局总结和关系分析的复杂查询时逐渐力不从心。GraphRAG(图检索增强生成)通过将知识库构建为知识…

2026/8/22 15:29:49 阅读更多 →
【Rust自学】1.3. Rust Cargo

【Rust自学】1.3. Rust Cargo

1.3 Rust Cargo 1.3.0. 回顾 1.2. Rust的基本认识与打印“Hello World”文章的末尾提到了只有小型简单的Rust项目适合使用rustc来编译,大型项目需要Cargo,本篇就对Cargo进行详细的介绍。 1.3.1. 什么是Cargo Cargo是Rust的构建系统和包管理工具&…

2026/8/21 12:08:44 阅读更多 →
嵌入式系统EMIF接口深度解析:SDRAM与异步存储器的配置与调试实战

嵌入式系统EMIF接口深度解析:SDRAM与异步存储器的配置与调试实战

1. 项目概述:为什么需要深入理解EMIF?在嵌入式系统开发中,尤其是涉及图像处理、数据采集或运行复杂算法的场景,片上存储(SRAM、Flash)常常捉襟见肘。这时,扩展外部存储器就成了刚需。但直接让CP…

2026/8/22 9:51:56 阅读更多 →

最新新闻

TCP 与 UDP 基础:建站场景下该关心什么

TCP 与 UDP 基础:建站场景下该关心什么

TCP 与 UDP 基础:建站场景下该关心什么工具地址:https://www.speedce.com 社区论坛:https://bbs.speedce.com 联系:speedceadsgmail.com写在前面 建站主要关心 TCP 443,UDP 是 DNS 和游戏场景。 本文是一份围绕「TCP 与…

2026/8/22 16:12:32 阅读更多 →
前端练习4

前端练习4

字体样式:font-size 改字体大小px font-family 改字体样式 微软雅黑 font-style 规定斜体 font-weight 字体粗细 …

2026/8/22 16:12:32 阅读更多 →
杰理之高低音测试【篇】

杰理之高低音测试【篇】

//************ 高低音测试// {// static int low_vol 0;// static int high_vol 0;// struct high_bass param {0};// low_vol;// if(low_vol>0)low_vol -12;// { // param.freq 125;// param.gain low_vol;// mi…

2026/8/22 16:12:32 阅读更多 →
TypeScript 核心语法应用 —— Vue 3 中的使用(上)

TypeScript 核心语法应用 —— Vue 3 中的使用(上)

阅读本文你能学到什么? 如何为 ref、reactive、computed 标注类型如何为事件处理函数标注类型如何为模板引用标注类型如何处理对象的非空值场景 前言 前面五篇我们学完了 TypeScript 的核心语法——类型注解、接口、泛型、类型断言……学完之后,你可能会…

2026/8/22 16:12:32 阅读更多 →
Stream 流式编程:并行流

Stream 流式编程:并行流

目录 并行流的定义 如何使用并行流提高性能 并行流的适用场景 并行流的注意事项 并行流的性能分析 用ForkJoinPool的眼光来看ParallelStream 并行流的定义 在Java 8中,Stream提供了顺序流(Sequential Stream)和并行流(Paral…

2026/8/22 16:12:32 阅读更多 →
【Docker项目实战】Docker环境下部署immich照片管理系统

【Docker项目实战】Docker环境下部署immich照片管理系统

【Docker项目实战】Docker环境下部署immich照片管理系统一、immich介绍1.1 immich简介1.2 immich注意事项1.3 immich使用场景二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载i…

2026/8/22 16:11:31 阅读更多 →

日新闻

沉金PCB工艺实战指南:从设计到SMT焊接的可靠性保障

沉金PCB工艺实战指南:从设计到SMT焊接的可靠性保障

在电子硬件开发领域,PCB(印制电路板)的沉金工艺是提升产品可靠性和焊接质量的关键环节。对于需要高密度互连、长期稳定运行或高频信号传输的板卡,如“黍姐仿通行证”这类可能涉及身份识别、数据交互的硬件项目,选择正确…

2026/8/22 0:00:11 阅读更多 →
电气考研电路八月强化四步法:从知识体系到真题实战的闭环攻略

电气考研电路八月强化四步法:从知识体系到真题实战的闭环攻略

这次我们来看一个针对电气考研电路科目的学习规划项目。它不是软件工具,而是一套聚焦于8月份关键节点的备考策略。对于电气工程考研的同学来说,电路分析是专业课的重中之重,也是拉开分差的关键。进入8月,复习进入强化阶段&#xf…

2026/8/22 0:00:11 阅读更多 →
消除AI代码的“AI味”:Claude Code设计优化技能配置与实战指南

消除AI代码的“AI味”:Claude Code设计优化技能配置与实战指南

大家好,我是专注于前端开发与AI工具实践的技术博主。在日常使用 Claude Code 等AI编程助手时,你是否也遇到过这样的困扰:生成的代码功能上没问题,但代码风格、组件设计、交互逻辑总透着一股“AI味”——布局单调、样式简陋、交互生…

2026/8/22 0:00:11 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/21 3:21:33 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/22 8:09:09 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/21 6:07:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/21 16:42:28 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/22 7:31:03 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →