恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
【共创稿事节】HarmonyOS 7 Agent 开发实战 01:云端大模型 API 接入底座
首页
资讯中心
/
【共创稿事节】HarmonyOS 7 Agent 开发实战 01:云端大模型 API 接入底座
【共创稿事节】HarmonyOS 7 Agent 开发实战 01:云端大模型 API 接入底座
发布时间:2026/10/14 3:32:56
做 Agent 开发第一步不是急着写提示词而是先把大模型接入底座打牢。很多团队在 HarmonyOS 7 上接云端大模型时都会在同一批坑里反复栽跟头鉴权头写错导致 401、没设超时界面直接卡死、遇到 429 限流不重试直接失败、各家响应格式不统一解析散落一地。底座不稳上层 Agent 再加花哨也随时崩。本系列《HarmonyOS 7 Agent 开发实战》全部基于一个共识整系列的 LLM 底座统一走云端大模型 API华为云盘古 / OpenAI 兼容网关不再依赖设备端侧模型分发通道。原因很现实——端侧模型下载入口在部分设备上尚未开放而云端 API 只要有网就能在真机或模拟器上实测纯逻辑还能脱离设备跑单测符合案例能跑通的硬要求。本文用一个可直接打开运行的 DevEco 工程把云端大模型接入收敛成一套统一抽象盘古和 OpenAI 兼容网关共用一个LlmClient内置超时、指数退避重试、JSON 解析业务侧只管拿文本。所有代码基于 HarmonyOS 7API 26的ohos.net.http并配了可脱离设备运行的 Hypium 单测。一、整体架构把不确定性收进一层抽象接入云端大模型真正要解决的不是发一个 POST而是把三件麻烦事管住鉴权差异、失败重试、响应解析。如果让每个业务页面自己拼请求代码会迅速腐化。整体分三层业务 / Agent 层只管组织messages、拿resp.text不关心背后是盘古还是 OpenAI。统一客户端层LlmClient抽象出chat()把超时 重试 解析三件事收口复用。具体实现层PanguClient与OpenAiClient两个子类只写它们真正不同的地方——请求地址、鉴权头、错误分类。切换厂商只改工厂一行参数业务代码零改动。这就是底座该有的样子把易变的东西关进一个可替换的盒子。二、统一消息模型先把契约定清楚任何客户端第一步是把请求和响应的形状定下来避免后面到处传any。// feature/llmclient/ChatModels.etsexportenumRole{SYSTEMsystem,USERuser,ASSISTANTassistant}exportinterfaceChatMessage{role:Role;content:string;}exportinterfaceChatRequest{model:string;messages:ChatMessage[];temperature?:number;maxTokens?:number;}exportinterfaceChatResponse{text:string;raw:string;finishReason?:string;usage?:TokenUsage;}这个模型同时被盘古和 OpenAI 复用因为两者在消息结构上高度一致——都是role content的数组。差异只在更外层的字段命名如max_tokens的下划线风格由子类在buildPayload里处理。三、重试策略纯逻辑先写可测的部分重试是最容易被写成无脑 for 循环的地方。正确的做法是把什么错误该重试、退避多久抽成纯函数脱离网络就能测。// feature/llmclient/RetryPolicy.ets节选exportenumRetryableError{NETWORKnetwork,TIMEOUTtimeout,SERVER_5XXserver_5xx,RATE_LIMITrate_limit,NON_RETRYABLEnon_retryable}exportclassRetryPolicy{readonlymaxRetries:number;readonlybaseDelayMs:number;readonlymaxDelayMs:number;constructor(maxRetries3,baseDelayMs300,maxDelayMs5000){this.maxRetriesmaxRetries;this.baseDelayMsbaseDelayMs;this.maxDelayMsmaxDelayMs;}classify(statusCode:number,errMsg?:string):RetryableError{if(statusCode0||statusCode-1)returnRetryableError.NETWORK;if(statusCode408||(errMsgerrMsg.toLowerCase().includes(timeout)))returnRetryableError.TIMEOUT;if(statusCode429)returnRetryableError.RATE_LIMIT;if(statusCode500statusCode600)returnRetryableError.SERVER_5XX;returnRetryableError.NON_RETRYABLE;}shouldRetry(attempt:number,error:RetryableError):boolean{if(errorRetryableError.NON_RETRYABLE)returnfalse;returnattemptthis.maxRetries;}calcBackoff(attempt:number):number{constexpthis.baseDelayMs*Math.pow(2,attempt);returnMath.min(exp,this.maxDelayMs);}}注意classify的设计401、400 这类客户端错误不重试重试也没用得改代码或参数只有网络抖动、超时、5xx、429 才进入重试序列。退避用指数封顶避免重试把服务端压垮。四、响应解析兼容两种格式再兜底云端网关的响应不是永远规整。OpenAI 格式是choices[0].message.content华为云盘古兼容 OpenAI 格式但部分网关会返回result、text、reply这类扁平字段。解析层先按标准格式取取不到再走兜底。// feature/llmclient/ResponseParser.ets节选staticparse(raw:string):ChatResponse{constobjJSON.parse(raw)asRecordstring,Object;constchoicesobj[choices];if(choicesArray.isArray(choices)choices.length0){constfirstchoices[0]asRecordstring,Object;constmessagefirst[message]asRecordstring,Object|undefined;consttextmessage?(message[content]asstring):;constfinishfirst[finish_reason]asstring|undefined;constusageObjobj[usage]asRecordstring,Object|undefined;// ……组装 ChatResponse含 usage 令牌统计return{text,raw,finishReason:finish,usage};}constfallback(obj[result]asstring)??(obj[text]asstring)??(obj[reply]asstring)??;return{text:fallback,raw};}把解析收敛到一处业务侧永远拿到结构化的ChatResponse不必在页面里写一堆JSON.parse加any强转。五、统一客户端差异留给子类复用收进父类LlmClient是抽象类只定义三个因厂商而异的抽象方法把公共的chat()实现好// feature/llmclient/LlmClient.ets节选exportabstractclassLlmClient{protectedconfig:LlmClientConfig;protectedretryPolicy:RetryPolicy;abstractbuildPayload(req:ChatRequest):Recordstring,Object;abstractbuildHeaders():Recordstring,string;abstractclassifyError(err:Error):RetryableError;asyncchat(req:ChatRequest):PromiseChatResponse{letattempt0;while(true){try{constrawawaitthis.sendOnce(req);returnResponseParser.parseOrThrow(raw);}catch(e){constkindthis.classifyError(easError);if(!this.retryPolicy.shouldRetry(attempt,kind))throwe;awaitthis.sleep(this.retryPolicy.calcBackoff(attempt));attempt;}}}protectedabstractsendOnce(req:ChatRequest):Promisestring;}两个子类真正不同的只有buildHeaders和sendOnce里拼的地址。盘古用X-Auth-Token或Authorization: Bearer按账号类型二选一OpenAI 用Authorization: Bearer底层都是ohos.net.http的request。// 切换只改工厂一行exportclassLlmClientFactory{staticcreate(provider:pangu|openai,config:LlmClientConfig):LlmClient{if(providerpangu)returnnewPanguClient(config);if(provideropenai)returnnewOpenAiClient(config);thrownewError(未知 provider: provider);}}六、纯逻辑单测不联网也能绿底座里最容易写错、又最该先测的是重试分类与响应解析——它们不碰网络用 Hypium 直接断言即可在模拟器上秒过。// ohosTest/ets/test/LlmClient.test.ets节选it(分类 429 为限流,0,(){expect(newRetryPolicy().classify(429)).assertEqual(RetryableError.RATE_LIMIT);});it(calcBackoff 指数且封顶,0,(){constpnewRetryPolicy(5,300,5000);expect(p.calcBackoff(0)).assertEqual(300);expect(p.calcBackoff(10)).assertEqual(5000);});it(解析 OpenAI 格式并取 usage,0,(){constrawJSON.stringify({choices:[{message:{content:你好},finish_reason:stop}],usage:{prompt_tokens:10,completion_tokens:5,total_tokens:15}});expect(ResponseParser.parseOrThrow(raw).usage?.totalTokens).assertEqual(15);});工程里共 9 条断言重试 6 条 解析 3 条全部不发起网络请求脱离设备也能验证底座逻辑的正确性。七、真机实测填三项即可对话演示页pages/Index.ets把工厂、客户端、UI 串起来。你只需把三处换成自己的配置baseUrl网关地址openai用https://…/v1/chat/completionspangu用盘古 endpointapiKey你的密钥model模型名连真机或 HarmonyOS 7 模拟器点发送即可看到云端大模型返回。chat()内部已经把超时、重试、解析全管住页面只处理resp.text。V哥真机测试截图点击发送按钮测试云端大模型请求中成功返回八、五个高频坑接入前先看一遍鉴权头写错 401盘古和 OpenAI 的鉴权头不同确认账号类型再填token 别带多余空格。超时必设ohos.net.http不显式设connectTimeout/readTimeout弱网下会一直挂起拖垮界面。重试别无脑401、400 这类错重试等于自伤只对网络/超时/5xx/429 重试。解析要兜底不要假设响应一定是 OpenAI 格式先按标准取取不到再走result等扁平字段。并发别共用同一把锁多会话并发请求时每个请求用独立的http.createHttp()实例并在finally里destroy()避免串响应。参考与出处华为云盘古大模型 API 文档对话补全 / 鉴权方式OpenAI Chat Completions API请求与响应结构HarmonyOSohos.net.http模块文档API 26request / 超时 / 错误处理最后一句底座稳了Agent 才敢往前跑把不确定性收进一层抽象剩下的复杂度才有地方安放。