随着大模型技术的普及,AI 交互已成为日常工作的重要组成部分。然而,如何写出高质量的提示词(Prompt)却成为普通用户面临的新挑战。
许多用户在使用 AI 工具时,往往因为提示词表达不清晰、结构混乱,导致 AI 输出结果不尽如人意。更关键的是,即使偶尔写出了优秀的提示词,也难以沉淀、复用和管理这些宝贵的“创作资产”。
基于这一痛点,我们决定开发 PromptTuner——一款专注于提示词发现、优化、管理和学习的 HarmonyOS 原生应用。本文将从技术实践的角度,分享 PromptTuner 从 0 到 1 的完整开发历程。
1. 应用诞生背景
2. 技术选型考量:为什么选择 HarmonyOS 6
3. 分享核心:HarmonyOS 6 新特性如何赋能 PromptTuner
4. 分享对象与阅读指引


PromptTuner 定位为一站式提示词管理工具,通过“发现-优化-学习-管理”的闭环,帮助用户提升 AI 交互效率。
本节将详细介绍应用的核心功能、技术架构与选型考量。
1. 应用定位与核心功能
PromptTuner 围绕提示词全生命周期,提供五大核心功能模块:
2. 目标用户群体
3. 技术架构概览
PromptTuner 采用经典的三层架构设计,确保代码结构清晰、职责分明:
4. 开发环境与技术栈
5. 整体架构示意
从“页面 → 能力 → 数据”的三层视角理解 PromptTuner 的架构设计:
在 PromptTuner 的开发过程中,我们遇到了诸多挑战。本节将分享这些挑战的具体表现、解决思路与最终方案,希望能为其他开发者提供参考。
1. 数据安全与隐私合规
挑战:提示词历史、个人模板、学习记录等都属于较为敏感的“创作资产”。如何在不接入复杂账号体系的前提下,保证数据本地可控、权限透明,并满足隐私合规要求?
解决方案:
XPrivacyDialog 组件在首次启动时展示隐私协议,明确告知用户数据使用方式,提升用户信任度。2. 性能与稳定性
挑战:广场列表、搜索和优化等场景需要频繁网络交互,需要控制首屏加载时间与滚动性能。同时,埋点与本地统计在高频操作下不能影响主线程体验。
解决方案:
eventCache 缓存事件,达到阈值时批量 Flush,减少频繁 IO 操作。3. 用户体验提升需求
挑战:工具类应用要尽量减少操作路径,找提示词、优化、复制/保存应在 3 步内完成。同时,需要在不同入口(广场、优化、学习中心)之间做流畅跳转,避免“页面迷路感”。
解决方案:
CreateAndOptimizePage),通过 Tab 切换不同模式,提升工具复用性。4. 数据同步与安全考虑
挑战:MVP 阶段以本地数据 + 轻后端为主,如何规划后续多设备、多账号同步的演进空间?本地存储结构如何设计才能保证可扩展性?
解决方案:
5. 工程规范与团队协作
挑战:如何确保团队代码风格一致,避免“胖页面”和重复业务逻辑,提升代码可维护性?
解决方案:
1. ArkUI 声明式 UI 与多 Tab 应用骨架
MainPage 的 Tab 结构实现:通过 @State 管理当前选中的 Tab,使用 @Builder 方法根据状态动态渲染对应页面内容。
@Entry
@Component
struct MainPage {
@State selectedTab: string = 'square'
@State optimizePromptParam: string = ''
@State optimizeModeParam: string = ''
// 定义标签列表(使用SymbolGlyph图标)
private tabs: TabItem[] = [
{
key: 'square',
title: '广场',
icon: $r('sys.symbol.square_grid_2x2'),
selectedIcon: $r('sys.symbol.square_fill_grid_2x2')
},
{
key: 'create',
title: '工具',
icon: $r('sys.symbol.wand_and_stars'),
selectedIcon: $r('sys.symbol.wand_and_stars_fill')
},
{
key: 'learning',
title: '学习',
icon: $r('sys.symbol.book_pages'),
selectedIcon: $r('sys.symbol.book_pages_fill')
},
{
key: 'profile',
title: '我的',
icon: $r('sys.symbol.person'),
selectedIcon: $r('sys.symbol.person_fill')
}
]
build() {
Column() {
// 内容区域
Stack() {
this.renderContent()
}
.layoutWeight(1)
// 底部TabBar
TabBar({
tabs: this.tabs,
selectedTab: this.selectedTab,
selectedColor: $r('app.color.color_primary'),
onTabChange: (key: string) => {
this.selectedTab = key
console.info('切换到标签:', key)
}
})
}
.width('100%')
.height('100%')
.backgroundColor($r('app.color.surface_background'))
}

Index 启动页的隐私弹窗实现:在 aboutToAppear 生命周期中检查隐私协议状态,未同意时展示弹窗,同意后跳转主页面。
aboutToAppear(): void {
// 初始化应用,检查隐私协议状态
this.initializeApp()
}
private async initializeApp(): Promise<void> {
// 先检查隐私协议状态
await this.checkPrivacyAgreementStatus()
// 如果已同意隐私协议,则继续正常启动流程
if (this.hasAgreedPrivacy) {
this.navigateToMain()
}
// 如果未同意,会显示隐私弹窗,用户同意后再继续
}
private async checkPrivacyAgreementStatus(): Promise<void> {
try {
const context = getContext(this) as common.UIAbilityContext
const store = await preferences.getPreferences(context, 'aiprompt_privacy_store')
const agreedRaw: Object = await store.get('privacy_agreed', false)
const agreedValue: boolean = (agreedRaw as boolean) === true
this.hasAgreedPrivacy = agreedValue
if (!this.hasAgreedPrivacy) {
this.showPrivacyDialog = true
console.info('隐私协议未同意,显示隐私弹窗')
} else {
console.info('隐私协议已同意')
}
} catch (error) {
console.error('检查隐私协议状态失败:', JSON.stringify(error))
this.hasAgreedPrivacy = false
this.showPrivacyDialog = true
}
}
private navigateToMain(): void {
// 立即跳转,减少延迟(数据加载状态已在各页面中处理)
router.replaceUrl({
url: RouteConstants.MAIN_PAGE
}).catch((error: BusinessError) => {
console.error(`跳转失败: ${error.code}, ${error.message}`)
})
}

2. 状态管理与跨页面参数传递实践
MainPage 中的参数接收与状态管理:在生命周期钩子中解析路由参数,实现跨页面参数传递。
aboutToAppear(): void {
// 检查是否有路由参数(从草稿列表返回时,或从诊断结果页跳转时)
const params: ESObject | null = RouterHelper.getParams(this.getUIContext()) as ESObject | null
if (params) {
if (params.selectedTab) {
this.selectedTab = params.selectedTab as string
}
if (params.optimizePrompt) {
this.optimizePromptParam = params.optimizePrompt as string
}
if (params.optimizeMode) {
this.optimizeModeParam = params.optimizeMode as string
}
}
}
onPageShow(): void {
// 页面显示时也检查参数(用于处理返回场景)
const params: ESObject | null = RouterHelper.getParams(this.getUIContext()) as ESObject | null
if (params) {
if (params.selectedTab) {
this.selectedTab = params.selectedTab as string
}
if (params.optimizePrompt) {
this.optimizePromptParam = params.optimizePrompt as string
}
if (params.optimizeMode) {
this.optimizeModeParam = params.optimizeMode as string
}
}
}
@Builder
renderContent() {
if (this.selectedTab === 'square') {
// 广场页面 - 显示实际内容
SquareListPage()
} else if (this.selectedTab === 'create') {
// 创作与优化页面(整合版)
CreateAndOptimizePage({
optimizePrompt: this.optimizePromptParam,
optimizeMode: this.optimizeModeParam
})
} else if (this.selectedTab === 'learning') {
// 学习中心页面 - 显示实际内容
LearningCenterPage()
} else if (this.selectedTab === 'profile') {
// 我的页面 - 显示实际内容
ProfilePage()
}
}
3. API 能力抽象与提示词优化流程
响应格式兼容处理:通过属性探测判断响应格式,统一处理不同格式的 API 响应。
static async generatePrompt(params: GeneratePromptParams): Promise<GeneratePromptResponse> {
try {
const response: ESObject = await doPost<ESObject>({
host: ApiConstants.HOST_URL,
url: `${ApiConstants.API_PREFIX}/generate`,
data: {
requirement: params.requirement
},
timeout: 60000
})
// 处理响应格式:可能是 { code, message, data } 或直接是 data
// 由于 doPost 可能已经处理了响应,先尝试直接作为 data 使用
let responseData: ESObject = response
// 检查是否是完整的 API 响应格式(通过访问属性判断)
const hasCode = (responseData as ESObject).code !== undefined
const hasData = (responseData as ESObject).data !== undefined
const hasMessage = (responseData as ESObject).message !== undefined
if (hasCode && hasData && hasMessage) {
// 是完整的 API 响应格式
const apiResponse = responseData as ApiResponse<GeneratePromptResponse>
if (apiResponse.code !== 0 || !apiResponse.data) {
throw new Error(apiResponse.message || '生成失败')
}
return apiResponse.data
}
// 直接是 data 格式,检查必要字段
const hasPrompt = (responseData as ESObject).prompt !== undefined
const hasTitle = (responseData as ESObject).title !== undefined
if (hasPrompt && hasTitle) {
return responseData as GeneratePromptResponse
}
throw new Error('响应格式不正确:缺少必要字段')
} catch (error) {
console.error('generatePrompt error:', JSON.stringify(error))
if (error instanceof Error) {
throw error
}
throw new Error('生成提示词失败,请稍后重试')
}
}
诊断结果数据转换:将后端返回的对象格式转换为前端 UI 需要的数组格式。
// 将 dimensions 对象转换为数组格式
const dimensions: DiagnoseDimension[] = [
{
name: 'completeness',
score: data.dimensions.completeness,
maxScore: 25,
label: '完整性'
},
{
name: 'clarity',
score: data.dimensions.clarity,
maxScore: 25,
label: '清晰度'
},
{
name: 'effectiveness',
score: data.dimensions.effectiveness,
maxScore: 25,
label: '有效性'
},
{
name: 'professionalism',
score: data.dimensions.professionalism,
maxScore: 25,
label: '专业性'
}
]

4. 本地埋点统计与用户行为分析
事件记录与缓存机制:通过内存缓存批量写入,减少频繁 IO 操作,提升性能。
static async trackEvent(
eventType: EventType,
eventName: string,
targetId: string,
targetType: string,
properties?: ESObject
): Promise<void> {
if (!AnalyticsUtil.dataPreferences) {
console.warn('AnalyticsUtil not initialized')
return
}
const event: EventRecord = {
eventType: eventType,
eventName: eventName,
targetId: targetId,
targetType: targetType,
properties: properties ? JSON.stringify(properties) : '{}',
timestamp: new Date().toISOString()
}
// 添加到缓存
AnalyticsUtil.eventCache.push(event)
// 如果缓存达到阈值,批量保存
if (AnalyticsUtil.eventCache.length >= AnalyticsUtil.MAX_CACHE_SIZE) {
await AnalyticsUtil.flushEvents()
}
}
批量刷新机制:当缓存达到阈值或应用退出时,批量将事件写入本地存储,并限制总事件数量。
static async flushEvents(): Promise<void> {
if (!AnalyticsUtil.dataPreferences || AnalyticsUtil.eventCache.length === 0) {
return
}
try {
const existingEvents = await AnalyticsUtil.getEvents()
// 合并新旧事件
let index = 0
const cacheLength = AnalyticsUtil.eventCache.length
while (index < cacheLength) {
existingEvents.push(AnalyticsUtil.eventCache[index])
index++
}
// 只保留最近的事件
let finalEvents = existingEvents
if (existingEvents.length > AnalyticsUtil.MAX_EVENTS) {
const startIndex = existingEvents.length - AnalyticsUtil.MAX_EVENTS
finalEvents = []
let i = startIndex
while (i < existingEvents.length) {
finalEvents.push(existingEvents[i])
i++
}
}
await AnalyticsUtil.dataPreferences.put('events', JSON.stringify(finalEvents))
await AnalyticsUtil.dataPreferences.flush()
// 清空缓存
AnalyticsUtil.eventCache = []
console.info(`Flushed ${cacheLength} events to storage`)
} catch (error) {
console.error('Failed to flush events:', JSON.stringify(error))
}
}
5. 安全与隐私:隐私弹窗与精细化权限管理
隐私弹窗的展示与交互:在 build 方法中使用条件渲染展示弹窗,通过回调处理用户同意/拒绝操作。
// 隐私协议弹窗
if (this.showPrivacyDialog) {
XPrivacyDialog({
appName: this.appName,
detailMode: XPrivacyDetailMode.CUSTOM,
onOpenDetail: (type: XPrivacyOpenType) => {
if (type === XPrivacyOpenType.USER) {
const userAgreementUrl = getUrlWithDarkMode(
getContext(this) as common.UIAbilityContext,
PrivacyConstants.USER_AGREEMENT_URL
)
router.pushUrl({
url: RouteConstants.COMMON_WEB,
params: {
url: userAgreementUrl,
title: '用户协议'
}
})
} else {
const privacyPolicyUrl = getUrlWithDarkMode(
getContext(this) as common.UIAbilityContext,
PrivacyConstants.PRIVACY_POLICY_URL
)
router.pushUrl({
url: RouteConstants.COMMON_WEB,
params: {
url: privacyPolicyUrl,
title: '隐私政策'
}
})
}
},
onAgree: async () => {
try {
const context = getContext(this) as common.UIAbilityContext
const store = await preferences.getPreferences(context, 'aiprompt_privacy_store')
await store.put('privacy_agreed', true)
await store.flush()
this.showPrivacyDialog = false
this.hasAgreedPrivacy = true
console.info('隐私协议已同意,继续应用初始化')
// 用户同意后,继续启动流程
this.navigateToMain()
} catch (error) {
console.error('保存隐私协议状态失败:', JSON.stringify(error))
}
},
onDecline: () => {
console.info('用户拒绝隐私协议')
// 用户拒绝隐私协议,可以选择退出应用或其他处理
// 这里可以显示提示,但不强制退出,让用户可以重新考虑
}
})
}
6. 设计与主题:一致的 Design Tokens 与深色模式适配
DesignTokens 统一定义:通过静态类定义间距、圆角、字体大小等设计常量,确保全应用风格一致。
// 间距常量
export class Spacing {
static readonly XS: number = 4
static readonly SM: number = 8
static readonly MD: number = 16
static readonly LG: number = 24
static readonly XL: number = 32
static readonly XXL: number = 48
// 小写别名
static readonly xs: number = 4
static readonly sm: number = 8
static readonly md: number = 16
static readonly lg: number = 24
static readonly xl: number = 32
static readonly xxl: number = 48
}
// 圆角常量
export class Radius {
static readonly SM: number = 8
static readonly MD: number = 12
static readonly LG: number = 16
static readonly XL: number = 20
// 小写别名
static readonly sm: number = 8
static readonly md: number = 12
static readonly lg: number = 16
static readonly xl: number = 20
}
公共组件使用 DesignTokens:在 CommonCard 组件中统一使用 Token,保证视觉一致性。
build() {
Column() {
if (this.content) {
this.content()
}
}
.width('100%')
.padding(Spacing.MD)
.backgroundColor($r('app.color.card_background'))
.borderRadius(Radius.MD)
.onClick(() => {
if (this.onCardClick) {
this.onCardClick()
}
})
}
7. 性能优化策略(列表与网络交互场景)
经过数月的开发与优化,PromptTuner 在开发效率、性能表现和用户体验等方面都取得了显著成效。本节将复盘这些成果,并分享可量化的优化数据。
1. 开发效率与架构收益
组件库复用带来的效率提升:
架构设计带来的维护性提升:
2. 性能与体验优化成效
首屏加载优化:
交互体验优化:
性能稳定性:
3. 用户反馈与评价(预留)
在 PromptTuner 的开发过程中,我们不仅完成了产品功能,更在技术实践、架构设计、团队协作等方面积累了宝贵经验。本节将分享这些思考与收获。
1. HarmonyOS 6 带来的开发体验变化
强类型系统带来的收益:
声明式 UI 的优势:
2. 声明式 UI 开发的优势与实践细节
状态管理最佳实践:
代码组织原则:
3. 本地化工具应用的跨设备思考
当前架构的扩展性:
未来扩展方向:
4. 对鸿蒙生态的期待
系统级能力增强:
生态建设:
1. 应用功能迭代计划
2. HarmonyOS 新特性跟进方向
3. 生态联动与开放合作
PromptTuner 的诞生,源于我们对“如何让 AI 更好地服务用户”这一问题的思考。
通过 HarmonyOS 6 的技术能力,我们将抽象的提示词工程化理念转化为可感知、可使用的产品功能,帮助用户提升 AI 交互效率。
1. PromptTuner 的价值与定位
PromptTuner 不仅仅是一个工具应用,更是连接用户与 AI 的桥梁。通过 HarmonyOS 原生体验,我们为用户提供了一个“找提示词、用提示词、学提示词”的一站式空间。
在这里,用户可以:
这种闭环体验,让提示词从“一次性消耗品”转变为可沉淀、可复用的“创作资产”。
2. 技术与创作的融合
技术本身不是目的,而是手段。PromptTuner 的价值在于,利用 HarmonyOS 6 的能力,将原本抽象的提示词工程化理念落地为可感知的功能与交互。
我们相信,好的技术应该“隐于幕后”,让用户专注于创作本身,而非技术细节。
在开发过程中,我们始终以用户体验为核心,通过声明式 UI、状态管理、性能优化等技术手段,打造流畅、稳定、易用的产品体验。这种“技术服务于创作”的理念,也是 PromptTuner 持续迭代的方向。
3. 致谢与展望
致谢:
展望:
结语
技术赋能,让创作更高效。PromptTuner 的旅程才刚刚开始,我们将持续迭代,用更好的技术、更好的体验,服务每一位用户。
也期待与更多开发者交流,共同推动 HarmonyOS 生态的繁荣发展。
文章来自于微信公众号 “51CTO技术栈”,作者 “51CTO技术栈”
【开源免费】字节工作流产品扣子两大核心业务:Coze Studio(扣子开发平台)和 Coze Loop(扣子罗盘)全面开源,而且采用的是 Apache 2.0 许可证,支持商用!
项目地址:https://github.com/coze-dev/coze-studio
【开源免费】n8n是一个可以自定义工作流的AI项目,它提供了200个工作节点来帮助用户实现工作流的编排。
项目地址:https://github.com/n8n-io/n8n
在线使用:https://n8n.io/(付费)
【开源免费】DB-GPT是一个AI原生数据应用开发框架,它提供开发多模型管理(SMMF)、Text2SQL效果优化、RAG框架以及优化、Multi-Agents框架协作、AWEL(智能体工作流编排)等多种技术能力,让围绕数据库构建大模型应用更简单、更方便。
项目地址:https://github.com/eosphoros-ai/DB-GPT?tab=readme-ov-file
【开源免费】VectorVein是一个不需要任何编程基础,任何人都能用的AI工作流编辑工具。你可以将复杂的工作分解成多个步骤,并通过VectorVein固定并让AI依次完成。VectorVein是字节coze的平替产品。
项目地址:https://github.com/AndersonBY/vector-vein?tab=readme-ov-file
在线使用:https://vectorvein.ai/(付费)
【开源免费】FASTGPT是基于LLM的知识库开源项目,提供开箱即用的数据处理、模型调用等能力。整体功能和“Dify”“RAGFlow”项目类似。很多接入微信,飞书的AI项目都基于该项目二次开发。
项目地址:https://github.com/labring/FastGPT
【开源免费】XTuner 是一个高效、灵活、全能的轻量化大模型微调工具库。它帮助开发者提供一个简单易用的平台,可以对大语言模型(LLM)和多模态图文模型(VLM)进行预训练和轻量级微调。XTuner 支持多种微调算法,如 QLoRA、LoRA 和全量参数微调。
项目地址:https://github.com/InternLM/xtuner
【开源免费】LangGPT 是一个通过结构化和模板化的方法,编写高质量的AI提示词的开源项目。它可以让任何非专业的用户轻松创建高水平的提示词,进而高质量的帮助用户通过AI解决问题。
项目地址:https://github.com/langgptai/LangGPT/blob/main/README_zh.md
在线使用:https://kimi.moonshot.cn/kimiplus/conpg00t7lagbbsfqkq0