【open harmony/harmonyos】从关键词到星图实现一个自动生成知识图谱的 ArkTS 应用前言 ✨在知识管理、学习工具、AI 应用中“关键词”是非常常见的输入形式。比如用户输入一组词太阳, 月亮, 银河, 彗星, 星云, 行星如果只是把它们放进列表里信息会比较平如果把它们自动生成一个星图就会更直观、更有探索感。这篇文章会结合我的 HarmonyOS / OpenHarmony 项目星图 Xingtu分享如何使用 ArkTS 实现一个“从关键词生成知识星图”的功能。这个功能主要包含 输入主题和关键词 自动解析、清洗、去重 创建中心节点 生成环绕节点✨ 自动建立节点连线 生成后直接进入可旋转缩放的星图界面一、功能效果说明在项目中用户可以点击“词图生成”输入一个主题和一组相关词。例如主题宇宙 关键词太阳, 月亮, 银河, 彗星, 星云, 行星生成后系统会创建一个以“宇宙”为中心的星图“宇宙”作为中心节点“太阳、月亮、银河”等作为环绕节点中心节点和每个关键词节点建立连接关键词节点之间也可以生成辅助连接这样用户看到的不是一组孤立文字而是一张可探索的知识关系图。二、输入弹层设计词图生成入口封装在XingtuGenerateWordMapSheet组件中。它包含两个输入项主题名可选相关词用逗号、换行等符号分隔核心代码如下ComponentexportstructXingtuGenerateWordMapSheet{visible:booleanfalse;onCancel:() void() {};onGenerate:(themeTitle:string, rawWords:string) void() {};StatethemeTitle:string;StatewordsText:string;privatesubmit():void{this.onGenerate(this.themeTitle.trim(),this.wordsText.trim());this.themeTitle;this.wordsText; } }这里没有把生成逻辑直接写在 UI 组件中而是通过onGenerate回调交给外层处理。这样做的好处是UI 组件只负责输入和展示数据生成逻辑放在 Store 中后续如果换成 AI 生成、接口生成也更容易扩展三、输入界面实现 弹层的 UI 使用玻璃拟态风格和星图主界面保持一致。Column({space: 12 }){Text(生成词语星图).fontSize(22).fontWeight(FontWeight.Bold).fontColor(XingtuTheme.textPrimary)Text(输入主题和一组相关词系统会自动生成可旋转、可缩放的 3D 星图。).fontSize(13).fontColor(XingtuTheme.textSecondary)TextInput({text:this.themeTitle,placeholder: 主题名可选例如宇宙、品牌、课程 }).height(46) .borderRadius(14).backgroundColor(XingtuTheme.surfaceElevated).fontColor(XingtuTheme.textPrimary).onChange((value:string) this.themeTitle value)TextInput({text:this.wordsText,placeholder: 相关词用逗号分隔例如太阳, 月亮, 银河, 彗星 }).height(76) .borderRadius(14).backgroundColor(XingtuTheme.surfaceElevated).fontColor(XingtuTheme.textPrimary).onChange((value:string) this.wordsText value) } .padding({ left:18, right:18, top:18, bottom:18}) .borderRadius(28).backgroundColor(XingtuTheme.materialGlass).backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THIN)这里有一个小设计主题名是可选的。如果用户不填主题系统会默认把第一个关键词作为中心节点。这可以降低输入成本让用户更快生成图谱。四、从页面触发生成逻辑在主壳组件XingtuAppShell中生成弹层通过showWordMapSheet控制显示。privateopenWordMapSheet(): void {this.showCreateSheet false;this.showCreateHint false;this.showWordMapSheet true;this.store.selectNode(null);this.revision 1; }当用户点击“生成星图”后页面会调用 Store 中的generateWordMap。XingtuGenerateWordMapSheet({ visible:this.showWordMapSheet, onCancel: () {this.showWordMapSheet false; }, onGenerate: (themeTitle: string, rawWords: string) {constgeneratedCount: number this.store.generateWordMap(themeTitle, rawWords);if(generatedCount 0) {this.switchTab(starMap);this.revision 1; }this.showWordMapSheet false; } })这里的体验处理比较关键生成成功后自动切回“星图”页面更新revision触发界面刷新关闭输入弹层用户不会停留在输入表单中而是直接看到生成结果。✨五、关键词解析与去重 用户输入的关键词可能来自不同场景逗号分隔中文逗号分隔顿号分隔分号分隔换行分隔中间带空格有重复词所以需要先对输入内容做清洗。项目中使用parseWordList方法处理关键词。privateparseWordList(rawWords:string):string[] {constnormalizedItems:string[] rawWords .split(/[\n,;、]/) .map((item:string) item.trim()) .filter((item:string) item.length0);constseen:Setstring newSetstring();constwords:string[] []; normalizedItems.forEach((item:string) {constkey:string item.toLowerCase();if(!seen.has(key)) { seen.add(key); words.push(item); } });returnwords; }这个方法做了三件事支持多种分隔符自动去掉空白内容使用Set去重这一步虽然不复杂但非常影响实际体验。用户输入越自由程序就越需要做好兜底。六、生成中心节点 生成星图的第一步是确定中心节点。let centerTitle:string normalizedTheme; let orbitWords:string[] words;if(centerTitle.length0) { centerTitle words[0]; orbitWords words.slice(1); }逻辑是如果用户填写了主题就用主题作为中心节点如果用户没填主题就用第一个关键词作为中心节点剩余关键词作为环绕节点然后创建中心节点const centerNode: XingtuNode createNodeAtPosition({title: centerTitle, note: orbitWords.length 0 ? ${orbitWords.length} 个关联词 :中心词, tags: normalizedTheme.length 0 ? [theme,center] : [center] }, {x: 0, y: 0, z: 40 });中心节点放在接近画面中心的位置作为整张星图的核心。七、生成环绕节点 关键词节点不能全部堆在一起需要分布在中心节点周围。项目中使用createOrbitPosition生成环绕坐标。privatecreateOrbitPosition(index:number, total:number): Vec3 { constcount:number Math.max(1, total); const goldenAngle:number Math.PI * (3- Math.sqrt(5)); const ratio:numbercount1?0.5:index/ (count-1); const yUnit:number1- ratio *2; const radiusUnit:number Math.sqrt(Math.max(0.08,1- yUnit * yUnit)); const theta:number goldenAngle *index; const radius:number180 (index%3) *34;return{ x: Math.cos(theta) * radiusUnit * radius, y: yUnit *170 ((index%2) -0.5) *24, z: Math.sin(theta) * radiusUnit * radius }; }这里使用了一个类似黄金角的分布方式让节点不会简单排成圆环而是更自然地散布在 3D 空间中。这样生成的节点会更像“星云”或“知识空间”而不是普通二维图表。✨八、建立节点连线生成环绕节点时会同时建立中心节点到关键词节点的关系。orbitWords.forEach((word:string, index:number) {constposition this.createOrbitPosition(index, orbitWords.length);constnode:XingtuNodecreateNodeAtPosition({title: word,note:${centerTitle}关联元素,tags: normalizedTheme.length0? [word-map, normalizedTheme] : [word-map] }, position); generatedNodes.push(node); orbitNodeIds.push(node.id); generatedEdges.push(this.createEdge(centerNode.id, node.id)); });当关键词数量较多时还会给相邻关键词增加辅助连接if(orbitNodeIds.length2) { orbitNodeIds.forEach((nodeId:string, index:number) {constnextIndex:number index 1;if(nextIndex orbitNodeIds.length) { generatedEdges.push(this.createEdge(nodeId, orbitNodeIds[nextIndex])); } }); }这样生成出来的图谱不是一堆孤立节点而是一个有中心、有扩散、有关系链路的知识网络。九、更新 Store 并重置相机生成完成后需要把旧图谱替换为新图谱并重置选中状态和相机状态。this.nodes generatedNodes;this.edges generatedEdges;this.selectedNodeId centerNode.id;this.linkingSourceId null;this.camera defaultCamera();returngeneratedNodes.length;这里有两个体验细节默认选中中心节点让用户知道本次生成的主题是什么重置相机角度和缩放避免用户在之前的视角中看不到新图谱这类细节对生成类功能非常重要。生成成功不只是数据成功还要让用户立刻“看见结果”。十、完整生成流程回顾整个关键词生成星图的流程如下用户输入主题和关键词 ↓ 解析关键词并去重 ↓ 确定中心节点 ↓ 为每个关键词生成3D坐标 ↓ 创建节点和连线 ↓ 更新Store↓ 切回星图页面并刷新UI用代码来看核心入口就是generateWordMapgenerateWordMap(themeTitle:string,rawWords:string): number { const words:string[] this.parseWordList(rawWords); const normalizedTheme:string themeTitle.trim();if(words.length0normalizedTheme.length0) { return0; }letcenterTitle:string normalizedTheme;letorbitWords:string[] words;if(centerTitle.length0) { centerTitle words[0]; orbitWords words.slice(1); } const centerNode: XingtuNode createNodeAtPosition({...}, {x: 0,y: 0,z: 40 }); const generatedNodes: XingtuNode[][centerNode]; const generatedEdges: XingtuEdge[][]; orbitWords.forEach((word:string,index:number) { const position this.createOrbitPosition(index,orbitWords.length); const node: XingtuNode createNodeAtPosition({...},position); generatedNodes.push(node); generatedEdges.push(this.createEdge(centerNode.id,node.id)); }); this.nodes generatedNodes; this.edges generatedEdges; this.selectedNodeId centerNode.id; this.camera defaultCamera(); return generatedNodes.length; }十一、可以继续扩展的方向 当前版本是基于用户输入关键词生成星图后续还可以继续扩展接入端侧 AI自动扩展相关词给节点增加分类、颜色和权重根据词频或重要程度调整节点大小支持多个主题图谱保存支持导出图片或分享图谱支持从文章中提取关键词生成星图这样这个功能就可以从“词图生成器”逐步变成一个真正的知识组织工具。十二、总结 这篇文章分享了如何在 HarmonyOS / OpenHarmony 中使用 ArkTS 实现一个“从关键词到星图”的功能。核心实现思路是用输入弹层收集主题和关键词用正则拆分多种分隔符用Set做关键词去重创建中心节点和环绕节点使用 3D 坐标生成空间布局自动创建节点关系边生成后切回星图页面并刷新这个功能虽然不是复杂 AI但已经具备了很强的产品表达力。用户输入一组词就能立即看到一张可旋转、可缩放的知识星图体验会比普通列表更直观也更有探索感。✨
【open harmony/harmonyos】从关键词到星图:实现一个自动生成知识图谱的 ArkTS 应用
【open harmony/harmonyos】从关键词到星图实现一个自动生成知识图谱的 ArkTS 应用前言 ✨在知识管理、学习工具、AI 应用中“关键词”是非常常见的输入形式。比如用户输入一组词太阳, 月亮, 银河, 彗星, 星云, 行星如果只是把它们放进列表里信息会比较平如果把它们自动生成一个星图就会更直观、更有探索感。这篇文章会结合我的 HarmonyOS / OpenHarmony 项目星图 Xingtu分享如何使用 ArkTS 实现一个“从关键词生成知识星图”的功能。这个功能主要包含 输入主题和关键词 自动解析、清洗、去重 创建中心节点 生成环绕节点✨ 自动建立节点连线 生成后直接进入可旋转缩放的星图界面一、功能效果说明在项目中用户可以点击“词图生成”输入一个主题和一组相关词。例如主题宇宙 关键词太阳, 月亮, 银河, 彗星, 星云, 行星生成后系统会创建一个以“宇宙”为中心的星图“宇宙”作为中心节点“太阳、月亮、银河”等作为环绕节点中心节点和每个关键词节点建立连接关键词节点之间也可以生成辅助连接这样用户看到的不是一组孤立文字而是一张可探索的知识关系图。二、输入弹层设计词图生成入口封装在XingtuGenerateWordMapSheet组件中。它包含两个输入项主题名可选相关词用逗号、换行等符号分隔核心代码如下ComponentexportstructXingtuGenerateWordMapSheet{visible:booleanfalse;onCancel:() void() {};onGenerate:(themeTitle:string, rawWords:string) void() {};StatethemeTitle:string;StatewordsText:string;privatesubmit():void{this.onGenerate(this.themeTitle.trim(),this.wordsText.trim());this.themeTitle;this.wordsText; } }这里没有把生成逻辑直接写在 UI 组件中而是通过onGenerate回调交给外层处理。这样做的好处是UI 组件只负责输入和展示数据生成逻辑放在 Store 中后续如果换成 AI 生成、接口生成也更容易扩展三、输入界面实现 弹层的 UI 使用玻璃拟态风格和星图主界面保持一致。Column({space: 12 }){Text(生成词语星图).fontSize(22).fontWeight(FontWeight.Bold).fontColor(XingtuTheme.textPrimary)Text(输入主题和一组相关词系统会自动生成可旋转、可缩放的 3D 星图。).fontSize(13).fontColor(XingtuTheme.textSecondary)TextInput({text:this.themeTitle,placeholder: 主题名可选例如宇宙、品牌、课程 }).height(46) .borderRadius(14).backgroundColor(XingtuTheme.surfaceElevated).fontColor(XingtuTheme.textPrimary).onChange((value:string) this.themeTitle value)TextInput({text:this.wordsText,placeholder: 相关词用逗号分隔例如太阳, 月亮, 银河, 彗星 }).height(76) .borderRadius(14).backgroundColor(XingtuTheme.surfaceElevated).fontColor(XingtuTheme.textPrimary).onChange((value:string) this.wordsText value) } .padding({ left:18, right:18, top:18, bottom:18}) .borderRadius(28).backgroundColor(XingtuTheme.materialGlass).backgroundBlurStyle(BlurStyle.COMPONENT_ULTRA_THIN)这里有一个小设计主题名是可选的。如果用户不填主题系统会默认把第一个关键词作为中心节点。这可以降低输入成本让用户更快生成图谱。四、从页面触发生成逻辑在主壳组件XingtuAppShell中生成弹层通过showWordMapSheet控制显示。privateopenWordMapSheet(): void {this.showCreateSheet false;this.showCreateHint false;this.showWordMapSheet true;this.store.selectNode(null);this.revision 1; }当用户点击“生成星图”后页面会调用 Store 中的generateWordMap。XingtuGenerateWordMapSheet({ visible:this.showWordMapSheet, onCancel: () {this.showWordMapSheet false; }, onGenerate: (themeTitle: string, rawWords: string) {constgeneratedCount: number this.store.generateWordMap(themeTitle, rawWords);if(generatedCount 0) {this.switchTab(starMap);this.revision 1; }this.showWordMapSheet false; } })这里的体验处理比较关键生成成功后自动切回“星图”页面更新revision触发界面刷新关闭输入弹层用户不会停留在输入表单中而是直接看到生成结果。✨五、关键词解析与去重 用户输入的关键词可能来自不同场景逗号分隔中文逗号分隔顿号分隔分号分隔换行分隔中间带空格有重复词所以需要先对输入内容做清洗。项目中使用parseWordList方法处理关键词。privateparseWordList(rawWords:string):string[] {constnormalizedItems:string[] rawWords .split(/[\n,;、]/) .map((item:string) item.trim()) .filter((item:string) item.length0);constseen:Setstring newSetstring();constwords:string[] []; normalizedItems.forEach((item:string) {constkey:string item.toLowerCase();if(!seen.has(key)) { seen.add(key); words.push(item); } });returnwords; }这个方法做了三件事支持多种分隔符自动去掉空白内容使用Set去重这一步虽然不复杂但非常影响实际体验。用户输入越自由程序就越需要做好兜底。六、生成中心节点 生成星图的第一步是确定中心节点。let centerTitle:string normalizedTheme; let orbitWords:string[] words;if(centerTitle.length0) { centerTitle words[0]; orbitWords words.slice(1); }逻辑是如果用户填写了主题就用主题作为中心节点如果用户没填主题就用第一个关键词作为中心节点剩余关键词作为环绕节点然后创建中心节点const centerNode: XingtuNode createNodeAtPosition({title: centerTitle, note: orbitWords.length 0 ? ${orbitWords.length} 个关联词 :中心词, tags: normalizedTheme.length 0 ? [theme,center] : [center] }, {x: 0, y: 0, z: 40 });中心节点放在接近画面中心的位置作为整张星图的核心。七、生成环绕节点 关键词节点不能全部堆在一起需要分布在中心节点周围。项目中使用createOrbitPosition生成环绕坐标。privatecreateOrbitPosition(index:number, total:number): Vec3 { constcount:number Math.max(1, total); const goldenAngle:number Math.PI * (3- Math.sqrt(5)); const ratio:numbercount1?0.5:index/ (count-1); const yUnit:number1- ratio *2; const radiusUnit:number Math.sqrt(Math.max(0.08,1- yUnit * yUnit)); const theta:number goldenAngle *index; const radius:number180 (index%3) *34;return{ x: Math.cos(theta) * radiusUnit * radius, y: yUnit *170 ((index%2) -0.5) *24, z: Math.sin(theta) * radiusUnit * radius }; }这里使用了一个类似黄金角的分布方式让节点不会简单排成圆环而是更自然地散布在 3D 空间中。这样生成的节点会更像“星云”或“知识空间”而不是普通二维图表。✨八、建立节点连线生成环绕节点时会同时建立中心节点到关键词节点的关系。orbitWords.forEach((word:string, index:number) {constposition this.createOrbitPosition(index, orbitWords.length);constnode:XingtuNodecreateNodeAtPosition({title: word,note:${centerTitle}关联元素,tags: normalizedTheme.length0? [word-map, normalizedTheme] : [word-map] }, position); generatedNodes.push(node); orbitNodeIds.push(node.id); generatedEdges.push(this.createEdge(centerNode.id, node.id)); });当关键词数量较多时还会给相邻关键词增加辅助连接if(orbitNodeIds.length2) { orbitNodeIds.forEach((nodeId:string, index:number) {constnextIndex:number index 1;if(nextIndex orbitNodeIds.length) { generatedEdges.push(this.createEdge(nodeId, orbitNodeIds[nextIndex])); } }); }这样生成出来的图谱不是一堆孤立节点而是一个有中心、有扩散、有关系链路的知识网络。九、更新 Store 并重置相机生成完成后需要把旧图谱替换为新图谱并重置选中状态和相机状态。this.nodes generatedNodes;this.edges generatedEdges;this.selectedNodeId centerNode.id;this.linkingSourceId null;this.camera defaultCamera();returngeneratedNodes.length;这里有两个体验细节默认选中中心节点让用户知道本次生成的主题是什么重置相机角度和缩放避免用户在之前的视角中看不到新图谱这类细节对生成类功能非常重要。生成成功不只是数据成功还要让用户立刻“看见结果”。十、完整生成流程回顾整个关键词生成星图的流程如下用户输入主题和关键词 ↓ 解析关键词并去重 ↓ 确定中心节点 ↓ 为每个关键词生成3D坐标 ↓ 创建节点和连线 ↓ 更新Store↓ 切回星图页面并刷新UI用代码来看核心入口就是generateWordMapgenerateWordMap(themeTitle:string,rawWords:string): number { const words:string[] this.parseWordList(rawWords); const normalizedTheme:string themeTitle.trim();if(words.length0normalizedTheme.length0) { return0; }letcenterTitle:string normalizedTheme;letorbitWords:string[] words;if(centerTitle.length0) { centerTitle words[0]; orbitWords words.slice(1); } const centerNode: XingtuNode createNodeAtPosition({...}, {x: 0,y: 0,z: 40 }); const generatedNodes: XingtuNode[][centerNode]; const generatedEdges: XingtuEdge[][]; orbitWords.forEach((word:string,index:number) { const position this.createOrbitPosition(index,orbitWords.length); const node: XingtuNode createNodeAtPosition({...},position); generatedNodes.push(node); generatedEdges.push(this.createEdge(centerNode.id,node.id)); }); this.nodes generatedNodes; this.edges generatedEdges; this.selectedNodeId centerNode.id; this.camera defaultCamera(); return generatedNodes.length; }十一、可以继续扩展的方向 当前版本是基于用户输入关键词生成星图后续还可以继续扩展接入端侧 AI自动扩展相关词给节点增加分类、颜色和权重根据词频或重要程度调整节点大小支持多个主题图谱保存支持导出图片或分享图谱支持从文章中提取关键词生成星图这样这个功能就可以从“词图生成器”逐步变成一个真正的知识组织工具。十二、总结 这篇文章分享了如何在 HarmonyOS / OpenHarmony 中使用 ArkTS 实现一个“从关键词到星图”的功能。核心实现思路是用输入弹层收集主题和关键词用正则拆分多种分隔符用Set做关键词去重创建中心节点和环绕节点使用 3D 坐标生成空间布局自动创建节点关系边生成后切回星图页面并刷新这个功能虽然不是复杂 AI但已经具备了很强的产品表达力。用户输入一组词就能立即看到一张可旋转、可缩放的知识星图体验会比普通列表更直观也更有探索感。✨