我为 HarmonyOS 做了一个统一大模型 SDK:@hmkit/ai 正式开源

我为 HarmonyOS 做了一个统一大模型 SDK:@hmkit/ai 正式开源

推荐摘要:@hmkit/ai 是一个面向 HarmonyOS NEXT / OpenHarmony 的统一大模型 SDK 与 ArkUI V2 聊天组件,支持多供应商、多模态、Responses API、Agent、RAG、语音和本地会话。目前项目已以 MIT 协议开源。

推荐标签:HarmonyOS、ArkTS、人工智能、开源、OpenAI

这两年,大模型能力已经从"聊天接口"逐渐变成应用的基础能力。

但当我尝试在 HarmonyOS 应用中真正接入 AI 时,很快就发现:调用一次 HTTP 接口并不难,难的是把流式响应、多供应商差异、多模态消息、工具调用、会话存储、RAG、语音和 UI 体验组合成一套可以长期维护的工程。

于是我做了 @hmkit/ai

它是一个面向 HarmonyOS NEXT / OpenHarmony 的统一大模型 SDK,同时提供可直接使用的 ArkUI V2 聊天组件。目前 2.0.0 已经开源:

为什么要做统一调用层

不同大模型供应商看起来都提供"OpenAI 兼容接口",但真正接入后仍然有不少差异:

  • Endpoint、模型名称和鉴权方式不同;
  • 支持的图片、文件、音频能力不同;
  • SSE 事件和结束标记可能存在细微差异;
  • Chat Completions 与 Responses API 的工具定义并不相同;
  • 流式工具参数可能被拆成多个不完整 JSON 片段;
  • 客户端还要处理取消、超时、错误归一化和生命周期清理。

如果业务页面直接依赖某一家供应商的请求结构,后面切换模型或增加 Provider 时,UI、会话和业务逻辑往往都要跟着修改。

@hmkit/ai 的思路是把这些问题拆成几个相互独立的层:

text 复制代码
ArkUI Chat View
      ↓
Conversation / Agent Runtime
      ↓
Unified Message & Tool Types
      ↓
Chat Completions / Responses / Voice
      ↓
Provider Configuration
      ↓
HarmonyOS HTTP / WebSocket / Audio

页面只处理消息、状态和用户操作;协议层负责请求映射和流式解析;Provider 层描述服务地址、模型与能力;平台层负责 HarmonyOS 网络、文件和音频能力。

从最基础的流式聊天开始

库中统一使用 HmAIMessage 表示消息,通过 HmAIConversationController 管理多轮对话、流式状态、取消和错误。

以 DeepSeek 为例:

ets 复制代码
import {
  HmAIConversationController,
  HmAIMessage,
  HmAIProviders
} from '@hmkit/ai';

const provider = HmAIProviders.deepSeek('deepseek-chat');
const client = provider.createChatClient(temporaryApiKey);
const controller = new HmAIConversationController(client);

await controller.sendMessage(
  HmAIMessage.user('请帮我整理今天的工作重点')
);

当前内置了以下 Provider 预设:

  • OpenAI
  • DeepSeek
  • 通义千问
  • 豆包
  • 智谱
  • 自定义 OpenAI-compatible 服务

Provider 不只是保存一个 URL,还会声明图片、文件、音频、工具调用和 structured output 等能力。应用可以在发送前检查能力,避免把不支持的内容交给模型。

多模态不是给消息加一个图片地址

多模态消息需要同时面对远程 URL、本地文件、Data URL、MIME 类型、大小限制和供应商序列化差异。

@hmkit/ai 使用 HmAIContentPart 统一描述文本、图片、文件和音频:

ets 复制代码
const message = HmAIMessage.userParts([
  HmAIContentPart.text('请分析这张图片'),
  HmAIContentPart.imageUrl('https://example.com/chart.png')
]);

对于本地附件,库提供需要显式传入 UIAbilityContext 的选择器和读取工具。这样做是为了避免 HAR 在内部偷偷持有全局 Context,也让宿主应用能够决定权限、大小限制和文件生命周期。

同时支持 Chat Completions 与 Responses API

OpenAI 新一代 Responses API 将输入、输出、工具和响应状态组合成了新的数据模型,它并不是简单换一个请求路径。

例如 function tool 在 Responses API 中采用更扁平的结构,流式响应也会出现具名事件:

text 复制代码
response.output_text.delta
response.function_call_arguments.delta
response.output_item.done
response.completed

因此项目为两个协议保留了各自的编码器和流式解码器,同时共享消息、工具和错误类型。

ets 复制代码
const options = new OpenAIResponsesClientOptions(
  'https://api.openai.com/v1',
  'gpt-5'
);
options.apiKey = temporaryApiKey;

const client = new OpenAIResponsesClient(options);
const request = new HmAIResponsesRequest([
  HmAIMessage.user('总结这份附件')
]);

request.previousResponseId = previousId;
const result = await client.createResponseStream(request).start();

目前支持文本、图片、文件输入,function tools、tool results、JSON Schema、previous_response_id、引用来源和 usage。

Agent 最重要的能力不是"自动",而是可控制

让模型调用工具很容易,但如果工具涉及文件、网络、支付或系统能力,"模型想调用就执行"并不是一个适合生产环境的默认值。

项目中的 Agent Runtime 默认要求人工审批,并记录每一步的状态:

ets 复制代码
const agentOptions = new HmAIAgentRuntimeOptions();
agentOptions.approvalPolicy = new HmAIAlwaysAskPolicy();

const repository = new HmAIFileAgentRepository(
  filesDir + '/agent-tasks'
);

const runtime = new HmAIAgentRuntime(
  client,
  registry,
  repository,
  observer,
  agentOptions
);

const task = await runtime.start('查询天气并整理结果');

Agent 只允许调用 HmAIToolRegistry 中明确注册的工具。运行中的任务可以:

  • 等待批准或拒绝;
  • 展示当前步骤状态;
  • 主动中断;
  • 从持久化任务恢复;
  • 通过最大轮次限制阻止无限循环。

这套设计更适合需要审计和用户确认的真实应用,而不仅是一个自动调用工具的演示。

本地会话和轻量 RAG

除了模型调用,项目也提供了本地会话存储:

  • 历史记录;
  • 搜索;
  • 导入和导出;
  • 删除与恢复;
  • 内存或应用沙箱文件存储。

RAG 部分包含文档分块、Embedding 抽象、本地向量索引、相似度检索和引用来源。

ets 复制代码
const rag = new HmAIRAGEngine();

await rag.ingest(new HmAIRAGDocument(
  'doc-1',
  '产品手册',
  documentText,
  sourceUrl
));

const sources = await rag.retrieve('如何取消流式响应?', 4);
const context = await rag.context('如何取消流式响应?', 4);

内置的 HmAIHashEmbeddingModel 完全离线,适合 Demo、测试和轻量检索。正式语义检索可以实现 HmAIEmbeddingModel,替换为自己的 Embedding 服务。

不只做 SDK,也提供可商用的 ArkUI 组件

最初的 Demo 更像一个开发者调试页面:模型配置占据首屏,历史记录和聊天区域缺少产品层级。

后来我重新设计了整个界面:

  • 使用克制的靛蓝色作为品牌色;
  • 技术配置默认收起;
  • 增加品牌化欢迎页和建议问题;
  • 助手内容采用编辑式排版,减少沉重气泡;
  • 输入区改成悬浮圆角 Composer;
  • 附件、错误、停止和重新生成都有独立状态;
  • 支持 light/dark 主题和 phone/tablet 布局。

配图建议:此处插入 Pura 90 模拟器首屏截图。

组件的使用方式保持简单:

ets 复制代码
HmAIChatView({
  messages: this.messages,
  state: this.state,
  emptyText: '今天想一起完成什么?',
  suggestions: [
    '整理工作重点',
    '分析一份文档',
    '制定执行计划'
  ],
  pendingAttachments: this.attachments.map(
    (item): string => item.name
  ),
  showAttachmentButton: true,
  onSend: (content: string): void => {
    this.send(content);
  },
  onSuggestion: (content: string): void => {
    this.send(content);
  },
  onAttachmentRequested: (): void => {
    this.pickFiles();
  },
  onCancel: (): void => {
    this.controller.cancel();
  }
})

Markdown 渲染支持标题、引用、列表、表格、链接、行内代码、公式和轻量语法高亮。长会话使用 ArkUI List 与可配置的消息窗口,并通过流式批处理降低高频刷新开销。

语音和实时对话

语音部分拆分为三个层次:

  • OpenAIVoiceClient:语音转写和流式 TTS;
  • HmAIMicrophoneCaptureHmAIPCMStreamPlayer:HarmonyOS 音频采集和播放;
  • OpenAIRealtimeSession:WebSocket 实时文本、音频和增量事件。

库不会替宿主静默申请麦克风权限。权限说明、动态授权、前后台生命周期、音频格式和临时令牌仍由应用负责。

如何保证它不只是"能跑"

流式协议最容易出现的问题,是网络分片并不会按照 JSON 或 UTF-8 字符边界到达。

测试中特别覆盖了:

  • SSE 的任意分片、CRLF 和 [DONE]
  • 多字节 UTF-8 被拆分;
  • 工具调用 ID、函数名和参数分片累积;
  • Chat Completions、Responses 和 Audio Mock 协议;
  • Agent 审批、拒绝、中断和恢复;
  • 多模态序列化;
  • Markdown、会话存储和 RAG;
  • 模拟器输入、发送、流式渲染和附件入口。

当前发布门禁包括:

  • 33 项 Hypium 单元测试全部通过;
  • 123 个公开 API 符号契约检查;
  • Chat、Responses、Audio Mock 服务测试;
  • HAR 版本、元数据和凭据扫描;
  • Pura 90 模拟器 UI 自动化;
  • GitHub Actions CI。

项目提供统一验证命令:

shell 复制代码
ohpm install --all
./scripts/verify.sh

连接模拟器或真机后可以运行:

shell 复制代码
./scripts/test-ui.sh

客户端 API Key 的安全边界

这一点非常重要:不要把生产环境的大模型 API Key 写进 HarmonyOS 客户端。

无论做多少混淆,随 App 分发的长期静态密钥都无法得到可靠保护。正式应用应该通过自己的服务端代理保存供应商密钥,再向客户端签发短期、最小权限的会话凭据。

Demo 中提供的 API Key 输入只用于本机调试:

  • 不持久化;
  • 不写日志;
  • 页面退出时清空;
  • 不进入开源仓库和构建产物。

会话、附件、Agent 任务和 RAG 向量也可能包含敏感信息,宿主需要自行决定沙箱存储、加密、备份和数据保留策略。

开源之后准备做什么

@hmkit/ai 当前以 MIT 协议开源。接下来会继续关注:

  • 更多供应商和模型的兼容性反馈;
  • 更完整的真实设备语音测试;
  • RAG 文档 Loader 与 Embedding 适配;
  • Agent 工具生态和审批 UI;
  • ArkUI 组件的主题与扩展能力;
  • OHPM 发布和版本维护。

如果你正在做 HarmonyOS AI 应用,欢迎试用、提交 Issue 或参与贡献。

项目地址:

github.com/lxshwyan/hm...

如果这个项目对你有帮助,也欢迎点一个 Star。


发布前可选优化

以下内容用于发布准备,可在复制到掘金时删除:

  1. 在"ArkUI 组件"章节插入 Pura 90 商用首屏截图。
  2. 封面标题建议使用:HarmonyOS 也有统一 AI SDK 了
  3. 封面副标题建议使用:多模型 / Agent / RAG / 语音 / ArkUI
  4. 掘金标题备选:
    • 我开源了一个 HarmonyOS 统一大模型 SDK:支持 Agent、RAG 与多模态
    • 从 SSE 到 Agent:如何为 HarmonyOS 设计一套 AI SDK
    • @hmkit/ai 2.0 开源:让 HarmonyOS 应用快速接入大模型
相关推荐
猪是念来过倒1 小时前
Semaphore 与 RateLimiter:并发控制双雄详解
后端
掘金者阿豪1 小时前
异构数据同步最怕什么?不是同步慢,而是数据对不上
后端
程序员cxuan2 小时前
Claude 官方的学习教程,太强了。
人工智能·后端·程序员
老孙讲技术2 小时前
把工地遮挡和掉线接进项目部:setMessageCallback 订 alarm 与 deviceStatus
后端·物联网·音视频开发
老孙讲技术2 小时前
关店后有人进门,监控值班群却没响:用 setMessageCallback 订 human,再用 aiHuman 打开人形
后端·物联网·音视频开发
LEE2 小时前
原来 Claude Code 最厉害的工具,一行代码都不写
前端·后端
老孙讲技术2 小时前
把园区几百路摄像头收进值班台账:bindDevice 入账与 listDeviceDetailsByPage 翻页
后端·物联网·音视频开发
用户7813667114452 小时前
emplace_back vs push_back 详解
后端
老孙讲技术2 小时前
把没网没电的户外点位接进出画值班台:unBindDeviceInfo 核 SIMCard 与 bindDeviceLive
后端·物联网·音视频开发