ArkTS 与智能融合案例智能日程Agent
一、实验目的
(一)知识目标
- 理解AI Agent的基本概念、核心特征与工作原理。
- 掌握ReAct(Reasoning + Acting)模式的工作流程与适用场景。
- 理解Function Calling机制的设计原理与安全优势。
- 掌握鸿蒙Calendar Kit日历服务API与Notification Kit通知服务API的使用方法。
(二)能力目标
- 能够设计并实现基于Function Calling的Agent工具函数体系。
- 能够实现多轮对话管理器,支持上下文维护与指代消解。
- 能够将云端大模型能力与鸿蒙系统能力(日历、通知)融合,构建完整的智能应用。
- 能够对Agent应用进行功能测试与性能优化。
(三)素养目标
- 培养"以用户为中心"的AI应用设计思维,关注交互体验与隐私安全。
- 理解端云协同架构中数据最小化原则的工程价值。
- 建立AI Agent在智能办公场景中的应用视野。
二、实验环境与准备
2.1 开发环境

2.2 知识准备
完成本实验前,请确认已掌握以下知识:
1.ArkTS基本语法:接口(interface)、枚举(enum)、泛型、异步编程(async/await)。
2.ArkUI声明式开发:组件定义(@Component)、状态管理(@State、@Prop、@Link)。
3.HTTP网络请求:使用@kit.NetworkKit的http模块发送POST请求。
4.第5章内容:云端大模型API调用方法、请求参数格式、响应解析。
5.第10章理论内容:Agent概念、ReAct模式、Function Calling机制、意图理解与实体抽取。
2.3 项目创建
请按以下步骤创建项目:
6.打开DevEco Studio,选择"Create Project"。
7.选择"Empty Ability"模板,点击Next。
8.项目配置:Project Name填入"SmartScheduleAgent",Bundle Name填入"com.example.smartschedule",Compile SDK选择API 12。
9.点击Finish完成项目创建。
【注意】请确保在module.json5中声明INTERNET权限,否则无法调用云端大模型API。
三、实验前思考题
请在进行实验前思考以下问题,并在实验报告中给出你的理解:
- 在传统的日程管理应用中,用户需要手动填写标题、时间、地点等表单字段。使用AI Agent方式后,用户只需用自然语言说"下周三下午3点开项目评审会"即可自动创建日程。请思考:这种交互方式相比传统表单有哪些优势?又可能带来哪些新的挑战?
- Function Calling机制中,大模型只返回工具调用指令(函数名+参数),而由应用程序负责实际执行。请思考:为什么不直接让大模型执行函数?这种"决策与执行分离"的设计在安全性和隐私保护方面有什么好处?
- 在多轮对话场景中,用户可能说"把那个会议改到后天"。Agent需要知道"那个会议"指的是哪条日程。请思考:Agent可以通过哪些信息来确定指代对象?上下文管理需要维护哪些状态?
四、实验任务与步骤
本实验共10个任务,按照从底层到上层、从数据到界面的顺序逐步构建智能日程Agent应用。

任务1 项目结构与类型定义
【任务目标】搭建项目工程结构,定义全局类型和数据模型。
步骤1.1 创建目录结构
在项目ets目录下创建以下子目录:
SmartScheduleAgent/
├── entry/
│ └── src/main/ets/
│ ├── entryability/
│ │ └── EntryAbility.ets # Ability入口
│ ├── model/
│ │ └── Types.ets # 全局类型定义
│ ├── service/
│ │ ├── CalendarManager.ets # 日历管理服务
│ │ ├── ReminderService.ets # 提醒通知服务
│ │ ├── DialogManager.ets # 对话管理器
│ │ └── AgentService.ets # Agent核心服务
│ ├── pages/
│ │ ├── ChatPage.ets # 对话界面
│ │ ├── ScheduleListPage.ets # 日程列表界面
│ │ └── IndexPage.ets # 入口页面(Tab)
│ └── common/
│ └── Constants.ets # 常量定义
└── entry/src/main/resources/
└── base/profile/
└── main_pages.json # 页面路由配置
步骤1.2 定义类型模型
在model/Types.ets中定义以下类型。请根据提示完成代码:
// model/Types.ets
// 意图类型枚举
export enum IntentType {
CREATE = 'create',
QUERY = 'query',
UPDATE = 'update',
DELETE = 'delete',
CHAT = 'chat'
}
// 日程事件接口
export interface ScheduleEvent {
id: string;
title: string;
startTime: string; // ISO格式时间字符串
endTime: string;
location?: string;
reminderMinutes?: number; // 提前提醒分钟数
isAllDay?: boolean;
}
// 对话消息接口
export interface ChatMessage {
role: 'user' | 'assistant' | 'tool';
content: string;
timestamp: number;
toolCallId?: string;
}
// 工具调用接口
export interface ToolCall {
name: string;
arguments: Record<string, string | number | undefined>;
}
// 对话状态接口
export interface DialogState {
messages: ChatMessage[]; // 对话历史
pendingAction: string | null; // 待确认操作
contextScheduleId: string | null; // 上下文日程ID
lastIntent: IntentType | null; // 上一轮意图
}
// Agent响应接口
export interface AgentResponse {
success: boolean;
message: string;
data?: Record<string, Object>;
}
【提示】Types.ets是全局类型定义文件,所有其他文件通过import引用这些类型。良好的类型定义是TypeScript/ArkTS工程的基础。
步骤1.3 定义常量
在common/Constants.ets中定义API地址等常量:
// common/Constants.ets
export const LLM_API_URL = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation';
export const LLM_MODEL = 'qwen-plus';
export const API_KEY = 'your_api_key'; // 请替换为你的实际API Key
export const MAX_HISTORY_LENGTH = 10; // 对话历史最大轮数
export const DEFAULT_REMINDER_MINUTES = 15; // 默认提前提醒时间
【注意】API Key属于敏感信息,请勿将真实Key提交到版本控制系统。实际部署时应使用安全存储机制,参见第14章。
任务2 日历管理服务
【任务目标】封装Calendar Kit API,实现日程的创建、查询、修改、删除和冲突检测。
步骤2.1 导入Calendar Kit
在service/CalendarManager.ets中导入日历服务模块:
// service/CalendarManager.ets
import { calendarMgr } from '@kit.CalendarKit';
import { ScheduleEvent } from '../model/Types';
步骤2.2 实现日程创建方法
创建日程需要调用calendarMgr.addEvent方法。请完成以下代码框架:
export class CalendarManager {
private calendar: calendarMgr.Calendar | null = null;
// 初始化日历
async init(): Promise<void> {
// TODO: 获取默认日历实例
// 提示:使用 calendarMgr.getCalendar() 获取日历对象
}
// 创建日程
async createSchedule(params: {
title: string;
startTime: Date;
endTime: Date;
location?: string;
reminderMinutes?: number;
}): Promise<string> {
// TODO: 构造Event对象并调用addEvent
// 提示:
// 1. 构造 calendarMgr.Event 对象
// 2. 设置 title, startTime, endTime, location
// 3. 设置 reminderTime 为 reminderMinutes
// 4. 调用 this.calendar.addEvent(event) 获取eventId
// 5. 返回 eventId
}
}
【提示】calendarMgr.Event对象的核心字段:title(标题)、startTime(开始时间Date)、endTime(结束时间Date)、location(地点)、reminderTime(提前提醒分钟数)。
步骤2.3 实现日程查询方法
查询日程使用getEvents方法,支持按时间范围查询:
// 查询日程
async querySchedules(
startDate: Date,
endDate: Date
): Promise<ScheduleEvent[]> {
// TODO: 按时间范围查询日程
// 提示:
// 1. 调用 this.calendar.getEvents({ start: startDate, end: endDate })
// 2. 将返回的Event数组转换为ScheduleEvent数组
// 3. 按startTime排序
}
步骤2.4 实现修改与删除方法
请参考创建方法的模式,实现以下两个方法:
// 修改日程
async updateSchedule(
scheduleId: string,
updates: Partial<ScheduleEvent>
): Promise<boolean> {
// TODO: 根据scheduleId获取日程,更新字段后保存
// 提示:使用 calendar.getEvent(id) 获取,修改后 updateEvent
}
// 删除日程
async deleteSchedule(scheduleId: string): Promise<boolean> {
// TODO: 根据scheduleId删除日程
// 提示:使用 calendar.deleteEvent(id)
}
步骤2.5 实现冲突检测
冲突检测是日程管理的核心逻辑,用于判断新日程是否与已有日程时间重叠:
// 检测时间冲突
async checkConflict(
startTime: Date,
endTime: Date
): Promise<{ hasConflict: boolean; conflictTitle?: string }> {
// TODO: 查询该时间段内是否已有日程
// 提示:
// 1. 查询 startTime 到 endTime 范围内的日程
// 2. 如果有日程,返回 hasConflict=true 和 conflictTitle
// 3. 时间重叠判断:两个时间段 [s1,e1) 和 [s2,e2) 重叠的条件是 s1 < e2 && s2 < e1
}
【注意】时间重叠判断是本任务的核心算法。两个时间段[A_start, A_end)和[B_start, B_end)重叠的条件是:A_start < B_end 且 B_start < A_end。请仔细验证这个逻辑。
任务3 提醒通知服务
【任务目标】封装Notification Kit,实现日程提醒通知功能。
步骤3.1 导入通知模块
// service/ReminderService.ets
import { notificationManager } from '@kit.NotificationKit';
import { ScheduleEvent } from '../model/Types';
步骤3.2 实现即时通知
当用户创建日程后,可以立即发送一条通知告知用户:
export class ReminderService {
// 发送即时通知
async publishNotification(
title: string,
content: string
): Promise<void> {
// TODO: 构造NotificationContent并发布
// 提示:
// 1. 构造 notificationManager.NotificationContent 对象
// 2. 设置 title 和 text
// 3. 构造 notificationManager.NotificationRequest
// 4. 调用 notificationManager.publish(request)
}
}
步骤3.3 实现定时提醒
定时提醒需要在日程开始前指定时间发送通知。请实现以下方法:
// 设置定时提醒
async setReminder(
schedule: ScheduleEvent,
minutesBefore: number = 15
): Promise<number> {
// TODO: 计算提醒时间并设置定时通知
// 提示:
// 1. 将schedule.startTime转为Date对象
// 2. 计算提醒时间 = startTime - minutesBefore分钟
// 3. 如果提醒时间已过,直接发送即时通知
// 4. 否则使用 notificationManager.publishScheduledNotification 设置定时通知
// 5. 返回通知ID用于后续取消
}
// 取消提醒
async cancelReminder(notificationId: number): Promise<void> {
// TODO: 调用 notificationManager.cancel(notificationId)
}
【提示】notificationManager.publishScheduledNotification需要传入触发时间戳(毫秒)。使用Date.getTime()获取时间戳。
任务4 对话管理器
【任务目标】实现多轮对话管理器,维护对话历史、上下文状态和待确认操作。
步骤4.1 定义对话管理器类
// service/DialogManager.ets
import { ChatMessage, DialogState, IntentType } from '../model/Types';
import { MAX_HISTORY_LENGTH } from '../common/Constants';
export class DialogManager {
private state: DialogState = {
messages: [],
pendingAction: null,
contextScheduleId: null,
lastIntent: null
};
// 添加用户消息
addUserMessage(content: string): void {
// TODO: 将用户消息添加到messages数组
// 提示:构造ChatMessage对象,role='user'
// 注意:如果消息数超过MAX_HISTORY_LENGTH,移除最早的消息
}
// 添加助手消息
addAssistantMessage(content: string): void {
// TODO: 将助手消息添加到messages数组
// 提示:构造ChatMessage对象,role='assistant'
}
}
步骤4.2 实现上下文管理
上下文管理是指代消解和多轮交互的关键。请实现以下方法:
// 设置上下文日程ID
setContextScheduleId(id: string | null): void {
// TODO: 更新state.contextScheduleId
}
// 设置待确认操作
setPendingAction(action: string | null): void {
// TODO: 更新state.pendingAction
}
// 设置上一轮意图
setLastIntent(intent: IntentType | null): void {
// TODO: 更新state.lastIntent
}
// 获取上下文日程ID
getContextScheduleId(): string | null {
// TODO: 返回state.contextScheduleId
}
步骤4.3 构建系统提示词
系统提示词需要包含上下文信息,帮助大模型理解指代关系:
// 获取系统提示词
getSystemPrompt(): string {
// TODO: 构建包含上下文信息的系统提示词
// 提示:
// 1. 基础提示词:你是一个智能日程助手,可以帮助用户创建、查询、修改和删除日程。
// 2. 如果有contextScheduleId,添加:当前上下文关联的日程ID为xxx
// 3. 如果有pendingAction,添加:待确认操作为xxx
// 4. 添加指代消解指引:当用户使用指代词时,使用上下文中的日程ID
}
步骤4.4 获取对话历史
对话历史需要格式化为大模型能理解的格式:
// 获取对话历史(供大模型使用)
getHistoryForLLM(): Array<{ role: string; content: string }> {
// TODO: 将messages数组转换为大模型API所需的格式
// 提示:将ChatMessage数组映射为 {role, content} 数组
// 注意:tool角色的消息可能需要特殊处理
}
【提示】对话历史长度限制(MAX_HISTORY_LENGTH=10)是为了控制Token消耗。每轮对话都会增加Token数量,过长的历史会导致API调用变慢且费用增加。
任务5 Agent核心服务
【任务目标】实现AgentService,集成云端大模型Function Calling能力,完成意图理解、工具调用和结果生成的完整ReAct流程。
步骤5.1 定义工具Schema
Function Calling的核心是工具函数的定义。大模型根据这些定义决定调用哪个工具:
// service/AgentService.ets
import { http } from '@kit.NetworkKit';
import { ToolCall, AgentResponse, ScheduleEvent } from '../model/Types';
import { LLM_API_URL, LLM_MODEL, API_KEY } from '../common/Constants';
import { CalendarManager } from './CalendarManager';
import { ReminderService } from './ReminderService';
import { DialogManager } from './DialogManager';
export class AgentService {
private calendarManager: CalendarManager = new CalendarManager();
private reminderService: ReminderService = new ReminderService();
private dialogManager: DialogManager = new DialogManager();
// 工具函数定义
private static readonly TOOLS = [
{
name: 'createSchedule',
description: '创建一条新日程。当用户想要新建、安排或设置日程时调用此工具。',
parameters: {
type: 'object',
properties: {
title: { type: 'string', description: '日程标题' },
startTime: { type: 'string', description: '开始时间,格式YYYY-MM-DD HH:mm' },
endTime: { type: 'string', description: '结束时间,格式YYYY-MM-DD HH:mm' },
location: { type: 'string', description: '地点(可选)' }
},
required: ['title', 'startTime']
}
},
{
name: 'querySchedule',
description: '查询日程。当用户想要查看、搜索或列出日程时调用此工具。',
parameters: {
type: 'object',
properties: {
startDate: { type: 'string', description: '查询开始日期' },
endDate: { type: 'string', description: '查询结束日期' }
},
required: ['startDate']
}
},
{
name: 'updateSchedule',
description: '修改已有日程。当用户想要修改、调整或变更日程时调用此工具。',
parameters: {
type: 'object',
properties: {
scheduleId: { type: 'string', description: '日程ID' },
updates: { type: 'object', description: '修改的字段' }
},
required: ['scheduleId', 'updates']
}
},
{
name: 'deleteSchedule',
description: '删除日程。当用户想要取消或删除日程时调用此工具。',
parameters: {
type: 'object',
properties: {
scheduleId: { type: 'string', description: '日程ID' }
},
required: ['scheduleId']
}
}
];
}
【注意】工具的description字段非常重要,大模型根据描述判断是否调用该工具。描述应清晰说明工具的用途和调用时机。
步骤5.2 实现callLLM方法
callLLM方法将用户输入、对话历史和工具定义发送给云端大模型:
// 调用大模型(带Function Calling)
private async callLLM(input: string): Promise<string> {
// TODO: 构造API请求并发送
// 提示:
// 1. 构造请求体:
// - model: LLM_MODEL
// - messages: [system_prompt, ...history, user_input]
// - tools: AgentService.TOOLS (转换为API要求的格式)
// - tool_choice: 'auto' (让模型自动选择)
// 2. 设置Header: Authorization: Bearer API_KEY
// 3. 发送POST请求到LLM_API_URL
// 4. 返回响应文本
}
【提示】qwen-plus的Function Calling API格式:在请求体中添加tools参数(工具定义数组)和tool_choice参数(设为"auto"表示自动选择工具)。响应中如果模型决定调用工具,会包含tool_calls字段。
步骤5.3 实现parseLLMResponse方法
解析大模型响应,判断是返回文本回复还是工具调用:
// 解析大模型响应
private parseLLMResponse(response: string): ToolCall | null {
// TODO: 解析响应JSON,判断是否包含工具调用
// 提示:
// 1. 解析JSON响应
// 2. 检查是否有 tool_calls 字段
// 3. 如果有,提取第一个tool_call的name和arguments
// 4. 如果没有,返回null(表示模型直接回复文本)
}
步骤5.4 实现executeTool方法
executeTool是工具执行分发器,根据工具名调用对应的方法:
// 执行工具调用
private async executeTool(toolCall: ToolCall): Promise<AgentResponse> {
// TODO: 根据toolCall.name分发到对应的工具方法
// 提示:使用switch语句
// case 'createSchedule': return this.toolCreateSchedule(toolCall.arguments)
// case 'querySchedule': return this.toolQuerySchedule(toolCall.arguments)
// case 'updateSchedule': return this.toolUpdateSchedule(toolCall.arguments)
// case 'deleteSchedule': return this.toolDeleteSchedule(toolCall.arguments)
// default: 返回错误信息
}
步骤5.5 实现各工具方法
请实现4个工具方法,每个方法调用CalendarManager完成实际操作:
// 工具:创建日程
private async toolCreateSchedule(args: Record<string, string>): Promise<AgentResponse> {
// TODO:
// 1. 解析参数:title, startTime, endTime, location
// 2. 转换时间为Date对象
// 3. 调用 calendarManager.createSchedule()
// 4. 设置提醒:reminderService.setReminder()
// 5. 更新上下文:dialogManager.setContextScheduleId()
// 6. 返回成功消息
}
// 工具:查询日程
private async toolQuerySchedule(args: Record<string, string>): Promise<AgentResponse> {
// TODO:
// 1. 解析参数:startDate, endDate
// 2. 调用 calendarManager.querySchedules()
// 3. 格式化查询结果为文本摘要
// 4. 返回查询结果
}
// 工具:修改日程
private async toolUpdateSchedule(args: Record<string, string>): Promise<AgentResponse> {
// TODO:
// 1. 解析参数:scheduleId, updates
// 2. 调用 calendarManager.updateSchedule()
// 3. 返回修改结果
}
// 工具:删除日程
private async toolDeleteSchedule(args: Record<string, string>): Promise<AgentResponse> {
// TODO:
// 1. 解析参数:scheduleId
// 2. 调用 calendarManager.deleteSchedule()
// 3. 清除上下文:dialogManager.setContextScheduleId(null)
// 4. 返回删除结果
}
步骤5.6 实现handleUserInput主方法
handleUserInput是Agent的入口方法,串联完整的ReAct流程:
// 处理用户输入(主入口)
async handleUserInput(input: string): Promise<AgentResponse> {
// TODO: 实现完整的ReAct流程
// 提示:
// 1. 将用户输入添加到对话历史:dialogManager.addUserMessage(input)
// 2. 调用大模型:const response = await this.callLLM(input)
// 3. 解析响应:const toolCall = this.parseLLMResponse(response)
// 4. 如果有工具调用:
// a. 执行工具:const result = await this.executeTool(toolCall)
// b. 将工具结果反馈给大模型:await this.callLLMWithToolResults(input, toolCall, result)
// c. 大模型基于工具结果生成最终回复
// 5. 如果没有工具调用,直接返回大模型的文本回复
// 6. 将助手回复添加到对话历史:dialogManager.addAssistantMessage(message)
}
【注意】步骤5.6是整个Agent的核心流程。如果工具执行后大模型需要再次生成回复,需要实现callLLMWithToolResults方法,将工具执行结果作为tool角色的消息发送给大模型。
任务6 对话界面
【任务目标】实现ChatPage对话界面,支持消息气泡展示、文本输入和发送。
步骤6.1 定义页面组件
// pages/ChatPage.ets
import { AgentService } from '../service/AgentService';
import { ChatMessage } from '../model/Types';
@Entry
@Component
struct ChatPage {
@State messages: ChatMessage[] = [];
@State inputText: string = '';
@State isLoading: boolean = false;
private agentService: AgentService = new AgentService();
build() {
Column() {
// 消息列表区域
List({ space: 12 }) {
ForEach(this.messages, (msg: ChatMessage) => {
// TODO: 根据msg.role渲染不同样式的消息气泡
})
}
.layoutWeight(1)
.padding(12)
// 输入栏区域
// TODO: 实现输入框和发送按钮
}
}
}
步骤6.2 实现消息气泡
用户消息和助手消息使用不同的样式(背景色、对齐方式):
// 消息气泡组件
@Builder
MessageBubble(msg: ChatMessage) {
Row() {
if (msg.role === 'user') {
// 用户消息:右对齐,蓝色背景
Column() {
Text(msg.content)
.fontSize(14)
.fontColor('#FFFFFF')
.padding(10)
}
.backgroundColor('#0070C0')
.borderRadius(12)
.constraintSize({ maxWidth: '75%' })
.margin({ left: 80 })
} else {
// 助手消息:左对齐,灰色背景
Column() {
Text(msg.content)
.fontSize(14)
.fontColor('#333333')
.padding(10)
}
.backgroundColor('#F0F0F0')
.borderRadius(12)
.constraintSize({ maxWidth: '75%' })
.margin({ right: 80 })
}
}
.width('100%')
}
步骤6.3 实现输入栏与发送逻辑
@Builder
InputBar() {
Row({ space: 8 }) {
TextInput({ text: this.inputText, placeholder: '输入消息...' })
.layoutWeight(1)
.height(40)
.onChange((value: string) => {
this.inputText = value;
})
Button('发送')
.height(40)
.enabled(this.inputText.length > 0 && !this.isLoading)
.onClick(() => {
this.sendMessage();
})
}
.padding(8)
}
private async sendMessage(): Promise<void> {
if (this.inputText.length === 0 || this.isLoading) return;
const userInput = this.inputText;
this.inputText = '';
// 添加用户消息到界面
this.messages.push({
role: 'user',
content: userInput,
timestamp: Date.now()
});
// 显示加载状态
this.isLoading = true;
try {
// 调用Agent处理
const response = await this.agentService.handleUserInput(userInput);
// 添加助手回复到界面
this.messages.push({
role: 'assistant',
content: response.message,
timestamp: Date.now()
});
} catch (e) {
this.messages.push({
role: 'assistant',
content: '处理失败:' + e.message,
timestamp: Date.now()
});
} finally {
this.isLoading = false;
}
}
【提示】使用isLoading状态控制加载指示器的显示。在等待大模型响应时,可以显示"正在思考…"的加载动画。
任务7 日程列表界面
【任务目标】实现ScheduleListPage日程列表界面,展示已有日程并支持查看详情。
步骤7.1 定义列表页面
// pages/ScheduleListPage.ets
import { CalendarManager } from '../service/CalendarManager';
import { ScheduleEvent } from '../model/Types';
@Entry
@Component
struct ScheduleListPage {
@State schedules: ScheduleEvent[] = [];
@State selectedDate: string = '';
private calendarManager: CalendarManager = new CalendarManager();
aboutToAppear(): void {
this.loadSchedules();
}
private async loadSchedules(): Promise<void> {
// TODO: 加载今日及未来7天的日程
// 提示:
// 1. 计算今天和7天后的日期
// 2. 调用 calendarManager.querySchedules()
// 3. 更新schedules状态
}
build() {
Column() {
// 标题栏
Text('我的日程')
.fontSize(20).fontWeight(FontWeight.Bold)
.padding(16)
// 日程列表
List({ space: 8 }) {
ForEach(this.schedules, (schedule: ScheduleEvent) => {
// TODO: 渲染日程卡片
})
}
.layoutWeight(1)
.padding(12)
}
}
}
步骤7.2 实现日程卡片
@Builder
ScheduleCard(schedule: ScheduleEvent) {
Row({ space: 12 }) {
// 时间列
Column() {
Text(this.formatTime(schedule.startTime))
.fontSize(16).fontWeight(FontWeight.Bold)
Text(this.formatTime(schedule.endTime))
.fontSize(12).fontColor('#999999')
}
.width(60)
.alignItems(HorizontalAlign.Center)
// 分隔线
Divider()
.vertical(true)
.height(40)
// 内容列
Column() {
Text(schedule.title)
.fontSize(15).fontWeight(FontWeight.Medium)
if (schedule.location) {
Text(schedule.location)
.fontSize(12).fontColor('#666666')
.margin({ top: 4 })
}
}
.layoutWeight(1)
.alignItems(HorizontalAlign.Start)
// 提醒图标
if (schedule.reminderMinutes && schedule.reminderMinutes > 0) {
Image($r('app.media.ic_reminder'))
.width(20).height(20)
}
}
.padding(12)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.shadow({ radius: 2, color: '#10000000' })
}
private formatTime(timeStr: string): string {
// TODO: 将ISO时间字符串格式化为HH:mm
const date = new Date(timeStr);
const h = date.getHours().toString().padStart(2, '0');
const m = date.getMinutes().toString().padStart(2, '0');
return h + ':' + m;
}
任务8 入口页面与配置
【任务目标】实现IndexPage入口页面(Tab布局)、EntryAbility和module.json5权限配置。
步骤8.1 实现Tab入口页面
// pages/IndexPage.ets
import { ChatPage } from './ChatPage';
import { ScheduleListPage } from './ScheduleListPage';
@Entry
@Component
struct IndexPage {
@State currentIndex: number = 0;
build() {
Column() {
TabBar({
currentIndex: this.currentIndex,
onTabClick: (index: number) => {
this.currentIndex = index;
}
})
if (this.currentIndex === 0) {
ChatPage()
} else {
ScheduleListPage()
}
}
}
}
@Component
struct TabBar {
@Prop currentIndex: number;
onTabClick: (index: number) => void;
build() {
Row() {
Button('对话')
.layoutWeight(1)
.backgroundColor(this.currentIndex === 0 ? '#0070C0' : '#F0F0F0')
.fontColor(this.currentIndex === 0 ? '#FFFFFF' : '#333333')
.onClick(() => this.onTabClick(0))
Button('日程')
.layoutWeight(1)
.backgroundColor(this.currentIndex === 1 ? '#0070C0' : '#F0F0F0')
.fontColor(this.currentIndex === 1 ? '#FFFFFF' : '#333333')
.onClick(() => this.onTabClick(1))
}
.height(50)
}
}
步骤8.2 配置module.json5权限
在module.json5中声明所需权限:
// module.json5
{
"module": {
"name": "entry",
"type": "entry",
"srcEntry": "./ets/entryability/EntryAbility.ets",
"requestPermissions": [
{
"name": "ohos.permission.INTERNET",
"reason": "调用云端大模型API",
"usedScene": {}
},
{
"name": "ohos.permission.READ_CALENDAR",
"reason": "读取日程信息",
"usedScene": {}
},
{
"name": "ohos.permission.WRITE_CALENDAR",
"reason": "创建和修改日程",
"usedScene": {}
},
{
"name": "ohos.permission.NOTIFICATION_CONTROLLER",
"reason": "发送日程提醒通知",
"usedScene": {}
}
]
}
}
步骤8.3 配置页面路由
// resources/base/profile/main_pages.json
{
"src": [
"pages/IndexPage",
"pages/ChatPage",
"pages/ScheduleListPage"
]
}
【注意】READ_CALENDAR和WRITE_CALENDAR属于用户授权权限,需要在运行时动态申请。请在EntryAbility的onWindowStageCreate中添加权限申请逻辑。
任务9 功能测试
【任务目标】对完成的智能日程Agent应用进行全面功能测试,验证各功能正常工作。
9.1 测试用例
请按以下测试用例逐一验证应用功能:

9.2 测试方法
1.启动应用,进入对话页面。
2.按T01-T08顺序输入测试用例中的文本。
3.观察Agent的回复是否符合预期结果。
4.切换到日程列表页面,验证日程是否正确创建/修改/删除。
5.在"通过"列填写测试结果(通过/失败)。
9.3 常见问题排查

任务10 优化与完善
【任务目标】对应用进行性能优化和体验完善。
10.1 延迟优化
大模型API调用通常需要1-3秒,在此期间应给用户明确的反馈:
// 在ChatPage中添加加载动画
@Builder
LoadingIndicator() {
if (this.isLoading) {
Row({ space: 8 }) {
LoadingProgress()
.width(20).height(20)
Text('Agent正在思考...')
.fontSize(13).fontColor('#999999')
}
.padding(12)
}
}
10.2 意图准确率优化
如果大模型偶尔无法正确理解意图,可以通过优化系统提示词提升准确率:
// 在DialogManager.getSystemPrompt()中添加few-shot示例
const FEW_SHOT = `
示例1:
用户:明天下午3点开会
意图:创建日程,title=会议,startTime=明天15:00
示例2:
用户:这周有什么安排
意图:查询日程,startDate=本周一,endDate=本周日
示例3:
用户:取消明天的会议
意图:删除日程,需要先查询确定scheduleId`;
10.3 用户体验优化
- 消息列表自动滚动到最新消息。
- 长按消息可复制内容。
- 日程列表支持下拉刷新。
- 添加空状态提示(无日程时显示"暂无日程")。
五、拓展任务(选做)
以下拓展任务供学有余力的同学选做,完成任意一项即可获得额外加分。
拓展1 语音输入扩展
在ChatPage中集成语音识别功能,允许用户通过语音输入创建日程。要求:使用AudioCapturer采集音频,调用云端ASR服务识别文本,自动填入输入框。
拓展2 日程冲突智能解决
当检测到时间冲突时,Agent不仅提示冲突,还主动建议替代时间。要求:修改toolCreateSchedule方法,在冲突时查询附近可用时间段并返回建议。
拓展3 日程智能分析
扩展Agent能力,支持自然语言分析日程统计。例如用户输入"这周开了多少会",Agent调用querySchedule获取数据并统计回复。要求:新增analyzeSchedule工具函数。
拓展4 重复日程支持
扩展createSchedule工具,支持重复日程(如"每周三下午3点开周会")。要求:解析重复规则,在日历API中设置recurrence字段。
更多推荐




所有评论(0)