news 2026/9/10 8:01:04

鸿蒙版“DeepSeek”客户端实战:基于 HarmonyOS 网络请求与 Markdown 渲染组件,手撸一个 AI 对话 App

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
鸿蒙版“DeepSeek”客户端实战:基于 HarmonyOS 网络请求与 Markdown 渲染组件,手撸一个 AI 对话 App

🚀 前言:当国产之光遇见国产之光

市面上全是 Web 版的套壳 AI,体验极其卡顿。
作为鸿蒙开发者,我们要做就做纯原生 (Native)的。
本教程将带你从零开始,使用ArkTS对接 DeepSeek 官方 API,并攻克最大的技术难点——在鸿蒙原生列表里完美渲染 Markdown 代码块


🏗️ 一、 架构设计:数据流转图

我们要实现的 App 包含三个核心层级:

  1. UI 层:基于List的聊天气泡界面。
  2. 渲染层:封装Web组件,通过loadData动态渲染 Markdown。
  3. 数据层:使用http模块发起 SSE (流式) 或普通 POST 请求。

系统架构 (Mermaid):

UI 渲染层

网络层

1. 点击发送
2. HTTP POST
3. 返回 JSON (Markdown)
4. 更新 @State 数组
5. 渲染数据
6. 注入 HTML

用户输入

ArkTS 业务逻辑

DeepSeek API

List 组件

聊天气泡

Web 组件 (Markdown 渲染器)


🛠️ 二、 核心实战:一步步手撸代码

1. 配置网络权限

首先,别忘了在module.json5中申请网络权限,否则 App 连网都连不上。

"requestPermissions":[{"name":"ohos.permission.INTERNET"}]
2. 封装 DeepSeek API 服务

DeepSeek 兼容 OpenAI 的 API 格式。我们封装一个简单的请求方法。

// DeepSeekService.etsimporthttpfrom'@ohos.net.http';exportclassDeepSeekService{privatestaticAPI_URL='https://api.deepseek.com/chat/completions';privatestaticAPI_KEY='你的_DEEPSEEK_API_KEY';// 记得替换!staticasyncchat(message:string):Promise<string>{lethttpRequest=http.createHttp();try{letresponse=awaithttpRequest.request(this.API_URL,{method:http.RequestMethod.POST,header:{'Content-Type':'application/json','Authorization':`Bearer${this.API_KEY}`},extraData:JSON.stringify({model:"deepseek-chat",messages:[{role:"system",content:"你是 DeepSeek,一个乐于助人的 AI 助手。"},{role:"user",content:message}],stream:false// 教程演示简单起见,暂不使用流式})});if(response.responseCode===200){// 解析返回结果constresult=JSON.parse(response.resultasstring);returnresult.choices[0].message.content;}else{return"网络请求失败: "+response.responseCode;}}catch(err){return"发生错误: "+JSON.stringify(err);}finally{httpRequest.destroy();}}}
3. 攻克难点:Markdown 渲染组件 (MarkdownView)

鸿蒙原生的RichText功能有限。为了支持代码高亮和表格,最稳妥的方案是使用 Web 组件加载本地 HTML 模板,并注入marked.jshighlight.js

// MarkdownView.etsimportweb_webviewfrom'@ohos.web.webview';@Componentexportstruct MarkdownView{@Propcontent:string;// 接收 Markdown 文本controller:web_webview.WebviewController=newweb_webview.WebviewController();// 构建一个包含 Markdown 解析库的 HTML 模板// 在真实项目中,建议将 marked.min.js 放进 rawfile 资源中读取privatehtmlTemplate(mdContent:string):string{return`<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/github-markdown-css/5.2.0/github-markdown-light.min.css"> <style> body { padding: 10px; background-color: transparent; } </style> </head> <body> <div id="content" class="markdown-body"></div> <script> document.getElementById('content').innerHTML = marked.parse(\`${mdContent.replace(/`/g,'\\`')}\`); </script> </body> </html>`;}build(){// 关键:使用 Web 组件渲染 HTMLWeb({src:'',controller:this.controller}).onControllerAttached(()=>{// 当 Web 组件加载完成后,注入内容this.controller.loadData(this.htmlTemplate(this.content),"text/html","UTF-8");}).width('100%')// 根据内容动态高度是个大坑,这里简化处理,固定高度或使用 JS 交互算高度.height(300).backgroundColor(Color.Transparent)}}
4. 组装主界面 (Index.ets)

最后,把聊天列表、输入框和我们的MarkdownView组装起来。

import{DeepSeekService}from'./DeepSeekService';import{MarkdownView}from'./MarkdownView';// 定义消息模型classChatMessage{id:number;role:'user'|'ai';content:string;constructor(role:'user'|'ai',content:string){this.id=Date.now();this.role=role;this.content=content;}}@Entry@Componentstruct Index{@Statemessages:ChatMessage[]=[];@StateinputText:string="";@StateisLoading:boolean=false;scroller:Scroller=newScroller();asyncsendMessage(){if(this.inputText.trim()==="")return;// 1. 添加用户消息constuserMsg=this.inputText;this.messages.push(newChatMessage('user',userMsg));this.inputText="";this.isLoading=true;// 2. 调用 DeepSeek APIconstaiResponse=awaitDeepSeekService.chat(userMsg);// 3. 添加 AI 消息this.messages.push(newChatMessage('ai',aiResponse));this.isLoading=false;// 滚动到底部this.scroller.scrollEdge(Edge.Bottom);}build(){Column(){// 标题Text("DeepSeek Harmony").fontSize(20).fontWeight(FontWeight.Bold).height(50)// 聊天列表区List({scroller:this.scroller}){ForEach(this.messages,(msg:ChatMessage)=>{ListItem(){Row(){if(msg.role==='user'){// 用户消息 (右侧)Text(msg.content).backgroundColor('#95EC69').padding(10).borderRadius(8).margin({left:50})}else{// AI 消息 (左侧,支持 Markdown)Column(){MarkdownView({content:msg.content})}.backgroundColor('#FFFFFF').borderRadius(8).margin({right:20}).width('90%')}}.width('100%').justifyContent(msg.role==='user'?FlexAlign.End:FlexAlign.Start).padding(10)}})}.layoutWeight(1)// 占据剩余空间.backgroundColor('#F5F5F5')// 底部输入区Row(){TextInput({text:this.inputText,placeholder:"问点什么..."}).onChange((value)=>this.inputText=value).layoutWeight(1).backgroundColor(Color.White).margin({right:10})Button(this.isLoading?"思考中...":"发送").onClick(()=>this.sendMessage()).enabled(!this.isLoading)}.padding(10).backgroundColor('#EEEEEE')}.height('100%')}}

💡 三、 避坑指南与性能优化

  1. Web 组件高度自适应
    上面的代码为了演示给MarkdownView写死了高度。在生产环境中,你需要使用Web组件的onConsole或 JSBridge,在 HTML 渲染完成后将document.body.scrollHeight传回给 ArkTS,然后动态设置组件高度,否则长文会被截断。
  2. 流式响应 (Stream)
    DeepSeek 支持 Stream 模式(打字机效果)。在鸿蒙中,这需要使用http模块的on('headerReceive')on('dataReceive')监听事件,逐步拼接字符串并刷新 UI。这能极大提升用户体验。
  3. API Key 安全
    永远不要把 API Key 硬编码在客户端代码里上传到 Git!建议通过自己的后端服务器中转请求。

🎯 总结

通过不到 200 行代码,我们就在 HarmonyOS Next 上跑通了一个“DeepSeek”客户端。
这不仅验证了鸿蒙生态的开发效率,也展示了ArkTS + Web 组件混合开发的强大能力。

Next Step:
尝试给你的 App 加上“流式输出”功能,让 AI 的回复像打字机一样一个个蹦出来,体验瞬间提升 10 倍!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/29 5:22:26

RMATS Turbo:解锁RNA剪接分析的极速体验 [特殊字符]

RMATS Turbo&#xff1a;解锁RNA剪接分析的极速体验 &#x1f680; 【免费下载链接】rmats-turbo 项目地址: https://gitcode.com/gh_mirrors/rm/rmats-turbo RNA剪接是基因表达调控的重要环节&#xff0c;而RMATS Turbo正是为此而生的一款革命性工具。它采用C/Cython重…

作者头像 李华
网站建设 2026/9/9 15:30:51

Conda clean清理磁盘空间释放Gigabytes存储

Conda Clean&#xff1a;释放被吞噬的磁盘空间&#xff0c;让开发环境轻装前行 你有没有经历过这样的时刻&#xff1f;在服务器上准备启动一个新模型训练任务时&#xff0c;突然收到“磁盘空间不足”的警告——而系统明明还有几十GB可用。深入排查后发现&#xff0c;~/minicond…

作者头像 李华
网站建设 2026/9/2 15:41:00

告别兼容性困扰:MediaPipe Tasks API迁移终极指南

告别兼容性困扰&#xff1a;MediaPipe Tasks API迁移终极指南 【免费下载链接】mediapipe Cross-platform, customizable ML solutions for live and streaming media. 项目地址: https://gitcode.com/GitHub_Trending/med/mediapipe 还在为MediaPipe Legacy Solutions的…

作者头像 李华
网站建设 2026/8/29 7:08:25

SURF:SLAC 开源 FPGA 与 ASIC 通用 RTL 框架详解

之前文章《使用 IP 核和开源库减少 FPGA 设计周期》中介绍过SURF开源库&#xff0c;今天我们就展开讲讲SURF&#xff0c;重点介绍能为我们带来哪些便利。SURF&#xff08;SLAC Ultimate RTL Framework&#xff09; 是斯坦福线性加速器中心&#xff08;SLAC National Accelerato…

作者头像 李华
网站建设 2026/8/31 2:44:48

OnePose快速上手指南:如何零基础掌握3D物体位姿估计?

OnePose快速上手指南&#xff1a;如何零基础掌握3D物体位姿估计&#xff1f; 【免费下载链接】OnePose Code for "OnePose: One-Shot Object Pose Estimation without CAD Models", CVPR 2022 项目地址: https://gitcode.com/gh_mirrors/on/OnePose 还在为3D物…

作者头像 李华
网站建设 2026/9/3 22:53:02

突破网页限制:Anti-Adblock Killer 高效使用全攻略

突破网页限制&#xff1a;Anti-Adblock Killer 高效使用全攻略 【免费下载链接】anti-adblock-killer Anti-Adblock Killer helps you keep your Ad-Blocker active, when you visit a website and it asks you to disable. 项目地址: https://gitcode.com/gh_mirrors/an/ant…

作者头像 李华