1. 项目概述:一个多供应商机器翻译插件的诞生

最近在折腾一个挺有意思的项目,叫 Multi-Supplier-MT-Plugin。简单来说,这是一个旨在解决单一翻译服务依赖问题的插件。如果你做过本地化、内容翻译或者开发过需要多语言支持的应用,肯定遇到过这样的场景:某个翻译引擎突然抽风、API调用次数超限、或者翻译质量在特定领域(比如技术文档、俚语)不尽如人意。这时候,如果能把多个翻译服务商(比如谷歌、微软、百度、DeepL等)整合到一个统一的接口后面,随时切换、择优选用,甚至组合使用,那效率和灵活性就大大提升了。

这个项目正是为此而生。它不是一个独立的翻译软件,而是一个“插件”或“适配器”,可以嵌入到你的现有工作流或应用中。它的核心价值在于“聚合”与“抽象”。通过一个统一的配置和调用方式,你就能轻松接入市面上主流的机器翻译服务,实现负载均衡、故障转移、质量对比和成本优化。对于开发者而言,这意味着不再需要为每个翻译API编写不同的集成代码;对于最终用户或内容运营者,这意味着更稳定、更高质量的翻译输出。

我自己在内容创作和技术文档维护中,经常需要处理多语言文本。依赖单一服务商的风险和局限性让我吃了不少亏,这也是驱动我深入研究和实践这个插件项目的直接原因。接下来,我会详细拆解这个项目的设计思路、核心实现、实操要点以及那些只有踩过坑才知道的经验。

2. 核心设计思路与架构拆解

2.1 为什么需要“多供应商”?

在深入代码之前,我们先聊聊设计哲学。为什么“多供应商”架构在今天变得如此重要?这背后有几个核心驱动力。

首先是 服务稳定性与风险分散 。没有任何一家云服务商能保证100%的可用性。区域性的网络波动、服务端临时维护、甚至偶发的API故障都可能让你的翻译流程瞬间中断。将鸡蛋放在多个篮子里,当A供应商不可用时,插件能自动、无缝地切换到B供应商,保障核心业务的连续性。这种设计对于自动化程度高、对延迟敏感的生产环境至关重要。

其次是 翻译质量与场景适配 。机器翻译的质量并非绝对,它高度依赖于训练数据、算法模型和具体的语言对。例如,在翻译技术文档时,某个引擎可能因为拥有更多相关领域的语料而表现更佳;而在处理口语化、带有文化背景的文本时,另一个引擎可能更擅长。多供应商架构允许你根据文本内容、语言对甚至成本预算,动态选择或组合最合适的翻译引擎,从而实现整体翻译质量的最优化。

再者是 成本控制与策略灵活 。不同翻译服务商的定价策略差异很大。有的按字符数计费,有的提供免费额度,有的对批量翻译有优惠。通过一个统一的插件进行管理,你可以更容易地实施成本控制策略,比如优先使用免费额度内的服务商,或者将非关键内容的翻译路由到成本更低的供应商。

最后是 避免供应商锁定 。技术选型最怕的就是被单一供应商“绑架”,导致后续迁移成本高昂。一个良好的多供应商插件,其接口设计应该是与具体服务商解耦的。这意味着,未来有新的、更好的翻译服务出现时,你只需要为插件增加一个新的适配器(Adapter),而不需要重构整个调用翻译服务的业务逻辑。这为技术栈的长期演进留足了空间。

2.2 插件核心架构:适配器模式与策略模式的应用

这个项目的架构精髓在于经典设计模式的巧妙运用,主要是 适配器模式(Adapter Pattern) 策略模式(Strategy Pattern)

适配器模式 在这里扮演了“转换头”的角色。每个翻译服务商(如Google Cloud Translation, Microsoft Azure Translator, Baidu Translate, DeepL等)都有自己独特的API接口、认证方式(API Key、OAuth等)、请求参数格式和响应数据结构。如果让业务代码直接面对这些差异,代码会变得臃肿且难以维护。适配器的任务就是将这些五花八门的接口,统一转换成插件内部定义的一套标准翻译接口。例如,插件内部可能定义一个统一的 translate(text, source_lang, target_lang) 方法,而Google适配器的职责就是接收这些标准参数,将其组装成Google API要求的JSON格式,发送请求,然后再将Google返回的复杂JSON解析,提取出翻译文本,以插件标准格式返回。

策略模式 则负责管理“用哪个适配器”的问题。插件需要根据配置或运行时条件,动态选择使用哪一个翻译服务。这个“选择算法”本身就是一种策略。简单的策略可以是轮询(Round Robin)、随机选择,或者根据配置的优先级顺序使用。更复杂的策略可以基于健康检查(哪个服务响应快)、成本(哪个服务更便宜)、或质量评分(历史翻译满意度)来动态决策。策略模式使得算法的变化独立于使用算法的客户端(即插件的主逻辑),新增一种路由策略就像新增一个适配器一样方便。

在实际架构中,通常会有一个核心的 TranslationManager MTClient 类。它持有所有已配置的适配器实例和一个路由策略实例。当收到翻译请求时, TranslationManager 询问路由策略:“这次用谁?”策略返回一个适配器实例, Manager 则调用该适配器的统一翻译方法。整个过程对请求方完全透明,它只知道发出了一个翻译请求并收到了结果,至于背后是谷歌还是百度干的,它无需关心。

这种架构带来了极高的可扩展性。增加一个新的翻译服务?只需实现对应的适配器类,并在配置中注册。想换一种负载均衡算法?只需实现新的策略类并修改配置。核心的业务调用代码一行都不用改。

2.3 配置驱动的服务管理

一个实用的多供应商插件必须是高度可配置的。硬编码的服务商密钥和策略会让插件失去灵活性。通常,我们会采用外部配置文件(如YAML、JSON或环境变量)来管理所有供应商的元数据。

一个典型的配置结构可能如下所示(以YAML示例):

translation_services:
  google:
    enabled: true
    credentials_path: “/path/to/google-key.json”
    default_project_id: “your-project-id”
    # 可选:自定义端点,用于私有化部署
    # endpoint: “https://private.example.com”
    priority: 1
    cost_per_million_char: 20.0 # 假设成本数据,用于策略

  azure:
    enabled: true
    key: “${AZURE_TRANSLATOR_KEY}” # 支持从环境变量读取
    region: “eastus”
    priority: 2

  baidu:
    enabled: true
    app_id: “your_app_id”
    secret_key: “your_secret_key”
    priority: 3

  deepl:
    enabled: false # 暂时禁用
    auth_key: “your_deepl_key”
    plan: “free” # 或 “pro”

routing_strategy: “priority_based” # 或 “round_robin”, “random”, “fallback”
fallback_order: [“google”, “azure”, “baidu”] # 故障转移顺序

配置驱动的好处显而易见:

  1. 环境隔离 :开发、测试、生产环境可以使用不同的配置(尤其是密钥),无需修改代码。
  2. 动态调整 :可以通过热重载配置(部分插件支持)或重启服务来启用/禁用某个供应商、调整优先级,实现运行时管理。
  3. 安全 :敏感的API密钥可以存储在环境变量或专门的密钥管理服务中,而不是明文写在配置文件里。

插件在初始化时,会读取这份配置,根据 enabled 标志和提供的凭证,动态实例化各个适配器,并按照配置初始化路由策略。如果某个服务的凭证无效或初始化失败,插件可以记录错误并自动将其标记为“不可用”,避免后续请求继续尝试导致延迟。

3. 核心实现细节与关键技术点

3.1 统一数据模型的设计

在聚合不同服务时,最大的挑战之一就是数据模型的统一。各家API返回的数据结构丰富度不一。有的只返回翻译文本,有的还返回源语言检测置信度、音译结果、词典释义等。

插件内部需要定义一套精简但足够通用的核心数据模型(DTO, Data Transfer Object)。至少应包括:

# 示例,使用Python的dataclass
from dataclasses import dataclass
from typing import Optional, List

@dataclass
class TranslationRequest:
    text: str
    source_language: Optional[str] = None  # 可选,为空时自动检测
    target_language: str
    # 可扩展字段:格式(html/text)、术语表ID、自定义模型等
    options: Optional[dict] = None

@dataclass
class TranslationResult:
    translated_text: str
    source_language: Optional[str] = None  # 如果请求时未指定,这里应返回检测到的语言
    detected_source_language_confidence: Optional[float] = None  # 语言检测置信度
    raw_response: Optional[dict] = None  # 原始API响应,用于调试或高级处理
    service_used: str  # 标识本次翻译由哪个供应商执行
    # 可扩展字段:音译、词典、备选翻译等

为什么需要 raw_response 这是一个重要的设计考量。虽然我们追求统一接口,但有时高级用户或后续处理可能需要访问某个服务商返回的独特信息。例如,某些引擎会返回句子级别的对齐信息或术语匹配情况。将这些信息封装在 raw_response 中,既保证了通用接口的简洁性,又为特殊需求提供了后门。不过,在插件的主要流程中,应只使用 translated_text 等标准化字段。

语言代码的标准化 是另一个细节点。谷歌用 “zh-CN” , 微软用 “zh-Hans” , 百度用 “zh” 。插件内部必须维护一个 语言代码映射表 ,将内部使用的标准代码(如遵循ISO 639-1和ISO 3166-1的 “zh-CN” )在请求时转换为对应服务商要求的格式,并在响应时转换回来。这个映射表应该是可配置的,以应对服务商更新其代码体系。

3.2 适配器(Adapter)的实现要点

实现一个健壮的适配器,远不止是简单的HTTP请求封装。以下是几个关键要点:

1. 认证与请求构造: 每个服务商的认证机制都不同。谷歌常用服务账户JSON文件或API密钥;微软Azure使用订阅密钥和区域;百度使用AppID和密钥通过MD5生成签名;DeepL使用授权密钥。适配器需要妥善处理这些凭证的加载、缓存和刷新(特别是对于有时效性的OAuth Token)。请求的构造也要严格遵循官方文档,包括正确的HTTP方法(GET/POST)、请求头(如 Content-Type Authorization )和请求体。

2. 错误处理与重试机制: 网络请求必然伴随失败。适配器必须实现完善的错误处理。这包括:

  • 区分错误类型 :是网络超时、认证失败(401/403)、额度不足(429)、还是服务端错误(5xx)?不同的错误类型对应不同的处理策略。例如,认证失败可能意味着配置错误,不应重试;而网络超时或服务端临时错误(503)则适合重试。
  • 实现指数退避重试 :对于可重试的错误,简单的立即重试可能会加剧服务端压力。标准的做法是指数退避(Exponential Backoff),即第一次重试等待1秒,第二次2秒,第三次4秒,以此类推,并设置最大重试次数上限。
  • 提供友好的错误信息 :将原始的、可能很晦涩的API错误信息,转换为插件统一的、用户友好的异常或错误码,便于上游业务逻辑处理。

3. 请求速率限制(Rate Limiting)与配额管理: 所有云服务都有调用频率限制。适配器需要内置请求限流器,确保不会在短时间内触发服务商的限流策略。这可以通过令牌桶(Token Bucket)或漏桶(Leaky Bucket)算法实现。更高级的插件还可以跟踪每个服务商的月度字符配额使用情况,并在接近限额时发出警告或自动切换到其他服务商。

4. 连接池与超时设置: 为每个适配器配置独立的HTTP连接池(如使用 requests.Session aiohttp.ClientSession )可以显著提升频繁调用时的性能。同时,必须设置合理的连接超时(Connection Timeout)和读取超时(Read Timeout)。超时时间不宜过短(导致不必要的失败),也不宜过长(导致线程/进程阻塞)。通常,连接超时设为3-5秒,读取超时设为10-30秒是比较通用的起点,可根据实际网络状况调整。

3.3 路由策略(Routing Strategy)的实现

路由策略是插件智能化的体现。以下是几种常见策略的实现思路:

1. 优先级策略(Priority-based): 最简单直接的策略。按照配置文件中定义的优先级顺序(如 [google, azure, baidu] )依次尝试。只有当前一个服务失败(如网络错误、认证失败)时,才会尝试下一个。这种策略适合有明确服务商偏好和成本考虑的场景。

2. 轮询策略(Round Robin): 平等对待所有启用的服务商,依次循环使用。这可以粗略地实现负载均衡,避免所有请求压到某一个服务上。实现时需要注意线程安全,对服务商列表的索引访问需要加锁或使用原子操作。

3. 随机策略(Random): 每次请求随机选择一个服务商。这也是负载均衡的一种形式,但结果不可预测。

4. 基于健康检查的权重策略(Weighted based on Health Check): 这是更高级的策略。插件定期(例如每30秒)向每个服务商发送一个轻量级的探测请求(比如翻译一个短句 “hello”)。根据响应时间和成功率,动态计算每个服务商的健康分数或权重。处理真实翻译请求时,按权重概率选择服务商。响应快、成功率高的服务商会获得更高的权重,从而承接更多流量。这种策略能自动规避临时性能下降的服务节点。

5. 成本最优策略(Cost-optimal): 在配置中定义每个服务商的翻译单价(如每百万字符的费用)。当请求的文本长度已知时,插件可以估算本次翻译的成本,并选择当前成本最低的可用服务商。这对于控制预算非常有效。

在实际项目中,这些策略可以组合使用。例如,主策略是“优先级策略”,但为每个优先级内的服务商配置“基于健康检查的权重”子策略。策略类本身也应设计为可插拔的,通过配置即可切换。

3.4 异步支持与性能考量

在现代应用开发中,异步(Async/Await)编程模型对于I/O密集型操作(如网络请求)至关重要。一个优秀的MT插件应该提供异步API。

同步与异步接口并存: 插件可以同时提供 translate() translate_async() 方法,以满足不同调用方的需求。底层实现上,异步适配器应使用像 aiohttp 这样的异步HTTP客户端库,避免阻塞事件循环。

批量翻译优化: 很多翻译API支持批量翻译,即一次请求传入多段文本。这比逐段翻译能大幅减少网络往返开销和可能存在的费率限制计数。插件应暴露一个 translate_batch() 方法,内部根据服务商的能力,将文本列表智能地打包成符合API格式的批量请求。对于不支持批量或批量有限制的服务商,插件需要实现并发请求(利用 asyncio.gather 或线程池)来模拟批量效果,提升整体吞吐量。

缓存机制: 翻译请求经常存在重复,特别是对于产品界面固定的UI文字、错误信息等。引入缓存层(如内存缓存 functools.lru_cache 或外部Redis)可以极大提升响应速度并节省API调用成本。缓存键的设计需要包含文本内容、源语言和目标语言。需要注意的是,缓存应有失效策略(TTL),并且对于付费服务,要谨慎评估缓存是否违反服务条款。

4. 插件集成与实操指南

4.1 环境准备与安装

假设这是一个Python插件(从项目名推测),我们来看看如何将其集成到你的项目中。

首先,安装依赖。除了插件本身,你还需要安装其依赖的HTTP客户端和可能用到的服务商SDK。一个良好的 setup.py requirements.txt 应该已经声明了这些依赖。

# 假设插件已发布到PyPI
pip install multi-supplier-mt-plugin

# 或者从源码安装
git clone https://github.com/JuchiaLu/Multi-Supplier-MT-Plugin.git
cd Multi-Supplier-MT-Plugin
pip install -e .

接下来,准备配置文件。我强烈推荐使用YAML格式,因为它结构清晰,支持注释。在项目根目录创建一个 config/translation.yaml 文件,内容参考上一节的示例。 重中之重是管理好你的API密钥。 绝对不要将它们提交到版本控制系统(如Git)中。有几种安全做法:

  • 环境变量 :在配置文件中使用 ${VAR_NAME} 占位符,在运行环境(服务器、容器)中设置这些变量。
  • 密钥管理服务 :如AWS Secrets Manager, HashiCorp Vault,在应用启动时动态拉取并注入到配置中。
  • 单独的密钥文件 :将密钥存放在一个不被版本控制的文件中(如 secrets.yaml ),并通过 !include 指令(如果使用某些YAML库)或程序逻辑将其合并到主配置中。

4.2 初始化与基本使用

在代码中初始化和使用插件通常很简单。

import yaml
from pathlib import Path
from multi_supplier_mt_plugin import TranslationManager, PriorityRoutingStrategy

# 1. 加载配置
config_path = Path(“config/translation.yaml”)
with open(config_path, ‘r’, encoding=‘utf-8’) as f:
    config = yaml.safe_load(f)

# 2. 初始化翻译管理器
# 插件可能会提供一个便捷的工厂方法,如 `from_config`
try:
    translator = TranslationManager.from_config(config)
except Exception as e:
    # 处理初始化错误,如密钥无效、网络不可达等
    print(f“Failed to initialize translator: {e}”)
    # 可以降级为使用本地词典或返回原文
    translator = None

# 3. 进行翻译
async def translate_demo():
    if translator is None:
        return “Translation service unavailable.”

    request_text = “这是一个测试句子,用于验证多供应商翻译插件的工作情况。”
    try:
        # 使用异步接口
        result = await translator.translate_async(
            text=request_text,
            target_language=“en”
            # source_language 不指定,自动检测
        )
        print(f“Translated: {result.translated_text}”)
        print(f“Detected source language: {result.source_language}”)
        print(f“Service used: {result.service_used}”)
        return result.translated_text
    except Exception as e:
        # 处理翻译过程中的异常,如所有服务商都失败
        print(f“Translation failed: {e}”)
        # 可以考虑重试、记录日志、或返回兜底结果
        return request_text  # 返回原文作为兜底

# 如果是脚本,运行异步函数
import asyncio
asyncio.run(translate_demo())

初始化最佳实践:

  • TranslationManager 实例作为单例或应用级全局对象创建。避免为每个请求都重新初始化和建立连接,这很耗时。
  • 在Web应用(如Flask、Django、FastAPI)中,可以在应用启动时初始化,并将其存储在应用上下文或依赖注入容器中。
  • 考虑实现一个“健康检查”端点,在初始化后对配置的所有服务商进行快速探测,确保它们基本可用,并在管理界面上报告状态。

4.3 高级功能与定制化

基础翻译满足大部分需求,但插件通常还提供一些高级功能。

术语表/词汇表支持: 专业领域翻译往往有特定的术语要求。许多云翻译服务(如谷歌、微软、Azure)支持自定义术语表。插件可以抽象此功能。你可以在配置中为某个服务商指定术语表ID,或者在请求的 options 参数中传入一个自定义的 {“术语”: “标准译法”} 字典。适配器在构造请求时,需要将这些术语信息以服务商要求的方式(如作为请求参数,或预先上传到云端术语库并引用其ID)加入进去。

模型选择: 一些服务商提供不同的翻译模型,例如通用模型、新闻模型、专利模型等。插件可以通过 options 参数暴露模型选择能力。

自定义路由策略: 如果内置的策略不满足你的需求,你可以实现自己的策略类。通常只需要继承一个基础的 BaseRoutingStrategy 类,实现 select_service(self, request, available_services) 方法即可。这个方法接收翻译请求和当前可用的服务列表,返回一个选中的服务适配器实例。然后将你的策略类名配置到 routing_strategy 字段(可能需要写全路径如 my_module.MyCustomStrategy ),插件在初始化时会通过反射动态加载。

请求/响应钩子(Hooks): 一个设计良好的插件会提供钩子机制,允许你在请求发出前和收到响应后插入自定义逻辑。例如:

  • 请求前 :日志记录、文本预处理(如清理HTML标签、处理特殊字符)、注入自定义参数。
  • 响应后 :日志记录、响应后处理(如统一标点格式、修复翻译中常见的错误)、缓存写入。 钩子机制极大地增强了插件的灵活性。

5. 生产环境部署与运维要点

5.1 监控与可观测性

将插件用于生产环境,必须建立完善的监控体系。关键监控指标包括:

  • 请求量 :总请求量、各服务商请求量、各语言对请求量。这有助于了解使用模式和进行成本分析。
  • 延迟 :各服务商翻译请求的P50、P95、P99延迟。延迟突增往往是服务商或网络出现问题的早期信号。
  • 成功率 :各服务商请求的成功率(HTTP 2xx/3xx 响应)。这是服务健康度的核心指标。
  • 错误类型分布 :认证错误、限流错误、网络错误、服务端错误各自的数量。这有助于快速定位问题根因。
  • 字符数统计 :翻译的总字符数,用于核对服务商账单和成本控制。

实现上,可以在适配器的请求/响应关键路径上埋点,将数据发送到监控系统(如Prometheus、StatsD)和日志系统(如ELK Stack)。插件本身也可以提供一个简单的状态端点,返回当前各适配器的健康状态、最近错误和基本统计信息。

5.2 故障排查与降级方案

即使有多重保障,故障仍可能发生。你需要有清晰的排查流程和降级方案。

常见问题排查清单:

  1. 所有翻译都失败

    • 检查网络连通性(是否能访问外部翻译API域名)。
    • 检查API密钥是否过期或被撤销。
    • 检查配置文件格式是否正确,特别是YAML缩进。
    • 查看插件日志,通常会有详细的错误信息。
  2. 某个特定服务商持续失败

    • 登录该服务商的控制台,检查配额和用量是否已用尽。
    • 检查该服务商在特定区域是否有已知的服务中断公告。
    • 验证该服务商的API端点(Endpoint)是否在配置中正确指定,特别是如果你使用了私有化部署版本。
  3. 翻译速度突然变慢

    • 检查监控指标,看是否是某个服务商延迟增高导致的。
    • 检查应用服务器或容器的资源使用率(CPU、内存、网络)。
    • 考虑是否触发了服务商的速率限制,导致请求被延迟处理。

降级方案设计:

  • 初级降级 :当主服务商失败时,按配置的 fallback_order 自动切换到备用服务商。这是插件内置的核心能力。
  • 中级降级 :如果所有远程服务商都不可用,可以切换到本地轻量级翻译库(如使用 translate 这样的开源库,或简单的词典映射)。虽然质量差很多,但能保证基本功能不中断。
  • 终极降级 :如果连本地库都无法工作,至少应该返回原文,并在日志中高亮错误,而不是让应用抛出异常导致崩溃。对于UI文本,甚至可以返回一个友好的占位符,如 “[Translation Pending]”。

5.3 成本优化与预算控制

使用多个付费翻译服务,成本管理变得重要。

  1. 精细化配置 :在配置中为每个服务商设置月度预算上限或字符数上限。插件可以定期(例如每天)统计用量,当接近某个服务商的限额时,在日志中告警,并自动调低其优先级或禁用它。
  2. 智能路由结合成本 :实现或使用“成本最优”路由策略。对于非关键、大批量的翻译任务(如用户生成内容的初步翻译),优先路由到成本最低的服务商。
  3. 利用免费额度 :像谷歌云、微软Azure等对新用户或某些产品都有免费额度。可以创建多个项目/订阅,在免费额度内轮换使用,但这需要更复杂的凭证管理和切换逻辑,需谨慎评估合规性。
  4. 缓存是最大的省钱利器 :如前所述,对重复内容进行缓存,能直接减少API调用次数。评估你的文本重复率,设置合理的缓存大小和TTL。
  5. 定期审计账单 :将插件统计的字符用量与服务商后台的账单进行比对,确保数据一致,并分析用量趋势,优化资源配置。

6. 扩展思路与未来演进

一个开源项目的生命力在于其可扩展性。围绕这个多供应商翻译插件,有很多值得探索的扩展方向。

更多服务商适配 :除了主流云服务商,还可以集成一些优秀的开源或小众翻译引擎,例如:

  • 本地化部署的模型 :集成 argos-translate , Bergamot (Firefox的本地翻译引擎) 或 Hugging Face 上的优质翻译模型。这为数据敏感或网络隔离的环境提供了可能。
  • 领域专用引擎 :寻找在医学、法律、金融等垂直领域有专长的翻译服务。

质量评估与自动优选 :可以引入翻译质量自动评估(QE)模块。例如,同时将一段文本发给两个服务商翻译,利用一个轻量级的QE模型(或基于规则的方法,如检查数字、日期、专有名词是否一致)对两个结果进行评分,选择评分高的一个返回,甚至将两者进行融合。这能让插件从“可用”走向“智能”。

工作流集成 :开发与常用工具链的集成插件,例如:

  • 命令行工具(CLI) :方便在脚本或终端中快速翻译文本或文件。
  • 代码编辑器插件 :为VS Code、IntelliJ IDEA等添加实时注释翻译功能。
  • 浏览器扩展 :划词翻译,或整页翻译。
  • 与CI/CD管道集成 :自动翻译项目中的国际化(i18n)资源文件。

社区与生态 :建立清晰的贡献指南,鼓励社区贡献新的适配器、路由策略和工具集成。定义完善的插件接口标准,使其真正成为一个生态的核心。

这个项目的价值远不止于代码本身。它体现的是一种解耦、聚合和面向失败的设计思想。在构建依赖外部服务的系统时,这种思路能极大地提升系统的韧性、灵活性和可维护性。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐