静态分析揪出MCP工具重复执行Bug:幂等性缺失与并发控制实战
最近在调试一个基于 MCPModel Context Protocol的工具时遇到了一个诡异的现象同一个查询请求有时会莫名其妙地执行两次。这可不是简单的网络抖动而是工具内部逻辑导致的重复执行。更关键的是这个 Bug 不是靠大语言模型LLM猜出来的而是通过静态代码分析Static Analysis这种传统但极其可靠的方法揪出来的。在 AI 工具满天飞的今天大家似乎习惯了让 LLM 去“理解”代码并找问题。但这次经历提醒我们对于并发、状态管理这类经典的软件工程问题传统的静态分析工具往往能提供更精确、更确定性的洞察。这个 Bug 的核心在于缺失了幂等性Idempotency保障在特定触发条件下会导致资源浪费、数据不一致甚至更严重的副作用。如果你也在开发或使用类似的 Agent、MCP Server 或任何涉及远程过程调用RPC的工具那么理解这个重复执行 Bug 的成因、危害和修复方法将能帮你提前规避一大类隐蔽的线上问题。本文将从一次真实的排查经历出发拆解如何不依赖 LLM仅通过代码审查和静态分析定位问题并给出通用的防重复执行最佳实践。1. 问题重现当你的 MCP 工具开始“自言自语”首先明确一下场景。MCPModel Context Protocol是一种协议用于在 AI 应用如 Cursor、Claude Desktop和外部工具如数据库、文件系统、API之间建立安全、标准化的通信。你可以把它理解为 AI 的“插件系统”或“技能Skills”底座。我遇到的问题发生在一个自研的 MCP Server 上它的功能是连接一个内部数据库执行查询。症状如下用户在 AI 助手界面发送一个自然语言查询例如“查询上个月的订单总数”。大多数情况下工作正常返回一个结果。但在某些特定条件下如网络延迟稍高、或首次连接时监控日志显示完全相同的 SQL 查询在数据库层面被执行了两次时间间隔极短毫秒级。这直接导致了数据库不必要的负载更糟糕的是如果查询是“插入一条日志”或“扣减库存”就会造成数据错误。最初怀疑是 AI 客户端如 Cursor重复发送了请求。但通过抓包和客户端日志排查排除了这种可能。问题一定出在 MCP Server 内部。2. 核心概念幂等性Idempotency为何是生命线在深入代码之前必须理解一个关键概念幂等性。通俗解释一个操作无论你执行一次还是多次只要输入相同产生的结果和副作用都应该是完全一样的。技术定义在分布式系统和 API 设计中幂等性是保证系统可靠性的重要属性。客户端在未收到响应时可能因为超时、网络中断进行重试服务器必须能够正确处理避免因重复请求导致重复扣款、重复创建订单等问题。MCP 场景下的重要性MCP Server 本质上是一个 RPC 服务器。AI 客户端通过网络调用它。网络是不可靠的超时、重连、客户端重试都是常态。如果 MCP Server 不具备幂等性那么任何一次网络波动都可能触发重复执行导致 Bug。这个 Bug 的根源就是工具在处理某些请求时违背了幂等性原则。静态分析的任务就是找到代码中哪些逻辑路径可能破坏这一原则。3. 环境与工具我们的“侦查装备”本次分析不依赖任何特定的 IDE 或 LLM 编程助手。主要工具链如下操作系统macOS/Linux (适用于大多数开发环境)分析对象一个用 Python 编写的 MCP Server 示例代码原理通用其他语言类似核心分析工具grep/ripgrep (rg)用于全局搜索关键代码模式。代码阅读与推理这是最重要的“工具”即开发者的逻辑思维能力。日志在关键位置添加日志记录请求 ID、执行阶段。可选辅助像pylint,flake8这类 linter 也能帮助发现一些代码异味但核心逻辑缺陷还需人工分析。我们的目标不是运行测试而是在不执行代码的情况下通过阅读源代码来推理出可能存在的并发或状态问题。4. 静态分析实战一步步揪出重复执行元凶假设我们有一个简化版的 MCP Server 代码文件mcp_server.py。4.1 第一步定位请求处理入口MCP 协议通常有标准的请求处理循环。我们首先搜索处理函数或主循环。rg -n def handle\|async def.*request\|main.*loop mcp_server.py假设我们找到如下关键代码段# mcp_server.py - 简化示例展示问题模式 import asyncio from some_mcp_library import Server class MyDatabaseServer: def __init__(self): self.connection_pool None # 问题标志1一个共享的、可能未正确同步的状态 self._is_initialized False async def initialize_connection(self): 初始化数据库连接池 if not self._is_initialized: print(Initializing database connection pool...) # 模拟一个耗时的初始化操作 await asyncio.sleep(0.1) self.connection_pool create_dummy_pool() self._is_initialized True print(Initialization complete.) return self.connection_pool async def handle_query_request(self, request_id: str, sql: str): 处理查询请求 print(f[{request_id}] Handling query: {sql}) # 问题标志2在关键业务逻辑前有一个可能产生竞态的条件检查 pool await self.initialize_connection() # 执行查询 result await pool.execute(sql) print(f[{request_id}] Query executed, result: {result}) return result async def main(): server MyDatabaseServer() mcp_server Server() mcp_server.function(query_database) async def query_database(sql: str): # 问题标志3为每个请求生成一个ID但处理函数本身可能被并发调用 request_id str(uuid.uuid4()) return await server.handle_query_request(request_id, sql) await mcp_server.run() if __name__ __main__: asyncio.run(main())4.2 第二步识别可疑的并发模式通过阅读上述代码静态分析发现了几个“危险信号”共享状态 (self._is_initialized) 的非原子性检查与设置在initialize_connection方法中先检查if not self._is_initialized然后执行初始化最后设置self._is_initialized True。在异步并发环境下如果两个请求几乎同时到达它们可能都通过if检查然后都去执行初始化的await asyncio.sleep(0.1)。虽然连接池可能只创建一次取决于create_dummy_pool的实现但print语句会执行两次更重要的是后续逻辑可能因此产生意想不到的副作用。缺少请求级别的幂等性校验handle_query_request方法直接执行查询。如果客户端因超时重试发送了完全相同的请求即使有相同的request_id但本例中并未利用服务器会无条件再次执行pool.execute(sql)。4.3 第三步推理 Bug 触发流程基于以上分析我们可以推理出 Bug 的触发场景客户端发送请求Req-A查询“订单总数”。MCP Server 开始处理Req-A进入handle_query_request。Req-A执行到await self.initialize_connection()。此时_is_initialized为False它开始执行耗时的初始化await asyncio.sleep(0.1)但尚未将_is_initialized设为True。就在这 0.1 秒内客户端可能因网络延迟未收到响应触发了重试机制发送了Req-B内容与Req-A完全相同。MCP Server 收到Req-B创建新的任务处理它。Req-B也进入handle_query_request调用initialize_connection()。此时_is_initialized仍然为False因为Req-A的任务还没执行到设置它为True的那行代码。于是Req-B也通过了if检查开始执行另一个“初始化”流程。现在我们有了两个并发的初始化任务。最终结果连接池可能被初始化两次如果create_dummy_pool不是幂等的或者至少初始化日志被打印两次并且两个请求都会继续向下执行pool.execute(sql)导致同一个 SQL 查询被执行两次。这就是典型的“重复执行” Bug。其根源在于对共享状态的检查和修改不是原子操作在并发环境下产生了竞态条件Race Condition。5. 修复方案从临时补丁到健壮设计发现了问题修复就有了方向。以下是几个不同层次的解决方案。5.1 方案一使用锁Lock保护临界区最直接的修复是为共享状态的访问加锁确保初始化过程是串行的。import asyncio from asyncio import Lock class MyDatabaseServer: def __init__(self): self.connection_pool None self._is_initialized False # 添加一个异步锁 self._init_lock Lock() async def initialize_connection(self): 初始化数据库连接池使用锁保证幂等性 # 快速路径如果已经初始化直接返回 if self._is_initialized: return self.connection_pool # 慢速路径获取锁防止并发初始化 async with self._init_lock: # 获取锁后再次检查双重检查锁定模式 if not self._is_initialized: print(Initializing database connection pool...) await asyncio.sleep(0.1) self.connection_pool create_dummy_pool() self._is_initialized True # 必须在所有初始化工作完成后才设置标志 print(Initialization complete.) return self.connection_pool关键点使用asyncio.Lock来同步。采用了“双重检查锁定”模式在获取锁之前先进行一次检查避免不必要的锁竞争提升性能。锁内再次检查确保万无一失。5.2 方案二利用asyncio.ensure_future或单次初始化模式对于“只需执行一次”的初始化任务可以使用一个 Future 对象来代表其结果。class MyDatabaseServer: def __init__(self): self._connection_pool_future None async def initialize_connection(self): 初始化数据库连接池使用Future保证只执行一次 if self._connection_pool_future is None: # 创建一个Future来表示初始化任务 loop asyncio.get_event_loop() self._connection_pool_future loop.create_future() # 启动初始化任务 asyncio.create_task(self._do_initialize()) # 等待初始化完成无论调用多少次都等待同一个Future return await self._connection_pool_future async def _do_initialize(self): 实际的初始化逻辑 try: print(Initializing database connection pool...) await asyncio.sleep(0.1) pool create_dummy_pool() print(Initialization complete.) # 设置Future的结果所有等待者都将得到这个pool self._connection_pool_future.set_result(pool) except Exception as e: # 如果初始化失败设置异常所有等待者都会收到这个异常 self._connection_pool_future.set_exception(e) # 可选将future重置为None允许后续重试 self._connection_pool_future None raise关键点将初始化任务抽象为一个Future。所有并发调用都await同一个Future初始化逻辑只会执行一次。结构更清晰更符合异步编程范式。5.3 方案三请求级别的幂等性更彻底的解决方案修复初始化竞态只是第一步。要彻底解决重复执行还应该在业务逻辑层实现请求幂等性。这通常需要客户端配合传递一个唯一的请求 IDidempotency key。import hashlib from typing import Dict class MyDatabaseServer: def __init__(self): self.connection_pool None self._init_lock Lock() # 用于存储已处理请求ID的缓存生产环境应用Redis等外部存储 self._processed_requests: Dict[str, str] {} async def handle_query_request(self, request_id: str, idempotency_key: str, sql: str): 处理查询请求支持幂等性 # 1. 幂等性检查 if idempotency_key in self._processed_requests: print(f[{request_id}] Request already processed, returning cached result.) # 返回之前存储的结果或者至少避免重复执行 # 这里简单返回一个标识实际应返回之前的结果 return {status: duplicate, request_id: self._processed_requests[idempotency_key]} # 2. 标记请求为“处理中”或“已处理”生产环境需考虑原子性 self._processed_requests[idempotency_key] request_id # 3. 执行核心业务逻辑受保护的初始化 pool await self.initialize_connection() result await pool.execute(sql) # 4. 存储结果可选如果业务需要返回完全相同的结果 # self._request_results[idempotency_key] result print(f[{request_id}] Query executed, result: {result}) return result关键点idempotency_key由客户端生成并传递通常是一个 UUID。服务器用这个 Key 作为缓存键如果发现重复 Key则直接返回之前的处理结果不执行实际逻辑。注意内存字典仅适用于单进程。生产环境需要使用 Redis 等分布式缓存并妥善处理缓存过期和清理。6. 验证修复如何测试幂等性修复后必须进行验证。可以编写一个简单的并发测试脚本。# test_idempotency.py import asyncio import uuid from mcp_server_fixed import MyDatabaseServer # 导入修复后的类 async def simulate_concurrent_requests(): server MyDatabaseServer() request_id_base test-req sql SELECT COUNT(*) FROM orders # 模拟10个几乎同时到达的请求使用相同的幂等键 idempotency_key str(uuid.uuid4()) tasks [] for i in range(10): req_id f{request_id_base}-{i} # 每个任务都调用处理函数 task asyncio.create_task( server.handle_query_request(req_id, idempotency_key, sql) ) tasks.append(task) print(Launching 10 concurrent requests with the same idempotency key...) results await asyncio.gather(*tasks, return_exceptionsTrue) execution_count 0 for i, result in enumerate(results): if isinstance(result, Exception): print(fTask {i} failed with: {result}) else: print(fTask {i} result: {result}) if result.get(status) ! duplicate: execution_count 1 print(f\n Summary ) print(fTotal requests: 10) print(fActual database executions (should be 1): {execution_count}) # 同时检查初始化日志只打印了一次 # 这需要你在代码中添加日志计数器或通过其他方式验证 if __name__ __main__: asyncio.run(simulate_concurrent_requests())预期输出 你应该看到“Initializing database connection pool...”只打印了一次并且只有第一个请求真正执行了数据库查询后续请求都返回了duplicate状态或缓存的结果。7. 常见问题与排查清单在实现 MCP Server 或任何异步服务时以下问题非常普遍问题现象可能原因排查方式解决方案数据库查询重复执行1. 客户端超时重试2. 服务端逻辑无幂等性校验3. 共享状态竞态条件1. 检查客户端日志是否有重试记录2. 在服务端关键函数入口/出口添加详细日志请求ID3. 审查代码中对共享变量如标志位、缓存的读写1. 实现请求幂等性Idempotency Key2. 使用锁或同步原语保护临界区3. 使用 Future 或run_once模式初始化函数被调用多次类似上述 Bugif检查与状态设置非原子性在初始化函数开始和结束处打印日志观察并发请求下的日志顺序采用“双重检查锁定”或“单次 Future”模式内存缓存 (dict) 中幂等键失效1. 服务重启丢失2. 多进程/多实例部署下缓存不共享1. 检查重启后问题是否复现2. 检查部署架构1. 使用外部存储如 Redis并设置合理 TTL2. 确保幂等键在业务时间内全局唯一性能下降锁竞争激烈过度使用粗粒度锁锁范围太大使用性能分析工具如cProfile,py-spy定位热点缩小锁范围使用更细粒度的锁或用无锁数据结构如asyncio.Queue客户端收到重复成功响应服务端处理成功但响应在网络中丢失客户端重试服务端因无幂等性再次执行成功对比客户端和服务端日志根据请求ID匹配发送和接收记录确保幂等性逻辑在业务副作用发生前生效并且成功结果可缓存返回8. 最佳实践构建健壮的 MCP Server 与异步服务基于这次静态分析的经验总结出以下工程实践可以帮助你避免类似陷阱假设并发总会发生在异步框架如asyncio、nodejs中即使你预期流量很低也要假设任何await点都可能发生任务切换导致并发问题。按最坏情况设计。状态外置尽可能避免在服务内存中维护复杂的共享状态。将状态存储在数据库、Redis 或其它外部持久化存储中利用其原子操作如SETNX来实现锁或标志位。幂等性作为核心设计在设计 MCP Server 的“工具”Tools或“技能”Skills时将幂等性作为接口契约的一部分。考虑为每个操作定义唯一的幂等键生成规则。全面的日志与追踪为每个请求分配唯一的request_id并在处理链路的所有关键步骤接收、开始处理、访问共享资源、执行副作用、返回都打印该 ID。这是诊断并发问题最有力的工具。代码审查关注共享状态在代码审查中将对共享变量尤其是布尔标志、计数器、缓存字典的读写操作列为高风险点仔细检查其并发安全性。善用静态分析工具虽然本文是人工分析但可以结合像bandit安全、pylint代码质量等工具进行扫描。对于其他语言如 Java 的FindBugs/SpotBugsGo 的race detector都能有效发现常见的并发 Bug 模式。压力测试与混沌工程在测试阶段使用asyncio.gather模拟高并发请求。引入随机的网络延迟asyncio.sleep模拟、故障验证系统的健壮性。通过这次对 MCP 工具中一个隐蔽的重复执行 Bug 的静态分析我们再次认识到在 AI 驱动的开发新时代传统的软件工程原则——如并发控制、幂等性和状态管理——不仅没有过时反而更加重要。LLM 能帮我们生成代码但理解和保证代码在复杂并发环境下的正确性仍然需要开发者扎实的功底和严谨的分析。下次当你怀疑自己的服务有 Bug 时不妨先暂时放下 AI 对话静下心来用grep和一双“人眼”仔细审视代码的逻辑流。你可能会发现最强大的调试工具始终是你自己的推理能力。

相关新闻

Meta-Harness:AI智能体开发与评估的标准化框架实践

Meta-Harness:AI智能体开发与评估的标准化框架实践

这次我们来看一个名为Meta-Harness的项目,它来自 Prompt Engineering 团队,旨在彻底改变 AI 智能体的运行方式。简单来说,它不是一个具体的图像或语音模型,而是一个框架或工具集,核心目标是解决大语言模型(…

2026/8/22 12:33:17 阅读更多 →
Linux服务器深度学习环境部署指南:从SSH连接到PyTorch GPU验证

Linux服务器深度学习环境部署指南:从SSH连接到PyTorch GPU验证

实验室服务器怎么用?手把手教你 Linux 远程部署深度学习环境(AnacondaPyTorchGPU) 如果你刚拿到一台实验室的 Linux 服务器,想在上面跑深度学习代码,但不知道从何下手,这篇文章就是为你准备的。很多新手会…

2026/8/22 12:26:12 阅读更多 →
SpringBoot+Vue家居物流管理系统毕业设计全栈实战指南

SpringBoot+Vue家居物流管理系统毕业设计全栈实战指南

最近在帮几个学弟学妹看毕业设计,发现一个挺有意思的现象:很多人一上来就问我“有没有现成的项目代码”,拿到手之后,第一件事不是去理解业务逻辑和代码结构,而是直接找“登录注册”和“增删改查”的代码,然…

2026/8/22 12:47:31 阅读更多 →

最新新闻

三步解包微信小程序:unwxapkg 解 wxapkg 工具完整指南

三步解包微信小程序:unwxapkg 解 wxapkg 工具完整指南

三步解包微信小程序:unwxapkg 解 wxapkg 工具完整指南 【免费下载链接】unwxapkg WeChat applet .wxapkg decoding tool 项目地址: https://gitcode.com/gh_mirrors/un/unwxapkg 这篇文章写给从没碰过微信小程序解包、连 .wxapkg 是什么都搞不清的你。我会手…

2026/8/22 14:01:41 阅读更多 →
以太网接口与链路配置实战:从基础参数到链路聚合优化

以太网接口与链路配置实战:从基础参数到链路聚合优化

1. 项目概述:从接口到链路的实战脉络搞网络的人,每天打交道最多的可能就是以太网接口了。从机房里一排排闪烁的交换机端口,到服务器后面板上的网卡,再到你工位下那个不起眼的RJ-45水晶头,这些都是以太网接口的物理呈现…

2026/8/22 14:01:41 阅读更多 →
Java插入排序算法详解:从原理到实战应用与优化

Java插入排序算法详解:从原理到实战应用与优化

1. 项目概述:为什么插入排序是算法入门的“定心丸”刚接触数据结构与算法的朋友,尤其是从Java开始的朋友,常常会被各种排序算法的名字和复杂度公式吓到。冒泡、选择、快速、归并……听起来一个比一个复杂。但如果你问我,在十大经典…

2026/8/22 14:01:41 阅读更多 →
HTTP 5xx服务器错误全解析:从502/504到503,实战排查与架构预防指南

HTTP 5xx服务器错误全解析:从502/504到503,实战排查与架构预防指南

1. 从一次深夜告警说起:为什么5xx错误让人头疼凌晨两点,手机突然开始疯狂震动。打开一看,监控系统里一片飘红,全是“HTTP 500 Internal Server Error”的告警。用户反馈页面打不开,订单提交失败,整个核心业…

2026/8/22 14:01:41 阅读更多 →
SprocketsHandler还是RackHandler?serviceworker-rails双处理器差异、切换与自定义方法完全指南

SprocketsHandler还是RackHandler?serviceworker-rails双处理器差异、切换与自定义方法完全指南

SprocketsHandler还是RackHandler?serviceworker-rails双处理器差异、切换与自定义方法完全指南 【免费下载链接】serviceworker-rails Use Service Worker with the Rails asset pipeline 项目地址: https://gitcode.com/gh_mirrors/se/serviceworker-rails …

2026/8/22 14:01:40 阅读更多 →
2000-2024年全球1km人口密度栅格数据全集

2000-2024年全球1km人口密度栅格数据全集

长时间序列的人口密度栅格数据是解析人类活动时空演进轨迹的核心工具。这套数据能够系统揭示人口分布随时间的动态演变规律,精准识别人口增长、空间迁移、集聚效应与收缩格局的多维模式,为城市规划优化、资源合理配置、环境保护决策、灾害风险预警以及社…

2026/8/22 14:00:40 阅读更多 →

日新闻

沉金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 阅读更多 →